Here you go, complete with doxygen-style comments. It’s mostly just a wrapper around Audiere’s SampleSource class. I’ve found one file it won’t play (maybe it’s unseekable? I’m not entirely sure what that means), but otherwise it seems to work pretty well. Note that it only works with FileInputStreams - see the comments below.
[code]// MP3AudioFormat.h - Audiere-based audio format for reading MP3s.
#ifndef MP3AUDIOFORMAT_H_
#define MP3AUDIOFORMAT_H_
#include “juce.h”
/// Audiere-based audio format for reading MP3s.
/*!
Please note this format can only be used with MP3 files.
The Audiere library has its own file classes which cannot be simply
distentangled from the MP3-reading classes. As such, the InputStream
passed into createReaderFor() is assumed to be a FileInputStream, and we
just pass the filepath to the Audiere decoding class (and ignore the
InputStream entirely). Obviously, MemoryInputStreams etc. will not work,
though I stuck a dynamic_cast in there so you should at least be safe from
bad things happening if you tried.
Also, this implementation doesn't handle writing to MP3, as I don't need
that myself (and Audiere only does decoding anyway - you'd need something
like lame to handle the encoding).
\todo Hack out the MP3 decoding code from Audiere's MP3InputStream class to
be implemented directly here so we can use all kinds of InputStreams.
*/
class MP3AudioFormat : public AudioFormat
{
public:
/// Constructor.
MP3AudioFormat();
/// Destructor.
~MP3AudioFormat();
/// Returns an array of samplerates the format can handle.
const Array<int> getPossibleSampleRates();
/// Returns an array of bitdepths the format can handle.
const Array<int> getPossibleBitDepths();
/// Returns whether or not the format can handle stereo.
bool canDoStereo() {return true;};
/// Returns whether or not the format can handle mono.
bool canDoMono() {return true;};
/// Returns whether or not the format is compressed.
bool isCompressed() {return true;};
/// Returns an array of quality options.
const StringArray getQualityOptions();
/// Creates an AudioFormatReader for MP3s.
AudioFormatReader* createReaderFor(InputStream *sourceStream,
const bool deleteStreamIfOpeningFails);
/// (Doesn't) Create an AudioFormatWriter for MP3s.
AudioFormatWriter* createWriterFor(OutputStream *streamToWriteTo,
double sampleRateToUse,
unsigned int numberOfChannels,
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex);
juce_UseDebuggingNewOperator
};
#endif
[/code]
[code]// MP3AudioFormat.cpp - Audiere-based audio format for reading MP3s.
#include “MP3AudioFormat.h”
#include “audiere.h”
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
#define formatName TRANS(“MP3 file”)
static const tchar* const extensions[] = { T(".mp3"), 0 };
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class MP3Reader : public AudioFormatReader
{
public:
//--------------------------------------------------------------------------
MP3Reader(InputStream * const inp):
AudioFormatReader(inp, formatName),
sampleSource(0)
{
FileInputStream *phil = dynamic_cast<FileInputStream *>(inp);
//We can only handle files directly for now.
if(phil)
{
sampleSource = audiere::OpenSampleSource((const char *)(phil->getFile().getFullPathName()));
if(sampleSource)
{
//Un-seekable files are a problem, so we just don't bother with
//them.
if(sampleSource->isSeekable())
{
int channels;
int srate;
audiere::SampleFormat format;
sampleSource->getFormat(channels, srate, format);
numChannels = channels;
sampleRate = (double)srate;
switch(format) //I'm not sure this is necessary?
{
case audiere::SF_U8:
bitsPerSample = 8;
break;
case audiere::SF_S16:
bitsPerSample = 16;
break;
}
lengthInSamples = sampleSource->getLength();
//We have to convert it to 32-bits anyway, we might as well
//make it float.
usesFloatingPointData = true;
tempBuffer.setSize(16384); //Reasonable amount?
}
else
{
sampleSource->unref();
sampleSource = 0;
}
}
}
};
//--------------------------------------------------------------------------
~MP3Reader()
{
if(sampleSource)
sampleSource->unref();
};
//--------------------------------------------------------------------------
bool read(int **destSamples,
int64 startSampleInFile,
int numSamples)
{
int i;
int numRead = 0;
bool retval = true;
short *tempData = 0;
float **destFloat = (float **)destSamples;
if(numSamples == 0)
return true;
if(sampleSource)
{
//Resize tempBuffer if necessary.
//(we assume all MP3s are 16-bit - is that right?)
if((numSamples * numChannels * 2) > (unsigned)tempBuffer.getSize())
tempBuffer.setSize(numSamples * numChannels * 2);
//Read samples.
if(startSampleInFile < sampleSource->getLength())
{
//Check position.
if(sampleSource->getPosition() != startSampleInFile)
sampleSource->setPosition((int)startSampleInFile);
numRead = sampleSource->read(numSamples, tempBuffer.getData());
}
else
numRead = 0;
//Copy tempBuffer samples to destSamples.
tempData = (short *)(tempBuffer.getData());
if((numChannels == 2) && (destFloat[1] != 0))
{
for(i=0;i<numRead;++i)
{
destFloat[0][i] = (float)tempData[i*2]/32768.0f;
destFloat[1][i] = (float)tempData[(i*2)+1]/32768.0f;
}
}
else if((numChannels == 1) || (destFloat[1] == 0))
{
for(i=0;i<numRead;++i)
destFloat[0][i] = (float)tempData[i]/32768.0f;
}
//Pad with zeros if we've gone past the end of the file.
if(numRead < numSamples)
{
if((numChannels == 2) && (destFloat[1] != 0))
{
for(i=numRead;i<numSamples;++i)
{
destFloat[0][i] = 0.0f;
destFloat[1][i] = 0.0f;
}
}
else if((numChannels == 1) || (destFloat[1] == 0))
{
for(i=numRead;i<numSamples;++i)
destFloat[0][i] = 0.0f;
}
}
retval = true;
}
return retval;
};
juce_UseDebuggingNewOperator
private:
/// Our Audiere sample source.
audiere::SampleSource *sampleSource;
/// Temporary storage.
MemoryBlock tempBuffer;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
MP3AudioFormat::MP3AudioFormat():
AudioFormat(formatName, (const tchar**)extensions)
{
}
//------------------------------------------------------------------------------
MP3AudioFormat::~MP3AudioFormat()
{
}
//------------------------------------------------------------------------------
const Array MP3AudioFormat::getPossibleSampleRates()
{
//Placeholder.
const int rates[] = {0};
return Array (rates);
}
//------------------------------------------------------------------------------
const Array MP3AudioFormat::getPossibleBitDepths()
{
//Placeholder.
const int rates[] = {0};
return Array (rates);
}
//------------------------------------------------------------------------------
const StringArray MP3AudioFormat::getQualityOptions()
{
return StringArray();
}
//------------------------------------------------------------------------------
AudioFormatReader* MP3AudioFormat::createReaderFor(InputStream sourceStream,
const bool deleteStreamIfOpeningFails)
{
//Copied from the Ogg AudioFormat.
MP3Reader r = new MP3Reader(sourceStream);
if(r->sampleRate == 0)
{
if(!deleteStreamIfOpeningFails)
r->input = 0;
deleteAndZero(r);
}
return r;
}
//------------------------------------------------------------------------------
AudioFormatWriter* MP3AudioFormat::createWriterFor(OutputStream *streamToWriteTo,
double sampleRateToUse,
unsigned int numberOfChannels,
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex)
{
//Placeholder.
return 0;
}
[/code]