From e0fc6123fc1f114d2ae1967cf64915f6eccc6b24 Mon Sep 17 00:00:00 2001 From: Samuel Walker Date: Sat, 1 Feb 2025 23:58:55 -0700 Subject: [PATCH] basic audio stream. --- .gitmodules | 3 ++ CMakeLists.txt | 1 + deps/portaudio | 1 + include/audio.h | 14 ++++++ lib/CMakeLists.txt | 4 +- lib/audio.c | 119 +++++++++++++++++++++++++++++++++++++++++++++ lib/emu.c | 2 + lib/ppu_pipeline.c | 1 - 8 files changed, 142 insertions(+), 3 deletions(-) create mode 160000 deps/portaudio create mode 100644 include/audio.h create mode 100644 lib/audio.c diff --git a/.gitmodules b/.gitmodules index 22cc8ff..9b62657 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,6 @@ [submodule "deps/SDL_ttf"] path = deps/SDL_ttf url = https://github.com/libsdl-org/SDL_ttf.git +[submodule "deps/portaudio"] + path = deps/portaudio + url = https://github.com/PortAudio/portaudio.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f95680a..3699dc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.10) set(BUILD_SHARED_LIBS OFF) add_subdirectory(deps/SDL) add_subdirectory(deps/SDL_ttf) +add_subdirectory(deps/portaudio) add_subdirectory(lib/) add_subdirectory(gbemu/) add_subdirectory(deps/check/) diff --git a/deps/portaudio b/deps/portaudio new file mode 160000 index 0000000..aa1cfb0 --- /dev/null +++ b/deps/portaudio @@ -0,0 +1 @@ +Subproject commit aa1cfb046f93b18db4d12986ddbff84cbaa952cb diff --git a/include/audio.h b/include/audio.h new file mode 100644 index 0000000..b7556f9 --- /dev/null +++ b/include/audio.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct { + float sq1_freq; + float sq1_duty; + float sq1_amp; + float sq2_freq; + float sq2_duty; + float sq2_amp; +} audio_context; + +void audio_init(); \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a831290..21bdfdc 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -2,5 +2,5 @@ cmake_minimum_required(VERSION 3.10) project(Lib) file(GLOB SRCS *.c) add_library(Lib STATIC ${SRCS}) -target_include_directories(Lib PUBLIC ../include ../deps/SDL/include ../deps/SDL_ttf) -target_link_libraries(Lib SDL2-static SDL2_ttf::SDL2_ttf-static) \ No newline at end of file +target_include_directories(Lib PUBLIC ../include ../deps/SDL/include ../deps/SDL_ttf ../deps/portaudio/include) +target_link_libraries(Lib SDL2-static SDL2_ttf::SDL2_ttf-static portaudio) \ No newline at end of file diff --git a/lib/audio.c b/lib/audio.c new file mode 100644 index 0000000..ddf11d3 --- /dev/null +++ b/lib/audio.c @@ -0,0 +1,119 @@ +#include +#include + +#include + +#define SAMPLE_RATE 44100 +#define FRAMES_PER_BUFFER 64 +#define TABLE_SIZE 44100 + +#define M_PI 3.14159265 + +static float sine[TABLE_SIZE]; +static unsigned long ind = 0; +static unsigned long ind2 = 0; + +static int sq1_on_time; +static int sq1_off_time; +static float sq1 = 1; +static int sq1_timer = 0; + +static int sq2_on_time; +static int sq2_off_time; +static float sq2 = 1; +static int sq2_timer = 0; + +static audio_context ctx; + +static int audio_callback(const void* input_uffer, void *output_buffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *time_info, + PaStreamCallbackFlags status_flags, + void *userData ) { + //printf("Audio Callback!\n"); + audio_context *ctx = (audio_context *) userData; + float *out = (float *)output_buffer; + PaTime time = time_info->currentTime; + sq1_on_time = 1/(ctx->sq1_freq/(float)SAMPLE_RATE) * ctx->sq1_duty; + sq1_off_time = 1/(ctx->sq1_freq/(float)SAMPLE_RATE) * (1 - ctx->sq1_duty); + + sq2_on_time = 1/(ctx->sq2_freq/(float)SAMPLE_RATE) * ctx->sq2_duty; + sq2_off_time = 1/(ctx->sq2_freq/(float)SAMPLE_RATE) * (1 - ctx->sq2_duty); + for(int i = 0; i < framesPerBuffer; i++) { + float val = 0; + //handle audio channels + //val += 0.2 * sine[ind]; + //ind += 500; + if((sq1_timer >= sq1_on_time && sq1 == 1)) { + sq1 = 0; + sq1_timer = 0; + } else if((sq1_timer >= sq1_off_time && sq1 == 0)){ + sq1 = 1; + sq1_timer = 0; + } + sq1_timer++; + val += ctx->sq1_amp * sq1; + + if((sq2_timer >= sq2_on_time && sq2 == 1)) { + sq2 = 0; + sq2_timer = 0; + } else if((sq2_timer >= sq2_off_time && sq2 == 0)){ + sq2 = 1; + sq2_timer = 0; + } + sq2_timer++; + val += ctx->sq2_amp * sq2; + //val += 0.2 * sine[ind2]; + //ind2 += 200; + if(ind >= TABLE_SIZE) ind -= TABLE_SIZE; + if(ind2 >= TABLE_SIZE) ind2 -= TABLE_SIZE; + *out++ = val; + } + return paContinue; +} + +void audio_init(){ + PaStreamParameters output_parameters; + PaStream *stream; + PaError err; + + ctx.sq1_amp = 0; + ctx.sq1_duty = 0; + ctx.sq1_freq = 0; + + ctx.sq2_amp = 0; + ctx.sq2_duty = 0; + ctx.sq2_freq = 0; + + for(int i = 0; i < TABLE_SIZE; i++) { + sine[i] = (float) sin(((double)i/(double)TABLE_SIZE) * M_PI * 2.); + } + + err = Pa_Initialize(); + if (err != paNoError) goto error; + output_parameters.device = Pa_GetDefaultOutputDevice(); + if(output_parameters.device == paNoDevice) { + fprintf(stderr, "No default audio device!\n"); + goto error; + } + output_parameters.channelCount = 1; + output_parameters.sampleFormat = paFloat32; + output_parameters.suggestedLatency = Pa_GetDeviceInfo(output_parameters.device)->defaultLowOutputLatency; + output_parameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream(&stream, + NULL, + &output_parameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + NULL, + audio_callback, + &ctx); + if(err != paNoError) goto error; + err = Pa_StartStream(stream); + if(err != paNoError) goto error; + return; +error: + Pa_Terminate(); + fprintf(stderr, "portaudio stream error\n\tError Number: %d\n\tError Message: %s\n", err, Pa_GetErrorText(err)); +} \ No newline at end of file diff --git a/lib/emu.c b/lib/emu.c index 4f1e896..17aa208 100644 --- a/lib/emu.c +++ b/lib/emu.c @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -25,6 +26,7 @@ DWORD WINAPI cpu_run(void *p) { ppu_init(); timer_init(); cpu_init(); + audio_init(); ctx.running = true; ctx.paused = false; diff --git a/lib/ppu_pipeline.c b/lib/ppu_pipeline.c index 7deea90..5fcf8a0 100644 --- a/lib/ppu_pipeline.c +++ b/lib/ppu_pipeline.c @@ -260,7 +260,6 @@ void pipeline_push_pixel() { ppu_get_context()->rendering_window = true; pipeline_fifo_reset(); ppu_get_context()->pfc.cur_fetch_state = FS_TILE; - printf("Window X: %d\n", lcd_get_context()->win_x); ppu_get_context()->pfc.fetch_x = 0; ppu_get_context()->pfc.fifo_x = 0; return;