diff --git a/CMakeLists.txt b/CMakeLists.txt index d8e38dc..fbe7142 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(base) add_subdirectory(processor) add_subdirectory(display) +add_subdirectory(sound) target_link_libraries(cchip8 PRIVATE SDL2::SDL2 SDL2::SDL2main) # use custom entry point diff --git a/processor/include/processor/processor.h b/processor/include/processor/processor.h index 9fbf628..483f360 100644 --- a/processor/include/processor/processor.h +++ b/processor/include/processor/processor.h @@ -21,6 +21,14 @@ class machine START_ADDRESS = 0x200, }; + enum audio_frequencies + { + TIMER_BEEP = 440, + ERROR_BEEP = 350, + }; + + static constexpr size_t BEEP_DURATION = 20; + enum general_purpose_registers { V0, diff --git a/processor/processor.cpp b/processor/processor.cpp index e3ac7ab..d23fc75 100644 --- a/processor/processor.cpp +++ b/processor/processor.cpp @@ -8,6 +8,7 @@ #include #include "processor/processor.h" +#include "sound/beeper.h" using namespace std; using namespace std::chrono; @@ -15,6 +16,7 @@ using namespace std::chrono; using namespace gsl; using namespace cchip8::processor; +using namespace cchip8::sound; machine::machine() : reg_pc_(START_ADDRESS), @@ -45,8 +47,11 @@ void machine::cycle() if (sound_timer_ > 0) { - // TODO: go off a beep sound_timer_ -= 1; + + beeper b{}; + b.beep(TIMER_BEEP, BEEP_DURATION); + b.wait(); } } @@ -105,8 +110,8 @@ void machine::load(std::string_view filename) void machine::op_default() { - // TODO: logging and/or sounding - int fuck = 0; + beeper b{}; + b.beep(ERROR_BEEP, BEEP_DURATION); } // 00e0: clear the screen diff --git a/sound/CMakeLists.txt b/sound/CMakeLists.txt index 1cb656d..4664b10 100644 --- a/sound/CMakeLists.txt +++ b/sound/CMakeLists.txt @@ -1,3 +1,6 @@ cmake_minimum_required(VERSION 3.21) +target_sources(cchip8 + PRIVATE beeper.cpp) + target_include_directories(cchip8 PRIVATE include) \ No newline at end of file diff --git a/sound/beeper.cpp b/sound/beeper.cpp new file mode 100644 index 0000000..2e2a5b2 --- /dev/null +++ b/sound/beeper.cpp @@ -0,0 +1,100 @@ +// +// Created by cleve on 1/16/2022. +// + +#include "sound/beeper.h" + +#include +#include + +using namespace std; + +cchip8::sound::beeper::beeper() +{ + SDL_AudioSpec spec; + + spec.freq = FREQUENCY; + spec.format = AUDIO_S16SYS; + spec.channels = 1; + spec.samples = 2048; + spec.callback = callback; + spec.userdata = this; + + SDL_AudioSpec obtained; + + // you might want to look for errors here + if (SDL_OpenAudio(&spec, &obtained) != 0) + { + throw runtime_error{ SDL_GetError() }; + } + + // start play audio + SDL_PauseAudio(0); +} + +cchip8::sound::beeper::~beeper() +{ + SDL_CloseAudio(); +} + +void cchip8::sound::beeper::beep(double freq, int duration) +{ + SDL_LockAudio(); + beep_segs_.emplace(freq, duration * FREQUENCY / 1000); + SDL_UnlockAudio(); +} + +void cchip8::sound::beeper::wait() +{ + int size = 0; + do + { + SDL_Delay(20); + SDL_LockAudio(); + size = beep_segs_.size(); + SDL_UnlockAudio(); + } while (size > 0); +} + +void cchip8::sound::beeper::sample(std::span stream) +{ + int i = 0; + while (i < stream.size()) + { + + if (beep_segs_.empty()) + { + while (i < stream.size()) + { + stream[i] = 0; + i++; + } + return; + } + auto& bo = beep_segs_.front(); + + int samples_count = std::min(i + bo.second, (int)stream.size()); + bo.second -= samples_count - i; + + while (i < samples_count) + { + stream[i] = AMPLITUDE * std::sin(v_ * 2 * 3.1415926f / FREQUENCY); + i++; + v_ += bo.first; + } + + if (bo.second == 0) + { + beep_segs_.pop(); + } + } +} + +void cchip8::sound::beeper::callback(void* b, Uint8* s, int len) +{ + auto* stream = (Sint16*)s; + int length = len / 2; + auto* bp = (beeper*)b; + + bp->sample({ stream, (size_t)length }); +} diff --git a/sound/include/sound/beeper.h b/sound/include/sound/beeper.h new file mode 100644 index 0000000..48dcb9d --- /dev/null +++ b/sound/include/sound/beeper.h @@ -0,0 +1,43 @@ +// +// Created by cleve on 1/16/2022. +// + +#pragma once + +#include +#include +#include + +#include +#include + +namespace cchip8::sound +{ +class beeper +{ +public: + static constexpr int AMPLITUDE = 28000; + static constexpr int FREQUENCY = 44100; + + beeper(); + + ~beeper(); + + beeper(beeper&&) = delete; + + beeper(const beeper&) = delete; + + beeper& operator=(const beeper&) = delete; + + void beep(double freq,int duration); + + void wait(); + +private: + void sample(std::span stream); + static void callback(void*,Uint8*,int); + + double v_{}; + std::queue> beep_segs_{}; +}; +} \ No newline at end of file