void FreqWindow::GetAudio() { int selcount = 0; int i; bool warning = false; //wxLogDebug(wxT("Entering FreqWindow::GetAudio()")); TrackListIterator iter(p->GetTracks()); Track *t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { WaveTrack *track = (WaveTrack *)t; if (selcount==0) { mRate = track->GetRate(); sampleCount start, end; start = track->TimeToLongSamples(p->mViewInfo.sel0); end = track->TimeToLongSamples(p->mViewInfo.sel1); mDataLen = (sampleCount)(end - start); if (mDataLen > 10485760) { warning = true; mDataLen = 10485760; } if (mBuffer) { delete [] mBuffer; } mBuffer = new float[mDataLen]; track->Get((samplePtr)mBuffer, floatSample, start, mDataLen); } else { if (track->GetRate() != mRate) { wxMessageBox(_("To plot the spectrum, all selected tracks must be the same sample rate.")); delete[] mBuffer; mBuffer = NULL; return; } sampleCount start; start = track->TimeToLongSamples(p->mViewInfo.sel0); float *buffer2 = new float[mDataLen]; track->Get((samplePtr)buffer2, floatSample, start, mDataLen); for(i=0; i<mDataLen; i++) mBuffer[i] += buffer2[i]; delete[] buffer2; } selcount++; } t = iter.Next(); } if (selcount == 0) return; if (warning) { wxString msg; msg.Printf(_("Too much audio was selected. Only the first %.1f seconds of audio will be analyzed."), (mDataLen / mRate)); //wxLogDebug(wxT("About to show length warning message")); wxMessageBox(msg); //wxLogDebug(wxT("Length warning message done")); } //wxLogDebug(wxT("Leaving FreqWindow::GetAudio()")); }
bool EffectClickRemoval::Process() { this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; if (t1 > t0) { sampleCount start = track->TimeToLongSamples(t0); sampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); if (!ProcessOne(count, track, start, len)) { bGoodResult = false; break; } } track = (WaveTrack *) iter.Next(); count++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }
bool EffectEqualization::Process() { TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; if (t1 > t0) { longSampleCount start = track->TimeToLongSamples(t0); longSampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); if (!ProcessOne(count, track, start, len)) return false; } track = (WaveTrack *) iter.Next(); count++; } return true; }
void FreqWindow::GetAudio() { mData.reset(); mDataLen = 0; int selcount = 0; bool warning = false; TrackListIterator iter(p->GetTracks()); Track *t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { WaveTrack *track = (WaveTrack *)t; if (selcount==0) { mRate = track->GetRate(); auto start = track->TimeToLongSamples(p->mViewInfo.selectedRegion.t0()); auto end = track->TimeToLongSamples(p->mViewInfo.selectedRegion.t1()); auto dataLen = end - start; if (dataLen > 10485760) { warning = true; mDataLen = 10485760; } else // dataLen is not more than 10 * 2 ^ 20 mDataLen = dataLen.as_size_t(); mData = Floats{ mDataLen }; // Don't allow throw for bad reads track->Get((samplePtr)mData.get(), floatSample, start, mDataLen, fillZero, false); } else { if (track->GetRate() != mRate) { AudacityMessageBox(_("To plot the spectrum, all selected tracks must be the same sample rate.")); mData.reset(); mDataLen = 0; return; } auto start = track->TimeToLongSamples(p->mViewInfo.selectedRegion.t0()); Floats buffer2{ mDataLen }; // Again, stop exceptions track->Get((samplePtr)buffer2.get(), floatSample, start, mDataLen, fillZero, false); for (size_t i = 0; i < mDataLen; i++) mData[i] += buffer2[i]; } selcount++; } t = iter.Next(); } if (selcount == 0) return; if (warning) { wxString msg; msg.Printf(_("Too much audio was selected. Only the first %.1f seconds of audio will be analyzed."), (mDataLen / mRate)); AudacityMessageBox(msg); } }
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 EffectNoiseRemoval::Process() { if (!mDoProfile && !mHasProfile) CleanSpeechMayReadNoisegate(); // If we still don't have a profile we have a problem. // This should only happen in CleanSpeech. if(!mDoProfile && !mHasProfile) { wxMessageBox( _("Attempt to run Noise Removal without a noise profile.\n")); return false; } Initialize(); // This same code will both remove noise and profile it, // depending on 'mDoProfile' this->CopyInputWaveTracks(); // Set up mOutputWaveTracks. bool bGoodResult = true; TrackListIterator iter(mOutputWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; if (t1 > t0) { sampleCount start = track->TimeToLongSamples(t0); sampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); if (!ProcessOne(count, track, start, len)) { Cleanup(); bGoodResult = false; break; } } track = (WaveTrack *) iter.Next(); count++; } if (bGoodResult && mDoProfile) { CleanSpeechMayWriteNoiseGate(); mHasProfile = true; mDoProfile = false; } if (bGoodResult) Cleanup(); this->ReplaceProcessedWaveTracks(bGoodResult); return bGoodResult; }
bool EffectFindClipping::Process() { LabelTrack *l = NULL; Track *original = NULL; TrackListOfKindIterator iter(Track::Label, mTracks); for (Track *t = iter.First(); t; t = iter.Next()) { if (t->GetName() == wxT("Clipping")) { l = (LabelTrack *) t; // copy LabelTrack here, so it can be undone on cancel l->Copy(l->GetStartTime(), l->GetEndTime(), &original); original->SetOffset(l->GetStartTime()); original->SetName(wxT("Clipping")); break; } } if (!l) { l = mFactory->NewLabelTrack(); l->SetName(_("Clipping")); mTracks->Add((Track *) l); } int count = 0; // JC: Only process selected tracks. SelectedTrackListOfKindIterator waves(Track::Wave, mTracks); WaveTrack *t = (WaveTrack *) waves.First(); while (t) { double trackStart = t->GetStartTime(); double trackEnd = t->GetEndTime(); double t0 = mT0 < trackStart ? trackStart : mT0; double t1 = mT1 > trackEnd ? trackEnd : mT1; if (t1 > t0) { sampleCount start = t->TimeToLongSamples(t0); sampleCount end = t->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); if (!ProcessOne(l, count, t, start, len)) { //put it back how it was mTracks->Remove((Track *) l); if(original) { mTracks->Add((Track *) original); } return false; } } count++; t = (WaveTrack *) waves.Next(); } return true; }
bool EffectSoundTouch::Process() { // Assumes that mSoundTouch has already been initialized // by the subclass for subclass-specific parameters. //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack* leftTrack = (WaveTrack*)(iter.First()); WaveTrack* rightTrack = NULL; mCurTrackNum = 0; m_maxNewLength = 0.0; while (leftTrack) { //Get start and end times from track double trackStart = leftTrack->GetStartTime(); double trackEnd = leftTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples longSampleCount start = leftTrack->TimeToLongSamples(mCurT0); longSampleCount end = leftTrack->TimeToLongSamples(mCurT1); rightTrack = NULL; if (leftTrack->GetLinked()) { rightTrack = (WaveTrack*)(iter.Next()); mSoundTouch->setChannels(2); if (!ProcessStereo(leftTrack, rightTrack, start, end)) return false; mCurTrackNum++; // Increment for rightTrack, too. } else { mSoundTouch->setChannels(1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(leftTrack, start, end)) return false; } } //Iterate to the next track leftTrack = (WaveTrack*)(iter.Next()); mCurTrackNum++; } delete mSoundTouch; mSoundTouch = NULL; mT1 = mT0 + m_maxNewLength; // Update selection. return true; }
bool EffectPopClickRemoval::Process() { TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; double deltat = t1 - t0; double tpre; double pretime; double rate = track->GetRate(); int max_matrix_size = 1500; pretime = 3*deltat; if (pretime * rate > max_matrix_size) pretime = max_matrix_size / rate; tpre = t0 - pretime; tpre = tpre < trackStart? trackStart: tpre; tpre = tpre > t0? t0: tpre; if (t1 - t0 > 1) { ::wxMessageBox(_("Cannot remove a pop or click longer than " "one second.")); } else if (t1 > t0) { if ((t0 - tpre) * rate < 20) ::wxMessageBox(_("Cannot remove a pop or click at the very " "beginning of a track.")); else { longSampleCount pre = track->TimeToLongSamples(tpre); longSampleCount start = track->TimeToLongSamples(t0); longSampleCount end = track->TimeToLongSamples(t1); sampleCount preLen = (sampleCount)(start - pre); sampleCount postLen = (sampleCount)(end - start); if (!ProcessOne(count, track, pre, preLen, postLen)) return false; } } track = (WaveTrack *) iter.Next(); count++; } return true; }
bool EffectChangeSpeed::Process() { // Similar to EffectSoundTouch::Process() //Iterate over each track this->CopyInputWaveTracks(); // Set up m_pOutputWaveTracks. bool bGoodResult = true; TrackListIterator iter(m_pOutputWaveTracks); WaveTrack* pOutWaveTrack = (WaveTrack*)(iter.First()); mCurTrackNum = 0; m_maxNewLength = 0.0; while (pOutWaveTrack != NULL) { //Get start and end times from track mCurT0 = pOutWaveTrack->GetStartTime(); mCurT1 = pOutWaveTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = wxMax(mT0, mCurT0); mCurT1 = wxMin(mT1, mCurT1); // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples longSampleCount start = pOutWaveTrack->TimeToLongSamples(mCurT0); longSampleCount end = pOutWaveTrack->TimeToLongSamples(mCurT1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(pOutWaveTrack, start, end)) { bGoodResult = false; break; } } //Iterate to the next track pOutWaveTrack = (WaveTrack*)(iter.Next()); mCurTrackNum++; } this->ReplaceProcessedWaveTracks(bGoodResult); // mT1 = mT0 + m_maxNewLength; // Update selection. return bGoodResult; }
bool EffectTwoPassSimpleMono::ProcessPass() { //Iterate over each track SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); WaveTrack *track = (WaveTrack *) iter.First(); mCurTrackNum = 0; while (track) { //Get start and end times from track double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples sampleCount start = track->TimeToLongSamples(mCurT0); sampleCount end = track->TimeToLongSamples(mCurT1); //Get the track rate and samples mCurRate = track->GetRate(); mCurChannel = track->GetChannel(); //NewTrackPass1/2() returns true by default bool ret; if (mPass == 0) ret = NewTrackPass1(); else ret = NewTrackPass2(); if (!ret) return false; //ProcessOne() (implemented below) processes a single track if (!ProcessOne(track, start, end)) return false; } //Iterate to the next track track = (WaveTrack *) iter.Next(); mCurTrackNum++; } return true; }
bool EffectSimpleMono::Process() { //Iterate over each track this->CopyInputWaveTracks(); // Set up mOutputWaveTracks. bool bGoodResult = true; TrackListIterator iter(mOutputWaveTracks); WaveTrack* pOutWaveTrack = (WaveTrack*)(iter.First()); mCurTrackNum = 0; while (pOutWaveTrack != NULL) { //Get start and end times from track double trackStart = pOutWaveTrack->GetStartTime(); double trackEnd = pOutWaveTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples longSampleCount start = pOutWaveTrack->TimeToLongSamples(mCurT0); longSampleCount end = pOutWaveTrack->TimeToLongSamples(mCurT1); //Get the track rate and samples mCurRate = pOutWaveTrack->GetRate(); mCurChannel = pOutWaveTrack->GetChannel(); //NewTrackSimpleMono() will returns true by default //ProcessOne() processes a single track if (!NewTrackSimpleMono() || !ProcessOne(pOutWaveTrack, start, end)) { bGoodResult = false; break; } } //Iterate to the next track pOutWaveTrack = (WaveTrack*)(iter.Next()); mCurTrackNum++; } this->ReplaceProcessedWaveTracks(bGoodResult); return bGoodResult; }
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 EffectSoundTouch::Process() { //Assumes that mSoundTouch has already been initialized //by the subclass //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); mCurTrackNum = 0; m_maxNewLength = 0.0; while (track) { //Get start and end times from track double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples longSampleCount start = track->TimeToLongSamples(mCurT0); longSampleCount end = track->TimeToLongSamples(mCurT1); //Get the track rate and samples mCurRate = track->GetRate(); mCurChannel = track->GetChannel(); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(track, start, end)) return false; } //Iterate to the next track track = (WaveTrack *) iter.Next(); mCurTrackNum++; } delete mSoundTouch; mSoundTouch = NULL; mT1 = mT0 + m_maxNewLength; // Update selection. return true; }
// Deduce m_FromFrequency from the samples at the beginning of // the selection. Then set some other params accordingly. void EffectChangePitch::DeduceFrequencies() { // As a neat trick, attempt to get the frequency of the note at the // beginning of the selection. SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks()); WaveTrack *track = (WaveTrack *) iter.First(); if (track) { double rate = track->GetRate(); // Auto-size window -- high sample rates require larger windowSize. // Aim for around 2048 samples at 44.1 kHz (good down to about 100 Hz). // To detect single notes, analysis period should be about 0.2 seconds. // windowSize must be a power of 2. const size_t windowSize = // windowSize < 256 too inaccurate std::max(256, wxRound(pow(2.0, floor((log(rate / 20.0)/log(2.0)) + 0.5)))); // we want about 0.2 seconds to catch the first note. // number of windows rounded to nearest integer >= 1. const unsigned numWindows = std::max(1, wxRound((double)(rate / (5.0f * windowSize)))); double trackStart = track->GetStartTime(); double t0 = mT0 < trackStart? trackStart: mT0; auto start = track->TimeToLongSamples(t0); auto analyzeSize = windowSize * numWindows; Floats buffer{ analyzeSize }; Floats freq{ windowSize / 2 }; Floats freqa{ windowSize / 2, true }; track->Get((samplePtr) buffer.get(), floatSample, start, analyzeSize); for(unsigned i = 0; i < numWindows; i++) { ComputeSpectrum(buffer.get() + i * windowSize, windowSize, windowSize, rate, freq.get(), true); for(size_t j = 0; j < windowSize / 2; j++) freqa[j] += freq[j]; } size_t argmax = 0; for(size_t j = 1; j < windowSize / 2; j++) if (freqa[j] > freqa[argmax]) argmax = j; auto lag = (windowSize / 2 - 1) - argmax; m_dStartFrequency = rate / lag; } double dFromMIDInote = FreqToMIDInote(m_dStartFrequency); double dToMIDInote = dFromMIDInote + m_dSemitonesChange; m_nFromPitch = PitchIndex(dFromMIDInote); m_nFromOctave = PitchOctave(dFromMIDInote); m_nToPitch = PitchIndex(dToMIDInote); m_nToOctave = PitchOctave(dToMIDInote); m_FromFrequency = m_dStartFrequency; Calc_PercentChange(); Calc_ToFrequency(); }
bool EffectNoiseRemoval::Process() { if (doProfile) { for(int i=0; i<windowSize; i++) { sum[i] = float(0.0); sumsq[i] = float(0.0); profileCount[i] = 0; } } TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; if (t1 > t0) { longSampleCount start = track->TimeToLongSamples(t0); longSampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); if (!ProcessOne(count, track, start, len)) return false; } track = (WaveTrack *) iter.Next(); count++; } if (doProfile) { for(int i=0; i<=windowSize/2; i++) { //float stddev = sqrt(sumsq[i] - (sum[i]*sum[i])/profileCount[i]) // / profileCount[i]; noiseGate[i] = sum[i] / profileCount[i]; // average } hasProfile = true; } return true; }
bool EffectNoiseRemoval::Process() { Initialize(); // This same code will both remove noise and profile it, // depending on 'mDoProfile' this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; if (t1 > t0) { sampleCount start = track->TimeToLongSamples(t0); sampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); if (!ProcessOne(count, track, start, len)) { Cleanup(); bGoodResult = false; break; } } track = (WaveTrack *) iter.Next(); count++; } if (bGoodResult && mDoProfile) { mHasProfile = true; mDoProfile = false; } if (bGoodResult) Cleanup(); this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }
bool EffectChangeSpeed::Process() { // Similar to EffectSoundTouch::Process() //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); mCurTrackNum = 0; m_maxNewLength = 0.0; double curT0; double curT1; while (track) { //Get start and end times from track double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: curT0 = mT0 < trackStart? trackStart: mT0; curT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (curT1 > curT0) { //Transform the marker timepoints to samples longSampleCount start = track->TimeToLongSamples(curT0); longSampleCount end = track->TimeToLongSamples(curT1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(track, start, end)) return false; } //Iterate to the next track track = (WaveTrack *) iter.Next(); mCurTrackNum++; } mT1 = mT0 + m_maxNewLength; // Update selection. return true; }
bool EffectReverse::Process() { //Track::All is needed because Reverse should move the labels too this->CopyInputTracks(Track::All); // Set up mOutputTracks. bool bGoodResult = true; TrackListIterator iter(mOutputTracks); Track *t = iter.First(); int count = 0; while (t) { if (t->GetKind() == Track::Wave && (t->GetSelected() || t->IsSyncLockSelected())) { WaveTrack *track = (WaveTrack*)t; if (mT1 > mT0) { sampleCount start = track->TimeToLongSamples(mT0); sampleCount end = track->TimeToLongSamples(mT1); sampleCount len = (sampleCount)(end - start); if (!ProcessOneWave(count, track, start, len)) { bGoodResult = false; break; } } } else if (t->GetKind() == Track::Label && (t->GetSelected() || t->IsSyncLockSelected())) { LabelTrack *track = (LabelTrack*)t; track->ChangeLabelsOnReverse(mT0, mT1); } t = iter.Next(); count++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }
// DeduceFrequencies is Dominic's extremely cool trick (Vaughan sez so!) // to set deduce m_FromFrequency from the samples at the beginning of // the selection. Then we set some other params accordingly. void EffectChangePitch::DeduceFrequencies() { // As a neat trick, attempt to get the frequency of the note at the // beginning of the selection. SelectedTrackListOfKindIterator iter(Track::Wave, mTracks); WaveTrack *track = (WaveTrack *) iter.First(); if (track) { const int windowSize = 1024; const int analyzeSize = 8192; const int numWindows = analyzeSize / windowSize; double trackStart = track->GetStartTime(); double t0 = mT0 < trackStart? trackStart: mT0; sampleCount start = track->TimeToLongSamples(t0); double rate = track->GetRate(); float buffer[analyzeSize]; float freq[windowSize/2]; float freqa[windowSize/2]; int i, j, argmax; int lag; for(j=0; j<windowSize/2; j++) freqa[j] = 0; track->Get((samplePtr) buffer, floatSample, start, analyzeSize); for(i=0; i<numWindows; i++) { ComputeSpectrum(buffer+i*windowSize, windowSize, windowSize, rate, freq, true); for(j=0; j<windowSize/2; j++) freqa[j] += freq[j]; } argmax=0; for(j=1; j<windowSize/2; j++) if (freqa[j] > freqa[argmax]) argmax = j; lag = (windowSize/2 - 1) - argmax; m_FromFrequency = rate / lag; m_ToFrequency = (m_FromFrequency * (100.0 + m_PercentChange)) / 100.0; // Now we can set the pitch control values. m_FromPitchIndex = PitchIndex(FreqToMIDInoteNumber(m_FromFrequency)); m_bWantPitchDown = (m_ToFrequency < m_FromFrequency); m_ToPitchIndex = PitchIndex(FreqToMIDInoteNumber(m_ToFrequency)); } }
bool EffectRepair::Process() { //v This may be too much copying for EffectRepair. To support Cancel, may be able to copy much less. // But for now, Cancel isn't supported without this. this->CopyInputTracks(); // Set up mOutputTracks. //v This may be too much copying for EffectRepair. bool bGoodResult = true; SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double repair_t0 = mT0; double repair_t1 = mT1; repair_t0 = (repair_t0 < trackStart? trackStart: repair_t0); repair_t1 = (repair_t1 > trackEnd? trackEnd: repair_t1); if (repair_t0 < repair_t1) { // selection is within track audio double rate = track->GetRate(); double repair_deltat = repair_t1 - repair_t0; double spacing = repair_deltat * 2; if (spacing < 128. / rate) spacing = 128. / rate; double t0 = repair_t0 - spacing; double t1 = repair_t1 + spacing; t0 = t0 < trackStart? trackStart: t0; t1 = t1 > trackEnd? trackEnd: t1; repair_t0 = (repair_t0 < t0? t0: repair_t0); repair_t1 = (repair_t1 > t1? t1: repair_t1); sampleCount s0 = track->TimeToLongSamples(t0); sampleCount repair0 = track->TimeToLongSamples(repair_t0); sampleCount repair1 = track->TimeToLongSamples(repair_t1); sampleCount s1 = track->TimeToLongSamples(t1); sampleCount repairStart = (sampleCount)(repair0 - s0); sampleCount repairLen = (sampleCount)(repair1 - repair0); sampleCount len = (sampleCount)(s1 - s0); if (repairLen > 128) { ::wxMessageBox(_("The Repair effect is intended to be used on very short sections of damaged audio (up to 128 samples).\n\nZoom in and select a tiny fraction of a second to repair.")); bGoodResult = false; break; } if (s0 == repair0 && s1 == repair1) { ::wxMessageBox(_("Repair works by using audio data outside the selection region.\n\nPlease select a region that has audio touching at least one side of it.\n\nThe more surrounding audio, the better it performs.")); /// The Repair effect needs some data to go on.\n\nPlease select an area to repair with some audio on at least one side (the more the better).")); bGoodResult = false; break; } if (!ProcessOne(count, track, s0, len, repairStart, repairLen)) { bGoodResult = false; break; } } track = (WaveTrack *) iter.Next(); count++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }
bool EffectChangeSpeed::Process() { // Similar to EffectSoundTouch::Process() // Iterate over each track. // Track::All is needed because this effect needs to introduce // silence in the sync-lock group tracks to keep sync this->CopyInputTracks(Track::All); // Set up mOutputTracks. bool bGoodResult = true; TrackListIterator iter(mOutputTracks); Track* t; mCurTrackNum = 0; mMaxNewLength = 0.0; mFactor = 100.0 / (100.0 + mPercentChange); t = iter.First(); while (t != NULL) { if (t->GetKind() == Track::Label) { if (t->GetSelected() || t->IsSyncLockSelected()) { if (!ProcessLabelTrack(t)) { bGoodResult = false; break; } } } else if (t->GetKind() == Track::Wave && t->GetSelected()) { WaveTrack *pOutWaveTrack = (WaveTrack*)t; //Get start and end times from track mCurT0 = pOutWaveTrack->GetStartTime(); mCurT1 = pOutWaveTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = wxMax(mT0, mCurT0); mCurT1 = wxMin(mT1, mCurT1); // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples sampleCount start = pOutWaveTrack->TimeToLongSamples(mCurT0); sampleCount end = pOutWaveTrack->TimeToLongSamples(mCurT1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(pOutWaveTrack, start, end)) { bGoodResult = false; break; } } mCurTrackNum++; } else if (t->IsSyncLockSelected()) { t->SyncLockAdjust(mT1, mT0 + (mT1 - mT0) * mFactor); } //Iterate to the next track t=iter.Next(); } if (bGoodResult) ReplaceProcessedTracks(bGoodResult); mT1 = mT0 + mMaxNewLength; // Update selection. return bGoodResult; }
bool EffectNoiseRemoval::Process() { // If we are creating a profile, we don't care whether we have // one already. We just prepare the counters. if (mDoProfile) { for(int i=0; i<windowSize; i++) { sum[i] = float(0.0); sumsq[i] = float(0.0); profileCount[i] = 0; } } else { // We need a profile. if( !mHasProfile ) { CleanSpeechMayReadNoisegate(); } // If we still don't have a profile we have a problem. if( !mHasProfile) { wxMessageBox( _("Attempt to run Noise Removal without a noise profile\n.") ); return false; } } // This same code will both remove noise and // profile it, depending on 'mDoProfile' TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); double t0 = mT0 < trackStart? trackStart: mT0; double t1 = mT1 > trackEnd? trackEnd: mT1; if (t1 > t0) { longSampleCount start = track->TimeToLongSamples(t0); longSampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); if (!ProcessOne(count, track, start, len)){ return false; } } track = (WaveTrack *) iter.Next(); count++; } if (mDoProfile) { for(int i=0; i<=windowSize/2; i++) { //float stddev = sqrt(sumsq[i] - (sum[i]*sum[i])/profileCount[i]) // / profileCount[i]; mNoiseGate[i] = sum[i] / profileCount[i]; // average } CleanSpeechMayWriteNoiseGate(); mHasProfile = true; mDoProfile = false; } return true; }
bool EffectTruncSilence::Process() { TrackListIterator iter(mWaveTracks); WaveTrack *t; double t0 = mT0; double t1 = mT1; int tndx; int tcount = 0; // 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(); } // Transform the marker timepoints to samples t = (WaveTrack *) iter.First(); longSampleCount start = t->TimeToLongSamples(t0); longSampleCount end = t->TimeToLongSamples(t1); // Bigger buffers reduce 'reset' blockLen *= 8; // Allocate buffers float **buffer = new float*[tcount]; for (tndx = 0; tndx < tcount; tndx++) { buffer[tndx] = new float[blockLen]; } // 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 truncLongestAllowedSilentSamples = int((wxMax( mTruncLongestAllowedSilentMs, minTruncMs) * rate) / 1000.0); // Figure out number of frames for ramping int quarterSecondFrames = int((rate * QUARTER_SECOND_MS) / 1000.0); int rampInFrames = (truncLongestAllowedSilentSamples / 4); if (rampInFrames > quarterSecondFrames) { rampInFrames = quarterSecondFrames; } // Start processing this->CopyInputWaveTracks(); // Set up mOutputWaveTracks. TrackListIterator iterOut(mOutputWaveTracks); longSampleCount index = start; longSampleCount outTrackOffset = start; bool cancelled = false; while (index < end) { // Limit size of current block if we've reached the end sampleCount limit = blockLen; if ((index + blockLen) > end) { limit = end - index; } // Fill the buffers tndx = 0; t = (WaveTrack *) iter.First(); while (t) { t->Get((samplePtr)buffer[tndx++], floatSample, index, blockLen); t = (WaveTrack *) iter.Next(); } // Reset bool ignoringFrames = false; sampleCount consecutiveSilentFrames = 0; sampleCount truncIndex = 0; // Look for silences in current block for (sampleCount i = 0; 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; } } // 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 > truncLongestAllowedSilentSamples) { ignoringFrames = true; continue; } } else { if (ignoringFrames == true) { sampleCount curOffset = i - rampInFrames; truncIndex -= rampInFrames; // backup into ignored frames for (tndx = 0; tndx < tcount; tndx++) { sampleCount trunci = truncIndex; for (int fr = 0; fr < rampInFrames; fr++) { buffer[tndx][trunci++] = buffer[tndx][curOffset + fr]; } if(((trunci - rampInFrames) - mBlendFrameCount) >= 0) { BlendFrames(buffer[tndx], mBlendFrameCount, ((trunci - rampInFrames) - mBlendFrameCount), ((i - rampInFrames) - mBlendFrameCount)); } } truncIndex += rampInFrames; } consecutiveSilentFrames = 0; ignoringFrames = 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 if (truncIndex < 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(); } } // Maintain output index outTrackOffset += truncIndex; // Update progress and bail if user cancelled cancelled = TrackProgress(0, ((double)index / (double)end)); if (cancelled) { break; } // Bump to next block index += limit; } // Remove stale data at end of output tracks. if (!cancelled && (outTrackOffset < end)) { t = (WaveTrack *) iterOut.First(); while (t) { t->Clear(outTrackOffset / rate, t1); 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->ReplaceProcessedWaveTracks(!cancelled); return !cancelled; }
bool EffectSBSMS::Process() { bool bGoodResult = true; //Iterate over each track //Track::All is needed because this effect needs to introduce silence in the group tracks to keep sync this->CopyInputTracks(Track::All); // Set up mOutputTracks. TrackListIterator iter(mOutputTracks); Track* t; mCurTrackNum = 0; double maxDuration = 0.0; // Must sync if selection length will change bool mustSync = (rateStart != rateEnd); Slide rateSlide(rateSlideType,rateStart,rateEnd); Slide pitchSlide(pitchSlideType,pitchStart,pitchEnd); mTotalStretch = rateSlide.getTotalStretch(); t = iter.First(); while (t != NULL) { if (t->GetKind() == Track::Label && (t->GetSelected() || (mustSync && t->IsSyncLockSelected())) ) { if (!ProcessLabelTrack(t)) { bGoodResult = false; break; } } else if (t->GetKind() == Track::Wave && t->GetSelected() ) { WaveTrack* leftTrack = (WaveTrack*)t; //Get start and end times from track mCurT0 = leftTrack->GetStartTime(); mCurT1 = leftTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less mCurT0 = wxMax(mT0, mCurT0); mCurT1 = wxMin(mT1, mCurT1); // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { sampleCount start; sampleCount end; start = leftTrack->TimeToLongSamples(mCurT0); end = leftTrack->TimeToLongSamples(mCurT1); WaveTrack* rightTrack = NULL; if (leftTrack->GetLinked()) { double t; rightTrack = (WaveTrack*)(iter.Next()); //Adjust bounds by the right tracks markers t = rightTrack->GetStartTime(); t = wxMax(mT0, t); mCurT0 = wxMin(mCurT0, t); t = rightTrack->GetEndTime(); t = wxMin(mT1, t); mCurT1 = wxMax(mCurT1, t); //Transform the marker timepoints to samples start = leftTrack->TimeToLongSamples(mCurT0); end = leftTrack->TimeToLongSamples(mCurT1); mCurTrackNum++; // Increment for rightTrack, too. } sampleCount trackStart = leftTrack->TimeToLongSamples(leftTrack->GetStartTime()); sampleCount trackEnd = leftTrack->TimeToLongSamples(leftTrack->GetEndTime()); // SBSMS has a fixed sample rate - we just convert to its sample rate and then convert back float srTrack = leftTrack->GetRate(); float srProcess = bLinkRatePitch?srTrack:44100.0; // the resampler needs a callback to supply its samples ResampleBuf rb; sampleCount maxBlockSize = leftTrack->GetMaxBlockSize(); rb.blockSize = maxBlockSize; rb.buf = (audio*)calloc(rb.blockSize,sizeof(audio)); rb.leftTrack = leftTrack; rb.rightTrack = rightTrack?rightTrack:leftTrack; rb.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float)); rb.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float)); // Samples in selection sampleCount samplesIn = end-start; // Samples for SBSMS to process after resampling sampleCount samplesToProcess = (sampleCount) ((float)samplesIn*(srProcess/srTrack)); SlideType outSlideType; SBSMSResampleCB outResampleCB; sampleCount processPresamples = 0; sampleCount trackPresamples = 0; if(bLinkRatePitch) { rb.bPitch = true; outSlideType = rateSlideType; outResampleCB = resampleCB; rb.offset = start; rb.end = end; rb.iface = new SBSMSInterfaceSliding(&rateSlide,&pitchSlide, bPitchReferenceInput, samplesToProcess,0, NULL); } else { rb.bPitch = false; outSlideType = (srProcess==srTrack?SlideIdentity:SlideConstant); outResampleCB = postResampleCB; rb.ratio = srProcess/srTrack; rb.quality = new SBSMSQuality(&SBSMSQualityStandard); rb.resampler = new Resampler(resampleCB, &rb, srProcess==srTrack?SlideIdentity:SlideConstant); rb.sbsms = new SBSMS(rightTrack?2:1,rb.quality,true); rb.SBSMSBlockSize = rb.sbsms->getInputFrameSize(); rb.SBSMSBuf = (audio*)calloc(rb.SBSMSBlockSize,sizeof(audio)); processPresamples = wxMin(rb.quality->getMaxPresamples(), (long)((float)(start-trackStart)*(srProcess/srTrack))); trackPresamples = wxMin(start-trackStart, (long)((float)(processPresamples)*(srTrack/srProcess))); rb.offset = start - trackPresamples; rb.end = trackEnd; rb.iface = new SBSMSEffectInterface(rb.resampler, &rateSlide,&pitchSlide, bPitchReferenceInput, samplesToProcess,processPresamples, rb.quality); } Resampler resampler(outResampleCB,&rb,outSlideType); audio outBuf[SBSMSOutBlockSize]; float outBufLeft[2*SBSMSOutBlockSize]; float outBufRight[2*SBSMSOutBlockSize]; // Samples in output after SBSMS sampleCount samplesToOutput = rb.iface->getSamplesToOutput(); // Samples in output after resampling back sampleCount samplesOut = (sampleCount) ((float)samplesToOutput * (srTrack/srProcess)); // Duration in track time double duration = (mCurT1-mCurT0) * mTotalStretch; if(duration > maxDuration) maxDuration = duration; TimeWarper *warper = createTimeWarper(mCurT0,mCurT1,maxDuration,rateStart,rateEnd,rateSlideType); SetTimeWarper(warper); rb.outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat(), leftTrack->GetRate()); if(rightTrack) rb.outputRightTrack = mFactory->NewWaveTrack(rightTrack->GetSampleFormat(), rightTrack->GetRate()); long pos = 0; long outputCount = -1; // process while(pos<samplesOut && outputCount) { long frames; if(pos+SBSMSOutBlockSize>samplesOut) { frames = samplesOut - pos; } else { frames = SBSMSOutBlockSize; } outputCount = resampler.read(outBuf,frames); for(int i = 0; i < outputCount; i++) { outBufLeft[i] = outBuf[i][0]; if(rightTrack) outBufRight[i] = outBuf[i][1]; } pos += outputCount; rb.outputLeftTrack->Append((samplePtr)outBufLeft, floatSample, outputCount); if(rightTrack) rb.outputRightTrack->Append((samplePtr)outBufRight, floatSample, outputCount); double frac = (double)pos/(double)samplesOut; int nWhichTrack = mCurTrackNum; if(rightTrack) { nWhichTrack = 2*(mCurTrackNum/2); if (frac < 0.5) frac *= 2.0; // Show twice as far for each track, because we're doing 2 at once. else { nWhichTrack++; frac -= 0.5; frac *= 2.0; // Show twice as far for each track, because we're doing 2 at once. } } if (TrackProgress(nWhichTrack, frac)) return false; } rb.outputLeftTrack->Flush(); if(rightTrack) rb.outputRightTrack->Flush(); bool bResult = leftTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputLeftTrack, true, false, GetTimeWarper()); wxASSERT(bResult); // TO DO: Actually handle this. wxUnusedVar(bResult); if(rightTrack) { bResult = rightTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputRightTrack, true, false, GetTimeWarper()); wxASSERT(bResult); // TO DO: Actually handle this. } } mCurTrackNum++; } else if (mustSync && t->IsSyncLockSelected()) { t->SyncLockAdjust(mCurT1, mCurT0 + (mCurT1 - mCurT0) * mTotalStretch); } //Iterate to the next track t = iter.Next(); } if (bGoodResult) ReplaceProcessedTracks(bGoodResult); // Update selection mT0 = mCurT0; mT1 = mCurT0 + maxDuration; return bGoodResult; }
bool ContrastDialog::GetDB(float &dB) { float rms = float(0.0); int numberSelecteTracks = 0; // For stereo tracks: sqrt((mean(L)+mean(R))/2) bool isStereo = false; double meanSq = 0.0; AudacityProject *p = GetActiveProject(); SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); WaveTrack *t = (WaveTrack *) iter.First(); while (t) { numberSelecteTracks++; if (numberSelecteTracks > 1 && !isStereo) { AudacityMessageDialog m(NULL, _("You can only measure one track at a time."), _("Error"), wxOK); m.ShowModal(); return false; } isStereo = t->GetLinked(); wxASSERT(mT0 <= mT1); // Ignore whitespace beyond ends of track. if(mT0 < t->GetStartTime()) mT0 = t->GetStartTime(); if(mT1 > t->GetEndTime()) mT1 = t->GetEndTime(); auto SelT0 = t->TimeToLongSamples(mT0); auto SelT1 = t->TimeToLongSamples(mT1); if(SelT0 > SelT1) { AudacityMessageDialog m(NULL, _("Invalid audio selection.\nPlease ensure that audio is selected."), _("Error"), wxOK); m.ShowModal(); return false; } if(SelT0 == SelT1) { AudacityMessageDialog m(NULL, _("Nothing to measure.\nPlease select a section of a track."), _("Error"), wxOK); m.ShowModal(); return false; } // Don't throw in this analysis dialog rms = ((WaveTrack *)t)->GetRMS(mT0, mT1, false); meanSq += rms * rms; t = (WaveTrack *) iter.Next(); } // TODO: This works for stereo, provided the audio clips are in both channels. // We should really count gaps between clips as silence. rms = (meanSq > 0.0)? sqrt(meanSq/(double)numberSelecteTracks) : 0.0; if(numberSelecteTracks == 0) { AudacityMessageDialog m(NULL, _("Please select an audio track."), _("Error"), wxOK); m.ShowModal(); return false; } // Gives warning C4056, Overflow in floating-point constant arithmetic // -INFINITY is intentional here. // Looks like we are stuck with this warning, as // #pragma warning( disable : 4056) // even around the whole function does not disable it successfully. dB = (rms == 0.0)? -INFINITY : LINEAR_TO_DB(rms); return true; }
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; }
// Deduce m_FromFrequency from the samples at the beginning of // the selection. Then set some other params accordingly. void EffectChangePitch::DeduceFrequencies() { // As a neat trick, attempt to get the frequency of the note at the // beginning of the selection. SelectedTrackListOfKindIterator iter(Track::Wave, mTracks); WaveTrack *track = (WaveTrack *) iter.First(); if (track) { double rate = track->GetRate(); // Auto-size window -- high sample rates require larger windowSize. // Aim for around 2048 samples at 44.1 kHz (good down to about 100 Hz). // To detect single notes, analysis period should be about 0.2 seconds. // windowSize must be a power of 2. int windowSize = wxRound(pow(2.0, floor((log(rate / 20.0)/log(2.0)) + 0.5))); // windowSize < 256 too inaccurate windowSize = (windowSize > 256)? windowSize : 256; // we want about 0.2 seconds to catch the first note. // number of windows rounded to nearest integer >= 1. int numWindows = wxRound((double)(rate / (5.0f * windowSize))); numWindows = (numWindows > 0)? numWindows : 1; double trackStart = track->GetStartTime(); double t0 = mT0 < trackStart? trackStart: mT0; sampleCount start = track->TimeToLongSamples(t0); int analyzeSize = windowSize * numWindows; float * buffer; buffer = new float[analyzeSize]; float * freq; freq = new float[windowSize/2]; float * freqa; freqa = new float[windowSize/2]; int i, j, argmax; int lag; for(j=0; j<windowSize/2; j++) freqa[j] = 0; track->Get((samplePtr) buffer, floatSample, start, analyzeSize); for(i=0; i<numWindows; i++) { ComputeSpectrum(buffer+i*windowSize, windowSize, windowSize, rate, freq, true); for(j=0; j<windowSize/2; j++) freqa[j] += freq[j]; } argmax=0; for(j=1; j<windowSize/2; j++) if (freqa[j] > freqa[argmax]) argmax = j; delete [] freq; delete [] freqa; delete [] buffer; lag = (windowSize/2 - 1) - argmax; m_dStartFrequency = rate / lag; } double dFromMIDInote = FreqToMIDInote(m_dStartFrequency); double dToMIDInote = dFromMIDInote + m_dSemitonesChange; m_nFromPitch = PitchIndex(dFromMIDInote); m_nFromOctave = PitchOctave(dFromMIDInote); m_nToPitch = PitchIndex(dToMIDInote); m_nToOctave = PitchOctave(dToMIDInote); m_FromFrequency = m_dStartFrequency; Calc_PercentChange(); Calc_ToFrequency(); }
bool ContrastDialog::GetDB(float &dB) { float rms = float(0.0); int numberSelecteTracks = 0; // For stereo tracks: sqrt((mean(L)+mean(R))/2) bool isStereo = false; double meanSq = 0.0; AudacityProject *p = GetActiveProject(); SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); WaveTrack *t = (WaveTrack *) iter.First(); while (t) { numberSelecteTracks++; if (numberSelecteTracks > 1 && !isStereo) { wxMessageDialog m(NULL, _("You can only measure one track at a time."), _("Error"), wxOK); m.ShowModal(); return false; } isStereo = t->GetLinked(); wxASSERT(mT0 <= mT1); // Ignore whitespace beyond ends of track. if(mT0 < t->GetStartTime()) mT0 = t->GetStartTime(); if(mT1 > t->GetEndTime()) mT1 = t->GetEndTime(); sampleCount SelT0 = t->TimeToLongSamples(mT0); sampleCount SelT1 = t->TimeToLongSamples(mT1); if(SelT0 > SelT1) { wxMessageDialog m(NULL, _("Invalid audio selection.\nPlease ensure that audio is selected."), _("Error"), wxOK); m.ShowModal(); return false; } if(SelT0 == SelT1) { wxMessageDialog m(NULL, _("Nothing to measure.\nPlease select a section of a track."), _("Error"), wxOK); m.ShowModal(); return false; } ((WaveTrack *)t)->GetRMS(&rms, mT0, mT1); meanSq += rms * rms; t = (WaveTrack *) iter.Next(); } // TODO: This works for stereo, provided the audio clips are in both channels. // We should really count gaps between clips as silence. rms = (meanSq > 0.0)? sqrt(meanSq/(double)numberSelecteTracks) : 0.0; if(numberSelecteTracks == 0) { wxMessageDialog m(NULL, _("Please select an audio track."), _("Error"), wxOK); m.ShowModal(); return false; } dB = (rms == 0.0)? -INFINITY : LINEAR_TO_DB(rms); return true; }
bool EffectChangeSpeed::Process() { // Similar to EffectSoundTouch::Process() //Iterate over each track this->CopyInputWaveTracks(); // Set up mOutputWaveTracks. bool bGoodResult = true; TrackListIterator iter(mOutputWaveTracks); WaveTrack* pOutWaveTrack = (WaveTrack*)(iter.First()); mCurTrackNum = 0; m_maxNewLength = 0.0; //Get start and end times from track mCurT0 = pOutWaveTrack->GetStartTime(); mCurT1 = pOutWaveTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = wxMax(mT0, mCurT0); mCurT1 = wxMin(mT1, mCurT1); double len = pOutWaveTrack->GetEndTime() - pOutWaveTrack->GetStartTime(); while (pOutWaveTrack != NULL) { //Get start and end times from track mCurT0 = pOutWaveTrack->GetStartTime(); mCurT1 = pOutWaveTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = wxMax(mT0, mCurT0); mCurT1 = wxMin(mT1, mCurT1); // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples sampleCount start = pOutWaveTrack->TimeToLongSamples(mCurT0); sampleCount end = pOutWaveTrack->TimeToLongSamples(mCurT1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(pOutWaveTrack, start, end)) { bGoodResult = false; break; } } //Iterate to the next track pOutWaveTrack = (WaveTrack*)(iter.Next()); mCurTrackNum++; } this->ReplaceProcessedWaveTracks(bGoodResult); #ifdef EXPERIMENTAL_FULL_LINKING AudacityProject *p = (AudacityProject*)mParent; if( p && p->IsSticky() ){ pOutWaveTrack = (WaveTrack*)(iter.First()); double newLen = pOutWaveTrack->GetEndTime() - pOutWaveTrack->GetStartTime(); double timeAdded = newLen-len; double sel = mCurT1-mCurT0; double percent = (sel/(timeAdded+sel))*100 - 100; if ( !(HandleGroupChangeSpeed(percent, mCurT0, mCurT1)) ) bGoodResult = false; } #endif // mT1 = mT0 + m_maxNewLength; // Update selection. return bGoodResult; }