Commit 50f9c98f authored by Jack's avatar Jack
Browse files

finished joining all the parts for basic (non working) audio engine

parent d2da05b7
......@@ -30,6 +30,13 @@ sprite_index_t LoaderP8::spriteIndexFromString(const char* c)
return (h << 4) | l;
}
uint8_t LoaderP8::valueForUint8(const char* c)
{
int h = valueForHexDigit(c[0]);
int l = valueForHexDigit(c[1]);
return (h << 4) | l;
}
retro8::sprite_flags_t LoaderP8::spriteFlagsFromString(const char* c)
{
int h = valueForHexDigit(c[0]);
......@@ -58,7 +65,7 @@ void LoaderP8::load(const std::string& path, Machine& m)
//TODO: not efficient but for now it's fine
std::stringstream code;
coord_t sy = 0, my = 0, fy = 0;
coord_t sy = 0, my = 0, fy = 0, snd = 0;
static constexpr size_t DIGITS_PER_PIXEL_ROW = 128;
static constexpr size_t BYTES_PER_GFX_ROW = DIGITS_PER_PIXEL_ROW / 2;
......@@ -66,6 +73,9 @@ void LoaderP8::load(const std::string& path, Machine& m)
static constexpr size_t DIGITS_PER_MAP_ROW = 256;
static constexpr size_t DIGITS_PER_SPRITE_FLAGS_ROW = 128*2;
static constexpr size_t DIGITS_PER_SOUND = 168;
for (auto& line : lines)
{
/* change state according to */
......@@ -129,6 +139,34 @@ void LoaderP8::load(const std::string& path, Machine& m)
++fy;
break;
}
case State::SFX:
{
assert(line.length() == DIGITS_PER_SOUND);
const char* p = line.c_str();
sfx::Sound* sound = m.memory().sound(snd);
sound->speed = valueForUint8(p+2); break;
sound->loopStart = valueForUint8(p+4); break;
sound->loopEnd = valueForUint8(p+6); break;
p = p + 8;
for (size_t i = 0; i < sound->samples.size(); ++i)
{
const char* s = p + (i * 5);
auto& sample = sound->samples[i];
sample.setPitch(valueForUint8(s));
sample.setWaveform(sfx::Waveform(valueForHexDigit(s[2])));
sample.setVolume(valueForHexDigit(s[3]));
sample.setEffect(sfx::Effect(valueForHexDigit(s[4])));
}
++snd;
break;
}
}
}
}
......
......@@ -16,6 +16,7 @@ namespace retro8
retro8::color_t colorFromDigit(char d);
retro8::sprite_index_t spriteIndexFromString(const char* c);
retro8::sprite_flags_t spriteFlagsFromString(const char* c);
uint8_t valueForUint8(const char* c);
public:
void load(const std::string& path, Machine& dest);
......
......@@ -10,7 +10,7 @@ namespace r8 = retro8;
retro8::Machine machine;
GameView::GameView(ViewManager* manager) : manager(manager)
GameView::GameView(ViewManager* manager) : manager(manager), _paused(false)
{
}
......@@ -107,9 +107,9 @@ void GameView::render()
retro8::io::LoaderP8 loader;
if (_path.empty())
_path = "test.p8";
_path = "breakout_hero.p8";
//loader.load(_path, machine);
loader.load(_path, machine);
/*SDL_Surface* surface = IMG_Load("pico-man.p8.png");
......@@ -125,8 +125,8 @@ void GameView::render()
if (machine.code().hasInit())
machine.code().init();
//machine.sound().init();
//machine.sound().resume();
machine.sound().init();
machine.sound().resume();
/*for (int i = 0; i < 32; ++i)
......@@ -144,16 +144,15 @@ void GameView::render()
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
update();
machine.flip();
int r = SDL_UpdateTexture(_outputTexture, nullptr, _output->pixels, _output->pitch);
if (r < 0)
if (!_paused)
{
printf("SDL Error: %s\n", SDL_GetError());
assert(false);
update();
machine.flip();
SDL_UpdateTexture(_outputTexture, nullptr, _output->pixels, _output->pitch);
}
text(_path, 2, 2);
SDL_Rect dest;
......@@ -294,6 +293,12 @@ void GameView::handleKeyboardEvent(const SDL_Event& event)
machine.state().buttons.set(retro8::button_t::ACTION2, event.type == SDL_KEYDOWN);
break;
case SDLK_p:
if (event.type == SDL_KEYDOWN)
_paused = !_paused;
break;
#ifndef _WIN32
case SDLK_TAB:
if (event.type == SDL_KEYDOWN)
......
......@@ -33,6 +33,8 @@ namespace ui
std::string _path;
bool _paused;
void rasterize();
void render();
void update();
......
......@@ -612,7 +612,13 @@ namespace sound
int sfx(lua_State* L)
{
//TODO: implement
sfx::sound_index_t index = lua_tonumber(L, 1);
sfx::channel_index_t channel = lua_to_or_default(L, number, 2, -1);
int32_t start = lua_to_or_default(L, number, 3, 0);
int32_t end = lua_to_or_default(L, number, 3, 31); //TODO: actual length
machine.sound().play(index, channel, start, end);
return 0;
}
}
......@@ -784,7 +790,7 @@ void lua::registerFunctions(lua_State* L)
lua_register(L, "music", ::sound::music);
lua_register(L, "sfx", ::sound::music);
lua_register(L, "sfx", ::sound::sfx);
lua_register(L, "sub", string::sub);
......
......@@ -17,6 +17,9 @@ namespace retro8
static constexpr address_t SPRITE_SHEET = 0x0000;
static constexpr address_t SPRITE_FLAGS = 0x3000;
static constexpr address_t MUSIC = 0x3100;
static constexpr address_t SOUNDS = 0x3200;
static constexpr address_t CART_DATA = 0x5e00;
static constexpr address_t PALETTES = 0x5f00;
static constexpr address_t CLIP_RECT = 0x5f20;
......@@ -64,6 +67,8 @@ namespace retro8
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!!
sfx::Sound* sound(sfx::sound_index_t i) { return as<sfx::Sound>(address::SOUNDS + sizeof(sfx::Sound)*i); }
sprite_flags_t* spriteFlagsFor(sprite_index_t index)
{
return as<sprite_flags_t>(address::SPRITE_FLAGS + index);
......
#include "sound.h"
#include "memory.h"
#include <random>
#include <cassert>
......@@ -206,6 +208,8 @@ void APU::init()
wantSpec.samples = 2048;
wantSpec.userdata = this;
wantSpec.callback = audio_callback;
for (auto& channel : channels) channel.sound = nullptr;
device = SDL_OpenAudioDevice(NULL, 0, &wantSpec, &spec, 0);
......@@ -215,39 +219,8 @@ void APU::init()
}
}
Sound ssound;
SoundState sstate;
void APU::resume()
{
ssound.speed = 32;
ssound.samples[0].setPitch(Note::pitch(Tone::E, 2));
ssound.samples[1].setPitch(Note::pitch(Tone::E, 2));
ssound.samples[2].setPitch(Note::pitch(Tone::F, 2));
ssound.samples[3].setPitch(Note::pitch(Tone::G, 2));
ssound.samples[4].setPitch(Note::pitch(Tone::G, 2));
ssound.samples[5].setPitch(Note::pitch(Tone::F, 2));
ssound.samples[6].setPitch(Note::pitch(Tone::E, 2));
ssound.samples[7].setPitch(Note::pitch(Tone::D, 2));
ssound.samples[8].setPitch(Note::pitch(Tone::C, 2));
ssound.samples[9].setPitch(Note::pitch(Tone::C, 2));
ssound.samples[10].setPitch(Note::pitch(Tone::D, 2));
ssound.samples[11].setPitch(Note::pitch(Tone::E, 2));
ssound.samples[12].setPitch(Note::pitch(Tone::E, 2));
ssound.samples[13].setPitch(Note::pitch(Tone::D, 2));
for (size_t i = 0; i < ssound.samples.size(); ++i)
{
ssound.samples[i].setVolume(7);
ssound.samples[i].setWaveform(Waveform::ORGAN);
printf("%d ", Note::frequency(ssound.samples[i].pitch()));
}
sstate.sound = &ssound;
sstate.position = 0;
sstate.sample = 0;
SDL_PauseAudioDevice(device, false);
}
......@@ -291,20 +264,31 @@ void APU::handleCommands()
}
/* find first available channel*/
else if (c.channel == -1)
for (size_t i = 0; channels.size(); ++i)
for (size_t i = 0; i < channels.size(); ++i)
if (!channels[i].sound)
{
c.channel = i;
break;
}
if (c.channel >= 0 && c.channel < channels.size() && c.index >= 0 && c.index <= SOUND_COUNT)
{
auto& channel = channels[c.channel];
//channel.sound =
channel.soundIndex = c.index;
channel.sound = memory.sound(c.index);
channel.end = c.end;
channel.sample = c.start;
size_t samplePerTick = (44100 / 128) * (channel.sound->speed + 1);
channel.position = c.start*samplePerTick;
}
}
queue.clear();
queueMutex.unlock();
}
/* stop sound on channel*/
......@@ -312,9 +296,10 @@ void APU::handleCommands()
void APU::renderSounds(int16_t* dest, size_t totalSamples)
{
handleCommands();
constexpr size_t rate = 44100;
size_t samplePerTick = (44100 / 128) * (sstate.sound->speed + 1);
int16_t maxVolume = 4096;
constexpr int16_t maxVolume = 4096;
for (SoundState state : channels)
{
......@@ -322,25 +307,54 @@ void APU::renderSounds(int16_t* dest, size_t totalSamples)
{
int16_t* buffer = dest;
size_t samples = totalSamples;
size_t samplePerTick = (44100 / 128) * (state.sound->speed + 1);
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));
size_t available = std::min(samples, samplePerTick - (state.position % samplePerTick));
const SoundSample& sample = state.sound->samples[state.sample];
const SoundSample& sample = sstate.sound->samples[sstate.sample];
const int16_t volume = (maxVolume / 8) * sample.volume();
const frequency_t frequency = Note::frequency(sample.pitch());
/* render samples */
dsp.squareWave(Note::frequency(sample.pitch()), (maxVolume / 8) * sample.volume(), 0, sstate.position, dest, samples);
switch (sample.waveform())
{
case Waveform::SQUARE:
dsp.squareWave(frequency, volume, 0, state.position, buffer, samples);
break;
case Waveform::TILTED_SAW:
dsp.tiltedSawtoothWave(frequency, volume, 0, 0.85f, state.position, buffer, samples);
break;
case Waveform::SAW:
dsp.sawtoothWave(frequency, volume, 0, state.position, buffer, samples);
break;
case Waveform::TRIANGLE:
dsp.triangleWave(frequency, volume, 0, state.position, buffer, samples);
break;
case Waveform::PULSE:
dsp.pulseWave(frequency, volume, 0, 1/3.0f, state.position, buffer, samples);
break;
case Waveform::ORGAN:
dsp.organWave(frequency, volume, 0, 0.5f, state.position, buffer, samples);
break;
case Waveform::NOISE:
dsp.noise(frequency, volume, state.position, buffer, samples);
break;
}
samples -= available;
dest += available;
sstate.position += available;
sstate.sample = sstate.position / samplePerTick;
buffer += available;
state.position += available;
state.sample = state.position / samplePerTick;
if (sstate.sample >= sstate.end)
sstate.sound = nullptr;
if (state.sample >= state.end)
state.sound = nullptr;
}
}
}
......
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