@@ -281,6 +281,18 @@ bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b, const i
281
281
return true;
282
282
}
283
283
284
+ bool SDL_AudioChannelMapsEqual (int channels , const int * channel_map_a , const int * channel_map_b )
285
+ {
286
+ if (channel_map_a == channel_map_b ) {
287
+ return true;
288
+ } else if ((channel_map_a != NULL ) != (channel_map_b != NULL )) {
289
+ return false;
290
+ } else if (channel_map_a && (SDL_memcmp (channel_map_a , channel_map_b , sizeof (* channel_map_a ) * channels ) != 0 )) {
291
+ return false;
292
+ }
293
+ return true;
294
+ }
295
+
284
296
285
297
// Zombie device implementation...
286
298
@@ -1134,7 +1146,7 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
1134
1146
SDL_AudioStream * stream = logdev -> bound_streams ;
1135
1147
1136
1148
// We should have updated this elsewhere if the format changed!
1137
- SDL_assert (SDL_AudioSpecsEqual (& stream -> dst_spec , & device -> spec , stream -> dst_chmap , device -> chmap ));
1149
+ SDL_assert (SDL_AudioSpecsEqual (& stream -> dst_spec , & device -> spec , NULL , NULL ));
1138
1150
1139
1151
const int br = SDL_GetAtomicInt (& logdev -> paused ) ? 0 : SDL_GetAudioStreamDataAdjustGain (stream , device_buffer , buffer_size , logdev -> gain );
1140
1152
if (br < 0 ) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
@@ -1143,6 +1155,12 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
1143
1155
} else if (br < buffer_size ) {
1144
1156
SDL_memset (device_buffer + br , device -> silence_value , buffer_size - br ); // silence whatever we didn't write to.
1145
1157
}
1158
+
1159
+ // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to device layout.
1160
+ if ((br > 0 ) && (!SDL_AudioChannelMapsEqual (device -> spec .channels , stream -> dst_chmap , device -> chmap ))) {
1161
+ ConvertAudio (br / SDL_AUDIO_FRAMESIZE (device -> spec ), device_buffer , device -> spec .format , device -> spec .channels , NULL ,
1162
+ device_buffer , device -> spec .format , device -> spec .channels , device -> chmap , NULL , 1.0f );
1163
+ }
1146
1164
} else { // need to actually mix (or silence the buffer)
1147
1165
float * final_mix_buffer = (float * ) ((device -> spec .format == SDL_AUDIO_F32 ) ? device_buffer : device -> mix_buffer );
1148
1166
const int needed_samples = buffer_size / SDL_AUDIO_BYTESIZE (device -> spec .format );
@@ -1170,7 +1188,7 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
1170
1188
1171
1189
for (SDL_AudioStream * stream = logdev -> bound_streams ; stream ; stream = stream -> next_binding ) {
1172
1190
// We should have updated this elsewhere if the format changed!
1173
- SDL_assert (SDL_AudioSpecsEqual (& stream -> dst_spec , & outspec , stream -> dst_chmap , device -> chmap ));
1191
+ SDL_assert (SDL_AudioSpecsEqual (& stream -> dst_spec , & outspec , NULL , NULL ));
1174
1192
1175
1193
/* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
1176
1194
for iterating here because the binding linked list can only change while the device lock is held.
@@ -1181,6 +1199,11 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
1181
1199
failed = true;
1182
1200
break ;
1183
1201
} else if (br > 0 ) { // it's okay if we get less than requested, we mix what we have.
1202
+ // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to device layout.
1203
+ if (!SDL_AudioChannelMapsEqual (device -> spec .channels , stream -> dst_chmap , device -> chmap )) {
1204
+ ConvertAudio (br / SDL_AUDIO_FRAMESIZE (device -> spec ), device -> work_buffer , device -> spec .format , device -> spec .channels , NULL ,
1205
+ device -> work_buffer , device -> spec .format , device -> spec .channels , device -> chmap , NULL , 1.0f );
1206
+ }
1184
1207
MixFloat32Audio (mix_buffer , (float * ) device -> work_buffer , br );
1185
1208
}
1186
1209
}
@@ -1303,11 +1326,20 @@ bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device)
1303
1326
SDL_assert (stream -> src_spec .channels == device -> spec .channels );
1304
1327
SDL_assert (stream -> src_spec .freq == device -> spec .freq );
1305
1328
1329
+ void * final_buf = output_buffer ;
1330
+
1331
+ // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to stream layout.
1332
+ if (!SDL_AudioChannelMapsEqual (device -> spec .channels , stream -> src_chmap , device -> chmap )) {
1333
+ final_buf = device -> mix_buffer ; // this is otherwise unused on recording devices, so it makes convenient scratch space here.
1334
+ ConvertAudio (br / SDL_AUDIO_FRAMESIZE (device -> spec ), output_buffer , device -> spec .format , device -> spec .channels , NULL ,
1335
+ final_buf , device -> spec .format , device -> spec .channels , stream -> src_chmap , NULL , 1.0f );
1336
+ }
1337
+
1306
1338
/* this will hold a lock on `stream` while putting. We don't explicitly lock the streams
1307
1339
for iterating here because the binding linked list can only change while the device lock is held.
1308
1340
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
1309
1341
the same stream to different devices at the same time, though.) */
1310
- if (!SDL_PutAudioStreamData (stream , output_buffer , br )) {
1342
+ if (!SDL_PutAudioStreamData (stream , final_buf , br )) {
1311
1343
// oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow.
1312
1344
failed = true;
1313
1345
break ;
0 commit comments