bool EffectNoise::Process() { if (noiseDuration <= 0.0) noiseDuration = sDefaultGenerateLen; //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *)iter.First(); while (track) { WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); numSamples = (longSampleCount)(noiseDuration * track->GetRate() + 0.5); longSampleCount i = 0; float *data = new float[tmp->GetMaxBlockSize()]; sampleCount block; while(i < numSamples) { block = tmp->GetBestBlockSize(i); if (block > (numSamples - i)) block = numSamples - i; MakeNoise(data, block, track->GetRate(), noiseAmplitude); tmp->Append((samplePtr)data, floatSample, block); i += block; } delete[] data; tmp->Flush(); track->Clear(mT0, mT1); track->Paste(mT0, tmp); delete tmp; //Iterate to the next track track = (WaveTrack *)iter.Next(); } /* save last used values save duration unless value was got from selection, so we save only when user explicitely setup a value */ if (mT1 == mT0) gPrefs->Write(wxT("/CsPresets/NoiseGen_Duration"), noiseDuration); gPrefs->Write(wxT("/CsPresets/NoiseGen_Type"), noiseType); gPrefs->Write(wxT("/CsPresets/NoiseGen_Amp"), noiseAmplitude); mT1 = mT0 + noiseDuration; // Update selection. return true; }
bool EffectToneGen::Process() { if (length <= 0.0) length = sDefaultGenerateLen; //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *)iter.First(); while (track) { mSample = 0; WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat()); mCurRate = track->GetRate(); tmp->SetRate(mCurRate); longSampleCount numSamples = (longSampleCount)(length * mCurRate + 0.5); longSampleCount i = 0; float *data = new float[tmp->GetMaxBlockSize()]; sampleCount block; while(i < numSamples) { block = tmp->GetBestBlockSize(i); if (block > (numSamples - i)) block = numSamples - i; MakeTone(data, block); tmp->Append((samplePtr)data, floatSample, block); i += block; } delete[] data; tmp->Flush(); track->Clear(mT0, mT1); track->Paste(mT0, tmp); delete tmp; //Iterate to the next track track = (WaveTrack *)iter.Next(); } mT1 = mT0 + length; // Update selection. return true; }
// ProcessOne() takes a track, transforms it to bunch of buffer-blocks, // and calls libsamplerate code on these blocks. bool EffectChangeSpeed::ProcessOne(WaveTrack * track, sampleCount start, sampleCount end) { if (track == NULL) return false; // initialization, per examples of Mixer::Mixer and // EffectSoundTouch::ProcessOne WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); //Get the length of the selection (as double). len is //used simple to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); // Initiate processing buffers, most likely shorter than // the length of the selection being processed. sampleCount inBufferSize = track->GetMaxBlockSize(); float * inBuffer = new float[inBufferSize]; sampleCount outBufferSize = (sampleCount)((mFactor * inBufferSize) + 10); float * outBuffer = new float[outBufferSize]; // Set up the resampling stuff for this track. Resample resample(true, mFactor, mFactor); //Go through the track one buffer at a time. samplePos counts which //sample the current buffer starts at. bool bResult = true; sampleCount blockSize; sampleCount samplePos = start; while (samplePos < end) { //Get a blockSize of samples (smaller than the size of the buffer) blockSize = track->GetBestBlockSize(samplePos); //Adjust the block size if it is the final block in the track if (samplePos + blockSize > end) blockSize = end - samplePos; //Get the samples from the track and put them in the buffer track->Get((samplePtr) inBuffer, floatSample, samplePos, blockSize); int inUsed; int outgen = resample.Process(mFactor, inBuffer, blockSize, ((samplePos + blockSize) >= end), &inUsed, outBuffer, outBufferSize); if (outgen < 0) { bResult = false; break; } if (outgen > 0) outputTrack->Append((samplePtr)outBuffer, floatSample, outgen); // Increment samplePos samplePos += inUsed; // Update the Progress meter if (TrackProgress(mCurTrackNum, (samplePos - start) / len)) { bResult = false; break; } } // Flush the output WaveTrack (since it's buffered, too) outputTrack->Flush(); // Clean up the buffers delete [] inBuffer; delete [] outBuffer; // Take the output track and insert it in place of the original // sample data double newLength = outputTrack->GetEndTime(); if (bResult) { SetTimeWarper(new LinearTimeWarper(mCurT0, mCurT0, mCurT1, mCurT0 + newLength)); bResult = track->ClearAndPaste(mCurT0, mCurT1, outputTrack, true, false, GetTimeWarper()); } if (newLength > mMaxNewLength) mMaxNewLength = newLength; // Delete the outputTrack now that its data is inserted in place delete outputTrack; return bResult; }
bool QuickMix(TrackList *tracks, DirManager *dirManager, double rate, sampleFormat format) { WaveTrack **waveArray; VTrack *t; int numWaves = 0; int numLeft = 0; int numRight = 0; int numMono = 0; bool mono = false; int w; TrackListIterator iter(tracks); t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == VTrack::Wave) { numWaves++; switch (t->GetChannel()) { case VTrack::MonoChannel: numLeft++; numRight++; numMono++; break; case VTrack::LeftChannel: numLeft++; break; case VTrack::RightChannel: numRight++; break; } } t = iter.Next(); } if (numMono == numWaves || numLeft == numWaves || numRight == numWaves) mono = true; double totalTime = 0.0; waveArray = new WaveTrack *[numWaves]; w = 0; t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == VTrack::Wave) { waveArray[w++] = (WaveTrack *) t; if (t->GetMaxLen() > totalTime) totalTime = t->GetMaxLen(); } t = iter.Next(); } WaveTrack *mixLeft = new WaveTrack(dirManager); mixLeft->SetSampleFormat(format); mixLeft->SetRate(rate); mixLeft->SetChannel(VTrack::MonoChannel); mixLeft->SetName(_("Mix")); WaveTrack *mixRight = 0; if (!mono) { mixRight = new WaveTrack(dirManager); mixRight->SetSampleFormat(format); mixRight->SetRate(rate); mixRight->SetName(_("Mix")); mixLeft->SetChannel(VTrack::LeftChannel); mixRight->SetChannel(VTrack::RightChannel); mixLeft->SetLinked(true); } int maxBlockLen = mixLeft->GetIdealBlockSize(); double maxBlockTime = maxBlockLen / mixLeft->GetRate(); Mixer *mixer = new Mixer(mono ? 1 : 2, maxBlockLen, false, rate, format); wxProgressDialog *progress = NULL; wxYield(); wxStartTimer(); wxBusyCursor busy; double tt = 0.0; while (tt < totalTime) { double blockTime = maxBlockTime; if (tt + blockTime > totalTime) blockTime = totalTime - tt; int blockLen = int (blockTime * mixLeft->GetRate()); mixer->Clear(); for (int i = 0; i < numWaves; i++) { if (mono) mixer->MixMono(waveArray[i], tt, tt + blockTime); else { switch (waveArray[i]->GetChannel()) { case VTrack::LeftChannel: mixer->MixLeft(waveArray[i], tt, tt + blockTime); break; case VTrack::RightChannel: mixer->MixRight(waveArray[i], tt, tt + blockTime); break; case VTrack::MonoChannel: mixer->MixMono(waveArray[i], tt, tt + blockTime); break; } } } if (mono) { samplePtr buffer = mixer->GetBuffer(); mixLeft->Append(buffer, format, blockLen); } else { samplePtr buffer; buffer = mixer->GetBuffer(0); mixLeft->Append(buffer, format, blockLen); buffer = mixer->GetBuffer(1); mixRight->Append(buffer, format, blockLen); } tt += blockTime; if (!progress && wxGetElapsedTime(false) > 500) { progress = new wxProgressDialog(_("Quick Mix"), _("Mixing tracks"), 1000); } if (progress) { int progressvalue = int (1000 * (tt / totalTime)); progress->Update(progressvalue); } } tracks->Add(mixLeft); if (!mono) tracks->Add(mixRight); delete progress; int elapsedMS = wxGetElapsedTime(); double elapsedTime = elapsedMS * 0.001; double maxTracks = totalTime / (elapsedTime / numWaves); #ifdef __WXGTK__ printf(_(" Tracks: %d\n"), numWaves); printf(_(" Mix length: %f sec\n"), totalTime); printf(_("Elapsed time: %f sec\n"), elapsedTime); printf(_("Max number of tracks to mix in real time: %f\n"), maxTracks); #endif delete waveArray; delete mixer; return true; }
bool EffectNoise::Process() { if (noiseDuration <= 0.0) noiseDuration = sDefaultGenerateLen; //Iterate over each track int ntrack = 0; this->CopyInputWaveTracks(); // Set up mOutputWaveTracks. bool bGoodResult = true; TrackListIterator iter(mOutputWaveTracks); WaveTrack *track = (WaveTrack *)iter.First(); while (track) { WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); numSamples = (longSampleCount)(noiseDuration * track->GetRate() + 0.5); longSampleCount i = 0; float *data = new float[tmp->GetMaxBlockSize()]; sampleCount block; while ((i < numSamples) && bGoodResult) { block = tmp->GetBestBlockSize(i); if (block > (numSamples - i)) block = numSamples - i; MakeNoise(data, block, track->GetRate(), noiseAmplitude); tmp->Append((samplePtr)data, floatSample, block); i += block; //Update the Progress meter if (TrackProgress(ntrack, (double)i / numSamples)) bGoodResult = false; } delete[] data; tmp->Flush(); track->Clear(mT0, mT1); track->Paste(mT0, tmp); delete tmp; if (!bGoodResult) break; //Iterate to the next track ntrack++; track = (WaveTrack *)iter.Next(); } if (bGoodResult) { /* save last used values save duration unless value was got from selection, so we save only when user explicitely setup a value */ if (mT1 == mT0) gPrefs->Write(wxT("/CsPresets/NoiseGen_Duration"), noiseDuration); gPrefs->Write(wxT("/CsPresets/NoiseGen_Type"), noiseType); gPrefs->Write(wxT("/CsPresets/NoiseGen_Amp"), noiseAmplitude); mT1 = mT0 + noiseDuration; // Update selection. } this->ReplaceProcessedWaveTracks(bGoodResult); return bGoodResult; }
bool MixAndRender(TrackList *tracks, TrackFactory *trackFactory, double rate, sampleFormat format, double startTime, double endTime, WaveTrack **newLeft, WaveTrack **newRight) { // This function was formerly known as "Quick Mix". It takes one or // more tracks as input; of all tracks that are selected, it mixes // them together, applying any envelopes, amplitude gain, panning, // and real-time effects in the process. The resulting pair of // tracks (stereo) are "rendered" and have no effects, gain, panning, // or envelopes. WaveTrack **waveArray; Track *t; int numWaves = 0; int numMono = 0; bool mono = false; int w; TrackListIterator iter(tracks); t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { numWaves++; float pan = ((WaveTrack*)t)->GetPan(); if (t->GetChannel() == Track::MonoChannel && pan == 0) numMono++; } t = iter.Next(); } if (numMono == numWaves) mono = true; double totalTime = 0.0; waveArray = new WaveTrack *[numWaves]; w = 0; t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { waveArray[w++] = (WaveTrack *) t; if (t->GetEndTime() > totalTime) totalTime = t->GetEndTime(); } t = iter.Next(); } WaveTrack *mixLeft = trackFactory->NewWaveTrack(format, rate); mixLeft->SetName(_("Mix")); WaveTrack *mixRight = 0; if (mono) { mixLeft->SetChannel(Track::MonoChannel); } else { mixRight = trackFactory->NewWaveTrack(format, rate); mixRight->SetName(_("Mix")); mixLeft->SetChannel(Track::LeftChannel); mixRight->SetChannel(Track::RightChannel); mixLeft->SetLinked(true); mixRight->SetTeamed(true); } int maxBlockLen = mixLeft->GetIdealBlockSize(); if (startTime == endTime) { startTime = 0.0; endTime = totalTime; } Mixer *mixer = new Mixer(numWaves, waveArray, tracks->GetTimeTrack(), startTime, endTime, mono ? 1 : 2, maxBlockLen, false, rate, format); wxYield(); GetActiveProject()->ProgressShow(_NoAcc("&Mix and Render"), _("Mixing and rendering tracks")); wxBusyCursor busy; bool cancelling = false; while(!cancelling) { sampleCount blockLen = mixer->Process(maxBlockLen); if (blockLen == 0) break; if (mono) { samplePtr buffer = mixer->GetBuffer(); mixLeft->Append(buffer, format, blockLen); } else { samplePtr buffer; buffer = mixer->GetBuffer(0); mixLeft->Append(buffer, format, blockLen); buffer = mixer->GetBuffer(1); mixRight->Append(buffer, format, blockLen); } int progressvalue = int (1000 * (mixer->MixGetCurrentTime() / totalTime)); cancelling = !GetActiveProject()->ProgressUpdate(progressvalue); } GetActiveProject()->ProgressHide(); mixLeft->Flush(); *newLeft = mixLeft; if (!mono) { mixRight->Flush(); *newRight = mixRight; } #if 0 int elapsedMS = wxGetElapsedTime(); double elapsedTime = elapsedMS * 0.001; double maxTracks = totalTime / (elapsedTime / numWaves); // Note: these shouldn't be translated - they're for debugging // and profiling only. printf(" Tracks: %d\n", numWaves); printf(" Mix length: %f sec\n", totalTime); printf("Elapsed time: %f sec\n", elapsedTime); printf("Max number of tracks to mix in real time: %f\n", maxTracks); #endif delete[] waveArray; delete mixer; return true; }
//ProcessOne() takes a track, transforms it to bunch of buffer-blocks, //and executes ProcessSoundTouch on these blocks bool EffectSoundTouch::ProcessOne(WaveTrack *track, longSampleCount start, longSampleCount end) { WaveTrack *outputTrack; longSampleCount s; mSoundTouch->setSampleRate((unsigned int)(track->GetRate()+0.5)); outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat()); //Get the length of the buffer (as double). len is //used simple to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); //Initiate a processing buffer. This buffer will (most likely) //be shorter than the length of the track being processed. float *buffer = new float[track->GetMaxBlockSize()]; //Go through the track one buffer at a time. s counts which //sample the current buffer starts at. s = start; while (s < end) { //Get a block of samples (smaller than the size of the buffer) sampleCount block = track->GetBestBlockSize(s); //Adjust the block size if it is the final block in the track if (s + block > end) block = end - s; //Get the samples from the track and put them in the buffer track->Get((samplePtr) buffer, floatSample, s, block); //Add samples to SoundTouch mSoundTouch->putSamples(buffer, block); //Get back samples from SoundTouch unsigned int outputCount = mSoundTouch->numSamples(); if (outputCount > 0) { float *buffer2 = new float[outputCount]; mSoundTouch->receiveSamples(buffer2, outputCount); outputTrack->Append((samplePtr)buffer2, floatSample, outputCount); delete[] buffer2; } //Increment s one blockfull of samples s += block; //Update the Progress meter if (TrackProgress(mCurTrackNum, (s - start) / len)) return false; } // Tell SoundTouch to finish processing any remaining samples mSoundTouch->flush(); unsigned int outputCount = mSoundTouch->numSamples(); if (outputCount > 0) { float *buffer2 = new float[outputCount]; mSoundTouch->receiveSamples(buffer2, outputCount); outputTrack->Append((samplePtr)buffer2, floatSample, outputCount); delete[] buffer2; } // Flush the output WaveTrack (since it's buffered, too) outputTrack->Flush(); // Clean up the buffer delete[]buffer; // Take the output track and insert it in place of the original // sample data track->Clear(mT0, mT1); track->Paste(mT0, outputTrack); double newLength = outputTrack->GetEndTime(); if (newLength > m_maxNewLength) m_maxNewLength = newLength; // Delete the outputTrack now that its data is inserted in place delete outputTrack; //Return true because the effect processing succeeded. return true; }
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count) { int stretch_buf_size = GetBufferSize(track->GetRate()); double amount = this->mAmount; sampleCount start = track->TimeToLongSamples(t0); sampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); int minDuration = stretch_buf_size * 2 + 1; if (len < minDuration){ //error because the selection is too short float maxTimeRes = log(len) / log(2.0); maxTimeRes = pow(2.0, floor(maxTimeRes) + 0.5); maxTimeRes = maxTimeRes / track->GetRate(); if (this->IsPreviewing()) { double defaultPreviewLen; gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &defaultPreviewLen, 6.0); /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/ if ((minDuration / mProjectRate) < defaultPreviewLen) { ::wxMessageBox (wxString::Format(_("Audio selection too short to preview.\n\n" "Try increasing the audio selection to at least %.1f seconds,\n" "or reducing the 'Time Resolution' to less than %.1f seconds."), (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s. floor(maxTimeRes * 10.0) / 10.0), GetName(), wxOK | wxICON_EXCLAMATION); } else { /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/ ::wxMessageBox (wxString::Format(_("Unable to Preview.\n\n" "For the current audio selection, the maximum\n" "'Time Resolution' is %.1f seconds."), floor(maxTimeRes * 10.0) / 10.0), GetName(), wxOK | wxICON_EXCLAMATION); } } else { /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/ ::wxMessageBox (wxString::Format(_("The 'Time Resolution' is too long for the selection.\n\n" "Try increasing the audio selection to at least %.1f seconds,\n" "or reducing the 'Time Resolution' to less than %.1f seconds."), (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s. floor(maxTimeRes * 10.0) / 10.0), GetName(), wxOK | wxICON_EXCLAMATION); } return false; } double adjust_amount=(double)len/((double)len-((double)stretch_buf_size*2.0)); amount=1.0+(amount-1.0)*adjust_amount; WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),track->GetRate()); PaulStretch *stretch=new PaulStretch(amount,stretch_buf_size,track->GetRate()); sampleCount nget=stretch->get_nsamples_for_fill(); int bufsize=stretch->poolsize; float *buffer0=new float[bufsize]; float *bufferptr0=buffer0; sampleCount outs=0; bool first_time=true; int fade_len=100; if (fade_len>(bufsize/2-1)) fade_len=bufsize/2-1; float *fade_track_smps=new float[fade_len]; sampleCount s=0; bool cancelled=false; while (s<len){ track->Get((samplePtr)bufferptr0,floatSample,start+s,nget); stretch->process(buffer0,nget); if (first_time) { stretch->process(buffer0,0); }; outs+=stretch->out_bufsize; s+=nget; if (first_time){//blend the the start of the selection track->Get((samplePtr)fade_track_smps,floatSample,start,fade_len); first_time=false; for (int i=0;i<fade_len;i++){ float fi=(float)i/(float)fade_len; stretch->out_buf[i]=stretch->out_buf[i]*fi+(1.0-fi)*fade_track_smps[i]; }; }; if (s>=len){//blend the end of the selection track->Get((samplePtr)fade_track_smps,floatSample,end-fade_len,fade_len); for (int i=0;i<fade_len;i++){ float fi=(float)i/(float)fade_len; int i2=bufsize/2-1-i; stretch->out_buf[i2]=stretch->out_buf[i2]*fi+(1.0-fi)*fade_track_smps[fade_len-1-i]; }; }; outputTrack->Append((samplePtr)stretch->out_buf,floatSample,stretch->out_bufsize); nget=stretch->get_nsamples(); if (TrackProgress(count, (s / (double) len))) { cancelled=true; break; }; }; delete [] fade_track_smps; outputTrack->Flush(); track->Clear(t0,t1); bool success = track->Paste(t0,outputTrack); if (!cancelled && success){ m_t1 = mT0 + outputTrack->GetEndTime(); } delete stretch; delete []buffer0; delete outputTrack; return !cancelled; };
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count){ int stretch_buf_size;//must be power of 2 (because Audacity's fft requires it) if (time_resolution<0.001) time_resolution=0.001f; { float tmp=track->GetRate()*time_resolution*0.5; tmp=log(tmp)/log(2.0); tmp=pow(2.0,floor(tmp+0.5)); stretch_buf_size=(int)tmp; }; if (stretch_buf_size<128) stretch_buf_size=128; double amount=this->amount; if (amount<1.0) amount=1.0; sampleCount start = track->TimeToLongSamples(t0); sampleCount end = track->TimeToLongSamples(t1); sampleCount len = (sampleCount)(end - start); m_t1=mT1; if (len<=(stretch_buf_size*2+1)){//error because the selection is too short /* i18n-hint: This is an effect error message, for the effect named Paulstretch. * Time Resolution is a parameter of the effect, the translation should match */ ::wxMessageBox(_("Error in Paulstretch:\nThe selection is too short.\n It must be much longer than the Time Resolution.")); return false; }; double adjust_amount=(double)len/((double)len-((double)stretch_buf_size*2.0)); amount=1.0+(amount-1.0)*adjust_amount; WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),track->GetRate()); PaulStretch *stretch=new PaulStretch(amount,stretch_buf_size,track->GetRate()); sampleCount nget=stretch->get_nsamples_for_fill(); int bufsize=stretch->poolsize; float *buffer0=new float[bufsize]; float *bufferptr0=buffer0; sampleCount outs=0; bool first_time=true; int fade_len=100; if (fade_len>(bufsize/2-1)) fade_len=bufsize/2-1; float *fade_track_smps=new float[fade_len]; sampleCount s=0; bool cancelled=false; while (s<len){ track->Get((samplePtr)bufferptr0,floatSample,start+s,nget); stretch->process(buffer0,nget); if (first_time) { stretch->process(buffer0,0); }; outs+=stretch->out_bufsize; s+=nget; if (first_time){//blend the the start of the selection track->Get((samplePtr)fade_track_smps,floatSample,start,fade_len); first_time=false; for (int i=0;i<fade_len;i++){ float fi=(float)i/(float)fade_len; stretch->out_buf[i]=stretch->out_buf[i]*fi+(1.0-fi)*fade_track_smps[i]; }; }; if (s>=len){//blend the end of the selection track->Get((samplePtr)fade_track_smps,floatSample,end-fade_len,fade_len); for (int i=0;i<fade_len;i++){ float fi=(float)i/(float)fade_len; int i2=bufsize/2-1-i; stretch->out_buf[i2]=stretch->out_buf[i2]*fi+(1.0-fi)*fade_track_smps[fade_len-1-i]; }; }; outputTrack->Append((samplePtr)stretch->out_buf,floatSample,stretch->out_bufsize); nget=stretch->get_nsamples(); if (TrackProgress(count, (s / (double) len))) { cancelled=true; break; }; }; delete [] fade_track_smps; outputTrack->Flush(); track->Clear(t0,t1); track->Paste(t0,outputTrack); if (!cancelled){ double flen=t1-t0; if (s>0) m_t1=t0+flen*(double)outs/(double)(s); }; delete stretch; delete []buffer0; delete outputTrack; return !cancelled; };
//TODO-MB: wouldn't it make more sense to delete the time track after 'mix and render'? bool MixAndRender(TrackList *tracks, TrackFactory *trackFactory, double rate, sampleFormat format, double startTime, double endTime, WaveTrack **newLeft, WaveTrack **newRight) { // This function was formerly known as "Quick Mix". WaveTrack **waveArray; Track *t; int numWaves = 0; /* number of wave tracks in the selection */ int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/ bool mono = false; /* flag if output can be mono without loosing anything*/ bool oneinput = false; /* flag set to true if there is only one input track (mono or stereo) */ int w; TrackListIterator iter(tracks); SelectedTrackListOfKindIterator usefulIter(Track::Wave, tracks); // this only iterates tracks which are relevant to this function, i.e. // selected WaveTracks. The tracklist is (confusingly) the list of all // tracks in the project t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { numWaves++; float pan = ((WaveTrack*)t)->GetPan(); if (t->GetChannel() == Track::MonoChannel && pan == 0) numMono++; } t = iter.Next(); } if (numMono == numWaves) mono = true; /* the next loop will do two things at once: * 1. build an array of all the wave tracks were are trying to process * 2. determine when the set of WaveTracks starts and ends, in case we * need to work out for ourselves when to start and stop rendering. */ double mixStartTime = 0.0; /* start time of first track to start */ bool gotstart = false; // flag indicates we have found a start time double mixEndTime = 0.0; /* end time of last track to end */ double tstart, tend; // start and end times for one track. waveArray = new WaveTrack *[numWaves]; w = 0; t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { waveArray[w++] = (WaveTrack *) t; tstart = t->GetStartTime(); tend = t->GetEndTime(); if (tend > mixEndTime) mixEndTime = tend; // try and get the start time. If the track is empty we will get 0, // which is ambiguous because it could just mean the track starts at // the beginning of the project, as well as empty track. The give-away // is that an empty track also ends at zero. if (tstart != tend) { // we don't get empty tracks here if (!gotstart) { // no previous start, use this one unconditionally mixStartTime = tstart; gotstart = true; } else if (tstart < mixStartTime) mixStartTime = tstart; // have a start, only make it smaller } // end if start and end are different } // end if track is a selected WaveTrack. /** @TODO: could we not use a SelectedTrackListOfKindIterator here? */ t = iter.Next(); } /* create the destination track (new track) */ if ((numWaves == 1) || ((numWaves == 2) && (usefulIter.First()->GetLink() != NULL))) oneinput = true; // only one input track (either 1 mono or one linked stereo pair) WaveTrack *mixLeft = trackFactory->NewWaveTrack(format, rate); if (oneinput) mixLeft->SetName(usefulIter.First()->GetName()); /* set name of output track to be the same as the sole input track */ else mixLeft->SetName(_("Mix")); mixLeft->SetOffset(mixStartTime); WaveTrack *mixRight = 0; if (mono) { mixLeft->SetChannel(Track::MonoChannel); } else { mixRight = trackFactory->NewWaveTrack(format, rate); if (oneinput) { if (usefulIter.First()->GetLink() != NULL) // we have linked track mixLeft->SetName(usefulIter.First()->GetLink()->GetName()); /* set name to match input track's right channel!*/ else mixLeft->SetName(usefulIter.First()->GetName()); /* set name to that of sole input channel */ } else mixRight->SetName(_("Mix")); mixLeft->SetChannel(Track::LeftChannel); mixRight->SetChannel(Track::RightChannel); mixRight->SetOffset(mixStartTime); mixLeft->SetLinked(true); } int maxBlockLen = mixLeft->GetIdealBlockSize(); // If the caller didn't specify a time range, use the whole range in which // any input track had clips in it. if (startTime == endTime) { startTime = mixStartTime; endTime = mixEndTime; } Mixer *mixer = new Mixer(numWaves, waveArray, Mixer::WarpOptions(tracks->GetTimeTrack()), startTime, endTime, mono ? 1 : 2, maxBlockLen, false, rate, format); ::wxSafeYield(); ProgressDialog *progress = new ProgressDialog(_("Mix and Render"), _("Mixing and rendering tracks")); int updateResult = eProgressSuccess; while(updateResult == eProgressSuccess) { sampleCount blockLen = mixer->Process(maxBlockLen); if (blockLen == 0) break; if (mono) { samplePtr buffer = mixer->GetBuffer(); mixLeft->Append(buffer, format, blockLen); } else { samplePtr buffer; buffer = mixer->GetBuffer(0); mixLeft->Append(buffer, format, blockLen); buffer = mixer->GetBuffer(1); mixRight->Append(buffer, format, blockLen); } updateResult = progress->Update(mixer->MixGetCurrentTime() - startTime, endTime - startTime); } delete progress; mixLeft->Flush(); if (!mono) mixRight->Flush(); if (updateResult == eProgressCancelled || updateResult == eProgressFailed) { delete mixLeft; if (!mono) delete mixRight; } else { *newLeft = mixLeft; if (!mono) *newRight = mixRight; #if 0 int elapsedMS = wxGetElapsedTime(); double elapsedTime = elapsedMS * 0.001; double maxTracks = totalTime / (elapsedTime / numWaves); // Note: these shouldn't be translated - they're for debugging // and profiling only. printf(" Tracks: %d\n", numWaves); printf(" Mix length: %f sec\n", totalTime); printf("Elapsed time: %f sec\n", elapsedTime); printf("Max number of tracks to mix in real time: %f\n", maxTracks); #endif } delete[] waveArray; delete mixer; return (updateResult == eProgressSuccess || updateResult == eProgressStopped); }
bool EffectDtmf::Process() { if (dtmfDuration <= 0.0) return false; //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *)iter.First(); while (track) { // new tmp track, to fill with dtmf sequence // we will build the track by adding a tone, then a silence, next tone, and so on... WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); // all dtmf sequence durations in samples from seconds numSamplesSequence = (longSampleCount)(dtmfDuration * track->GetRate() + 0.5); numSamplesTone = (longSampleCount)(dtmfTone * track->GetRate() + 0.5); numSamplesSilence = (longSampleCount)(dtmfSilence * track->GetRate() + 0.5); // recalculate the sum, and spread the difference - due to approximations. // Since diff should be in the order of "some" samples, a division (resulting in zero) // is not sufficient, so we add the additional remaining samples in each tone/silence block, // at least until available. int diff = numSamplesSequence - (dtmfNTones*numSamplesTone) - (dtmfNTones-1)*numSamplesSilence; if (diff>dtmfNTones) { // in this case, both these values would change, so it makes sense to recalculate diff // otherwise just keep the value we already have // should always be the case that dtmfNTones>1, as if 0, we don't even start processing, // and with 1 there is no difference to spread (no silence slot)... wxASSERT(dtmfNTones > 1); numSamplesTone += (diff/(dtmfNTones)); numSamplesSilence += (diff/(dtmfNTones-1)); diff = numSamplesSequence - (dtmfNTones*numSamplesTone) - (dtmfNTones-1)*numSamplesSilence; } // this var will be used as extra samples distributor int extra=0; longSampleCount i = 0; longSampleCount j = 0; int n=0; // pointer to string in dtmfString sampleCount block; bool isTone = true; float *data = new float[tmp->GetMaxBlockSize()]; // for the whole dtmf sequence, we will be generating either tone or silence // according to a bool value, and this might be done in small chunks of size // 'block', as a single tone might sometimes be larger than the block // tone and silence generally have different duration, thus two generation blocks // // Note: to overcome a 'clicking' noise introduced by the abrupt transition from/to // silence, I added a fade in/out of 1/250th of a second (4ms). This can still be // tweaked but gives excellent results at 44.1kHz: I haven't tried other freqs. // A problem might be if the tone duration is very short (<10ms)... (?) // // One more problem is to deal with the approximations done when calculating the duration // of both tone and silence: in some cases the final sum might not be same as the initial // duration. So, to overcome this, we had a redistribution block up, and now we will spread // the remaining samples in every bin in order to achieve the full duration: test case was // to generate an 11 tone DTMF sequence, in 4 seconds, and with DutyCycle=75%: after generation // you ended up with 3.999s or in other units: 3 seconds and 44097 samples. // while(i < numSamplesSequence) { if (isTone) // generate tone { // the statement takes care of extracting one sample from the diff bin and // adding it into the tone block until depletion extra=(diff-- > 0?1:0); for(j=0; j < numSamplesTone+extra; j+=block) { block = tmp->GetBestBlockSize(j); if (block > (numSamplesTone+extra - j)) block = numSamplesTone+extra - j; // generate the tone and append MakeDtmfTone(data, block, track->GetRate(), dtmfString[n], j, numSamplesTone); tmp->Append((samplePtr)data, floatSample, block); } i += numSamplesTone; n++; if(n>=dtmfNTones)break; } else // generate silence { // the statement takes care of extracting one sample from the diff bin and // adding it into the silence block until depletion extra=(diff-- > 0?1:0); for(j=0; j < numSamplesSilence+extra; j+=block) { block = tmp->GetBestBlockSize(j); if (block > (numSamplesSilence+extra - j)) block = numSamplesSilence+extra - j; // generate silence and append memset(data, 0, sizeof(float)*block); tmp->Append((samplePtr)data, floatSample, block); } i += numSamplesSilence; } // flip flag isTone=!isTone; } // finished the whole dtmf sequence delete[] data; tmp->Flush(); track->Clear(mT0, mT1); track->Paste(mT0, tmp); delete tmp; //Iterate to the next track track = (WaveTrack *)iter.Next(); } /* save last used values save duration unless value was got from selection, so we save only when user explicitely setup a value */ if (mT1 == mT0) gPrefs->Write(wxT("/CsPresets/DtmfGen_SequenceDuration"), dtmfDuration); gPrefs->Write(wxT("/CsPresets/DtmfGen_String"), dtmfString); gPrefs->Write(wxT("/CsPresets/DtmfGen_DutyCycle"), dtmfDutyCycle); // Update selection: this is not accurate if my calculations are wrong. // To validate, once the effect is done, unselect, and select all, then // see what the selection length is being reported (in sec,ms,samples) mT1 = mT0 + dtmfDuration; return true; }