/* This file is part of BKBTL. BKBTL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. BKBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with BKBTL. If not, see . */ // SoundGen.cpp #include "stdafx.h" #include "emubase/Emubase.h" #include "SoundGen.h" #include "mmsystem.h" ////////////////////////////////////////////////////////////////////// static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD, DWORD, DWORD); static CRITICAL_SECTION waveCriticalSection; static WAVEHDR* waveBlocks; static volatile int waveFreeBlockCount; static int waveCurrentBlock; static bool m_SoundGenInitialized = false; HWAVEOUT hWaveOut; WAVEFORMATEX wfx; char buffer[BUFSIZE]; int bufcurpos; ////////////////////////////////////////////////////////////////////// static void CALLBACK WaveCallback(HWAVEOUT /*hwo*/, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR /*dwParam1*/, DWORD_PTR /*dwParam2*/) { int* freeBlockCounter = reinterpret_cast(dwInstance); if (uMsg != WOM_DONE) return; EnterCriticalSection(&waveCriticalSection); (*freeBlockCounter)++; LeaveCriticalSection(&waveCriticalSection); } void SoundGen_Initialize(WORD volume) { if (m_SoundGenInitialized) return; size_t totalBufferSize = (BLOCK_SIZE + sizeof(WAVEHDR)) * BLOCK_COUNT; unsigned char* mbuffer = static_cast(HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, totalBufferSize)); if (mbuffer == nullptr) { return; } waveBlocks = reinterpret_cast(mbuffer); mbuffer += sizeof(WAVEHDR) * BLOCK_COUNT; for (int i = 0; i < BLOCK_COUNT; i++) { waveBlocks[i].dwBufferLength = BLOCK_SIZE; waveBlocks[i].lpData = reinterpret_cast(mbuffer); mbuffer += BLOCK_SIZE; } waveFreeBlockCount = BLOCK_COUNT; waveCurrentBlock = 0; wfx.nSamplesPerSec = SOUNDSAMPLERATE; wfx.wBitsPerSample = 16; wfx.nChannels = 2; wfx.cbSize = 0; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) >> 3; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; MMRESULT result = waveOutOpen( &hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)WaveCallback, (DWORD_PTR)&waveFreeBlockCount, CALLBACK_FUNCTION); if (result != MMSYSERR_NOERROR) { return; } waveOutSetVolume(hWaveOut, ((DWORD)volume << 16) | ((DWORD)volume)); InitializeCriticalSection(&waveCriticalSection); bufcurpos = 0; m_SoundGenInitialized = true; } void SoundGen_Finalize() { if (!m_SoundGenInitialized) return; while (waveFreeBlockCount < BLOCK_COUNT) Sleep(1); for (int i = 0; i < waveFreeBlockCount; i++) { if (waveBlocks[i].dwFlags & WHDR_PREPARED) waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof(WAVEHDR)); } DeleteCriticalSection(&waveCriticalSection); waveOutClose(hWaveOut); HeapFree(GetProcessHeap(), 0, waveBlocks); waveBlocks = nullptr; m_SoundGenInitialized = false; } void SoundGen_SetVolume(WORD volume) { if (!m_SoundGenInitialized) return; waveOutSetVolume(hWaveOut, ((DWORD)volume << 16) | ((DWORD)volume)); } void SoundGen_SetSpeed(WORD speedpercent) { DWORD dwRate = 0x00010000; if (speedpercent > 0 && speedpercent < 1000) dwRate = (((DWORD)speedpercent / 100) << 16) | ((speedpercent % 100) * 0x00010000 / 100); waveOutSetPlaybackRate(hWaveOut, dwRate); } void CALLBACK SoundGen_FeedDAC(unsigned short L, unsigned short R) { if (!m_SoundGenInitialized) return; unsigned int word = ((unsigned int)R << 16) + L; memcpy(&buffer[bufcurpos], &word, 4); bufcurpos += 4; if (bufcurpos >= BUFSIZE) { WAVEHDR* current = &waveBlocks[waveCurrentBlock]; if (current->dwFlags & WHDR_PREPARED) waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); memcpy(current->lpData, buffer, BUFSIZE); current->dwBufferLength = BLOCK_SIZE; waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR)); waveOutWrite(hWaveOut, current, sizeof(WAVEHDR)); EnterCriticalSection(&waveCriticalSection); waveFreeBlockCount--; LeaveCriticalSection(&waveCriticalSection); while (!waveFreeBlockCount) Sleep(1); waveCurrentBlock++; if (waveCurrentBlock >= BLOCK_COUNT) waveCurrentBlock = 0; bufcurpos = 0; } } //////////////////////////////////////////////////////////////////////