Mixer::Mixer(int numChannels, int bufferSize, bool interleaved, double rate, sampleFormat format) { mNumChannels = numChannels; mBufferSize = bufferSize; mInterleaved = interleaved; mRate = rate; mFormat = format; mUseVolumeSlider = false; mControlToolBar= NULL; if (mInterleaved) { mNumBuffers = 1; mInterleavedBufferSize = mBufferSize * mNumChannels; } else { mNumBuffers = mNumChannels; mInterleavedBufferSize = mBufferSize; } mBuffer = new samplePtr[mNumBuffers]; for (int c = 0; c < mNumBuffers; c++) mBuffer[c] = NewSamples(mInterleavedBufferSize, mFormat); mTempBufferSize = mBufferSize*2; mTemp = NewSamples(mTempBufferSize, mFormat); mEnvValues = new double[mBufferSize]; }
// 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"); }
bool AudioIO::Start() { mT = mT0; mOutID = 1; mInID = 1; mBufferSize = 4096; mInUnderruns = 0; mRepeats = 0; unsigned int i; if (mNumInChannels > 0) { mInBuffer = new AudioIOBuffer[mMaxBuffers]; for(i=0; i<mMaxBuffers; i++) { mInBuffer[i].ID = 0; // means it's empty mInBuffer[i].len = 0; mInBuffer[i].data = NewSamples(mBufferSize * mNumInChannels, mFormat); } } else mInBuffer = NULL; if (mNumOutChannels > 0) { mOutBuffer = new AudioIOBuffer[mMaxBuffers]; for(i=0; i<mMaxBuffers; i++) { mOutBuffer[i].ID = 0; // means it's empty mOutBuffer[i].len = 0; mOutBuffer[i].data = NewSamples(mBufferSize * mNumOutChannels, mFormat); } } else mOutBuffer = NULL; FillBuffers(); if (!OpenDevice()) { wxMessageBox(_("Error opening audio device.\n" "(Change the device in the Preferences dialog.)")); return false; } PaError error = Pa_StartStream(mPortStream); mProject->SetStop(false); mProject->ReReadSettings(); mProject->HandleResize(); return (error == paNoError); }
/// Write the summary to disk. Derived classes must call this method /// from their constructors for the summary to be correctly written. /// It uses the derived class's ReadData() to retrieve the data to /// summarize. void AliasBlockFile::WriteSummary() { // Now checked carefully in the DirManager //wxASSERT( !wxFileExists(FILENAME(mFileName.GetFullPath()))); // I would much rather have this code as part of the constructor, but // I can't call virtual functions from the constructor. So we just // need to ensure that every derived class calls this in *its* constructor wxFFile summaryFile(mFileName.GetFullPath(), wxT("wb")); if( !summaryFile.IsOpened() ){ // Never silence the Log w.r.t write errors; they always count // as new errors wxLogError(wxT("Unable to write summary data to file %s"), mFileName.GetFullPath().c_str()); // If we can't write, there's nothing to do. return; } // To build the summary data, call ReadData (implemented by the // derived classes) to get the sample data samplePtr sampleData = NewSamples(mLen, floatSample); this->ReadData(sampleData, floatSample, 0, mLen); void *summaryData = BlockFile::CalcSummary(sampleData, mLen, floatSample); summaryFile.Write(summaryData, mSummaryInfo.totalSummaryBytes); DeleteSamples(sampleData); }
void Mixer::GetSamples(WaveTrack *src, int s0, int slen) { // Retrieves samples from a track, even outside of the range which // contains samples. (Fills in extra space with zeros.) // Puts samples in mTemp if (slen > mTempBufferSize) { mTempBufferSize = slen; DeleteSamples(mTemp); mTemp = NewSamples(mTempBufferSize, mFormat); } int soffset = 0; int getlen = slen; if (s0 < 0) { soffset = -s0; getlen -= soffset; s0 = 0; } if (s0+getlen > src->GetNumSamples()) { getlen = src->GetNumSamples() - s0; } src->Get(mTemp + soffset*SAMPLE_SIZE(mFormat), mFormat, (sampleCount)s0, (sampleCount)getlen); ClearSamples(mTemp, mFormat, 0, soffset); ClearSamples(mTemp, mFormat, soffset+getlen, slen-(soffset+getlen)); }
bool Sequence::CopyWrite(samplePtr buffer, SeqBlock *b, sampleCount start, sampleCount len) { // We don't ever write to an existing block; to support Undo, // we copy the old block entirely into memory, dereference it, // make the change, and then write the new block to disk. wxASSERT(b); wxASSERT(b->f->GetLength() <= mMaxSamples); wxASSERT(start + len <= b->f->GetLength()); int sampleSize = SAMPLE_SIZE(mSampleFormat); samplePtr newBuffer = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(newBuffer); Read(newBuffer, mSampleFormat, b, 0, b->f->GetLength()); memcpy(newBuffer + start*sampleSize, buffer, len*sampleSize); BlockFile *oldBlockFile = b->f; b->f = mDirManager->NewSimpleBlockFile(newBuffer, b->f->GetLength(), mSampleFormat); mDirManager->Deref(oldBlockFile); DeleteSamples(newBuffer); return true; }
/// Retrieves the minimum, maximum, and maximum RMS of the /// specified sample data in this block. /// /// @param start The offset in this block where the region should begin /// @param len The number of samples to include in the region /// @param *outMin A pointer to where the minimum value for this region /// should be stored /// @param *outMax A pointer to where the maximum value for this region /// should be stored /// @param *outRMS A pointer to where the maximum RMS value for this /// region should be stored. void BlockFile::GetMinMax(sampleCount start, sampleCount len, float *outMin, float *outMax, float *outRMS) { // TODO: actually use summaries samplePtr blockData = NewSamples(len, floatSample); this->ReadData(blockData, floatSample, start, len); float min = FLT_MAX; float max = -FLT_MAX; float sumsq = 0; for( int i = 0; i < len; i++ ) { float sample = ((float*)blockData)[i]; if( sample > max ) max = sample; if( sample < min ) min = sample; sumsq = (sample*sample); } DeleteSamples(blockData); *outMin = min; *outMax = max; *outRMS = sqrt(sumsq/len); }
/// 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 LegacyBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { SF_INFO info; memset(&info, 0, sizeof(info)); switch(mFormat) { case int16Sample: info.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_CPU; break; default: case floatSample: info.format = SF_FORMAT_RAW | SF_FORMAT_FLOAT | SF_ENDIAN_CPU; break; case int24Sample: info.format = SF_FORMAT_RAW | SF_FORMAT_PCM_32 | SF_ENDIAN_CPU; break; } info.samplerate = 44100; // Doesn't matter info.channels = 1; info.frames = mLen + (mSummaryInfo.totalSummaryBytes / SAMPLE_SIZE(mFormat)); SNDFILE *sf = sf_open(mFileName.GetFullPath(), SFM_READ, &info); if (!sf) return 0; sf_count_t seekstart = start + (mSummaryInfo.totalSummaryBytes / SAMPLE_SIZE(mFormat)); sf_seek(sf, seekstart , SEEK_SET); samplePtr buffer = NewSamples(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; }
RingBuffer::RingBuffer(sampleFormat format, int size) { mFormat = format; mBufferSize = (size > 64? size: 64); mStart = 0; mEnd = 0; mBuffer = NewSamples(mBufferSize, mFormat); }
void testReads() { // Now use the blockfile's method to read data and compare it to what we originally wrote std::cout << "\tVerifying that we can read back correctly..." << std::flush; samplePtr int16buf = NewSamples(dataLen, int16Sample); samplePtr int24buf = NewSamples(dataLen, int24Sample); samplePtr floatbuf = NewSamples(dataLen, floatSample); // First try a read of the entire buffer int16BlockFile->ReadData(int16buf, int16Sample, 0, dataLen); int24BlockFile->ReadData(int24buf, int24Sample, 0, dataLen); floatBlockFile->ReadData(floatbuf, floatSample, 0, dataLen); AssertBuffersEqual(int16Data, (short*)int16buf, dataLen); AssertBuffersEqual(int24Data, (int*)int24buf, dataLen); AssertBuffersEqual(floatData, (float*)floatbuf, dataLen); // Now test a read that starts at the beginning but quits // before the end int someOffset = 537; int16BlockFile->ReadData(int16buf, int16Sample, 0, someOffset); int24BlockFile->ReadData(int24buf, int24Sample, 0, someOffset); floatBlockFile->ReadData(floatbuf, floatSample, 0, someOffset); AssertBuffersEqual(int16Data, (short*)int16buf, someOffset); AssertBuffersEqual(int24Data, (int*)int24buf, someOffset); AssertBuffersEqual(floatData, (float*)floatbuf, someOffset); // Now try a read that starts in the middle and goes to the // end int16BlockFile->ReadData(int16buf, int16Sample, someOffset, dataLen-someOffset); int24BlockFile->ReadData(int24buf, int24Sample, someOffset, dataLen-someOffset); floatBlockFile->ReadData(floatbuf, floatSample, someOffset, dataLen-someOffset); AssertBuffersEqual(int16Data+someOffset, (short*)int16buf, dataLen-someOffset); AssertBuffersEqual(int24Data+someOffset, (int*)int24buf, dataLen-someOffset); AssertBuffersEqual(floatData+someOffset, (float*)floatbuf, dataLen-someOffset); std::cout << "OK\n"; }
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; }
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; }
/// Write the summary to disk, using the derived ReadData() to get the data void ODPCMAliasBlockFile::WriteSummary() { //the mFileName path may change, for example, when the project is saved. //(it moves from /tmp/ to wherever it is saved to. mFileNameMutex.Lock(); //wxFFile is not thread-safe - if any error occurs in opening the file, // it posts a wxlog message which WILL crash // Audacity because it goes into the wx GUI. // For this reason I left the wxFFile method commented out. (mchinen) // wxFFile summaryFile(mFileName.GetFullPath(), wxT("wb")); // ...and we use fopen instead. wxString sFullPath = mFileName.GetFullPath(); char* fileNameChar = new char[strlen(sFullPath.mb_str(wxConvFile)) + 1]; strcpy(fileNameChar, sFullPath.mb_str(wxConvFile)); FILE* summaryFile = fopen(fileNameChar, "wb"); mFileNameMutex.Unlock(); if( !summaryFile) { //.IsOpened() ){ // Never silence the Log w.r.t write errors; they always count //however, this is going to be called from a non-main thread, //and wxLog calls are not thread safe. printf("Unable to write summary data to file: %s", fileNameChar); delete [] fileNameChar; return; } delete [] fileNameChar; // To build the summary data, call ReadData (implemented by the // derived classes) to get the sample data samplePtr sampleData = NewSamples(mLen, floatSample); this->ReadData(sampleData, floatSample, 0, mLen); void *summaryData = CalcSummary(sampleData, mLen, floatSample); //summaryFile.Write(summaryData, mSummaryInfo.totalSummaryBytes); fwrite(summaryData, 1, mSummaryInfo.totalSummaryBytes, summaryFile); fclose(summaryFile); DeleteSamples(sampleData); delete [] (char *) summaryData; // printf("write successful. filename: %s\n", fileNameChar); mSummaryAvailableMutex.Lock(); mSummaryAvailable=true; mSummaryAvailableMutex.Unlock(); }
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::InsertSilence(sampleCount s0, sampleCount len) { // Create a new track containing as much silence as we // need to insert, and then call Paste to do the insertion Sequence *sTrack = new Sequence(mDirManager, mSampleFormat); sampleCount idealSamples = GetIdealBlockSize(); // Allocate a zeroed buffer samplePtr buffer = NewSamples(idealSamples, mSampleFormat); ClearSamples(buffer, mSampleFormat, 0, idealSamples); sampleCount pos = 0; BlockFile *firstBlockFile = NULL; while (len) { sampleCount l = (len > idealSamples ? idealSamples : len); SeqBlock *w = new SeqBlock(); w->start = pos; w->len = l; w->min = float(0.0); w->max = float(0.0); w->rms = float(0.0); if (pos == 0 || len == l) { w->f = mDirManager->NewBlockFile(mSummary->totalSummaryBytes); firstBlockFile = w->f; FirstWrite(buffer, w, l); } else { w->f = mDirManager->CopyBlockFile(firstBlockFile); if (!w->f) { // TODO set error message return false; } } sTrack->mBlock->Add(w); pos += l; len -= l; } sTrack->mNumSamples = pos; Paste(s0, sTrack); delete sTrack; DeleteSamples(buffer); return ConsistencyCheck("InsertSilence"); }
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 Sequence::Copy(sampleCount s0, sampleCount s1, Sequence **dest) { *dest = 0; if (s0 >= s1 || s0 >= mNumSamples || s1 < 0) return false; int numBlocks = mBlock->Count(); int b0 = FindBlock(s0); int b1 = FindBlock(s1); if (s1 == mNumSamples) b1 = numBlocks; *dest = new Sequence(mDirManager, mSampleFormat); samplePtr buffer = NewSamples(mMaxSamples, mSampleFormat); int blocklen; // Do the first block if (b0 >= 0 && b0 < numBlocks && s0 != mBlock->Item(b0)->start) { blocklen = (mBlock->Item(b0)->start + mBlock->Item(b0)->f->GetLength() - s0); if (blocklen > (s1 - s0)) blocklen = s1 - s0; Get(buffer, mSampleFormat, s0, blocklen); (*dest)->Append(buffer, mSampleFormat, blocklen); } if (b0 >= 0 && b0 < numBlocks && s0 == mBlock->Item(b0)->start) { b0--; } // If there are blocks in the middle, copy the blockfiles directly for (int b = b0 + 1; b < b1; b++) ((Sequence *)*dest)->AppendBlock(mBlock->Item(b)); // Do the last block if (b1 > b0 && b1 < numBlocks) { blocklen = (s1 - mBlock->Item(b1)->start); Get(buffer, mSampleFormat, mBlock->Item(b1)->start, blocklen); (*dest)->Append(buffer, mSampleFormat, blocklen); } DeleteSamples(buffer); 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; }
/// Write the summary to disk, using the derived ReadData() to get the data void ODPCMAliasBlockFile::WriteSummary() { //Below from BlockFile.cpp's method. We need to delete the data returned by //CalcSummary, because it uses local info. In the future we might do something //smarter and thread-dependant like a static thread context. wxFFile summaryFile(mFileName.GetFullPath(), wxT("wb")); if( !summaryFile.IsOpened() ){ // Never silence the Log w.r.t write errors; they always count // as new errors //however, this is going to be called from a non-main thread, //and wxLog calls are not thread safe. printf("Unable to write summary data to file %s", mFileName.GetFullPath().c_str()); // If we can't write, there's nothing to do. return; } // To build the summary data, call ReadData (implemented by the // derived classes) to get the sample data samplePtr sampleData = NewSamples(mLen, floatSample); this->ReadData(sampleData, floatSample, 0, mLen); void *summaryData = CalcSummary(sampleData, mLen, floatSample); summaryFile.Write(summaryData, mSummaryInfo.totalSummaryBytes); DeleteSamples(sampleData); delete [] summaryData; //above from BlockFiles.cpps method mSummaryAvailableMutex.Lock(); mSummaryAvailable=true; mSummaryAvailableMutex.Unlock(); }
bool Sequence::AppendAlias(wxString fullPath, sampleCount start, sampleCount len, int channel) { SeqBlock *newBlock = new SeqBlock(); newBlock->start = mNumSamples; newBlock->len = len; newBlock->f = mDirManager->NewBlockFile(mSummary->totalSummaryBytes); newBlock->f->SetAliasedData(fullPath, start, len, channel); samplePtr buffer = NewSamples(len, mSampleFormat); Read(buffer, mSampleFormat, newBlock, 0, len); UpdateSummaries(buffer, newBlock, len); DeleteSamples(buffer); mBlock->Add(newBlock); mNumSamples += newBlock->len; return true; }
/// Write the summary to disk, using the derived ReadData() to get the data /// Here, the decoder ODTask associated with this file must fetch the samples with /// the ODDecodeTask::Decode() method. void ODDecodeBlockFile::WriteODDecodeBlockFile() { // To build the summary data, call ReadData (implemented by the // derived classes) to get the sample data samplePtr sampleData = NewSamples(mLen, floatSample); //use the decoder here. mDecoderMutex.Lock(); if(!mDecoder) { mDecoderMutex.Unlock(); return; } mDecoder->Decode(sampleData, mFormat, mDecodeFileStart, mLen); mDecoderMutex.Unlock(); this->ReadData(sampleData, floatSample, 0, mLen); void *summaryData = CalcSummary(sampleData, mLen, floatSample); //OD TODO: use new write() // summaryFile.Write(summaryData, mSummaryInfo.totalSummaryBytes); WriteSimpleBlockFile( sampleData, mLen, mFormat, summaryData); DeleteSamples(sampleData); delete [] (char *) summaryData; mDataAvailableMutex.Lock(); mDataAvailable=true; mDataAvailableMutex.Unlock(); }
/// 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) { SF_INFO info; memset(&info, 0, sizeof(info)); SNDFILE *sf = sf_open(mFileName.GetFullPath(), SFM_READ, &info); if (!sf) return 0; sf_seek(sf, start, SEEK_SET); samplePtr buffer = NewSamples(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; }
/// 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::Paste(sampleCount s, const Sequence *src) { if (s < 0) s = 0; if (s >= mNumSamples) s = mNumSamples; // Quick check to make sure that it doesn't overflow if (((double)mNumSamples) + ((double)src->mNumSamples) > wxLL(9223372036854775807)) return false; BlockArray *srcBlock = src->mBlock; sampleCount addedLen = src->mNumSamples; unsigned int srcNumBlocks = srcBlock->Count(); int sampleSize = SAMPLE_SIZE(mSampleFormat); if (addedLen == 0 || srcNumBlocks == 0) return true; unsigned int b = FindBlock(s); unsigned int numBlocks = mBlock->Count(); if (numBlocks == 0 || (s == mNumSamples && mBlock->Item(numBlocks-1)->f->GetLength() >= mMinSamples)) { // Special case: this track is currently empty, or it's safe to append // onto the end because the current last block is longer than the // minimum size for (unsigned int i = 0; i < srcNumBlocks; i++) AppendBlock(srcBlock->Item(i)); return ConsistencyCheck(wxT("Paste branch one")); } if (b >= 0 && b < numBlocks && mBlock->Item(b)->f->GetLength() + addedLen < mMaxSamples) { // Special case: we can fit all of the new samples inside of // one block! samplePtr buffer = NewSamples(mMaxSamples, mSampleFormat); int splitPoint = s - mBlock->Item(b)->start; Read(buffer, mSampleFormat, mBlock->Item(b), 0, splitPoint); src->Get(buffer + splitPoint*sampleSize, mSampleFormat, 0, addedLen); Read(buffer + (splitPoint + addedLen)*sampleSize, mSampleFormat, mBlock->Item(b), splitPoint, mBlock->Item(b)->f->GetLength() - splitPoint); SeqBlock *largerBlock = new SeqBlock(); largerBlock->start = mBlock->Item(b)->start; int largerBlockLen = mBlock->Item(b)->f->GetLength() + addedLen; if (largerBlockLen > mMaxSamples) largerBlockLen = mMaxSamples; // Prevent overruns, per NGS report for UmixIt. largerBlock->f = mDirManager->NewSimpleBlockFile(buffer, largerBlockLen, mSampleFormat); mDirManager->Deref(mBlock->Item(b)->f); delete mBlock->Item(b); mBlock->Item(b) = largerBlock; for (unsigned int i = b + 1; i < numBlocks; i++) mBlock->Item(i)->start += addedLen; mNumSamples += addedLen; DeleteSamples(buffer); return ConsistencyCheck(wxT("Paste branch two")); } // Case two: if we are inserting four or fewer blocks, // it's simplest to just lump all the data together // into one big block along with the split block, // then resplit it all unsigned int i; BlockArray *newBlock = new BlockArray(); newBlock->Alloc(numBlocks + srcNumBlocks + 2); int newNumBlocks = 0; for (i = 0; i < b; i++) { newBlock->Add(mBlock->Item(i)); newNumBlocks++; } SeqBlock *splitBlock = mBlock->Item(b); sampleCount splitLen = mBlock->Item(b)->f->GetLength(); int splitPoint = s - splitBlock->start; if (srcNumBlocks <= 4) { sampleCount sum = splitLen + addedLen; samplePtr sumBuffer = NewSamples(sum, mSampleFormat); Read(sumBuffer, mSampleFormat, splitBlock, 0, splitPoint); src->Get(sumBuffer + splitPoint * sampleSize, mSampleFormat, 0, addedLen); Read(sumBuffer + (splitPoint + addedLen) * sampleSize, mSampleFormat, splitBlock, splitPoint, splitBlock->f->GetLength() - splitPoint); BlockArray *split = Blockify(sumBuffer, sum); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += splitBlock->start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(sumBuffer); } else { // The final case is that we're inserting at least five blocks. // We divide these into three groups: the first two get merged // with the first half of the split block, the middle ones get // copied in as is, and the last two get merged with the last // half of the split block. sampleCount srcFirstTwoLen = srcBlock->Item(0)->f->GetLength() + srcBlock->Item(1)->f->GetLength(); sampleCount leftLen = splitPoint + srcFirstTwoLen; samplePtr leftBuffer = NewSamples(leftLen, mSampleFormat); Read(leftBuffer, mSampleFormat, splitBlock, 0, splitPoint); src->Get(leftBuffer + splitPoint*sampleSize, mSampleFormat, 0, srcFirstTwoLen); BlockArray *split = Blockify(leftBuffer, leftLen); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += splitBlock->start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(leftBuffer); for (i = 2; i < srcNumBlocks - 2; i++) { SeqBlock *insertBlock = new SeqBlock(); insertBlock->start = srcBlock->Item(i)->start + s; insertBlock->f = mDirManager->CopyBlockFile(srcBlock->Item(i)->f); if (!insertBlock->f) { // TODO error: Could not paste! (Out of disk space?) return false; } newBlock->Add(insertBlock); newNumBlocks++; } sampleCount srcLastTwoLen = srcBlock->Item(srcNumBlocks - 2)->f->GetLength() + srcBlock->Item(srcNumBlocks - 1)->f->GetLength(); sampleCount rightSplit = splitBlock->f->GetLength() - splitPoint; sampleCount rightLen = rightSplit + srcLastTwoLen; samplePtr rightBuffer = NewSamples(rightLen, mSampleFormat); sampleCount lastStart = srcBlock->Item(srcNumBlocks - 2)->start; src->Get(rightBuffer, mSampleFormat, lastStart, srcLastTwoLen); Read(rightBuffer + srcLastTwoLen * sampleSize, mSampleFormat, splitBlock, splitPoint, rightSplit); sampleCount pos = s + lastStart; split = Blockify(rightBuffer, rightLen); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += pos; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(rightBuffer); } mDirManager->Deref(splitBlock->f); delete splitBlock; // Copy remaining blocks to new block array and // swap the new block array in for the old for (i = b + 1; i < numBlocks; i++) { mBlock->Item(i)->start += addedLen; newBlock->Add(mBlock->Item(i)); newNumBlocks++; } delete mBlock; mBlock = newBlock; mNumSamples += addedLen; return ConsistencyCheck(wxT("Paste branch three")); }
bool Sequence::Delete(sampleCount start, sampleCount len) { if (len == 0) return true; if (len < 0 || start < 0 || start >= mNumSamples) return false; //TODO: add a ref-deref mechanism to SeqBlock/BlockArray so we don't have to make this a critical section. //On-demand threads iterate over the mBlocks and the GUI thread deletes them, so for now put a mutex here over //both functions, LockDeleteUpdateMutex(); unsigned int numBlocks = mBlock->Count(); unsigned int newNumBlocks = 0; unsigned int b0 = FindBlock(start); unsigned int b1 = FindBlock(start + len - 1); int sampleSize = SAMPLE_SIZE(mSampleFormat); // Special case: if the samples to delete are all within a single // block and the resulting length is not too small, perform the // deletion within this block: if (b0 == b1 && mBlock->Item(b0)->f->GetLength() - len >= mMinSamples) { SeqBlock *b = mBlock->Item(b0); sampleCount pos = start - b->start; sampleCount newLen = b->f->GetLength() - len; samplePtr buffer = NewSamples(newLen, mSampleFormat); Read(buffer, mSampleFormat, b, 0, pos); Read(buffer + (pos * sampleSize), mSampleFormat, b, pos + len, newLen - pos); SeqBlock *newBlock = new SeqBlock(); newBlock->start = b->start; newBlock->f = mDirManager->NewSimpleBlockFile(buffer, newLen, mSampleFormat); mBlock->Item(b0) = newBlock; for (unsigned int j = b0 + 1; j < numBlocks; j++) mBlock->Item(j)->start -= len; DeleteSamples(buffer); mDirManager->Deref(b->f); delete b; mNumSamples -= len; UnlockDeleteUpdateMutex(); return ConsistencyCheck(wxT("Delete - branch one")); } // Create a new array of blocks BlockArray *newBlock = new BlockArray(); newBlock->Alloc(numBlocks - (b1 - b0) + 2); // Copy the blocks before the deletion point over to // the new array unsigned int i; for (i = 0; i < b0; i++) { newBlock->Add(mBlock->Item(i)); newNumBlocks++; } // First grab the samples in block b0 before the deletion point // into preBuffer. If this is enough samples for its own block, // or if this would be the first block in the array, write it out. // Otherwise combine it with the previous block (splitting them // 50/50 if necessary). SeqBlock *preBlock = mBlock->Item(b0); sampleCount preBufferLen = start - preBlock->start; if (preBufferLen) { if (preBufferLen >= mMinSamples || b0 == 0) { SeqBlock *insBlock = new SeqBlock(); insBlock->start = preBlock->start; samplePtr preBuffer = NewSamples(preBufferLen, mSampleFormat); Read(preBuffer, mSampleFormat, preBlock, 0, preBufferLen); insBlock->f = mDirManager->NewSimpleBlockFile(preBuffer, preBufferLen, mSampleFormat); DeleteSamples(preBuffer); newBlock->Add(insBlock); newNumBlocks++; if (b0 != b1) { mDirManager->Deref(preBlock->f); delete preBlock; } } else { SeqBlock *prepreBlock = mBlock->Item(b0 - 1); sampleCount prepreLen = prepreBlock->f->GetLength(); sampleCount sum = prepreLen + preBufferLen; samplePtr sumBuffer = NewSamples(sum, mSampleFormat); Read(sumBuffer, mSampleFormat, prepreBlock, 0, prepreLen); Read(sumBuffer + prepreLen*sampleSize, mSampleFormat, preBlock, 0, preBufferLen); BlockArray *split = Blockify(sumBuffer, sum); split->Item(0)->start += prepreBlock->start; newBlock->Item(b0 - 1) = split->Item(0); for (i = 1; i < split->Count(); i++) { split->Item(i)->start += prepreBlock->start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(sumBuffer); mDirManager->Deref(prepreBlock->f); delete prepreBlock; if (b0 != b1) { mDirManager->Deref(preBlock->f); delete preBlock; } } } else { // The sample where we begin deletion happens to fall // right on the beginning of a block. if (b0 != b1) { mDirManager->Deref(mBlock->Item(b0)->f); delete mBlock->Item(b0); } } // Next, delete blocks strictly between b0 and b1 for (i = b0 + 1; i < b1; i++) { mDirManager->Deref(mBlock->Item(i)->f); delete mBlock->Item(i); } // Now, symmetrically, grab the samples in block b1 after the // deletion point into postBuffer. If this is enough samples // for its own block, or if this would be the last block in // the array, write it out. Otherwise combine it with the // subsequent block (splitting them 50/50 if necessary). SeqBlock *postBlock = mBlock->Item(b1); sampleCount postBufferLen = (postBlock->start + postBlock->f->GetLength()) - (start + len); if (postBufferLen) { if (postBufferLen >= mMinSamples || b1 == numBlocks - 1) { SeqBlock *insBlock = new SeqBlock(); insBlock->start = start; samplePtr postBuffer = NewSamples(postBufferLen, mSampleFormat); sampleCount pos = (start + len) - postBlock->start; Read(postBuffer, mSampleFormat, postBlock, pos, postBufferLen); insBlock->f = mDirManager->NewSimpleBlockFile(postBuffer, postBufferLen, mSampleFormat); DeleteSamples(postBuffer); newBlock->Add(insBlock); newNumBlocks++; mDirManager->Deref(postBlock->f); delete postBlock; } else { SeqBlock *postpostBlock = mBlock->Item(b1 + 1); sampleCount postpostLen = postpostBlock->f->GetLength(); sampleCount sum = postpostLen + postBufferLen; samplePtr sumBuffer = NewSamples(sum, mSampleFormat); sampleCount pos = (start + len) - postBlock->start; Read(sumBuffer, mSampleFormat, postBlock, pos, postBufferLen); Read(sumBuffer + (postBufferLen * sampleSize), mSampleFormat, postpostBlock, 0, postpostLen); BlockArray *split = Blockify(sumBuffer, sum); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; b1++; DeleteSamples(sumBuffer); mDirManager->Deref(postpostBlock->f); delete postpostBlock; mDirManager->Deref(postBlock->f); delete postBlock; } } else { // The sample where we begin deletion happens to fall // right on the end of a block. mDirManager->Deref(mBlock->Item(b1)->f); delete mBlock->Item(b1); } // Copy the remaining blocks over from the old array for (i = b1 + 1; i < numBlocks; i++) { mBlock->Item(i)->start -= len; newBlock->Add(mBlock->Item(i)); newNumBlocks++; } // Substitute our new array for the old one delete mBlock; mBlock = newBlock; // Update total number of samples and do a consistency check. mNumSamples -= len; UnlockDeleteUpdateMutex(); return ConsistencyCheck(wxT("Delete - branch two")); }
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")); }
Mixer::Mixer(int numInputTracks, WaveTrack **inputTracks, TimeTrack *timeTrack, double startTime, double stopTime, int numOutChannels, int outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, bool highQuality, MixerSpec *mixerSpec) { int i; mNumInputTracks = numInputTracks; mInputTrack = new WaveTrack*[mNumInputTracks]; mSamplePos = new longSampleCount[mNumInputTracks]; for(i=0; i<mNumInputTracks; i++) { mInputTrack[i] = inputTracks[i]; mSamplePos[i] = inputTracks[i]->TimeToLongSamples(startTime); } mTimeTrack = timeTrack; mT0 = startTime; mT1 = stopTime; mT = startTime; mNumChannels = numOutChannels; mBufferSize = outBufferSize; mInterleaved = outInterleaved; mRate = outRate; mFormat = outFormat; mApplyTrackGains = true; mGains = new float[mNumChannels]; if( mixerSpec && mixerSpec->GetNumChannels() == mNumChannels && mixerSpec->GetNumTracks() == mNumInputTracks ) mMixerSpec = mixerSpec; else mMixerSpec = NULL; if (mInterleaved) { mNumBuffers = 1; mInterleavedBufferSize = mBufferSize * mNumChannels; } else { mNumBuffers = mNumChannels; mInterleavedBufferSize = mBufferSize; } mBuffer = new samplePtr[mNumBuffers]; mTemp = new samplePtr[mNumBuffers]; for (int c = 0; c < mNumBuffers; c++) { mBuffer[c] = NewSamples(mInterleavedBufferSize, mFormat); mTemp[c] = NewSamples(mInterleavedBufferSize, floatSample); } mFloatBuffer = new float[mInterleavedBufferSize]; mQueueMaxLen = 65536; mProcessLen = 1024; mQueueStart = new int[mNumInputTracks]; mQueueLen = new int[mNumInputTracks]; mSampleQueue = new float *[mNumInputTracks]; mSRC = new Resample*[mNumInputTracks]; for(i=0; i<mNumInputTracks; i++) { double factor = (mRate / mInputTrack[i]->GetRate()); double lowFactor = factor, highFactor = factor; if (timeTrack) { highFactor /= timeTrack->GetRangeLower() / 100.0; lowFactor /= timeTrack->GetRangeUpper() / 100.0; } mSRC[i] = new Resample(highQuality, lowFactor, highFactor); mSampleQueue[i] = new float[mQueueMaxLen]; mQueueStart[i] = 0; mQueueLen[i] = 0; } int envLen = mInterleavedBufferSize; if (mQueueMaxLen > envLen) envLen = mQueueMaxLen; mEnvValues = new double[envLen]; }
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; }