2007-11-07 01:27:54 +00:00
|
|
|
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
|
|
|
// Copyright (C) 1999-2003 Forgotten
|
|
|
|
// Copyright (C) 2004 Forgotten and the VBA development team
|
|
|
|
// Copyright (C) 2004-2006 VBA development team
|
2007-12-31 04:52:00 +00:00
|
|
|
// Copyright (C) 2007-2008 VBA-M development team
|
2007-11-07 01:27:54 +00:00
|
|
|
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation; either version 2, or(at your option)
|
|
|
|
// any later version.
|
|
|
|
//
|
|
|
|
// This program 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 General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software Foundation,
|
|
|
|
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
// MFC
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
|
|
|
// Tools
|
|
|
|
#include "AVIWrite.h"
|
|
|
|
#include "WavWriter.h"
|
|
|
|
|
|
|
|
// Internals
|
|
|
|
#include "../System.h"
|
|
|
|
#include "../GBA.h"
|
|
|
|
#include "../Globals.h"
|
|
|
|
#include "../Sound.h"
|
|
|
|
|
|
|
|
// DirectSound8
|
|
|
|
#include <Dsound.h>
|
|
|
|
#pragma comment( lib, "Dsound" )
|
|
|
|
#pragma comment( lib, "Dxguid" )
|
|
|
|
|
|
|
|
extern bool soundBufferLow;
|
2007-11-17 10:16:24 +00:00
|
|
|
extern void setsystemSoundOn(bool value);
|
2007-11-07 01:27:54 +00:00
|
|
|
|
|
|
|
class DirectSound : public ISound
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
|
|
|
|
LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer
|
|
|
|
LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer
|
|
|
|
LPDIRECTSOUNDNOTIFY8 dsbNotify;
|
|
|
|
HANDLE dsbEvent;
|
|
|
|
WAVEFORMATEX wfx; // Primary buffer wave format
|
2007-12-06 15:06:43 +00:00
|
|
|
int soundBufferTotalLen;
|
2007-12-06 13:54:29 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
public:
|
|
|
|
DirectSound();
|
|
|
|
virtual ~DirectSound();
|
|
|
|
|
|
|
|
bool init(); // initialize the primary and secondary sound buffer
|
|
|
|
void pause(); // pause the secondary sound buffer
|
|
|
|
void reset(); // stop and reset the secondary sound buffer
|
|
|
|
void resume(); // resume the secondary sound buffer
|
|
|
|
void write(); // write the emulated sound to the secondary sound buffer
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
DirectSound::DirectSound()
|
|
|
|
{
|
|
|
|
CoInitialize( NULL );
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
pDirectSound = NULL;
|
|
|
|
dsbPrimary = NULL;
|
|
|
|
dsbSecondary = NULL;
|
|
|
|
dsbNotify = NULL;
|
|
|
|
dsbEvent = NULL;
|
2007-12-06 15:06:43 +00:00
|
|
|
|
|
|
|
soundBufferTotalLen = 14700;
|
2007-11-07 01:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DirectSound::~DirectSound()
|
|
|
|
{
|
|
|
|
if(dsbNotify) {
|
|
|
|
dsbNotify->Release();
|
|
|
|
dsbNotify = NULL;
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
if(dsbEvent) {
|
|
|
|
CloseHandle(dsbEvent);
|
|
|
|
dsbEvent = NULL;
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
if(pDirectSound) {
|
|
|
|
if(dsbPrimary) {
|
|
|
|
dsbPrimary->Release();
|
|
|
|
dsbPrimary = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(dsbSecondary) {
|
|
|
|
dsbSecondary->Release();
|
|
|
|
dsbSecondary = NULL;
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
pDirectSound->Release();
|
|
|
|
pDirectSound = NULL;
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
CoUninitialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DirectSound::init()
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD freq;
|
|
|
|
DSBUFFERDESC dsbdesc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize DirectSound
|
|
|
|
if( FAILED( hr = DirectSoundCreate8( &DSDEVID_DefaultPlayback, &pDirectSound, NULL ) ) ) {
|
|
|
|
systemMessage( IDS_CANNOT_CREATE_DIRECTSOUND, _T("Cannot create DirectSound %08x"), hr );
|
|
|
|
pDirectSound = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if( FAILED( hr = pDirectSound->SetCooperativeLevel( theApp.m_pMainWnd->GetSafeHwnd(), DSSCL_EXCLUSIVE ) ) ) {
|
|
|
|
systemMessage( IDS_CANNOT_SETCOOPERATIVELEVEL, _T("Cannot SetCooperativeLevel %08x"), hr );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create primary sound buffer
|
|
|
|
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
|
|
|
|
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
|
|
|
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
|
|
|
if( theApp.dsoundDisableHardwareAcceleration ) {
|
|
|
|
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbPrimary, NULL ) ) ) {
|
|
|
|
systemMessage(IDS_CANNOT_CREATESOUNDBUFFER, _T("Cannot CreateSoundBuffer %08x"), hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-12-06 15:06:43 +00:00
|
|
|
freq = 44100 / soundQuality;
|
2007-12-06 17:30:43 +00:00
|
|
|
// calculate the number of samples per frame first
|
|
|
|
// then multiply it with the size of a sample frame (16 bit * stereo)
|
|
|
|
soundBufferLen = ( freq / 60 ) * 4;
|
2007-11-07 01:27:54 +00:00
|
|
|
soundBufferTotalLen = soundBufferLen * 10;
|
|
|
|
|
|
|
|
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
wfx.nChannels = 2;
|
|
|
|
wfx.nSamplesPerSec = freq;
|
|
|
|
wfx.wBitsPerSample = 16;
|
|
|
|
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
|
|
|
|
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
|
|
|
|
|
|
|
if( FAILED( hr = dsbPrimary->SetFormat( &wfx ) ) ) {
|
|
|
|
systemMessage( IDS_CANNOT_SETFORMAT_PRIMARY, _T("CreateSoundBuffer(primary) failed %08x"), hr );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create secondary sound buffer
|
|
|
|
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
|
|
|
|
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
|
|
|
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;
|
|
|
|
if( theApp.dsoundDisableHardwareAcceleration ) {
|
|
|
|
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
|
|
|
}
|
|
|
|
dsbdesc.dwBufferBytes = soundBufferTotalLen;
|
|
|
|
dsbdesc.lpwfxFormat = &wfx;
|
|
|
|
|
|
|
|
if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbSecondary, NULL ) ) ) {
|
|
|
|
systemMessage( IDS_CANNOT_CREATESOUNDBUFFER, _T("CreateSoundBuffer(secondary) failed %08x"), hr );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( FAILED( hr = dsbSecondary->SetCurrentPosition( 0 ) ) ) {
|
|
|
|
systemMessage( 0, _T("dsbSecondary->SetCurrentPosition failed %08x"), hr );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-15 18:22:17 +00:00
|
|
|
if( SUCCEEDED( hr = dsbSecondary->QueryInterface( IID_IDirectSoundNotify8, (LPVOID*)&dsbNotify ) ) ) {
|
|
|
|
dsbEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
|
|
DSBPOSITIONNOTIFY notify[10];
|
|
|
|
for( i = 0; i < 10; i++ ) {
|
|
|
|
notify[i].dwOffset = i * soundBufferLen;
|
|
|
|
notify[i].hEventNotify = dsbEvent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( FAILED( dsbNotify->SetNotificationPositions( 10, notify ) ) ) {
|
|
|
|
dsbNotify->Release();
|
|
|
|
dsbNotify = NULL;
|
|
|
|
CloseHandle(dsbEvent);
|
|
|
|
dsbEvent = NULL;
|
2007-11-07 01:27:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Play primary buffer
|
|
|
|
if( FAILED( hr = dsbPrimary->Play( 0, 0, DSBPLAY_LOOPING ) ) ) {
|
|
|
|
systemMessage( IDS_CANNOT_PLAY_PRIMARY, _T("Cannot Play primary %08x"), hr );
|
|
|
|
return false;
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-17 10:16:24 +00:00
|
|
|
setsystemSoundOn(true);
|
2007-11-07 01:27:54 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DirectSound::pause()
|
|
|
|
{
|
|
|
|
if( dsbSecondary == NULL ) return;
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
DWORD status;
|
|
|
|
|
|
|
|
dsbSecondary->GetStatus( &status );
|
|
|
|
|
|
|
|
if( status & DSBSTATUS_PLAYING ) dsbSecondary->Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DirectSound::reset()
|
|
|
|
{
|
|
|
|
if( dsbSecondary == NULL ) return;
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
dsbSecondary->Stop();
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
dsbSecondary->SetCurrentPosition( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DirectSound::resume()
|
|
|
|
{
|
|
|
|
if( dsbSecondary == NULL ) return;
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
dsbSecondary->Play( 0, 0, DSBPLAY_LOOPING );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DirectSound::write()
|
|
|
|
{
|
|
|
|
if(!pDirectSound) return;
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD status = 0;
|
|
|
|
DWORD play = 0;
|
|
|
|
LPVOID lpvPtr1;
|
|
|
|
DWORD dwBytes1 = 0;
|
|
|
|
LPVOID lpvPtr2;
|
|
|
|
DWORD dwBytes2 = 0;
|
|
|
|
|
|
|
|
if( !speedup && synchronize && !theApp.throttle ) {
|
|
|
|
hr = dsbSecondary->GetStatus(&status);
|
|
|
|
if( status & DSBSTATUS_PLAYING ) {
|
2007-12-06 15:06:43 +00:00
|
|
|
if( !soundPaused ) {
|
2007-11-07 01:27:54 +00:00
|
|
|
while( true ) {
|
|
|
|
dsbSecondary->GetCurrentPosition(&play, NULL);
|
|
|
|
int BufferLeft = ((soundNextPosition <= play) ?
|
|
|
|
play - soundNextPosition :
|
|
|
|
soundBufferTotalLen - soundNextPosition + play);
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
if(BufferLeft > soundBufferLen)
|
|
|
|
{
|
|
|
|
if (BufferLeft > soundBufferTotalLen - (soundBufferLen * 3))
|
|
|
|
soundBufferLow = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
soundBufferLow = false;
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
if(dsbEvent) {
|
|
|
|
WaitForSingleObject(dsbEvent, 50);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setsoundPaused(true);
|
|
|
|
}
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
|
|
|
|
// Obtain memory address of write block.
|
|
|
|
// This will be in two parts if the block wraps around.
|
|
|
|
if( DSERR_BUFFERLOST == ( hr = dsbSecondary->Lock(
|
|
|
|
soundNextPosition,
|
|
|
|
soundBufferLen,
|
|
|
|
&lpvPtr1,
|
|
|
|
&dwBytes1,
|
|
|
|
&lpvPtr2,
|
|
|
|
&dwBytes2,
|
|
|
|
0 ) ) ) {
|
|
|
|
// If DSERR_BUFFERLOST is returned, restore and retry lock.
|
|
|
|
dsbSecondary->Restore();
|
|
|
|
hr = dsbSecondary->Lock(
|
|
|
|
soundNextPosition,
|
|
|
|
soundBufferLen,
|
|
|
|
&lpvPtr1,
|
|
|
|
&dwBytes1,
|
|
|
|
&lpvPtr2,
|
|
|
|
&dwBytes2,
|
|
|
|
0 );
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
soundNextPosition += soundBufferLen;
|
|
|
|
soundNextPosition = soundNextPosition % soundBufferTotalLen;
|
|
|
|
|
|
|
|
if( SUCCEEDED( hr ) ) {
|
|
|
|
// Write to pointers.
|
|
|
|
CopyMemory( lpvPtr1, soundFinalWave, dwBytes1 );
|
|
|
|
if ( lpvPtr2 ) {
|
|
|
|
CopyMemory( lpvPtr2, soundFinalWave + dwBytes1, dwBytes2 );
|
|
|
|
}
|
2007-11-14 12:28:27 +00:00
|
|
|
|
2007-11-07 01:27:54 +00:00
|
|
|
// Release the data back to DirectSound.
|
|
|
|
hr = dsbSecondary->Unlock( lpvPtr1, dwBytes1, lpvPtr2, dwBytes2 );
|
|
|
|
} else {
|
|
|
|
systemMessage( 0, _T("dsbSecondary->Lock() failed: %08x"), hr );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ISound *newDirectSound()
|
|
|
|
{
|
|
|
|
return new DirectSound();
|
|
|
|
}
|