// 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(); 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(); 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); } }