#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "soloud.h"
#include "dr_flac.h"
#include "dr_mp3.h"
#include "dr_wav.h"
#include "soloud_wavstream.h"
#include "soloud_file.h"
#include "stb_vorbis.h"
namespace SoLoud
{
size_t drflac_read_func(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
File *fp = (File*)pUserData;
return fp->read((unsigned char*)pBufferOut, (unsigned int)bytesToRead);
}
size_t drmp3_read_func(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
File *fp = (File*)pUserData;
return fp->read((unsigned char*)pBufferOut, (unsigned int)bytesToRead);
}
size_t drwav_read_func(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
File *fp = (File*)pUserData;
return fp->read((unsigned char*)pBufferOut, (unsigned int)bytesToRead);
}
drflac_bool32 drflac_seek_func(void* pUserData, int offset, drflac_seek_origin origin)
{
File *fp = (File*)pUserData;
if (origin != drflac_seek_origin_start)
offset += fp->pos();
fp->seek(offset);
return 1;
}
drmp3_bool32 drmp3_seek_func(void* pUserData, int offset, drmp3_seek_origin origin)
{
File *fp = (File*)pUserData;
if (origin != drmp3_seek_origin_start)
offset += fp->pos();
fp->seek(offset);
return 1;
}
drmp3_bool32 drwav_seek_func(void* pUserData, int offset, drwav_seek_origin origin)
{
File *fp = (File*)pUserData;
if (origin != drwav_seek_origin_start)
offset += fp->pos();
fp->seek(offset);
return 1;
}
WavStreamInstance::WavStreamInstance(WavStream *aParent)
{
mOggFrameSize = 0;
mParent = aParent;
mOffset = 0;
mCodec.mOgg = 0;
mCodec.mFlac = 0;
mFile = 0;
if (aParent->mMemFile)
{
MemoryFile *mf = new MemoryFile();
mFile = mf;
mf->openMem(aParent->mMemFile->getMemPtr(), aParent->mMemFile->length(), false, false);
}
else
if (aParent->mFilename)
{
DiskFile *df = new DiskFile;
mFile = df;
df->open(aParent->mFilename);
}
else
if (aParent->mStreamFile)
{
mFile = aParent->mStreamFile;
mFile->seek(0); }
else
{
return;
}
if (mFile)
{
if (mParent->mFiletype == WAVSTREAM_WAV)
{
mCodec.mWav = new drwav;
if (!drwav_init(mCodec.mWav, drwav_read_func, drwav_seek_func, (void*)mFile, NULL))
{
delete mCodec.mWav;
mCodec.mWav = 0;
if (mFile != mParent->mStreamFile)
delete mFile;
mFile = 0;
}
}
else
if (mParent->mFiletype == WAVSTREAM_OGG)
{
int e;
mCodec.mOgg = stb_vorbis_open_file((Soloud_Filehack *)mFile, 0, &e, 0);
if (!mCodec.mOgg)
{
if (mFile != mParent->mStreamFile)
delete mFile;
mFile = 0;
}
mOggFrameSize = 0;
mOggFrameOffset = 0;
mOggOutputs = 0;
}
else
if (mParent->mFiletype == WAVSTREAM_FLAC)
{
mCodec.mFlac = drflac_open(drflac_read_func, drflac_seek_func, (void*)mFile, NULL);
if (!mCodec.mFlac)
{
if (mFile != mParent->mStreamFile)
delete mFile;
mFile = 0;
}
}
else
if (mParent->mFiletype == WAVSTREAM_MP3)
{
mCodec.mMp3 = new drmp3;
if (!drmp3_init(mCodec.mMp3, drmp3_read_func, drmp3_seek_func, (void*)mFile, NULL))
{
delete mCodec.mMp3;
mCodec.mMp3 = 0;
if (mFile != mParent->mStreamFile)
delete mFile;
mFile = 0;
}
}
else
{
if (mFile != mParent->mStreamFile)
delete mFile;
mFile = NULL;
return;
}
}
}
WavStreamInstance::~WavStreamInstance()
{
switch (mParent->mFiletype)
{
case WAVSTREAM_OGG:
if (mCodec.mOgg)
{
stb_vorbis_close(mCodec.mOgg);
}
break;
case WAVSTREAM_FLAC:
if (mCodec.mFlac)
{
drflac_close(mCodec.mFlac);
}
break;
case WAVSTREAM_MP3:
if (mCodec.mMp3)
{
drmp3_uninit(mCodec.mMp3);
delete mCodec.mMp3;
mCodec.mMp3 = 0;
}
break;
case WAVSTREAM_WAV:
if (mCodec.mWav)
{
drwav_uninit(mCodec.mWav);
delete mCodec.mWav;
mCodec.mWav = 0;
}
break;
}
if (mFile != mParent->mStreamFile)
{
delete mFile;
}
}
static int getOggData(float **aOggOutputs, float *aBuffer, int aSamples, int aPitch, int aFrameSize, int aFrameOffset, int aChannels)
{
if (aFrameSize <= 0)
return 0;
int samples = aSamples;
if (aFrameSize - aFrameOffset < samples)
{
samples = aFrameSize - aFrameOffset;
}
int i;
for (i = 0; i < aChannels; i++)
{
memcpy(aBuffer + aPitch * i, aOggOutputs[i] + aFrameOffset, sizeof(float) * samples);
}
return samples;
}
unsigned int WavStreamInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
{
unsigned int offset = 0;
float tmp[512 * MAX_CHANNELS];
if (mFile == NULL)
return 0;
switch (mParent->mFiletype)
{
case WAVSTREAM_FLAC:
{
unsigned int i, j, k;
for (i = 0; i < aSamplesToRead; i += 512)
{
unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
offset += (unsigned int)drflac_read_pcm_frames_f32(mCodec.mFlac, blockSize, tmp);
for (j = 0; j < blockSize; j++)
{
for (k = 0; k < mChannels; k++)
{
aBuffer[k * aSamplesToRead + i + j] = tmp[j * mCodec.mFlac->channels + k];
}
}
}
mOffset += offset;
return offset;
}
break;
case WAVSTREAM_MP3:
{
unsigned int i, j, k;
for (i = 0; i < aSamplesToRead; i += 512)
{
unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
offset += (unsigned int)drmp3_read_pcm_frames_f32(mCodec.mMp3, blockSize, tmp);
for (j = 0; j < blockSize; j++)
{
for (k = 0; k < mChannels; k++)
{
aBuffer[k * aSamplesToRead + i + j] = tmp[j * mCodec.mMp3->channels + k];
}
}
}
mOffset += offset;
return offset;
}
break;
case WAVSTREAM_OGG:
{
if (mOggFrameOffset < mOggFrameSize)
{
int b = getOggData(mOggOutputs, aBuffer, aSamplesToRead, aBufferSize, mOggFrameSize, mOggFrameOffset, mChannels);
mOffset += b;
offset += b;
mOggFrameOffset += b;
}
while (offset < aSamplesToRead)
{
mOggFrameSize = stb_vorbis_get_frame_float(mCodec.mOgg, NULL, &mOggOutputs);
mOggFrameOffset = 0;
int b = getOggData(mOggOutputs, aBuffer + offset, aSamplesToRead - offset, aBufferSize, mOggFrameSize, mOggFrameOffset, mChannels);
mOffset += b;
offset += b;
mOggFrameOffset += b;
if (mOffset >= mParent->mSampleCount || b == 0)
{
mOffset += offset;
return offset;
}
}
}
break;
case WAVSTREAM_WAV:
{
unsigned int i, j, k;
for (i = 0; i < aSamplesToRead; i += 512)
{
unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
offset += (unsigned int)drwav_read_pcm_frames_f32(mCodec.mWav, blockSize, tmp);
for (j = 0; j < blockSize; j++)
{
for (k = 0; k < mChannels; k++)
{
aBuffer[k * aSamplesToRead + i + j] = tmp[j * mCodec.mWav->channels + k];
}
}
}
mOffset += offset;
return offset;
}
break;
}
return aSamplesToRead;
}
result WavStreamInstance::seek(double aSeconds, float* mScratch, unsigned int mScratchSize)
{
if (mCodec.mOgg)
{
int pos = (int)floor(mBaseSamplerate * aSeconds);
stb_vorbis_seek(mCodec.mOgg, pos);
mOffset = stb_vorbis_get_sample_offset(mCodec.mOgg);
double newPosition = float(mOffset / mBaseSamplerate);
mStreamPosition = newPosition;
return 0;
}
else {
return AudioSourceInstance::seek(aSeconds, mScratch, mScratchSize);
}
}
result WavStreamInstance::rewind()
{
switch (mParent->mFiletype)
{
case WAVSTREAM_OGG:
if (mCodec.mOgg)
{
stb_vorbis_seek_start(mCodec.mOgg);
}
break;
case WAVSTREAM_FLAC:
if (mCodec.mFlac)
{
drflac_seek_to_pcm_frame(mCodec.mFlac, 0);
}
break;
case WAVSTREAM_MP3:
if (mCodec.mMp3)
{
drmp3_seek_to_pcm_frame(mCodec.mMp3, 0);
}
break;
case WAVSTREAM_WAV:
if (mCodec.mWav)
{
drwav_seek_to_pcm_frame(mCodec.mWav, 0);
}
break;
}
mOffset = 0;
mStreamPosition = 0.0f;
return 0;
}
bool WavStreamInstance::hasEnded()
{
if (mOffset >= mParent->mSampleCount)
{
return 1;
}
return 0;
}
WavStream::WavStream()
{
mFilename = 0;
mSampleCount = 0;
mFiletype = WAVSTREAM_WAV;
mMemFile = 0;
mStreamFile = 0;
}
WavStream::~WavStream()
{
stop();
delete[] mFilename;
delete mMemFile;
}
#define MAKEDWORD(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a))
result WavStream::loadwav(File * fp)
{
fp->seek(0);
drwav decoder;
if (!drwav_init(&decoder, drwav_read_func, drwav_seek_func, (void*)fp, NULL))
return FILE_LOAD_FAILED;
mChannels = decoder.channels;
if (mChannels > MAX_CHANNELS)
{
mChannels = MAX_CHANNELS;
}
mBaseSamplerate = (float)decoder.sampleRate;
mSampleCount = (unsigned int)decoder.totalPCMFrameCount;
mFiletype = WAVSTREAM_WAV;
drwav_uninit(&decoder);
return SO_NO_ERROR;
}
result WavStream::loadogg(File * fp)
{
fp->seek(0);
int e;
stb_vorbis *v;
v = stb_vorbis_open_file((Soloud_Filehack *)fp, 0, &e, 0);
if (v == NULL)
return FILE_LOAD_FAILED;
stb_vorbis_info info = stb_vorbis_get_info(v);
mChannels = info.channels;
if (info.channels > MAX_CHANNELS)
{
mChannels = MAX_CHANNELS;
}
mBaseSamplerate = (float)info.sample_rate;
int samples = stb_vorbis_stream_length_in_samples(v);
stb_vorbis_close(v);
mFiletype = WAVSTREAM_OGG;
mSampleCount = samples;
return 0;
}
result WavStream::loadflac(File * fp)
{
fp->seek(0);
drflac* decoder = drflac_open(drflac_read_func, drflac_seek_func, (void*)fp, NULL);
if (decoder == NULL)
return FILE_LOAD_FAILED;
mChannels = decoder->channels;
if (mChannels > MAX_CHANNELS)
{
mChannels = MAX_CHANNELS;
}
mBaseSamplerate = (float)decoder->sampleRate;
mSampleCount = (unsigned int)decoder->totalPCMFrameCount;
mFiletype = WAVSTREAM_FLAC;
drflac_close(decoder);
return SO_NO_ERROR;
}
result WavStream::loadmp3(File * fp)
{
fp->seek(0);
drmp3 decoder;
if (!drmp3_init(&decoder, drmp3_read_func, drmp3_seek_func, (void*)fp, NULL))
return FILE_LOAD_FAILED;
mChannels = decoder.channels;
if (mChannels > MAX_CHANNELS)
{
mChannels = MAX_CHANNELS;
}
drmp3_uint64 samples = drmp3_get_pcm_frame_count(&decoder);
mBaseSamplerate = (float)decoder.sampleRate;
mSampleCount = (unsigned int)samples;
mFiletype = WAVSTREAM_MP3;
drmp3_uninit(&decoder);
return SO_NO_ERROR;
}
result WavStream::load(const char *aFilename)
{
delete[] mFilename;
delete mMemFile;
mMemFile = 0;
mFilename = 0;
mSampleCount = 0;
DiskFile fp;
int res = fp.open(aFilename);
if (res != SO_NO_ERROR)
return res;
int len = (int)strlen(aFilename);
mFilename = new char[len+1];
memcpy(mFilename, aFilename, len);
mFilename[len] = 0;
res = parse(&fp);
if (res != SO_NO_ERROR)
{
delete[] mFilename;
mFilename = 0;
return res;
}
return 0;
}
result WavStream::loadMem(const unsigned char *aData, unsigned int aDataLen, bool aCopy, bool aTakeOwnership)
{
delete[] mFilename;
delete mMemFile;
mStreamFile = 0;
mMemFile = 0;
mFilename = 0;
mSampleCount = 0;
if (aData == NULL || aDataLen == 0)
return INVALID_PARAMETER;
MemoryFile *mf = new MemoryFile();
int res = mf->openMem(aData, aDataLen, aCopy, aTakeOwnership);
if (res != SO_NO_ERROR)
{
delete mf;
return res;
}
res = parse(mf);
if (res != SO_NO_ERROR)
{
delete mf;
return res;
}
mMemFile = mf;
return 0;
}
result WavStream::loadToMem(const char *aFilename)
{
DiskFile df;
int res = df.open(aFilename);
if (res == SO_NO_ERROR)
{
res = loadFileToMem(&df);
}
return res;
}
result WavStream::loadFile(File *aFile)
{
delete[] mFilename;
delete mMemFile;
mStreamFile = 0;
mMemFile = 0;
mFilename = 0;
mSampleCount = 0;
int res = parse(aFile);
if (res != SO_NO_ERROR)
{
return res;
}
mStreamFile = aFile;
return 0;
}
result WavStream::loadFileToMem(File *aFile)
{
delete[] mFilename;
delete mMemFile;
mStreamFile = 0;
mMemFile = 0;
mFilename = 0;
mSampleCount = 0;
MemoryFile *mf = new MemoryFile();
int res = mf->openFileToMem(aFile);
if (res != SO_NO_ERROR)
{
delete mf;
return res;
}
res = parse(mf);
if (res != SO_NO_ERROR)
{
delete mf;
return res;
}
mMemFile = mf;
return res;
}
result WavStream::parse(File *aFile)
{
int tag = aFile->read32();
int res = SO_NO_ERROR;
if (tag == MAKEDWORD('O', 'g', 'g', 'S'))
{
res = loadogg(aFile);
}
else
if (tag == MAKEDWORD('R', 'I', 'F', 'F'))
{
res = loadwav(aFile);
}
else
if (tag == MAKEDWORD('f', 'L', 'a', 'C'))
{
res = loadflac(aFile);
}
else
if (loadmp3(aFile) == SO_NO_ERROR)
{
res = SO_NO_ERROR;
}
else
{
res = FILE_LOAD_FAILED;
}
return res;
}
AudioSourceInstance *WavStream::createInstance()
{
return new WavStreamInstance(this);
}
double WavStream::getLength()
{
if (mBaseSamplerate == 0)
return 0;
return mSampleCount / mBaseSamplerate;
}
};