2008-10-12 17:29:15 +00:00
|
|
|
//GiGaHeRz's SPU2 Driver
|
|
|
|
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
|
|
|
//
|
|
|
|
//This library 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 2.1 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
//This library 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 this library; if not, write to the Free Software
|
|
|
|
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
//
|
|
|
|
#define _WIN32_DCOM
|
|
|
|
#include "spu2.h"
|
|
|
|
#include <windows.h>
|
|
|
|
#include <xaudio2.h>
|
|
|
|
#include <strsafe.h>
|
|
|
|
#include <shellapi.h>
|
|
|
|
#include <mmsystem.h>
|
|
|
|
#include <conio.h>
|
|
|
|
|
|
|
|
class XAudio2Mod: public SndOutModule
|
|
|
|
{
|
|
|
|
private:
|
2008-10-29 13:40:20 +00:00
|
|
|
static const int PacketsPerBuffer = 1;
|
|
|
|
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
2008-10-28 11:17:17 +00:00
|
|
|
static const int BufferSizeBytes = BufferSize * 2;
|
2008-10-12 17:29:15 +00:00
|
|
|
|
|
|
|
s16* qbuffer;
|
|
|
|
s32 out_num;
|
|
|
|
|
|
|
|
#define MAX_BUFFER_COUNT 3
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// Callback structure
|
|
|
|
//--------------------------------------------------------------------------------------
|
2008-10-29 13:40:20 +00:00
|
|
|
class StreamingVoiceContext : public IXAudio2VoiceCallback
|
2008-10-12 17:29:15 +00:00
|
|
|
{
|
2008-10-29 13:40:20 +00:00
|
|
|
public:
|
|
|
|
SndBuffer* sndout;
|
|
|
|
IXAudio2SourceVoice* pSourceVoice;
|
2008-12-03 19:22:10 +00:00
|
|
|
CRITICAL_SECTION cs;
|
2008-10-29 13:40:20 +00:00
|
|
|
|
|
|
|
protected:
|
2008-10-12 17:29:15 +00:00
|
|
|
STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
|
|
|
|
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) { };
|
|
|
|
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
|
|
|
STDMETHOD_(void, OnStreamEnd) () {}
|
|
|
|
STDMETHOD_(void, OnBufferStart) ( void* ) {}
|
2008-10-29 13:40:20 +00:00
|
|
|
STDMETHOD_(void, OnBufferEnd) ( void* context )
|
|
|
|
{
|
2008-12-03 19:22:10 +00:00
|
|
|
EnterCriticalSection( &cs );
|
|
|
|
|
2008-11-04 00:29:19 +00:00
|
|
|
// All of these checks are necessary because XAudio2 is wonky shizat.
|
2008-12-03 19:22:10 +00:00
|
|
|
if( pSourceVoice == NULL || context == NULL ) return;
|
2008-11-04 00:29:19 +00:00
|
|
|
|
2008-10-29 13:40:20 +00:00
|
|
|
s16* qb = (s16*)context;
|
|
|
|
|
|
|
|
for(int p=0; p<PacketsPerBuffer; p++, qb+=SndOutPacketSize )
|
|
|
|
sndout->ReadSamples( qb );
|
|
|
|
|
|
|
|
XAUDIO2_BUFFER buf = {0};
|
|
|
|
buf.AudioBytes = BufferSizeBytes;
|
|
|
|
buf.pAudioData=(const BYTE*)context;
|
|
|
|
buf.pContext=context;
|
|
|
|
|
|
|
|
pSourceVoice->SubmitSourceBuffer( &buf );
|
2008-12-03 19:22:10 +00:00
|
|
|
LeaveCriticalSection( &cs );
|
2008-10-29 13:40:20 +00:00
|
|
|
}
|
2008-10-12 17:29:15 +00:00
|
|
|
STDMETHOD_(void, OnLoopEnd) ( void* ) {}
|
|
|
|
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) { };
|
|
|
|
|
|
|
|
} voiceContext;
|
|
|
|
|
|
|
|
IXAudio2* pXAudio2;
|
|
|
|
IXAudio2MasteringVoice* pMasteringVoice;
|
|
|
|
IXAudio2SourceVoice* pSourceVoice;
|
|
|
|
|
|
|
|
WAVEFORMATEX wfx;
|
|
|
|
|
|
|
|
public:
|
2008-10-29 13:40:20 +00:00
|
|
|
|
2008-10-12 17:29:15 +00:00
|
|
|
s32 Init(SndBuffer *sb)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Initialize XAudio2
|
|
|
|
//
|
|
|
|
CoInitializeEx( NULL, COINIT_MULTITHREADED );
|
|
|
|
|
|
|
|
UINT32 flags = 0;
|
|
|
|
#ifdef _DEBUG
|
|
|
|
flags |= XAUDIO2_DEBUG_ENGINE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ( FAILED(hr = XAudio2Create( &pXAudio2, flags ) ) )
|
|
|
|
{
|
|
|
|
SysMessage( "Failed to init XAudio2 engine: %#X\n", hr );
|
|
|
|
CoUninitialize();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Create a mastering voice
|
|
|
|
//
|
2008-11-01 21:04:22 +00:00
|
|
|
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice, 0, SampleRate ) ) )
|
2008-10-12 17:29:15 +00:00
|
|
|
{
|
|
|
|
SysMessage( "Failed creating mastering voice: %#X\n", hr );
|
|
|
|
SAFE_RELEASE( pXAudio2 );
|
|
|
|
CoUninitialize();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
wfx.nSamplesPerSec = SampleRate;
|
|
|
|
wfx.nChannels=2;
|
|
|
|
wfx.wBitsPerSample = 16;
|
|
|
|
wfx.nBlockAlign = 2*2;
|
2008-10-28 11:17:17 +00:00
|
|
|
wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign;
|
2008-10-12 17:29:15 +00:00
|
|
|
wfx.cbSize=0;
|
|
|
|
|
2008-12-03 19:22:10 +00:00
|
|
|
InitializeCriticalSection( &voiceContext.cs );
|
|
|
|
EnterCriticalSection( &voiceContext.cs );
|
2008-10-31 00:18:52 +00:00
|
|
|
|
2008-10-12 17:29:15 +00:00
|
|
|
//
|
|
|
|
// Create an XAudio2 voice to stream this wave
|
|
|
|
//
|
2008-10-31 00:18:52 +00:00
|
|
|
if( FAILED(hr = pXAudio2->CreateSourceVoice( &pSourceVoice, &wfx,
|
2008-11-01 21:04:22 +00:00
|
|
|
XAUDIO2_VOICE_NOSRC, 1.0f, &voiceContext ) ) )
|
2008-10-12 17:29:15 +00:00
|
|
|
{
|
|
|
|
SysMessage( "Error %#X creating source voice\n", hr );
|
|
|
|
SAFE_RELEASE( pXAudio2 );
|
|
|
|
return -1;
|
|
|
|
}
|
2008-10-31 00:18:52 +00:00
|
|
|
|
2008-10-29 13:40:20 +00:00
|
|
|
voiceContext.pSourceVoice = pSourceVoice;
|
2008-12-03 19:22:10 +00:00
|
|
|
voiceContext.sndout = sb;
|
2008-11-04 00:29:19 +00:00
|
|
|
pSourceVoice->FlushSourceBuffers();
|
2008-10-12 17:29:15 +00:00
|
|
|
pSourceVoice->Start( 0, 0 );
|
|
|
|
|
|
|
|
qbuffer = new s16[BufferSize*MAX_BUFFER_COUNT];
|
2008-10-29 13:40:20 +00:00
|
|
|
ZeroMemory(qbuffer,BufferSizeBytes*MAX_BUFFER_COUNT);
|
2008-10-12 17:29:15 +00:00
|
|
|
|
2008-10-29 13:40:20 +00:00
|
|
|
// Start two buffers.
|
|
|
|
// Frankly two buffers is all we should ever need since the buffer fill code
|
|
|
|
// is tied directly to the XAudio2 engine.
|
2008-10-12 17:29:15 +00:00
|
|
|
|
2008-10-31 00:18:52 +00:00
|
|
|
{
|
|
|
|
XAUDIO2_BUFFER buf = {0};
|
|
|
|
buf.AudioBytes = BufferSizeBytes;
|
|
|
|
buf.pContext=qbuffer;
|
|
|
|
buf.pAudioData=(BYTE*)buf.pContext;
|
|
|
|
pSourceVoice->SubmitSourceBuffer( &buf );
|
|
|
|
}
|
2008-10-12 17:29:15 +00:00
|
|
|
|
2008-10-31 00:18:52 +00:00
|
|
|
{
|
|
|
|
XAUDIO2_BUFFER buf = {0};
|
|
|
|
buf.AudioBytes = BufferSizeBytes;
|
|
|
|
buf.pContext=&qbuffer[BufferSize];
|
|
|
|
buf.pAudioData=(BYTE*)buf.pContext;
|
|
|
|
pSourceVoice->SubmitSourceBuffer( &buf );
|
|
|
|
}
|
2008-12-03 19:22:10 +00:00
|
|
|
LeaveCriticalSection( &voiceContext.cs );
|
2008-11-04 00:29:19 +00:00
|
|
|
|
2008-10-12 17:29:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Close()
|
|
|
|
{
|
2008-12-03 19:22:10 +00:00
|
|
|
EnterCriticalSection( &voiceContext.cs );
|
2008-10-12 17:29:15 +00:00
|
|
|
|
2008-12-03 19:22:10 +00:00
|
|
|
// Clean up?
|
2008-10-29 13:40:20 +00:00
|
|
|
// All XAudio2 interfaces are released when the engine is destroyed,
|
|
|
|
// but being tidy never hurt.
|
|
|
|
|
2008-12-03 19:22:10 +00:00
|
|
|
// Actually it can hurt. As of DXSDK Aug 2008, doing a full cleanup causes
|
|
|
|
// XA2 on Vista to crash. Even if you copy/paste code directly from Microsoft.
|
|
|
|
// But doing no cleanup at all causes XA2 under XP to crash. So after much trial
|
|
|
|
// and error we found a happy comprimise as follows:
|
|
|
|
|
|
|
|
if( pSourceVoice != NULL )
|
|
|
|
pSourceVoice->DestroyVoice();
|
|
|
|
|
2008-10-29 13:40:20 +00:00
|
|
|
if( pMasteringVoice != NULL )
|
|
|
|
pMasteringVoice->DestroyVoice();
|
2008-10-12 17:29:15 +00:00
|
|
|
|
|
|
|
SAFE_RELEASE( pXAudio2 );
|
2008-10-29 13:40:20 +00:00
|
|
|
SAFE_DELETE_ARRAY( qbuffer );
|
|
|
|
|
2008-11-05 16:14:55 +00:00
|
|
|
pMasteringVoice = NULL;
|
|
|
|
voiceContext.sndout = NULL;
|
|
|
|
voiceContext.pSourceVoice = NULL;
|
|
|
|
pSourceVoice = NULL;
|
2008-12-03 19:22:10 +00:00
|
|
|
LeaveCriticalSection( &voiceContext.cs );
|
|
|
|
DeleteCriticalSection( &voiceContext.cs );
|
2008-10-12 17:29:15 +00:00
|
|
|
CoUninitialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Configure(HWND parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-10-28 11:17:17 +00:00
|
|
|
virtual bool Is51Out() const { return false; }
|
2008-10-12 17:29:15 +00:00
|
|
|
|
2008-10-28 11:17:17 +00:00
|
|
|
s32 Test() const
|
2008-10-12 17:29:15 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2008-10-28 11:17:17 +00:00
|
|
|
|
|
|
|
int GetEmptySampleCount() const
|
|
|
|
{
|
2008-10-29 13:40:20 +00:00
|
|
|
XAUDIO2_VOICE_STATE state;
|
|
|
|
pSourceVoice->GetState( &state );
|
|
|
|
return state.SamplesPlayed & (BufferSize-1);
|
2008-10-28 11:17:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* GetIdent() const
|
|
|
|
{
|
|
|
|
return "xaudio2";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* GetLongName() const
|
|
|
|
{
|
2008-12-03 18:53:59 +00:00
|
|
|
return "XAudio 2 (Recommended)";
|
2008-10-28 11:17:17 +00:00
|
|
|
}
|
|
|
|
|
2008-10-12 17:29:15 +00:00
|
|
|
} XA2;
|
|
|
|
|
|
|
|
SndOutModule *XAudio2Out=&XA2;
|