diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 75fd9ac8cd..ec889d4446 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
@@ -228,8 +228,10 @@
+
+
@@ -404,7 +406,10 @@
+
+
+
diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp
index d463cbdb49..47d82998b0 100644
--- a/Core/HLE/sceAtrac.cpp
+++ b/Core/HLE/sceAtrac.cpp
@@ -74,6 +74,10 @@ extern "C" {
}
#endif // USE_FFMPEG
+#ifdef _USE_DSHOW_
+#include "../HW/audioPlayer.h"
+#endif // _USE_DSHOW_
+
struct InputBuffer {
u32 addr;
u32 size;
@@ -113,11 +117,21 @@ struct Atrac {
}
~Atrac() {
+ CleanStuff();
+ }
+
+ void CleanStuff() {
#ifdef USE_FFMPEG
ReleaseFFMPEGContext();
#endif // USE_FFMPEG
+
+#ifdef _USE_DSHOW_
+ deleteAtrac3Audio(atracID);
+#endif // _USE_DSHOW_
+
if (data_buf)
delete [] data_buf;
+ data_buf = 0;
}
void DoState(PointerWrap &p) {
@@ -128,6 +142,11 @@ struct Atrac {
p.Do(first);
p.Do(atracBufSize);
p.Do(codeType);
+
+ p.Do(currentSample);
+ p.Do(endSample);
+ p.Do(firstSampleoffset);
+
u32 has_data_buf = data_buf != NULL;
p.Do(has_data_buf);
if (has_data_buf) {
@@ -147,10 +166,6 @@ struct Atrac {
p.Do(atracBitrate);
p.Do(atracBytesPerFrame);
- p.Do(currentSample);
- p.Do(endSample);
- p.Do(firstSampleoffset);
-
p.Do(loopinfo);
p.Do(loopinfoNum);
@@ -254,6 +269,11 @@ void __AtracInit() {
avcodec_register_all();
av_register_all();
#endif // USE_FFMPEG
+
+#ifdef _USE_DSHOW_
+ initaudioEngine();
+#endif //_USE_DSHOW_
+
}
void __AtracDoState(PointerWrap &p) {
@@ -268,6 +288,9 @@ void __AtracShutdown() {
delete it->second;
}
atracMap.clear();
+#ifdef _USE_DSHOW_
+ shutdownEngine();
+#endif // _USE_DSHOW_
}
Atrac *getAtrac(int atracID) {
@@ -447,6 +470,13 @@ u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd)
int addbytes = std::min(bytesToAdd, atrac->first.filesize - atrac->first.fileoffset);
Memory::Memcpy(atrac->data_buf + atrac->first.fileoffset, atrac->first.addr + atrac->first.offset, addbytes);
}
+#ifdef _USE_DSHOW_
+ audioEngine *engine = getaudioEngineByID(atracID);
+ if (engine && bytesToAdd > 0) {
+ engine->addStreamData(atrac->first.fileoffset - atrac->firstSampleoffset + 96, Memory::GetPointer(atrac->first.addr + atrac->first.offset),
+ bytesToAdd, atrac->currentSample);
+ }
+#endif
atrac->first.size += bytesToAdd;
if (atrac->first.size > atrac->first.filesize)
atrac->first.size = atrac->first.filesize;
@@ -462,6 +492,10 @@ u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF
DEBUG_LOG(HLE, "sceAtracDecodeData(%i, %08x, %08x, %08x, %08x)", atracID, outAddr, numSamplesAddr, finishFlagAddr, remainAddr);
Atrac *atrac = getAtrac(atracID);
+#ifdef _USE_DSHOW_
+ audioEngine *engine = getaudioEngineByID(atracID);
+#endif // _USE_DSHOW_
+
u32 ret = 0;
if (atrac != NULL) {
// We already passed the end - return an error (many games check for this.)
@@ -511,6 +545,24 @@ u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF
} else
#endif // USE_FFMPEG
+#ifdef _USE_DSHOW_
+ if (engine) {
+ static s16 buf[ATRAC_MAX_SAMPLES * 2];
+ int gotsize = engine->getNextSamples((u8*)buf, ATRAC_MAX_SAMPLES * sizeof(s16) * atrac->atracChannels);
+ numSamples = gotsize / sizeof(s16) / atrac->atracChannels;
+ s16* in = buf;
+ s16* out = (s16*)Memory::GetPointer(outAddr);
+ for (int i = 0; i < numSamples; i++) {
+ s16 sampleL = *in++;
+ s16 sampleR = sampleL;
+ if (atrac->atracChannels == 2)
+ sampleR = *in++;
+ *out++ = sampleL;
+ if (atrac->atracOutputChannels == 2)
+ *out++ = sampleR;
+ }
+ } else
+#endif // _USE_DSHOW
{
numSamples = atrac->endSample - atrac->currentSample;
if (atrac->currentSample >= atrac->endSample) {
@@ -534,6 +586,10 @@ u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF
if (atrac->loopNum != 0 && (atrac->currentSample >= atrac->loopEndSample ||
(numSamples == 0 && atrac->first.size >= atrac->first.filesize))) {
atrac->currentSample = atrac->loopStartSample;
+#ifdef _USE_DSHOW_
+ if (engine)
+ engine->setPlaySample(atrac->currentSample);
+#endif // _USE_DSHOW
if (atrac->loopNum > 0)
atrac->loopNum --;
} else if (atrac->currentSample >= atrac->endSample ||
@@ -784,6 +840,13 @@ u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFirstBuf,
//return -1;
} else {
atrac->currentSample = sample;
+#ifdef _USE_DSHOW_
+ audioEngine *engine = getaudioEngineByID(atracID);
+ if (engine != NULL)
+ {
+ engine->setPlaySample(sample);
+ }
+#endif // _USE_DSHOW_
#ifdef USE_FFMPEG
if (atrac->codeType == PSP_MODE_AT_3 && atrac->pCodecCtx) {
atrac->SeekToSample(sample);
@@ -829,6 +892,17 @@ int64_t _AtracSeekbuffer(void *opaque, int64_t offset, int whence)
int __AtracSetContext(Atrac *atrac, u32 buffer, u32 bufferSize)
{
+#ifdef _USE_DSHOW_
+ if (atrac->codeType == PSP_MODE_AT_3_PLUS && atrac->atracChannels != 1) {
+ addAtrac3Audio(atrac->data_buf, atrac->first.size, atrac->atracID);
+ if (atrac->currentSample != 0) {
+ audioEngine *engine = getaudioEngineByID(atrac->atracID);
+ if (engine)
+ engine->setPlaySample(atrac->currentSample);
+ }
+ return 0;
+ }
+#endif // _USE_DSHOW_
#ifdef USE_FFMPEG
u8* tempbuf = (u8*)av_malloc(atrac->atracBufSize);
@@ -910,22 +984,30 @@ int _AtracSetData(Atrac *atrac, u32 buffer, u32 bufferSize)
atrac->first.writableBytes = (u32)std::max((int)bufferSize - (int)atrac->first.size, 0);
atrac->first.offset = atrac->first.size;
+ // some games may reuse an atracID for playing sound
+ atrac->CleanStuff();
+
#ifdef USE_FFMPEG
if (atrac->codeType == PSP_MODE_AT_3) {
WARN_LOG(HLE, "This is an atrac3 audio");
- // some games may reuse an atracID for playing sound
- atrac->ReleaseFFMPEGContext();
-
- if (atrac->data_buf)
- delete [] atrac->data_buf;
-
atrac->data_buf = new u8[atrac->first.filesize];
Memory::Memcpy(atrac->data_buf, buffer, std::min(bufferSize, atrac->first.filesize));
return __AtracSetContext(atrac, buffer, bufferSize);
- } else if (atrac->codeType == PSP_MODE_AT_3_PLUS)
+ } else if (atrac->codeType == PSP_MODE_AT_3_PLUS) {
WARN_LOG(HLE, "This is an atrac3+ audio");
+#ifdef _USE_DSHOW_
+ if (atrac->atracChannels == 1) {
+ WARN_LOG(HLE, "Unsupported mono atrac3+ audio!");
+ } else {
+ atrac->data_buf = new u8[atrac->first.filesize];
+ Memory::Memcpy(atrac->data_buf, buffer, std::min(bufferSize, atrac->first.filesize));
+
+ return __AtracSetContext(atrac, buffer, bufferSize);
+ }
+#endif // _USE_DSHOW
+ }
#endif // USE_FFMPEG
return 0;
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..08ea0dc6c5
--- /dev/null
+++ b/Core/HW/audioPlayer.cpp
@@ -0,0 +1,523 @@
+#ifdef _USE_DSHOW_
+
+#include "audioPlayer.h"
+#include
+#include "qeditsimple.h"
+#pragma comment(lib, "Strmiids.lib")
+#pragma comment(lib, "Quartz.lib")
+
+#include "OMAConvert.h"
+#include