From 9b0c22f87de03f3c6851682ebab7cfd01a0044cd Mon Sep 17 00:00:00 2001 From: oioitff Date: Tue, 14 May 2013 19:01:33 +0800 Subject: [PATCH] Add atrac3plus lib. --- Core/Core.vcxproj | 8 +- Core/HW/OMAConvert.cpp | 358 +++++++++++++++++++++++++++ Core/HW/OMAConvert.h | 27 +++ Core/HW/audioPlayer.cpp | 523 ++++++++++++++++++++++++++++++++++++++++ Core/HW/audioPlayer.h | 60 +++++ 5 files changed, 974 insertions(+), 2 deletions(-) create mode 100644 Core/HW/OMAConvert.cpp create mode 100644 Core/HW/OMAConvert.h create mode 100644 Core/HW/audioPlayer.cpp create mode 100644 Core/HW/audioPlayer.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c4d8dd55b8..ea56d93e09 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -67,7 +67,7 @@ Level3 Disabled ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib - USE_FFMPEG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + USE_FFMPEG;_USE_DSHOW_;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS StreamingSIMDExtensions2 Fast @@ -107,7 +107,7 @@ false StreamingSIMDExtensions2 Fast - USE_FFMPEG;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + USE_FFMPEG;_USE_DSHOW_;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS Speed @@ -226,8 +226,10 @@ + + @@ -400,7 +402,9 @@ + + diff --git a/Core/HW/OMAConvert.cpp b/Core/HW/OMAConvert.cpp new file mode 100644 index 0000000000..eaf625bd96 --- /dev/null +++ b/Core/HW/OMAConvert.cpp @@ -0,0 +1,358 @@ +#include "OMAConvert.h" + +namespace OMAConvert { + +const u32 OMA_EA3_MAGIC = 0x45413301; +const u8 OMA_CODECID_ATRAC3P = 1; +const int OMAHeaderSize = 96; +const int FMT_CHUNK_MAGIC = 0x20746D66; +const int DATA_CHUNK_MAGIC = 0x61746164; +const int SMPL_CHUNK_MAGIC = 0x6C706D73; +const int FACT_CHUNK_MAGIC = 0x74636166; +const int AT3_MAGIC = 0x0270; +const int AT3_PLUS_MAGIC = 0xFFFE; + +template void BigEndianWriteBuf(u8* buf, T x, int &pos) +{ + int k = sizeof(T); + for (int i = k - 1; i >= 0; i--) + { + buf[pos + i] = (u8)(x & 0xFF); + x >>= 8; + } + pos += k; +} + +template inline T getBufValue(T* buf, int offsetbytes) +{ + return *(T*)(((u8*)buf) + offsetbytes); +} + +inline void WriteBuf(u8* dst, int &pos, u8* src, int size) +{ + memcpy(dst + pos, src, size); + pos += size; +} + +bool isHeader(u8* audioStream, int offset) +{ + const u8 header1 = (u8)0x0F; + const u8 header2 = (u8)0xD0; + return (audioStream[offset] == header1) && (audioStream[offset+1] == header2); +} + +// header set to the headerbuf, and return it's size +int getOmaHeader(u8 codecId, u8 headerCode0, u8 headerCode1, u8 headerCode2, u8* headerbuf) +{ + int pos = 0; + BigEndianWriteBuf(headerbuf, (u32)OMA_EA3_MAGIC, pos); + BigEndianWriteBuf(headerbuf, (u16)OMAHeaderSize, pos); + BigEndianWriteBuf(headerbuf, (u16)-1, pos); + + // Unknown 24 bytes... + BigEndianWriteBuf(headerbuf, (u32)0x00000000, pos); + BigEndianWriteBuf(headerbuf, (u32)0x010f5000, pos); + BigEndianWriteBuf(headerbuf, (u32)0x00040000, pos); + BigEndianWriteBuf(headerbuf, (u32)0x0000f5ce, pos); + BigEndianWriteBuf(headerbuf, (u32)0xd2929132, pos); + BigEndianWriteBuf(headerbuf, (u32)0x2480451c, pos); + + BigEndianWriteBuf(headerbuf, (u8)codecId, pos); + BigEndianWriteBuf(headerbuf, (u8)headerCode0, pos); + BigEndianWriteBuf(headerbuf, (u8)headerCode1, pos); + BigEndianWriteBuf(headerbuf, (u8)headerCode2, pos); + + while (pos < OMAHeaderSize) BigEndianWriteBuf(headerbuf, (u8)0, pos); + + return pos; +} + +int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int frameSize) +{ + int endScan = limit - 1; + + // Most common case: the header can be found at each frameSize + int offset = curpos + frameSize - 8; + if (offset < endScan && isHeader(audioStream, offset)) + return offset; + for (int scan = curpos; scan < endScan; scan++) { + if (isHeader(audioStream, scan)) + return scan; + } + + return -1; +} + +void releaseStream(u8** stream) +{ + if (*stream) delete [] (*stream); + *stream = 0; +} + +// atrac3plus radio +// 352kbps 0xff +// 320kbps 0xe8 +// 256kbps 0xb9 +// 192kbps 0x8b +// 160kbps 0x74 +// 128kbps 0x5c +// 96kbps 0x45 +// 64kbps 0x2e +// 48kbps 0x22 +const u8 atrac3plusradio[] = {0xff, 0xe8, 0xb9, 0x8b, 0x74, 0x5c, 0x45, 0x2e, 0x22}; +const int atrac3plusradiosize = sizeof(atrac3plusradio); + +int convertStreamtoOMA(u8* audioStream, int audioSize, u8** outputStream) +{ + if (!isHeader(audioStream, 0)) + { + *outputStream = 0; + return 0; + } + u8 headerCode1 = audioStream[2]; + u8 headerCode2 = audioStream[3]; + + if (headerCode1 == 0x28) + { + bool bsupported = false; + for (int i = 0; i < atrac3plusradiosize; i++) { + if (atrac3plusradio[i] == headerCode2) + { + bsupported = true; + break; + } + } + if (bsupported == false) + { + *outputStream = 0; + return 0; + } + } + else + { + *outputStream = 0; + return 0; + } + + int frameSize = ((headerCode1 & 0x03) << 8) | (headerCode2 & 0xFF) * 8 + 0x10; + int numCompleteFrames = audioSize / (frameSize + 8); + int lastFrameSize = audioSize - (numCompleteFrames * (frameSize + 8)); + + int omaStreamSize = OMAHeaderSize + numCompleteFrames * frameSize + lastFrameSize; + + // Allocate an OMA stream size large enough (better too large than too short) + if (audioSize > omaStreamSize) omaStreamSize = audioSize; + u8* oma = new u8[omaStreamSize]; + int omapos = 0; + int audiopos = 0; + + omapos += getOmaHeader(OMA_CODECID_ATRAC3P, 0, headerCode1, headerCode2, oma); + while (audioSize - audiopos > 8) { + // Skip 8 bytes frame header + audiopos += 8; + int nextHeader = getNextHeaderPosition(audioStream, audiopos, audioSize, frameSize); + u8* frame = audioStream + audiopos; + int framelimit = audioSize - audiopos; + if (nextHeader >= 0) { + framelimit = nextHeader - audiopos; + audiopos = nextHeader; + } else + audiopos = audioSize; + WriteBuf(oma, omapos, frame, framelimit); + } + + *outputStream = oma; + return omapos; +} + +int getChunkOffset(u8* riff, int limit, int chunkMagic, int offset) { + for (int i = offset; i <= limit - 8;) { + if (getBufValue((int*)riff, i) == chunkMagic) + return i; + // Move to next chunk + int chunkSize = getBufValue((int*)riff, i + 4); + i += chunkSize + 8; + } + + return -1; +} + +int convertRIFFtoOMA(u8* riff, int riffSize, u8** outputStream, int* readSize) +{ + const int firstChunkOffset = 12; + int fmtChunkOffset = getChunkOffset(riff, riffSize, FMT_CHUNK_MAGIC, firstChunkOffset); + if (fmtChunkOffset < 0) { + *outputStream = 0; + return 0; + } + u8 codecId = getBufValue(riff, fmtChunkOffset + 0x30); + u8 headerCode0 = getBufValue(riff, fmtChunkOffset + 0x31); + u8 headerCode1 = getBufValue(riff, fmtChunkOffset + 0x32); + u8 headerCode2 = getBufValue(riff, fmtChunkOffset + 0x33); + + bool bsupported = false; + u16 magic = getBufValue((u16*)riff, fmtChunkOffset + 0x08); + if (magic == AT3_MAGIC) + { + u8 key = getBufValue((u8*)riff, fmtChunkOffset + 0x11); + u16 channel = getBufValue((u16*)riff, fmtChunkOffset + 0x0a); + switch (key) + { + case 0x20: + { + // 66kpbs + codecId = 0; + headerCode0 = 0x02; + headerCode1 = 0x20; + headerCode2 = 0x18; + } + break; + case 0x40: + { + // 132kpbs + codecId = 0; + headerCode0 = 0x00; + headerCode1 = 0x20; + headerCode2 = 0x30; + } + break; + default: + { + // 105kpbs + codecId = 0; + headerCode0 = 0x00; + headerCode1 = 0x20; + headerCode2 = 0x26; + } + break; + } + if (channel == 2) + bsupported = true; + else + bsupported = false; + } + else if (magic == AT3_PLUS_MAGIC && headerCode0 == 0x00 + && headerCode1 == 0x28) + { + for (int i = 0; i < atrac3plusradiosize; i++) { + if (atrac3plusradio[i] == headerCode2) + { + bsupported = true; + break; + } + } + } + + if (bsupported == false) + { + *outputStream = 0; + return 0; + } + + int dataChunkOffset = getChunkOffset(riff, riffSize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (dataChunkOffset < 0) { + *outputStream = 0; + return 0; + } + int dataSize = getBufValue((int*)riff, dataChunkOffset + 4); + int datapos = dataChunkOffset + 8; + + u8* oma = new u8[OMAHeaderSize + dataSize]; + int omapos = 0; + + omapos += getOmaHeader(codecId, headerCode0, headerCode1, headerCode2, oma); + WriteBuf(oma, omapos, riff + datapos, dataSize); + + *outputStream = oma; + if (readSize) + *readSize = OMAHeaderSize + riffSize - datapos; + return omapos; +} + +int getOMANumberAudioChannels(u8* oma) +{ + int headerParameters = getBufValue((int*)oma, 0x20); + int channels = (headerParameters >> 18) & 0x7; + + return channels; +} + +int getRIFFSize(u8* riff, int bufsize) +{ + const int firstChunkOffset = 12; + int fmtChunkOffset = getChunkOffset(riff, bufsize, FMT_CHUNK_MAGIC, firstChunkOffset); + int dataChunkOffset = getChunkOffset(riff, bufsize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (fmtChunkOffset < 0 || dataChunkOffset < 0) + return 0; + int dataSize = getBufValue((int*)riff, dataChunkOffset + 4); + return dataSize + dataChunkOffset + 8; +} + +int getRIFFLoopNum(u8* riff, int bufsize, int *startsample, int *endsample) +{ + const int firstChunkOffset = 12; + int dataChunkOffset = getChunkOffset(riff, bufsize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (dataChunkOffset < 0) + return 0; + int smplChunkOffset = getChunkOffset(riff, dataChunkOffset, SMPL_CHUNK_MAGIC, firstChunkOffset); + if (smplChunkOffset < 0) + return 0; + int factChunkOffset = getChunkOffset(riff, dataChunkOffset, FACT_CHUNK_MAGIC, firstChunkOffset); + int atracSampleOffset = 0; + if (factChunkOffset >= 0) { + int factChunkSize = getBufValue((int*)riff, factChunkOffset + 4); + if (factChunkSize >= 8) { + atracSampleOffset = getBufValue((int*)riff, factChunkOffset + 12); + } + } + int smplChunkSize = getBufValue((int*)riff, smplChunkOffset + 4); + int checkNumLoops = getBufValue((int*)riff, smplChunkOffset + 36); + if (smplChunkSize >= 36 + checkNumLoops * 24) + { + // find loop info, simple return -1 now for endless loop + int numLoops = checkNumLoops; + int loopInfoAddr = smplChunkOffset + 44; + int loopcounts = (numLoops > 1) ? -1 : 0; + for (int i = 0; i < 1; i++) { + if (startsample) + *startsample = getBufValue((int*)riff, loopInfoAddr + 8) - atracSampleOffset; + if (endsample) + *endsample = getBufValue((int*)riff, loopInfoAddr + 12) - atracSampleOffset; + int playcount = getBufValue((int*)riff, loopInfoAddr + 20); + if (playcount != 1) + loopcounts = -1; + loopInfoAddr += 24; + } + return loopcounts; + } + return 0; +} + +int getRIFFendSample(u8* riff, int bufsize) +{ + const int firstChunkOffset = 12; + int dataChunkOffset = getChunkOffset(riff, bufsize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (dataChunkOffset < 0) + return -1; + int factChunkOffset = getChunkOffset(riff, dataChunkOffset, FACT_CHUNK_MAGIC, firstChunkOffset); + if (factChunkOffset >= 0) { + int factChunkSize = getBufValue((int*)riff, factChunkOffset + 4); + if (factChunkSize >= 8) { + int endSample = getBufValue((int*)riff, factChunkOffset + 8); + return endSample; + } + } + return -1; +} + +int getRIFFChannels(u8* riff, int bufsize) +{ + const int firstChunkOffset = 12; + int fmtChunkOffset = getChunkOffset(riff, bufsize, FMT_CHUNK_MAGIC, firstChunkOffset); + if (fmtChunkOffset < 0) + return 0; + u16 channel = getBufValue((u16*)riff, fmtChunkOffset + 0x0a); + return channel; +} + +} // namespace OMAConvert \ No newline at end of file diff --git a/Core/HW/OMAConvert.h b/Core/HW/OMAConvert.h new file mode 100644 index 0000000000..c8b4b805f8 --- /dev/null +++ b/Core/HW/OMAConvert.h @@ -0,0 +1,27 @@ +// It can simply convert a at3+ file or stream to oma format +// Thanks to JPCSP project + +#pragma once + +#include "../../Globals.h" + +namespace OMAConvert { + + // output OMA to outputStream, and return it's size. You need to release it by use releaseStream() + int convertStreamtoOMA(u8* audioStream, int audioSize, u8** outputStream); + // output OMA to outputStream, and return it's size. You need to release it by use releaseStream() + int convertRIFFtoOMA(u8* riff, int riffSize, u8** outputStream, int *readSize = 0); + + void releaseStream(u8** stream); + + int getOMANumberAudioChannels(u8* oma); + + int getRIFFSize(u8* riff, int bufsize); + + int getRIFFLoopNum(u8* riff, int bufsize, int *startsample = 0, int *endsample = 0); + + int getRIFFendSample(u8* riff, int bufsize); + + int getRIFFChannels(u8* riff, int bufsize); +} // namespace OMAConvert + diff --git a/Core/HW/audioPlayer.cpp b/Core/HW/audioPlayer.cpp new file mode 100644 index 0000000000..e58b627081 --- /dev/null +++ b/Core/HW/audioPlayer.cpp @@ -0,0 +1,523 @@ +#ifdef _USE_DSHOW_ + +#include "audioPlayer.h" +#include +#include +#pragma comment(lib, "Strmiids.lib") +#pragma comment(lib, "Quartz.lib") + +#include "OMAConvert.h" +#include +#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))) \ + {} + +// {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(CoCreateInstance(CLSID_AsyncStreamSource, NULL, CLSCTX_INPROC_SERVER, + 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 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() +{ + CoInitialize(0); +} + +void shutdownEngine() +{ + atracsection.lock(); + for (auto it = audioMap.begin(); it != audioMap.end(); ++it) { + delete it->second; + } + audioMap.clear(); + atracsection.unlock(); + CoUninitialize(); + system("cleanAudios.bat"); +} + +#endif // _USE_DSHOW_ \ No newline at end of file diff --git a/Core/HW/audioPlayer.h b/Core/HW/audioPlayer.h new file mode 100644 index 0000000000..9a7b710110 --- /dev/null +++ b/Core/HW/audioPlayer.h @@ -0,0 +1,60 @@ +#pragma once + +#ifdef _USE_DSHOW_ + +#include "../../Globals.h" + +class audioPlayer +{ +public: + audioPlayer(void); + ~audioPlayer(void); + // if samplebuffermode is true, it would provide sample buffers instead play sounds + // if filename is set, then load a file, otherwise load from stream + bool load(const char* filename, u8* stream = 0, int readSize = 0, int streamSize = 0, + bool samplebuffermode = false, bool isWave = false); + bool play(); + bool pause(); + bool stop(); + bool closeMedia(); + bool setVolume(int volume); + bool isEnd(long *mstimetoend = 0); + bool setPlayPos(long ms); + bool getPlayPos(long *ms); + int getNextSamples(u8* buf, int wantedbufsize); +protected: + void *m_pGB; + void *m_pMC; + void *m_pMS; + void *m_pStreamReader; + void *m_pGrabber; + void *m_pGrabberCB; + // 0 for stop, 1 for playing, 2 for pause, -1 for not loaded files + int m_playmode; + int m_volume; +protected: + s64 m_startpos; + s64 m_endpos; +}; + +class audioEngine: public audioPlayer{ +public: + audioEngine(void):audioPlayer(), m_ID(-1){} + ~audioEngine(void){ closeStream();} + bool loadRIFFStream(u8* stream, int streamsize, int atracID); + bool closeStream(); + bool setPlaySample(int sample); + void addStreamData(int offset, u8* buf, int size, int cursample); +private: + int m_ID; + int m_channel; +}; + +void addAtrac3Audio(u8* stream, int streamsize, int atracID); +audioEngine* getaudioEngineByID(int atracID); +void deleteAtrac3Audio(int atracID); + +void initaudioEngine(); +void shutdownEngine(); + +#endif // _USE_DSHOW_ \ No newline at end of file