rendered paste body//==============================================================================#define formatName TRANS("Ogg-Vorbis file")static const tchar* const extensions[] = { T(".ogg"), 0 };// photon#define READS_UNTIL_REDUCTION 50//==============================================================================class OggReader : public AudioFormatReader{ OggVorbis_File ovFile; ov_callbacks callbacks; AudioSampleBuffer reservoir; int reservoirStart, samplesInReservoir; int readsUntilReduction; // photon int reservoirWriteIndex; // photon public: //============================================================================== OggReader (InputStream* const inp) : AudioFormatReader (inp, formatName), reservoir (2, 2048), reservoirStart (0), samplesInReservoir (0), readsUntilReduction (READS_UNTIL_REDUCTION), // photon reservoirWriteIndex (0) // photon { sampleRate = 0; usesFloatingPointData = true; callbacks.read_func = &oggReadCallback; callbacks.seek_func = &oggSeekCallback; callbacks.close_func = &oggCloseCallback; callbacks.tell_func = &oggTellCallback; const int err = ov_open_callbacks (input, &ovFile, 0, 0, callbacks); if (err == 0) { vorbis_info* info = ov_info (&ovFile, -1); lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1); numChannels = info->channels; bitsPerSample = 16; sampleRate = info->rate; reservoir.setSize (numChannels, (int) jmin (lengthInSamples, (int64) reservoir.getNumSamples())); } } ~OggReader() { ov_clear (&ovFile); } //============================================================================== // photon bool read (int** destSamples, int64 startSampleInFile, int numSamples) { // out-of-bounds read, fillin zeros if (startSampleInFile > lengthInSamples || startSampleInFile < 0) { for (uint32 i = 0; i < numChannels && destSamples[i]; i++) zeromem(destSamples[i], sizeof(float) * numSamples); return true; } // resize reservoir int possibleNewReservoirSize = (numSamples + 1024 - 1 - ((numSamples - 1) % 1024)) + 1024; if (possibleNewReservoirSize > reservoir.getNumSamples() || (possibleNewReservoirSize < reservoir.getNumSamples() / 2 && !readsUntilReduction)) { // only resize reservoir if it needs to be larger, or if the last // READS_UNTIL_REDUCTION reads allow a size half as big as the current one. // this way single small reads wont trigger a reduction reservoir.setSize(numChannels, possibleNewReservoirSize); ov_pcm_seek (&ovFile, startSampleInFile); reservoirStart = startSampleInFile; samplesInReservoir = 0; reservoirWriteIndex = 0; readsUntilReduction = READS_UNTIL_REDUCTION; } else if (possibleNewReservoirSize < reservoir.getNumSamples() / 2) --readsUntilReduction; else readsUntilReduction = READS_UNTIL_REDUCTION; // seek if (startSampleInFile != reservoirStart) { ov_pcm_seek (&ovFile, startSampleInFile); reservoirStart = startSampleInFile; samplesInReservoir = 0; reservoirWriteIndex = 0; } // decode if (samplesInReservoir < numSamples) { while (samplesInReservoir < reservoir.getNumSamples()) { float** samples = NULL; int numToRead = reservoir.getNumSamples() - samplesInReservoir; int numToWrite = ov_read_float (&ovFile, &samples, numToRead, 0); int numToWriteAsFirstBlock = jmin (reservoir.getNumSamples() - reservoirWriteIndex, numToWrite); if (! numToWrite) break; for (int i = jmin (numChannels, reservoir.getNumChannels()); --i >= 0;) { memcpy (reservoir.getSampleData (i, reservoirWriteIndex), samples[i], sizeof(float) * numToWriteAsFirstBlock); if (numToWrite > numToWriteAsFirstBlock) { memcpy (reservoir.getSampleData (i, 0), samples[i] + numToWriteAsFirstBlock, sizeof(float) * (numToWrite - numToWriteAsFirstBlock)); } } reservoirWriteIndex = (reservoirWriteIndex + numToWrite) % reservoir.getNumSamples(); samplesInReservoir += numToWrite; } } // read if (numSamples > 0) { int numToRead = jmin(numSamples, samplesInReservoir); int readIndex = reservoirWriteIndex - samplesInReservoir; if (readIndex < 0) readIndex += reservoir.getNumSamples(); int numToReadAsFirstBlock = jmin(reservoir.getNumSamples() - readIndex, numToRead); for (uint32 i = 0; i < numChannels && destSamples[i]; ++i) { int channelIndex = jmin (i, reservoir.getNumChannels()); memcpy(destSamples[i], reservoir.getSampleData(channelIndex, readIndex), sizeof(float) * numToReadAsFirstBlock); if (numToRead > numToReadAsFirstBlock) { memcpy (destSamples[i] + numToReadAsFirstBlock, reservoir.getSampleData(channelIndex, 0), sizeof(float) * (numToRead - numToReadAsFirstBlock)); } if (numSamples > numToRead) zeromem (destSamples[i] + numToRead, sizeof(float) * (numSamples - numToRead)); } reservoirStart += numToRead; samplesInReservoir -= numToRead; } return true; } //============================================================================== static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource) { return (size_t) (((InputStream*) datasource)->read (ptr, (int) (size * nmemb)) / size); } static int oggSeekCallback (void* datasource, ogg_int64_t offset, int whence) { InputStream* const in = (InputStream*) datasource; if (whence == SEEK_CUR) offset += in->getPosition(); else if (whence == SEEK_END) offset += in->getTotalLength(); in->setPosition (offset); return 0; } static int oggCloseCallback (void*) { return 0; } static long oggTellCallback (void* datasource) { return (long) ((InputStream*) datasource)->getPosition(); } juce_UseDebuggingNewOperator};