1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23// This provides the default mixing callback for the SDL audio routines
24
25#include "SDL_sysaudio.h"
26
27/* This table is used to add two sound values together and pin
28 * the value to avoid overflow. (used with permission from ARDI)
29 */
30static const Uint8 mix8[] = {
31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
43 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
44 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
45 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
46 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
48 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
49 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
50 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
51 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
52 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
53 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
54 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
55 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
56 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
57 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
58 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
59 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
60 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
61 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
62 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
63 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
64 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
65 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF,
66 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
67 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
68 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
69 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
70 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
71 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
72 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
73 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
74 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
75 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
76 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
77 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
78};
79
80// The volume ranges from 0 - 128
81#define MIX_MAXVOLUME 128
82#define ADJUST_VOLUME(type, s, v) ((s) = (type)(((s) * (v)) / MIX_MAXVOLUME))
83#define ADJUST_VOLUME_U8(s, v) ((s) = (Uint8)(((((s) - 128) * (v)) / MIX_MAXVOLUME) + 128))
84
85// !!! FIXME: This needs some SIMD magic.
86// !!! FIXME: Add fast-path for volume = 1
87// !!! FIXME: Use larger scales for 16-bit/32-bit integers
88
89bool SDL_MixAudio(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, Uint32 len, float fvolume)
90{
91 int volume = (int)SDL_roundf(fvolume * MIX_MAXVOLUME);
92
93 if (volume == 0) {
94 return true;
95 }
96
97 switch (format) {
98
99 case SDL_AUDIO_U8:
100 {
101 Uint8 src_sample;
102
103 while (len--) {
104 src_sample = *src;
105 ADJUST_VOLUME_U8(src_sample, volume);
106 *dst = mix8[*dst + src_sample];
107 ++dst;
108 ++src;
109 }
110 } break;
111
112 case SDL_AUDIO_S8:
113 {
114 Sint8 *dst8, *src8;
115 Sint8 src_sample;
116 int dst_sample;
117 const int max_audioval = SDL_MAX_SINT8;
118 const int min_audioval = SDL_MIN_SINT8;
119
120 src8 = (Sint8 *)src;
121 dst8 = (Sint8 *)dst;
122 while (len--) {
123 src_sample = *src8;
124 ADJUST_VOLUME(Sint8, src_sample, volume);
125 dst_sample = *dst8 + src_sample;
126 if (dst_sample > max_audioval) {
127 dst_sample = max_audioval;
128 } else if (dst_sample < min_audioval) {
129 dst_sample = min_audioval;
130 }
131 *dst8 = (Sint8)dst_sample;
132 ++dst8;
133 ++src8;
134 }
135 } break;
136
137 case SDL_AUDIO_S16LE:
138 {
139 Sint16 src1, src2;
140 int dst_sample;
141 const int max_audioval = SDL_MAX_SINT16;
142 const int min_audioval = SDL_MIN_SINT16;
143
144 len /= 2;
145 while (len--) {
146 src1 = SDL_Swap16LE(*(Sint16 *)src);
147 ADJUST_VOLUME(Sint16, src1, volume);
148 src2 = SDL_Swap16LE(*(Sint16 *)dst);
149 src += 2;
150 dst_sample = src1 + src2;
151 if (dst_sample > max_audioval) {
152 dst_sample = max_audioval;
153 } else if (dst_sample < min_audioval) {
154 dst_sample = min_audioval;
155 }
156 *(Sint16 *)dst = SDL_Swap16LE((Sint16)dst_sample);
157 dst += 2;
158 }
159 } break;
160
161 case SDL_AUDIO_S16BE:
162 {
163 Sint16 src1, src2;
164 int dst_sample;
165 const int max_audioval = SDL_MAX_SINT16;
166 const int min_audioval = SDL_MIN_SINT16;
167
168 len /= 2;
169 while (len--) {
170 src1 = SDL_Swap16BE(*(Sint16 *)src);
171 ADJUST_VOLUME(Sint16, src1, volume);
172 src2 = SDL_Swap16BE(*(Sint16 *)dst);
173 src += 2;
174 dst_sample = src1 + src2;
175 if (dst_sample > max_audioval) {
176 dst_sample = max_audioval;
177 } else if (dst_sample < min_audioval) {
178 dst_sample = min_audioval;
179 }
180 *(Sint16 *)dst = SDL_Swap16BE((Sint16)dst_sample);
181 dst += 2;
182 }
183 } break;
184
185 case SDL_AUDIO_S32LE:
186 {
187 const Uint32 *src32 = (Uint32 *)src;
188 Uint32 *dst32 = (Uint32 *)dst;
189 Sint64 src1, src2;
190 Sint64 dst_sample;
191 const Sint64 max_audioval = SDL_MAX_SINT32;
192 const Sint64 min_audioval = SDL_MIN_SINT32;
193
194 len /= 4;
195 while (len--) {
196 src1 = (Sint64)((Sint32)SDL_Swap32LE(*src32));
197 src32++;
198 ADJUST_VOLUME(Sint64, src1, volume);
199 src2 = (Sint64)((Sint32)SDL_Swap32LE(*dst32));
200 dst_sample = src1 + src2;
201 if (dst_sample > max_audioval) {
202 dst_sample = max_audioval;
203 } else if (dst_sample < min_audioval) {
204 dst_sample = min_audioval;
205 }
206 *(dst32++) = SDL_Swap32LE((Uint32)((Sint32)dst_sample));
207 }
208 } break;
209
210 case SDL_AUDIO_S32BE:
211 {
212 const Uint32 *src32 = (Uint32 *)src;
213 Uint32 *dst32 = (Uint32 *)dst;
214 Sint64 src1, src2;
215 Sint64 dst_sample;
216 const Sint64 max_audioval = SDL_MAX_SINT32;
217 const Sint64 min_audioval = SDL_MIN_SINT32;
218
219 len /= 4;
220 while (len--) {
221 src1 = (Sint64)((Sint32)SDL_Swap32BE(*src32));
222 src32++;
223 ADJUST_VOLUME(Sint64, src1, volume);
224 src2 = (Sint64)((Sint32)SDL_Swap32BE(*dst32));
225 dst_sample = src1 + src2;
226 if (dst_sample > max_audioval) {
227 dst_sample = max_audioval;
228 } else if (dst_sample < min_audioval) {
229 dst_sample = min_audioval;
230 }
231 *(dst32++) = SDL_Swap32BE((Uint32)((Sint32)dst_sample));
232 }
233 } break;
234
235 case SDL_AUDIO_F32LE:
236 {
237 const float *src32 = (float *)src;
238 float *dst32 = (float *)dst;
239 float src1, src2;
240 float dst_sample;
241 const float max_audioval = 1.0f;
242 const float min_audioval = -1.0f;
243
244 len /= 4;
245 while (len--) {
246 src1 = SDL_SwapFloatLE(*src32) * fvolume;
247 src2 = SDL_SwapFloatLE(*dst32);
248 src32++;
249
250 dst_sample = src1 + src2;
251 if (dst_sample > max_audioval) {
252 dst_sample = max_audioval;
253 } else if (dst_sample < min_audioval) {
254 dst_sample = min_audioval;
255 }
256 *(dst32++) = SDL_SwapFloatLE(dst_sample);
257 }
258 } break;
259
260 case SDL_AUDIO_F32BE:
261 {
262 const float *src32 = (float *)src;
263 float *dst32 = (float *)dst;
264 float src1, src2;
265 float dst_sample;
266 const float max_audioval = 1.0f;
267 const float min_audioval = -1.0f;
268
269 len /= 4;
270 while (len--) {
271 src1 = SDL_SwapFloatBE(*src32) * fvolume;
272 src2 = SDL_SwapFloatBE(*dst32);
273 src32++;
274
275 dst_sample = src1 + src2;
276 if (dst_sample > max_audioval) {
277 dst_sample = max_audioval;
278 } else if (dst_sample < min_audioval) {
279 dst_sample = min_audioval;
280 }
281 *(dst32++) = SDL_SwapFloatBE(dst_sample);
282 }
283 } break;
284
285 default: // If this happens... FIXME!
286 return SDL_SetError("SDL_MixAudio(): unknown audio format");
287 }
288
289 return true;
290}
291