Skip to content

Commit

Permalink
Audio works
Browse files Browse the repository at this point in the history
  • Loading branch information
ferris committed May 17, 2013
1 parent 994b194 commit c4bed5a
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 12 deletions.
23 changes: 23 additions & 0 deletions Chip8/Apu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

Apu::Apu()
{
audioDriver = nullptr;
mutex = Mutex::Create();
beeping = false;
beepPhase = 0.0;
}

Apu::~Apu()
Expand All @@ -12,6 +15,10 @@ Apu::~Apu()

void Apu::SetAudioDriver(IAudioDriver *audioDriver)
{
auto oldAudioDriver = this->audioDriver;
if (oldAudioDriver) oldAudioDriver->SetRenderCallback(nullptr, nullptr);
this->audioDriver = audioDriver;
audioDriver->SetRenderCallback(renderCallbackRouter, this);
}

void Apu::SetBeeping(bool beeping)
Expand All @@ -20,3 +27,19 @@ void Apu::SetBeeping(bool beeping)
this->beeping = beeping;
mutex->Unlock();
}

void Apu::renderCallbackRouter(float *leftBuffer, float *rightBuffer, int numSamples, void *userData)
{
if (userData) ((Apu *)userData)->renderCallback(leftBuffer, rightBuffer, numSamples);
}

void Apu::renderCallback(float *leftBuffer, float *rightBuffer, int numSamples)
{
mutex->Lock();
for (int i = 0; i < numSamples; i++)
{
leftBuffer[i] = rightBuffer[i] = beeping ? (sin(beepPhase) > 0.0 ? 1.0f : -1.0f) : 0.0f;
beepPhase += .04;
}
mutex->Unlock();
}
5 changes: 5 additions & 0 deletions Chip8/Apu.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ class Apu
void SetBeeping(bool beeping);

private:
static void renderCallbackRouter(float *leftBuffer, float *rightBuffer, int numSamples, void *userData);

void renderCallback(float *leftBuffer, float *rightBuffer, int numSamples);

IAudioDriver *audioDriver;
Mutex *mutex;
bool beeping;
double beepPhase;
};

#endif
165 changes: 160 additions & 5 deletions DirectSoundAudioDriver.cpp
Original file line number Diff line number Diff line change
@@ -1,27 +1,182 @@
#include "DirectSoundAudioDriver.h"

DirectSoundAudioDriver::DirectSoundAudioDriver(RenderCallback renderCallback, int latencyMs, int sampleRate)
DirectSoundAudioDriver::DirectSoundAudioDriver(int latencyMs, int sampleRate, RenderCallback renderCallback, void *userData)
{
if (!renderCallback) throw FSL_EXCEPTION("Render callback cannot be null");
this->renderCallback = renderCallback;
this->latencyMs = latencyMs;
this->sampleRate = sampleRate;
enabled = true;
this->renderCallback = renderCallback;
this->userData = userData;

DirectSoundCreate8(0, &device, 0);
device->SetCooperativeLevel(GetForegroundWindow(), DSSCL_NORMAL);

createBuffers();

mutex = Mutex::Create();
mutex->Lock();
thread = CreateThread(0, 0, threadProc, (LPVOID)this, 0, 0);
SetThreadPriority(thread, THREAD_PRIORITY_HIGHEST);
mutex->Unlock();
}

DirectSoundAudioDriver::~DirectSoundAudioDriver()
{
mutex->Lock();
TerminateThread(thread, 0);
mutex->Unlock();
delete mutex;

destroyBuffers();

device->Release();
}

void DirectSoundAudioDriver::SetRenderCallback(RenderCallback renderCallback, void *userData)
{
mutex->Lock();
this->renderCallback = renderCallback;
this->userData = userData;
mutex->Unlock();
}

void DirectSoundAudioDriver::SetEnabled(bool enabled)
{
mutex->Lock();
this->enabled = enabled;
mutex->Unlock();
}

bool DirectSoundAudioDriver::GetEnabled() const
{
mutex->Lock();
auto ret = enabled;
mutex->Unlock();
return ret;
}

void DirectSoundAudioDriver::SetLatencyMs(int latencyMs)
{
mutex->Lock();
this->latencyMs = latencyMs;
recreateBuffers();
mutex->Unlock();
}

int DirectSoundAudioDriver::GetLatencyMs() const
{
return latencyMs;
mutex->Lock();
auto ret = latencyMs;
mutex->Unlock();
return ret;
}

int DirectSoundAudioDriver::GetSampleRate() const
{
return sampleRate;
mutex->Lock();
auto ret = sampleRate;
mutex->Unlock();
return ret;
}

DWORD WINAPI DirectSoundAudioDriver::threadProc(LPVOID lpParameter)
{
auto driver = (DirectSoundAudioDriver *)lpParameter;

while (true)
{
driver->mutex->Lock();

int playCursorPos;
driver->buffer->GetCurrentPosition((LPDWORD)&playCursorPos, 0);
int bytesToRender = playCursorPos - driver->oldPlayCursorPos;
if (bytesToRender)
{
if (bytesToRender < 0) bytesToRender += driver->bufferSizeBytes;
short *p1, *p2;
int b1, b2;
driver->buffer->Lock(driver->oldPlayCursorPos, bytesToRender, (LPVOID *)&p1, (LPDWORD)&b1, (LPVOID *)&p2, (LPDWORD)&b2, 0);
driver->renderSamples(p1, b1 / sizeof(short));
if (b2) driver->renderSamples(p2, b2 / sizeof(short));
driver->buffer->Unlock(p1, b1, p2, b2);
driver->oldPlayCursorPos = playCursorPos;
}

driver->mutex->Unlock();

Sleep(3);
}
}

void DirectSoundAudioDriver::createBuffers()
{
int channels = 2;
int bitsPerSample = 16;
int bytesPerSample = bitsPerSample / 8;
int blockAlign = channels * bytesPerSample;
int bytesPerSec = sampleRate * blockAlign;

int floatBuffersSize = sampleRate * channels * latencyMs / 1000;
leftBuffer = new float[floatBuffersSize];
rightBuffer = new float[floatBuffersSize];

bufferSizeBytes = floatBuffersSize * bytesPerSample;
WAVEFORMATEX bufferFormat =
{
WAVE_FORMAT_PCM,
channels,
sampleRate,
bytesPerSec,
blockAlign,
bitsPerSample,
0
};
DSBUFFERDESC bufferDesc =
{
sizeof(DSBUFFERDESC),
DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2,
bufferSizeBytes,
0,
&bufferFormat,
GUID_NULL
};
device->CreateSoundBuffer(&bufferDesc, &buffer, 0);

oldPlayCursorPos = 0;
buffer->Play(0, 0, DSBPLAY_LOOPING);
}

void DirectSoundAudioDriver::destroyBuffers()
{
delete leftBuffer;
delete rightBuffer;

buffer->Stop();
buffer->Release();
}

void DirectSoundAudioDriver::recreateBuffers()
{
destroyBuffers();
createBuffers();
}

void DirectSoundAudioDriver::renderSamples(short *buffer, int numSamples)
{
if (renderCallback && enabled)
{
int numFloatSamples = numSamples / 2;
renderCallback(leftBuffer, rightBuffer, numFloatSamples, userData);
for (int i = 0; i < numSamples; i++)
{
int sample = (int)((i & 1 ? rightBuffer : leftBuffer)[i / 2] * 32767.0f);
if (sample < -32768) sample = -32768;
if (sample > 32767) sample = 32767;
buffer[i] = (short)sample;
}
}
else
{
memset(buffer, 0, sizeof(short) * numSamples);
}
}
26 changes: 24 additions & 2 deletions DirectSoundAudioDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,41 @@
class DirectSoundAudioDriver : public IAudioDriver
{
public:
DirectSoundAudioDriver(RenderCallback renderCallback, int latencyMs = 100, int sampleRate = 44100);
DirectSoundAudioDriver(int latencyMs = 60, int sampleRate = 44100, RenderCallback renderCallback = nullptr, void *userData = nullptr);
virtual ~DirectSoundAudioDriver();

virtual void SetRenderCallback(RenderCallback renderCallback, void *userData);

virtual void SetEnabled(bool enabled);
virtual bool GetEnabled() const;

virtual void SetLatencyMs(int latencyMs);
virtual int GetLatencyMs() const;

virtual int GetSampleRate() const;

private:
RenderCallback renderCallback;
static DWORD WINAPI threadProc(LPVOID lpParameter);

void createBuffers();
void destroyBuffers();
void recreateBuffers();

void renderSamples(short *buffer, int numSamples);

int latencyMs;
int sampleRate;
bool enabled;
RenderCallback renderCallback;
void *userData;

LPDIRECTSOUND8 device;
int bufferSizeBytes;
float *leftBuffer, *rightBuffer;
LPDIRECTSOUNDBUFFER buffer;
int oldPlayCursorPos;

Mutex *mutex;
HANDLE thread;
};

Expand Down
5 changes: 5 additions & 0 deletions IAudioDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ class IAudioDriver

virtual ~IAudioDriver();

virtual void SetRenderCallback(RenderCallback renderCallback, void *userData) = 0;

virtual void SetEnabled(bool enabled) = 0;
virtual bool GetEnabled() const = 0;

virtual void SetLatencyMs(int latencyMs) = 0;
virtual int GetLatencyMs() const = 0;

Expand Down
17 changes: 12 additions & 5 deletions Main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Common.h"
#include "Chip8/Chip8.h"
#include "GLVideoDriver.h"
#include "DirectSoundAudioDriver.h"

int Main(const List<String>& arguments)
{
Expand Down Expand Up @@ -57,20 +58,24 @@ int Main(const List<String>& arguments)
systemMenu->AddChild(systemReset);
systemMenu->AddSeparator();

auto audioDriver = new DirectSoundAudioDriver();
chip8.SetAudioDriver(audioDriver);

auto systemAudioMenu = Menu::Create("Audio");
auto systemAudioEnabled = MenuItem::Create("Enabled");
systemAudioEnabled->SetChecked(true);
systemAudioEnabled->SetChecked(audioDriver->GetEnabled());
systemAudioEnabled->SetToggleEnabled(true);
systemAudioEnabled->CheckedChanged += [&] { audioDriver->SetEnabled(systemAudioEnabled->GetChecked()); };
systemAudioMenu->AddChild(systemAudioEnabled);
auto systemAudioLatencyMenu = Menu::Create("Latency");
const int numLatencies = 5;
const int latencies[] = { 50, 100, 250, 500, 1000 };
int latencyIndex = 0;
const int numLatencies = 8;
const int latencies[] = { 20, 40, 60, 80, 100, 200, 500, 1000 };
int latencyIndex = 2;
List<MenuItem *> systemAudioLatencyItems;
auto reflectLatencyIndex = [&]
{
auto latency = latencies[latencyIndex];
// TODO: make functional
audioDriver->SetLatencyMs(latency);
for (int i = 0; i < systemAudioLatencyItems.Count(); i++) systemAudioLatencyItems[i]->SetChecked(i == latencyIndex);
};
for (int i = 0; i < numLatencies; i++)
Expand Down Expand Up @@ -189,8 +194,10 @@ int Main(const List<String>& arguments)

delete helpMenu;
delete helpAbout;

delete viewport;
delete videoDriver;
delete audioDriver;
}
catch (const Exception& e)
{
Expand Down

0 comments on commit c4bed5a

Please sign in to comment.