@@ -140,8 +140,8 @@ static void phonon_destroy(void);
140140static void phonon_update (float dt );
141141static bool phonon_set_hrtf (Blob * blob );
142142static void phonon_mix_begin (void );
143- static bool phonon_mix_source (Source * source , float * src , float * dst , float * tmp );
144- static void phonon_mix_tail (float * output , float * temp );
143+ static bool phonon_mix_source (Source * source , float * src , float * dst );
144+ static void phonon_mix_reverb (float * dst );
145145static bool phonon_source_init (Source * source );
146146static void phonon_source_destroy (Source * source );
147147static void phonon_source_add (Source * source );
@@ -168,12 +168,14 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
168168 uint32_t play = atomic_exchange (& source -> playRequest , ~0u );
169169
170170 if (play != ~0u ) {
171+ #ifdef LOVR_USE_PHONON
171172 if (!source -> playing && play == 1 ) {
172173 iplDirectEffectReset (source -> directEffect );
173174 iplPanningEffectReset (source -> panningEffect );
174175 iplBinauralEffectReset (source -> binauralEffect );
175176 iplReflectionEffectReset (source -> reflectionEffect );
176177 }
178+ #endif
177179
178180 source -> playing = !!play ;
179181 }
@@ -184,23 +186,21 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
184186 bool pitchChanged = atomic_exchange (& source -> pitchChanged , false);
185187 if (pitchChanged ) ma_data_converter_set_rate_ratio (source -> converter , source -> pitchRatio );
186188
187- bool hasTail = false ;
189+ uint32_t channels = lovrSoundGetChannelCount ( source -> sound ) ;
188190
189191 if (source -> playing ) {
190192 // Read and convert raw frames until there's BUFFER_SIZE converted frames
191193 // - No converter: just read frames into raw
192194 // - Converter: keep reading as many frames as possible/needed into raw and convert into tmp.
193195 // - If EOF is reached, rewind and continue for looping sources, otherwise pad end with zero.
194196 float * cursor = source -> converter ? tmp : raw ; // Edge of processed frames
195- uint32_t channelsOut = source -> spatial ? 1 : 2 ; // If spatializer isn't converting to stereo, converter must do it
196197 uint32_t framesRemaining = BUFFER_SIZE ;
197198
198199 while (framesRemaining > 0 ) {
199200 uint32_t framesRead ;
200201
201202 if (source -> converter ) {
202- uint32_t channelsIn = lovrSoundGetChannelCount (source -> sound );
203- uint32_t capacity = sizeof (raw ) / (channelsIn * sizeof (float ));
203+ uint32_t capacity = sizeof (raw ) / (channels * sizeof (float ));
204204 ma_uint64 chunk ;
205205 ma_data_converter_get_required_input_frame_count (source -> converter , framesRemaining , & chunk );
206206 framesRead = lovrSoundRead (source -> sound , source -> offset , MIN (chunk , capacity ), raw );
@@ -215,7 +215,7 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
215215 } else {
216216 source -> offset = 0 ;
217217 source -> playing = false;
218- memset (cursor , 0 , framesRemaining * channelsOut * sizeof (float ));
218+ memset (cursor , 0 , framesRemaining * channels * sizeof (float ));
219219 break ;
220220 }
221221 } else {
@@ -226,35 +226,41 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
226226 ma_uint64 framesIn = framesRead ;
227227 ma_uint64 framesOut = framesRemaining ;
228228 ma_data_converter_process_pcm_frames (source -> converter , raw , & framesIn , cursor , & framesOut );
229- cursor += framesOut * channelsOut ;
229+ cursor += framesOut * channels ;
230230 framesRemaining -= framesOut ;
231231 } else {
232- cursor += framesRead * channelsOut ;
232+ cursor += framesRead * channels ;
233233 framesRemaining -= framesRead ;
234234 }
235235 }
236236
237237 buf = source -> converter ? tmp : raw ;
238238 }
239239
240- bool tail = false;
240+ // Scale
241+ for (uint32_t i = 0 ; i < channels * BUFFER_SIZE ; i ++ ) {
242+ buf [i ] *= source -> volume ;
243+ }
241244
245+ // Spatialize
242246 if (source -> spatial ) {
243- tail = phonon_mix_source (source , buf , mix , buf == raw ? tmp : raw );
247+ source -> hasTail = phonon_mix_source (source , buf , mix );
248+ buf = mix ;
249+ } else if (channels == 1 ) {
250+ for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
251+ mix [i * 2 + 0 ] = buf [i ];
252+ mix [i * 2 + 1 ] = buf [i ];
253+ }
244254 buf = mix ;
245255 }
246256
247257 // Mix
248- float volume = source -> volume ;
249258 for (uint32_t i = 0 ; i < 2 * BUFFER_SIZE ; i ++ ) {
250- dst [i ] += buf [i ] * volume ;
259+ dst [i ] += buf [i ];
251260 }
252-
253- // Once we set this to false, the source could get destroyed (if it's not playing)
254- source -> hasTail = tail ;
255261 }
256262
257- phonon_mix_tail (dst , tmp );
263+ phonon_mix_reverb (dst );
258264
259265 if (state .sinks [AUDIO_PLAYBACK ]) {
260266 uint64_t capacity = sizeof (tmp ) / lovrSoundGetChannelCount (state .sinks [AUDIO_PLAYBACK ]) / sizeof (float );
@@ -549,12 +555,12 @@ Source* lovrSourceCreate(Sound* sound, bool pitchable, bool spatial) {
549555 config .formatIn = miniaudioFormats [lovrSoundGetFormat (sound )];
550556 config .formatOut = miniaudioFormats [OUTPUT_FORMAT ];
551557 config .channelsIn = lovrSoundGetChannelCount (sound );
552- config .channelsOut = spatial ? 1 : 2 ;
558+ config .channelsOut = lovrSoundGetChannelCount ( sound ) ;
553559 config .sampleRateIn = lovrSoundGetSampleRate (sound );
554560 config .sampleRateOut = state .config .sampleRate ;
555561 config .allowDynamicSampleRate = pitchable ;
556562
557- if (pitchable || config .formatIn != config .formatOut || config .channelsIn != config . channelsOut || config . sampleRateIn != config .sampleRateOut ) {
563+ if (pitchable || config .formatIn != config .formatOut || config .sampleRateIn != config .sampleRateOut ) {
558564 ma_data_converter * converter = lovrMalloc (sizeof (ma_data_converter ));
559565 ma_result status = ma_data_converter_init (& config , NULL , converter );
560566
@@ -1199,91 +1205,132 @@ static void phonon_mix_begin(void) {
11991205 }
12001206}
12011207
1202- static bool phonon_mix_source (Source * source , float * _src , float * dst , float * _tmp ) {
1203- IPLAudioBuffer src = { .numChannels = 1 , .numSamples = BUFFER_SIZE , .data = & _src };
1204- IPLAudioBuffer tmp1 = { .numChannels = 1 , .numSamples = BUFFER_SIZE , .data = & _tmp };
1205- IPLAudioBuffer tmp2 = { .numChannels = 2 , .numSamples = BUFFER_SIZE , .data = (float * [2 ]) { _tmp , _tmp + BUFFER_SIZE } };
1206-
1208+ static bool phonon_mix_source (Source * source , float * src , float * dst ) {
1209+ float left [BUFFER_SIZE ], right [BUFFER_SIZE ];
1210+ uint32_t channels = lovrSoundGetChannelCount (source -> sound );
12071211 uint32_t index = !state .backbuffer ;
12081212 bool tail = false;
12091213
12101214 // Tail
12111215 if (!source -> playing ) {
1216+ IPLAudioBuffer buffer = { 2 , BUFFER_SIZE , (float * [2 ]) { left , right } };
1217+
12121218 if (iplBinauralEffectGetTailSize (source -> binauralEffect ) > 0 ) {
1213- tail |= !iplBinauralEffectGetTail (source -> binauralEffect , & tmp2 );
1214- iplAudioBufferInterleave (state .phonon , & tmp2 , dst );
1219+ tail |= !iplBinauralEffectGetTail (source -> binauralEffect , & buffer );
1220+ iplAudioBufferInterleave (state .phonon , & buffer , dst );
12151221 } else {
12161222 memset (dst , 0 , 2 * BUFFER_SIZE * sizeof (float ));
12171223 }
12181224
12191225 if (iplReflectionEffectGetTailSize (source -> reflectionEffect ) > 0 ) {
12201226 if (state .config .reverb .type == REVERB_CONVOLUTION ) {
1221- tail |= !iplReflectionEffectGetTail (source -> reflectionEffect , & tmp1 , state .reflectionMixer );
1227+ tail |= !iplReflectionEffectGetTail (source -> reflectionEffect , & buffer , state .reflectionMixer );
12221228 } else {
1223- tail |= !iplReflectionEffectGetTail (source -> reflectionEffect , & tmp1 , NULL );
1224- iplAudioBufferMix (state .phonon , & tmp1 , & state .reflectionBuffer );
1229+ buffer .numChannels = 1 ;
1230+ tail |= !iplReflectionEffectGetTail (source -> reflectionEffect , & buffer , NULL );
1231+ iplAudioBufferMix (state .phonon , & buffer , & state .reflectionBuffer );
12251232 }
12261233 }
12271234
12281235 return tail ;
12291236 }
12301237
1238+ // Prepare input (since we always copy src, it can be reused as a temporary buffer after this)
1239+ IPLAudioBuffer input = { channels , BUFFER_SIZE , (float * [2 ]) { left , right } };
1240+
1241+ if (channels == 1 ) {
1242+ input .data [1 ] = left ; // Alias both channels, useful for spatialization
1243+ memcpy (left , src , BUFFER_SIZE * sizeof (float ));
1244+ } else {
1245+ iplAudioBufferDeinterleave (state .phonon , src , & input );
1246+ }
1247+
12311248 // Reverb
12321249 if (source -> reverb > 0.f && state .enabledMeshCount > 0 ) {
1250+ if (channels == 2 ) {
1251+ for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1252+ src [i ] = (left [i ] + right [i ]) * .5f * source -> reverb ;
1253+ }
1254+ } else if (source -> reverb != 1.f ) {
1255+ for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1256+ src [i ] *= source -> reverb ;
1257+ }
1258+ }
1259+
1260+ IPLAudioBuffer reverbInput = { 1 , BUFFER_SIZE , & src };
1261+
12331262 if (source -> reverbMode == REVERB_SOURCE ) {
12341263 IPLSimulationOutputs outputs = { 0 };
12351264 iplSourceGetOutputs (source -> handle , IPL_SIMULATIONFLAGS_REFLECTIONS , & outputs );
12361265
12371266 if (state .config .reverb .type == REVERB_CONVOLUTION ) {
1238- tail |= !iplReflectionEffectApply (source -> reflectionEffect , & outputs .reflections , & src , & tmp1 , state .reflectionMixer );
1267+ tail |= !iplReflectionEffectApply (source -> reflectionEffect , & outputs .reflections , & reverbInput , & reverbInput , state .reflectionMixer );
12391268 } else {
1240- tail |= !iplReflectionEffectApply (source -> reflectionEffect , & outputs .reflections , & src , & tmp1 , NULL );
1241- iplAudioBufferMix (state .phonon , & tmp1 , & state .reflectionBuffer );
1269+ IPLAudioBuffer out = { 1 , BUFFER_SIZE , (float * [1 ]) { dst } };
1270+ tail |= !iplReflectionEffectApply (source -> reflectionEffect , & outputs .reflections , & reverbInput , & out , NULL );
1271+ iplAudioBufferMix (state .phonon , & out , & state .reflectionBuffer );
12421272 }
12431273 } else if (state .reverb > 0.f ) {
1244- iplAudioBufferMix (state .phonon , & src , & state .listenerReverbInput );
1274+ iplAudioBufferMix (state .phonon , & reverbInput , & state .listenerReverbInput );
12451275 }
12461276 }
12471277
12481278 // Direct effects, applied in-place
1249- IPLDirectEffectParams * directParams = & source -> outputs [index ].direct ;
1250- if (directParams -> flags ) {
1251- tail |= !iplDirectEffectApply (source -> directEffect , directParams , & src , & src );
1279+ if (source -> outputs [index ].direct .flags ) {
1280+ tail |= !iplDirectEffectApply (source -> directEffect , & source -> outputs [index ].direct , & input , & input );
12521281 }
12531282
12541283 // Spatialization (either binaural, panning, or upmix)
12551284 if (source -> spatialization > 0.f ) {
1285+ // Can reuse src as temporary buffer for spatialization output
1286+ IPLAudioBuffer spatialized = { 2 , BUFFER_SIZE , (float * [2 ]) { src , src + BUFFER_SIZE } };
1287+
12561288 if (state .hrtf [0 ]) {
12571289 IPLBinauralEffectParams params = {
12581290 .direction = source -> relativeDirection [index ],
12591291 .interpolation = IPL_HRTFINTERPOLATION_BILINEAR ,
12601292 .spatialBlend = source -> spatialization ,
12611293 .hrtf = state .hrtf [0 ]
12621294 };
1263-
1264- tail |= !iplBinauralEffectApply (source -> binauralEffect , & params , & src , & tmp2 );
1295+ input . numChannels = 2 ; // For mono input, left/right channels are aliased to same buffer
1296+ tail |= !iplBinauralEffectApply (source -> binauralEffect , & params , & input , & spatialized );
12651297 } else {
1266- IPLPanningEffectParams params = {
1267- .direction = source -> relativeDirection [index ]
1268- };
1298+ IPLAudioBuffer mono = { 1 , BUFFER_SIZE , .data = channels == 2 ? & dst : input .data };
1299+
1300+ // Stereo input uses dst as temporary buffer to hold downmixed audio, for panning effect
1301+ if (channels == 2 ) {
1302+ for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1303+ dst [i ] = (left [i ] + right [i ]) * .5f ;
1304+ }
1305+ }
12691306
1270- iplPanningEffectApply (source -> panningEffect , & params , & src , & tmp2 );
1307+ IPLPanningEffectParams params = { .direction = source -> relativeDirection [index ] };
1308+ iplPanningEffectApply (source -> panningEffect , & params , & mono , & spatialized );
1309+
1310+ if (source -> spatialization < 1.f ) {
1311+ float s = source -> spatialization , t = 1.f - s ;
1312+ for (uint32_t c = 0 ; c < 2 ; c ++ ) {
1313+ for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1314+ spatialized .data [c ][i ] = spatialized .data [c ][i ] * s + input .data [c ][i ] * t ;
1315+ }
1316+ }
1317+ }
12711318 }
12721319
1273- iplAudioBufferInterleave (state .phonon , & tmp2 , dst );
1320+ iplAudioBufferInterleave (state .phonon , & spatialized , dst );
12741321 } else {
1275- for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1276- dst [2 * i + 0 ] = _src [i ];
1277- dst [2 * i + 1 ] = _src [i ];
1278- }
1322+ input .numChannels = 2 ;
1323+ iplAudioBufferInterleave (state .phonon , & input , dst );
12791324 }
12801325
12811326 return tail ;
12821327}
12831328
1284- static void phonon_mix_tail (float * dst , float * _tmp ) {
1285- IPLAudioBuffer tmp1 = { .numChannels = 1 , .numSamples = BUFFER_SIZE , .data = & _tmp };
1286- IPLAudioBuffer tmp2 = { .numChannels = 2 , .numSamples = BUFFER_SIZE , .data = (float * [2 ]) { _tmp , _tmp + BUFFER_SIZE } };
1329+ static void phonon_mix_reverb (float * dst ) {
1330+ float left [BUFFER_SIZE ], right [BUFFER_SIZE ];
1331+
1332+ IPLAudioBuffer mono = { 1 , BUFFER_SIZE , (float * [1 ]) { left } };
1333+ IPLAudioBuffer stereo = { 2 , BUFFER_SIZE , (float * [2 ]) { left , right } };
12871334
12881335 bool anyReverb = state .sourceReverbMask || state .listenerReverbMask ;
12891336
@@ -1293,17 +1340,17 @@ static void phonon_mix_tail(float* dst, float* _tmp) {
12931340 iplSourceGetOutputs (state .listener , IPL_SIMULATIONFLAGS_REFLECTIONS , & outputs );
12941341
12951342 if (state .config .reverb .type == REVERB_CONVOLUTION ) {
1296- iplReflectionEffectApply (state .reflectionEffect , & outputs .reflections , & state .listenerReverbInput , & tmp1 , state .reflectionMixer );
1343+ iplReflectionEffectApply (state .reflectionEffect , & outputs .reflections , & state .listenerReverbInput , & mono , state .reflectionMixer );
12971344 } else {
1298- iplReflectionEffectApply (state .reflectionEffect , & outputs .reflections , & state .listenerReverbInput , & tmp1 , NULL );
1299- iplAudioBufferMix (state .phonon , & tmp1 , & state .reflectionBuffer );
1345+ iplReflectionEffectApply (state .reflectionEffect , & outputs .reflections , & state .listenerReverbInput , & mono , NULL );
1346+ iplAudioBufferMix (state .phonon , & mono , & state .reflectionBuffer );
13001347 }
13011348 } else if (iplReflectionEffectGetTailSize (state .reflectionEffect ) > 0 ) {
13021349 if (state .config .reverb .type == REVERB_CONVOLUTION ) {
1303- iplReflectionEffectGetTail (state .reflectionEffect , & tmp1 , state .reflectionMixer );
1350+ iplReflectionEffectGetTail (state .reflectionEffect , & mono , state .reflectionMixer );
13041351 } else {
1305- iplReflectionEffectGetTail (state .reflectionEffect , & tmp1 , NULL );
1306- iplAudioBufferMix (state .phonon , & tmp1 , & state .reflectionBuffer );
1352+ iplReflectionEffectGetTail (state .reflectionEffect , & mono , NULL );
1353+ iplAudioBufferMix (state .phonon , & mono , & state .reflectionBuffer );
13071354 }
13081355
13091356 anyReverb = true;
@@ -1325,16 +1372,16 @@ static void phonon_mix_tail(float* dst, float* _tmp) {
13251372 .binaural = !!state .hrtf [0 ]
13261373 };
13271374
1328- iplAmbisonicsDecodeEffectApply (state .ambisonicsDecodeEffect , & ambisonicsDecodeParams , & state .reflectionBuffer , & tmp2 );
1375+ iplAmbisonicsDecodeEffectApply (state .ambisonicsDecodeEffect , & ambisonicsDecodeParams , & state .reflectionBuffer , & stereo );
13291376 } else if (iplAmbisonicsDecodeEffectGetTailSize (state .ambisonicsDecodeEffect ) > 0 ) {
1330- iplAmbisonicsDecodeEffectGetTail (state .ambisonicsDecodeEffect , & tmp2 );
1377+ iplAmbisonicsDecodeEffectGetTail (state .ambisonicsDecodeEffect , & stereo );
13311378 } else {
13321379 return ;
13331380 }
13341381
13351382 for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1336- dst [2 * i + 0 ] += tmp2 .data [0 ][i ];
1337- dst [2 * i + 1 ] += tmp2 .data [1 ][i ];
1383+ dst [2 * i + 0 ] += stereo .data [0 ][i ];
1384+ dst [2 * i + 1 ] += stereo .data [1 ][i ];
13381385 }
13391386 } else if (anyReverb ) {
13401387 for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
@@ -1364,7 +1411,7 @@ static bool phonon_source_init(Source* source) {
13641411 vec3_set (source -> inputs .reverbScale , 1.f , 1.f , 1.f );
13651412
13661413 IPLDirectEffectSettings directEffectSettings = {
1367- .numChannels = 1
1414+ .numChannels = lovrSoundGetChannelCount ( source -> sound )
13681415 };
13691416
13701417 if (iplDirectEffectCreate (state .phonon , & state .audioSettings , & directEffectSettings , & source -> directEffect )) {
@@ -1569,8 +1616,8 @@ static void phonon_destroy(void) {}
15691616static void phonon_update (float dt ) {}
15701617static bool phonon_set_hrtf (Blob * blob ) { return true; }
15711618static void phonon_mix_begin (void ) {}
1572- static bool phonon_mix_source (Source * source , float * src , float * dst , float * tmp ) {}
1573- static void phonon_mix_tail (float * output , float * temp ) {}
1619+ static bool phonon_mix_source (Source * source , float * src , float * dst ) { return false; }
1620+ static void phonon_mix_reverb (float * dst ) {}
15741621static bool phonon_source_init (Source * source ) { return true; }
15751622static void phonon_source_destroy (Source * source ) {}
15761623static void phonon_source_add (Source * source ) {}
0 commit comments