Post

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)
Esta postagem está licenciada sob CC BY 4.0 pelo autor.