ppsspp/Core/HW/audioPlayer.cpp
2013-05-27 19:45:57 +02:00

596 lines
14 KiB
C++

#ifdef _USE_DSHOW_
#include "audioPlayer.h"
#include "Core/Config.h"
#include <dshow.h>
#include "qeditsimple.h"
#pragma comment(lib, "Strmiids.lib")
#pragma comment(lib, "Quartz.lib")
#include "OMAConvert.h"
#include <map>
#include "StdMutex.h"
#include "../Core/System.h"
#define JIF(x) if (FAILED(hr=(x))) \
{return false;}
#define KIF(x) if (FAILED(hr=(x))) \
{return hr;}
#define LIF(x) if (FAILED(hr=(x))) \
{}
// Add filter from a dll/ax library directly
#include <comdef.h>
typedef HRESULT (STDAPICALLTYPE* FN_DLLGETCLASSOBJECT)(REFCLSID clsid, REFIID iid, void** ppv);
static const int importlibnum = 1;
static HMODULE lib[importlibnum] = {0};
static FN_DLLGETCLASSOBJECT fn[importlibnum] = {0};
HRESULT LoadFilterLibrary(int index, const char *pPath)
{
if (index < 0 || index >= importlibnum)
return E_FAIL;
// load the target DLL directly
lib[index] = LoadLibrary(pPath);
if (!lib[index])
{
return HRESULT_FROM_WIN32(GetLastError());
}
// the entry point is an exported function
fn[index] = (FN_DLLGETCLASSOBJECT)GetProcAddress(lib[index], "DllGetClassObject");
if (fn[index] == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
HRESULT FreeFilterLibrary(int index)
{
if (index < 0 || index >= importlibnum)
return E_FAIL;
if (lib[index])
FreeLibrary(lib[index]);
lib[index] = 0;
return S_OK;
}
HRESULT CreateFilterFromLibrary(int index, REFCLSID clsid, REFIID iid, void** ppUnk)
{
if (index < 0 || index >= importlibnum)
return E_FAIL;
if (!fn[index])
return E_FAIL;
// create a class factory
IUnknownPtr pUnk;
HRESULT hr = fn[index](clsid, IID_IUnknown, (void**)&pUnk);
if (SUCCEEDED(hr))
{
IClassFactoryPtr pCF = pUnk;
if (pCF == NULL)
{
hr = E_NOINTERFACE;
}
else
{
// ask the class factory to create the object
hr = pCF->CreateInstance(NULL, iid, (void**)ppUnk);
}
}
return hr;
}
// {2AE44C10-B451-4B01-9BBE-A5FBEF68C9D4}
static const GUID CLSID_AsyncStreamSource =
{ 0x2ae44c10, 0xb451, 0x4b01, { 0x9b, 0xbe, 0xa5, 0xfb, 0xef, 0x68, 0xc9, 0xd4 } };
// {268424D1-B6E9-4B28-8751-B7774F5ECF77}
static const GUID IID_IStreamSourceFilter =
{ 0x268424d1, 0xb6e9, 0x4b28, { 0x87, 0x51, 0xb7, 0x77, 0x4f, 0x5e, 0xcf, 0x77 } };
// We define the interface the app can use to program us
MIDL_INTERFACE("268424D1-B6E9-4B28-8751-B7774F5ECF77")
IStreamSourceFilter : public IFileSourceFilter
{
public:
virtual STDMETHODIMP LoadStream(void *stream, LONGLONG readSize, LONGLONG streamSize, AM_MEDIA_TYPE *pmt ) = 0;
virtual STDMETHODIMP AddStreamData(LONGLONG offset, void *stream, LONGLONG addSize) = 0;
virtual STDMETHODIMP GetStreamInfo(LONGLONG *readSize, LONGLONG *streamSize) = 0;
virtual STDMETHODIMP SetReadSize(LONGLONG readSize) = 0;
virtual BOOL STDMETHODCALLTYPE IsReadPassEnd() = 0;
};
HRESULT GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
{
IEnumPins *pEnum;
IPin *pPin;
pFilter->EnumPins(&pEnum);
while(pEnum->Next(1, &pPin, 0) == S_OK)
{
PIN_DIRECTION PinDirThis;
pPin->QueryDirection(&PinDirThis);
if (PinDir == PinDirThis)
{
pEnum->Release();
*ppPin = pPin;
return S_OK;
}
pPin->Release();
}
pEnum->Release();
return E_FAIL;
}
HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pFirst, IBaseFilter *pSecond)
{
IPin *pOut = NULL, *pIn = NULL;
HRESULT hr = GetPin(pFirst, PINDIR_OUTPUT, &pOut);
if (FAILED(hr)) return hr;
hr = GetPin(pSecond, PINDIR_INPUT, &pIn);
if (FAILED(hr))
{
pOut->Release();
return E_FAIL;
}
hr = pGraph->Connect(pOut, pIn);
pIn->Release();
pOut->Release();
return hr;
}
class CSampleGrabberCallback : public ISampleGrabberCB
{
public:
CSampleGrabberCallback(audioPlayer *player)
{
m_player = player;
// 1MB round buffer size
m_bufsize = 0x100000;
m_buf = new u8[m_bufsize];
isNeedReset = true;
m_readPos = m_writePos = 0;
}
~CSampleGrabberCallback(){ if (m_buf) delete [] m_buf; }
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
{
return S_OK;
}
STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
{
return S_OK;
}
STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long buflen)
{
m_mutex.lock();
if (isNeedReset) {
isNeedReset = false;
m_readPos = 0;
m_writePos = 0;
}
if (m_writePos + buflen <= m_bufsize) {
memcpy(m_buf + m_writePos, pBuffer, buflen);
} else {
int size = m_bufsize - m_writePos;
memcpy(m_buf + m_writePos, pBuffer, size);
memcpy(m_buf, pBuffer + size, buflen - size);
}
m_writePos = (m_writePos + buflen) % m_bufsize;
// check how much space left to write
int space = (m_readPos - m_writePos + m_bufsize) % m_bufsize;
m_mutex.unlock();
if (space < buflen * 3) {
m_player->pause();
}
return S_OK;
}
int getNextSamples(u8* buf, int wantedbufsize) {
int timecount = 0;
while (isNeedReset) {
Sleep(1);
timecount++;
if (timecount >= 10)
return 0;
}
m_mutex.lock();
// check how much space left to read
int space = (m_writePos - m_readPos + m_bufsize) % m_bufsize;
if (m_readPos + wantedbufsize <= m_bufsize) {
memcpy(buf, m_buf + m_readPos, wantedbufsize);
} else {
int size = m_bufsize - m_readPos;
memcpy(buf, m_buf + m_readPos, size);
memcpy(buf + size, m_buf, wantedbufsize - size);
}
int bytesgot = min(wantedbufsize, space);
m_readPos = (m_readPos + bytesgot) % m_bufsize;
// check how much space left to read
space = (m_writePos - m_readPos + m_bufsize) % m_bufsize;
m_mutex.unlock();
if (space < wantedbufsize * 3) {
m_player->play();
}
return bytesgot;
}
void setResetflag(bool reset) { isNeedReset = reset; }
private:
audioPlayer *m_player;
u8* m_buf;
int m_bufsize;
int m_readPos;
int m_writePos;
bool isNeedReset;
std::recursive_mutex m_mutex;
};
bool addSampleGrabber(IGraphBuilder *pGB, IBaseFilter *pSrc,
ISampleGrabberCB *callback, void **outgrabber)
{
HRESULT hr;
ISampleGrabber *pGrabber = 0;
IBaseFilter *pGrabberF = 0;
JIF(CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void **)&pGrabberF));
JIF(pGB->AddFilter(pGrabberF, L"Sample Grabber"));
JIF(pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber));
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
JIF(pGrabber->SetMediaType(&mt));
JIF(ConnectFilters(pGB, pSrc, pGrabberF));
pGrabberF->Release();
IBaseFilter *pNull = NULL;
JIF(CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void**)&pNull));
JIF(pGB->AddFilter(pNull, L"NullRenderer"));
JIF(ConnectFilters(pGB, pGrabberF, pNull));
// Set one-shot mode and buffering.
JIF(pGrabber->SetOneShot(FALSE));
JIF(pGrabber->SetBufferSamples(TRUE));
JIF(pGrabber->SetCallback(callback, 1));
// close the clock to run as fast as possible
IMediaFilter *pMediaFilter = 0;
pGB->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter);
pMediaFilter->SetSyncSource(0);
pMediaFilter->Release();
*outgrabber = (void*)pGrabber;
return true;
}
static volatile int g_volume = 60;
audioPlayer::audioPlayer(void)
{
m_playmode = -1;
m_volume = g_volume;
m_pMC = 0;
m_pGB = 0;
m_pMS = 0;
m_pGrabber = 0;
m_pGrabberCB = 0;
m_pStreamReader = 0;
}
audioPlayer::~audioPlayer(void)
{
closeMedia();
}
bool audioPlayer::load(const char* filename, u8* stream, int readSize, int streamSize, bool samplebuffermode, bool isWave)
{
if (m_playmode == 1)
return false;
WCHAR wstrfilename[MAX_PATH + 1];
MultiByteToWideChar( CP_ACP, 0, filename ? filename : "stream", -1,
wstrfilename, MAX_PATH );
wstrfilename[MAX_PATH] = 0;
HRESULT hr;
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&m_pGB));
IGraphBuilder *pGB=(IGraphBuilder*)m_pGB;
JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&m_pMS));
IBaseFilter *pSrc = 0;
JIF(CreateFilterFromLibrary(0, CLSID_AsyncStreamSource, IID_IBaseFilter, (void**)&pSrc));
JIF(pGB->AddFilter(pSrc,wstrfilename));
JIF(pSrc->QueryInterface(IID_IStreamSourceFilter,(void**)&m_pStreamReader));
IStreamSourceFilter* pStreamReader = (IStreamSourceFilter*)m_pStreamReader;
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Stream;
mt.subtype = isWave ? MEDIASUBTYPE_WAVE : MEDIASUBTYPE_NULL;
if (filename) {
JIF(pStreamReader->Load(wstrfilename, &mt));
} else {
JIF(pStreamReader->LoadStream(stream, readSize, streamSize, &mt));
}
if (samplebuffermode) {
m_pGrabberCB = new CSampleGrabberCallback(this);
addSampleGrabber(pGB, pSrc, (ISampleGrabberCB*)m_pGrabberCB, &m_pGrabber);
pSrc->Release();
m_playmode = 0;
play();
} else {
IPin *pOut;
JIF(GetPin(pSrc, PINDIR_OUTPUT, &pOut));
pSrc->Release();
JIF(pGB->Render(pOut));
pOut->Release();
setVolume(m_volume);
m_playmode = 0;
}
IMediaSeeking *pMS=(IMediaSeeking*)m_pMS;
m_startpos = 0;
JIF(pMS->GetStopPosition(&m_endpos));
return true;
}
bool audioPlayer::play()
{
if ((!m_pMC) || (m_playmode == -1))
return false;
IMediaControl *pMC = (IMediaControl*)m_pMC;
HRESULT hr;
JIF(pMC->Run());
m_playmode = 1;
return true;
}
bool audioPlayer::pause()
{
if ((!m_pMC) || (m_playmode == -1))
return false;
IMediaControl *pMC = (IMediaControl*)m_pMC;
HRESULT hr;
JIF(pMC->Pause());
m_playmode = 2;
return true;
}
bool audioPlayer::stop()
{
if ((!m_pMC) || (m_playmode <= 0))
return true;
IMediaControl *pMC = (IMediaControl*)m_pMC;
HRESULT hr;
JIF(pMC->Stop());
m_playmode = 0;
return true;
}
bool audioPlayer::closeMedia()
{
if (m_pGrabber) {
ISampleGrabber *pGrabber = (ISampleGrabber *)m_pGrabber;
pGrabber->SetCallback(0, 1);
pGrabber->Release();
}
if (m_pGrabberCB)
delete ((CSampleGrabberCallback*)m_pGrabberCB);
m_pGrabber = 0;
m_pGrabberCB = 0;
stop();
if (m_pMS)
((IMediaSeeking*)m_pMS)->Release();
if (m_pMC)
((IMediaControl*)m_pMC)->Release();
if (m_pStreamReader)
((IStreamSourceFilter*)m_pStreamReader)->Release();
if (m_pGB)
((IGraphBuilder*)m_pGB)->Release();
m_pMS = 0;
m_pMC = 0;
m_pStreamReader = 0;
m_pGB = 0;
m_playmode = -1;
return true;
}
bool audioPlayer::setVolume(int volume)
{
if ((volume < 0) || (volume > 100))
return false;
m_volume = volume;
if (!m_pGB)
return true;
IBasicAudio *pBA = NULL;
HRESULT hr;
int now = -(int)(exp(log((double)10001)/100*(100-volume))-1+0.5);
JIF(((IGraphBuilder*)m_pGB)->QueryInterface(IID_IBasicAudio, (void**)&pBA));
pBA->put_Volume(now);
pBA->Release();
return true;
}
bool audioPlayer::isEnd(long *mstimetoend)
{
if (!m_pMS)
return false;
IMediaSeeking *pMS=(IMediaSeeking*)m_pMS;
LONGLONG curpos;
HRESULT hr;
JIF(pMS->GetCurrentPosition(&curpos));
if (curpos >= m_endpos)
return true;
if (mstimetoend)
*mstimetoend = (m_endpos - curpos) / 10000;
return false;
}
bool audioPlayer::setPlayPos(long ms)
{
if (!m_pGB)
return false;
HRESULT hr;
IMediaSeeking *pMS = (IMediaSeeking*)m_pMS;
LONGLONG pos = ((LONGLONG)ms)*10000;
if (!m_pGrabberCB) {
JIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning,
NULL, AM_SEEKING_NoPositioning));
} else {
pause();
JIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning,
NULL, AM_SEEKING_NoPositioning));
((CSampleGrabberCallback*)m_pGrabberCB)->setResetflag(true);
play();
}
return true;
}
bool audioPlayer::getPlayPos(long *ms)
{
if (!m_pGB)
return false;
HRESULT hr;
IMediaSeeking *pMS = (IMediaSeeking*)m_pMS;
LONGLONG curpos;
JIF(pMS->GetCurrentPosition(&curpos));
if (ms)
*ms = curpos / 10000;
return true;
}
int audioPlayer::getNextSamples(u8* buf, int wantedbufsize)
{
if (!m_pGrabberCB || !m_pMC) {
memset(buf, 0, wantedbufsize);
return wantedbufsize;
}
return ((CSampleGrabberCallback*)m_pGrabberCB)->getNextSamples(buf, wantedbufsize);
}
////////////////////////////////////////////////////////////////////////
// audioEngine
bool audioEngine::loadRIFFStream(u8* stream, int streamsize, int atracID)
{
u8 *oma = 0;
m_ID = atracID;
m_channel = OMAConvert::getRIFFChannels(stream, streamsize);
bool bResult = false;
if (m_channel != 1) {
int readsize = 0;
int omasize = OMAConvert::convertRIFFtoOMA(stream, streamsize, &oma, &readsize);
if (omasize > 0){
bResult = load(0, oma, readsize, omasize, true);
OMAConvert::releaseStream(&oma);
}
}
return bResult;
}
bool audioEngine::closeStream()
{
bool bResult = closeMedia();
m_ID = -1;
return bResult;
}
bool audioEngine::setPlaySample(int sample)
{
return setPlayPos(((s64)sample) * 1000 / 44100);
}
void audioEngine::addStreamData(int offset, u8* buf, int size, int cursample)
{
if ((!m_pGB) || (m_channel == 1))
return;
IStreamSourceFilter* pStreamReader = (IStreamSourceFilter*)m_pStreamReader;
pStreamReader->AddStreamData(offset, buf, size);
bool bsetpos = pStreamReader->IsReadPassEnd();
if (bsetpos)
setPlaySample(cursample);
}
//////////////////////////////////////////////////////////////////////////////
//
std::map<int, audioEngine*> audioMap;
std::recursive_mutex atracsection;
void addAtrac3Audio(u8* stream, int streamsize, int atracID)
{
if (audioMap.find(atracID) != audioMap.end())
return;
audioEngine *temp = new audioEngine();
bool bResult = temp->loadRIFFStream(stream, streamsize, atracID);
atracsection.lock();
audioMap[atracID] = temp;
atracsection.unlock();
if (!bResult)
temp->closeMedia();
}
audioEngine* getaudioEngineByID(int atracID)
{
if (audioMap.find(atracID) == audioMap.end()) {
return NULL;
}
return audioMap[atracID];
}
void deleteAtrac3Audio(int atracID)
{
atracsection.lock();
if (audioMap.find(atracID) != audioMap.end()) {
delete audioMap[atracID];
audioMap.erase(atracID);
}
atracsection.unlock();
}
void initaudioEngine()
{
if (g_Config.bAutoLoadDShow) {
if (LoadFilterLibrary(0, "filter\\lib\\AsyncStreamflt.ax") != S_OK) {
WARN_LOG(HLE, "AsyncStreamflt.ax failed to load");
} else {
NOTICE_LOG(HLE, "AsyncStreamflt.ax loading completed successfully");
}
}
CoInitialize(0);
}
void shutdownEngine()
{
atracsection.lock();
for (auto it = audioMap.begin(); it != audioMap.end(); ++it) {
delete it->second;
}
audioMap.clear();
atracsection.unlock();
CoUninitialize();
FreeFilterLibrary(0);
}
#endif // _USE_DSHOW_