pureikyubu/src/audio.cpp
2023-08-02 23:43:02 +03:00

335 lines
7.4 KiB
C++

// Audio mixer (DirectSound)
#include "pch.h"
using namespace Debug;
namespace Flipper
{
AudioRing::AudioRing(AudioMixer* parentInst)
{
HRESULT hr = DS_OK;
mixer = parentInst;
ringBuffer = new uint8_t[ringSize + 0x10];
memset(ringBuffer, 0, ringSize);
ringWritePtr = 0;
ringReadPtr = 0;
WAVEFORMATEX waveFmt = { 0 };
waveFmt.wFormatTag = WAVE_FORMAT_PCM;
waveFmt.nChannels = 2;
waveFmt.wBitsPerSample = 16;
waveFmt.nSamplesPerSec = 44100;
waveFmt.nBlockAlign = (waveFmt.wBitsPerSample / 8) * waveFmt.nChannels;
waveFmt.nAvgBytesPerSec = waveFmt.nSamplesPerSec * waveFmt.nBlockAlign;
waveFmt.cbSize = 0;
DSBUFFERDESC desc = { 0 };
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME;
desc.dwBufferBytes = ringSize;
desc.lpwfxFormat = &waveFmt;
desc.guid3DAlgorithm = GUID_NULL;
hr = mixer->lpds->CreateSoundBuffer(&desc, &DSBuffer, NULL);
assert(hr == DS_OK);
hr = DSBuffer->SetVolume(DSBVOLUME_MAX);
assert(hr == DS_OK);
}
AudioRing::~AudioRing()
{
delete[] ringBuffer;
if (DSBuffer)
{
DSBuffer->Stop();
DSBuffer->Release();
}
}
// Get distance between Wrptr and Rdptr
size_t AudioRing::CurrentSize()
{
if (ringWritePtr >= ringReadPtr)
{
return ringWritePtr - ringReadPtr;
}
else
{
return ringWritePtr + (ringSize - ringReadPtr);
}
}
uint8_t AudioRing::FetchByte()
{
uint8_t byte = ringBuffer[ringReadPtr++];
if (ringReadPtr >= ringSize)
{
ringReadPtr = 0;
}
return byte;
}
void AudioRing::WaitBufferOverflow(void)
{
HRESULT hr = DS_OK;
DWORD dwWriteCursor, dwPlayCursor;
hr = DSBuffer->GetCurrentPosition(&dwPlayCursor, &dwWriteCursor);
assert(hr == DS_OK);
if (dwPlayCursor < dwWriteCursor)
{
while (dwPlayCursor < dwWriteCursor)
{
hr = DSBuffer->GetCurrentPosition(&dwPlayCursor, &dwWriteCursor);
assert(hr == DS_OK);
}
}
}
void AudioRing::EmitSound()
{
HRESULT hr = DS_OK;
if (!enabled)
return;
// Wait
//WaitBufferOverflow();
// Lock and load
PVOID part1 = nullptr;
DWORD part1Size = 0;
PVOID part2 = nullptr;
DWORD part2Size = 0;
hr = DSBuffer->Lock(0, (DWORD)(frameSize * framesPerEmit), &part1, &part1Size, &part2, &part2Size,
DSBLOCK_FROMWRITECURSOR);
assert(hr == DS_OK);
uint8_t* ptr = (uint8_t*)part1;
size_t lockedBufferSize = part1Size;
size_t byteCount = frameSize * framesPerEmit;
while (byteCount--)
{
*ptr++ = FetchByte();
lockedBufferSize--;
if (lockedBufferSize == 0)
{
ptr = (uint8_t*)part2;
lockedBufferSize = part2Size;
if (!ptr)
break;
}
}
hr = DSBuffer->Unlock(part1, part1Size, part2, part2Size);
assert(hr == DS_OK);
}
void AudioRing::Enable(bool enable)
{
HRESULT hr = DS_OK;
if (enable)
{
hr = DSBuffer->Play(0, 0, bufferMode);
assert(hr == DS_OK);
enabled = true;
ringReadPtr = ringWritePtr = 0;
}
else
{
hr = DSBuffer->Stop();
assert(hr == DS_OK);
hr = DSBuffer->SetCurrentPosition(0);
assert(hr == DS_OK);
enabled = false;
}
}
void AudioRing::SetSampleRate(AudioSampleRate value)
{
HRESULT hr = DSBuffer->SetFrequency(value == AudioSampleRate::Rate_32000 ? 32000 : 48000);
assert(hr == DS_OK);
}
void AudioRing::PushBytes(uint8_t* sampleData, size_t sampleDataSize)
{
while (sampleDataSize != 0)
{
ringBuffer[ringWritePtr + 1] = *sampleData++;
ringBuffer[ringWritePtr] = *sampleData++;
ringWritePtr += 2;
sampleDataSize -= 2;
if (ringWritePtr >= ringSize)
{
ringWritePtr = 0;
}
}
if (CurrentSize() >= frameSize * framesPerEmit && enabled)
{
EmitSound();
frameCounter += framesPerEmit;
//DumpRing();
//DumpDSBuffer();
//exit(0);
}
}
void AudioRing::DumpDSBuffer()
{
HRESULT hr = DS_OK;
char filename[0x100] = { 0, };
sprintf_s(filename, sizeof(filename), "Data\\AXDSBuffer_%04i.bin", (int)frameCounter);
hr = DSBuffer->Stop();
assert(hr == DS_OK);
DWORD readCursor = 0;
DWORD writeCursor = 0;
hr = DSBuffer->GetCurrentPosition(&readCursor, &writeCursor);
assert(hr == DS_OK);
Report(Channel::AX, "frame: %i, readCursor: %i, writeCursor: %i\n", frameCounter, readCursor, writeCursor);
PVOID part1 = nullptr;
DWORD part1Size = 0;
PVOID part2 = nullptr;
DWORD part2Size = 0;
hr = DSBuffer->Lock(0, 0, &part1, &part1Size, &part2, &part2Size, DSBLOCK_ENTIREBUFFER);
assert(hr == DS_OK);
auto part1_buff = std::vector<uint8_t>();
part1_buff.assign((uint8_t*)part1, (uint8_t*)part1 + part1Size);
std::string filenameStr = filename;
Util::FileSave(filenameStr, part1_buff);
Report(Channel::AX, "DSBuffer dumped to: %s\n", filename);
hr = DSBuffer->Unlock(part1, part1Size, part2, part2Size);
assert(hr == DS_OK);
hr = DSBuffer->Play(0, 0, bufferMode);
assert(hr == DS_OK);
}
void AudioRing::DumpRing()
{
char filename[0x100] = { 0, };
sprintf_s(filename, sizeof(filename), "Data\\AXRing_%04i.bin", (int)frameCounter);
Report(Channel::AX, "frame: %i, readPtr: %i, writePtr: %i\n", frameCounter, ringReadPtr, ringWritePtr);
auto ringBuff = std::vector<uint8_t>();
ringBuff.assign(ringBuffer, ringBuffer + ringSize);
std::string filenameStr = filename;
Util::FileSave(filenameStr, ringBuff);
Report(Channel::AX, "Ring dumped to: %s\n", filename);
}
AudioMixer::AudioMixer(HWConfig* config)
{
HRESULT hr = DirectSoundCreate8(NULL, &lpds, NULL);
assert(hr == DS_OK);
assert(lpds);
hr = lpds->SetCooperativeLevel((HWND)config->renderTarget, DSSCL_PRIORITY);
assert(hr == DS_OK);
// Create primary buffer
DSBUFFERDESC bufferDesc = { 0 };
WAVEFORMATEX waveFormat = { 0 };
// Setup the primary buffer description.
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
bufferDesc.dwBufferBytes = 0;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = NULL;
bufferDesc.guid3DAlgorithm = GUID_NULL;
hr = lpds->CreateSoundBuffer(&bufferDesc, &PrimaryBuffer, NULL);
assert(hr == DS_OK);
// Setup the format of the primary sound bufffer.
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nSamplesPerSec = 44100;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 2;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
hr = PrimaryBuffer->SetFormat(&waveFormat);
assert(hr == DS_OK);
// Create rings
Sources = new AudioRing * [numSources];
assert(Sources);
for (int i = 0; i < numSources; i++)
{
Sources[i] = new AudioRing(this);
assert(Sources[i]);
// Set default sample rate
Sources[i]->SetSampleRate(AudioSampleRate::Rate_48000);
Sources[i]->Enable(false);
}
}
AudioMixer::~AudioMixer()
{
// Release rings
for (int i = 0; i < numSources; i++)
{
delete Sources[i];
}
delete[] Sources;
PrimaryBuffer->Release();
lpds->Release();
}
void AudioMixer::Enable(AxChannel channel, bool enable)
{
Sources[(int)channel]->Enable(enable);
}
bool AudioMixer::IsEnabled(AxChannel channel)
{
return Sources[(int)channel]->IsEnabled();
}
void AudioMixer::SetSampleRate(AxChannel channel, AudioSampleRate value)
{
Sources[(int)channel]->SetSampleRate(value);
}
void AudioMixer::PushBytes(AxChannel channel, uint8_t* sampleData, size_t sampleDataSize)
{
Sources[(int)channel]->PushBytes(sampleData, sampleDataSize);
}
}