mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
335 lines
7.4 KiB
C++
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);
|
|
}
|
|
|
|
}
|