Skip to content

Commit

Permalink
Refactor audio stream stuff into sound source class
Browse files Browse the repository at this point in the history
  • Loading branch information
aceiii committed Apr 25, 2024
1 parent 4f8560f commit a4ebed9
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 46 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(SOURCE_FILES
src/screen.cpp
src/assembly.cpp
src/keyboard.cpp
src/sound.cpp
src/toml_impl.cpp
)

Expand Down
89 changes: 50 additions & 39 deletions src/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "spdlog/sinks/stdout_color_sinks.h"

#include <algorithm>
#include <array>
#include <filesystem>
#include <fstream>
#include <imgui.h>
Expand All @@ -25,21 +26,24 @@
namespace fs = std::filesystem;

constexpr int kDefaultFPS = 60;
constexpr int kMaxSamplesPerUpdate = 4096;
constexpr int kAudioSampleRate = 44100;
constexpr int kDefaultScreenPixelSize = 4;
constexpr int kDefaultWindowWidth = 1200;
constexpr int kDefaultWindowHeight = 800;

const char* const kWindowTitle = "CHIP-8";
const char* const kSettingsFile = "settings.toml";

AudioStream stream;
static bool should_close = false;
static bool init_dock = true;
static bool rom_loaded = false;
static float sine_idx = 0.0f;

bool play_sound = false;
float sine_idx = 0.0f;
static const char* const sound_source_names[] = {
"Waveform Generator",
"Wave file",
};

bool rom_loaded = false;
static size_t current_source = 0;

static float square(float val) {
if (val > 0) {
Expand Down Expand Up @@ -145,9 +149,8 @@ void Interface::initialize() {
int monitor_height = GetMonitorHeight(monitor);
spdlog::trace("Monitor resolution: {}x{}", monitor_width, monitor_height);

SetAudioStreamBufferSizeDefault(kMaxSamplesPerUpdate);
stream = LoadAudioStream(kAudioSampleRate, 16, 1);
PlayAudioStream(stream);
sound_source = std::make_unique<WaveGeneratorSource>();
sound_source->initialize();

if (settings.lock_fps) {
SetTargetFPS(kDefaultFPS);
Expand Down Expand Up @@ -224,30 +227,8 @@ bool Interface::update() {

keyboard.update();

play_sound = regs->st > 0;

if (IsAudioStreamProcessed(stream)) {
constexpr int frame_count = kMaxSamplesPerUpdate;
const float frequency = 440.0f;

static std::array<short, frame_count> buffer;

float incr = frequency / float(kAudioSampleRate);

for (int i = 0; i < frame_count; i++) {
if (!play_sound) {
buffer[i] = 0;
continue;
}

buffer[i] =
static_cast<short>(((1 << 15) - 1) * square(sinf(2 * PI * sine_idx)));
sine_idx += incr;
if (sine_idx > 1.0f)
sine_idx -= 1.0f;
}

UpdateAudioStream(stream, buffer.data(), frame_count);
if (sound_source) {
sound_source->update(regs->st > 0);
}

screen.update();
Expand Down Expand Up @@ -544,16 +525,42 @@ bool Interface::update() {

if (settings.show_audio) {
if (ImGui::Begin("Audio", &settings.show_audio)) {
if (ImGui::SliderFloat("Volume", &settings.volume, 0.0f, 100.0f, "%.0f")) {
if (ImGui::SliderFloat("Master Volume", &settings.volume, 0.0f, 100.0f, "%.0f")) {
spdlog::debug("Set volume: {}", settings.volume);
SetMasterVolume(settings.volume / 100.0f);
}

std::array<float, 100> samples;
for (int n = 0; n < 100; n += 1) {
samples[n] = sinf(n * 0.2f + ImGui::GetTime() * 5.f);
ImGui::Separator();

if (ImGui::BeginCombo("Source", sound_source_names[current_source])) {
for (int n = 0; n < IM_ARRAYSIZE(sound_source_names); n += 1) {
bool is_selected = current_source == n;
if (ImGui::Selectable(sound_source_names[n], is_selected) && n != current_source) {
current_source = n;

if (sound_source) {
sound_source->cleanup();
}

if (current_source == 0) {
sound_source = std::make_unique<WaveGeneratorSource>();
sound_source->initialize();
} else {
sound_source = nullptr;
}
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}

ImGui::Separator();

if (sound_source) {
sound_source->render();
}
ImGui::PlotLines("Sound", samples.data(), samples.size(), 0, nullptr, -1.5f, 1.5f, ImVec2(0, 80.0f));
}
ImGui::End();
}
Expand Down Expand Up @@ -697,8 +704,12 @@ void Interface::cleanup() {
settings.window_height = GetScreenHeight();

spdlog::info("Cleaning up interface");

if (sound_source) {
sound_source->cleanup();
}

rlImGuiShutdown();
UnloadAudioStream(stream);
CloseAudioDevice();
CloseWindow();
NFD_Quit();
Expand Down
12 changes: 5 additions & 7 deletions src/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "screen.h"
#include "assembly.h"
#include "keyboard.h"
#include "sound.h"

#include <imgui.h>
#include <imgui_memory_editor/imgui_memory_editor.h>
Expand Down Expand Up @@ -67,18 +68,15 @@ class Interface {
void reset_windows();
void set_window_title(const std::string &string);

private:
Interpreter *interpreter;
std::shared_ptr<registers> regs;
MemoryEditor mem_editor;
AppLog app_log;
Screen screen;
AssemblyViewer assembly;
Keyboard keyboard;

std::vector<uint8_t> rom;

Config<interface_settings> config;

bool should_close = false;
bool init_dock = true;
std::vector<uint8_t> rom;
std::shared_ptr<registers> regs;
std::unique_ptr<SoundSource> sound_source;
};
113 changes: 113 additions & 0 deletions src/sound.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "sound.h"

#include <cmath>
#include <spdlog/spdlog.h>
#include <raylib.h>
#include <imgui.h>
#include <random>

void SoundSource::initialize() {
SetAudioStreamBufferSizeDefault(kMaxSamplesPerUpdate);
stream = LoadAudioStream(kAudioSampleRate, kAudioSampleSize, kAudioNumChannels);
PlayAudioStream(stream);
}

void SoundSource::cleanup() {
StopAudioStream(stream);
UnloadAudioStream(stream);
}

void SoundSource::update(bool play_sound) {
if (!IsAudioStreamProcessed(stream)) {
return;
}

gen_sound_data(play_sound, buffer.data(), buffer.size());
UpdateAudioStream(stream, buffer.data(), buffer.size());
}

void WaveGeneratorSource::render() {
static const char* const wave_type_names[] = {
"Sine",
"Square",
"Triangle",
"Sawtooth",
"Noise",
};

if (ImGui::BeginCombo("Type", wave_type_names[wave_type])) {
for (int n = 0; n < IM_ARRAYSIZE(wave_type_names); n += 1) {
bool is_selected = wave_type == n;
if (ImGui::Selectable(wave_type_names[n], is_selected)) {
wave_type = n;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}

std::array<float, 100> samples;
for (int n = 0; n < 100; n += 1) {
samples[n] = sinf(n * 0.2f + ImGui::GetTime() * 5.f);
}
ImGui::PlotLines("Sound", samples.data(), samples.size(), 0, nullptr, -1.5f, 1.5f, ImVec2(0, 80.0f));
ImGui::SliderFloat("Volume", &volume, 0.0f, 100.0f);
ImGui::SliderFloat("Frequency", &frequency, 10.0f, 2048.0f);
ImGui::Checkbox("Force Play", &force_play);
}

static inline float noise(float idx) {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_real_distribution<> dist(-1.0f, 1.0f);
return dist(gen);
}

static inline float sine_wave(float idx) {
return std::sin(2 * PI * idx);
}

static inline float square_wave(float idx) {
float s = sine_wave(idx);
if (std::abs(s) <= 0.001f) {
return 0.f;
}
return std::abs(s) / s;
}

static inline float triangle_wave(float idx) {
return std::abs(fmod((idx - 0.25f), 1.0f) - 0.5f) * 4 - 1;
}

static inline float sawtooth_wave(float idx) {
return 0;
}

void WaveGeneratorSource::gen_sound_data(bool play_sound, short buffer[], size_t buffer_size) {
if (!play_sound && !force_play) {
memset(buffer, 0, sizeof(short) * buffer_size);
return;
}

float incr = frequency / static_cast<float>(kAudioSampleRate);

auto wave_func = ([this]() {
switch (wave_type) {
case 0: return sine_wave;
case 1: return square_wave;
case 2: return triangle_wave;
case 3: return sawtooth_wave;
default: return noise;
}
})();

for (int i = 0; i < buffer_size; i++) {
buffer[i] = static_cast<short>(((1 << 15) - 1) * wave_func(wave_idx) * (volume / 100.0f));

wave_idx += incr;
if (wave_idx > 1.0f)
wave_idx -= 1.0f;
}
}
36 changes: 36 additions & 0 deletions src/sound.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <raylib.h>
#include <array>

constexpr int kMaxSamplesPerUpdate = 4096;
constexpr int kAudioSampleRate = 44100;
constexpr int kAudioSampleSize = 16;
constexpr int kAudioNumChannels = 1;

class SoundSource {
public:
virtual void render() = 0;
virtual void gen_sound_data(bool play_sound, short buffer[], size_t buffer_size) = 0;

virtual void initialize();
void update(bool play_sound);
virtual void cleanup();

private:
AudioStream stream;
std::array<short, kMaxSamplesPerUpdate> buffer;
};

class WaveGeneratorSource : public SoundSource {
public:
virtual void render() override;
virtual void gen_sound_data(bool play_sound, short buffer[], size_t buffer_size) override;

private:
bool force_play = false;
float frequency = 440.0f;
float volume = 100.0f;
float wave_idx = 0.0f;
int wave_type = 0;
};

0 comments on commit a4ebed9

Please sign in to comment.