mirror of
https://github.com/libretro/ppsspp.git
synced 2025-02-20 08:52:51 +00:00
Add atrac3plus lib.
This commit is contained in:
parent
a1d7d8f25f
commit
9b0c22f87d
@ -67,7 +67,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>USE_FFMPEG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USE_FFMPEG;_USE_DSHOW_;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
</ClCompile>
|
||||
@ -107,7 +107,7 @@
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<PreprocessorDefinitions>USE_FFMPEG;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USE_FFMPEG;_USE_DSHOW_;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@ -226,8 +226,10 @@
|
||||
<ClCompile Include="HLE\sceVaudio.cpp" />
|
||||
<ClCompile Include="HLE\__sceAudio.cpp" />
|
||||
<ClCompile Include="Host.cpp" />
|
||||
<ClCompile Include="HW\audioPlayer.cpp" />
|
||||
<ClCompile Include="HW\MediaEngine.cpp" />
|
||||
<ClCompile Include="HW\MemoryStick.cpp" />
|
||||
<ClCompile Include="HW\OMAConvert.cpp" />
|
||||
<ClCompile Include="HW\SasAudio.cpp" />
|
||||
<ClCompile Include="Loaders.cpp" />
|
||||
<ClCompile Include="MemMap.cpp" />
|
||||
@ -400,7 +402,9 @@
|
||||
<ClInclude Include="HLE\sceVaudio.h" />
|
||||
<ClInclude Include="HLE\__sceAudio.h" />
|
||||
<ClInclude Include="Host.h" />
|
||||
<ClInclude Include="HW\audioPlayer.h" />
|
||||
<ClInclude Include="HW\MediaEngine.h" />
|
||||
<ClInclude Include="HW\OMAConvert.h" />
|
||||
<ClInclude Include="HW\SasAudio.h" />
|
||||
<ClInclude Include="HW\MemoryStick.h" />
|
||||
<ClInclude Include="Loaders.h" />
|
||||
|
358
Core/HW/OMAConvert.cpp
Normal file
358
Core/HW/OMAConvert.cpp
Normal file
@ -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 <typename T> 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 <typename T> 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
|
27
Core/HW/OMAConvert.h
Normal file
27
Core/HW/OMAConvert.h
Normal file
@ -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
|
||||
|
523
Core/HW/audioPlayer.cpp
Normal file
523
Core/HW/audioPlayer.cpp
Normal file
@ -0,0 +1,523 @@
|
||||
#ifdef _USE_DSHOW_
|
||||
|
||||
#include "audioPlayer.h"
|
||||
#include <dshow.h>
|
||||
#include <qedit.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))) \
|
||||
{}
|
||||
|
||||
// {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<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()
|
||||
{
|
||||
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_
|
60
Core/HW/audioPlayer.h
Normal file
60
Core/HW/audioPlayer.h
Normal file
@ -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_
|
Loading…
x
Reference in New Issue
Block a user