project64/Source/Project64-audio/Driver/DirectSound.cpp

282 lines
8.8 KiB
C++
Raw Normal View History

2021-03-02 02:13:17 +00:00
// Project64 - A Nintendo 64 emulator
// https://www.pj64-emu.com/
2021-03-02 02:13:17 +00:00
// Copyright(C) 2001-2021 Project64.
// Copyright(C) 2000-2015 Azimer
// GNU/GPLv2 licensed: https://gnu.org/licenses/gpl-2.0.html
2017-09-21 13:20:36 +00:00
#include <windows.h>
#include <mmreg.h>
#include <dsound.h>
#include "DirectSound.h"
#include "AudioMain.h"
#include "trace.h"
#include "AudioSettings.h"
DirectSoundDriver::DirectSoundDriver() :
m_AudioIsDone(true),
m_LOCK_SIZE(0),
2021-04-12 11:35:39 +00:00
m_lpds(nullptr),
m_lpdsb(nullptr),
m_lpdsbuf(nullptr),
m_handleAudioThread(nullptr),
2017-09-21 13:20:36 +00:00
m_dwAudioThreadId(0)
{
}
bool DirectSoundDriver::Initialize()
{
WriteTrace(TraceAudioDriver, TraceDebug, "Start");
if (!SoundDriverBase::Initialize())
{
WriteTrace(TraceAudioDriver, TraceDebug, "Done (res: false)");
return false;
}
CGuard guard(m_CS);
LPDIRECTSOUND8 & lpds = (LPDIRECTSOUND8 &)m_lpds;
2021-04-12 11:35:39 +00:00
HRESULT hr = DirectSoundCreate8(nullptr, &lpds, nullptr);
2017-09-21 13:20:36 +00:00
if (FAILED(hr))
{
WriteTrace(TraceAudioDriver, TraceWarning, "Unable to DirectSoundCreate (hr: 0x%08X)", hr);
WriteTrace(TraceAudioDriver, TraceDebug, "Done (res: false)");
return false;
}
hr = lpds->SetCooperativeLevel((HWND)g_AudioInfo.hWnd, DSSCL_PRIORITY);
2017-09-21 13:20:36 +00:00
if (FAILED(hr))
{
WriteTrace(TraceAudioDriver, TraceWarning, "Failed to SetCooperativeLevel (hr: 0x%08X)", hr);
WriteTrace(TraceAudioDriver, TraceDebug, "Done (res: false)");
return false;
}
LPDIRECTSOUNDBUFFER & lpdsbuf = (LPDIRECTSOUNDBUFFER &)m_lpdsbuf;
if (lpdsbuf)
{
IDirectSoundBuffer_Release(lpdsbuf);
2021-04-12 11:35:39 +00:00
lpdsbuf = nullptr;
2017-09-21 13:20:36 +00:00
}
DSBUFFERDESC dsPrimaryBuff = { 0 };
dsPrimaryBuff.dwSize = sizeof(DSBUFFERDESC);
dsPrimaryBuff.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
dsPrimaryBuff.dwBufferBytes = 0;
2021-04-12 11:35:39 +00:00
dsPrimaryBuff.lpwfxFormat = nullptr;
2017-09-21 13:20:36 +00:00
WAVEFORMATEX wfm = { 0 };
wfm.wFormatTag = WAVE_FORMAT_PCM;
wfm.nChannels = 2;
wfm.nSamplesPerSec = 48000;
2017-09-21 13:20:36 +00:00
wfm.wBitsPerSample = 16;
wfm.nBlockAlign = wfm.wBitsPerSample / 8 * wfm.nChannels;
wfm.nAvgBytesPerSec = wfm.nSamplesPerSec * wfm.nBlockAlign;
LPDIRECTSOUNDBUFFER & lpdsb = (LPDIRECTSOUNDBUFFER &)m_lpdsb;
2021-04-12 11:35:39 +00:00
hr = lpds->CreateSoundBuffer(&dsPrimaryBuff, &lpdsb, nullptr);
2017-09-21 13:20:36 +00:00
if (SUCCEEDED(hr))
{
lpdsb->SetFormat(&wfm);
lpdsb->Play(0, 0, DSBPLAY_LOOPING);
}
WriteTrace(TraceAudioDriver, TraceDebug, "Done (res: true)");
return true;
}
void DirectSoundDriver::StopAudio()
{
2021-04-12 11:35:39 +00:00
if (m_handleAudioThread != nullptr)
2017-09-21 13:20:36 +00:00
{
m_AudioIsDone = true;
if (WaitForSingleObject((HANDLE)m_handleAudioThread, 5000) == WAIT_TIMEOUT)
{
WriteTrace(TraceAudioDriver, TraceError, "Timeout on close");
2017-09-21 13:20:36 +00:00
TerminateThread((HANDLE)m_handleAudioThread, 1);
}
CloseHandle((HANDLE)m_handleAudioThread);
2021-04-12 11:35:39 +00:00
m_handleAudioThread = nullptr;
2017-09-21 13:20:36 +00:00
}
}
void DirectSoundDriver::StartAudio()
{
WriteTrace(TraceAudioDriver, TraceDebug, "Start");
2021-04-12 11:35:39 +00:00
if (m_handleAudioThread == nullptr)
2017-09-21 13:20:36 +00:00
{
m_AudioIsDone = false;
2021-04-12 11:35:39 +00:00
m_handleAudioThread = CreateThread(nullptr, NULL, (LPTHREAD_START_ROUTINE)stAudioThreadProc, this, 0, (LPDWORD)&m_dwAudioThreadId);
2017-09-21 13:20:36 +00:00
LPDIRECTSOUNDBUFFER & lpdsbuf = (LPDIRECTSOUNDBUFFER &)m_lpdsbuf;
2021-04-12 11:35:39 +00:00
if (lpdsbuf != nullptr)
2017-09-21 13:20:36 +00:00
{
lpdsbuf->Play(0, 0, DSBPLAY_LOOPING);
}
}
WriteTrace(TraceAudioDriver, TraceDebug, "Done");
}
2017-10-13 00:42:18 +00:00
void DirectSoundDriver::SetFrequency(uint32_t Frequency, uint32_t BufferSize)
2017-09-21 13:20:36 +00:00
{
WriteTrace(TraceAudioDriver, TraceDebug, "Start (Frequency: 0x%08X)", Frequency);
StopAudio();
2017-10-13 00:42:18 +00:00
m_LOCK_SIZE = (BufferSize * 2);
2017-09-21 13:20:36 +00:00
SetSegmentSize(m_LOCK_SIZE, Frequency);
StartAudio();
WriteTrace(TraceAudioDriver, TraceDebug, "Done");
}
2017-09-25 12:00:04 +00:00
void DirectSoundDriver::SetVolume(uint32_t Volume)
{
2017-10-15 16:50:07 +00:00
LPDIRECTSOUNDBUFFER & lpdsb = (LPDIRECTSOUNDBUFFER &)m_lpdsb;
int32_t dsVolume = -((100 - (int32_t)Volume) * 25);
if (Volume == 0)
{
dsVolume = DSBVOLUME_MIN;
}
2021-04-12 11:35:39 +00:00
if (lpdsb != nullptr)
2017-10-15 16:50:07 +00:00
{
lpdsb->SetVolume(dsVolume);
}
2017-09-25 12:00:04 +00:00
}
2017-09-21 13:20:36 +00:00
void DirectSoundDriver::SetSegmentSize(uint32_t length, uint32_t SampleRate)
{
if (SampleRate == 0) { return; }
CGuard guard(m_CS);
WAVEFORMATEX wfm = { 0 };
wfm.wFormatTag = WAVE_FORMAT_PCM;
wfm.nChannels = 2;
wfm.nSamplesPerSec = SampleRate;
wfm.wBitsPerSample = 16;
wfm.nBlockAlign = wfm.wBitsPerSample / 8 * wfm.nChannels;
wfm.nAvgBytesPerSec = wfm.nSamplesPerSec * wfm.nBlockAlign;
DSBUFFERDESC dsbdesc = { 0 };
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;
dsbdesc.dwBufferBytes = length * DS_SEGMENTS;
dsbdesc.lpwfxFormat = &wfm;
LPDIRECTSOUND8 & lpds = (LPDIRECTSOUND8 &)m_lpds;
LPDIRECTSOUNDBUFFER & lpdsbuf = (LPDIRECTSOUNDBUFFER &)m_lpdsbuf;
2021-04-12 11:35:39 +00:00
if (lpds != nullptr)
2017-09-21 13:20:36 +00:00
{
2021-04-12 11:35:39 +00:00
HRESULT hr = lpds->CreateSoundBuffer(&dsbdesc, &lpdsbuf, nullptr);
2017-09-21 13:20:36 +00:00
if (FAILED(hr))
{
WriteTrace(TraceAudioDriver, TraceWarning, "CreateSoundBuffer failed (hr: 0x%08X)", hr);
}
}
2021-04-12 11:35:39 +00:00
if (lpdsbuf != nullptr)
2017-09-21 13:20:36 +00:00
{
lpdsbuf->Play(0, 0, DSBPLAY_LOOPING);
}
}
void DirectSoundDriver::AudioThreadProc()
{
LPDIRECTSOUNDBUFFER & lpdsbuff = (LPDIRECTSOUNDBUFFER &)m_lpdsbuf;
2021-04-12 11:35:39 +00:00
while (lpdsbuff == nullptr && !m_AudioIsDone)
2017-09-21 13:20:36 +00:00
{
Sleep(10);
}
if (!m_AudioIsDone)
{
WriteTrace(TraceAudioDriver, TraceDebug, "Audio thread started...");
2017-09-21 13:20:36 +00:00
DWORD dwStatus;
lpdsbuff->GetStatus(&dwStatus);
if ((dwStatus & DSBSTATUS_PLAYING) == 0)
{
lpdsbuff->Play(0, 0, 0);
}
SetThreadPriority(m_handleAudioThread, THREAD_PRIORITY_HIGHEST);
}
uint32_t next_pos = 0, write_pos = 0, last_pos = 0;
2021-04-12 11:35:39 +00:00
while (lpdsbuff != nullptr && !m_AudioIsDone)
2017-09-21 13:20:36 +00:00
{
WriteTrace(TraceAudioDriver, TraceVerbose, "last_pos: 0x%08X write_pos: 0x%08X next_pos: 0x%08X", last_pos, write_pos, next_pos);
while (last_pos == write_pos)
{ // Cycle around until a new buffer position is available
2021-04-12 11:35:39 +00:00
if (lpdsbuff == nullptr)
2017-09-21 13:20:36 +00:00
{
break;
}
uint32_t play_pos = 0;
2021-04-12 11:35:39 +00:00
if (lpdsbuff == nullptr || FAILED(lpdsbuff->GetCurrentPosition((unsigned long*)&play_pos, nullptr)))
2017-09-21 13:20:36 +00:00
{
WriteTrace(TraceAudioDriver, TraceDebug, "Error getting audio position...");
m_AudioIsDone = true;
break;
}
write_pos = play_pos < m_LOCK_SIZE ? (m_LOCK_SIZE * DS_SEGMENTS) - m_LOCK_SIZE : ((play_pos / m_LOCK_SIZE) * m_LOCK_SIZE) - m_LOCK_SIZE;
WriteTrace(TraceAudioDriver, TraceVerbose, "play_pos: 0x%08X m_write_pos: 0x%08X next_pos: 0x%08X m_LOCK_SIZE: 0x%08X", play_pos, write_pos, next_pos, m_LOCK_SIZE);
if (last_pos == write_pos)
{
WriteTrace(TraceAudioDriver, TraceVerbose, "Sleep");
Sleep(1);
}
2017-09-21 13:20:36 +00:00
}
// This means we had a buffer segment skipped
if (next_pos != write_pos)
{
WriteTrace(TraceAudioDriver, TraceDebug, "Buffer segment skipped");
2017-09-21 13:20:36 +00:00
}
// Store our last position
last_pos = write_pos;
// Set out next anticipated segment
next_pos = write_pos + m_LOCK_SIZE;
if (next_pos >= (m_LOCK_SIZE*DS_SEGMENTS))
{
next_pos -= (m_LOCK_SIZE*DS_SEGMENTS);
}
if (m_AudioIsDone)
{
break;
}
// Time to write out to the buffer
LPVOID lpvPtr1, lpvPtr2;
DWORD dwBytes1, dwBytes2;
WriteTrace(TraceAudioDriver, TraceVerbose, "Lock buffer");
2017-09-21 13:20:36 +00:00
if (lpdsbuff->Lock(write_pos, m_LOCK_SIZE, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0) != DS_OK)
{
WriteTrace(TraceAudioDriver, TraceError, "Error locking sound buffer");
break;
}
WriteTrace(TraceAudioDriver, TraceVerbose, "dwBytes1: 0x%08X dwBytes2: 0x%08X", dwBytes1, dwBytes2);
{
CGuard guard(m_CS);
LoadAiBuffer((uint8_t *)lpvPtr1, dwBytes1);
if (dwBytes2)
{
WriteTrace(TraceAudioDriver, TraceDebug, "Loading second buffer");
LoadAiBuffer((BYTE *)lpvPtr2, dwBytes2);
}
}
// Fills dwBytes to the sound buffer
WriteTrace(TraceAudioDriver, TraceVerbose, "Unlock buffer");
2017-09-21 13:20:36 +00:00
if (FAILED(lpdsbuff->Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2)))
{
WriteTrace(TraceAudioDriver, TraceError, "Error unlocking sound buffer");
break;
}
}
LPDIRECTSOUNDBUFFER & lpdsbuf = (LPDIRECTSOUNDBUFFER &)m_lpdsbuf;
if (lpdsbuf)
{
lpdsbuf->Stop();
}
WriteTrace(TraceAudioDriver, TraceDebug, "Audio thread terminated...");
2017-10-13 00:42:18 +00:00
}