You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix.
428
-
AudioStreamPlaybackBusDetails bus_details = *ptr;
433
+
// Get the bus details for this playback. This contains information about which buses the playback is assigned to and the volume of the playback on each bus.
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
432
441
if (!bus_details.bus_active[idx]) {
433
442
continue;
434
443
}
444
+
// This is the AudioServer-internal index of the bus we're mixing to in this step of the loop. Not to be confused with `idx` which is an index into `AudioStreamPlaybackBusDetails` member var arrays.
435
445
int bus_idx = thread_find_bus_index(bus_details.bus[idx]);
436
446
447
+
// It's important to know whether or not this bus was active in the previous mix step of this stream. If it was, we need to perform volume interpolation to avoid pops.
437
448
int prev_bus_idx = -1;
438
449
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
439
450
if (!playback->prev_bus_details->bus_active[search_idx]) {
440
451
continue;
441
452
}
453
+
// If the StringNames of the buses match, we've found the previous bus index. This indicates that this playback mixed to `prev_bus_details->bus[prev_bus_index]` in the previous mix step, which gives us a way to look up the playback's previous volume.
442
454
if (playback->prev_bus_details->bus[search_idx].hash() == bus_details.bus[idx].hash()) {
443
455
prev_bus_idx = search_idx;
456
+
break;
444
457
}
445
458
}
446
459
460
+
// It's now time to mix to the bus. We do this by going through each channel of the bus and mixing to it.
461
+
// The channels correspond to output channels of the audio device, e.g. stereo or 5.1. To reduce needless nesting, this is done with a helper method named `_mix_step_for_channel`.
447
462
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
// If this bus was not active in the previous mix step, we want to start playback at the full volume to avoid crushing transients.
471
+
AudioFrame prev_channel_vol = channel_vol;
472
+
// If this bus was active in the previous mix step, we need to interpolate between the previous volume and the current volume to avoid pops. Set `prev_channel_volume` accordingly.
// TODO: In the future it could be nice to replace all of these hardcoded effects with something a bit cleaner and more flexible, but for now this is what we do to support 3D audio players.
for (unsignedint frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
668
-
// Make this buffer sizeinvariant if buffer_size ever becomes a project setting.
684
+
//TODO: Make lerp speed buffer-size-invariant if buffer_size ever becomes a project setting to avoid very small buffer sizes causing pops due to too-fast lerps.
for (unsignedint frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
679
-
// Make this buffer sizeinvariant if buffer_size ever becomes a project setting.
695
+
//TODO: Make lerp speed buffer-size-invariant if buffer_size ever becomes a project setting to avoid very small buffer sizes causing pops due to too-fast lerps.
// TODO: Buffer size is hardcoded for now. This would be really nice to have as a project setting because currently it limits audio latency to an absolute minimum of 11ms with default mix rate, but there's some additional work required to make that happen. See TODOs in `_mix_step_for_channel`.
1488
+
// When this becomes a project setting, it should be specified in milliseconds rather than raw sample count, because 512 samples at 192khz is shorter than it is at 48khz, for example.
Copy file name to clipboardexpand all lines: servers/audio_server.h
+8
Original file line number
Diff line number
Diff line change
@@ -270,6 +270,14 @@ class AudioServer : public Object {
270
270
};
271
271
272
272
structAudioStreamPlaybackListNode {
273
+
// The state machine for audio stream playbacks is as follows:
274
+
// 1. The playback is created and added to the playback list in the playing state.
275
+
// 2. The playback is (maybe) paused, and the state is set to FADE_OUT_TO_PAUSE.
276
+
// 2.1. The playback is mixed after being paused, and the audio server thread atomically sets the state to PAUSED after performing a brief fade-out.
277
+
// 3. The playback is (maybe) deleted, and the state is set to FADE_OUT_TO_DELETION.
278
+
// 3.1. The playback is mixed after being deleted, and the audio server thread atomically sets the state to AWAITING_DELETION after performing a brief fade-out.
279
+
// NOTE: The playback is not deallocated at this time because allocation and deallocation are not realtime-safe.
280
+
// 4. The playback is removed and deallocated on the main thread using the SafeList maybe_cleanup method.
273
281
enum PlaybackState {
274
282
PAUSED = 0, // Paused. Keep this stream playback around though so it can be restarted.
275
283
PLAYING = 1, // Playing. Fading may still be necessary if volume changes!
0 commit comments