2010-04-24 21:37:39 +00:00
|
|
|
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
|
|
|
* Developed and maintained by the Pcsx2 Development Team.
|
2010-04-25 00:31:27 +00:00
|
|
|
*
|
2010-04-24 21:37:39 +00:00
|
|
|
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
|
|
|
*
|
|
|
|
* SPU2-X 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 Found-
|
|
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* SPU2-X 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 SPU2-X. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Global.h"
|
|
|
|
|
|
|
|
#define _WIN32_DCOM
|
|
|
|
#include "Dialogs.h"
|
|
|
|
|
2010-06-11 13:51:43 +00:00
|
|
|
#include "portaudio.h"
|
2010-04-24 21:37:39 +00:00
|
|
|
|
|
|
|
#ifdef __WIN32__
|
2010-06-11 13:51:43 +00:00
|
|
|
#include "pa_win_wasapi.h"
|
2010-04-24 21:37:39 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __LINUX__
|
|
|
|
int PaLinuxCallback( const void *inputBuffer, void *outputBuffer,
|
|
|
|
unsigned long framesPerBuffer,
|
|
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
|
|
PaStreamCallbackFlags statusFlags,
|
|
|
|
void *userData );
|
|
|
|
#endif
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
class Portaudio : public SndOutModule
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Configuration Vars (unused still)
|
|
|
|
|
|
|
|
int m_ApiId;
|
2010-05-29 11:55:53 +00:00
|
|
|
wxString m_Device;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
|
|
|
bool m_UseHardware;
|
|
|
|
|
|
|
|
bool m_WasapiExclusiveMode;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Instance vars
|
|
|
|
|
|
|
|
int writtenSoFar;
|
|
|
|
int writtenLastTime;
|
|
|
|
int availableLastTime;
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
bool started;
|
|
|
|
PaStream* stream;
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
#ifndef __LINUX__
|
|
|
|
static int PaCallback( const void *inputBuffer, void *outputBuffer,
|
|
|
|
unsigned long framesPerBuffer,
|
|
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
|
|
PaStreamCallbackFlags statusFlags,
|
|
|
|
void *userData )
|
|
|
|
{
|
|
|
|
return PA.ActualPaCallback(inputBuffer,outputBuffer,framesPerBuffer,timeInfo,statusFlags,userData);
|
|
|
|
}
|
|
|
|
#endif
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
public:
|
|
|
|
int ActualPaCallback( const void *inputBuffer, void *outputBuffer,
|
|
|
|
unsigned long framesPerBuffer,
|
|
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
|
|
PaStreamCallbackFlags statusFlags,
|
|
|
|
void *userData )
|
|
|
|
{
|
|
|
|
StereoOut32* p1 = (StereoOut32*)outputBuffer;
|
|
|
|
|
|
|
|
int packets = framesPerBuffer / SndOutPacketSize;
|
|
|
|
|
|
|
|
for(int p=0; p<packets; p++, p1+=SndOutPacketSize )
|
|
|
|
SndBuffer::ReadSamples( p1 );
|
|
|
|
|
|
|
|
writtenSoFar += packets * SndOutPacketSize;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
Portaudio()
|
|
|
|
{
|
|
|
|
m_ApiId=-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Init()
|
|
|
|
{
|
|
|
|
started=false;
|
|
|
|
stream=NULL;
|
|
|
|
|
|
|
|
ReadSettings();
|
|
|
|
|
|
|
|
PaError err = Pa_Initialize();
|
|
|
|
if( err != paNoError )
|
|
|
|
{
|
2010-05-31 15:18:49 +00:00
|
|
|
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
|
2010-04-24 21:37:39 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
started=true;
|
|
|
|
|
|
|
|
int deviceIndex = -1;
|
|
|
|
|
2010-05-31 15:18:49 +00:00
|
|
|
fprintf(stderr,"* SPU2-X: Enumerating PortAudio devices:");
|
2010-04-24 21:37:39 +00:00
|
|
|
for(int i=0;i<Pa_GetDeviceCount();i++)
|
|
|
|
{
|
|
|
|
const PaDeviceInfo * info = Pa_GetDeviceInfo(i);
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
const PaHostApiInfo * apiinfo = Pa_GetHostApiInfo(info->hostApi);
|
|
|
|
|
|
|
|
fprintf(stderr," *** Device %d: '%s' (%s)", i, info->name, apiinfo->name);
|
|
|
|
|
|
|
|
if(apiinfo->type == m_ApiId)
|
|
|
|
{
|
|
|
|
#ifdef __WIN32__
|
|
|
|
static wchar_t buffer [1000];
|
|
|
|
MultiByteToWideChar(CP_UTF8,0,info->name,strlen(info->name),buffer,999);
|
|
|
|
buffer[999]=0;
|
|
|
|
#else
|
|
|
|
//# error TODO
|
|
|
|
static wchar_t buffer [1000];
|
|
|
|
//MultiByteToWideChar(CP_UTF8,0,info->name,strlen(info->name),buffer,999);
|
|
|
|
buffer[999]=0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if(m_Device == buffer)
|
|
|
|
{
|
|
|
|
deviceIndex = i;
|
|
|
|
fprintf(stderr," (selected)");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
fprintf(stderr,"\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(deviceIndex<0 && m_ApiId>=0)
|
|
|
|
{
|
|
|
|
for(int i=0;i<Pa_GetHostApiCount();i++)
|
|
|
|
{
|
|
|
|
const PaHostApiInfo * apiinfo = Pa_GetHostApiInfo(i);
|
|
|
|
if(apiinfo->type == m_ApiId)
|
|
|
|
{
|
|
|
|
deviceIndex = apiinfo->defaultOutputDevice;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
if(deviceIndex>=0)
|
|
|
|
{
|
|
|
|
void* infoPtr = NULL;
|
|
|
|
|
|
|
|
#ifdef __WIN32__
|
2010-04-25 00:31:27 +00:00
|
|
|
PaWasapiStreamInfo info = {
|
|
|
|
sizeof(PaWasapiStreamInfo),
|
|
|
|
paWASAPI,
|
|
|
|
1,
|
|
|
|
paWinWasapiExclusive
|
2010-04-24 21:37:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if((m_ApiId == paWASAPI) && m_WasapiExclusiveMode)
|
|
|
|
{
|
|
|
|
// Pass it the Exclusive mode enable flag
|
|
|
|
infoPtr = &info;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
PaStreamParameters outParams = {
|
|
|
|
|
|
|
|
// PaDeviceIndex device;
|
|
|
|
// int channelCount;
|
|
|
|
// PaSampleFormat sampleFormat;
|
|
|
|
// PaTime suggestedLatency;
|
|
|
|
// void *hostApiSpecificStreamInfo;
|
|
|
|
deviceIndex,
|
|
|
|
2,
|
|
|
|
paInt32,
|
|
|
|
0, //?
|
|
|
|
infoPtr
|
|
|
|
};
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
err = Pa_OpenStream(&stream,
|
|
|
|
NULL, &outParams, SampleRate,
|
|
|
|
SndOutPacketSize,
|
2010-04-25 00:31:27 +00:00
|
|
|
paNoFlag,
|
2010-04-24 21:37:39 +00:00
|
|
|
#ifndef __LINUX__
|
2010-04-25 00:31:27 +00:00
|
|
|
PaCallback,
|
2010-04-24 21:37:39 +00:00
|
|
|
#else
|
2010-04-25 00:31:27 +00:00
|
|
|
PaLinuxCallback,
|
2010-04-24 21:37:39 +00:00
|
|
|
#endif
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
err = Pa_OpenDefaultStream( &stream,
|
|
|
|
0, 2, paInt32, 48000,
|
|
|
|
SndOutPacketSize,
|
|
|
|
#ifndef __LINUX__
|
2010-04-25 00:31:27 +00:00
|
|
|
PaCallback,
|
2010-04-24 21:37:39 +00:00
|
|
|
#else
|
2010-04-25 00:31:27 +00:00
|
|
|
PaLinuxCallback,
|
2010-04-24 21:37:39 +00:00
|
|
|
#endif
|
|
|
|
NULL );
|
|
|
|
}
|
|
|
|
if( err != paNoError )
|
|
|
|
{
|
2010-05-31 15:18:49 +00:00
|
|
|
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
|
2010-04-24 21:37:39 +00:00
|
|
|
Pa_Terminate();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = Pa_StartStream( stream );
|
|
|
|
if( err != paNoError )
|
|
|
|
{
|
2010-05-31 15:18:49 +00:00
|
|
|
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
|
2010-04-24 21:37:39 +00:00
|
|
|
Pa_CloseStream(stream);
|
|
|
|
stream=NULL;
|
|
|
|
Pa_Terminate();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Close()
|
|
|
|
{
|
|
|
|
PaError err;
|
|
|
|
if(started)
|
|
|
|
{
|
|
|
|
if(stream)
|
|
|
|
{
|
|
|
|
if(Pa_IsStreamActive(stream))
|
|
|
|
{
|
|
|
|
err = Pa_StopStream(stream);
|
|
|
|
if( err != paNoError )
|
2010-05-31 15:18:49 +00:00
|
|
|
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = Pa_CloseStream(stream);
|
|
|
|
if( err != paNoError )
|
2010-05-31 15:18:49 +00:00
|
|
|
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
|
2010-04-24 21:37:39 +00:00
|
|
|
|
|
|
|
stream=NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PaError err = Pa_Terminate();
|
|
|
|
if( err != paNoError )
|
2010-05-31 15:18:49 +00:00
|
|
|
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
|
2010-04-24 21:37:39 +00:00
|
|
|
|
|
|
|
started=false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Configure(uptr parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool Is51Out() const { return false; }
|
|
|
|
|
|
|
|
s32 Test() const
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetEmptySampleCount()
|
|
|
|
{
|
|
|
|
long availableNow = Pa_GetStreamWriteAvailable(stream);
|
|
|
|
|
|
|
|
int playedSinceLastTime = (writtenSoFar - writtenLastTime) + (availableNow - availableLastTime);
|
|
|
|
writtenLastTime = writtenSoFar;
|
|
|
|
availableLastTime = availableNow;
|
|
|
|
|
|
|
|
// Lowest resolution here is the SndOutPacketSize we use.
|
|
|
|
return playedSinceLastTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
const wchar_t* GetIdent() const
|
|
|
|
{
|
|
|
|
return L"portaudio";
|
|
|
|
}
|
|
|
|
|
|
|
|
const wchar_t* GetLongName() const
|
|
|
|
{
|
|
|
|
return L"Portaudio (crossplatform)";
|
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
void ReadSettings()
|
|
|
|
{
|
2010-05-29 11:55:53 +00:00
|
|
|
wxString api( L"EMPTYEMPTYEMPTY" );
|
2010-04-24 21:37:39 +00:00
|
|
|
m_Device = L"EMPTYEMPTYEMPTY";
|
2010-05-29 11:55:53 +00:00
|
|
|
CfgReadStr( L"PORTAUDIO", L"HostApi", api, L"Unknown" );
|
|
|
|
CfgReadStr( L"PORTAUDIO", L"Device", m_Device, L"default" );
|
2010-04-24 21:37:39 +00:00
|
|
|
|
|
|
|
m_ApiId = -1;
|
|
|
|
if(api == L"InDevelopment") m_ApiId = paInDevelopment; /* use while developing support for a new host API */
|
|
|
|
if(api == L"DirectSound") m_ApiId = paDirectSound;
|
|
|
|
if(api == L"MME") m_ApiId = paMME;
|
|
|
|
if(api == L"ASIO") m_ApiId = paASIO;
|
|
|
|
if(api == L"SoundManager") m_ApiId = paSoundManager;
|
|
|
|
if(api == L"CoreAudio") m_ApiId = paCoreAudio;
|
|
|
|
if(api == L"OSS") m_ApiId = paOSS;
|
|
|
|
if(api == L"ALSA") m_ApiId = paALSA;
|
|
|
|
if(api == L"AL") m_ApiId = paAL;
|
|
|
|
if(api == L"BeOS") m_ApiId = paBeOS;
|
|
|
|
if(api == L"WDMKS") m_ApiId = paWDMKS;
|
|
|
|
if(api == L"JACK") m_ApiId = paJACK;
|
|
|
|
if(api == L"WASAPI") m_ApiId = paWASAPI;
|
|
|
|
if(api == L"AudioScienceHPI") m_ApiId = paAudioScienceHPI;
|
|
|
|
|
|
|
|
m_WasapiExclusiveMode = CfgReadBool( L"PORTAUDIO", L"Wasapi_Exclusive_Mode", false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteSettings() const
|
|
|
|
{
|
2010-05-29 11:55:53 +00:00
|
|
|
wxString api;
|
2010-04-24 21:37:39 +00:00
|
|
|
switch(m_ApiId)
|
|
|
|
{
|
|
|
|
case paInDevelopment: api = L"InDevelopment"; break; /* use while developing support for a new host API */
|
|
|
|
case paDirectSound: api = L"DirectSound"; break;
|
|
|
|
case paMME: api = L"MME"; break;
|
|
|
|
case paASIO: api = L"ASIO"; break;
|
|
|
|
case paSoundManager: api = L"SoundManager"; break;
|
|
|
|
case paCoreAudio: api = L"CoreAudio"; break;
|
|
|
|
case paOSS: api = L"OSS"; break;
|
|
|
|
case paALSA: api = L"ALSA"; break;
|
|
|
|
case paAL: api = L"AL"; break;
|
|
|
|
case paBeOS: api = L"BeOS"; break;
|
|
|
|
case paWDMKS: api = L"WDMKS"; break;
|
|
|
|
case paJACK: api = L"JACK"; break;
|
|
|
|
case paWASAPI: api = L"WASAPI"; break;
|
|
|
|
case paAudioScienceHPI: api = L"AudioScienceHPI"; break;
|
|
|
|
default: api = L"Unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
CfgWriteStr( L"PORTAUDIO", L"HostApi", api);
|
|
|
|
CfgWriteStr( L"PORTAUDIO", L"Device", m_Device);
|
|
|
|
|
|
|
|
CfgWriteBool( L"PORTAUDIO", L"Wasapi_Exclusive_Mode", m_WasapiExclusiveMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
} static PA;
|
|
|
|
|
|
|
|
#ifdef __LINUX__
|
|
|
|
int PaLinuxCallback( const void *inputBuffer, void *outputBuffer,
|
|
|
|
unsigned long framesPerBuffer,
|
|
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
|
|
PaStreamCallbackFlags statusFlags,
|
|
|
|
void *userData )
|
|
|
|
{
|
|
|
|
return PA.ActualPaCallback(inputBuffer,outputBuffer,framesPerBuffer,timeInfo,statusFlags,userData);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SndOutModule *PortaudioOut = &PA;
|