Hi Simon!
Thanks for your observations. I suppose I really have to read more digital signal processing... :P
As for the code problems, I implemented your suggestions (that made it more correct), but there were 2 other big problems that ended up causing the artifact.
The first one was that the volume was only being update once per audio tick. Since it may be varying (towards a target_volume), I really should update that
every sample! That improved the artifacts happening on volume change (I was testing both the volume and pitch at once... :P so the overall sound improved).
The other major thing was the first line you pointed out. Although you are probably correct and I will make it so non-looping sounds just get the last sample again for the next sample, the if (and the assignment) were just wrong. The number of samples doesn't consider the channel count, but the next_sample did! So if I have a 44100-sample sound with two channels, I must fetch all the way to sample_count*sample_count.
This is the improved code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 | s16 *at = sound_buffer->samples;
for (int i = 0; i < sound_buffer->samples_to_write; i++) {
f32 left_sample = 0;
f32 right_sample = 0;
for (Playing_Sound *sound = playing_sounds; sound != playing_sounds + array_count(playing_sounds); sound++) {
if (!sound->active) continue;
sound->volume = move_towards(sound->volume, sound->target_volume, sound->fading_speed/sound_buffer->samples_per_second);
if (sound->volume) {
int sample = (int)sound->position;
f32 frac = sound->position - (f32)sample;
sample *= sound->sound->channel_count;
int next_sample = sample + sound->sound->channel_count-1 + 1;
if (next_sample >= sound->sound->sample_count*sound->sound->channel_count)
next_sample-= sound->sound->sample_count*sound->sound->channel_count;
f32 left_sound_sample_1 = (f32)sound->sound->samples[sample];
f32 left_sound_sample_2 = (f32)sound->sound->samples[next_sample];
f32 right_sound_sample_1 = (f32)sound->sound->samples[sample + sound->sound->channel_count-1];
f32 right_sound_sample_2 = (f32)sound->sound->samples[next_sample + sound->sound->channel_count-1];
f32 left_sound_sample = lerp(left_sound_sample_1, frac, left_sound_sample_2)*sound->volume;
f32 right_sound_sample = lerp(right_sound_sample_1, frac, right_sound_sample_2)*sound->volume;
left_sample += (left_sound_sample*clampf(0, (1.f-sound->pan), 1.f) +
right_sound_sample*clampf(0, (-sound->pan), 1.f));
right_sample += (right_sound_sample*clampf(0, (1.f+sound->pan), 1.f) +
left_sound_sample*clampf(0, (sound->pan), 1.f));
}
sound->position += sound->speed_multiplier;
if (sound->position >= sound->sound->sample_count) {
if (sound->looping) sound->position -= (f32)sound->sound->sample_count;
else stop_sound(sound);
}
}
f32 min = (f32)MIN_S16;
f32 max = (f32)MAX_S16;
*at++ = (s16)clampf(min, left_sample*.5f, max);
*at++ = (s16)clampf(min, right_sample*.5f, max);
}
|
There are some things to make it better still (like changing the loops so that I iterated the samples inside the sound and not the way around to make it more cache friendly). But that'll do for now! :D
The whole code is, in fact, open sourced on
itch.io! :D
https://danzaidan.itch.io/break-arcade-games-out
(And the whole development archived!
https://www.youtube.com/DanZaidan )
Thanks so much for taking the time to help me out!
Cheers!