pcsx2/plugins/spu2-x/src/SndOut_Portaudio.cpp

368 lines
8.8 KiB
C++
Raw Normal View History

/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* 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"
#include "portaudio.h"
#include "wchar.h"
#ifdef __WIN32__
#include "pa_win_wasapi.h"
#endif
#ifdef __LINUX__
int PaLinuxCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData );
#endif
class Portaudio : public SndOutModule
{
private:
//////////////////////////////////////////////////////////////////////////////////////////
// Configuration Vars (unused still)
int m_ApiId;
wxString m_Device;
bool m_UseHardware;
bool m_WasapiExclusiveMode;
//////////////////////////////////////////////////////////////////////////////////////////
// Instance vars
int writtenSoFar;
int writtenLastTime;
int availableLastTime;
bool started;
PaStream* stream;
#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
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;
}
Portaudio()
{
m_ApiId=-1;
}
s32 Init()
{
started=false;
stream=NULL;
ReadSettings();
PaError err = Pa_Initialize();
if( err != paNoError )
{
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
return -1;
}
started=true;
int deviceIndex = -1;
fprintf(stderr,"* SPU2-X: Enumerating PortAudio devices:");
for(int i=0;i<Pa_GetDeviceCount();i++)
{
const PaDeviceInfo * info = Pa_GetDeviceInfo(i);
const PaHostApiInfo * apiinfo = Pa_GetHostApiInfo(info->hostApi);
fprintf(stderr," *** Device %d: '%s' (%s)", i, info->name, apiinfo->name);
if(apiinfo->type == m_ApiId)
{
static wchar_t buffer [1000];
mbstowcs(buffer,info->name,1000);
buffer[999]=0;
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;
}
}
}
if(deviceIndex>=0)
{
void* infoPtr = NULL;
#ifdef __WIN32__
PaWasapiStreamInfo info = {
sizeof(PaWasapiStreamInfo),
paWASAPI,
1,
paWinWasapiExclusive
};
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,
SndOutPacketSize/(float)SampleRate, // 0.2f,
infoPtr
};
err = Pa_OpenStream(&stream,
NULL, &outParams, SampleRate,
SndOutPacketSize,
paNoFlag,
#ifndef __LINUX__
PaCallback,
#else
PaLinuxCallback,
#endif
NULL);
}
else
{
err = Pa_OpenDefaultStream( &stream,
0, 2, paInt32, 48000,
SndOutPacketSize,
#ifndef __LINUX__
PaCallback,
#else
PaLinuxCallback,
#endif
NULL );
}
if( err != paNoError )
{
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
Pa_Terminate();
return -1;
}
err = Pa_StartStream( stream );
if( err != paNoError )
{
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
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 )
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
}
err = Pa_CloseStream(stream);
if( err != paNoError )
fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
stream=NULL;
}
// Seems to do more harm than good.
//PaError err = Pa_Terminate();
//if( err != paNoError )
// fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
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)";
}
void ReadSettings()
{
wxString api( L"EMPTYEMPTYEMPTY" );
m_Device = L"EMPTYEMPTYEMPTY";
#ifdef __LINUX__
// By default on linux use the ALSA API (+99% users) -- Gregory
CfgReadStr( L"PORTAUDIO", L"HostApi", api, L"ALSA" );
#else
CfgReadStr( L"PORTAUDIO", L"HostApi", api, L"Unknown" );
#endif
CfgReadStr( L"PORTAUDIO", L"Device", m_Device, L"default" );
SetApiSettings(api);
m_WasapiExclusiveMode = CfgReadBool( L"PORTAUDIO", L"Wasapi_Exclusive_Mode", false);
}
void SetApiSettings(wxString api)
{
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;
}
void WriteSettings() const
{
wxString api;
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;