Supermodel/Src/Sound/MPEG/MpegAudio.cpp
toxieainc db455ba5c1 add undefd codepath for correct use of the 18bit DAC path (which seems to be triggered for all games)
the volume correction to bring the data back into a valid range is not really needed in practice though, only Daytona2 seems to need it, and also only extremely rarely, so lets just live with a tiny bit of clamping for that game then

while add it, make some formatting similar to MAME, and add one comment regarding a most likely wrong recent MAME change
2022-11-29 12:36:27 -08:00

135 lines
2.6 KiB
C++

#define MINIMP3_IMPLEMENTATION
#include "Pkgs/minimp3.h"
#include "MpegAudio.h"
struct Decoder
{
mp3dec_t mp3d;
mp3dec_frame_info_t info;
const uint8_t* buffer;
int size, pos;
bool loop;
bool stopped;
int numSamples;
int pcmPos;
short pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
};
static Decoder dec = { 0 };
void MpegDec::SetMemory(const uint8_t *data, int length, bool loop)
{
mp3dec_init(&dec.mp3d);
dec.buffer = data;
dec.size = length;
dec.pos = 0;
dec.numSamples = 0;
dec.pcmPos = 0;
dec.loop = loop;
dec.stopped = false;
}
void MpegDec::UpdateMemory(const uint8_t* data, int length, bool loop)
{
int diff;
if (data > dec.buffer) {
diff = (int)(data - dec.buffer);
}
else {
diff = -(int)(dec.buffer - data);
}
dec.buffer = data;
dec.size = length;
dec.pos = dec.pos - diff; // update position relative to our new start location
dec.loop = loop;
}
int MpegDec::GetPosition()
{
return (int)dec.pos;
}
void MpegDec::SetPosition(int pos)
{
dec.pos = pos;
}
static void FlushBuffer(int16_t*& left, int16_t*& right, int& numStereoSamples)
{
int numChans = dec.info.channels;
int &i = dec.pcmPos;
for (; i < (dec.numSamples * numChans) && numStereoSamples; i += numChans) {
*left++ = dec.pcm[i];
*right++ = dec.pcm[i + numChans - 1];
numStereoSamples--;
}
}
// might need to copy some silence to end the buffer if we aren't looping
static void EndWithSilence(int16_t*& left, int16_t*& right, int& numStereoSamples)
{
while (numStereoSamples)
{
*left++ = 0;
*right++ = 0;
numStereoSamples--;
}
}
static bool EndOfBuffer()
{
return dec.pos >= dec.size - HDR_SIZE;
}
void MpegDec::Stop()
{
dec.stopped = true;
}
bool MpegDec::IsLoaded()
{
return dec.buffer != nullptr;
}
void MpegDec::DecodeAudio(int16_t* left, int16_t* right, int numStereoSamples)
{
// if we are stopped return silence
if (dec.stopped || !dec.buffer) {
EndWithSilence(left, right, numStereoSamples);
}
// copy any left over samples first
FlushBuffer(left, right, numStereoSamples);
while (numStereoSamples) {
dec.numSamples = mp3dec_decode_frame(
&dec.mp3d,
dec.buffer + dec.pos,
dec.size - dec.pos,
dec.pcm,
&dec.info);
dec.pos += dec.info.frame_bytes;
dec.pcmPos = 0; // reset pos
FlushBuffer(left, right, numStereoSamples);
// check end of buffer handling
if (EndOfBuffer()) {
if (dec.loop) {
dec.pos = 0;
}
else {
EndWithSilence(left, right, numStereoSamples);
}
}
}
}