183 lines
5.2 KiB
C++
183 lines
5.2 KiB
C++
/* 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 <http://www.gnu.org/licenses/>. */
|
|
|
|
// 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<int*>(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<unsigned char*>(HeapAlloc(
|
|
GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
totalBufferSize));
|
|
if (mbuffer == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
waveBlocks = reinterpret_cast<WAVEHDR*>(mbuffer);
|
|
mbuffer += sizeof(WAVEHDR) * BLOCK_COUNT;
|
|
for (int i = 0; i < BLOCK_COUNT; i++)
|
|
{
|
|
waveBlocks[i].dwBufferLength = BLOCK_SIZE;
|
|
waveBlocks[i].lpData = reinterpret_cast<LPSTR>(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;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|