Tocando o Som de Uma Onda Senoidal
Preenchendo o Som
Para construir uma saída senoidal para cada sample, precisamos calcular com base no seno.
Como o seno é um valor indo de -1 à 1, precisamos mapear um ciclo completo (periodo). Esse ciclo é dado pela operação 2 * PI * (f32) sample_index / (f32) wave_period
.
1
2
3
s16 t = 2.0f * PI * (f32) sample_index / (f32) wave_c_note_period;
f32 sine_value = sinf(t);
s16 value = sine_value * volume;
No exemplo acima t
é número entre -1 a 1 para cada sample que corresponde a nota C (Dó).
Com isso, podemos normaliza com a amplitude da onda (volume) para produzir o sinal senoidal.
Normalizando o sinal
Para não ter uma alteração repentia no sinal ao mudar a frequência (skip), vamos mudar a fórmula para calcular o t_sine.
1
2
3
4
5
for (DWORD i = 0; i < sample_count_1; ++i) {
f32 sine_value = sinf(t_sine);
t_sine += 2.0f * PI * (f32) 1.0f / (f32) wave_c_note_period;
// ...
}
Latência
O Buffer é responsável por calcular um segundo por completo.
Ao iniciar o programa, vamos preencher algumas porções do buffer como 1/15 de segundo para evitar uma latência muito distante.
Quando calculamos o Lock, usamos o play_cursor
para ter a origem do offset e os bytes para serem escritos.
Agora, para adicionar uma latência, vamos considerar que o play_cursor
esteja um pouco à frente conforma a latência pré-definida.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
f32 latency_sample_count = samples_per_second / 15;
// ....
DWORD target_cursor = (DWORD)(play_cursor + (latency_sample_count * bytes_per_sample)) % buffer_size;
DWORD bytes_to_write = 0;
if (offset_in_bytes == target_cursor) {
if (!sound_playing) {
bytes_to_write = buffer_size;
}
} else if (offset_in_bytes > target_cursor) {
bytes_to_write = buffer_size - offset_in_bytes;
bytes_to_write += target_cursor;
} else {
bytes_to_write = target_cursor - offset_in_bytes;
}
Refatoração
Vamos extrair as partes de lock e unlock para preenchimento e construção dos samples em uma função. Dessa forma, podemos iniciar o programa com a quantidade inicial de bytes já preenchidos no buffer de som.
1
2
3
4
5
6
7
8
9
10
11
struct Win32_Sound
{
s32 tone_hz;
s32 volume;
s32 samples_per_second;
s32 wave_period;
s32 bytes_per_sample;
s32 sample_index;
};
win32_fill_sound_buffer(Win32_Sound *sound, DWORD offset_in_bytes, DWORD bytes_to_write)