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 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; }
void Effect::Preview() { wxWindow* FocusDialog = wxWindow::FindFocus(); if (gAudioIO->IsBusy()) return; // Mix a few seconds of audio from all of the tracks double previewLen = 6.0; gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLen); WaveTrack *mixLeft = NULL; WaveTrack *mixRight = NULL; double rate = mProjectRate; double t0 = mT0; double t1 = t0 + previewLen; if (t1 > mT1) t1 = mT1; if (t1 <= t0) return; bool success = ::MixAndRender(mTracks, mFactory, rate, floatSample, t0, t1, &mixLeft, &mixRight); if (!success) { return; } // Save the original track list TrackList *saveTracks = mTracks; // Build new tracklist from rendering tracks mTracks = new TrackList(); mixLeft->SetSelected(true); mTracks->Add(mixLeft); if (mixRight) { mixRight->SetSelected(true); mTracks->Add(mixRight); } // Update track/group counts CountWaveTracks(); // Reset times t0 = mixLeft->GetStartTime(); t1 = mixLeft->GetEndTime(); double t0save = mT0; double t1save = mT1; mT0 = t0; mT1 = t1; // Apply effect // Effect is already inited; we call Process, End, and then Init // again, so the state is exactly the way it was before Preview // was called. mProgress = new ProgressDialog(StripAmpersand(GetEffectName()), _("Preparing preview"), pdlgHideCancelButton); // Have only "Stop" button. bool bSuccess = Process(); delete mProgress; End(); Init(); if (bSuccess) { mT0 = t0save; mT1 = t1save; WaveTrackArray playbackTracks; WaveTrackArray recordingTracks; // Probably not the same tracks post-processing, so can't rely on previous values of mixLeft & mixRight. TrackListOfKindIterator iter(Track::Wave, mTracks); mixLeft = (WaveTrack*)(iter.First()); mixRight = (WaveTrack*)(iter.Next()); playbackTracks.Add(mixLeft); if (mixRight) playbackTracks.Add(mixRight); #ifdef EXPERIMENTAL_MIDI_OUT NoteTrackArray empty; #endif // Start audio playing int token = gAudioIO->StartStream(playbackTracks, recordingTracks, #ifdef EXPERIMENTAL_MIDI_OUT empty, #endif NULL, rate, t0, t1, NULL); if (token) { int previewing = eProgressSuccess; mProgress = new ProgressDialog(StripAmpersand(GetEffectName()), _("Previewing"), pdlgHideCancelButton); while (gAudioIO->IsStreamActive(token) && previewing == eProgressSuccess) { ::wxMilliSleep(100); previewing = mProgress->Update(gAudioIO->GetStreamTime() - t0, t1 - t0); } gAudioIO->StopStream(); while (gAudioIO->IsBusy()) { ::wxMilliSleep(100); } delete mProgress; } else { wxMessageBox(_("Error while opening sound device. Please check the output device settings and the project sample rate."), _("Error"), wxOK | wxICON_EXCLAMATION, FocusDialog); } } if (FocusDialog) { FocusDialog->SetFocus(); } delete mOutputTracks; mOutputTracks = NULL; mTracks->Clear(true); // true => delete the tracks delete mTracks; mTracks = saveTracks; }
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; }
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; }
// 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 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.get()); WaveTrack *track = (WaveTrack *) iter.First(); int count = 0; while (track) { const double trackStart = track->GetStartTime(); const double repair_t0 = std::max(mT0, trackStart); const double trackEnd = track->GetEndTime(); const double repair_t1 = std::min(mT1, trackEnd); const double repair_deltat = repair_t1 - repair_t0; if (repair_deltat > 0) { // selection is within track audio const auto repair0 = track->TimeToLongSamples(repair_t0); const auto repair1 = track->TimeToLongSamples(repair_t1); const auto repairLen = repair1 - repair0; if (repairLen > 128) { ::Effect::MessageBox(_("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; } const double rate = track->GetRate(); const double spacing = std::max(repair_deltat * 2, 128. / rate); const double t0 = std::max(repair_t0 - spacing, trackStart); const double t1 = std::min(repair_t1 + spacing, trackEnd); const auto s0 = track->TimeToLongSamples(t0); const auto s1 = track->TimeToLongSamples(t1); // The difference is at most 2 * 128: const auto repairStart = (repair0 - s0).as_size_t(); const auto len = s1 - s0; if (s0 == repair0 && s1 == repair1) { ::Effect::MessageBox(_("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 is at most 5 * 128. len.as_size_t(), repairStart, // repairLen is at most 128. repairLen.as_size_t() )) { bGoodResult = false; break; } } track = (WaveTrack *) iter.Next(); count++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }
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 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 EffectSoundTouch::Process() { // Assumes that mSoundTouch has already been initialized // by the subclass for subclass-specific parameters. The // time warper should also be set. // Check if this effect will alter the selection length; if so, we need // to operate on sync-lock selected tracks. bool mustSync = true; if (mT1 == GetTimeWarper()->Warp(mT1)) { mustSync = false; } //Iterate over each track // Needs Track::All for sync-lock grouping. this->CopyInputTracks(Track::All); bool bGoodResult = true; TrackListIterator iter(mOutputTracks); Track* t; mCurTrackNum = 0; m_maxNewLength = 0.0; t = iter.First(); while (t != NULL) { if (t->GetKind() == Track::Label && (t->GetSelected() || (mustSync && t->IsSyncLockSelected())) ) { if (!ProcessLabelTrack(t)) { bGoodResult = false; break; } } #ifdef USE_MIDI else if (t->GetKind() == Track::Note && (t->GetSelected() || (mustSync && t->IsSyncLockSelected()))) { if (!ProcessNoteTrack(t)) { bGoodResult = false; break; } } #endif 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, end; if (leftTrack->GetLinked()) { double t; WaveTrack* 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); //Inform soundtouch there's 2 channels mSoundTouch->setChannels(2); //ProcessStereo() (implemented below) processes a stereo track if (!ProcessStereo(leftTrack, rightTrack, start, end)) { bGoodResult = false; break; } mCurTrackNum++; // Increment for rightTrack, too. } else { //Transform the marker timepoints to samples start = leftTrack->TimeToLongSamples(mCurT0); end = leftTrack->TimeToLongSamples(mCurT1); //Inform soundtouch there's a single channel mSoundTouch->setChannels(1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(leftTrack, start, end)) { bGoodResult = false; break; } } } mCurTrackNum++; } else if (mustSync && t->IsSyncLockSelected()) { t->SyncLockAdjust(mT1, GetTimeWarper()->Warp(mT1)); } //Iterate to the next track t = iter.Next(); } if (bGoodResult) ReplaceProcessedTracks(bGoodResult); delete mSoundTouch; mSoundTouch = NULL; // mT0 = mCurT0; // mT1 = mCurT0 + m_maxNewLength; // Update selection. return bGoodResult; }
bool EffectSBSMS::Process() { if(!bInit) { sbsms_init(4096); bInit = TRUE; } 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; if(rateStart == rateEnd) mTotalStretch = 1.0/rateStart; else mTotalStretch = 1.0/(rateEnd-rateStart)*log(rateEnd/rateStart); // Must sync if selection length will change bool mustSync = (mTotalStretch != 1.0); t = iter.First(); while (t != NULL) { if (t->GetKind() == Track::Label && (t->GetSelected() || (mustSync && t->IsSynchroSelected())) ) { 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 trackEnd = leftTrack->TimeToLongSamples(leftTrack->GetEndTime()); // SBSMS has a fixed sample rate - we just convert to its sample rate and then convert back float srIn = leftTrack->GetRate(); float srSBSMS = 44100.0; // the resampler needs a callback to supply its samples resampleBuf rb; sampleCount maxBlockSize = leftTrack->GetMaxBlockSize(); rb.block = maxBlockSize; rb.buf = (audio*)calloc(rb.block,sizeof(audio)); rb.leftTrack = leftTrack; rb.rightTrack = rightTrack?rightTrack:leftTrack; rb.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float)); rb.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float)); rb.offset = start; rb.end = trackEnd; rb.ratio = srSBSMS/srIn; rb.resampler = new Resampler(resampleCB, &rb); // Samples in selection sampleCount samplesIn = end-start; // Samples for SBSMS to process after resampling sampleCount samplesToProcess = (sampleCount) ((real)samplesIn*(srSBSMS/srIn)); // Samples in output after resampling back sampleCount samplesToGenerate = (sampleCount) ((real)samplesToProcess * mTotalStretch); sampleCount samplesOut = (sampleCount) ((real)samplesIn * mTotalStretch); double duration = (mCurT1-mCurT0) * mTotalStretch; if(duration > maxDuration) maxDuration = duration; TimeWarper *warper = NULL; if (rateStart == rateEnd) { warper = new LinearTimeWarper(mCurT0, mCurT0, mCurT1, mCurT0+maxDuration); } else { warper = new LogarithmicTimeWarper(mCurT0, mCurT1, rateStart, rateEnd); } SetTimeWarper(warper); sbsmsInfo si; si.rs = rb.resampler; si.samplesToProcess = samplesToProcess; si.samplesToGenerate = samplesToGenerate; si.stretch0 = rateStart; si.stretch1 = rateEnd; si.ratio0 = pitchStart; si.ratio1 = pitchEnd; rb.sbsmser = sbsms_create(&samplesCB,&stretchCB,&ratioCB,rightTrack?2:1,quality,bPreAnalyze,true); rb.pitch = pitch_create(rb.sbsmser,&si,srIn/srSBSMS); rb.outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat(), leftTrack->GetRate()); if(rightTrack) rb.outputRightTrack = mFactory->NewWaveTrack(rightTrack->GetSampleFormat(), rightTrack->GetRate()); sampleCount blockSize = SBSMS_FRAME_SIZE[quality]; rb.outBuf = (audio*)calloc(blockSize,sizeof(audio)); rb.outputLeftBuffer = (float*)calloc(blockSize*2,sizeof(float)); if(rightTrack) rb.outputRightBuffer = (float*)calloc(blockSize*2,sizeof(float)); long pos = 0; long outputCount = -1; // pre analysis real fracPre = 0.0f; if(bPreAnalyze) { fracPre = 0.05f; resampleBuf rbPre; rbPre.block = maxBlockSize; rbPre.buf = (audio*)calloc(rb.block,sizeof(audio)); rbPre.leftTrack = leftTrack; rbPre.rightTrack = rightTrack?rightTrack:leftTrack; rbPre.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float)); rbPre.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float)); rbPre.offset = start; rbPre.end = end; rbPre.ratio = srSBSMS/srIn; rbPre.resampler = new Resampler(resampleCB, &rbPre); si.rs = rbPre.resampler; long pos = 0; long lastPos = 0; long ret = 0; while(lastPos<samplesToProcess) { ret = sbsms_pre_analyze(&samplesCB,&si,rb.sbsmser); lastPos = pos; pos += ret; real completion = (real)lastPos/(real)samplesToProcess; if (TrackProgress(0,fracPre*completion)) return false; } sbsms_pre_analyze_complete(rb.sbsmser); sbsms_reset(rb.sbsmser); si.rs = rb.resampler; } // process while(pos<samplesOut && outputCount) { long frames; if(pos+blockSize>samplesOut) { frames = samplesOut - pos; } else { frames = blockSize; } outputCount = pitch_process(rb.outBuf, frames, rb.pitch); for(int i = 0; i < outputCount; i++) { rb.outputLeftBuffer[i] = rb.outBuf[i][0]; if(rightTrack) rb.outputRightBuffer[i] = rb.outBuf[i][1]; } pos += outputCount; rb.outputLeftTrack->Append((samplePtr)rb.outputLeftBuffer, floatSample, outputCount); if(rightTrack) rb.outputRightTrack->Append((samplePtr)rb.outputRightBuffer, 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, fracPre + (1.0-fracPre)*frac)) return false; } rb.outputLeftTrack->Flush(); if(rightTrack) rb.outputRightTrack->Flush(); leftTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputLeftTrack, true, false, GetTimeWarper()); if(rightTrack) { rightTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputRightTrack, true, false, GetTimeWarper()); } } mCurTrackNum++; } else if (mustSync && t->IsSynchroSelected()) { t->SyncAdjust(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 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 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 EffectNormalize::Process() { if (mGain == false && mDC == false) return true; float ratio; if( mGain ) ratio = pow(10.0,TrapDouble(mLevel, // same value used for all tracks NORMALIZE_DB_MIN, NORMALIZE_DB_MAX)/20.0); else ratio = 1.0; //Iterate over each track this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); WaveTrack *track = (WaveTrack *) iter.First(); WaveTrack *prevTrack; prevTrack = track; mCurTrackNum = 0; wxString topMsg; if(mDC & mGain) topMsg = _("Removing DC offset and Normalizing...\n"); else if(mDC & !mGain) topMsg = _("Removing DC offset...\n"); else if(!mDC & mGain) topMsg = _("Normalizing without removing DC offset...\n"); else if(!mDC & !mGain) topMsg = wxT("Not doing anything)...\n"); // shouldn't get here 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) { wxString msg; wxString trackName = track->GetName(); if(!track->GetLinked() || mStereoInd) msg = topMsg + _("Analyzing: ") + trackName; else msg = topMsg + _("Analyzing first track of stereo pair: ") + trackName; AnalyseTrack(track, msg); // sets mOffset and offset-adjusted mMin and mMax if(!track->GetLinked() || mStereoInd) { // mono or 'stereo tracks independently' float extent = wxMax(fabs(mMax), fabs(mMin)); if( (extent > 0) && mGain ) mMult = ratio / extent; else mMult = 1.0; msg = topMsg + _("Processing: ") + trackName; if(track->GetLinked() || prevTrack->GetLinked()) // only get here if there is a linked track but we are processing independently msg = topMsg + _("Processing stereo channels independently: ") + trackName; if (!ProcessOne(track, msg)) { bGoodResult = false; break; } } else { // we have a linked stereo track // so we need to find it's min, max and offset // as they are needed to calc the multiplier for both tracks float offset1 = mOffset; // remember ones from first track float min1 = mMin; float max1 = mMax; track = (WaveTrack *) iter.Next(); // get the next one mCurTrackNum++; // keeps progress bar correct msg = topMsg + _("Analyzing second track of stereo pair: ") + trackName; AnalyseTrack(track, msg); // sets mOffset and offset-adjusted mMin and mMax float offset2 = mOffset; // ones for second track float min2 = mMin; float max2 = mMax; float extent = wxMax(fabs(min1), fabs(max1)); extent = wxMax(extent, fabs(min2)); extent = wxMax(extent, fabs(max2)); if( (extent > 0) && mGain ) mMult = ratio / extent; // we need to use this for both linked tracks else mMult = 1.0; mOffset = offset1; track = (WaveTrack *) iter.Prev(); // go back to the first linked one mCurTrackNum--; // keeps progress bar correct msg = topMsg + _("Processing first track of stereo pair: ") + trackName; if (!ProcessOne(track, msg)) { bGoodResult = false; break; } mOffset = offset2; track = (WaveTrack *) iter.Next(); // go to the second linked one mCurTrackNum++; // keeps progress bar correct msg = topMsg + _("Processing second track of stereo pair: ") + trackName; if (!ProcessOne(track, msg)) { bGoodResult = false; break; } } } //Iterate to the next track prevTrack = track; track = (WaveTrack *) iter.Next(); mCurTrackNum++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }
bool EffectNormalize::Process() { bool wasLinked = false; // set when a track has a linked (stereo) track if (mGain == false && mDC == false) return true; //Iterate over each track this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; 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(); if(mStereoInd) // do stereo tracks independently (the easy way) track->GetMinMax(&mMin, &mMax, mCurT0, mCurT1); else { if(!wasLinked) // new mono track or first of a stereo pair { track->GetMinMax(&mMin, &mMax, mCurT0, mCurT1); if(track->GetLinked()) { wasLinked = true; // so we use these values for the next (linked) track track = (WaveTrack *) iter.Next(); // get the next one for the max/min float min, max; track->GetMinMax(&min, &max, mCurT0, mCurT1); mMin = min < mMin ? min : mMin; mMax = max > mMax ? max : mMax; track = (WaveTrack *) iter.Prev(); // back to the one we are on } } else wasLinked = false; // second of the stereo pair, next one is mono or first } //ProcessOne() (implemented below) processes a single track if (!ProcessOne(track, start, end)) { bGoodResult = false; break; } } //Iterate to the next track track = (WaveTrack *) iter.Next(); mCurTrackNum++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }