Agregando sonido
This commit is contained in:
@@ -7,6 +7,8 @@ target_sources(
|
|||||||
main.cpp
|
main.cpp
|
||||||
Machine.cpp
|
Machine.cpp
|
||||||
Machine.h
|
Machine.h
|
||||||
|
Sound.cpp
|
||||||
|
Sound.h
|
||||||
Graphics/Graphics.cpp
|
Graphics/Graphics.cpp
|
||||||
Graphics/Graphics.h
|
Graphics/Graphics.h
|
||||||
Graphics/Color.h
|
Graphics/Color.h
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ void Graphics::create_sdl()
|
|||||||
{
|
{
|
||||||
SDL_SetAppMetadata("CHIP-8 Emulator", "0.0.1", "fun.skrd.chip8");
|
SDL_SetAppMetadata("CHIP-8 Emulator", "0.0.1", "fun.skrd.chip8");
|
||||||
|
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO))
|
||||||
{
|
{
|
||||||
throw std::runtime_error(std::format("Couldn't initialize SDL: {}", SDL_GetError()));
|
throw std::runtime_error(std::format("Couldn't initialize SDL: {}", SDL_GetError()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ Interpreter::Interpreter(std::shared_ptr<MachineState> machine_state) :
|
|||||||
random_generator{std::random_device{}()}
|
random_generator{std::random_device{}()}
|
||||||
{
|
{
|
||||||
srand(time(nullptr));
|
srand(time(nullptr));
|
||||||
|
// quirks = static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
|
||||||
for (bool& display : this->machine_state->display)
|
for (bool& display : this->machine_state->display)
|
||||||
{
|
{
|
||||||
display = rand() % 100 > 50;
|
display = rand() % 100 > 50;
|
||||||
@@ -345,16 +346,19 @@ void Interpreter::ld_vx_vy(const Instruction& instruction) const
|
|||||||
void Interpreter::or_vx_vy(const Instruction& instruction) const
|
void Interpreter::or_vx_vy(const Instruction& instruction) const
|
||||||
{
|
{
|
||||||
this->machine_state->v[instruction.x] |= this->machine_state->v[instruction.y];
|
this->machine_state->v[instruction.x] |= this->machine_state->v[instruction.y];
|
||||||
|
this->machine_state->v[0xF] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::and_vx_vy(const Instruction& instruction) const
|
void Interpreter::and_vx_vy(const Instruction& instruction) const
|
||||||
{
|
{
|
||||||
this->machine_state->v[instruction.x] &= this->machine_state->v[instruction.y];
|
this->machine_state->v[instruction.x] &= this->machine_state->v[instruction.y];
|
||||||
|
this->machine_state->v[0xF] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::xor_vx_vy(const Instruction& instruction) const
|
void Interpreter::xor_vx_vy(const Instruction& instruction) const
|
||||||
{
|
{
|
||||||
this->machine_state->v[instruction.x] ^= this->machine_state->v[instruction.y];
|
this->machine_state->v[instruction.x] ^= this->machine_state->v[instruction.y];
|
||||||
|
this->machine_state->v[0xF] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::add_vx_vy(const Instruction& instruction) const
|
void Interpreter::add_vx_vy(const Instruction& instruction) const
|
||||||
@@ -531,22 +535,39 @@ void Interpreter::ld_vx_dt(const Instruction& instruction) const
|
|||||||
this->machine_state->v[instruction.x] = this->machine_state->dt;
|
this->machine_state->v[instruction.x] = this->machine_state->dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::ld_vx_k(const Instruction& instruction) const
|
void Interpreter::ld_vx_k(const Instruction& instruction)
|
||||||
{
|
{
|
||||||
if (this->machine_state->keyboard == 0)
|
if (this->machine_state->keyboard == 0 && this->wait_for_key_released == 0)
|
||||||
{
|
{
|
||||||
this->machine_state->pc -= 2;
|
this->machine_state->pc -= 2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto key = 0; key < 16; key++)
|
if (this->machine_state->keyboard != 0 && this->wait_for_key_released == 0)
|
||||||
{
|
{
|
||||||
if (this->machine_state->keyboard & 1 << key)
|
this->wait_for_key_released = this->machine_state->keyboard;
|
||||||
|
this->machine_state->pc -= 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->wait_for_key_released != 0)
|
||||||
|
{
|
||||||
|
const auto released_keys = this->wait_for_key_released & (~this->machine_state->keyboard);
|
||||||
|
if (released_keys != 0)
|
||||||
{
|
{
|
||||||
this->machine_state->v[instruction.x] = key;
|
for (auto key = 0; key < 16; key++)
|
||||||
break;
|
{
|
||||||
|
if (released_keys & 1 << key)
|
||||||
|
{
|
||||||
|
this->machine_state->v[instruction.x] = key;
|
||||||
|
this->wait_for_key_released = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->machine_state->pc -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::ld_dt_vx(const Instruction& instruction) const
|
void Interpreter::ld_dt_vx(const Instruction& instruction) const
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class Interpreter
|
|||||||
std::mt19937 random_generator;
|
std::mt19937 random_generator;
|
||||||
|
|
||||||
uint8_t quirks = 0;
|
uint8_t quirks = 0;
|
||||||
|
uint16_t wait_for_key_released = false;
|
||||||
|
|
||||||
void execute_instruction(const Instruction& instruction);
|
void execute_instruction(const Instruction& instruction);
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ class Interpreter
|
|||||||
void skp_vx(const Instruction& instruction) const;
|
void skp_vx(const Instruction& instruction) const;
|
||||||
void sknp_vx(const Instruction& instruction) const;
|
void sknp_vx(const Instruction& instruction) const;
|
||||||
void ld_vx_dt(const Instruction& instruction) const;
|
void ld_vx_dt(const Instruction& instruction) const;
|
||||||
void ld_vx_k(const Instruction& instruction) const;
|
void ld_vx_k(const Instruction& instruction);
|
||||||
void ld_dt_vx(const Instruction& instruction) const;
|
void ld_dt_vx(const Instruction& instruction) const;
|
||||||
void ld_st_vx(const Instruction& instruction) const;
|
void ld_st_vx(const Instruction& instruction) const;
|
||||||
void add_i_vx(const Instruction& instruction) const;
|
void add_i_vx(const Instruction& instruction) const;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
Machine::Machine() :
|
Machine::Machine() :
|
||||||
machine_state{std::make_shared<MachineState>()},
|
machine_state{std::make_shared<MachineState>()},
|
||||||
graphics{std::make_shared<Graphics>()},
|
graphics{std::make_shared<Graphics>()},
|
||||||
|
sound{std::make_unique<Sound>()},
|
||||||
callback_manager(std::make_shared<CallbackManager>()),
|
callback_manager(std::make_shared<CallbackManager>()),
|
||||||
|
|
||||||
interpreter{std::make_shared<Interpreter>(this->machine_state)},
|
interpreter{std::make_shared<Interpreter>(this->machine_state)},
|
||||||
@@ -26,6 +27,9 @@ Machine::Machine() :
|
|||||||
last_update_time{0},
|
last_update_time{0},
|
||||||
accumulator{0},
|
accumulator{0},
|
||||||
target_cycle_time{1.0 / this->ips},
|
target_cycle_time{1.0 / this->ips},
|
||||||
|
last_timer_time{0},
|
||||||
|
timer_accumulator{0},
|
||||||
|
timer_cycle_time{1.0 / 60},
|
||||||
running{false}
|
running{false}
|
||||||
{
|
{
|
||||||
this->register_callbacks();
|
this->register_callbacks();
|
||||||
@@ -36,9 +40,14 @@ void Machine::iterate()
|
|||||||
{
|
{
|
||||||
if (running)
|
if (running)
|
||||||
{
|
{
|
||||||
|
this->update_timers();
|
||||||
this->execute_interpreter();
|
this->execute_interpreter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (machine_state->st > 0)
|
||||||
|
{
|
||||||
|
this->sound->square();
|
||||||
|
}
|
||||||
this->graphics->start();
|
this->graphics->start();
|
||||||
this->ui_manager->render();
|
this->ui_manager->render();
|
||||||
this->graphics->end();
|
this->graphics->end();
|
||||||
@@ -158,6 +167,21 @@ void Machine::execute_interpreter()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Machine::update_timers()
|
||||||
|
{
|
||||||
|
const auto current_time = SDL_GetTicks();
|
||||||
|
const auto delta_time = static_cast<double>(current_time - this->last_update_time) / 1000.0;
|
||||||
|
this->last_timer_time = current_time;
|
||||||
|
this->timer_accumulator += delta_time;
|
||||||
|
|
||||||
|
while (this->timer_accumulator >= this->timer_cycle_time)
|
||||||
|
{
|
||||||
|
machine_state->dt = machine_state->dt > 0 ? machine_state->dt - 1 : 0;
|
||||||
|
machine_state->st = machine_state->st > 0 ? machine_state->st - 1 : 0;
|
||||||
|
this->timer_accumulator -= this->timer_cycle_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Machine::register_callbacks()
|
void Machine::register_callbacks()
|
||||||
{
|
{
|
||||||
callback_manager->rom_load_callback.push_back(std::bind(
|
callback_manager->rom_load_callback.push_back(std::bind(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef MACHINE_H
|
#ifndef MACHINE_H
|
||||||
#define MACHINE_H
|
#define MACHINE_H
|
||||||
|
|
||||||
|
#include "Sound.h"
|
||||||
#include "Graphics/Graphics.h"
|
#include "Graphics/Graphics.h"
|
||||||
#include "Interpreter/Interpreter.h"
|
#include "Interpreter/Interpreter.h"
|
||||||
#include "SDL3/SDL_events.h"
|
#include "SDL3/SDL_events.h"
|
||||||
@@ -11,6 +12,7 @@ class Machine
|
|||||||
{
|
{
|
||||||
std::shared_ptr<MachineState> machine_state;
|
std::shared_ptr<MachineState> machine_state;
|
||||||
std::shared_ptr<Graphics> graphics;
|
std::shared_ptr<Graphics> graphics;
|
||||||
|
std::unique_ptr<Sound> sound;
|
||||||
std::shared_ptr<CallbackManager> callback_manager;
|
std::shared_ptr<CallbackManager> callback_manager;
|
||||||
|
|
||||||
std::shared_ptr<Interpreter> interpreter;
|
std::shared_ptr<Interpreter> interpreter;
|
||||||
@@ -21,10 +23,16 @@ class Machine
|
|||||||
double accumulator;
|
double accumulator;
|
||||||
double target_cycle_time;
|
double target_cycle_time;
|
||||||
|
|
||||||
|
uint64_t last_timer_time;
|
||||||
|
double timer_accumulator;
|
||||||
|
double timer_cycle_time;
|
||||||
|
|
||||||
std::string rom;
|
std::string rom;
|
||||||
bool running;
|
bool running;
|
||||||
|
|
||||||
void execute_interpreter();
|
void execute_interpreter();
|
||||||
|
void update_timers();
|
||||||
|
|
||||||
void on_rom_load(const std::string& path);
|
void on_rom_load(const std::string& path);
|
||||||
void on_reset() const;
|
void on_reset() const;
|
||||||
void on_stop();
|
void on_stop();
|
||||||
|
|||||||
88
src/Sound.cpp
Normal file
88
src/Sound.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#include "Sound.h"
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
#include "SDL3/SDL.h"
|
||||||
|
|
||||||
|
void SDLAudioStreamDestroyer::operator()(SDL_AudioStream* stream) const
|
||||||
|
{
|
||||||
|
SDL_DestroyAudioStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound::Sound()
|
||||||
|
{
|
||||||
|
spec.channels = 1;
|
||||||
|
spec.format = SDL_AUDIO_F32;
|
||||||
|
spec.freq = 8000;
|
||||||
|
|
||||||
|
SDL_AudioStream* raw_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, nullptr, nullptr);
|
||||||
|
if (!raw_stream)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::format("Couldn't initialize audio stream: {}", SDL_GetError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = std::unique_ptr<SDL_AudioStream, SDLAudioStreamDestroyer>(raw_stream);
|
||||||
|
|
||||||
|
|
||||||
|
SDL_ResumeAudioStreamDevice(stream.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::sine()
|
||||||
|
{
|
||||||
|
constexpr int minimum_audio = (8000 * sizeof(float)) / 6;
|
||||||
|
constexpr int freq = 440;
|
||||||
|
|
||||||
|
if (SDL_GetAudioStreamQueued(stream.get()) < minimum_audio)
|
||||||
|
{
|
||||||
|
static float samples[512];
|
||||||
|
for (std::size_t i = 0; i < std::size(samples); i++)
|
||||||
|
{
|
||||||
|
const float phase = current_sine_sample * freq / 8000.0f;
|
||||||
|
samples[i] = SDL_sinf(phase * 2 * SDL_PI_F);
|
||||||
|
current_sine_sample++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_sine_sample %= 8000;
|
||||||
|
|
||||||
|
SDL_PutAudioStreamData(stream.get(), samples, sizeof (samples));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::noise()
|
||||||
|
{
|
||||||
|
constexpr int minimum_audio = (8000 * sizeof(float)) / 6;
|
||||||
|
constexpr int freq = 440;
|
||||||
|
if (SDL_GetAudioStreamQueued(stream.get()) < minimum_audio)
|
||||||
|
{
|
||||||
|
static float samples[512];
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < std::size(samples); i++)
|
||||||
|
{
|
||||||
|
samples[i] = SDL_rand(2) * freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PutAudioStreamData(stream.get(), samples, sizeof (samples));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::square()
|
||||||
|
{
|
||||||
|
constexpr int minimum_audio = (8000 * sizeof(float)) / 6;
|
||||||
|
constexpr int freq = 440;
|
||||||
|
constexpr float amplitude = 0.5f;
|
||||||
|
|
||||||
|
if (SDL_GetAudioStreamQueued(stream.get()) < minimum_audio)
|
||||||
|
{
|
||||||
|
static float samples[512];
|
||||||
|
for (std::size_t i = 0; i < std::size(samples); i++)
|
||||||
|
{
|
||||||
|
const float phase = current_sine_sample * freq / 8000.0f;
|
||||||
|
samples[i] = (SDL_sinf(phase * 2 * SDL_PI_F) >= 0.0f) ? amplitude : -amplitude;
|
||||||
|
current_sine_sample++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_sine_sample %= 8000;
|
||||||
|
|
||||||
|
SDL_PutAudioStreamData(stream.get(), samples, sizeof(samples));
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Sound.h
Normal file
28
src/Sound.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef SOUND_H
|
||||||
|
#define SOUND_H
|
||||||
|
#include <memory>
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct SDLAudioStreamDestroyer
|
||||||
|
{
|
||||||
|
void operator()(SDL_AudioStream* stream) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Sound
|
||||||
|
{
|
||||||
|
SDL_AudioSpec spec;
|
||||||
|
std::unique_ptr<SDL_AudioStream, SDLAudioStreamDestroyer> stream;
|
||||||
|
|
||||||
|
int current_sine_sample = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Sound();
|
||||||
|
|
||||||
|
void sine();
|
||||||
|
void noise();
|
||||||
|
void square();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SOUND_H
|
||||||
Reference in New Issue
Block a user