Volume control

This commit is contained in:
Charlie 2024-07-16 13:31:33 +02:00
parent c6bf5bc8bb
commit fa36beaef3
5 changed files with 88 additions and 32 deletions

View file

@ -52,6 +52,7 @@ Other than examples, a great way to learn is to delve into the code of whatever
## Known bugs ## Known bugs
- Window positioning & sizing is fucky wucky - Window positioning & sizing is fucky wucky
- Converting 24-bit audio files doesn't really work
## Licensing ## Licensing
By default, the repository has an educational license that makes the engine free to use for personal projects. By default, the repository has an educational license that makes the engine free to use for personal projects.

11
TODO
View file

@ -1,17 +1,16 @@
- Audio - Audio
- Player volume control
- Optimize
- Spam simd
- Concurrent jobs for players?
- Mega buffer for contiguous intermediate buffers
We definitely also want a limit to how much memory we want allocated to intermediate buffers.
- Avoid playing identical audio at the same time - Avoid playing identical audio at the same time
- Custom audio mixing - Custom audio mixing
- Fix vorbis >FILE< streaming (#StbVorbisFileStream) - Fix vorbis >FILE< streaming (#StbVorbisFileStream)
- Handle audio sources which had their format defaulted to update when default device changes - Handle audio sources which had their format defaulted to update when default device changes
For streamed audio sources this should be easy enough, because the conversion happens from raw format to source format as we stream it. For streamed audio sources this should be easy enough, because the conversion happens from raw format to source format as we stream it.
For loaded sources though, we would need to convert the source->pcm_frames. For loaded sources though, we would need to convert the source->pcm_frames.
- Optimize
- Spam simd
- Concurrent jobs for players?
- Mega buffer for contiguous intermediate buffers
We definitely also want a limit to how much memory we want allocated to intermediate buffers.
- Bugs: - Bugs:
- Small fadeout on pause is slightly noisy - Small fadeout on pause is slightly noisy
- Setting time stamp/progression causes noise (need fade transition like on pause/play) - Setting time stamp/progression causes noise (need fade transition like on pause/play)

View file

@ -6,6 +6,7 @@
- Added position overloads for play_one_clip - Added position overloads for play_one_clip
play_one_audio_clip_source_at_position(source, pos) play_one_audio_clip_source_at_position(source, pos)
play_one_audio_clip_at_position(path, pos) play_one_audio_clip_at_position(path, pos)
- Implemented volume control with player->volume
- Renderer - Renderer
- Added draw_line(p0, p1, width, color) - Added draw_line(p0, p1, width, color)
- Implemented culling of quads out of view - Implemented culling of quads out of view

View file

@ -390,8 +390,8 @@ wav_read_frames(Wav_Stream *wav, Audio_Format format, void *frames,
return 0; return 0;
} }
bool raw_is_float = wav->format == 0x0003; bool raw_is_float32 = wav->format == 0x0003;
bool raw_is_f32 = raw_is_float && wav->valid_bits_per_sample == 32; bool raw_is_f32 = raw_is_float32 && wav->valid_bits_per_sample == 32;
bool raw_is_int = wav->format == 0x0001; bool raw_is_int = wav->format == 0x0001;
bool raw_is_s16 = raw_is_int && wav->valid_bits_per_sample == 16; bool raw_is_s16 = raw_is_int && wav->valid_bits_per_sample == 16;
@ -981,8 +981,8 @@ resample_frames(void *dst, Audio_Format dst_format,
void *dst_comp = (u8*)dst_frame + c * dst_comp_size; void *dst_comp = (u8*)dst_frame + c * dst_comp_size;
if (src_format.bit_width == AUDIO_BITS_32) { if (src_format.bit_width == AUDIO_BITS_32) {
float sample_1 = *((f32*)src_comp_1); float32 sample_1 = *((f32*)src_comp_1);
float sample_2 = *((f32*)src_comp_2); float32 sample_2 = *((f32*)src_comp_2);
f32 s = sample_1 + lerp_factor * (sample_2 - sample_1); f32 s = sample_1 + lerp_factor * (sample_2 - sample_1);
memcpy(dst_comp, &s, sizeof(f32)); memcpy(dst_comp, &s, sizeof(f32));
} else if (src_format.bit_width == AUDIO_BITS_16) { } else if (src_format.bit_width == AUDIO_BITS_16) {
@ -1164,6 +1164,7 @@ typedef struct Audio_Player {
// These can be set safely // These can be set safely
Vector3 position; // ndc space -1 to 1 Vector3 position; // ndc space -1 to 1
bool disable_spacialization; bool disable_spacialization;
float32 volume;
} Audio_Player; } Audio_Player;
#define AUDIO_PLAYERS_PER_BLOCK 128 #define AUDIO_PLAYERS_PER_BLOCK 128
@ -1188,6 +1189,7 @@ audio_player_get_one() {
memset(&block->players[i], 0, sizeof(block->players[i])); memset(&block->players[i], 0, sizeof(block->players[i]));
block->players[i].allocated = true; block->players[i].allocated = true;
block->players[i].volume = 1.0;
return &block->players[i]; return &block->players[i];
} }
@ -1208,6 +1210,7 @@ audio_player_get_one() {
last->next = new_block; last->next = new_block;
new_block->players[0].allocated = true; new_block->players[0].allocated = true;
new_block->players[0].volume = 1.0;
return &new_block->players[0]; return &new_block->players[0];
} }
@ -1424,22 +1427,22 @@ audio_apply_fade_out(void *frames, u64 number_of_frames, Audio_Format format,
} }
} }
void spacialize_audio_mono(void* frames, Audio_Format format, u64 number_of_frames, Vector3 pos) { void apply_audio_spacialization_mono(void* frames, Audio_Format format, u64 number_of_frames, Vector3 pos) {
// No idea if this actually gives the perception of audio being positioned. // No idea if this actually gives the perception of audio being positioned.
// I also don't have a mono audio device to test it. // I also don't have a mono audio device to test it.
float* audio_data = (float*)frames; float* audio_data = (float*)frames;
float distance = sqrtf(pos.x * pos.x + pos.y * pos.y + pos.z * pos.z); float32 distance = sqrtf(pos.x * pos.x + pos.y * pos.y + pos.z * pos.z);
float attenuation = 1.0f / (1.0f + distance); float32 attenuation = 1.0f / (1.0f + distance);
float alpha = 0.1f; float32 alpha = 0.1f;
float prev_sample = 0.0f; float32 prev_sample = 0.0f;
u64 comp_size = get_audio_bit_width_byte_size(format.bit_width); u64 comp_size = get_audio_bit_width_byte_size(format.bit_width);
u64 frame_size = comp_size * format.channels; u64 frame_size = comp_size * format.channels;
for (u64 i = 0; i < number_of_frames; ++i) { for (u64 i = 0; i < number_of_frames; ++i) {
float sample = audio_data[i]; float32 sample = audio_data[i];
convert_one_component( convert_one_component(
&sample, &sample,
AUDIO_BITS_32, AUDIO_BITS_32,
@ -1460,43 +1463,43 @@ void spacialize_audio_mono(void* frames, Audio_Format format, u64 number_of_fram
); );
} }
} }
void spacialize_audio(void* frames, Audio_Format format, u64 number_of_frames, Vector3 pos) { void apply_audio_spacialization(void* frames, Audio_Format format, u64 number_of_frames, Vector3 pos) {
if (format.channels == 1) { if (format.channels == 1) {
spacialize_audio_mono(frames, format, number_of_frames, pos); apply_audio_spacialization_mono(frames, format, number_of_frames, pos);
} }
float distance = sqrtf(pos.x * pos.x + pos.y * pos.y + pos.z * pos.z); float32 distance = sqrtf(pos.x * pos.x + pos.y * pos.y + pos.z * pos.z);
float attenuation = 1.0f / (1.0f + distance); float32 attenuation = 1.0f / (1.0f + distance);
float left_right_pan = (pos.x + 1.0f) * 0.5f; float32 left_right_pan = (pos.x + 1.0f) * 0.5f;
float up_down_pan = (pos.y + 1.0f) * 0.5f; float32 up_down_pan = (pos.y + 1.0f) * 0.5f;
float front_back_pan = (pos.z + 1.0f) * 0.5f; float32 front_back_pan = (pos.z + 1.0f) * 0.5f;
u64 comp_size = get_audio_bit_width_byte_size(format.bit_width); u64 comp_size = get_audio_bit_width_byte_size(format.bit_width);
u64 frame_size = comp_size * format.channels; u64 frame_size = comp_size * format.channels;
float high_pass_coeff = 0.8f + 0.2f * up_down_pan; float32 high_pass_coeff = 0.8f + 0.2f * up_down_pan;
float low_pass_coeff = 1.0f - high_pass_coeff; float32 low_pass_coeff = 1.0f - high_pass_coeff;
// Apply gains to each frame // Apply gains to each frame
for (u64 i = 0; i < number_of_frames; ++i) { for (u64 i = 0; i < number_of_frames; ++i) {
for (u64 c = 0; c < format.channels; ++c) { for (u64 c = 0; c < format.channels; ++c) {
// Convert whatever to float -1 to 1 // Convert whatever to float32 -1 to 1
float sample; float32 sample;
convert_one_component( convert_one_component(
&sample, &sample,
AUDIO_BITS_32, AUDIO_BITS_32,
(u8*)frames+i*frame_size+c*comp_size, (u8*)frames+i*frame_size+c*comp_size,
format.bit_width format.bit_width
); );
float gain = 1.0f / format.channels; float32 gain = 1.0f / format.channels;
if (format.channels == 2) { if (format.channels == 2) {
// time delay and phase shift for vertical position // time delay and phase shift for vertical position
float time_delay = (up_down_pan - 0.5f) * 0.0005f; // 0.5ms delay range float32 time_delay = (up_down_pan - 0.5f) * 0.0005f; // 0.5ms delay range
float phase_shift = (up_down_pan - 0.5f) * 0.5f; // 0.5 radians phase shift range float32 phase_shift = (up_down_pan - 0.5f) * 0.5f; // 0.5 radians phase shift range
// Stereo // Stereo
if (c == 0) { if (c == 0) {
@ -1549,6 +1552,39 @@ void spacialize_audio(void* frames, Audio_Format format, u64 number_of_frames, V
} }
} }
void apply_audio_volume(void* frames, Audio_Format format, u64 number_of_frames, float32 vol) {
// #Speed
// This is lazy, also it can be combined with other passes.
u64 comp_size = get_audio_bit_width_byte_size(format.bit_width);
u64 frame_size = comp_size * format.channels;
if (vol <= 0.0) {
memset(frames, 0, frame_size*number_of_frames);
}
for (u64 i = 0; i < number_of_frames; ++i) {
for (u64 c = 0; c < format.channels; ++c) {
float32 sample;
convert_one_component(
&sample,
AUDIO_BITS_32,
(u8*)frames+i*frame_size+c*comp_size,
format.bit_width
);
sample *= vol;
convert_one_component(
(u8*)frames+i*frame_size+c*comp_size,
format.bit_width,
&sample,
AUDIO_BITS_32
);
}
}
}
// This is supposed to be called by OS layer audio thread whenever it wants more audio samples // This is supposed to be called by OS layer audio thread whenever it wants more audio samples
void void
do_program_audio_sample(u64 number_of_output_frames, Audio_Format out_format, do_program_audio_sample(u64 number_of_output_frames, Audio_Format out_format,
@ -1725,7 +1761,10 @@ do_program_audio_sample(u64 number_of_output_frames, Audio_Format out_format,
} }
if (!p->disable_spacialization) { if (!p->disable_spacialization) {
spacialize_audio(mix_buffer, out_format, number_of_output_frames, p->position); apply_audio_spacialization(mix_buffer, out_format, number_of_output_frames, p->position);
}
if (p->volume != 0.0) {
apply_audio_volume(mix_buffer, out_format, number_of_output_frames, p->volume);
} }
mix_frames(output, mix_buffer, number_of_output_frames, out_format); mix_frames(output, mix_buffer, number_of_output_frames, out_format);

View file

@ -71,7 +71,7 @@ int entry(int argc, char **argv) {
Vector4 rect; Vector4 rect;
rect.x = -window.width/2+40; rect.x = -window.width/2+40;
rect.y = window.height/2-FONT_HEIGHT-40; rect.y = window.height/2-FONT_HEIGHT-40;
rect.z = FONT_HEIGHT*5; rect.z = FONT_HEIGHT*8;
rect.w = FONT_HEIGHT*1.5; rect.w = FONT_HEIGHT*1.5;
bool clip_playing = clip_player->state == AUDIO_PLAYER_STATE_PLAYING; bool clip_playing = clip_player->state == AUDIO_PLAYER_STATE_PLAYING;
@ -96,7 +96,23 @@ int entry(int argc, char **argv) {
audio_player_set_progression_factor(song_player, 0); audio_player_set_progression_factor(song_player, 0);
} }
rect.y = window.height/2-FONT_HEIGHT-40;
rect.x += rect.z + FONT_HEIGHT;
if (button(STR("Song vol up"), rect.xy, rect.zw, false)) {
song_player->volume += 0.05;
}
rect.y -= FONT_HEIGHT*1.8;
if (button(STR("Song vol down"), rect.xy, rect.zw, false)) {
song_player->volume -= 0.05;
}
song_player->volume = clamp(song_player->volume, 0, 5);
rect.x += rect.z + FONT_HEIGHT;
draw_text(font, tprint("Song volume: %d%%", (s64)round(song_player->volume*100)), FONT_HEIGHT, v2_sub(rect.xy, v2(2, -2)), v2(1, 1), COLOR_BLACK);
draw_text(font, tprint("Song volume: %d%%", (s64)round(song_player->volume*100)), FONT_HEIGHT, rect.xy, v2(1, 1), COLOR_WHITE);
rect.y -= FONT_HEIGHT*3; rect.y -= FONT_HEIGHT*3;
draw_text(font, STR("Right-click for thing"), FONT_HEIGHT, v2_sub(rect.xy, v2(2, -2)), v2(1, 1), COLOR_BLACK); draw_text(font, STR("Right-click for thing"), FONT_HEIGHT, v2_sub(rect.xy, v2(2, -2)), v2(1, 1), COLOR_BLACK);
draw_text(font, STR("Right-click for thing"), FONT_HEIGHT, rect.xy, v2(1, 1), COLOR_WHITE); draw_text(font, STR("Right-click for thing"), FONT_HEIGHT, rect.xy, v2(1, 1), COLOR_WHITE);