Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 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 Down
124 changes: 86 additions & 38 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 @@ -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){
Expand Down
16 changes: 9 additions & 7 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 @@ -107,15 +109,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 @@ -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;
}

Expand All @@ -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);
Expand All @@ -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++;
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 7 additions & 7 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
2 changes: 2 additions & 0 deletions shared/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading