Commit d2da05b7 authored by Jack's avatar Jack
Browse files

working on APU sfx queue, moved Memory to its own file

parent 4ae08092
......@@ -172,6 +172,7 @@
<ClCompile Include="..\..\..\src\vm\gfx.cpp" />
<ClCompile Include="..\..\..\src\vm\lua_bridge.cpp" />
<ClCompile Include="..\..\..\src\vm\machine.cpp" />
<ClCompile Include="..\..\..\src\vm\memory.cpp" />
<ClCompile Include="..\..\..\src\vm\sound.cpp" />
</ItemGroup>
<ItemGroup>
......@@ -213,6 +214,7 @@
<ClInclude Include="..\..\..\src\vm\gfx.h" />
<ClInclude Include="..\..\..\src\vm\lua_bridge.h" />
<ClInclude Include="..\..\..\src\vm\machine.h" />
<ClInclude Include="..\..\..\src\vm\memory.h" />
<ClInclude Include="..\..\..\src\vm\sound.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
......
......@@ -138,6 +138,9 @@
<ClInclude Include="..\..\..\src\io\stegano.h">
<Filter>src\io</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\vm\memory.h">
<Filter>src\vm</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\main.cpp">
......@@ -272,5 +275,8 @@
<ClCompile Include="..\..\..\src\io\stegano.cpp">
<Filter>src\io</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\vm\memory.cpp">
<Filter>src\vm</Filter>
</ClCompile>
</ItemGroup>
</Project>
\ No newline at end of file
......@@ -5,6 +5,7 @@
#include "gfx.h"
#include "sound.h"
#include "lua_bridge.h"
#include "memory.h"
#include <SDL.h>
#include <array>
......@@ -21,89 +22,6 @@ namespace retro8
bit_mask<button_t> previousButtons;
};
namespace address
{
static constexpr address_t SPRITE_SHEET = 0x0000;
static constexpr address_t SPRITE_FLAGS = 0x3000;
static constexpr address_t CART_DATA = 0x5e00;
static constexpr address_t PALETTES = 0x5f00;
static constexpr address_t CLIP_RECT = 0x5f20;
static constexpr address_t PEN_COLOR = 0x5f25;
static constexpr address_t CURSOR = 0x5f26;
static constexpr address_t CAMERA = 0x5f28;
static constexpr address_t SCREEN_DATA = 0x6000;
static constexpr address_t TILE_MAP_LOW = 0x1000;
static constexpr address_t TILE_MAP_HIGH = 0x2000;
};
class Memory
{
private:
uint8_t memory[1024 * 32];
static constexpr size_t BYTES_PER_PALETTE = sizeof(retro8::gfx::palette_t);
static constexpr size_t BYTES_PER_SPRITE = sizeof(retro8::gfx::sprite_t);
static constexpr size_t ROWS_PER_TILE_MAP_HALF = 32;
public:
Memory()
{
memset(memory, 0, 1024 * 32);
paletteAt(gfx::DRAW_PALETTE_INDEX)->reset();
paletteAt(gfx::SCREEN_PALETTE_INDEX)->reset();
clipRect()->reset();
cursor()->reset();
}
uint8_t* base() { return memory; }
gfx::color_byte_t* penColor() { return as<gfx::color_byte_t>(address::PEN_COLOR); }
gfx::cursor_t* cursor() { return as<gfx::cursor_t>(address::CURSOR); }
gfx::camera_t* camera() { return as<gfx::camera_t>(address::CAMERA); }
gfx::clip_rect_t* clipRect() { return as<gfx::clip_rect_t>(address::CLIP_RECT); }
gfx::color_byte_t* spriteSheet(coord_t x, coord_t y) { return spriteSheet() + x / gfx::PIXEL_TO_BYTE_RATIO + y * gfx::SPRITE_SHEET_PITCH; }
gfx::color_byte_t* spriteSheet() { return as<gfx::color_byte_t>(address::SPRITE_SHEET); }
gfx::color_byte_t* screenData() { return as<gfx::color_byte_t>(address::SCREEN_DATA); }
gfx::color_byte_t* screenData(coord_t x, coord_t y) { return screenData() + y * gfx::SCREEN_PITCH + x / gfx::PIXEL_TO_BYTE_RATIO; }
integral_t* cartData(index_t idx) { return as<integral_t>(address::CART_DATA + idx * sizeof(integral_t)); } //TODO: ENDIANNESS!!
sprite_flags_t* spriteFlagsFor(sprite_index_t index)
{
return as<sprite_flags_t>(address::SPRITE_FLAGS + index);
}
sprite_index_t* spriteInTileMap(coord_t x, coord_t y)
{
static_assert(sizeof(sprite_index_t) == 1, "sprite_index_t must be 1 byte");
sprite_index_t *addr;
if (y >= ROWS_PER_TILE_MAP_HALF)
addr = as<sprite_index_t>(address::TILE_MAP_LOW) + x + (y - ROWS_PER_TILE_MAP_HALF) * gfx::TILE_MAP_WIDTH * sizeof(sprite_index_t);
else
addr = as<sprite_index_t>(address::TILE_MAP_HIGH) + x + y * gfx::TILE_MAP_WIDTH * sizeof(sprite_index_t);
assert((addr >= memory + address::TILE_MAP_LOW && addr <= memory + address::TILE_MAP_LOW * gfx::TILE_MAP_WIDTH * gfx::TILE_MAP_HEIGHT * sizeof(sprite_index_t)));
return addr;
}
gfx::sprite_t* spriteAt(sprite_index_t index) {
return reinterpret_cast<gfx::sprite_t*>(&memory[address::SPRITE_SHEET
+ (index % gfx::SPRITES_PER_SPRITE_SHEET_ROW) * gfx::SPRITE_BYTES_PER_SPRITE_ROW]
+ (index / gfx::SPRITES_PER_SPRITE_SHEET_ROW) * gfx::SPRITE_SHEET_PITCH * gfx::SPRITE_HEIGHT
); }
gfx::palette_t* paletteAt(palette_index_t index) { return reinterpret_cast<gfx::palette_t*>(&memory[address::PALETTES + index * BYTES_PER_PALETTE]); }
template<typename T> T* as(address_t addr) { return reinterpret_cast<T*>(&memory[addr]); }
};
class Machine
{
private:
......@@ -122,7 +40,7 @@ namespace retro8
public:
Machine()
Machine() : _sound(_memory)
{
}
......
#include "memory.h"
\ No newline at end of file
#pragma once
#include "common.h"
#include "defines.h"
#include "gfx.h"
#include "sound.h"
#include "lua_bridge.h"
#include <SDL.h>
#include <array>
#include <random>
namespace retro8
{
namespace address
{
static constexpr address_t SPRITE_SHEET = 0x0000;
static constexpr address_t SPRITE_FLAGS = 0x3000;
static constexpr address_t CART_DATA = 0x5e00;
static constexpr address_t PALETTES = 0x5f00;
static constexpr address_t CLIP_RECT = 0x5f20;
static constexpr address_t PEN_COLOR = 0x5f25;
static constexpr address_t CURSOR = 0x5f26;
static constexpr address_t CAMERA = 0x5f28;
static constexpr address_t SCREEN_DATA = 0x6000;
static constexpr address_t TILE_MAP_LOW = 0x1000;
static constexpr address_t TILE_MAP_HIGH = 0x2000;
};
class Memory
{
private:
uint8_t memory[1024 * 32];
static constexpr size_t BYTES_PER_PALETTE = sizeof(retro8::gfx::palette_t);
static constexpr size_t BYTES_PER_SPRITE = sizeof(retro8::gfx::sprite_t);
static constexpr size_t ROWS_PER_TILE_MAP_HALF = 32;
public:
Memory()
{
memset(memory, 0, 1024 * 32);
paletteAt(gfx::DRAW_PALETTE_INDEX)->reset();
paletteAt(gfx::SCREEN_PALETTE_INDEX)->reset();
clipRect()->reset();
cursor()->reset();
}
uint8_t* base() { return memory; }
gfx::color_byte_t* penColor() { return as<gfx::color_byte_t>(address::PEN_COLOR); }
gfx::cursor_t* cursor() { return as<gfx::cursor_t>(address::CURSOR); }
gfx::camera_t* camera() { return as<gfx::camera_t>(address::CAMERA); }
gfx::clip_rect_t* clipRect() { return as<gfx::clip_rect_t>(address::CLIP_RECT); }
gfx::color_byte_t* spriteSheet(coord_t x, coord_t y) { return spriteSheet() + x / gfx::PIXEL_TO_BYTE_RATIO + y * gfx::SPRITE_SHEET_PITCH; }
gfx::color_byte_t* spriteSheet() { return as<gfx::color_byte_t>(address::SPRITE_SHEET); }
gfx::color_byte_t* screenData() { return as<gfx::color_byte_t>(address::SCREEN_DATA); }
gfx::color_byte_t* screenData(coord_t x, coord_t y) { return screenData() + y * gfx::SCREEN_PITCH + x / gfx::PIXEL_TO_BYTE_RATIO; }
integral_t* cartData(index_t idx) { return as<integral_t>(address::CART_DATA + idx * sizeof(integral_t)); } //TODO: ENDIANNESS!!
sprite_flags_t* spriteFlagsFor(sprite_index_t index)
{
return as<sprite_flags_t>(address::SPRITE_FLAGS + index);
}
sprite_index_t* spriteInTileMap(coord_t x, coord_t y)
{
static_assert(sizeof(sprite_index_t) == 1, "sprite_index_t must be 1 byte");
sprite_index_t *addr;
if (y >= ROWS_PER_TILE_MAP_HALF)
addr = as<sprite_index_t>(address::TILE_MAP_LOW) + x + (y - ROWS_PER_TILE_MAP_HALF) * gfx::TILE_MAP_WIDTH * sizeof(sprite_index_t);
else
addr = as<sprite_index_t>(address::TILE_MAP_HIGH) + x + y * gfx::TILE_MAP_WIDTH * sizeof(sprite_index_t);
assert((addr >= memory + address::TILE_MAP_LOW && addr <= memory + address::TILE_MAP_LOW * gfx::TILE_MAP_WIDTH * gfx::TILE_MAP_HEIGHT * sizeof(sprite_index_t)));
return addr;
}
gfx::sprite_t* spriteAt(sprite_index_t index) {
return reinterpret_cast<gfx::sprite_t*>(&memory[address::SPRITE_SHEET
+ (index % gfx::SPRITES_PER_SPRITE_SHEET_ROW) * gfx::SPRITE_BYTES_PER_SPRITE_ROW]
+ (index / gfx::SPRITES_PER_SPRITE_SHEET_ROW) * gfx::SPRITE_SHEET_PITCH * gfx::SPRITE_HEIGHT
); }
gfx::palette_t* paletteAt(palette_index_t index) { return reinterpret_cast<gfx::palette_t*>(&memory[address::PALETTES + index * BYTES_PER_PALETTE]); }
template<typename T> T* as(address_t addr) { return reinterpret_cast<T*>(&memory[addr]); }
};
}
\ No newline at end of file
#include "sound.h"
#include <random>
#include <cassert>
using namespace retro8;
using namespace retro8::sfx;
......@@ -255,28 +256,92 @@ void APU::close()
SDL_CloseAudioDevice(device);
}
void APU::play(sound_index_t index, channel_index_t channel, uint32_t start, uint32_t end)
{
queueMutex.lock();
queue.push_back({ index, channel, start, end });
queueMutex.unlock();
}
void APU::handleCommands()
{
if (queueMutex.try_lock())
{
for (Command& c : queue)
{
/* stop sound on channel*/
if (c.index == -1)
{
assert(c.channel >= 0 && c.channel <= channels.size());
channels[c.channel].sound = nullptr;
continue;
}
/* stop sound from looping */
else if (c.index == -2)
{
continue;
}
/* stop sound on all channels that are playing it*/
else if (c.channel == -2)
{
for (auto& chan : channels)
if (chan.soundIndex == c.index)
chan.sound = nullptr;
continue;
}
/* find first available channel*/
else if (c.channel == -1)
for (size_t i = 0; channels.size(); ++i)
if (!channels[i].sound)
c.channel = i;
if (c.channel >= 0 && c.channel < channels.size() && c.index >= 0 && c.index <= SOUND_COUNT)
{
auto& channel = channels[c.channel];
//channel.sound =
}
}
queue.clear();
}
void APU::renderSounds(int16_t* dest, size_t samples)
/* stop sound on channel*/
}
void APU::renderSounds(int16_t* dest, size_t totalSamples)
{
constexpr size_t rate = 44100;
size_t samplePerTick = (44100 / 128) * (sstate.sound->speed + 1);
int16_t maxVolume = 4096;
while (samples > 0)
for (SoundState state : channels)
{
/* generate the maximum amount of samples available for same note */
// TODO: optimize if next note is equal to current
size_t available = std::min(samples, samplePerTick - (sstate.position % samplePerTick));
const SoundSample& sample = sstate.sound->samples[sstate.sample];
/* render samples */
dsp.squareWave(Note::frequency(sample.pitch()), (4096 / 8) * sample.volume(), 0, sstate.position, dest, samples);
samples -= available;
dest += available;
sstate.position += available;
sstate.sample = sstate.position / samplePerTick;
}
if (state.sound)
{
int16_t* buffer = dest;
size_t samples = totalSamples;
while (samples > 0 && state.sound)
{
/* generate the maximum amount of samples available for same note */
// TODO: optimize if next note is equal to current
size_t available = std::min(samples, samplePerTick - (sstate.position % samplePerTick));
const SoundSample& sample = sstate.sound->samples[sstate.sample];
/* render samples */
dsp.squareWave(Note::frequency(sample.pitch()), (maxVolume / 8) * sample.volume(), 0, sstate.position, dest, samples);
samples -= available;
dest += available;
sstate.position += available;
sstate.sample = sstate.position / samplePerTick;
if (sstate.sample >= sstate.end)
sstate.sound = nullptr;
}
}
}
}
\ No newline at end of file
......@@ -4,17 +4,23 @@
#include "common.h"
#include <array>
#include <vector>
#include <mutex>
#include <SDL_audio.h>
#if SOUND_ENABLED
namespace retro8
{
class Memory;
namespace sfx
{
using volume_t = int32_t;
using pitch_t = int32_t;
using frequency_t = int32_t;
using channel_index_t = int32_t;
using sound_index_t = int32_t;
enum class Waveform
{
......@@ -85,12 +91,15 @@ namespace retro8
static constexpr size_t SOUND_COUNT = 64;
static constexpr size_t MUSIC_COUNT = 64;
static constexpr size_t TICKS_PER_SECOND = 128;
struct SoundState
{
const Sound* sound;
uint32_t soundIndex;
uint32_t sample;
uint32_t position; // absolute
uint32_t end;
};
class DSP
......@@ -116,16 +125,38 @@ namespace retro8
class APU
{
retro8::Memory& memory;
struct Command
{
sound_index_t index;
channel_index_t channel;
uint32_t start;
uint32_t end;
};
static constexpr size_t CHANNEL_COUNT = 4;
SDL_AudioSpec spec;
SDL_AudioDeviceID device;
std::array<SoundState, CHANNEL_COUNT> channels;
std::mutex queueMutex;
std::vector<Command> queue;
void handleCommands();
public:
APU(Memory& memory) : memory(memory) { }
void init();
void close();
void resume();
void pause();
void play(sound_index_t index, channel_index_t channel, uint32_t start, uint32_t end);
void renderSounds(int16_t* dest, size_t samples);
};
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment