bool EffectAmplify::ProcessOne(int count, WaveTrack *t, sampleCount start, sampleCount len) { sampleCount s = start; sampleCount originalLen = len; sampleCount blockSize = t->GetMaxBlockSize(); sampleType *buffer = new sampleType[blockSize]; while (len) { unsigned int block = t->GetBestBlockSize(s); if (block > len) block = len; t->Get(buffer, s, block); for (unsigned int i = 0; i < block; i++) { buffer[i] = (sampleType) (buffer[i] * ratio); } t->Set(buffer, s, block); len -= block; s += block; if (TrackProgress(count, (s-start)/(double)originalLen)) break; } delete[] buffer; return true; }
bool EffectInvert::ProcessOne(int count, WaveTrack *t, sampleCount start, sampleCount len) { // keep track of two blocks whose data we will swap sampleCount s = start; sampleCount originalLen = len; sampleCount blockSize = t->GetMaxBlockSize(); sampleType *buffer = new sampleType[blockSize]; while (len > 0) { unsigned int block = t->GetBestBlockSize(s); if (block > len) block = len; t->Get(buffer, s, block); for (unsigned int i = 0; i < block; i++) { buffer[i] = (sampleType) (-buffer[i]); } t->Set(buffer, s, block); len -= block; s += block; TrackProgress(count, (s-start)/(double)originalLen); } delete[] buffer; return true; }
bool BlockGenerator::GenerateTrack(WaveTrack *tmp, const WaveTrack &track, int ntrack) { bool bGoodResult = true; numSamples = track.TimeToLongSamples(GetDuration()); sampleCount i = 0; float *data = new float[tmp->GetMaxBlockSize()]; sampleCount block = 0; while ((i < numSamples) && bGoodResult) { block = tmp->GetBestBlockSize(i); if (block > (numSamples - i)) block = numSamples - i; GenerateBlock(data, track, block); // Add the generated data to the temporary track tmp->Append((samplePtr)data, floatSample, block); i += block; // Update the progress meter if (TrackProgress(ntrack, (double)i / numSamples)) bGoodResult = false; } delete[] data; return bGoodResult; }
bool EffectRepeat::Process() { this->CopyInputWaveTracks(); // Set up mOutputWaveTracks. bool bGoodResult = true; TrackListIterator iter(mOutputWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); int nTrack = 0; double maxDestLen = 0.0; // used to change selection to generated bit while ((track != NULL) && bGoodResult) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; if (t1 <= t0) continue; sampleCount start = track->TimeToLongSamples(t0); sampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); double tLen = track->LongSamplesToTime(len); double tc = t0 + tLen; if (len <= 0) continue; Track *dest; track->Copy(t0, t1, &dest); for(int j=0; j<repeatCount; j++) { if (!track->Paste(tc, dest) || TrackProgress(nTrack, j / repeatCount)) // TrackProgress returns true on Cancel. { bGoodResult = false; break; } tc += tLen; } if (tc > maxDestLen) maxDestLen = tc; delete dest; track = (WaveTrack *) iter.Next(); nTrack++; } if (bGoodResult) { // Change selection to just the generated bits. mT0 = mT1; mT1 = maxDestLen; } this->ReplaceProcessedWaveTracks(bGoodResult); return bGoodResult; }
bool EffectClickRemoval::ProcessOne(int count, WaveTrack * track, sampleCount start, sampleCount len) { bool rc = true; sampleCount s = 0; sampleCount idealBlockLen = track->GetMaxBlockSize() * 4; if (idealBlockLen % windowSize != 0) idealBlockLen += (windowSize - (idealBlockLen % windowSize)); float *buffer = new float[idealBlockLen]; float *datawindow = new float[windowSize]; int i; while((s < len)&&((len-s)>(windowSize/2))) { sampleCount block = idealBlockLen; if (s + block > len) block = len - s; track->Get((samplePtr) buffer, floatSample, start + s, block); for(i=0; i<(block-windowSize/2); i+=windowSize/2) { int wcopy = windowSize; if (i + wcopy > block) wcopy = block - i; int j; for(j=0; j<wcopy; j++) datawindow[j] = buffer[i+j]; for(j=wcopy; j<windowSize; j++) datawindow[j] = 0; RemoveClicks(windowSize, datawindow); for(j=0; j<wcopy; j++) buffer[i+j] = datawindow[j]; } track->Set((samplePtr) buffer, floatSample, start + s, block); s += block; if (TrackProgress(count, s / (double) len)) { rc = false; break; } } delete[] buffer; delete[] datawindow; return rc; }
//ProcessOne() takes a track, transforms it to bunch of buffer-blocks, //and executes ProcessData, on it... // uses mMult and mOffset to normalize a track. Needs to have them set before being called bool EffectNormalize::ProcessOne(WaveTrack * track, wxString msg) { bool rc = true; sampleCount s; //Transform the marker timepoints to samples sampleCount start = track->TimeToLongSamples(mCurT0); sampleCount end = track->TimeToLongSamples(mCurT1); //Get the length of the buffer (as double). len is //used simply to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); //Initiate a processing buffer. This buffer will (most likely) //be shorter than the length of the track being processed. float *buffer = new float[track->GetMaxBlockSize()]; //Go through the track one buffer at a time. s counts which //sample the current buffer starts at. s = start; while (s < end) { //Get a block of samples (smaller than the size of the buffer) sampleCount block = track->GetBestBlockSize(s); //Adjust the block size if it is the final block in the track if (s + block > end) block = end - s; //Get the samples from the track and put them in the buffer track->Get((samplePtr) buffer, floatSample, s, block); //Process the buffer. ProcessData(buffer, block); //Copy the newly-changed samples back onto the track. track->Set((samplePtr) buffer, floatSample, s, block); //Increment s one blockfull of samples s += block; //Update the Progress meter if (TrackProgress(mCurTrackNum, 0.5+((double)(s - start) / len)/2.0, msg)) { rc = false; //lda .. break, not return, so that buffer is deleted break; } } //Clean up the buffer delete[] buffer; //Return true because the effect processing succeeded ... unless cancelled return rc; }
//ProcessOne() takes a track, transforms it to bunch of buffer-blocks, //and executes ProcessSimpleMono on these blocks bool EffectSimpleMono::ProcessOne(WaveTrack * track, longSampleCount start, longSampleCount end) { longSampleCount s; //Get the length of the buffer (as double). len is //used simple to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); //Initiate a processing buffer. This buffer will (most likely) //be shorter than the length of the track being processed. float *buffer = new float[track->GetMaxBlockSize()]; //Go through the track one buffer at a time. s counts which //sample the current buffer starts at. s = start; while (s < end) { //Get a block of samples (smaller than the size of the buffer) sampleCount block = track->GetBestBlockSize(s); //Adjust the block size if it is the final block in the track if (s + block > end) block = end - s; //Get the samples from the track and put them in the buffer track->Get((samplePtr) buffer, floatSample, s, block); //Process the buffer. If it fails, clean up and exit. if (!ProcessSimpleMono(buffer, block)) { delete[]buffer; //Return false because the effect failed. return false; } //Processing succeeded. copy the newly-changed samples back //onto the track. track->Set((samplePtr) buffer, floatSample, s, block); //Increment s one blockfull of samples s += block; //Update the Progress meter if (TrackProgress(mCurTrackNum, s / len)) return false; } //Clean up the buffer delete[]buffer; //Return true because the effect processing succeeded. return true; }
bool EffectRepair::ProcessOne(int count, WaveTrack * track, sampleCount start, size_t len, size_t repairStart, size_t repairLen) { Floats buffer{ len }; track->Get((samplePtr) buffer.get(), floatSample, start, len); InterpolateAudio(buffer.get(), len, repairStart, repairLen); track->Set((samplePtr)&buffer[repairStart], floatSample, start + repairStart, repairLen); return !TrackProgress(count, 1.0); // TrackProgress returns true on Cancel. }
bool EffectEcho::ProcessOne(int count, WaveTrack * track, sampleCount start, sampleCount len) { sampleCount s = 0; sampleCount blockSize = (sampleCount) (track->GetRate() * delay); //do nothing if the delay is less than 1 sample or greater than //the length of the selection if (blockSize < 1 || blockSize > len) return true; float *buffer0 = new float[blockSize]; float *buffer1 = new float[blockSize]; float *ptr0 = buffer0; float *ptr1 = buffer1; bool first = true; while (s < len) { sampleCount block = blockSize; if (s + block > len) block = len - s; track->Get((samplePtr)ptr0, floatSample, start + s, block); if (!first) { for (sampleCount i = 0; i < block; i++) ptr0[i] += ptr1[i] * decay; track->Set((samplePtr)ptr0, floatSample, start + s, block); } float *ptrtemp = ptr0; ptr0 = ptr1; ptr1 = ptrtemp; first = false; s += block; if (TrackProgress(count, s / (double) len)) { delete[]buffer0; delete[]buffer1; return false; } } delete[]buffer0; delete[]buffer1; return true; }
bool EffectRepair::ProcessOne(int count, WaveTrack * track, sampleCount start, sampleCount len, sampleCount repairStart, sampleCount repairLen) { float *buffer = new float[len]; track->Get((samplePtr) buffer, floatSample, start, len); InterpolateAudio(buffer, len, repairStart, repairLen); track->Set((samplePtr)&buffer[repairStart], floatSample, start + repairStart, repairLen); delete[] buffer; return !TrackProgress(count, 1.0); // TrackProgress returns true on Cancel. }
bool EffectEcho::ProcessOne(int count, WaveTrack * t, sampleCount start, sampleCount len) { sampleCount s = start; sampleCount blockSize = (sampleCount) (t->rate * delay); sampleCount originalLen = len; if (blockSize < 1 || blockSize > len) return true; sampleType *buffer0 = new sampleType[blockSize]; sampleType *buffer1 = new sampleType[blockSize]; sampleType *ptr0 = buffer0; sampleType *ptr1 = buffer1; bool first = true; while (len) { sampleCount block = blockSize; if (block > len) block = len; t->Get(ptr0, s, block); if (!first) { for (sampleCount i = 0; i < block; i++) ptr0[i] += (sampleType) (ptr1[i] * decay); t->Set(ptr0, s, block); } sampleType *ptrtemp = ptr0; ptr0 = ptr1; ptr1 = ptrtemp; first = false; len -= block; s += block; TrackProgress(count, (s-start)/(double)originalLen); } delete[]buffer0; delete[]buffer1; return true; }
bool EffectStereoToMono::ProcessOne(int count) { float curLeftFrame; float curRightFrame; float curMonoFrame; sampleCount idealBlockLen = mLeftTrack->GetMaxBlockSize() * 2; sampleCount index = mStart; float *leftBuffer = new float[idealBlockLen]; float *rightBuffer = new float[idealBlockLen]; bool bResult = true; while (index < mEnd) { bResult &= mLeftTrack->Get((samplePtr)leftBuffer, floatSample, index, idealBlockLen); bResult &= mRightTrack->Get((samplePtr)rightBuffer, floatSample, index, idealBlockLen); sampleCount limit = idealBlockLen; if ((index + idealBlockLen) > mEnd) { limit = mEnd - index; } for (sampleCount i = 0; i < limit; ++i) { index++; curLeftFrame = leftBuffer[i]; curRightFrame = rightBuffer[i]; curMonoFrame = (curLeftFrame + curRightFrame) / 2.0; leftBuffer[i] = curMonoFrame; } bResult &= mOutTrack->Append((samplePtr)leftBuffer, floatSample, limit); if (TrackProgress(count, 2.*((double)index / (double)(mEnd - mStart)))) return false; } double minStart = wxMin(mLeftTrack->GetStartTime(), mRightTrack->GetStartTime()); bResult &= mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime()); bResult &= mOutTrack->Flush(); bResult &= mLeftTrack->Paste(minStart, mOutTrack); mLeftTrack->SetLinked(false); mRightTrack->SetLinked(false); mLeftTrack->SetChannel(Track::MonoChannel); mOutputTracks->Remove(mRightTrack); delete mRightTrack; delete [] leftBuffer; delete [] rightBuffer; return bResult; }
bool EffectReverse::ProcessOneClip(int count, WaveTrack *track, sampleCount start, sampleCount len, sampleCount originalStart, sampleCount originalEnd) { bool rc = true; // keep track of two blocks whose data we will swap sampleCount first = start; sampleCount second; sampleCount blockSize = track->GetMaxBlockSize(); float tmp; float *buffer1 = new float[blockSize]; float *buffer2 = new float[blockSize]; sampleCount originalLen = (sampleCount)originalEnd-originalStart; while (len > 1) { sampleCount block = track->GetBestBlockSize(first); if (block > len / 2) block = len / 2; second = first + (len - block); track->Get((samplePtr)buffer1, floatSample, first, block); track->Get((samplePtr)buffer2, floatSample, second, block); for (int i = 0; i < block; i++) { tmp = buffer1[i]; buffer1[i] = buffer2[block-i-1]; buffer2[block-i-1] = tmp; } track->Set((samplePtr)buffer1, floatSample, first, block); track->Set((samplePtr)buffer2, floatSample, second, block); len -= 2 * block; first += block; if( TrackProgress(count, 2*(first-originalStart) / (double) originalLen) ) { rc = false; break; } } delete[] buffer1; delete[] buffer2; return rc; }
bool EffectReverse::ProcessOneClip(int count, WaveTrack *track, sampleCount start, sampleCount len, sampleCount originalStart, sampleCount originalEnd) { bool rc = true; // keep track of two blocks whose data we will swap auto first = start; auto blockSize = track->GetMaxBlockSize(); float tmp; Floats buffer1{ blockSize }; Floats buffer2{ blockSize }; auto originalLen = originalEnd - originalStart; while (len > 1) { auto block = limitSampleBufferSize( track->GetBestBlockSize(first), len / 2 ); auto second = first + (len - block); track->Get((samplePtr)buffer1.get(), floatSample, first, block); track->Get((samplePtr)buffer2.get(), floatSample, second, block); for (decltype(block) i = 0; i < block; i++) { tmp = buffer1[i]; buffer1[i] = buffer2[block-i-1]; buffer2[block-i-1] = tmp; } track->Set((samplePtr)buffer1.get(), floatSample, first, block); track->Set((samplePtr)buffer2.get(), floatSample, second, block); len -= 2 * block; first += block; if( TrackProgress(count, 2 * ( first - originalStart ).as_double() / originalLen.as_double() ) ) { rc = false; break; } } return rc; }
bool EffectSineSweepGenerator::GenerateTrack(WaveTrack *tmp, const WaveTrack &track, int ntrack) { #ifdef __AUDEBUG__ printf("elsg: GENERATETRACK\n"); fflush(stdout); // ************DEBUG #endif bool bGoodResult = true; //numSamples = track.TimeToLongSamples(mDuration); sampleCount numSamples; //Filter has different length if(ntrack == m_pSsg->GetFilterChannel()) numSamples = sampleCount(m_pSsg->GetFilterLength()); else numSamples = sampleCount(m_pSsg->GetBuffersLength()); sampleCount i = 0; AFSample *data = new AFSample[tmp->GetMaxBlockSize()]; sampleCount block = 0; while ((i < numSamples) && bGoodResult) { block = tmp->GetBestBlockSize(i); if (block > (numSamples - i)) block = numSamples - i; // GenerateBlock(data, track, block); m_pSsg->FillBlock(data, AFSampleCount(block), AFSampleCount(i), ntrack); // Add the generated data to the temporary track tmp->Append((samplePtr)data, floatSample, block); i += block; // Update the progress meter if (TrackProgress(ntrack, (double)i / numSamples)) bGoodResult = false; } tmp->Flush(); delete[] data; return bGoodResult; }
void EffectStereoToMono::ProcessOne() { float curLeftFrame; float curRightFrame; float curMonoFrame; sampleCount idealBlockLen = mLeftTrack->GetMaxBlockSize() * 2; sampleCount index = 0; sampleCount outTrackOffset = 0; float *leftBuffer = new float[idealBlockLen]; float *rightBuffer = new float[idealBlockLen]; bool rc; while (index < mLeftTrackLen) { rc = mLeftTrack->Get((samplePtr)leftBuffer, floatSample, index, idealBlockLen); rc = mRightTrack->Get((samplePtr)rightBuffer, floatSample, index, idealBlockLen); sampleCount limit = idealBlockLen; if ((index + idealBlockLen) > mLeftTrackLen) { limit = mLeftTrackLen - index; } for (sampleCount i = 0; i < limit; ++i) { index++; curLeftFrame = leftBuffer[i]; curRightFrame = rightBuffer[i]; curMonoFrame = (curLeftFrame + curRightFrame) / 2.0; leftBuffer[i] = curMonoFrame; } rc = mLeftTrack->Set((samplePtr)leftBuffer, floatSample, outTrackOffset, limit); outTrackOffset += limit; TrackProgress(0, ((double)index / (double)mLeftTrackLen)); } mLeftTrack->SetLinked(false); mRightTrack->SetLinked(false); mLeftTrack->SetChannel(Track::MonoChannel); mTracks->Remove(mRightTrack); delete mRightTrack; delete [] leftBuffer; delete [] rightBuffer; }
bool EffectFilter::ProcessOne(int count, WaveTrack * t, sampleCount start, sampleCount len) { sampleCount s = start; sampleCount idealBlockLen = t->GetMaxBlockSize() * 4; if (idealBlockLen % windowSize != 0) idealBlockLen += (windowSize - (idealBlockLen % windowSize)); sampleType *buffer = new sampleType[idealBlockLen]; sampleType *window1 = new sampleType[windowSize]; sampleType *window2 = new sampleType[windowSize]; sampleType *thisWindow = window1; sampleType *lastWindow = window2; sampleCount originalLen = len; unsigned int i; for(i=0; i<windowSize; i++) lastWindow[i] = 0; while(len) { unsigned int block = idealBlockLen; if (block > len) block = len; t->Get(buffer, s, block); for(i=0; i<block; i+=windowSize/2) { unsigned int wcopy = windowSize; if (i + wcopy > block) wcopy = block - i; unsigned int j; for(j=0; j<wcopy; j++) thisWindow[j] = buffer[i+j]; for(j=wcopy; j<windowSize; j++) thisWindow[j] = 0; Filter(windowSize, thisWindow); for(j=0; j<windowSize/2; j++) buffer[i+j] = thisWindow[j] + lastWindow[windowSize/2 + j]; sampleType *tempP = thisWindow; thisWindow = lastWindow; lastWindow = tempP; } if (len > block && len > windowSize/2) block -= windowSize/2; t->Set(buffer, s, block); len -= block; s += block; TrackProgress(count, (s-start)/(double)originalLen); } delete[] buffer; delete[] window1; delete[] window2; return true; }
bool VampEffect::Process() { if (!mPlugin) return false; TrackListIterator iter(mWaveTracks); int count = 0; WaveTrack *left = (WaveTrack *)iter.First(); bool multiple = false; int prevTrackChannels = 0; TrackListIterator scooter(iter); if (left->GetLinked()) scooter.Next(); if (scooter.Next()) { // if there is another track beyond this one and any linked one, // then we're processing more than one track. That means we // should use the originating track name in each new label // track's name, to make clear which is which multiple = true; } while (left) { sampleCount lstart, rstart; sampleCount len; GetSamples(left, &lstart, &len); WaveTrack *right = NULL; int channels = 1; if (left->GetLinked()) { right = (WaveTrack *)iter.Next(); channels = 2; GetSamples(right, &rstart, &len); } size_t step = mPlugin->getPreferredStepSize(); size_t block = mPlugin->getPreferredBlockSize(); bool initialiseRequired = true; if (block == 0) { if (step != 0) block = step; else block = 1024; } if (step == 0) { step = block; } if (prevTrackChannels > 0) { // Plugin has already been initialised, so if the number of // channels remains the same, we only need to do a reset. // Otherwise we need to re-construct the whole plugin, // because a Vamp plugin can't be re-initialised. if (prevTrackChannels == channels) { mPlugin->reset(); initialiseRequired = false; } else { //!!! todo: retain parameters previously set Init(); } } if (initialiseRequired) { if (!mPlugin->initialise(channels, step, block)) { wxMessageBox(_("Sorry, Vamp Plug-in failed to initialize.")); return false; } } LabelTrack *ltrack = mFactory->NewLabelTrack(); if (!multiple) { ltrack->SetName(GetEffectName()); } else { ltrack->SetName(wxString::Format(wxT("%s: %s"), left->GetName().c_str(), GetEffectName().c_str())); } mTracks->Add(ltrack); float **data = new float*[channels]; for (int c = 0; c < channels; ++c) data[c] = new float[block]; sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; while (len) { int request = block; if (request > len) request = len; if (left) left->Get((samplePtr)data[0], floatSample, ls, request); if (right) right->Get((samplePtr)data[1], floatSample, rs, request); if (request < (int)block) { for (int c = 0; c < channels; ++c) { for (int i = request; i < (int)block; ++i) { data[c][i] = 0.f; } } } Vamp::RealTime timestamp = Vamp::RealTime::frame2RealTime (ls, (int)(mRate + 0.5)); Vamp::Plugin::FeatureSet features = mPlugin->process(data, timestamp); AddFeatures(ltrack, features); if (len > (int)step) len -= step; else len = 0; ls += step; rs += step; if (channels > 1) { if (TrackGroupProgress(count, (ls - lstart) / double(originalLen))) return false; } else { if (TrackProgress(count, (ls - lstart) / double(originalLen))) return false; } } Vamp::Plugin::FeatureSet features = mPlugin->getRemainingFeatures(); AddFeatures(ltrack, features); prevTrackChannels = channels; left = (WaveTrack *)iter.Next(); } return true; }
//ProcessOne() takes a track, transforms it to bunch of buffer-blocks, //and executes ProcessSoundTouch on these blocks bool EffectSoundTouch::ProcessOne(WaveTrack *track, longSampleCount start, longSampleCount end) { WaveTrack *outputTrack; longSampleCount s; mSoundTouch->setSampleRate((unsigned int)(track->GetRate()+0.5)); outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat()); //Get the length of the buffer (as double). len is //used simple to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); //Initiate a processing buffer. This buffer will (most likely) //be shorter than the length of the track being processed. float *buffer = new float[track->GetMaxBlockSize()]; //Go through the track one buffer at a time. s counts which //sample the current buffer starts at. s = start; while (s < end) { //Get a block of samples (smaller than the size of the buffer) sampleCount block = track->GetBestBlockSize(s); //Adjust the block size if it is the final block in the track if (s + block > end) block = end - s; //Get the samples from the track and put them in the buffer track->Get((samplePtr) buffer, floatSample, s, block); //Add samples to SoundTouch mSoundTouch->putSamples(buffer, block); //Get back samples from SoundTouch unsigned int outputCount = mSoundTouch->numSamples(); if (outputCount > 0) { float *buffer2 = new float[outputCount]; mSoundTouch->receiveSamples(buffer2, outputCount); outputTrack->Append((samplePtr)buffer2, floatSample, outputCount); delete[] buffer2; } //Increment s one blockfull of samples s += block; //Update the Progress meter if (TrackProgress(mCurTrackNum, (s - start) / len)) return false; } // Tell SoundTouch to finish processing any remaining samples mSoundTouch->flush(); unsigned int outputCount = mSoundTouch->numSamples(); if (outputCount > 0) { float *buffer2 = new float[outputCount]; mSoundTouch->receiveSamples(buffer2, outputCount); outputTrack->Append((samplePtr)buffer2, floatSample, outputCount); delete[] buffer2; } // Flush the output WaveTrack (since it's buffered, too) outputTrack->Flush(); // Clean up the buffer delete[]buffer; // Take the output track and insert it in place of the original // sample data track->Clear(mT0, mT1); track->Paste(mT0, outputTrack); double newLength = outputTrack->GetEndTime(); if (newLength > m_maxNewLength) m_maxNewLength = newLength; // Delete the outputTrack now that its data is inserted in place delete outputTrack; //Return true because the effect processing succeeded. return true; }
bool EffectBassBoost::ProcessOne(int count, WaveTrack * t, sampleCount start, sampleCount len) { float samplerate = (float) (t->GetRate()); /* Compute coefficents of the biquand IIR filter */ float omega = 2 * 3.141592653589 * frequency / samplerate; float sn = sin(omega); float cs = cos(omega); float a = exp(log(10) * dB_boost / 40); float shape = 1.0; /*Low Shelf filter's shape, if this is too large or too small it will result an unstable filter */ float beta = sqrt((a * a + 1) / shape - (pow((a - 1), 2))); /* Coefficients */ float b0 = a * ((a + 1) - (a - 1) * cs + beta * sn); float b1 = 2 * a * ((a - 1) - (a + 1) * cs); float b2 = a * ((a + 1) - (a - 1) * cs - beta * sn); float a0 = ((a + 1) + (a - 1) * cs + beta * sn); float a1 = -2 * ((a - 1) + (a + 1) * cs); float a2 = (a + 1) + (a - 1) * cs - beta * sn; /* initialise the filter */ float xn1 = 0, xn2 = 0, yn1 = 0, yn2 = 0; float out, in = 0; sampleCount s = start; sampleCount originalLen = len; sampleCount blockSize = t->GetMaxBlockSize(); float *buffer = new float[blockSize]; while (len) { sampleCount block = t->GetBestBlockSize(s); if (block > len) block = len; t->Get(buffer, s, block); for (int i = 0; i < block; i++) { in = buffer[i]; out = (b0 * in + b1 * xn1 + b2 * xn2 - a1 * yn1 - a2 * yn2) / a0; xn2 = xn1; xn1 = in; yn2 = yn1; yn1 = out; if (out < -1.0) out = -1.0; else if (out > 1.0) out = 1.0; //Prevents clipping buffer[i] = out; } t->Set(buffer, s, block); len -= block; s += block; TrackProgress(count, (s-start)/(double)originalLen); } delete[]buffer; return true; }
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count) { auto badAllocMessage = _("Requested value exceeds memory capacity."); const auto stretch_buf_size = GetBufferSize(track->GetRate()); if (stretch_buf_size == 0) { ::Effect::MessageBox( badAllocMessage ); return false; } double amount = this->mAmount; auto start = track->TimeToLongSamples(t0); auto end = track->TimeToLongSamples(t1); auto len = end - start; const auto minDuration = stretch_buf_size * 2 + 1; if (minDuration < stretch_buf_size) { // overflow! ::Effect::MessageBox( badAllocMessage ); return false; } if (len < minDuration) { //error because the selection is too short float maxTimeRes = log( len.as_double() ) / log(2.0); maxTimeRes = pow(2.0, floor(maxTimeRes) + 0.5); maxTimeRes = maxTimeRes / track->GetRate(); if (this->IsPreviewing()) { double defaultPreviewLen; gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &defaultPreviewLen, 6.0); /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/ if ((minDuration / mProjectRate) < defaultPreviewLen) { ::Effect::MessageBox (wxString::Format(_("Audio selection too short to preview.\n\n" "Try increasing the audio selection to at least %.1f seconds,\n" "or reducing the 'Time Resolution' to less than %.1f seconds."), (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s. floor(maxTimeRes * 10.0) / 10.0), wxOK | wxICON_EXCLAMATION); } else { /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/ ::Effect::MessageBox (wxString::Format(_("Unable to Preview.\n\n" "For the current audio selection, the maximum\n" "'Time Resolution' is %.1f seconds."), floor(maxTimeRes * 10.0) / 10.0), wxOK | wxICON_EXCLAMATION); } } else { /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/ ::Effect::MessageBox (wxString::Format(_("The 'Time Resolution' is too long for the selection.\n\n" "Try increasing the audio selection to at least %.1f seconds,\n" "or reducing the 'Time Resolution' to less than %.1f seconds."), (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s. floor(maxTimeRes * 10.0) / 10.0), wxOK | wxICON_EXCLAMATION); } return false; } auto dlen = len.as_double(); double adjust_amount = dlen / (dlen - ((double)stretch_buf_size * 2.0)); amount = 1.0 + (amount - 1.0) * adjust_amount; auto outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),track->GetRate()); try { // This encloses all the allocations of buffers, including those in // the constructor of the PaulStretch object PaulStretch stretch(amount, stretch_buf_size, track->GetRate()); auto nget = stretch.get_nsamples_for_fill(); auto bufsize = stretch.poolsize; Floats buffer0{ bufsize }; float *bufferptr0 = buffer0.get(); bool first_time = true; const auto fade_len = std::min<size_t>(100, bufsize / 2 - 1); bool cancelled = false; { Floats fade_track_smps{ fade_len }; decltype(len) s=0; while (s < len) { track->Get((samplePtr)bufferptr0, floatSample, start + s, nget); stretch.process(buffer0.get(), nget); if (first_time) { stretch.process(buffer0.get(), 0); }; s += nget; if (first_time){//blend the the start of the selection track->Get((samplePtr)fade_track_smps.get(), floatSample, start, fade_len); first_time = false; for (size_t i = 0; i < fade_len; i++){ float fi = (float)i / (float)fade_len; stretch.out_buf[i] = stretch.out_buf[i] * fi + (1.0 - fi) * fade_track_smps[i]; } } if (s >= len){//blend the end of the selection track->Get((samplePtr)fade_track_smps.get(), floatSample, end - fade_len, fade_len); for (size_t i = 0; i < fade_len; i++){ float fi = (float)i / (float)fade_len; auto i2 = bufsize / 2 - 1 - i; stretch.out_buf[i2] = stretch.out_buf[i2] * fi + (1.0 - fi) * fade_track_smps[fade_len - 1 - i]; } } outputTrack->Append((samplePtr)stretch.out_buf.get(), floatSample, stretch.out_bufsize); nget = stretch.get_nsamples(); if (TrackProgress(count, s.as_double() / len.as_double() )) { cancelled = true; break; } } } if (!cancelled){ outputTrack->Flush(); track->Clear(t0,t1); track->Paste(t0, outputTrack.get()); m_t1 = mT0 + outputTrack->GetEndTime(); } return !cancelled; } catch ( const std::bad_alloc& ) { ::Effect::MessageBox( badAllocMessage ); return false; } };
bool LadspaEffect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { /* Allocate buffers */ if (mBlockSize == 0) { mBlockSize = left->GetMaxBlockSize() * 2; fInBuffer = new float *[inputs]; unsigned long i; for (i = 0; i < inputs; i++) fInBuffer[i] = new float[mBlockSize]; fOutBuffer = new float *[outputs]; for (i = 0; i < outputs; i++) fOutBuffer[i] = new float[mBlockSize]; } /* Instantiate the plugin */ unsigned long rate = (unsigned long)(left->GetRate() + 0.5); LADSPA_Handle handle = mData->instantiate(mData, rate); unsigned long p; for(p=0; p<inputs; p++) { mData->connect_port(handle, inputPorts[p], fInBuffer[p]); } for(p=0; p<outputs; p++) { mData->connect_port(handle, outputPorts[p], fOutBuffer[p]); } for(p=0; p<mData->PortCount; p++) { LADSPA_PortDescriptor d = mData->PortDescriptors[p]; if (LADSPA_IS_PORT_CONTROL(d)) { if (LADSPA_IS_PORT_INPUT(d)) { mData->connect_port(handle, p, &inputControls[p]); } else mData->connect_port(handle, p, &outputControls[p]); } } if (mData->activate) mData->activate(handle); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; while (len) { int block = mBlockSize; if (block > len) block = len; if (left && inputs > 0) { left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); } if (right && inputs > 1) { right->Get((samplePtr)fInBuffer[1], floatSample, rs, block); } mData->run(handle, block); if (left && outputs > 0) { left->Set((samplePtr)fOutBuffer[0], floatSample, ls, block); } if (right && outputs > 1) { right->Set((samplePtr)fOutBuffer[1], floatSample, rs, block); } len -= block; ls += block; rs += block; if (inputs > 1) { if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen)) return false; } else { if (TrackProgress(count, (ls-lstart)/(double)originalLen)) return false; } } if (mData->deactivate) mData->deactivate(handle); if (mData->cleanup) mData->cleanup(handle); return true; }
bool VSTEffect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { bool rc = true; // Initialize time info mTimeInfo.samplePos = 0.0; mTimeInfo.sampleRate = left->GetRate(); mTimeInfo.flags |= kVstTransportPlaying; // Turn the power on callDispatcher(effMainsChanged, 0, 1, NULL, 0.0); // Tell effect we're starting to process callDispatcher(effStartProcess, 0, 0, NULL, 0.0); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; while (len) { int block = mBlockSize; if (block > len) { block = len; } left->Get((samplePtr)mInBuffer[0], floatSample, ls, block); if (right) { right->Get((samplePtr)mInBuffer[1], floatSample, rs, block); } callProcessReplacing(mInBuffer, mOutBuffer, block); left->Set((samplePtr)mOutBuffer[0], floatSample, ls, block); if (right) { right->Set((samplePtr)mOutBuffer[1], floatSample, rs, block); } len -= block; ls += block; rs += block; mTimeInfo.samplePos += ((double) block / mTimeInfo.sampleRate); if (mInputs > 1) { if (TrackGroupProgress(count, (ls - lstart) / (double)originalLen)) { rc = false; break; } } else { if (TrackProgress(count, (ls - lstart) / (double)originalLen)) { rc = false; break; } } } // Tell effect we're done callDispatcher(effStopProcess, 0, 0, NULL, 0.0); // Turn the power off callDispatcher(effMainsChanged, 0, 0, NULL, 0.0); // No longer playing mTimeInfo.samplePos = 0.0; mTimeInfo.sampleRate = 44100.0; mTimeInfo.tempo = 120.0; mTimeInfo.timeSigNumerator = 4; mTimeInfo.timeSigDenominator = 4; mTimeInfo.flags = kVstTempoValid | kVstNanosValid; return rc; }
bool EffectNoiseRemoval::ProcessOne(int count, WaveTrack * track, longSampleCount start, sampleCount len) { bool retCode = true; sampleCount s = 0; sampleCount idealBlockLen = track->GetMaxBlockSize() * 4; if (idealBlockLen % windowSize != 0) idealBlockLen += (windowSize - (idealBlockLen % windowSize)); float *buffer = new float[idealBlockLen]; float *window1 = new float[windowSize]; float *window2 = new float[windowSize]; float *thisWindow = window1; float *lastWindow = window2; int i; for(i=0; i<windowSize; i++) { lastWindow[i] = 0; smoothing[i] = float(0.0); } while((s < len)&&((len-s)>(windowSize/2))) { sampleCount block = idealBlockLen; if (s + block > len) block = len - s; track->Get((samplePtr) buffer, floatSample, start + s, block); for(i=0; i<(block-windowSize/2); i+=windowSize/2) { int wcopy = windowSize; if (i + wcopy > block) wcopy = block - i; int j; for(j=0; j<wcopy; j++) thisWindow[j] = buffer[i+j]; for(j=wcopy; j<windowSize; j++) thisWindow[j] = 0; if (mDoProfile) GetProfile(windowSize, thisWindow); else { //TIDY-ME: could we just test mLevel=<0 in CheckWhetherSkipEffect? if (mLevel > 0) { // Skip NoiseRemoval if zero ... may apply for CleanChain RemoveNoise(windowSize, thisWindow); for(j=0; j<windowSize/2; j++) { buffer[i+j] = thisWindow[j] + lastWindow[windowSize/2 + j]; } } } float *tempP = thisWindow; thisWindow = lastWindow; lastWindow = tempP; } // Shift by half-a-window less than the block size we loaded // (so that the blocks properly overlap) block -= windowSize/2; if (!mDoProfile) track->Set((samplePtr) buffer, floatSample, start + s, block); s += block; if (TrackProgress(count, s / (double) len)) { retCode = false; break; } } delete[] buffer; delete[] window1; delete[] window2; return retCode; }
// ProcessOne() takes a track, transforms it to bunch of buffer-blocks, // and calls libsamplerate code on these blocks. bool EffectChangeSpeed::ProcessOne(WaveTrack * track, sampleCount start, sampleCount end) { if (track == NULL) return false; // initialization, per examples of Mixer::Mixer and // EffectSoundTouch::ProcessOne WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); //Get the length of the selection (as double). len is //used simple to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); // Initiate processing buffers, most likely shorter than // the length of the selection being processed. sampleCount inBufferSize = track->GetMaxBlockSize(); float * inBuffer = new float[inBufferSize]; sampleCount outBufferSize = (sampleCount)((mFactor * inBufferSize) + 10); float * outBuffer = new float[outBufferSize]; // Set up the resampling stuff for this track. Resample resample(true, mFactor, mFactor); //Go through the track one buffer at a time. samplePos counts which //sample the current buffer starts at. bool bResult = true; sampleCount blockSize; sampleCount samplePos = start; while (samplePos < end) { //Get a blockSize of samples (smaller than the size of the buffer) blockSize = track->GetBestBlockSize(samplePos); //Adjust the block size if it is the final block in the track if (samplePos + blockSize > end) blockSize = end - samplePos; //Get the samples from the track and put them in the buffer track->Get((samplePtr) inBuffer, floatSample, samplePos, blockSize); int inUsed; int outgen = resample.Process(mFactor, inBuffer, blockSize, ((samplePos + blockSize) >= end), &inUsed, outBuffer, outBufferSize); if (outgen < 0) { bResult = false; break; } if (outgen > 0) outputTrack->Append((samplePtr)outBuffer, floatSample, outgen); // Increment samplePos samplePos += inUsed; // Update the Progress meter if (TrackProgress(mCurTrackNum, (samplePos - start) / len)) { bResult = false; break; } } // Flush the output WaveTrack (since it's buffered, too) outputTrack->Flush(); // Clean up the buffers delete [] inBuffer; delete [] outBuffer; // Take the output track and insert it in place of the original // sample data double newLength = outputTrack->GetEndTime(); if (bResult) { SetTimeWarper(new LinearTimeWarper(mCurT0, mCurT0, mCurT1, mCurT0 + newLength)); bResult = track->ClearAndPaste(mCurT0, mCurT1, outputTrack, true, false, GetTimeWarper()); } if (newLength > mMaxNewLength) mMaxNewLength = newLength; // Delete the outputTrack now that its data is inserted in place delete outputTrack; return bResult; }
bool LV2Effect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { /* Allocate buffers */ if (mBlockSize == 0) { mBlockSize = left->GetMaxBlockSize() * 2; fInBuffer = new float *[mAudioInputs.GetCount()]; for (size_t i = 0; i < mAudioInputs.GetCount(); i++) { fInBuffer[i] = new float[mBlockSize]; } fOutBuffer = new float *[mAudioOutputs.GetCount()]; for (size_t i = 0; i < mAudioOutputs.GetCount(); i++) { fOutBuffer[i] = new float[mBlockSize]; } } /* Instantiate the plugin */ LilvInstance *handle = lilv_plugin_instantiate(mData, left->GetRate(), gLV2Features); if (!handle) { wxMessageBox(wxString::Format(_("Unable to load plug-in %s"), pluginName.c_str())); return false; } /* Write the Note On to the MIDI event buffer and connect it */ LV2_Event_Buffer *midiBuffer = NULL; int noteOffTime; if (mMidiInput) { midiBuffer = lv2_event_buffer_new(40, 2); LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOn[] = { 0x90, mNoteKey, mNoteVelocity }; lv2_event_write(&iter, 0, 0, 1, 3, noteOn); noteOffTime = mNoteLength * left->GetRate(); if (noteOffTime < len && noteOffTime < mBlockSize) { uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); } lilv_instance_connect_port(handle, mMidiInput->mIndex, midiBuffer); } for (size_t p = 0; p < mAudioInputs.GetCount(); p++) { lilv_instance_connect_port(handle, mAudioInputs[p].mIndex, fInBuffer[p]); } for (size_t p = 0; p < mAudioOutputs.GetCount(); p++) { lilv_instance_connect_port(handle, mAudioOutputs[p].mIndex, fOutBuffer[p]); } for (size_t p = 0; p < mControlInputs.GetCount(); p++) { lilv_instance_connect_port(handle, mControlInputs[p].mIndex, &mControlInputs[p].mControlBuffer); } for (size_t p = 0; p < mControlOutputs.GetCount(); p++) { lilv_instance_connect_port(handle, mControlOutputs[p].mIndex, &mControlOutputs[p].mControlBuffer); } float latency = 0.0; if (mLatencyPortIndex >= 0) { lilv_instance_connect_port(handle, mLatencyPortIndex, &latency); } lilv_instance_activate(handle); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; sampleCount ols = ls; sampleCount ors = rs; bool noteOver = false; sampleCount delayed = 0; sampleCount delay = 0; bool cleared = false; while (len || delayed) { int block = mBlockSize; if (len) { if (block > len) { block = len; } if (left && mAudioInputs.GetCount() > 0) { left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); } if (right && mAudioInputs.GetCount() > 1) { right->Get((samplePtr)fInBuffer[1], floatSample, rs, block); } } else if (delayed) { // At the end if we don't have enough left for a whole block if (block > delayed) { block = delayed; } // Clear the input buffer so that we only pass zeros to the effect. if (!cleared) { for (int i = 0; i < mBlockSize; i++) { fInBuffer[0][i] = 0.0; } if (right) { memcpy(fInBuffer[1], fOutBuffer[0], mBlockSize); } cleared = true; } } lilv_instance_run(handle, block); if (delayed == 0 && latency != 0) { delayed = delay = latency; } if (delay >= block) { delay -= block; } else if (delay > 0) { sampleCount oblock = block - delay; if (left && mAudioOutputs.GetCount() > 0) { left->Set((samplePtr)(fOutBuffer[0] + delay), floatSample, ols, oblock); } if (right && mAudioOutputs.GetCount() > 1) { right->Set((samplePtr)(fOutBuffer[1] + delay), floatSample, ors, oblock); } ols += oblock; ors += oblock; delay = 0; } else { if (left && mAudioOutputs.GetCount() > 0) { left->Set((samplePtr)fOutBuffer[0], floatSample, ols, block); } if (right && mAudioOutputs.GetCount() > 1) { right->Set((samplePtr)fOutBuffer[1], floatSample, ors, block); } ols += block; ors += block; } if (len) { len -= block; noteOffTime -= block; // Clear the event buffer and add the note off event if needed if (mMidiInput) { lv2_event_buffer_reset(midiBuffer, 1, (uint8_t *)midiBuffer + sizeof(LV2_Event_Buffer)); if (!noteOver && noteOffTime < len && noteOffTime < block) { LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); noteOver = true; } } } else if (delayed) { delayed -= block; } ls += block; rs += block; if (mAudioInputs.GetCount() > 1) { if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen)) { return false; } } else { if (TrackProgress(count, (ls-lstart)/(double)originalLen)) { return false; } } } lilv_instance_deactivate(handle); lilv_instance_free(handle); return true; }
//AnalyseDC() takes a track, transforms it to bunch of buffer-blocks, //and executes AnalyzeData on it... // sets mOffset bool EffectNormalize::AnalyseDC(WaveTrack * track, wxString msg) { bool rc = true; sampleCount s; mOffset = 0.0; // we might just return if(!mDC) // don't do analysis if not doing dc removal return(rc); //Transform the marker timepoints to samples sampleCount start = track->TimeToLongSamples(mCurT0); sampleCount end = track->TimeToLongSamples(mCurT1); //Get the length of the buffer (as double). len is //used simply to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); //Initiate a processing buffer. This buffer will (most likely) //be shorter than the length of the track being processed. float *buffer = new float[track->GetMaxBlockSize()]; mSum = 0.0; // dc offset inits mCount = 0; //Go through the track one buffer at a time. s counts which //sample the current buffer starts at. s = start; while (s < end) { //Get a block of samples (smaller than the size of the buffer) sampleCount block = track->GetBestBlockSize(s); //Adjust the block size if it is the final block in the track if (s + block > end) block = end - s; //Get the samples from the track and put them in the buffer track->Get((samplePtr) buffer, floatSample, s, block); //Process the buffer. AnalyzeData(buffer, block); //Increment s one blockfull of samples s += block; //Update the Progress meter if (TrackProgress(mCurTrackNum, ((double)(s - start) / len)/2.0, msg)) { rc = false; //lda .. break, not return, so that buffer is deleted break; } } //Clean up the buffer delete[] buffer; mOffset = (float)(-mSum / mCount); // calculate actual offset (amount that needs to be added on) //Return true because the effect processing succeeded ... unless cancelled return rc; }
bool EffectNoise::Process() { if (noiseDuration <= 0.0) noiseDuration = sDefaultGenerateLen; //Iterate over each track int ntrack = 0; this->CopyInputWaveTracks(); // Set up mOutputWaveTracks. bool bGoodResult = true; TrackListIterator iter(mOutputWaveTracks); WaveTrack *track = (WaveTrack *)iter.First(); while (track) { WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); numSamples = (longSampleCount)(noiseDuration * track->GetRate() + 0.5); longSampleCount i = 0; float *data = new float[tmp->GetMaxBlockSize()]; sampleCount block; while ((i < numSamples) && bGoodResult) { block = tmp->GetBestBlockSize(i); if (block > (numSamples - i)) block = numSamples - i; MakeNoise(data, block, track->GetRate(), noiseAmplitude); tmp->Append((samplePtr)data, floatSample, block); i += block; //Update the Progress meter if (TrackProgress(ntrack, (double)i / numSamples)) bGoodResult = false; } delete[] data; tmp->Flush(); track->Clear(mT0, mT1); track->Paste(mT0, tmp); delete tmp; if (!bGoodResult) break; //Iterate to the next track ntrack++; track = (WaveTrack *)iter.Next(); } if (bGoodResult) { /* save last used values save duration unless value was got from selection, so we save only when user explicitely setup a value */ if (mT1 == mT0) gPrefs->Write(wxT("/CsPresets/NoiseGen_Duration"), noiseDuration); gPrefs->Write(wxT("/CsPresets/NoiseGen_Type"), noiseType); gPrefs->Write(wxT("/CsPresets/NoiseGen_Amp"), noiseAmplitude); mT1 = mT0 + noiseDuration; // Update selection. } this->ReplaceProcessedWaveTracks(bGoodResult); return bGoodResult; }
bool EffectDtmf::GenerateTrack(WaveTrack *tmp, const WaveTrack &track, int ntrack) { bool bGoodResult = true; // all dtmf sequence durations in samples from seconds numSamplesSequence = (sampleCount)(mDuration * track.GetRate() + 0.5); numSamplesTone = (sampleCount)(dtmfTone * track.GetRate() + 0.5); numSamplesSilence = (sampleCount)(dtmfSilence * track.GetRate() + 0.5); // recalculate the sum, and spread the difference - due to approximations. // Since diff should be in the order of "some" samples, a division (resulting in zero) // is not sufficient, so we add the additional remaining samples in each tone/silence block, // at least until available. int diff = numSamplesSequence - (dtmfNTones*numSamplesTone) - (dtmfNTones-1)*numSamplesSilence; if (diff>dtmfNTones) { // in this case, both these values would change, so it makes sense to recalculate diff // otherwise just keep the value we already have // should always be the case that dtmfNTones>1, as if 0, we don't even start processing, // and with 1 there is no difference to spread (no silence slot)... wxASSERT(dtmfNTones > 1); numSamplesTone += (diff/(dtmfNTones)); numSamplesSilence += (diff/(dtmfNTones-1)); diff = numSamplesSequence - (dtmfNTones*numSamplesTone) - (dtmfNTones-1)*numSamplesSilence; } // this var will be used as extra samples distributor int extra=0; sampleCount i = 0; sampleCount j = 0; int n=0; // pointer to string in dtmfString sampleCount block; bool isTone = true; float *data = new float[tmp->GetMaxBlockSize()]; // for the whole dtmf sequence, we will be generating either tone or silence // according to a bool value, and this might be done in small chunks of size // 'block', as a single tone might sometimes be larger than the block // tone and silence generally have different duration, thus two generation blocks // // Note: to overcome a 'clicking' noise introduced by the abrupt transition from/to // silence, I added a fade in/out of 1/250th of a second (4ms). This can still be // tweaked but gives excellent results at 44.1kHz: I haven't tried other freqs. // A problem might be if the tone duration is very short (<10ms)... (?) // // One more problem is to deal with the approximations done when calculating the duration // of both tone and silence: in some cases the final sum might not be same as the initial // duration. So, to overcome this, we had a redistribution block up, and now we will spread // the remaining samples in every bin in order to achieve the full duration: test case was // to generate an 11 tone DTMF sequence, in 4 seconds, and with DutyCycle=75%: after generation // you ended up with 3.999s or in other units: 3 seconds and 44097 samples. // while ((i < numSamplesSequence) && bGoodResult) { if (isTone) // generate tone { // the statement takes care of extracting one sample from the diff bin and // adding it into the tone block until depletion extra=(diff-- > 0?1:0); for(j=0; j < numSamplesTone+extra && bGoodResult; j+=block) { block = tmp->GetBestBlockSize(j); if (block > (numSamplesTone+extra - j)) block = numSamplesTone+extra - j; // generate the tone and append MakeDtmfTone(data, block, track.GetRate(), dtmfString[n], j, numSamplesTone, dtmfAmplitude); tmp->Append((samplePtr)data, floatSample, block); //Update the Progress meter if (TrackProgress(ntrack, (double)(i+j) / numSamplesSequence)) bGoodResult = false; } i += numSamplesTone; n++; if(n>=dtmfNTones)break; } else // generate silence { // the statement takes care of extracting one sample from the diff bin and // adding it into the silence block until depletion extra=(diff-- > 0?1:0); for(j=0; j < numSamplesSilence+extra && bGoodResult; j+=block) { block = tmp->GetBestBlockSize(j); if (block > (numSamplesSilence+extra - j)) block = numSamplesSilence+extra - j; // generate silence and append memset(data, 0, sizeof(float)*block); tmp->Append((samplePtr)data, floatSample, block); //Update the Progress meter if (TrackProgress(ntrack, (double)(i+j) / numSamplesSequence)) bGoodResult = false; } i += numSamplesSilence; } // flip flag isTone=!isTone; } // finished the whole dtmf sequence delete[] data; return bGoodResult; }
bool EffectTruncSilence::Process() { SelectedTrackListOfKindIterator iter(Track::Wave, mTracks); WaveTrack *t; double t0 = mT0; double t1 = mT1; int tndx; int tcount = 0; int fr; // Init using first track t = (WaveTrack *) iter.First(); double rate = t->GetRate(); sampleCount blockLen = t->GetMaxBlockSize(); // Get the left and right bounds for all tracks while (t) { // Make sure all tracks have the same sample rate if (rate != t->GetRate()) { wxMessageBox(_("All tracks must have the same sample rate"), _("Truncate Silence")); return false; } // Count the tracks tcount++; // Set the current bounds to whichever left marker is // greater and whichever right marker is less t0 = wxMax(mT0, t->GetStartTime()); t1 = wxMin(mT1, t->GetEndTime()); // Use the smallest block size of all the tracks blockLen = wxMin(blockLen, t->GetMaxBlockSize()); // Iterate to the next track t = (WaveTrack*) iter.Next(); } // Just a sanity check, really it should be much higher if(blockLen < 4*mBlendFrameCount) blockLen = 4*mBlendFrameCount; // Transform the marker timepoints to samples t = (WaveTrack *) iter.First(); sampleCount start = t->TimeToLongSamples(t0); sampleCount end = t->TimeToLongSamples(t1); // Bigger buffers reduce 'reset' //blockLen *= 8; // Stress-test the logic for cutting samples through block endpoints //blockLen /= 8; // Set thresholds // We have a lower bound on the amount of silence we chop out at a time // to avoid chopping up low frequency sounds. We're good down to 10Hz // if we use 100ms. const float minTruncMs = 1.0f; double truncDbSilenceThreshold = Enums::Db2Signal[mTruncDbChoiceIndex]; int truncInitialAllowedSilentSamples = int((wxMax( mTruncInitialAllowedSilentMs, minTruncMs) * rate) / 1000.0); int truncLongestAllowedSilentSamples = int((wxMax( mTruncLongestAllowedSilentMs, minTruncMs) * rate) / 1000.0); // Require at least 4 samples for lengths if(truncInitialAllowedSilentSamples < 4) truncInitialAllowedSilentSamples = 4; if(truncLongestAllowedSilentSamples < 4) truncLongestAllowedSilentSamples = 4; // If the cross-fade is longer than the minimum length, // then limit the cross-fade length to the minimum length // This allows us to have reasonable cross-fade by default // and still allow for 1ms minimum lengths if(truncInitialAllowedSilentSamples < mBlendFrameCount) mBlendFrameCount = truncInitialAllowedSilentSamples; if(truncLongestAllowedSilentSamples < mBlendFrameCount) mBlendFrameCount = truncLongestAllowedSilentSamples; // For sake of efficiency, don't let blockLen be less than double the longest silent samples // up until a sane limit of 1Meg samples while((blockLen > 0) && (blockLen < truncLongestAllowedSilentSamples*2) && (blockLen < 1048576)) { blockLen *= 2; } // Don't allow either value to be more than half of the block length if(truncLongestAllowedSilentSamples > blockLen/2) truncLongestAllowedSilentSamples = blockLen/2; if(truncInitialAllowedSilentSamples > truncLongestAllowedSilentSamples) truncInitialAllowedSilentSamples = truncLongestAllowedSilentSamples; // We use the 'longest' variable as additive to the 'initial' variable truncLongestAllowedSilentSamples -= truncInitialAllowedSilentSamples; // Perform the crossfade half-way through the minimum removed silence duration int rampInFrames = (truncInitialAllowedSilentSamples + mBlendFrameCount) / 2; if(rampInFrames > truncInitialAllowedSilentSamples) rampInFrames = truncInitialAllowedSilentSamples; // Allocate buffers float **buffer = new float*[tcount]; for (tndx = 0; tndx < tcount; tndx++) { buffer[tndx] = new float[blockLen]; } // Start processing //Track::All is needed because this effect has clear functionality this->CopyInputTracks(Track::All); // Set up mOutputTracks. SelectedTrackListOfKindIterator iterOut(Track::Wave, mOutputTracks); sampleCount index = start; sampleCount outTrackOffset = start; bool cancelled = false; // Reset bool ignoringFrames = false; bool truncToMinimum = true; // Ignore the initial samples until we get above the noise floor sampleCount consecutiveSilentFrames = 0; sampleCount truncIndex = 0; sampleCount i = 0; sampleCount keep; while (index < end) { // Limit size of current block if we've reached the end sampleCount count = blockLen-i; if ((index + count) > end) { count = end - index; } // Fill the buffers tndx = 0; t = (WaveTrack *) iter.First(); while (t) { t->Get((samplePtr)(buffer[tndx++]+i), floatSample, index, count); t = (WaveTrack *) iter.Next(); } // Shift over to account for samples remaining from prior block sampleCount limit = count+i; // Look for silences in current block for ( ; i < limit; i++) { // Is current frame in all tracks below threshold bool below = true; for (tndx = 0; tndx < tcount; tndx++) { if (fabs(buffer[tndx][i]) >= truncDbSilenceThreshold) { below = false; break; } } // Make sure we cross-fade and output the last silence // so we get a smooth transition into whatever follows the selected region // Also set the 'truncToMinimum' flag so that the last silence is truncated to the minimum amount if(below && ((index+i+1) == end)) { below = false; truncToMinimum = true; } // Count frame if it's below threshold if (below) { consecutiveSilentFrames++; // Ignore this frame (equivalent to cutting it) // otherwise, keep sample to be part of allowed silence if (consecutiveSilentFrames > truncInitialAllowedSilentSamples) { ignoringFrames = true; continue; } } else { if (ignoringFrames == true) { // Scale the consectiveSilentFrames so we keep a silence duration // which is proportional to the original silence up to the limit keep = consecutiveSilentFrames - truncInitialAllowedSilentSamples; keep /= mSilenceCompressRatio; // The first and last samples always get truncated to the minimum amount if(truncToMinimum == true) keep = 0; if(keep > truncLongestAllowedSilentSamples) keep = truncLongestAllowedSilentSamples; if(keep < 0) keep = 0; // Compute the location of the cross-fade to be halfway through the silence // with restriction to the samples we still have available to use rampInFrames = (truncInitialAllowedSilentSamples - keep + mBlendFrameCount) / 2; if(rampInFrames > truncInitialAllowedSilentSamples) rampInFrames = truncInitialAllowedSilentSamples; if(rampInFrames < mBlendFrameCount) rampInFrames = mBlendFrameCount; // Include the cross-fade samples in the count to make the loop logic easier keep += rampInFrames; truncIndex -= rampInFrames; // back up for cross-fade sampleCount curOffset = i - keep; if(curOffset < 0) { // This should never happen, but just in case... keep += curOffset - rampInFrames; if(keep < mBlendFrameCount) keep = mBlendFrameCount; curOffset = 0; } if(truncIndex < 0) { // This should never happen, but just in case... truncIndex = 0; } for (tndx = 0; tndx < tcount; tndx++) { // Cross fade the cut point for (fr = 0; fr < mBlendFrameCount; fr++) { buffer[tndx][truncIndex+fr] = ((mBlendFrameCount-fr)*buffer[tndx][truncIndex+fr] + fr*buffer[tndx][curOffset + fr]) / mBlendFrameCount; } // Append the 'keep' samples, if any for ( ; fr < keep; fr++) { buffer[tndx][truncIndex+fr] = buffer[tndx][curOffset + fr]; } } truncIndex += keep; } consecutiveSilentFrames = 0; ignoringFrames = false; truncToMinimum = false; } // Can get here either because > dbThreshold // or silence duration isn't longer than allowed for (tndx = 0; tndx < tcount; tndx++) { buffer[tndx][truncIndex] = buffer[tndx][i]; } truncIndex++; } // Update tracks if any samples were removed, now or before if (outTrackOffset + truncIndex != index + limit) { // Put updated sample back into output tracks. tndx = 0; t = (WaveTrack *) iterOut.First(); while (t) { t->Set((samplePtr)buffer[tndx++], floatSample, outTrackOffset, truncIndex); t = (WaveTrack *) iterOut.Next(); } } // If currently in a silent section, retain samples for the next pass if(ignoringFrames) { keep = consecutiveSilentFrames - truncInitialAllowedSilentSamples; if(keep > (truncLongestAllowedSilentSamples+mBlendFrameCount)) keep = truncLongestAllowedSilentSamples+mBlendFrameCount; for (tndx = 0; tndx < tcount; tndx++) { for(fr = 0; fr < truncInitialAllowedSilentSamples; fr++) { buffer[tndx][fr] = buffer[tndx][truncIndex-truncInitialAllowedSilentSamples+fr]; } for(fr = 0; fr < keep; fr++) { buffer[tndx][truncInitialAllowedSilentSamples+fr] = buffer[tndx][i-keep+fr]; } } // Update the output index, less what we are retaining for next time outTrackOffset += truncIndex - truncInitialAllowedSilentSamples; // Append the following buffer to the existing data i = consecutiveSilentFrames = truncInitialAllowedSilentSamples + keep; truncIndex = truncInitialAllowedSilentSamples; } else { // Maintain output index outTrackOffset += truncIndex; // Reset the buffer pointers to the beginning i = 0; truncIndex = 0; consecutiveSilentFrames = 0; } // Update progress and bail if user cancelled cancelled = TrackProgress(0, ((double)index / (double)end)); if (cancelled) { break; } // Bump to next block index += count; } AudacityProject *p = GetActiveProject(); if (!p) return false; // Remove stale data at end of output tracks. if (!cancelled && (outTrackOffset < end)) { t = (WaveTrack *) iterOut.First(); if( p->IsSticky() ) t->Clear(outTrackOffset / rate, t1, mOutputTracks); else while(t) { t->Clear(outTrackOffset / rate, t1, mOutputTracks); t = (WaveTrack *) iterOut.Next(); } t1 = outTrackOffset / rate; } // Free buffers for (tndx = 0; tndx < tcount; tndx++) { delete [] buffer[tndx]; } delete [] buffer; mT0 = t0; mT1 = t1; this->ReplaceProcessedTracks(!cancelled); return !cancelled; }