Skip to content
4 changes: 2 additions & 2 deletions kernel/audio/OutputAudioDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
}
117 changes: 85 additions & 32 deletions kernel/audio/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -22,47 +23,99 @@ 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/50.f);
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;

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 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;
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;
}
}

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);
}
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 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;
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 / (3 - channels); // TODO: There must be a better way...
}
}

void play_startup(){
wav_data wav = {};
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);
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){
Expand All @@ -76,4 +129,4 @@ driver_module audio_module = (driver_module){
.write = 0,
.seek = 0,
.readdir = 0,
};
};
14 changes: 8 additions & 6 deletions kernel/audio/virtio_audio_pci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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;
}

Expand All @@ -95,15 +97,15 @@ bool VirtioAudioDriver::init(){
pci_enable_device(addr);

if (!virtio_init_device(&audio_dev)){
kprintf("Failed disk initialization");
kprintf("[VIRTIO_AUDIO] Failed initialization");
return false;
}

select_queue(&audio_dev, EVENT_QUEUE);

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
Expand Down Expand Up @@ -169,7 +171,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;
}

Expand All @@ -179,7 +181,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);
Expand Down
16 changes: 8 additions & 8 deletions shared/audio/cuatro.c
Original file line number Diff line number Diff line change
@@ -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;
}
13 changes: 11 additions & 2 deletions shared/audio/cuatro.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading