From 91382aff69d2c349038fe6bcad11840b3edca335 Mon Sep 17 00:00:00 2001 From: Richard Swingwood Date: Tue, 23 Sep 2025 14:33:18 +0100 Subject: [PATCH 1/6] [AUDIO] Support playing of startup sound from WAV file or hardcoded 'jingle' --- kernel/audio/OutputAudioDevice.cpp | 2 +- kernel/audio/audio.cpp | 124 ++++++++++++++++++++--------- kernel/audio/virtio_audio_pci.cpp | 16 ++-- shared/audio/cuatro.c | 14 ++-- shared/audio/cuatro.h | 13 ++- shared/types.h | 2 + 6 files changed, 116 insertions(+), 55 deletions(-) diff --git a/kernel/audio/OutputAudioDevice.cpp b/kernel/audio/OutputAudioDevice.cpp index 99dca660..77e18f47 100644 --- a/kernel/audio/OutputAudioDevice.cpp +++ b/kernel/audio/OutputAudioDevice.cpp @@ -2,7 +2,7 @@ #include "memory/page_allocator.h" //TODO: We should allocate at least 40 pages, possibly 64 to have more than a second at once -#define BUF_SIZE PAGE_SIZE * 10 +#define BUF_SIZE PAGE_SIZE * 20 void OutputAudioDevice::populate(){ buffer = (uintptr_t)palloc(BUF_SIZE, MEM_PRIV_KERNEL, MEM_RW, true); diff --git a/kernel/audio/audio.cpp b/kernel/audio/audio.cpp index 8f87aece..25cc6045 100644 --- a/kernel/audio/audio.cpp +++ b/kernel/audio/audio.cpp @@ -4,8 +4,9 @@ #include "console/kio.h" #include "math/math.h" #include "audio/cuatro.h" -#include "graph/graphics.h" +#include "audio/wav.h" #include "theme/theme.h" +#include "syscalls/syscalls.h" VirtioAudioDriver *audio_driver; @@ -26,53 +27,100 @@ void audio_submit_buffer(){ audio_driver->out_dev->submit_buffer(audio_driver); } -void make_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ - gpu_clear(0x1fb03f); - uint32_t period = 441/((freq/100.f) * 2);//TODO: improve this formula - kprintf("Period %i",period); - uint32_t accumulator = 0; - uint32_t increment = (uint32_t)(freq * (float)UINT32_MAX / 44100.0); - gpu_size size = gpu_get_screen_size(); - uint32_t previous_pixel = UINT32_MAX; - - //TODO: distorsion - //size of the buffer should be bigger, - //palloc should be 64 pages - //in the virtio driver, cmd_index is waiting for the device to catch up - - for (int i = 0; i < seconds * 100; i++){ +// TODO: get sample rate, channels etc. from audio driver. +// Currently ASSUMES output stream has two channels in U32 format. +const float SAMPLE_RATE = 44100.f; + + +void play_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ + uint32_t phase = 0; + // TODO: validate freqency to ensure phase_incr is sensible value. + uint32_t phase_incr = (uint32_t)(freq * (float)PHASE_MAX / SAMPLE_RATE); + uint32_t samples_remaining = ceil(SAMPLE_RATE * seconds); + + while (samples_remaining > 0){ + sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); + uint32_t* buffer = (uint32_t*)buf.ptr; + uint32_t samples_in_buffer = (uint32_t)min(buf.size/2, samples_remaining); + uint32_t sample = 0; + size_t slot = 0; + while (sample++ < samples_in_buffer){ + float wave = sample_raw_wave(type, phase) * amplitude; + buffer[slot++] = wave; // Left ch. + buffer[slot++] = wave; // Right ch. + phase = (phase + phase_incr) & PHASE_MASK; + } + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; + } + audio_submit_buffer(); + samples_remaining -= samples_in_buffer; + } +} + +void play_silence(float seconds){ + if (seconds < 0.01) return; + sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); + uint32_t buffer_count = (uint32_t)ceil(SAMPLE_RATE * seconds * 2 / buf.size); + do{ + uint32_t* buffer = (uint32_t*)buf.ptr; + size_t slot = 0; + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; + } + audio_submit_buffer(); + buf = audio_request_buffer(audio_driver->out_dev->stream_id); + } while (--buffer_count > 0); +} + +static inline uint32_t int16_to_uint32(int16_t sample, uint32_t amplitude){ + return (uint64_t)(((int64_t)sample * amplitude) >> 16) + WAVE_MID_VALUE; +} + +void play_int16_samples(int16_t *samples, size_t smpl_per_channel, size_t channels, uint32_t amplitude){ + size_t samples_remaining = smpl_per_channel * channels; + while (samples_remaining > 0){ sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); - - uint32_t num_samples = buf.size; - uint32_t *buffer = (uint32_t*)buf.ptr; - for (uint32_t sample = 0; sample < num_samples; sample++){ - float wave = sample_raw_wave(type, accumulator, period); - uint32_t min = 64 * num_samples; - buffer[sample] = wave * amplitude; - - accumulator += increment; - if (accumulator >= min && accumulator < min + size.width){ - gpu_point p = (gpu_point){ accumulator - min, 100-(uint32_t)(100*wave)}; - if (previous_pixel != UINT32_MAX && abs(p.y-previous_pixel) > 10){ - gpu_draw_line({ p.x - 1, previous_pixel}, p, 0xFFB4DD13); - } - previous_pixel = p.y; - gpu_draw_pixel(p, 0xFFB4DD13); - } + uint32_t* buffer = (uint32_t*)buf.ptr; + size_t samples_in_buffer = (size_t)min(buf.size, samples_remaining); + size_t slot = 0; + while (slot < samples_in_buffer){ + uint32_t sample = int16_to_uint32(*samples++, amplitude); + buffer[slot++] = sample; // Left ch. + buffer[slot++] = (channels == 1) ? sample : int16_to_uint32(*samples++, amplitude); // Right ch. + } + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; } audio_submit_buffer(); + samples_remaining -= samples_in_buffer; + } +} + +void play_startup(){ + wav_data wav = {}; + if (wav_load("/boot/redos/startup.wav", &wav)){ + // TODO: other WAV formats are available + play_int16_samples(wav.samples, wav.smpls_per_channel, wav.channels, AUDIO_LEVEL_MAX/2); + free((void*)wav.file_content.ptr, wav.file_content.size); + }else{ + play_wave(WAVE_SAW, 440, 0.1, AUDIO_LEVEL_MAX/2); + play_silence(0.05); + play_wave(WAVE_SAW, 494, 0.1, AUDIO_LEVEL_MAX/2); + play_silence(0.05); + play_wave(WAVE_SAW, 523, 0.3, AUDIO_LEVEL_MAX/2); + play_silence(0.5); } - gpu_flush(); - while(1); } -int play_test_audio(int argc, char* argv[]){ - // make_wave(WAVE_SAW, 440, 3, UINT32_MAX/4); +int audio_mixer(int argc, char* argv[]){ + play_startup(); + while (1) sleep(1000); // Idle sound process for time(!) being return 0; } process_t* init_audio_mixer(){ - return create_kernel_process("audiotest", play_test_audio, 0, 0); + return create_kernel_process("Audio out", audio_mixer, 0, 0); } driver_module audio_module = (driver_module){ diff --git a/kernel/audio/virtio_audio_pci.cpp b/kernel/audio/virtio_audio_pci.cpp index 8e97d627..f8014707 100644 --- a/kernel/audio/virtio_audio_pci.cpp +++ b/kernel/audio/virtio_audio_pci.cpp @@ -3,6 +3,7 @@ #include "console/kio.h" #include "memory/page_allocator.h" #include "std/memory_access.h" +#include "syscalls/syscalls.h" #include "audio.h" #include "std/memory.h" #include "OutputAudioDevice.hpp" @@ -26,7 +27,8 @@ #define VIRTIO_SND_PCM_RATE_44100 6 #define VIRTIO_SND_PCM_RATE_48000 7 -#define SND_44100_BUFFER_SIZE 441 +#define SND_44100_BUFFER_SIZE 256 +static_assert((SND_44100_BUFFER_SIZE & 0x01) == 0x00, "Audio buffer size must be even."); #define SND_U32_BYTES 4 #define SND_PERIOD 1 #define TOTAL_PERIOD_SIZE SND_44100_BUFFER_SIZE * SND_U32_BYTES * channels @@ -83,7 +85,7 @@ typedef struct virtio_snd_event { bool VirtioAudioDriver::init(){ uint64_t addr = find_pci_device(VIRTIO_VENDOR, VIRTIO_AUDIO_ID); if (!addr){ - kprintf("Disk device not found"); + kprintf("Audio device not found"); return false; } @@ -107,7 +109,7 @@ bool VirtioAudioDriver::init(){ pci_enable_device(addr); if (!virtio_init_device(&audio_dev)){ - kprintf("Failed disk initialization"); + kprintf("[VIRTIO_AUDIO] Failed initialization"); return false; } @@ -115,7 +117,7 @@ bool VirtioAudioDriver::init(){ audio_dev.common_cfg->queue_msix_vector = 0; if (audio_dev.common_cfg->queue_msix_vector != 0){ - kprintf("[VIRTIO_AUDIO error] failed to setup interrupts for event queue"); + kprintf("[VIRTIO_AUDIO] failed to setup interrupts for event queue"); return false; } //TODO: This should (probably) be for input devices only @@ -181,7 +183,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ kprintf("[VIRTIO_AUDIO] Stream %i (%s): Features %x. Format %x. Sample %x. Channels %i-%i",stream, (uintptr_t)(stream_info[stream].direction ? "IN" : "OUT"), stream_info[stream].features, format, rate, stream_info->channels_min, stream_info->channels_max); if (!(format & (1 << VIRTIO_SND_PCM_FMT_U32))){ - kprintf("[VIRTIO_AUDIO implementation error] stream does not support Float32 format"); + kprintf("[VIRTIO_AUDIO implementation error] stream does not support Uint32 format"); return false; } @@ -191,7 +193,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ } //TODO: Stereo - uint8_t channels = 1;//stream_info->channels_max; + uint8_t channels = 2; // 1;//stream_info->channels_max; if (!stream_set_params(stream, stream_info[stream].features, VIRTIO_SND_PCM_FMT_U32, VIRTIO_SND_PCM_RATE_44100, channels)){ kprintf("[VIRTIO_AUDIO error] Failed to configure stream %i",stream); @@ -215,6 +217,7 @@ void VirtioAudioDriver::send_buffer(sizedptr buf){ virtio_add_buffer(&audio_dev, cmd_index % audio_dev.common_cfg->queue_size, buf.ptr, buf.size, true); struct virtq_used* u = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; if (u->idx < cmd_index-20){ +//sleep(1); while (u->idx < cmd_index-5); } cmd_index++; @@ -286,7 +289,6 @@ void VirtioAudioDriver::handle_interrupt(){ select_queue(&audio_dev, EVENT_QUEUE); struct virtq_used* used = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; struct virtq_avail* avail = (struct virtq_avail*)(uintptr_t)audio_dev.common_cfg->queue_driver; - uint16_t new_idx = used->idx; if (new_idx != last_used_idx) { uint16_t used_ring_index = last_used_idx % 128; diff --git a/shared/audio/cuatro.c b/shared/audio/cuatro.c index 977f9632..3a3ac74f 100644 --- a/shared/audio/cuatro.c +++ b/shared/audio/cuatro.c @@ -1,21 +1,21 @@ #include "cuatro.h" #include "math/math.h" -float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period){ - float t = ((float)accumulator/(float)UINT32_MAX); +float sample_raw_wave(WAVE_TYPE type, uint32_t phase){ switch (type) { case WAVE_TRIG: { - float trig = 2*(absf(t-floor(t + 0.5))); + float t = ((float)phase/(float)PHASE_MAX); + float trig = 2*(absf(t-floor(t + 0.5f))); return trig; } case WAVE_SAW: - return ((t-floor(t + 0.5)) + 0.5f); + return (float)(PHASE_MAX - phase) / (float)PHASE_MAX; case WAVE_SQUARE: - return (accumulator < UINT32_MAX/2) ? 0 : 1; + return (phase < PHASE_MID) ? 0.f : 1.f; } return 0; } -uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude){ - return sample_raw_wave(type, accumulator, period) * amplitude; +uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude){ + return sample_raw_wave(type, phase) * amplitude; } \ No newline at end of file diff --git a/shared/audio/cuatro.h b/shared/audio/cuatro.h index 452c084d..c71c1667 100644 --- a/shared/audio/cuatro.h +++ b/shared/audio/cuatro.h @@ -8,11 +8,20 @@ typedef enum WAVE_TYPE { WAVE_SAW, } WAVE_TYPE; +#define PHASE_MASK 0x00FFFFFF +#define PHASE_MAX PHASE_MASK +#define PHASE_MID (PHASE_MAX >> 1) + +#define WAVE_MID_VALUE 0x80000000 + +#define AUDIO_LEVEL_MAX UINT32_MAX + + #ifdef __cplusplus extern "C" { #endif -float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period); -uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude); +float sample_raw_wave(WAVE_TYPE type, uint32_t phase); +uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude); // void make_wave(WAVE_TYPE type, float freq, float seconds); #ifdef __cplusplus } diff --git a/shared/types.h b/shared/types.h index 6019b68b..4007295e 100644 --- a/shared/types.h +++ b/shared/types.h @@ -61,6 +61,8 @@ typedef signed long intptr_t; typedef signed short int16_t; typedef signed char int8_t; +#define INT16_MAX 0x7FFF + typedef struct sizedptr { uintptr_t ptr; size_t size; From 8ad37e02f4f45ecbf612e552a727653d026157c3 Mon Sep 17 00:00:00 2001 From: Richard Swingwood Date: Tue, 23 Sep 2025 14:47:43 +0100 Subject: [PATCH 2/6] [AUDIO] Support playing of startup sound from WAV file or hardcoded 'jingle' --- kernel/audio/OutputAudioDevice.cpp | 2 +- kernel/audio/audio.cpp | 124 ++++++++++++++++++++--------- kernel/audio/virtio_audio_pci.cpp | 16 ++-- shared/audio/cuatro.c | 14 ++-- shared/audio/cuatro.h | 13 ++- shared/audio/wav.c | 79 ++++++++++++++++++ shared/audio/wav.h | 18 +++++ shared/types.h | 2 + 8 files changed, 213 insertions(+), 55 deletions(-) create mode 100644 shared/audio/wav.c create mode 100644 shared/audio/wav.h diff --git a/kernel/audio/OutputAudioDevice.cpp b/kernel/audio/OutputAudioDevice.cpp index 99dca660..77e18f47 100644 --- a/kernel/audio/OutputAudioDevice.cpp +++ b/kernel/audio/OutputAudioDevice.cpp @@ -2,7 +2,7 @@ #include "memory/page_allocator.h" //TODO: We should allocate at least 40 pages, possibly 64 to have more than a second at once -#define BUF_SIZE PAGE_SIZE * 10 +#define BUF_SIZE PAGE_SIZE * 20 void OutputAudioDevice::populate(){ buffer = (uintptr_t)palloc(BUF_SIZE, MEM_PRIV_KERNEL, MEM_RW, true); diff --git a/kernel/audio/audio.cpp b/kernel/audio/audio.cpp index 8f87aece..25cc6045 100644 --- a/kernel/audio/audio.cpp +++ b/kernel/audio/audio.cpp @@ -4,8 +4,9 @@ #include "console/kio.h" #include "math/math.h" #include "audio/cuatro.h" -#include "graph/graphics.h" +#include "audio/wav.h" #include "theme/theme.h" +#include "syscalls/syscalls.h" VirtioAudioDriver *audio_driver; @@ -26,53 +27,100 @@ void audio_submit_buffer(){ audio_driver->out_dev->submit_buffer(audio_driver); } -void make_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ - gpu_clear(0x1fb03f); - uint32_t period = 441/((freq/100.f) * 2);//TODO: improve this formula - kprintf("Period %i",period); - uint32_t accumulator = 0; - uint32_t increment = (uint32_t)(freq * (float)UINT32_MAX / 44100.0); - gpu_size size = gpu_get_screen_size(); - uint32_t previous_pixel = UINT32_MAX; - - //TODO: distorsion - //size of the buffer should be bigger, - //palloc should be 64 pages - //in the virtio driver, cmd_index is waiting for the device to catch up - - for (int i = 0; i < seconds * 100; i++){ +// TODO: get sample rate, channels etc. from audio driver. +// Currently ASSUMES output stream has two channels in U32 format. +const float SAMPLE_RATE = 44100.f; + + +void play_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ + uint32_t phase = 0; + // TODO: validate freqency to ensure phase_incr is sensible value. + uint32_t phase_incr = (uint32_t)(freq * (float)PHASE_MAX / SAMPLE_RATE); + uint32_t samples_remaining = ceil(SAMPLE_RATE * seconds); + + while (samples_remaining > 0){ + sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); + uint32_t* buffer = (uint32_t*)buf.ptr; + uint32_t samples_in_buffer = (uint32_t)min(buf.size/2, samples_remaining); + uint32_t sample = 0; + size_t slot = 0; + while (sample++ < samples_in_buffer){ + float wave = sample_raw_wave(type, phase) * amplitude; + buffer[slot++] = wave; // Left ch. + buffer[slot++] = wave; // Right ch. + phase = (phase + phase_incr) & PHASE_MASK; + } + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; + } + audio_submit_buffer(); + samples_remaining -= samples_in_buffer; + } +} + +void play_silence(float seconds){ + if (seconds < 0.01) return; + sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); + uint32_t buffer_count = (uint32_t)ceil(SAMPLE_RATE * seconds * 2 / buf.size); + do{ + uint32_t* buffer = (uint32_t*)buf.ptr; + size_t slot = 0; + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; + } + audio_submit_buffer(); + buf = audio_request_buffer(audio_driver->out_dev->stream_id); + } while (--buffer_count > 0); +} + +static inline uint32_t int16_to_uint32(int16_t sample, uint32_t amplitude){ + return (uint64_t)(((int64_t)sample * amplitude) >> 16) + WAVE_MID_VALUE; +} + +void play_int16_samples(int16_t *samples, size_t smpl_per_channel, size_t channels, uint32_t amplitude){ + size_t samples_remaining = smpl_per_channel * channels; + while (samples_remaining > 0){ sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); - - uint32_t num_samples = buf.size; - uint32_t *buffer = (uint32_t*)buf.ptr; - for (uint32_t sample = 0; sample < num_samples; sample++){ - float wave = sample_raw_wave(type, accumulator, period); - uint32_t min = 64 * num_samples; - buffer[sample] = wave * amplitude; - - accumulator += increment; - if (accumulator >= min && accumulator < min + size.width){ - gpu_point p = (gpu_point){ accumulator - min, 100-(uint32_t)(100*wave)}; - if (previous_pixel != UINT32_MAX && abs(p.y-previous_pixel) > 10){ - gpu_draw_line({ p.x - 1, previous_pixel}, p, 0xFFB4DD13); - } - previous_pixel = p.y; - gpu_draw_pixel(p, 0xFFB4DD13); - } + uint32_t* buffer = (uint32_t*)buf.ptr; + size_t samples_in_buffer = (size_t)min(buf.size, samples_remaining); + size_t slot = 0; + while (slot < samples_in_buffer){ + uint32_t sample = int16_to_uint32(*samples++, amplitude); + buffer[slot++] = sample; // Left ch. + buffer[slot++] = (channels == 1) ? sample : int16_to_uint32(*samples++, amplitude); // Right ch. + } + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; } audio_submit_buffer(); + samples_remaining -= samples_in_buffer; + } +} + +void play_startup(){ + wav_data wav = {}; + if (wav_load("/boot/redos/startup.wav", &wav)){ + // TODO: other WAV formats are available + play_int16_samples(wav.samples, wav.smpls_per_channel, wav.channels, AUDIO_LEVEL_MAX/2); + free((void*)wav.file_content.ptr, wav.file_content.size); + }else{ + play_wave(WAVE_SAW, 440, 0.1, AUDIO_LEVEL_MAX/2); + play_silence(0.05); + play_wave(WAVE_SAW, 494, 0.1, AUDIO_LEVEL_MAX/2); + play_silence(0.05); + play_wave(WAVE_SAW, 523, 0.3, AUDIO_LEVEL_MAX/2); + play_silence(0.5); } - gpu_flush(); - while(1); } -int play_test_audio(int argc, char* argv[]){ - // make_wave(WAVE_SAW, 440, 3, UINT32_MAX/4); +int audio_mixer(int argc, char* argv[]){ + play_startup(); + while (1) sleep(1000); // Idle sound process for time(!) being return 0; } process_t* init_audio_mixer(){ - return create_kernel_process("audiotest", play_test_audio, 0, 0); + return create_kernel_process("Audio out", audio_mixer, 0, 0); } driver_module audio_module = (driver_module){ diff --git a/kernel/audio/virtio_audio_pci.cpp b/kernel/audio/virtio_audio_pci.cpp index 8e97d627..f8014707 100644 --- a/kernel/audio/virtio_audio_pci.cpp +++ b/kernel/audio/virtio_audio_pci.cpp @@ -3,6 +3,7 @@ #include "console/kio.h" #include "memory/page_allocator.h" #include "std/memory_access.h" +#include "syscalls/syscalls.h" #include "audio.h" #include "std/memory.h" #include "OutputAudioDevice.hpp" @@ -26,7 +27,8 @@ #define VIRTIO_SND_PCM_RATE_44100 6 #define VIRTIO_SND_PCM_RATE_48000 7 -#define SND_44100_BUFFER_SIZE 441 +#define SND_44100_BUFFER_SIZE 256 +static_assert((SND_44100_BUFFER_SIZE & 0x01) == 0x00, "Audio buffer size must be even."); #define SND_U32_BYTES 4 #define SND_PERIOD 1 #define TOTAL_PERIOD_SIZE SND_44100_BUFFER_SIZE * SND_U32_BYTES * channels @@ -83,7 +85,7 @@ typedef struct virtio_snd_event { bool VirtioAudioDriver::init(){ uint64_t addr = find_pci_device(VIRTIO_VENDOR, VIRTIO_AUDIO_ID); if (!addr){ - kprintf("Disk device not found"); + kprintf("Audio device not found"); return false; } @@ -107,7 +109,7 @@ bool VirtioAudioDriver::init(){ pci_enable_device(addr); if (!virtio_init_device(&audio_dev)){ - kprintf("Failed disk initialization"); + kprintf("[VIRTIO_AUDIO] Failed initialization"); return false; } @@ -115,7 +117,7 @@ bool VirtioAudioDriver::init(){ audio_dev.common_cfg->queue_msix_vector = 0; if (audio_dev.common_cfg->queue_msix_vector != 0){ - kprintf("[VIRTIO_AUDIO error] failed to setup interrupts for event queue"); + kprintf("[VIRTIO_AUDIO] failed to setup interrupts for event queue"); return false; } //TODO: This should (probably) be for input devices only @@ -181,7 +183,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ kprintf("[VIRTIO_AUDIO] Stream %i (%s): Features %x. Format %x. Sample %x. Channels %i-%i",stream, (uintptr_t)(stream_info[stream].direction ? "IN" : "OUT"), stream_info[stream].features, format, rate, stream_info->channels_min, stream_info->channels_max); if (!(format & (1 << VIRTIO_SND_PCM_FMT_U32))){ - kprintf("[VIRTIO_AUDIO implementation error] stream does not support Float32 format"); + kprintf("[VIRTIO_AUDIO implementation error] stream does not support Uint32 format"); return false; } @@ -191,7 +193,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ } //TODO: Stereo - uint8_t channels = 1;//stream_info->channels_max; + uint8_t channels = 2; // 1;//stream_info->channels_max; if (!stream_set_params(stream, stream_info[stream].features, VIRTIO_SND_PCM_FMT_U32, VIRTIO_SND_PCM_RATE_44100, channels)){ kprintf("[VIRTIO_AUDIO error] Failed to configure stream %i",stream); @@ -215,6 +217,7 @@ void VirtioAudioDriver::send_buffer(sizedptr buf){ virtio_add_buffer(&audio_dev, cmd_index % audio_dev.common_cfg->queue_size, buf.ptr, buf.size, true); struct virtq_used* u = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; if (u->idx < cmd_index-20){ +//sleep(1); while (u->idx < cmd_index-5); } cmd_index++; @@ -286,7 +289,6 @@ void VirtioAudioDriver::handle_interrupt(){ select_queue(&audio_dev, EVENT_QUEUE); struct virtq_used* used = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; struct virtq_avail* avail = (struct virtq_avail*)(uintptr_t)audio_dev.common_cfg->queue_driver; - uint16_t new_idx = used->idx; if (new_idx != last_used_idx) { uint16_t used_ring_index = last_used_idx % 128; diff --git a/shared/audio/cuatro.c b/shared/audio/cuatro.c index 977f9632..3a3ac74f 100644 --- a/shared/audio/cuatro.c +++ b/shared/audio/cuatro.c @@ -1,21 +1,21 @@ #include "cuatro.h" #include "math/math.h" -float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period){ - float t = ((float)accumulator/(float)UINT32_MAX); +float sample_raw_wave(WAVE_TYPE type, uint32_t phase){ switch (type) { case WAVE_TRIG: { - float trig = 2*(absf(t-floor(t + 0.5))); + float t = ((float)phase/(float)PHASE_MAX); + float trig = 2*(absf(t-floor(t + 0.5f))); return trig; } case WAVE_SAW: - return ((t-floor(t + 0.5)) + 0.5f); + return (float)(PHASE_MAX - phase) / (float)PHASE_MAX; case WAVE_SQUARE: - return (accumulator < UINT32_MAX/2) ? 0 : 1; + return (phase < PHASE_MID) ? 0.f : 1.f; } return 0; } -uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude){ - return sample_raw_wave(type, accumulator, period) * amplitude; +uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude){ + return sample_raw_wave(type, phase) * amplitude; } \ No newline at end of file diff --git a/shared/audio/cuatro.h b/shared/audio/cuatro.h index 452c084d..c71c1667 100644 --- a/shared/audio/cuatro.h +++ b/shared/audio/cuatro.h @@ -8,11 +8,20 @@ typedef enum WAVE_TYPE { WAVE_SAW, } WAVE_TYPE; +#define PHASE_MASK 0x00FFFFFF +#define PHASE_MAX PHASE_MASK +#define PHASE_MID (PHASE_MAX >> 1) + +#define WAVE_MID_VALUE 0x80000000 + +#define AUDIO_LEVEL_MAX UINT32_MAX + + #ifdef __cplusplus extern "C" { #endif -float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period); -uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude); +float sample_raw_wave(WAVE_TYPE type, uint32_t phase); +uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude); // void make_wave(WAVE_TYPE type, float freq, float seconds); #ifdef __cplusplus } diff --git a/shared/audio/wav.c b/shared/audio/wav.c new file mode 100644 index 00000000..46c25380 --- /dev/null +++ b/shared/audio/wav.c @@ -0,0 +1,79 @@ +#include "types.h" +#include "filesystem/filesystem.h" +#include "memory/talloc.h" +#include "std/memory.h" +#include "math/math.h" +#include "console/kio.h" +#include "wav.h" +#include "syscalls/syscalls.h" + +// TODO: Handle non-trivial wav headers and other sample formats: +// https://web.archive.org/web/20100325183246/http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html + +typedef struct wav_header { + uint32_t id; + uint32_t fSize; + uint32_t wave_id; + uint32_t format_id; + uint32_t format_size; + uint16_t format; + uint16_t channels; + uint32_t sample_rate; + uint32_t idk; + uint16_t align; + uint16_t sample_bits; + uint32_t data_id; + uint32_t data_size; + int16_t first_sample; +}__attribute__((packed)) wav_header; + + +static uint32_t inline int16_to_uint32(int16_t sample) { + return (INT16_MAX + sample) << 16; +} + +bool wav_load(const char*path, wav_data *wav){ + file fd = {}; + FS_RESULT result = open_file(path, &fd); + + if (result != FS_RESULT_SUCCESS) + { + kprintf("[WAV] File not found: %s", path); + return false; + } + + wav->file_content.ptr = (uintptr_t)malloc(fd.size); + wav->file_content.size = fd.size; + wav_header* hdr = (wav_header*)wav->file_content.ptr; + size_t read_size = read_file(&fd, (char*)hdr, fd.size); + if (read_size != fd.size || + hdr->id != (uint32_t)'FFIR' || + hdr->wave_id != (uint32_t)'EVAW' || + hdr->format != 1 || + hdr->channels < 1 || hdr->channels > 2 || + hdr->sample_rate != 44100 || + hdr->sample_bits != 16 || + hdr->data_id != (uint32_t)'atad' + ) + { + // close_file(&fd) + kprintf("[WAV] Incorrect file format %s", path); + kprintf("=== Sizes %i, %i", read_size, fd.size); + kprintf("=== id %x", hdr->id); + kprintf("=== wave id %x", hdr->wave_id); + kprintf("=== format %i", hdr->format_id); + kprintf("=== channels %i", hdr->channels); + kprintf("=== sample rate %i", hdr->sample_rate); + kprintf("=== sample_bits %i", hdr->sample_bits); + kprintf("=== data id %x", hdr->data_id); + free((void*)wav->file_content.ptr, wav->file_content.size); + return false; + } + + wav->smpls_per_channel = hdr->data_size / (sizeof(int16_t) * hdr->channels); + wav->channels = hdr->channels; + wav->samples = &hdr->first_sample; + + //fclose(&fd); + return true; +} diff --git a/shared/audio/wav.h b/shared/audio/wav.h new file mode 100644 index 00000000..fa40644f --- /dev/null +++ b/shared/audio/wav.h @@ -0,0 +1,18 @@ +#pragma once + +typedef struct wav_data { + uint32_t channels; + uint32_t smpls_per_channel; + sizedptr file_content; + int16_t* samples; +} wav_data; + +#ifdef __cplusplus +extern "C" { +#endif + +bool wav_load(const char*path, wav_data *wav); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/shared/types.h b/shared/types.h index 6019b68b..4007295e 100644 --- a/shared/types.h +++ b/shared/types.h @@ -61,6 +61,8 @@ typedef signed long intptr_t; typedef signed short int16_t; typedef signed char int8_t; +#define INT16_MAX 0x7FFF + typedef struct sizedptr { uintptr_t ptr; size_t size; From f8c8586c133a02424fa264408aee22e3b5378c9a Mon Sep 17 00:00:00 2001 From: Richard Swingwood Date: Tue, 23 Sep 2025 15:16:58 +0100 Subject: [PATCH 3/6] Revert "[AUDIO] Support playing of startup sound from WAV file or hardcoded 'jingle'" This reverts commit 8ad37e02f4f45ecbf612e552a727653d026157c3. --- kernel/audio/OutputAudioDevice.cpp | 2 +- kernel/audio/audio.cpp | 124 +++++++++-------------------- kernel/audio/virtio_audio_pci.cpp | 16 ++-- shared/audio/cuatro.c | 14 ++-- shared/audio/cuatro.h | 13 +-- shared/audio/wav.c | 79 ------------------ shared/audio/wav.h | 18 ----- shared/types.h | 2 - 8 files changed, 55 insertions(+), 213 deletions(-) delete mode 100644 shared/audio/wav.c delete mode 100644 shared/audio/wav.h diff --git a/kernel/audio/OutputAudioDevice.cpp b/kernel/audio/OutputAudioDevice.cpp index 77e18f47..99dca660 100644 --- a/kernel/audio/OutputAudioDevice.cpp +++ b/kernel/audio/OutputAudioDevice.cpp @@ -2,7 +2,7 @@ #include "memory/page_allocator.h" //TODO: We should allocate at least 40 pages, possibly 64 to have more than a second at once -#define BUF_SIZE PAGE_SIZE * 20 +#define BUF_SIZE PAGE_SIZE * 10 void OutputAudioDevice::populate(){ buffer = (uintptr_t)palloc(BUF_SIZE, MEM_PRIV_KERNEL, MEM_RW, true); diff --git a/kernel/audio/audio.cpp b/kernel/audio/audio.cpp index 25cc6045..8f87aece 100644 --- a/kernel/audio/audio.cpp +++ b/kernel/audio/audio.cpp @@ -4,9 +4,8 @@ #include "console/kio.h" #include "math/math.h" #include "audio/cuatro.h" -#include "audio/wav.h" +#include "graph/graphics.h" #include "theme/theme.h" -#include "syscalls/syscalls.h" VirtioAudioDriver *audio_driver; @@ -27,100 +26,53 @@ void audio_submit_buffer(){ audio_driver->out_dev->submit_buffer(audio_driver); } -// TODO: get sample rate, channels etc. from audio driver. -// Currently ASSUMES output stream has two channels in U32 format. -const float SAMPLE_RATE = 44100.f; - - -void play_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ - uint32_t phase = 0; - // TODO: validate freqency to ensure phase_incr is sensible value. - uint32_t phase_incr = (uint32_t)(freq * (float)PHASE_MAX / SAMPLE_RATE); - uint32_t samples_remaining = ceil(SAMPLE_RATE * seconds); - - while (samples_remaining > 0){ - sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); - uint32_t* buffer = (uint32_t*)buf.ptr; - uint32_t samples_in_buffer = (uint32_t)min(buf.size/2, samples_remaining); - uint32_t sample = 0; - size_t slot = 0; - while (sample++ < samples_in_buffer){ - float wave = sample_raw_wave(type, phase) * amplitude; - buffer[slot++] = wave; // Left ch. - buffer[slot++] = wave; // Right ch. - phase = (phase + phase_incr) & PHASE_MASK; - } - while (slot < buf.size){ - buffer[slot++] = WAVE_MID_VALUE; - } - audio_submit_buffer(); - samples_remaining -= samples_in_buffer; - } -} - -void play_silence(float seconds){ - if (seconds < 0.01) return; - sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); - uint32_t buffer_count = (uint32_t)ceil(SAMPLE_RATE * seconds * 2 / buf.size); - do{ - uint32_t* buffer = (uint32_t*)buf.ptr; - size_t slot = 0; - while (slot < buf.size){ - buffer[slot++] = WAVE_MID_VALUE; - } - audio_submit_buffer(); - buf = audio_request_buffer(audio_driver->out_dev->stream_id); - } while (--buffer_count > 0); -} - -static inline uint32_t int16_to_uint32(int16_t sample, uint32_t amplitude){ - return (uint64_t)(((int64_t)sample * amplitude) >> 16) + WAVE_MID_VALUE; -} - -void play_int16_samples(int16_t *samples, size_t smpl_per_channel, size_t channels, uint32_t amplitude){ - size_t samples_remaining = smpl_per_channel * channels; - while (samples_remaining > 0){ +void make_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ + gpu_clear(0x1fb03f); + uint32_t period = 441/((freq/100.f) * 2);//TODO: improve this formula + kprintf("Period %i",period); + uint32_t accumulator = 0; + uint32_t increment = (uint32_t)(freq * (float)UINT32_MAX / 44100.0); + gpu_size size = gpu_get_screen_size(); + uint32_t previous_pixel = UINT32_MAX; + + //TODO: distorsion + //size of the buffer should be bigger, + //palloc should be 64 pages + //in the virtio driver, cmd_index is waiting for the device to catch up + + for (int i = 0; i < seconds * 100; i++){ sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); - uint32_t* buffer = (uint32_t*)buf.ptr; - size_t samples_in_buffer = (size_t)min(buf.size, samples_remaining); - size_t slot = 0; - while (slot < samples_in_buffer){ - uint32_t sample = int16_to_uint32(*samples++, amplitude); - buffer[slot++] = sample; // Left ch. - buffer[slot++] = (channels == 1) ? sample : int16_to_uint32(*samples++, amplitude); // Right ch. - } - while (slot < buf.size){ - buffer[slot++] = WAVE_MID_VALUE; + + uint32_t num_samples = buf.size; + uint32_t *buffer = (uint32_t*)buf.ptr; + for (uint32_t sample = 0; sample < num_samples; sample++){ + float wave = sample_raw_wave(type, accumulator, period); + uint32_t min = 64 * num_samples; + buffer[sample] = wave * amplitude; + + accumulator += increment; + if (accumulator >= min && accumulator < min + size.width){ + gpu_point p = (gpu_point){ accumulator - min, 100-(uint32_t)(100*wave)}; + if (previous_pixel != UINT32_MAX && abs(p.y-previous_pixel) > 10){ + gpu_draw_line({ p.x - 1, previous_pixel}, p, 0xFFB4DD13); + } + previous_pixel = p.y; + gpu_draw_pixel(p, 0xFFB4DD13); + } } audio_submit_buffer(); - samples_remaining -= samples_in_buffer; - } -} - -void play_startup(){ - wav_data wav = {}; - if (wav_load("/boot/redos/startup.wav", &wav)){ - // TODO: other WAV formats are available - play_int16_samples(wav.samples, wav.smpls_per_channel, wav.channels, AUDIO_LEVEL_MAX/2); - free((void*)wav.file_content.ptr, wav.file_content.size); - }else{ - play_wave(WAVE_SAW, 440, 0.1, AUDIO_LEVEL_MAX/2); - play_silence(0.05); - play_wave(WAVE_SAW, 494, 0.1, AUDIO_LEVEL_MAX/2); - play_silence(0.05); - play_wave(WAVE_SAW, 523, 0.3, AUDIO_LEVEL_MAX/2); - play_silence(0.5); } + gpu_flush(); + while(1); } -int audio_mixer(int argc, char* argv[]){ - play_startup(); - while (1) sleep(1000); // Idle sound process for time(!) being +int play_test_audio(int argc, char* argv[]){ + // make_wave(WAVE_SAW, 440, 3, UINT32_MAX/4); return 0; } process_t* init_audio_mixer(){ - return create_kernel_process("Audio out", audio_mixer, 0, 0); + return create_kernel_process("audiotest", play_test_audio, 0, 0); } driver_module audio_module = (driver_module){ diff --git a/kernel/audio/virtio_audio_pci.cpp b/kernel/audio/virtio_audio_pci.cpp index f8014707..8e97d627 100644 --- a/kernel/audio/virtio_audio_pci.cpp +++ b/kernel/audio/virtio_audio_pci.cpp @@ -3,7 +3,6 @@ #include "console/kio.h" #include "memory/page_allocator.h" #include "std/memory_access.h" -#include "syscalls/syscalls.h" #include "audio.h" #include "std/memory.h" #include "OutputAudioDevice.hpp" @@ -27,8 +26,7 @@ #define VIRTIO_SND_PCM_RATE_44100 6 #define VIRTIO_SND_PCM_RATE_48000 7 -#define SND_44100_BUFFER_SIZE 256 -static_assert((SND_44100_BUFFER_SIZE & 0x01) == 0x00, "Audio buffer size must be even."); +#define SND_44100_BUFFER_SIZE 441 #define SND_U32_BYTES 4 #define SND_PERIOD 1 #define TOTAL_PERIOD_SIZE SND_44100_BUFFER_SIZE * SND_U32_BYTES * channels @@ -85,7 +83,7 @@ typedef struct virtio_snd_event { bool VirtioAudioDriver::init(){ uint64_t addr = find_pci_device(VIRTIO_VENDOR, VIRTIO_AUDIO_ID); if (!addr){ - kprintf("Audio device not found"); + kprintf("Disk device not found"); return false; } @@ -109,7 +107,7 @@ bool VirtioAudioDriver::init(){ pci_enable_device(addr); if (!virtio_init_device(&audio_dev)){ - kprintf("[VIRTIO_AUDIO] Failed initialization"); + kprintf("Failed disk initialization"); return false; } @@ -117,7 +115,7 @@ bool VirtioAudioDriver::init(){ audio_dev.common_cfg->queue_msix_vector = 0; if (audio_dev.common_cfg->queue_msix_vector != 0){ - kprintf("[VIRTIO_AUDIO] failed to setup interrupts for event queue"); + kprintf("[VIRTIO_AUDIO error] failed to setup interrupts for event queue"); return false; } //TODO: This should (probably) be for input devices only @@ -183,7 +181,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ kprintf("[VIRTIO_AUDIO] Stream %i (%s): Features %x. Format %x. Sample %x. Channels %i-%i",stream, (uintptr_t)(stream_info[stream].direction ? "IN" : "OUT"), stream_info[stream].features, format, rate, stream_info->channels_min, stream_info->channels_max); if (!(format & (1 << VIRTIO_SND_PCM_FMT_U32))){ - kprintf("[VIRTIO_AUDIO implementation error] stream does not support Uint32 format"); + kprintf("[VIRTIO_AUDIO implementation error] stream does not support Float32 format"); return false; } @@ -193,7 +191,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ } //TODO: Stereo - uint8_t channels = 2; // 1;//stream_info->channels_max; + uint8_t channels = 1;//stream_info->channels_max; if (!stream_set_params(stream, stream_info[stream].features, VIRTIO_SND_PCM_FMT_U32, VIRTIO_SND_PCM_RATE_44100, channels)){ kprintf("[VIRTIO_AUDIO error] Failed to configure stream %i",stream); @@ -217,7 +215,6 @@ void VirtioAudioDriver::send_buffer(sizedptr buf){ virtio_add_buffer(&audio_dev, cmd_index % audio_dev.common_cfg->queue_size, buf.ptr, buf.size, true); struct virtq_used* u = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; if (u->idx < cmd_index-20){ -//sleep(1); while (u->idx < cmd_index-5); } cmd_index++; @@ -289,6 +286,7 @@ void VirtioAudioDriver::handle_interrupt(){ select_queue(&audio_dev, EVENT_QUEUE); struct virtq_used* used = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; struct virtq_avail* avail = (struct virtq_avail*)(uintptr_t)audio_dev.common_cfg->queue_driver; + uint16_t new_idx = used->idx; if (new_idx != last_used_idx) { uint16_t used_ring_index = last_used_idx % 128; diff --git a/shared/audio/cuatro.c b/shared/audio/cuatro.c index 3a3ac74f..977f9632 100644 --- a/shared/audio/cuatro.c +++ b/shared/audio/cuatro.c @@ -1,21 +1,21 @@ #include "cuatro.h" #include "math/math.h" -float sample_raw_wave(WAVE_TYPE type, uint32_t phase){ +float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period){ + float t = ((float)accumulator/(float)UINT32_MAX); switch (type) { case WAVE_TRIG: { - float t = ((float)phase/(float)PHASE_MAX); - float trig = 2*(absf(t-floor(t + 0.5f))); + float trig = 2*(absf(t-floor(t + 0.5))); return trig; } case WAVE_SAW: - return (float)(PHASE_MAX - phase) / (float)PHASE_MAX; + return ((t-floor(t + 0.5)) + 0.5f); case WAVE_SQUARE: - return (phase < PHASE_MID) ? 0.f : 1.f; + return (accumulator < UINT32_MAX/2) ? 0 : 1; } return 0; } -uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude){ - return sample_raw_wave(type, phase) * amplitude; +uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude){ + return sample_raw_wave(type, accumulator, period) * amplitude; } \ No newline at end of file diff --git a/shared/audio/cuatro.h b/shared/audio/cuatro.h index c71c1667..452c084d 100644 --- a/shared/audio/cuatro.h +++ b/shared/audio/cuatro.h @@ -8,20 +8,11 @@ typedef enum WAVE_TYPE { WAVE_SAW, } WAVE_TYPE; -#define PHASE_MASK 0x00FFFFFF -#define PHASE_MAX PHASE_MASK -#define PHASE_MID (PHASE_MAX >> 1) - -#define WAVE_MID_VALUE 0x80000000 - -#define AUDIO_LEVEL_MAX UINT32_MAX - - #ifdef __cplusplus extern "C" { #endif -float sample_raw_wave(WAVE_TYPE type, uint32_t phase); -uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude); +float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period); +uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude); // void make_wave(WAVE_TYPE type, float freq, float seconds); #ifdef __cplusplus } diff --git a/shared/audio/wav.c b/shared/audio/wav.c deleted file mode 100644 index 46c25380..00000000 --- a/shared/audio/wav.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "types.h" -#include "filesystem/filesystem.h" -#include "memory/talloc.h" -#include "std/memory.h" -#include "math/math.h" -#include "console/kio.h" -#include "wav.h" -#include "syscalls/syscalls.h" - -// TODO: Handle non-trivial wav headers and other sample formats: -// https://web.archive.org/web/20100325183246/http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html - -typedef struct wav_header { - uint32_t id; - uint32_t fSize; - uint32_t wave_id; - uint32_t format_id; - uint32_t format_size; - uint16_t format; - uint16_t channels; - uint32_t sample_rate; - uint32_t idk; - uint16_t align; - uint16_t sample_bits; - uint32_t data_id; - uint32_t data_size; - int16_t first_sample; -}__attribute__((packed)) wav_header; - - -static uint32_t inline int16_to_uint32(int16_t sample) { - return (INT16_MAX + sample) << 16; -} - -bool wav_load(const char*path, wav_data *wav){ - file fd = {}; - FS_RESULT result = open_file(path, &fd); - - if (result != FS_RESULT_SUCCESS) - { - kprintf("[WAV] File not found: %s", path); - return false; - } - - wav->file_content.ptr = (uintptr_t)malloc(fd.size); - wav->file_content.size = fd.size; - wav_header* hdr = (wav_header*)wav->file_content.ptr; - size_t read_size = read_file(&fd, (char*)hdr, fd.size); - if (read_size != fd.size || - hdr->id != (uint32_t)'FFIR' || - hdr->wave_id != (uint32_t)'EVAW' || - hdr->format != 1 || - hdr->channels < 1 || hdr->channels > 2 || - hdr->sample_rate != 44100 || - hdr->sample_bits != 16 || - hdr->data_id != (uint32_t)'atad' - ) - { - // close_file(&fd) - kprintf("[WAV] Incorrect file format %s", path); - kprintf("=== Sizes %i, %i", read_size, fd.size); - kprintf("=== id %x", hdr->id); - kprintf("=== wave id %x", hdr->wave_id); - kprintf("=== format %i", hdr->format_id); - kprintf("=== channels %i", hdr->channels); - kprintf("=== sample rate %i", hdr->sample_rate); - kprintf("=== sample_bits %i", hdr->sample_bits); - kprintf("=== data id %x", hdr->data_id); - free((void*)wav->file_content.ptr, wav->file_content.size); - return false; - } - - wav->smpls_per_channel = hdr->data_size / (sizeof(int16_t) * hdr->channels); - wav->channels = hdr->channels; - wav->samples = &hdr->first_sample; - - //fclose(&fd); - return true; -} diff --git a/shared/audio/wav.h b/shared/audio/wav.h deleted file mode 100644 index fa40644f..00000000 --- a/shared/audio/wav.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -typedef struct wav_data { - uint32_t channels; - uint32_t smpls_per_channel; - sizedptr file_content; - int16_t* samples; -} wav_data; - -#ifdef __cplusplus -extern "C" { -#endif - -bool wav_load(const char*path, wav_data *wav); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/shared/types.h b/shared/types.h index 4007295e..6019b68b 100644 --- a/shared/types.h +++ b/shared/types.h @@ -61,8 +61,6 @@ typedef signed long intptr_t; typedef signed short int16_t; typedef signed char int8_t; -#define INT16_MAX 0x7FFF - typedef struct sizedptr { uintptr_t ptr; size_t size; From 9b2738a2e01a153427e6b747a852b7e65a021493 Mon Sep 17 00:00:00 2001 From: Richard Swingwood Date: Tue, 23 Sep 2025 14:47:43 +0100 Subject: [PATCH 4/6] [AUDIO] Support playing of startup sound from WAV file or hardcoded 'jingle' --- kernel/audio/OutputAudioDevice.cpp | 2 +- kernel/audio/audio.cpp | 124 ++++++++++++++++++++--------- kernel/audio/virtio_audio_pci.cpp | 16 ++-- shared/audio/cuatro.c | 14 ++-- shared/audio/cuatro.h | 13 ++- shared/audio/wav.c | 79 ++++++++++++++++++ shared/audio/wav.h | 18 +++++ shared/types.h | 2 + 8 files changed, 213 insertions(+), 55 deletions(-) create mode 100644 shared/audio/wav.c create mode 100644 shared/audio/wav.h diff --git a/kernel/audio/OutputAudioDevice.cpp b/kernel/audio/OutputAudioDevice.cpp index 99dca660..77e18f47 100644 --- a/kernel/audio/OutputAudioDevice.cpp +++ b/kernel/audio/OutputAudioDevice.cpp @@ -2,7 +2,7 @@ #include "memory/page_allocator.h" //TODO: We should allocate at least 40 pages, possibly 64 to have more than a second at once -#define BUF_SIZE PAGE_SIZE * 10 +#define BUF_SIZE PAGE_SIZE * 20 void OutputAudioDevice::populate(){ buffer = (uintptr_t)palloc(BUF_SIZE, MEM_PRIV_KERNEL, MEM_RW, true); diff --git a/kernel/audio/audio.cpp b/kernel/audio/audio.cpp index 8f87aece..25cc6045 100644 --- a/kernel/audio/audio.cpp +++ b/kernel/audio/audio.cpp @@ -4,8 +4,9 @@ #include "console/kio.h" #include "math/math.h" #include "audio/cuatro.h" -#include "graph/graphics.h" +#include "audio/wav.h" #include "theme/theme.h" +#include "syscalls/syscalls.h" VirtioAudioDriver *audio_driver; @@ -26,53 +27,100 @@ void audio_submit_buffer(){ audio_driver->out_dev->submit_buffer(audio_driver); } -void make_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ - gpu_clear(0x1fb03f); - uint32_t period = 441/((freq/100.f) * 2);//TODO: improve this formula - kprintf("Period %i",period); - uint32_t accumulator = 0; - uint32_t increment = (uint32_t)(freq * (float)UINT32_MAX / 44100.0); - gpu_size size = gpu_get_screen_size(); - uint32_t previous_pixel = UINT32_MAX; - - //TODO: distorsion - //size of the buffer should be bigger, - //palloc should be 64 pages - //in the virtio driver, cmd_index is waiting for the device to catch up - - for (int i = 0; i < seconds * 100; i++){ +// TODO: get sample rate, channels etc. from audio driver. +// Currently ASSUMES output stream has two channels in U32 format. +const float SAMPLE_RATE = 44100.f; + + +void play_wave(WAVE_TYPE type, float freq, float seconds, uint32_t amplitude){ + uint32_t phase = 0; + // TODO: validate freqency to ensure phase_incr is sensible value. + uint32_t phase_incr = (uint32_t)(freq * (float)PHASE_MAX / SAMPLE_RATE); + uint32_t samples_remaining = ceil(SAMPLE_RATE * seconds); + + while (samples_remaining > 0){ + sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); + uint32_t* buffer = (uint32_t*)buf.ptr; + uint32_t samples_in_buffer = (uint32_t)min(buf.size/2, samples_remaining); + uint32_t sample = 0; + size_t slot = 0; + while (sample++ < samples_in_buffer){ + float wave = sample_raw_wave(type, phase) * amplitude; + buffer[slot++] = wave; // Left ch. + buffer[slot++] = wave; // Right ch. + phase = (phase + phase_incr) & PHASE_MASK; + } + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; + } + audio_submit_buffer(); + samples_remaining -= samples_in_buffer; + } +} + +void play_silence(float seconds){ + if (seconds < 0.01) return; + sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); + uint32_t buffer_count = (uint32_t)ceil(SAMPLE_RATE * seconds * 2 / buf.size); + do{ + uint32_t* buffer = (uint32_t*)buf.ptr; + size_t slot = 0; + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; + } + audio_submit_buffer(); + buf = audio_request_buffer(audio_driver->out_dev->stream_id); + } while (--buffer_count > 0); +} + +static inline uint32_t int16_to_uint32(int16_t sample, uint32_t amplitude){ + return (uint64_t)(((int64_t)sample * amplitude) >> 16) + WAVE_MID_VALUE; +} + +void play_int16_samples(int16_t *samples, size_t smpl_per_channel, size_t channels, uint32_t amplitude){ + size_t samples_remaining = smpl_per_channel * channels; + while (samples_remaining > 0){ sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); - - uint32_t num_samples = buf.size; - uint32_t *buffer = (uint32_t*)buf.ptr; - for (uint32_t sample = 0; sample < num_samples; sample++){ - float wave = sample_raw_wave(type, accumulator, period); - uint32_t min = 64 * num_samples; - buffer[sample] = wave * amplitude; - - accumulator += increment; - if (accumulator >= min && accumulator < min + size.width){ - gpu_point p = (gpu_point){ accumulator - min, 100-(uint32_t)(100*wave)}; - if (previous_pixel != UINT32_MAX && abs(p.y-previous_pixel) > 10){ - gpu_draw_line({ p.x - 1, previous_pixel}, p, 0xFFB4DD13); - } - previous_pixel = p.y; - gpu_draw_pixel(p, 0xFFB4DD13); - } + uint32_t* buffer = (uint32_t*)buf.ptr; + size_t samples_in_buffer = (size_t)min(buf.size, samples_remaining); + size_t slot = 0; + while (slot < samples_in_buffer){ + uint32_t sample = int16_to_uint32(*samples++, amplitude); + buffer[slot++] = sample; // Left ch. + buffer[slot++] = (channels == 1) ? sample : int16_to_uint32(*samples++, amplitude); // Right ch. + } + while (slot < buf.size){ + buffer[slot++] = WAVE_MID_VALUE; } audio_submit_buffer(); + samples_remaining -= samples_in_buffer; + } +} + +void play_startup(){ + wav_data wav = {}; + if (wav_load("/boot/redos/startup.wav", &wav)){ + // TODO: other WAV formats are available + play_int16_samples(wav.samples, wav.smpls_per_channel, wav.channels, AUDIO_LEVEL_MAX/2); + free((void*)wav.file_content.ptr, wav.file_content.size); + }else{ + play_wave(WAVE_SAW, 440, 0.1, AUDIO_LEVEL_MAX/2); + play_silence(0.05); + play_wave(WAVE_SAW, 494, 0.1, AUDIO_LEVEL_MAX/2); + play_silence(0.05); + play_wave(WAVE_SAW, 523, 0.3, AUDIO_LEVEL_MAX/2); + play_silence(0.5); } - gpu_flush(); - while(1); } -int play_test_audio(int argc, char* argv[]){ - // make_wave(WAVE_SAW, 440, 3, UINT32_MAX/4); +int audio_mixer(int argc, char* argv[]){ + play_startup(); + while (1) sleep(1000); // Idle sound process for time(!) being return 0; } process_t* init_audio_mixer(){ - return create_kernel_process("audiotest", play_test_audio, 0, 0); + return create_kernel_process("Audio out", audio_mixer, 0, 0); } driver_module audio_module = (driver_module){ diff --git a/kernel/audio/virtio_audio_pci.cpp b/kernel/audio/virtio_audio_pci.cpp index 8e97d627..f8014707 100644 --- a/kernel/audio/virtio_audio_pci.cpp +++ b/kernel/audio/virtio_audio_pci.cpp @@ -3,6 +3,7 @@ #include "console/kio.h" #include "memory/page_allocator.h" #include "std/memory_access.h" +#include "syscalls/syscalls.h" #include "audio.h" #include "std/memory.h" #include "OutputAudioDevice.hpp" @@ -26,7 +27,8 @@ #define VIRTIO_SND_PCM_RATE_44100 6 #define VIRTIO_SND_PCM_RATE_48000 7 -#define SND_44100_BUFFER_SIZE 441 +#define SND_44100_BUFFER_SIZE 256 +static_assert((SND_44100_BUFFER_SIZE & 0x01) == 0x00, "Audio buffer size must be even."); #define SND_U32_BYTES 4 #define SND_PERIOD 1 #define TOTAL_PERIOD_SIZE SND_44100_BUFFER_SIZE * SND_U32_BYTES * channels @@ -83,7 +85,7 @@ typedef struct virtio_snd_event { bool VirtioAudioDriver::init(){ uint64_t addr = find_pci_device(VIRTIO_VENDOR, VIRTIO_AUDIO_ID); if (!addr){ - kprintf("Disk device not found"); + kprintf("Audio device not found"); return false; } @@ -107,7 +109,7 @@ bool VirtioAudioDriver::init(){ pci_enable_device(addr); if (!virtio_init_device(&audio_dev)){ - kprintf("Failed disk initialization"); + kprintf("[VIRTIO_AUDIO] Failed initialization"); return false; } @@ -115,7 +117,7 @@ bool VirtioAudioDriver::init(){ audio_dev.common_cfg->queue_msix_vector = 0; if (audio_dev.common_cfg->queue_msix_vector != 0){ - kprintf("[VIRTIO_AUDIO error] failed to setup interrupts for event queue"); + kprintf("[VIRTIO_AUDIO] failed to setup interrupts for event queue"); return false; } //TODO: This should (probably) be for input devices only @@ -181,7 +183,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ kprintf("[VIRTIO_AUDIO] Stream %i (%s): Features %x. Format %x. Sample %x. Channels %i-%i",stream, (uintptr_t)(stream_info[stream].direction ? "IN" : "OUT"), stream_info[stream].features, format, rate, stream_info->channels_min, stream_info->channels_max); if (!(format & (1 << VIRTIO_SND_PCM_FMT_U32))){ - kprintf("[VIRTIO_AUDIO implementation error] stream does not support Float32 format"); + kprintf("[VIRTIO_AUDIO implementation error] stream does not support Uint32 format"); return false; } @@ -191,7 +193,7 @@ bool VirtioAudioDriver::config_streams(uint32_t streams){ } //TODO: Stereo - uint8_t channels = 1;//stream_info->channels_max; + uint8_t channels = 2; // 1;//stream_info->channels_max; if (!stream_set_params(stream, stream_info[stream].features, VIRTIO_SND_PCM_FMT_U32, VIRTIO_SND_PCM_RATE_44100, channels)){ kprintf("[VIRTIO_AUDIO error] Failed to configure stream %i",stream); @@ -215,6 +217,7 @@ void VirtioAudioDriver::send_buffer(sizedptr buf){ virtio_add_buffer(&audio_dev, cmd_index % audio_dev.common_cfg->queue_size, buf.ptr, buf.size, true); struct virtq_used* u = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; if (u->idx < cmd_index-20){ +//sleep(1); while (u->idx < cmd_index-5); } cmd_index++; @@ -286,7 +289,6 @@ void VirtioAudioDriver::handle_interrupt(){ select_queue(&audio_dev, EVENT_QUEUE); struct virtq_used* used = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; struct virtq_avail* avail = (struct virtq_avail*)(uintptr_t)audio_dev.common_cfg->queue_driver; - uint16_t new_idx = used->idx; if (new_idx != last_used_idx) { uint16_t used_ring_index = last_used_idx % 128; diff --git a/shared/audio/cuatro.c b/shared/audio/cuatro.c index 977f9632..3a3ac74f 100644 --- a/shared/audio/cuatro.c +++ b/shared/audio/cuatro.c @@ -1,21 +1,21 @@ #include "cuatro.h" #include "math/math.h" -float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period){ - float t = ((float)accumulator/(float)UINT32_MAX); +float sample_raw_wave(WAVE_TYPE type, uint32_t phase){ switch (type) { case WAVE_TRIG: { - float trig = 2*(absf(t-floor(t + 0.5))); + float t = ((float)phase/(float)PHASE_MAX); + float trig = 2*(absf(t-floor(t + 0.5f))); return trig; } case WAVE_SAW: - return ((t-floor(t + 0.5)) + 0.5f); + return (float)(PHASE_MAX - phase) / (float)PHASE_MAX; case WAVE_SQUARE: - return (accumulator < UINT32_MAX/2) ? 0 : 1; + return (phase < PHASE_MID) ? 0.f : 1.f; } return 0; } -uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude){ - return sample_raw_wave(type, accumulator, period) * amplitude; +uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude){ + return sample_raw_wave(type, phase) * amplitude; } \ No newline at end of file diff --git a/shared/audio/cuatro.h b/shared/audio/cuatro.h index 452c084d..c71c1667 100644 --- a/shared/audio/cuatro.h +++ b/shared/audio/cuatro.h @@ -8,11 +8,20 @@ typedef enum WAVE_TYPE { WAVE_SAW, } WAVE_TYPE; +#define PHASE_MASK 0x00FFFFFF +#define PHASE_MAX PHASE_MASK +#define PHASE_MID (PHASE_MAX >> 1) + +#define WAVE_MID_VALUE 0x80000000 + +#define AUDIO_LEVEL_MAX UINT32_MAX + + #ifdef __cplusplus extern "C" { #endif -float sample_raw_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period); -uint32_t sample_wave(WAVE_TYPE type, uint32_t accumulator, uint32_t period, uint32_t amplitude); +float sample_raw_wave(WAVE_TYPE type, uint32_t phase); +uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude); // void make_wave(WAVE_TYPE type, float freq, float seconds); #ifdef __cplusplus } diff --git a/shared/audio/wav.c b/shared/audio/wav.c new file mode 100644 index 00000000..46c25380 --- /dev/null +++ b/shared/audio/wav.c @@ -0,0 +1,79 @@ +#include "types.h" +#include "filesystem/filesystem.h" +#include "memory/talloc.h" +#include "std/memory.h" +#include "math/math.h" +#include "console/kio.h" +#include "wav.h" +#include "syscalls/syscalls.h" + +// TODO: Handle non-trivial wav headers and other sample formats: +// https://web.archive.org/web/20100325183246/http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html + +typedef struct wav_header { + uint32_t id; + uint32_t fSize; + uint32_t wave_id; + uint32_t format_id; + uint32_t format_size; + uint16_t format; + uint16_t channels; + uint32_t sample_rate; + uint32_t idk; + uint16_t align; + uint16_t sample_bits; + uint32_t data_id; + uint32_t data_size; + int16_t first_sample; +}__attribute__((packed)) wav_header; + + +static uint32_t inline int16_to_uint32(int16_t sample) { + return (INT16_MAX + sample) << 16; +} + +bool wav_load(const char*path, wav_data *wav){ + file fd = {}; + FS_RESULT result = open_file(path, &fd); + + if (result != FS_RESULT_SUCCESS) + { + kprintf("[WAV] File not found: %s", path); + return false; + } + + wav->file_content.ptr = (uintptr_t)malloc(fd.size); + wav->file_content.size = fd.size; + wav_header* hdr = (wav_header*)wav->file_content.ptr; + size_t read_size = read_file(&fd, (char*)hdr, fd.size); + if (read_size != fd.size || + hdr->id != (uint32_t)'FFIR' || + hdr->wave_id != (uint32_t)'EVAW' || + hdr->format != 1 || + hdr->channels < 1 || hdr->channels > 2 || + hdr->sample_rate != 44100 || + hdr->sample_bits != 16 || + hdr->data_id != (uint32_t)'atad' + ) + { + // close_file(&fd) + kprintf("[WAV] Incorrect file format %s", path); + kprintf("=== Sizes %i, %i", read_size, fd.size); + kprintf("=== id %x", hdr->id); + kprintf("=== wave id %x", hdr->wave_id); + kprintf("=== format %i", hdr->format_id); + kprintf("=== channels %i", hdr->channels); + kprintf("=== sample rate %i", hdr->sample_rate); + kprintf("=== sample_bits %i", hdr->sample_bits); + kprintf("=== data id %x", hdr->data_id); + free((void*)wav->file_content.ptr, wav->file_content.size); + return false; + } + + wav->smpls_per_channel = hdr->data_size / (sizeof(int16_t) * hdr->channels); + wav->channels = hdr->channels; + wav->samples = &hdr->first_sample; + + //fclose(&fd); + return true; +} diff --git a/shared/audio/wav.h b/shared/audio/wav.h new file mode 100644 index 00000000..fa40644f --- /dev/null +++ b/shared/audio/wav.h @@ -0,0 +1,18 @@ +#pragma once + +typedef struct wav_data { + uint32_t channels; + uint32_t smpls_per_channel; + sizedptr file_content; + int16_t* samples; +} wav_data; + +#ifdef __cplusplus +extern "C" { +#endif + +bool wav_load(const char*path, wav_data *wav); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/shared/types.h b/shared/types.h index 6019b68b..4007295e 100644 --- a/shared/types.h +++ b/shared/types.h @@ -61,6 +61,8 @@ typedef signed long intptr_t; typedef signed short int16_t; typedef signed char int8_t; +#define INT16_MAX 0x7FFF + typedef struct sizedptr { uintptr_t ptr; size_t size; From 8a7281031a67a5d36e9b38336fbb8c60c9ae291e Mon Sep 17 00:00:00 2001 From: Richard Swingwood Date: Tue, 23 Sep 2025 15:45:49 +0100 Subject: [PATCH 5/6] [AUDIO] Support playing startup sound from WAV file or hardcoded 'jingle' --- kernel/audio/OutputAudioDevice.cpp | 2 +- kernel/audio/audio.cpp | 2 +- kernel/audio/virtio_audio_pci.cpp | 2 +- shared/audio/cuatro.c | 2 +- shared/audio/cuatro.h | 2 +- shared/audio/wav.c | 1 + shared/audio/wav.h | 1 + shared/types.h | 2 +- 8 files changed, 8 insertions(+), 6 deletions(-) diff --git a/kernel/audio/OutputAudioDevice.cpp b/kernel/audio/OutputAudioDevice.cpp index 77e18f47..32bcadb2 100644 --- a/kernel/audio/OutputAudioDevice.cpp +++ b/kernel/audio/OutputAudioDevice.cpp @@ -20,4 +20,4 @@ void OutputAudioDevice::submit_buffer(AudioDriver *driver){ driver->send_buffer((sizedptr){buffer + read_ptr, packet_size}); read_ptr += packet_size; if (read_ptr + packet_size >= BUF_SIZE) read_ptr = 0; -} \ No newline at end of file +} diff --git a/kernel/audio/audio.cpp b/kernel/audio/audio.cpp index 25cc6045..2415a289 100644 --- a/kernel/audio/audio.cpp +++ b/kernel/audio/audio.cpp @@ -134,4 +134,4 @@ driver_module audio_module = (driver_module){ .write = 0, .seek = 0, .readdir = 0, -}; \ No newline at end of file +}; diff --git a/kernel/audio/virtio_audio_pci.cpp b/kernel/audio/virtio_audio_pci.cpp index f8014707..15edaa66 100644 --- a/kernel/audio/virtio_audio_pci.cpp +++ b/kernel/audio/virtio_audio_pci.cpp @@ -27,7 +27,7 @@ #define VIRTIO_SND_PCM_RATE_44100 6 #define VIRTIO_SND_PCM_RATE_48000 7 -#define SND_44100_BUFFER_SIZE 256 +#define SND_44100_BUFFER_SIZE 256 static_assert((SND_44100_BUFFER_SIZE & 0x01) == 0x00, "Audio buffer size must be even."); #define SND_U32_BYTES 4 #define SND_PERIOD 1 diff --git a/shared/audio/cuatro.c b/shared/audio/cuatro.c index 3a3ac74f..66527c07 100644 --- a/shared/audio/cuatro.c +++ b/shared/audio/cuatro.c @@ -18,4 +18,4 @@ float sample_raw_wave(WAVE_TYPE type, uint32_t phase){ uint32_t sample_wave(WAVE_TYPE type, uint32_t phase, uint32_t amplitude){ return sample_raw_wave(type, phase) * amplitude; -} \ No newline at end of file +} diff --git a/shared/audio/cuatro.h b/shared/audio/cuatro.h index c71c1667..1601bccc 100644 --- a/shared/audio/cuatro.h +++ b/shared/audio/cuatro.h @@ -12,7 +12,7 @@ typedef enum WAVE_TYPE { #define PHASE_MAX PHASE_MASK #define PHASE_MID (PHASE_MAX >> 1) -#define WAVE_MID_VALUE 0x80000000 +#define WAVE_MID_VALUE 0x80000000 #define AUDIO_LEVEL_MAX UINT32_MAX diff --git a/shared/audio/wav.c b/shared/audio/wav.c index 46c25380..0eff9322 100644 --- a/shared/audio/wav.c +++ b/shared/audio/wav.c @@ -7,6 +7,7 @@ #include "wav.h" #include "syscalls/syscalls.h" + // TODO: Handle non-trivial wav headers and other sample formats: // https://web.archive.org/web/20100325183246/http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html diff --git a/shared/audio/wav.h b/shared/audio/wav.h index fa40644f..f73e0bd5 100644 --- a/shared/audio/wav.h +++ b/shared/audio/wav.h @@ -7,6 +7,7 @@ typedef struct wav_data { int16_t* samples; } wav_data; + #ifdef __cplusplus extern "C" { #endif diff --git a/shared/types.h b/shared/types.h index 4007295e..27c06c35 100644 --- a/shared/types.h +++ b/shared/types.h @@ -61,7 +61,7 @@ typedef signed long intptr_t; typedef signed short int16_t; typedef signed char int8_t; -#define INT16_MAX 0x7FFF +#define INT16_MAX 0x7FFF typedef struct sizedptr { uintptr_t ptr; From e6815742fc995fa5dc2e4c938bad22a359290730 Mon Sep 17 00:00:00 2001 From: Richard Swingwood Date: Fri, 26 Sep 2025 21:22:18 +0100 Subject: [PATCH 6/6] Support for 8-bit WAV files and upsampling certain rates to 44100 --- kernel/audio/audio.cpp | 13 ++-- kernel/audio/virtio_audio_pci.cpp | 1 - shared/audio/wav.c | 110 ++++++++++++++++++++++-------- shared/audio/wav.h | 5 +- 4 files changed, 88 insertions(+), 41 deletions(-) diff --git a/kernel/audio/audio.cpp b/kernel/audio/audio.cpp index 2415a289..ae80f088 100644 --- a/kernel/audio/audio.cpp +++ b/kernel/audio/audio.cpp @@ -77,8 +77,8 @@ static inline uint32_t int16_to_uint32(int16_t sample, uint32_t amplitude){ return (uint64_t)(((int64_t)sample * amplitude) >> 16) + WAVE_MID_VALUE; } -void play_int16_samples(int16_t *samples, size_t smpl_per_channel, size_t channels, uint32_t amplitude){ - size_t samples_remaining = smpl_per_channel * channels; +void play_int16_samples(int16_t *samples, size_t smpls_per_channel, size_t channels, uint32_t amplitude){ + size_t samples_remaining = smpls_per_channel * channels; while (samples_remaining > 0){ sizedptr buf = audio_request_buffer(audio_driver->out_dev->stream_id); uint32_t* buffer = (uint32_t*)buf.ptr; @@ -93,16 +93,15 @@ void play_int16_samples(int16_t *samples, size_t smpl_per_channel, size_t channe buffer[slot++] = WAVE_MID_VALUE; } audio_submit_buffer(); - samples_remaining -= samples_in_buffer; + samples_remaining -= samples_in_buffer / (3 - channels); // TODO: There must be a better way... } } void play_startup(){ wav_data wav = {}; - if (wav_load("/boot/redos/startup.wav", &wav)){ - // TODO: other WAV formats are available - play_int16_samples(wav.samples, wav.smpls_per_channel, wav.channels, AUDIO_LEVEL_MAX/2); - free((void*)wav.file_content.ptr, wav.file_content.size); + if (wav_load_as_int16("/boot/redos/startup.wav", &wav)){ + play_int16_samples((int16_t*)wav.samples.ptr, wav.smpls_per_channel, wav.channels, AUDIO_LEVEL_MAX/2); + free((void*)wav.samples.ptr, wav.samples.size); }else{ play_wave(WAVE_SAW, 440, 0.1, AUDIO_LEVEL_MAX/2); play_silence(0.05); diff --git a/kernel/audio/virtio_audio_pci.cpp b/kernel/audio/virtio_audio_pci.cpp index 15edaa66..f761f8fb 100644 --- a/kernel/audio/virtio_audio_pci.cpp +++ b/kernel/audio/virtio_audio_pci.cpp @@ -217,7 +217,6 @@ void VirtioAudioDriver::send_buffer(sizedptr buf){ virtio_add_buffer(&audio_dev, cmd_index % audio_dev.common_cfg->queue_size, buf.ptr, buf.size, true); struct virtq_used* u = (struct virtq_used*)(uintptr_t)audio_dev.common_cfg->queue_device; if (u->idx < cmd_index-20){ -//sleep(1); while (u->idx < cmd_index-5); } cmd_index++; diff --git a/shared/audio/wav.c b/shared/audio/wav.c index 0eff9322..86a1f16a 100644 --- a/shared/audio/wav.c +++ b/shared/audio/wav.c @@ -25,15 +25,48 @@ typedef struct wav_header { uint16_t sample_bits; uint32_t data_id; uint32_t data_size; - int16_t first_sample; }__attribute__((packed)) wav_header; -static uint32_t inline int16_to_uint32(int16_t sample) { - return (INT16_MAX + sample) << 16; +void transform_16bit(wav_header *hdr, wav_data *wav, uint32_t upsample, file *fd){ + wav->samples.size = hdr->data_size * upsample; + wav->samples.ptr = (uintptr_t)malloc(wav->samples.size); + int16_t* tbuf = (int16_t*)malloc(hdr->data_size); + read_file(fd, (char*)tbuf, hdr->data_size); + wav->smpls_per_channel = (wav->samples.size) / (sizeof(int16_t) * hdr->channels); + wav->channels = hdr->channels; + uint32_t samples_remaining = hdr->data_size / sizeof(int16_t); + int16_t* source = tbuf; + int16_t* dest = (int16_t*)wav->samples.ptr; + while (samples_remaining-- > 0){ + for (int i = upsample; i > 0; i--){ + *dest++ = *source; // TODO: interpolate + } + ++source; + } + free(tbuf, hdr->data_size); +} + +void transform_8bit(wav_header *hdr, wav_data *wav, uint32_t upsample, file *fd){ + uint8_t* tbuf = (int8_t*)malloc(hdr->data_size); + read_file(fd, (char*)tbuf, hdr->data_size); + wav->samples.size = hdr->data_size * upsample * 2; + wav->samples.ptr = (uintptr_t)malloc(wav->samples.size); + wav->smpls_per_channel = wav->samples.size / (sizeof(int16_t) * hdr->channels); + wav->channels = hdr->channels; + uint32_t samples_remaining = hdr->data_size; + uint8_t* source = tbuf; + int16_t* dest = (int16_t*)wav->samples.ptr; + while (samples_remaining-- > 0){ + int16_t sample = (int16_t)((*source++ - 128) * 256); // offset binary to signed + for (int i = upsample; i > 0; i--){ + *dest++ = sample; // TODO: interpolate + } + } + free(tbuf, hdr->data_size); } -bool wav_load(const char*path, wav_data *wav){ +bool wav_load_as_int16(const char*path, wav_data *wav){ file fd = {}; FS_RESULT result = open_file(path, &fd); @@ -43,38 +76,55 @@ bool wav_load(const char*path, wav_data *wav){ return false; } - wav->file_content.ptr = (uintptr_t)malloc(fd.size); - wav->file_content.size = fd.size; - wav_header* hdr = (wav_header*)wav->file_content.ptr; - size_t read_size = read_file(&fd, (char*)hdr, fd.size); - if (read_size != fd.size || - hdr->id != (uint32_t)'FFIR' || - hdr->wave_id != (uint32_t)'EVAW' || - hdr->format != 1 || - hdr->channels < 1 || hdr->channels > 2 || - hdr->sample_rate != 44100 || - hdr->sample_bits != 16 || - hdr->data_id != (uint32_t)'atad' + wav_header hdr = {}; + size_t read_size = read_file(&fd, (char*)&hdr, sizeof(wav_header)); + if (read_size != sizeof(wav_header) || + hdr.id != (uint32_t)'FFIR' || + hdr.wave_id != (uint32_t)'EVAW' || + hdr.format != 1 || + hdr.channels < 1 || hdr.channels > 2 || + hdr.sample_rate > 44100 || + (44100 % hdr.sample_rate != 0) || + (hdr.sample_bits != 8 && hdr.sample_bits != 16) || + hdr.data_id != (uint32_t)'atad' || + fd.size < hdr.data_size + sizeof(wav_header) || + hdr.data_size == 0 ) { // close_file(&fd) - kprintf("[WAV] Incorrect file format %s", path); - kprintf("=== Sizes %i, %i", read_size, fd.size); - kprintf("=== id %x", hdr->id); - kprintf("=== wave id %x", hdr->wave_id); - kprintf("=== format %i", hdr->format_id); - kprintf("=== channels %i", hdr->channels); - kprintf("=== sample rate %i", hdr->sample_rate); - kprintf("=== sample_bits %i", hdr->sample_bits); - kprintf("=== data id %x", hdr->data_id); - free((void*)wav->file_content.ptr, wav->file_content.size); + kprintf("[WAV] Unsupported file format %s", path); + kprintf("=== Sizes %i, %i, %i", read_size, fd.size, hdr.data_size); + kprintf("=== id %x", hdr.id); + kprintf("=== wave id %x", hdr.wave_id); + kprintf("=== format %x", hdr.format_id); + kprintf("=== channels %i", hdr.channels); + kprintf("=== sample rate %i", hdr.sample_rate); + kprintf("=== sample_bits %i", hdr.sample_bits); + kprintf("=== data id %x", hdr.data_id); return false; } - wav->smpls_per_channel = hdr->data_size / (sizeof(int16_t) * hdr->channels); - wav->channels = hdr->channels; - wav->samples = &hdr->first_sample; + uint32_t upsample = 44100 / hdr.sample_rate; // for up-sampling - //fclose(&fd); + if (hdr.sample_bits == 16 && upsample == 1){ + // simple case: slurp samples direct from file to wav buffer + wav->samples.size = hdr.data_size; + wav->samples.ptr = (uintptr_t)malloc(wav->samples.size); + read_file(&fd, (char*)wav->samples.ptr, wav->samples.size); + wav->smpls_per_channel = hdr.data_size / (sizeof(int16_t) * hdr.channels); + wav->channels = hdr.channels; + }else if (hdr.sample_bits == 16){ + transform_16bit(&hdr, wav, upsample, &fd); + }else if (hdr.sample_bits == 8){ + transform_8bit(&hdr, wav, upsample, &fd); + }else{ + //close_file(&fd); + return false; + } + //close_file(&fd); +kprintf("===Samples size %i", wav->samples.size); +kprintf("===Per channel %i", wav->smpls_per_channel); +kprintf("===Channels %i", wav->channels); return true; } + diff --git a/shared/audio/wav.h b/shared/audio/wav.h index f73e0bd5..7dab3d2a 100644 --- a/shared/audio/wav.h +++ b/shared/audio/wav.h @@ -3,8 +3,7 @@ typedef struct wav_data { uint32_t channels; uint32_t smpls_per_channel; - sizedptr file_content; - int16_t* samples; + sizedptr samples; } wav_data; @@ -12,7 +11,7 @@ typedef struct wav_data { extern "C" { #endif -bool wav_load(const char*path, wav_data *wav); +bool wav_load_as_int16(const char*path, wav_data *wav); #ifdef __cplusplus }