// Prepare a sample buffer for autocorrelation bool Autotune::PrepareSample(SmpLength maxShift) //---------------------------------------------- { // Determine which parts of the sample should be examined. SmpLength sampleOffset = 0, sampleLoopStart = 0, sampleLoopEnd = sample.nLength; if(selectionEnd >= sampleLoopStart + MIN_SAMPLE_LENGTH) { // A selection has been specified: Examine selection sampleOffset = selectionStart; sampleLoopStart = 0; sampleLoopEnd = selectionEnd - selectionStart; } else if(sample.uFlags[CHN_SUSTAINLOOP] && sample.nSustainEnd >= sample.nSustainStart + MIN_SAMPLE_LENGTH) { // A sustain loop is set: Examine sample up to sustain loop and, if necessary, execute the loop several times sampleOffset = 0; sampleLoopStart = sample.nSustainStart; sampleLoopEnd = sample.nSustainEnd; } else if(sample.uFlags[CHN_LOOP] && sample.nLoopEnd >= sample.nLoopStart + MIN_SAMPLE_LENGTH) { // A normal loop is set: Examine sample up to loop and, if necessary, execute the loop several times sampleOffset = 0; sampleLoopStart = sample.nLoopStart; sampleLoopEnd = sample.nLoopEnd; } // We should analyse at least a one second (= GetSampleRate() samples) long sample. sampleLength = std::max<SmpLength>(sampleLoopEnd, sample.GetSampleRate(modType)) + maxShift; sampleLength = (sampleLength + 7) & ~7; if(sampleData != nullptr) { delete[] sampleData; } sampleData = new int16[sampleLength]; if(sampleData == nullptr) { return false; } // Copy sample over. switch(sample.GetElementarySampleSize()) { case 1: CopySamples(static_cast<int8 *>(sample.pSample) + sampleOffset * sample.GetNumChannels(), sampleLoopStart, sampleLoopEnd); return true; case 2: CopySamples(static_cast<int16 *>(sample.pSample) + sampleOffset * sample.GetNumChannels(), sampleLoopStart, sampleLoopEnd); return true; } return false; }
/// Retrieves a portion of the 64K summary buffer from this BlockFile. This /// data provides information about the minimum value, the maximum /// value, and the maximum RMS value for every group of 64K samples in the /// file. /// /// @param *buffer The area where the summary information will be /// written. It must be at least len*3 long. /// @param start The offset in 64K-sample increments /// @param len The number of 64K-sample summary frames to read bool BlockFile::Read64K(float *buffer, sampleCount start, sampleCount len) { wxASSERT(start >= 0); char *summary = new char[mSummaryInfo.totalSummaryBytes]; this->ReadSummary(summary); if (start+len > mSummaryInfo.frames64K) len = mSummaryInfo.frames64K - start; CopySamples(summary + mSummaryInfo.offset64K + (start * mSummaryInfo.bytesPerFrame), mSummaryInfo.format, (samplePtr)buffer, floatSample, len*mSummaryInfo.fields); if (mSummaryInfo.fields == 2) { // No RMS info; make guess int i; for(i=len-1; i>=0; i--) { buffer[3*i+2] = (fabs(buffer[2*i]) + fabs(buffer[2*i+1]))/4.0; buffer[3*i+1] = buffer[2*i+1]; buffer[3*i] = buffer[2*i]; } } delete[] summary; return true; }
/// A thread-safe version of CalcSummary. BlockFile::CalcSummary /// uses a static summary array across the class, which we can't use. /// Get a buffer containing a summary block describing this sample /// data. This must be called by derived classes when they /// are constructed, to allow them to construct their summary data, /// after which they should write that data to their disk file. /// /// This method also has the side effect of setting the mMin, mMax, /// and mRMS members of this class. /// /// Unlike BlockFile's implementation You SHOULD DELETE the returned buffer. /// this is protected so it shouldn't be hard to deal with - just override /// all BlockFile methods that use this method. /// /// @param buffer A buffer containing the sample data to be analyzed /// @param len The length of the sample data /// @param format The format of the sample data. void *ODPCMAliasBlockFile::CalcSummary(samplePtr buffer, size_t len, sampleFormat format, ArrayOf<char> &cleanup) { cleanup.reinit(mSummaryInfo.totalSummaryBytes); char* localFullSummary = cleanup.get(); memcpy(localFullSummary, aheaderTag, aheaderTagLen); float *summary64K = (float *)(localFullSummary + mSummaryInfo.offset64K); float *summary256 = (float *)(localFullSummary + mSummaryInfo.offset256); float *fbuffer; //mchinen: think we can hack this - don't allocate and copy if we don't need to., if(format==floatSample) { fbuffer = (float*)buffer; } else { fbuffer = new float[len]; CopySamples(buffer, format, (samplePtr)fbuffer, floatSample, len); } BlockFile::CalcSummaryFromBuffer(fbuffer, len, summary256, summary64K); //if we've used the float sample.. if(format!=floatSample) { delete[] fbuffer; } return localFullSummary; }
int RingBuffer::Get(samplePtr buffer, sampleFormat format, int samplesToCopy) { samplePtr dest; int block; int copied; int len = Len(); if (samplesToCopy > len) samplesToCopy = len; dest = buffer; copied = 0; while(samplesToCopy) { block = samplesToCopy; if (block > mBufferSize - mStart) block = mBufferSize - mStart; CopySamples(mBuffer.ptr() + mStart * SAMPLE_SIZE(mFormat), mFormat, dest, format, block); dest += block * SAMPLE_SIZE(format); mStart = (mStart + block) % mBufferSize; samplesToCopy -= block; copied += block; } return copied; }
int RingBuffer::Put(samplePtr buffer, sampleFormat format, int samplesToCopy) { samplePtr src; int block; int copied; int pos; int len = Len(); if (samplesToCopy > (mBufferSize-4) - len) samplesToCopy = (mBufferSize-4) - len; src = buffer; copied = 0; pos = mEnd; while(samplesToCopy) { block = samplesToCopy; if (block > mBufferSize - pos) block = mBufferSize - pos; CopySamples(src, format, mBuffer.ptr() + pos * SAMPLE_SIZE(mFormat), mFormat, block); src += block * SAMPLE_SIZE(format); pos = (pos + block) % mBufferSize; samplesToCopy -= block; copied += block; } mEnd = pos; return copied; }
size_t RingBuffer::Put(samplePtr buffer, sampleFormat format, size_t samplesToCopy) { samplesToCopy = std::min( samplesToCopy, AvailForPut() ); auto src = buffer; size_t copied = 0; auto pos = mEnd; while(samplesToCopy) { auto block = std::min( samplesToCopy, mBufferSize - pos ); CopySamples(src, format, mBuffer.ptr() + pos * SAMPLE_SIZE(mFormat), mFormat, block); src += block * SAMPLE_SIZE(format); pos = (pos + block) % mBufferSize; samplesToCopy -= block; copied += block; } mEnd = pos; return copied; }
/// A thread-safe version of CalcSummary. BlockFile::CalcSummary /// uses a static summary array across the class, which we can't use. /// Get a buffer containing a summary block describing this sample /// data. This must be called by derived classes when they /// are constructed, to allow them to construct their summary data, /// after which they should write that data to their disk file. /// /// This method also has the side effect of setting the mMin, mMax, /// and mRMS members of this class. /// /// Unlike BlockFile's implementation You SHOULD DELETE the returned buffer. /// this is protected so it shouldn't be hard to deal with - just override /// all BlockFile methods that use this method. /// /// @param buffer A buffer containing the sample data to be analyzed /// @param len The length of the sample data /// @param format The format of the sample data. void *ODDecodeBlockFile::CalcSummary(samplePtr buffer, size_t len, sampleFormat format, ArrayOf<char> &cleanup) { cleanup.reinit(mSummaryInfo.totalSummaryBytes); char* localFullSummary = cleanup.get(); memcpy(localFullSummary, bheaderTag, bheaderTagLen); float *summary64K = (float *)(localFullSummary + mSummaryInfo.offset64K); float *summary256 = (float *)(localFullSummary + mSummaryInfo.offset256); Floats floats; float *fbuffer; //mchinen: think we can hack this - don't allocate and copy if we don't need to., if(format==floatSample) { fbuffer = (float*)buffer; } else { floats.reinit(len); fbuffer = floats.get(); CopySamples(buffer, format, (samplePtr)fbuffer, floatSample, len); } BlockFile::CalcSummaryFromBuffer(fbuffer, len, summary256, summary64K); return localFullSummary; }
// Pass NULL to set silence bool Sequence::Set(samplePtr buffer, sampleFormat format, sampleCount start, sampleCount len) { if (start < 0 || start > mNumSamples || start+len > mNumSamples) return false; samplePtr temp = NULL; if (format != mSampleFormat) { temp = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(temp); } samplePtr silence = NULL; if (!buffer) { silence = NewSamples(mMaxSamples, format); wxASSERT(silence); ClearSamples(silence, format, 0, mMaxSamples); } int b = FindBlock(start); while (len) { int blen = mBlock->Item(b)->start + mBlock->Item(b)->len - start; if (blen > len) blen = len; if (buffer) { if (format == mSampleFormat) CopyWrite(buffer, mBlock->Item(b), start - mBlock->Item(b)->start, blen); else { CopySamples(buffer, format, temp, mSampleFormat, blen); CopyWrite(temp, mBlock->Item(b), start - mBlock->Item(b)->start, blen); } buffer += (blen * SAMPLE_SIZE(format)); } else CopyWrite(silence, mBlock->Item(b), start - mBlock->Item(b)->start, blen); len -= blen; start += blen; b++; } if (!buffer) DeleteSamples(silence); if (format != mSampleFormat) DeleteSamples(temp); return ConsistencyCheck("Set"); }
int EffectNyquist::GetCallback(float *buffer, int ch, long start, long len, long totlen) { if (mCurBuffer[ch]) { if ((mCurStart[ch] + start) < mCurBufferStart[ch] || (mCurStart[ch] + start)+len > mCurBufferStart[ch]+mCurBufferLen[ch]) { delete[] mCurBuffer[ch]; mCurBuffer[ch] = NULL; } } if (!mCurBuffer[ch]) { mCurBufferStart[ch] = (mCurStart[ch] + start); mCurBufferLen[ch] = mCurTrack[ch]->GetBestBlockSize(mCurBufferStart[ch]); if (mCurBufferLen[ch] < len) { mCurBufferLen[ch] = mCurTrack[ch]->GetIdealBlockSize(); } if (mCurBufferStart[ch] + mCurBufferLen[ch] > mCurStart[ch] + mCurLen) { mCurBufferLen[ch] = mCurStart[ch] + mCurLen - mCurBufferStart[ch]; } mCurBuffer[ch] = NewSamples(mCurBufferLen[ch], floatSample); if (!mCurTrack[ch]->Get(mCurBuffer[ch], floatSample, mCurBufferStart[ch], mCurBufferLen[ch])) { wxPrintf(wxT("GET error\n")); return -1; } } long offset = (mCurStart[ch] + start) - mCurBufferStart[ch]; CopySamples(mCurBuffer[ch] + offset*SAMPLE_SIZE(floatSample), floatSample, (samplePtr)buffer, floatSample, len); if (ch == 0) { double progress = mScale*(((float)start+len)/mCurLen); if (progress > mProgressIn) { mProgressIn = progress; } if (TotalProgress(mProgressIn+mProgressOut+mProgressTot)) { return -1; } } return 0; }
bool WaveClip::Append(samplePtr buffer, sampleFormat format, sampleCount len, unsigned int stride /* = 1 */, XMLWriter* blockFileLog /*=NULL*/) { //wxLogDebug(wxT("Append: len=%i"), len); sampleCount maxBlockSize = mSequence->GetMaxBlockSize(); sampleCount blockSize = mSequence->GetIdealAppendLen(); sampleFormat seqFormat = mSequence->GetSampleFormat(); if (!mAppendBuffer) mAppendBuffer = NewSamples(maxBlockSize, seqFormat); for(;;) { if (mAppendBufferLen >= blockSize) { bool success = mSequence->Append(mAppendBuffer, seqFormat, blockSize, blockFileLog); if (!success) return false; memmove(mAppendBuffer, mAppendBuffer + blockSize * SAMPLE_SIZE(seqFormat), (mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat)); mAppendBufferLen -= blockSize; blockSize = mSequence->GetIdealAppendLen(); } if (len == 0) break; int toCopy = maxBlockSize - mAppendBufferLen; if (toCopy > len) toCopy = len; CopySamples(buffer, format, mAppendBuffer + mAppendBufferLen * SAMPLE_SIZE(seqFormat), seqFormat, toCopy, true, // high quality stride); mAppendBufferLen += toCopy; buffer += toCopy * SAMPLE_SIZE(format) * stride; len -= toCopy; } UpdateEnvelopeTrackLen(); MarkChanged(); return true; }
bool WaveTrack::Append(samplePtr buffer, sampleFormat format, sampleCount len, unsigned int stride /* = 1 */) { //wxCriticalSectionLocker locker(mAppendCriticalSection); sampleCount maxBlockSize = mSequence->GetMaxBlockSize(); sampleCount blockSize = mSequence->GetIdealAppendLen(); sampleFormat seqFormat = mSequence->GetSampleFormat(); if (!mAppendBuffer) mAppendBuffer = NewSamples(maxBlockSize, seqFormat); for(;;) { if (mAppendBufferLen >= blockSize) { bool success = mSequence->Append(mAppendBuffer, seqFormat, blockSize); if (!success) return false; memmove(mAppendBuffer, mAppendBuffer + blockSize * SAMPLE_SIZE(seqFormat), (mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat)); mAppendBufferLen -= blockSize; blockSize = mSequence->GetIdealAppendLen(); } if (len == 0) break; int toCopy = maxBlockSize - mAppendBufferLen; if (toCopy > len) toCopy = len; CopySamples(buffer, format, mAppendBuffer + mAppendBufferLen * SAMPLE_SIZE(seqFormat), seqFormat, toCopy, true, /* high quality */ stride); mAppendBufferLen += toCopy; buffer += toCopy * SAMPLE_SIZE(format) * stride; len -= toCopy; } mEnvelope->SetTrackLen(mSequence->GetNumSamples() / mRate); MarkChanged(); return true; }
/// Reads the specified data from the aliased file, using libsndfile, /// and converts it to the given sample format. /// /// @param data The buffer to read the sample data into. /// @param format The format to convert the data into /// @param start The offset within the block to begin reading /// @param len The number of samples to read int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { SF_INFO info; memset(&info, 0, sizeof(info)); SNDFILE *sf = sf_open(mAliasedFullPath, SFM_READ, &info); if (!sf) return 0; sf_seek(sf, mAliasStart + start, SEEK_SET); samplePtr buffer = NewSamples(len * info.channels, floatSample); int framesRead = 0; if (format == int16Sample && !sf_subtype_more_than_16_bits(info.format)) { // Special case: if the file is in 16-bit (or less) format, // and the calling method wants 16-bit data, go ahead and // read 16-bit data directly. This is a pretty common // case, as most audio files are 16-bit. framesRead = sf_readf_short(sf, (short *)buffer, len); for (int i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer)[(info.channels * i) + mAliasChannel]; } else { // Otherwise, let libsndfile handle the conversion and // scaling, and pass us normalized data as floats. We can // then convert to whatever format we want. framesRead = sf_readf_float(sf, (float *)buffer, len); float *bufferPtr = &((float *)buffer)[mAliasChannel]; CopySamples((samplePtr)bufferPtr, floatSample, (samplePtr)data, format, framesRead, true, info.channels); } DeleteSamples(buffer); sf_close(sf); return framesRead; }
sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrack *track, longSampleCount *pos) { int slen = mMaxOut; int c; double t = *pos / track->GetRate(); if (t + slen/track->GetRate() > mT1) slen = (int)((mT1 - t) * track->GetRate() + 0.5); if (slen <= 0) return 0; if (slen > mMaxOut) slen = mMaxOut; track->Get((samplePtr)mFloatBuffer, floatSample, *pos, slen); track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate); for(int i=0; i<slen; i++) mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here? if (mFormat == track->GetSampleFormat()) { CopySamplesNoDither((samplePtr)mFloatBuffer, floatSample, mTemp, mFormat, slen); } else { CopySamples((samplePtr)mFloatBuffer, floatSample, mTemp, mFormat, slen); } for(c=0; c<mNumChannels; c++) if (mApplyTrackGains) mGains[c] = track->GetChannelGain(c); else mGains[c] = 1.0; MixBuffers(mNumChannels, channelFlags, mGains, mFormat, mTemp, mBuffer, slen, mInterleaved); *pos += slen; return slen; }
bool Sequence::ConvertToSampleFormat(sampleFormat format) { if (format == mSampleFormat) return true; if (mBlock->Count() == 0) { mSampleFormat = format; return true; } sampleFormat oldFormat = mSampleFormat; mSampleFormat = format; for (unsigned int i = 0; i < mBlock->Count(); i++) { BlockFile *oldBlock = mBlock->Item(i)->f; sampleCount len = mBlock->Item(i)->len; if (!oldBlock->IsAlias()) { BlockFile *newBlock = mDirManager->NewBlockFile(mSummary->totalSummaryBytes); samplePtr buffer1 = NewSamples(len, oldFormat); samplePtr buffer2 = NewSamples(len, mSampleFormat); oldBlock->ReadData(buffer1, oldFormat, 0, len); CopySamples(buffer1, oldFormat, buffer2, mSampleFormat, len); newBlock->WriteData(buffer2, mSampleFormat, len); mBlock->Item(i)->f = newBlock; mDirManager->Deref(oldBlock); UpdateSummaries(buffer2, mBlock->Item(i), len); DeleteSamples(buffer2); DeleteSamples(buffer1); } } return true; }
size_t RingBuffer::Get(samplePtr buffer, sampleFormat format, size_t samplesToCopy) { samplesToCopy = std::min( samplesToCopy, Len() ); auto dest = buffer; size_t copied = 0; while(samplesToCopy) { auto block = std::min( samplesToCopy, mBufferSize - mStart ); CopySamples(mBuffer.ptr() + mStart * SAMPLE_SIZE(mFormat), mFormat, dest, format, block); dest += block * SAMPLE_SIZE(format); mStart = (mStart + block) % mBufferSize; samplesToCopy -= block; copied += block; } return copied; }
bool Sequence::ConvertToSampleFormat(sampleFormat format) { if (format == mSampleFormat) return true; if (mBlock->Count() == 0) { mSampleFormat = format; return true; } sampleFormat oldFormat = mSampleFormat; mSampleFormat = format; for (unsigned int i = 0; i < mBlock->Count(); i++) { SeqBlock *b = mBlock->Item(i); BlockFile *oldBlock = b->f; sampleCount len = b->f->GetLength(); if (!oldBlock->IsAlias()) { BlockFile *newBlock; samplePtr buffer1 = NewSamples(len, oldFormat); samplePtr buffer2 = NewSamples(len, mSampleFormat); oldBlock->ReadData(buffer1, oldFormat, 0, len); CopySamples(buffer1, oldFormat, buffer2, mSampleFormat, len); newBlock = mDirManager->NewSimpleBlockFile(buffer2, len, mSampleFormat); mBlock->Item(i)->f = newBlock; mDirManager->Deref(oldBlock); DeleteSamples(buffer2); DeleteSamples(buffer1); } } return true; }
bool Sequence::Append(samplePtr buffer, sampleFormat format, sampleCount len, XMLWriter* blockFileLog /*=NULL*/) { // Quick check to make sure that it doesn't overflow if (((double)mNumSamples) + ((double)len) > wxLL(9223372036854775807)) return false; samplePtr temp = NULL; if (format != mSampleFormat) { temp = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(temp); } // If the last block is not full, we need to add samples to it int numBlocks = mBlock->Count(); if (numBlocks > 0 && mBlock->Item(numBlocks - 1)->f->GetLength() < mMinSamples) { SeqBlock *lastBlock = mBlock->Item(numBlocks - 1); sampleCount addLen; if (lastBlock->f->GetLength() + len < mMaxSamples) addLen = len; else addLen = GetIdealBlockSize() - lastBlock->f->GetLength(); SeqBlock *newLastBlock = new SeqBlock(); samplePtr buffer2 = NewSamples((lastBlock->f->GetLength() + addLen), mSampleFormat); Read(buffer2, mSampleFormat, lastBlock, 0, lastBlock->f->GetLength()); CopySamples(buffer, format, buffer2 + lastBlock->f->GetLength() * SAMPLE_SIZE(mSampleFormat), mSampleFormat, addLen); newLastBlock->start = lastBlock->start; int newLastBlockLen = lastBlock->f->GetLength() + addLen; newLastBlock->f = mDirManager->NewSimpleBlockFile(buffer2, newLastBlockLen, mSampleFormat, blockFileLog != NULL); if (blockFileLog) ((SimpleBlockFile*)newLastBlock->f)->SaveXML(*blockFileLog); DeleteSamples(buffer2); mDirManager->Deref(lastBlock->f); delete lastBlock; mBlock->Item(numBlocks - 1) = newLastBlock; len -= addLen; mNumSamples += addLen; buffer += addLen * SAMPLE_SIZE(format); } // Append the rest as new blocks while (len) { sampleCount idealSamples = GetIdealBlockSize(); sampleCount l = (len > idealSamples ? idealSamples : len); SeqBlock *w = new SeqBlock(); w->start = mNumSamples; if (format == mSampleFormat) { w->f = mDirManager->NewSimpleBlockFile(buffer, l, mSampleFormat, blockFileLog != NULL); } else { CopySamples(buffer, format, temp, mSampleFormat, l); w->f = mDirManager->NewSimpleBlockFile(temp, l, mSampleFormat, blockFileLog != NULL); } if (blockFileLog) ((SimpleBlockFile*)w->f)->SaveXML(*blockFileLog); mBlock->Add(w); buffer += l * SAMPLE_SIZE(format); mNumSamples += l; len -= l; } if (format != mSampleFormat) DeleteSamples(temp); // JKC: During generate we use Append again and again. // If generating a long sequence this test would give O(n^2) // performance - not good! #ifdef VERY_SLOW_CHECKING ConsistencyCheck(wxT("Append")); #endif return true; }
// Pass NULL to set silence bool Sequence::Set(samplePtr buffer, sampleFormat format, sampleCount start, sampleCount len) { if (start < 0 || start > mNumSamples || start+len > mNumSamples) return false; samplePtr temp = NULL; if (format != mSampleFormat) { temp = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(temp); } samplePtr silence = NULL; if (!buffer) { silence = NewSamples(mMaxSamples, format); wxASSERT(silence); ClearSamples(silence, format, 0, mMaxSamples); } int b = FindBlock(start); while (len) { int blen = mBlock->Item(b)->start + mBlock->Item(b)->f->GetLength() - start; if (blen > len) blen = len; if (buffer) { if (format == mSampleFormat) CopyWrite(buffer, mBlock->Item(b), start - mBlock->Item(b)->start, blen); else { CopySamples(buffer, format, temp, mSampleFormat, blen); CopyWrite(temp, mBlock->Item(b), start - mBlock->Item(b)->start, blen); } buffer += (blen * SAMPLE_SIZE(format)); } else { // If it's a full block of silence if (start == mBlock->Item(b)->start && blen == mBlock->Item(b)->f->GetLength()) { mDirManager->Deref(mBlock->Item(b)->f); mBlock->Item(b)->f = new SilentBlockFile(blen); } else { // Otherwise write silence just to the portion of the block CopyWrite(silence, mBlock->Item(b), start - mBlock->Item(b)->start, blen); } } len -= blen; start += blen; b++; } if (!buffer) DeleteSamples(silence); if (format != mSampleFormat) DeleteSamples(temp); return ConsistencyCheck(wxT("Set")); }
/// A thread-safe version of CalcSummary. BlockFile::CalcSummary /// uses a static summary array across the class, which we can't use. /// Get a buffer containing a summary block describing this sample /// data. This must be called by derived classes when they /// are constructed, to allow them to construct their summary data, /// after which they should write that data to their disk file. /// /// This method also has the side effect of setting the mMin, mMax, /// and mRMS members of this class. /// /// Unlike BlockFile's implementation You SHOULD delete the returned buffer. /// this is protected so it shouldn't be hard to deal with - just override /// all BlockFile methods that use this method. /// /// @param buffer A buffer containing the sample data to be analyzed /// @param len The length of the sample data /// @param format The format of the sample data. void *ODPCMAliasBlockFile::CalcSummary(samplePtr buffer, sampleCount len, sampleFormat format) { char* localFullSummary = new char[mSummaryInfo.totalSummaryBytes]; memcpy(localFullSummary, aheaderTag, aheaderTagLen); float *summary64K = (float *)(localFullSummary + mSummaryInfo.offset64K); float *summary256 = (float *)(localFullSummary + mSummaryInfo.offset256); float *fbuffer; //mchinen: think we can hack this - don't allocate and copy if we don't need to., if(format==floatSample) { fbuffer = (float*)buffer; } else { fbuffer = new float[len]; CopySamples(buffer, format, (samplePtr)fbuffer, floatSample, len); } sampleCount sumLen; sampleCount i, j, jcount; float min, max; float sumsq; // Recalc 256 summaries sumLen = (len + 255) / 256; for (i = 0; i < sumLen; i++) { min = fbuffer[i * 256]; max = fbuffer[i * 256]; sumsq = ((float)min) * ((float)min); jcount = 256; if (i * 256 + jcount > len) jcount = len - i * 256; for (j = 1; j < jcount; j++) { float f1 = fbuffer[i * 256 + j]; sumsq += ((float)f1) * ((float)f1); if (f1 < min) min = f1; else if (f1 > max) max = f1; } float rms = (float)sqrt(sumsq / jcount); summary256[i * 3] = min; summary256[i * 3 + 1] = max; summary256[i * 3 + 2] = rms; } for (i = sumLen; i < mSummaryInfo.frames256; i++) { summary256[i * 3] = 0.0f; summary256[i * 3 + 1] = 0.0f; summary256[i * 3 + 2] = 0.0f; } // Recalc 64K summaries sumLen = (len + 65535) / 65536; for (i = 0; i < sumLen; i++) { min = summary256[3 * i * 256]; max = summary256[3 * i * 256 + 1]; sumsq = (float)summary256[3 * i * 256 + 2]; sumsq *= sumsq; for (j = 1; j < 256; j++) { if (summary256[3 * (i * 256 + j)] < min) min = summary256[3 * (i * 256 + j)]; if (summary256[3 * (i * 256 + j) + 1] > max) max = summary256[3 * (i * 256 + j) + 1]; float r1 = summary256[3 * (i * 256 + j) + 2]; sumsq += r1*r1; } float rms = (float)sqrt(sumsq / 256); summary64K[i * 3] = min; summary64K[i * 3 + 1] = max; summary64K[i * 3 + 2] = rms; } for (i = sumLen; i < mSummaryInfo.frames64K; i++) { summary64K[i * 3] = 0.0f; summary64K[i * 3 + 1] = 0.0f; summary64K[i * 3 + 2] = 0.0f; } // Recalc block-level summary min = summary64K[0]; max = summary64K[1]; sumsq = (float)summary64K[2]; sumsq *= sumsq; for (i = 1; i < sumLen; i++) { if (summary64K[3*i] < min) min = summary64K[3*i]; if (summary64K[3*i+1] > max) max = summary64K[3*i+1]; float r1 = (float)summary64K[3*i+2]; sumsq += (r1*r1); } mMin = min; mMax = max; mRMS = sqrt(sumsq / sumLen); //if we've used the float sample.. if(format!=floatSample) { delete[] fbuffer; } return localFullSummary; }
int BlockFile::ReadData(void *data, sampleFormat format, sampleCount start, sampleCount len) { int i; if (mType == BLOCK_TYPE_ALIAS) { SF_INFO info; SNDFILE *sf = sf_open(mAliasFullPath, SFM_READ, &info); if (!sf) return 0; sf_seek(sf, mStart + start, SEEK_SET); samplePtr buffer = NewSamples(len * info.channels, floatSample); int framesRead = 0; switch(format) { case int16Sample: framesRead = sf_readf_short(sf, (short *)buffer, len); for (i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer)[(info.channels * i) + mChannel]; break; case floatSample: framesRead = sf_readf_float(sf, (float *)buffer, len); for (i = 0; i < framesRead; i++) ((float *)data)[i] = ((float *)buffer)[(info.channels * i) + mChannel]; break; default: framesRead = sf_readf_float(sf, (float *)buffer, len); for (i = 0; i < framesRead; i++) ((float *)buffer)[i] = ((float *)buffer)[(info.channels * i) + mChannel]; CopySamples((samplePtr)buffer, floatSample, (samplePtr)data, format, framesRead); } DeleteSamples(buffer); sf_close(sf); return framesRead; } else { wxFFile file; int read; if (!file.Open((const wxChar *) mFullPath, "rb")) return 0; file.Seek(mSummaryLen + start * SAMPLE_SIZE(mSampleFormat), wxFromStart); if (format == mSampleFormat) { int bytes = len * SAMPLE_SIZE(mSampleFormat); read = (int)file.Read(data, (size_t)bytes); read /= SAMPLE_SIZE(mSampleFormat); } else { samplePtr buffer = NewSamples(len, mSampleFormat); int srcBytes = len * SAMPLE_SIZE(mSampleFormat); read = (int)file.Read(buffer, (size_t)srcBytes); read /= SAMPLE_SIZE(mSampleFormat); CopySamples(buffer, mSampleFormat, (samplePtr)data, format, read); DeleteSamples(buffer); } file.Close(); return read; } }
/// Get a buffer containing a summary block describing this sample /// data. This must be called by derived classes when they /// are constructed, to allow them to construct their summary data, /// after which they should write that data to their disk file. /// /// This method also has the side effect of setting the mMin, mMax, /// and mRMS members of this class. /// /// You must not delete the returned buffer; it is static to this /// method. /// /// @param buffer A buffer containing the sample data to be analyzed /// @param len The length of the sample data /// @param format The format of the sample data. void *BlockFile::CalcSummary(samplePtr buffer, sampleCount len, sampleFormat format) { if(fullSummary)delete[] fullSummary; fullSummary = new char[mSummaryInfo.totalSummaryBytes]; memcpy(fullSummary, headerTag, headerTagLen); float *summary64K = (float *)(fullSummary + mSummaryInfo.offset64K); float *summary256 = (float *)(fullSummary + mSummaryInfo.offset256); float *fbuffer = new float[len]; CopySamples(buffer, format, (samplePtr)fbuffer, floatSample, len); sampleCount sumLen; sampleCount i, j, jcount; float min, max; float sumsq; // Recalc 256 summaries sumLen = (len + 255) / 256; for (i = 0; i < sumLen; i++) { min = fbuffer[i * 256]; max = fbuffer[i * 256]; sumsq = ((float)min) * ((float)min); jcount = 256; if (i * 256 + jcount > len) jcount = len - i * 256; for (j = 1; j < jcount; j++) { float f1 = fbuffer[i * 256 + j]; sumsq += ((float)f1) * ((float)f1); if (f1 < min) min = f1; else if (f1 > max) max = f1; } float rms = (float)sqrt(sumsq / jcount); summary256[i * 3] = min; summary256[i * 3 + 1] = max; summary256[i * 3 + 2] = rms; } for (i = sumLen; i < mSummaryInfo.frames256; i++) { summary256[i * 3] = 0.0f; summary256[i * 3 + 1] = 0.0f; summary256[i * 3 + 2] = 0.0f; } // Recalc 64K summaries sumLen = (len + 65535) / 65536; for (i = 0; i < sumLen; i++) { min = summary256[3 * i * 256]; max = summary256[3 * i * 256 + 1]; sumsq = (float)summary256[3 * i * 256 + 2]; sumsq *= sumsq; for (j = 1; j < 256; j++) { if (summary256[3 * (i * 256 + j)] < min) min = summary256[3 * (i * 256 + j)]; if (summary256[3 * (i * 256 + j) + 1] > max) max = summary256[3 * (i * 256 + j) + 1]; float r1 = summary256[3 * (i * 256 + j) + 2]; sumsq += r1*r1; } float rms = (float)sqrt(sumsq / 256); summary64K[i * 3] = min; summary64K[i * 3 + 1] = max; summary64K[i * 3 + 2] = rms; } for (i = sumLen; i < mSummaryInfo.frames64K; i++) { summary64K[i * 3] = 0.0f; summary64K[i * 3 + 1] = 0.0f; summary64K[i * 3 + 2] = 0.0f; } // Recalc block-level summary min = summary64K[0]; max = summary64K[1]; sumsq = (float)summary64K[2]; sumsq *= sumsq; for (i = 1; i < sumLen; i++) { if (summary64K[3*i] < min) min = summary64K[3*i]; else if (summary64K[3*i+1] > max) max = summary64K[3*i+1]; float r1 = (float)summary64K[3*i+2]; sumsq += (r1*r1); } mMin = min; mMax = max; mRMS = sqrt(sumsq / sumLen); delete[] fbuffer; return fullSummary; }
bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, sampleCount *where, int numPixels, double t0, double pixelsPerSecond, bool &isLoadingOD) { mWaveCacheMutex.Lock(); if (mWaveCache && mWaveCache->dirty == mDirty && mWaveCache->start == t0 && mWaveCache->len >= numPixels && mWaveCache->pps == pixelsPerSecond) { //check for invalid regions, and make the bottom if an else if. //invalid regions are kept in a sorted array. for(int i=0;i<mWaveCache->GetNumInvalidRegions();i++) { int invStart; invStart = mWaveCache->GetInvalidRegionStart(i); int invEnd; invEnd = mWaveCache->GetInvalidRegionEnd(i); int regionODPixels; regionODPixels =0; int regionODPixelsAfter; regionODPixelsAfter =0; //before check number of ODPixels for(int j=invStart;j<invEnd;j++) { if(mWaveCache->bl[j]<0) regionODPixels++; } mSequence->GetWaveDisplay(&mWaveCache->min[invStart], &mWaveCache->max[invStart], &mWaveCache->rms[invStart], &mWaveCache->bl[invStart], invEnd-invStart, &mWaveCache->where[invStart], mRate / pixelsPerSecond); //after check number of ODPixels for(int j=invStart;j<invEnd;j++) { if(mWaveCache->bl[j]<0) regionODPixelsAfter++; } //decrement the number of od pixels. mWaveCache->numODPixels -= (regionODPixels - regionODPixelsAfter); } mWaveCache->ClearInvalidRegions(); memcpy(min, mWaveCache->min, numPixels*sizeof(float)); memcpy(max, mWaveCache->max, numPixels*sizeof(float)); memcpy(rms, mWaveCache->rms, numPixels*sizeof(float)); memcpy(bl, mWaveCache->bl, numPixels*sizeof(int)); memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount)); isLoadingOD = mWaveCache->numODPixels>0; mWaveCacheMutex.Unlock(); return true; } WaveCache *oldCache = mWaveCache; mWaveCache = new WaveCache(numPixels); mWaveCache->pps = pixelsPerSecond; mWaveCache->rate = mRate; mWaveCache->start = t0; double tstep = 1.0 / pixelsPerSecond; sampleCount x; for (x = 0; x < mWaveCache->len + 1; x++) { mWaveCache->where[x] = (sampleCount) floor(t0 * mRate + ((double) x) * mRate * tstep + 0.5); } //mchinen: I think s0 - s1 represents the range of samples that we will need to look up. likewise p0-p1 the number of pixels. sampleCount s0 = mWaveCache->where[0]; sampleCount s1 = mWaveCache->where[mWaveCache->len]; int p0 = 0; int p1 = mWaveCache->len; // Optimization: if the old cache is good and overlaps // with the current one, re-use as much of the cache as // possible if (oldCache->dirty == mDirty && oldCache->pps == pixelsPerSecond && oldCache->where[0] < mWaveCache->where[mWaveCache->len] && oldCache->where[oldCache->len] > mWaveCache->where[0]) { //now we are assuming the entire range is covered by the old cache and reducing s1/s0 as we find out otherwise. s0 = mWaveCache->where[mWaveCache->len]; //mchinen:s0 is the min sample covered up to by the wave cache. will shrink if old doen't overlap s1 = mWaveCache->where[0]; //mchinen - same, but the maximum sample covered. p0 = mWaveCache->len; p1 = 0; //check for invalid regions, and make the bottom if an else if. //invalid regions are keep in a sorted array. //TODO:integrate into below for loop so that we only load inval regions if //necessary. (usually is the case, so no rush.) //also, we should be updating the NEW cache, but here we are patching the old one up. for(int i=0;i<oldCache->GetNumInvalidRegions();i++) { int invStart; invStart = oldCache->GetInvalidRegionStart(i); int invEnd; invEnd = oldCache->GetInvalidRegionEnd(i); mSequence->GetWaveDisplay(&oldCache->min[invStart], &oldCache->max[invStart], &oldCache->rms[invStart], &oldCache->bl[invStart], invEnd-invStart, &oldCache->where[invStart], mRate / pixelsPerSecond); } oldCache->ClearInvalidRegions(); for (x = 0; x < mWaveCache->len; x++) { //below is regular cache access. if (mWaveCache->where[x] >= oldCache->where[0] && mWaveCache->where[x] <= oldCache->where[oldCache->len - 1]) { //if we hit an invalid region, load it up. int ox = int ((double (oldCache->len) * (mWaveCache->where[x] - oldCache->where[0])) /(oldCache->where[oldCache->len] - oldCache->where[0]) + 0.5); mWaveCache->min[x] = oldCache->min[ox]; mWaveCache->max[x] = oldCache->max[ox]; mWaveCache->rms[x] = oldCache->rms[ox]; mWaveCache->bl[x] = oldCache->bl[ox]; } else { if (mWaveCache->where[x] < s0) { s0 = mWaveCache->where[x]; p0 = x; } if (mWaveCache->where[x + 1] > s1) { s1 = mWaveCache->where[x + 1]; p1 = x + 1; } } } } if (p1 > p0) { /* handle values in the append buffer */ int numSamples = mSequence->GetNumSamples(); int a; for(a=p0; a<p1; a++) if (mWaveCache->where[a+1] > numSamples) break; //compute the values that are outside the overlap from scratch. if (a < p1) { int i; sampleFormat seqFormat = mSequence->GetSampleFormat(); bool didUpdate = false; for(i=a; i<p1; i++) { sampleCount left; left = mWaveCache->where[i] - numSamples; sampleCount right; right = mWaveCache->where[i+1] - numSamples; //wxCriticalSectionLocker locker(mAppendCriticalSection); if (left < 0) left = 0; if (right > mAppendBufferLen) right = mAppendBufferLen; if (right > left) { float *b; sampleCount len = right-left; sampleCount j; if (seqFormat == floatSample) b = &((float *)mAppendBuffer)[left]; else { b = new float[len]; CopySamples(mAppendBuffer + left*SAMPLE_SIZE(seqFormat), seqFormat, (samplePtr)b, floatSample, len); } float max = b[0]; float min = b[0]; float sumsq = b[0] * b[0]; for(j=1; j<len; j++) { if (b[j] > max) max = b[j]; if (b[j] < min) min = b[j]; sumsq += b[j]*b[j]; } mWaveCache->min[i] = min; mWaveCache->max[i] = max; mWaveCache->rms[i] = (float)sqrt(sumsq / len); mWaveCache->bl[i] = 1; //for now just fake it. if (seqFormat != floatSample) delete[] b; didUpdate=true; } } // So that the sequence doesn't try to write any // of these values //mchinen: but only do this if we've updated pixels in the cache. if(didUpdate) p1 = a; } if (p1 > p0) { if (!mSequence->GetWaveDisplay(&mWaveCache->min[p0], &mWaveCache->max[p0], &mWaveCache->rms[p0], &mWaveCache->bl[p0], p1-p0, &mWaveCache->where[p0], mRate / pixelsPerSecond)) { isLoadingOD=false; mWaveCacheMutex.Unlock(); return false; } } } mWaveCache->dirty = mDirty; delete oldCache; memcpy(min, mWaveCache->min, numPixels*sizeof(float)); memcpy(max, mWaveCache->max, numPixels*sizeof(float)); memcpy(rms, mWaveCache->rms, numPixels*sizeof(float)); memcpy(bl, mWaveCache->bl, numPixels*sizeof(int)); memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount)); //find the number of OD pixels - the only way to do this is by recounting since we've lost some old cache. mWaveCache->numODPixels = 0; for(int j=0;j<mWaveCache->len;j++) if(mWaveCache->bl[j]<0) mWaveCache->numODPixels++; isLoadingOD = mWaveCache->numODPixels>0; mWaveCacheMutex.Unlock(); return true; }
sampleCount Mixer::Process(int maxToProcess) { if (mT >= mT1) return 0; int i, j; sampleCount out; sampleCount maxOut = 0; int *channelFlags = new int[mNumChannels]; mMaxOut = maxToProcess; Clear(); for(i=0; i<mNumInputTracks; i++) { WaveTrack *track = mInputTrack[i]; for(j=0; j<mNumChannels; j++) channelFlags[j] = 0; if( mMixerSpec ) { //ignore left and right when downmixing is not required for( j = 0; j < mNumChannels; j++ ) channelFlags[ j ] = mMixerSpec->mMap[ i ][ j ] ? 1 : 0; } else { switch(track->GetChannel()) { case Track::MonoChannel: default: for(j=0; j<mNumChannels; j++) channelFlags[j] = 1; break; case Track::LeftChannel: channelFlags[0] = 1; break; case Track::RightChannel: if (mNumChannels >= 2) channelFlags[1] = 1; else channelFlags[0] = 1; break; } } if (mTimeTrack || track->GetRate() != mRate) out = MixVariableRates(channelFlags, track, &mSamplePos[i], mSampleQueue[i], &mQueueStart[i], &mQueueLen[i], mSRC[i]); else out = MixSameRate(channelFlags, track, &mSamplePos[i]); if (out > maxOut) maxOut = out; } out = mInterleaved ? maxOut * mNumChannels : maxOut; for(int c=0; c<mNumBuffers; c++) CopySamples(mTemp[c], floatSample, mBuffer[c], mFormat, out); mT += (maxOut / mRate); delete [] channelFlags; return maxOut; }
/// Read the data portion of the block file using libsndfile. Convert it /// to the given format if it is not already. /// /// @param data The buffer where the data will be stored /// @param format The format the data will be stored in /// @param start The offset in this block file /// @param len The number of samples to read int SimpleBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { if (mCache.active) { //wxLogDebug("SimpleBlockFile::ReadData(): Data are already in cache."); if (len > mLen - start) len = mLen - start; CopySamples( (samplePtr)(((char*)mCache.sampleData) + start * SAMPLE_SIZE(mCache.format)), mCache.format, data, format, len); return len; } else { //wxLogDebug("SimpleBlockFile::ReadData(): Reading data from disk."); SF_INFO info; wxFile f; // will be closed when it goes out of scope SNDFILE *sf = NULL; { Maybe<wxLogNull> silence{}; if (mSilentLog) silence.create(); memset(&info, 0, sizeof(info)); if (f.Open(mFileName.GetFullPath())) { // Even though there is an sf_open() that takes a filename, use the one that // takes a file descriptor since wxWidgets can open a file with a Unicode name and // libsndfile can't (under Windows). sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE); } if (!sf) { memset(data, 0, SAMPLE_SIZE(format)*len); mSilentLog = TRUE; return len; } } mSilentLog=FALSE; sf_seek(sf, start, SEEK_SET); SampleBuffer buffer(len, floatSample); int framesRead = 0; // If both the src and dest formats are integer formats, // read integers from the file (otherwise we would be // converting to float and back, which is unneccesary) if (format == int16Sample && sf_subtype_is_integer(info.format)) { framesRead = sf_readf_short(sf, (short *)data, len); } else if (format == int24Sample && sf_subtype_is_integer(info.format)) { framesRead = sf_readf_int(sf, (int *)data, len); // libsndfile gave us the 3 byte sample in the 3 most // significant bytes -- we want it in the 3 least // significant bytes. int *intPtr = (int *)data; for( int i = 0; i < framesRead; i++ ) intPtr[i] = intPtr[i] >> 8; } else {
bool Sequence::UpdateSummaries(samplePtr buffer, SeqBlock * b, sampleCount len) { char *fullSummary = (char *)malloc(mSummary->totalSummaryBytes); memcpy(fullSummary, headerTag, headerTagLen); float *summary64K = (float *)(fullSummary + mSummary->offset64K); float *summary256 = (float *)(fullSummary + mSummary->offset256); float *fbuffer = new float[len]; CopySamples(buffer, mSampleFormat, (samplePtr)fbuffer, floatSample, len); sampleCount sumLen; sampleCount i, j, jcount; float min, max; float sumsq; // Recalc 256 summaries sumLen = (len + 255) / 256; for (i = 0; i < sumLen; i++) { min = fbuffer[i * 256]; max = fbuffer[i * 256]; sumsq = ((float)min) * ((float)min); jcount = 256; if (i * 256 + jcount > len) jcount = len - i * 256; for (j = 1; j < jcount; j++) { float f1 = fbuffer[i * 256 + j]; sumsq += ((float)f1) * ((float)f1); if (f1 < min) min = f1; else if (f1 > max) max = f1; } float rms = (float)sqrt(sumsq / jcount); summary256[i * 3] = min; summary256[i * 3 + 1] = max; summary256[i * 3 + 2] = rms; } for (i = sumLen; i < mSummary->frames256; i++) { summary256[i * 3] = 0.0f; summary256[i * 3 + 1] = 0.0f; summary256[i * 3 + 2] = 0.0f; } // Recalc 64K summaries sumLen = (len + 65535) / 65536; for (i = 0; i < sumLen; i++) { min = summary256[3 * i * 256]; max = summary256[3 * i * 256 + 1]; sumsq = (float)summary256[3 * i * 256 + 2]; sumsq *= sumsq; for (j = 1; j < 256; j++) { if (summary256[3 * (i * 256 + j)] < min) min = summary256[3 * (i * 256 + j)]; if (summary256[3 * (i * 256 + j) + 1] > max) max = summary256[3 * (i * 256 + j) + 1]; float r1 = summary256[3 * (i * 256 + j) + 2]; sumsq += r1*r1; } float rms = (float)sqrt(sumsq / 256); summary64K[i * 3] = min; summary64K[i * 3 + 1] = max; summary64K[i * 3 + 2] = rms; } for (i = sumLen; i < mSummary->frames64K; i++) { summary64K[i * 3] = 0.0f; summary64K[i * 3 + 1] = 0.0f; summary64K[i * 3 + 2] = 0.0f; } // Recalc block-level summary min = summary64K[0]; max = summary64K[1]; sumsq = (float)summary64K[2]; sumsq *= sumsq; for (i = 1; i < sumLen; i++) { if (summary64K[3*i] < min) min = summary64K[3*i]; else if (summary64K[3*i+1] > max) max = summary64K[3*i+1]; float r1 = (float)summary64K[3*i+2]; sumsq += (r1*r1); } b->min = min; b->max = max; b->rms = sqrt(sumsq / sumLen); b->f->WriteSummary(fullSummary); delete[] fbuffer; free(fullSummary); return true; }
void ComputeLegacySummaryInfo(wxFileName fileName, int summaryLen, sampleFormat format, SummaryInfo *info, bool noRMS,bool Silent, float *min, float *max, float *rms) { int fields = 3; /* min, max, rms */ if (noRMS) fields = 2; info->fields = fields; info->format = format; info->bytesPerFrame = SAMPLE_SIZE(info->format) * fields; info->totalSummaryBytes = summaryLen; info->offset64K = 20; /* legacy header tag len */ info->frames64K = (summaryLen-20) / (info->bytesPerFrame * 256); info->offset256 = info->offset64K + (info->frames64K * info->bytesPerFrame); info->frames256 = (summaryLen - 20 - (info->frames64K * info->bytesPerFrame)) / info->bytesPerFrame; // // Compute the min, max, and RMS of the block from the // 64K summary data // float *summary = new float[info->frames64K * fields]; SampleBuffer data(info->frames64K * fields, info->format); int read; { Maybe<wxLogNull> silence{}; wxFFile summaryFile(fileName.GetFullPath(), wxT("rb")); if (Silent) silence.create(); if (!summaryFile.IsOpened()) { wxLogWarning(wxT("Unable to access summary file %s; substituting silence for remainder of session"), fileName.GetFullPath().c_str()); read = info->frames64K * info->bytesPerFrame; memset(data.ptr(), 0, read); } else{ summaryFile.Seek(info->offset64K); read = summaryFile.Read(data.ptr(), info->frames64K * info->bytesPerFrame); } } int count = read / info->bytesPerFrame; CopySamples(data.ptr(), info->format, (samplePtr)summary, floatSample, count); (*min) = FLT_MAX; (*max) = FLT_MIN; float sumsq = 0; for(int i=0; i<count; i++) { if (summary[fields*i] < (*min)) (*min) = summary[fields*i]; if (summary[fields*i+1] > (*max)) (*max) = summary[fields*i+1]; if (fields >= 3) sumsq += summary[fields*i+2]*summary[fields*i+2]; } if (fields >= 3) (*rms) = sqrt(sumsq / count); else (*rms) = 0; delete[] summary; }
void ComputeLegacySummaryInfo(wxFileName fileName, int summaryLen, sampleFormat format, SummaryInfo *info, bool noRMS, float *min, float *max, float *rms) { int fields = 3; /* min, max, rms */ if (noRMS) fields = 2; info->fields = fields; info->format = format; info->bytesPerFrame = SAMPLE_SIZE(info->format) * fields; info->totalSummaryBytes = summaryLen; info->offset64K = 20; /* legacy header tag len */ info->frames64K = (summaryLen-20) / (info->bytesPerFrame * 256); info->offset256 = info->offset64K + (info->frames64K * info->bytesPerFrame); info->frames256 = (summaryLen - 20 - (info->frames64K * info->bytesPerFrame)) / info->bytesPerFrame; // // Compute the min, max, and RMS of the block from the // 64K summary data // float *summary = new float[info->frames64K * fields]; samplePtr data = NewSamples(info->frames64K * fields, info->format); wxFFile summaryFile; if( !summaryFile.Open(fileName.GetFullPath(), "rb") ) { delete[] data; return; } summaryFile.Seek(info->offset64K); int read = summaryFile.Read(data, info->frames64K * info->bytesPerFrame); int count = read / info->bytesPerFrame; CopySamples(data, info->format, (samplePtr)summary, floatSample, count); (*min) = FLT_MAX; (*max) = FLT_MIN; float sumsq = 0; for(int i=0; i<count; i++) { if (summary[fields*i] < (*min)) (*min) = summary[fields*i]; if (summary[fields*i+1] > (*max)) (*max) = summary[fields*i+1]; if (fields >= 3) sumsq += summary[fields*i+2]*summary[fields*i+2]; } if (fields >= 3) (*rms) = sqrt(sumsq / count); else (*rms) = 0; DeleteSamples(data); delete[] summary; }
/// Reads the specified data from the aliased file, using libsndfile, /// and converts it to the given sample format. /// /// @param data The buffer to read the sample data into. /// @param format The format to convert the data into /// @param start The offset within the block to begin reading /// @param len The number of samples to read int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { SF_INFO info; if(!mAliasedFileName.IsOk()){ // intentionally silenced memset(data,0,SAMPLE_SIZE(format)*len); return len; } wxFile f; // will be closed when it goes out of scope SNDFILE *sf = NULL; { Maybe<wxLogNull> silence{}; if (mSilentAliasLog) silence.create(); memset(&info, 0, sizeof(info)); if (f.Exists(mAliasedFileName.GetFullPath())) { // Don't use Open if file does not exits if (f.Open(mAliasedFileName.GetFullPath())) { // Even though there is an sf_open() that takes a filename, use the one that // takes a file descriptor since wxWidgets can open a file with a Unicode name and // libsndfile can't (under Windows). ODManager::LockLibSndFileMutex(); sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE); ODManager::UnlockLibSndFileMutex(); } } if (!sf){ memset(data, 0, SAMPLE_SIZE(format)*len); silence.reset(); mSilentAliasLog = TRUE; // Set a marker to display an error message for the silence if (!wxGetApp().ShouldShowMissingAliasedFileWarning()) wxGetApp().MarkAliasedFilesMissingWarning(this); return len; } } mSilentAliasLog=FALSE; ODManager::LockLibSndFileMutex(); sf_seek(sf, mAliasStart + start, SEEK_SET); ODManager::UnlockLibSndFileMutex(); SampleBuffer buffer(len * info.channels, floatSample); int framesRead = 0; if (format == int16Sample && !sf_subtype_more_than_16_bits(info.format)) { // Special case: if the file is in 16-bit (or less) format, // and the calling method wants 16-bit data, go ahead and // read 16-bit data directly. This is a pretty common // case, as most audio files are 16-bit. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_short(sf, (short *)buffer.ptr(), len); ODManager::UnlockLibSndFileMutex(); for (int i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer.ptr())[(info.channels * i) + mAliasChannel]; } else { // Otherwise, let libsndfile handle the conversion and // scaling, and pass us normalized data as floats. We can // then convert to whatever format we want. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_float(sf, (float *)buffer.ptr(), len); ODManager::UnlockLibSndFileMutex(); float *bufferPtr = &((float *)buffer.ptr())[mAliasChannel]; CopySamples((samplePtr)bufferPtr, floatSample, (samplePtr)data, format, framesRead, true, info.channels); } ODManager::LockLibSndFileMutex(); sf_close(sf); ODManager::UnlockLibSndFileMutex(); return framesRead; }
/// Reads the specified data from the aliased file, using libsndfile, /// and converts it to the given sample format. /// Copied from PCMAliasBlockFIle but wxLog calls taken out for thread safety /// /// @param data The buffer to read the sample data into. /// @param format The format to convert the data into /// @param start The offset within the block to begin reading /// @param len The number of samples to read int ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { mReadDataMutex.Lock(); SF_INFO info; if(!mAliasedFileName.IsOk()){ // intentionally silenced memset(data,0,SAMPLE_SIZE(format)*len); mReadDataMutex.Unlock(); return len; } memset(&info, 0, sizeof(info)); wxString aliasPath = mAliasedFileName.GetFullPath(); //there are thread-unsafe crashes here - not sure why. sf_open may be called on the same file //from different threads, but this seems okay, unless it is implemented strangely.. static ODLock sfMutex; wxFile f; // will be closed when it goes out of scope SNDFILE *sf = NULL; if (f.Open(aliasPath)) { // Even though there is an sf_open() that takes a filename, use the one that // takes a file descriptor since wxWidgets can open a file with a Unicode name and // libsndfile can't (under Windows). ODManager::LockLibSndFileMutex(); sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE); ODManager::UnlockLibSndFileMutex(); } if (!sf){ memset(data,0,SAMPLE_SIZE(format)*len); mSilentAliasLog=TRUE; mReadDataMutex.Unlock(); return len; } mSilentAliasLog=FALSE; ODManager::LockLibSndFileMutex(); sf_seek(sf, mAliasStart + start, SEEK_SET); ODManager::UnlockLibSndFileMutex(); samplePtr buffer = NewSamples(len * info.channels, floatSample); int framesRead = 0; if (format == int16Sample && !sf_subtype_more_than_16_bits(info.format)) { // Special case: if the file is in 16-bit (or less) format, // and the calling method wants 16-bit data, go ahead and // read 16-bit data directly. This is a pretty common // case, as most audio files are 16-bit. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_short(sf, (short *)buffer, len); ODManager::UnlockLibSndFileMutex(); for (int i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer)[(info.channels * i) + mAliasChannel]; } else { // Otherwise, let libsndfile handle the conversion and // scaling, and pass us normalized data as floats. We can // then convert to whatever format we want. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_float(sf, (float *)buffer, len); ODManager::UnlockLibSndFileMutex(); float *bufferPtr = &((float *)buffer)[mAliasChannel]; CopySamples((samplePtr)bufferPtr, floatSample, (samplePtr)data, format, framesRead, true, info.channels); } DeleteSamples(buffer); ODManager::LockLibSndFileMutex(); sf_close(sf); ODManager::UnlockLibSndFileMutex(); mReadDataMutex.Unlock(); return framesRead; }
bool Sequence::Append(samplePtr buffer, sampleFormat format, sampleCount len) { samplePtr temp = NULL; if (format != mSampleFormat) { temp = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(temp); } // If the last block is not full, we need to add samples to it int numBlocks = mBlock->Count(); if (numBlocks > 0 && mBlock->Item(numBlocks - 1)->len < mMinSamples) { SeqBlock *lastBlock = mBlock->Item(numBlocks - 1); sampleCount addLen; if (lastBlock->len + len < mMaxSamples) addLen = len; else addLen = GetIdealBlockSize() - lastBlock->len; SeqBlock *newLastBlock = NewInitedSeqBlock(); samplePtr buffer2 = NewSamples((lastBlock->len + addLen), mSampleFormat); Read(buffer2, mSampleFormat, lastBlock, 0, lastBlock->len); if (format == mSampleFormat) memcpy(buffer2 + lastBlock->len * SAMPLE_SIZE(format), buffer, addLen * SAMPLE_SIZE(format)); else { CopySamples(buffer, format, temp, mSampleFormat, addLen); memcpy(buffer2 + lastBlock->len * SAMPLE_SIZE(format), temp, addLen * SAMPLE_SIZE(format)); } newLastBlock->start = lastBlock->start; newLastBlock->len = lastBlock->len + addLen; FirstWrite(buffer2, newLastBlock, lastBlock->len + addLen); DeleteSamples(buffer2); mDirManager->Deref(lastBlock->f); delete lastBlock; mBlock->Item(numBlocks - 1) = newLastBlock; len -= addLen; mNumSamples += addLen; buffer += addLen * SAMPLE_SIZE(format); } // Append the rest as new blocks while (len) { sampleCount idealSamples = GetIdealBlockSize(); sampleCount l = (len > idealSamples ? idealSamples : len); SeqBlock *w = new SeqBlock(); w->f = mDirManager->NewBlockFile(mSummary->totalSummaryBytes); w->start = mNumSamples; w->len = l; if (format == mSampleFormat) FirstWrite(buffer, w, l); else { CopySamples(buffer, format, temp, mSampleFormat, l); FirstWrite(temp, w, l); } mBlock->Add(w); buffer += l * SAMPLE_SIZE(format); mNumSamples += l; len -= l; } if (format != mSampleFormat) DeleteSamples(temp); ConsistencyCheck("Append"); return true; }