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; }
bool EffectSilence::Process() { if (length <= 0.0) length = sDefaultGenerateLen; TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *)iter.First(); while (track) { WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); tmp->InsertSilence(0.0, length); 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; }
//------------------------- Processing methods ------------------------- bool EffectSineSweepGenerator::Process() { // taken `as is` from Audacity`s Generator.cpp to resolve // a vc++ linking problem... if (mDuration < 0.0) return false; BeforeGenerate(); // Set up mOutputTracks. This effect needs Track::All for grouping this->CopyInputTracks(Track::All); // Iterate over the tracks bool bGoodResult = true; int ntrack = 0; TrackListIterator iter(mOutputTracks); Track* t = iter.First(); while (t != NULL) { if (t->GetKind() == Track::Wave && t->GetSelected()) { WaveTrack* track = (WaveTrack*)t; bool editClipCanMove = true; //gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove, true); //if we can't move clips, and we're generating into an empty space, //make sure there's room. if (!editClipCanMove && track->IsEmpty(mT0, mT1+1.0/track->GetRate()) && !track->IsEmpty(mT0, mT0+mDuration-(mT1-mT0)-1.0/track->GetRate())) { wxMessageBox(_("There is not enough room available to generate the audio"), _("Error"), wxICON_STOP); Failure(); return false; } if (mDuration > 0.0) { // Create a temporary track WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); //BeforeTrack(*track); // Fill it with data if (!GenerateTrack(tmp, *track, ntrack)) bGoodResult = false; else { // Transfer the data from the temporary track to the actual one tmp->Flush(); SetTimeWarper(new AFStepTimeWarper(mT0+mDuration, mDuration-(mT1-mT0))); bGoodResult = track->ClearAndPaste(mT0, mT1, tmp, true, false, GetTimeWarper()); delete tmp; } if (!bGoodResult) { Failure(); return false; } } else { // If the duration is zero, there's no need to actually // generate anything track->Clear(mT0, mT1); } ntrack++; } else if (t->IsSyncLockSelected()) { t->SyncLockAdjust(mT1, mT0 + mDuration); } // Move on to the next track t = iter.Next(); } Success(); this->ReplaceProcessedTracks(bGoodResult); mT1 = mT0 + mDuration; // Update selection. 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; }
void ControlToolBar::OnRecord(wxCommandEvent &evt) { if (gAudioIO->IsBusy()) { mRecord->PopUp(); return; } AudacityProject *p = GetActiveProject(); if( evt.GetInt() == 1 ) // used when called by keyboard shortcut. Default (0) ignored. mRecord->SetShift(true); if( evt.GetInt() == 2 ) mRecord->SetShift(false); SetRecord(true, mRecord->WasShiftDown()); if (p) { TrackList *t = p->GetTracks(); TrackListIterator it(t); if(it.First() == NULL) mRecord->SetShift(false); double t0 = p->GetSel0(); double t1 = p->GetSel1(); if (t1 == t0) t1 = 1000000000.0; // record for a long, long time (tens of years) /* TODO: set up stereo tracks if that is how the user has set up * their preferences, and choose sample format based on prefs */ WaveTrackArray newRecordingTracks, playbackTracks; #ifdef EXPERIMENTAL_MIDI_OUT NoteTrackArray midiTracks; #endif bool duplex; gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true); if(duplex){ playbackTracks = t->GetWaveTrackArray(false); #ifdef EXPERIMENTAL_MIDI_OUT midiTracks = t->GetNoteTrackArray(false); #endif } else { playbackTracks = WaveTrackArray(); #ifdef EXPERIMENTAL_MIDI_OUT midiTracks = NoteTrackArray(); #endif } // If SHIFT key was down, the user wants append to tracks int recordingChannels = 0; bool shifted = mRecord->WasShiftDown(); if (shifted) { bool sel = false; double allt0 = t0; // Find the maximum end time of selected and all wave tracks // Find whether any tracks were selected. (If any are selected, // record only into them; else if tracks exist, record into all.) for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave) { WaveTrack *wt = static_cast<WaveTrack *>(tt); if (wt->GetEndTime() > allt0) { allt0 = wt->GetEndTime(); } if (tt->GetSelected()) { sel = true; if (wt->GetEndTime() > t0) { t0 = wt->GetEndTime(); } } } } // Use end time of all wave tracks if none selected if (!sel) { t0 = allt0; } // Pad selected/all wave tracks to make them all the same length // Remove recording tracks from the list of tracks for duplex ("overdub") // playback. for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave && (tt->GetSelected() || !sel)) { WaveTrack *wt = static_cast<WaveTrack *>(tt); if (duplex) playbackTracks.Remove(wt); t1 = wt->GetEndTime(); if (t1 < t0) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->InsertSilence(0.0, t0 - t1); newTrack->Flush(); wt->Clear(t1, t0); bool bResult = wt->Paste(t1, newTrack); wxASSERT(bResult); // TO DO: Actually handle this. delete newTrack; } newRecordingTracks.Add(wt); } } t1 = 1000000000.0; // record for a long, long time (tens of years) } else { recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2); for (int c = 0; c < recordingChannels; c++) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->SetOffset(t0); if (recordingChannels > 2) newTrack->SetMinimized(true); if (recordingChannels == 2) { if (c == 0) { newTrack->SetChannel(Track::LeftChannel); newTrack->SetLinked(true); } else { newTrack->SetChannel(Track::RightChannel); } } else { newTrack->SetChannel( Track::MonoChannel ); } newRecordingTracks.Add(newTrack); } // msmeyer: StartStream calls a callback which triggers auto-save, so // we add the tracks where recording is done into now. We remove them // later if starting the stream fails for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) t->Add(newRecordingTracks[i]); } //Automated Input Level Adjustment Initialization #ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT gAudioIO->AILAInitialize(); #endif int token = gAudioIO->StartStream(playbackTracks, newRecordingTracks, #ifdef EXPERIMENTAL_MIDI_OUT midiTracks, #endif t->GetTimeTrack(), p->GetRate(), t0, t1, p); bool success = (token != 0); if (success) { p->SetAudioIOToken(token); mBusyProject = p; } else { // msmeyer: Delete recently added tracks if opening stream fails if (!shifted) { for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) { t->Remove(newRecordingTracks[i]); delete newRecordingTracks[i]; } } // msmeyer: Show error message if stream could not be opened wxMessageBox(_("Error while opening sound device. Please check the recording device settings and the project sample rate."), _("Error"), wxOK | wxICON_EXCLAMATION, this); SetPlay(false); SetStop(false); SetRecord(false); } } }
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 EffectSoundTouch::ProcessStereo(WaveTrack* leftTrack, WaveTrack* rightTrack, longSampleCount start, longSampleCount end) { mSoundTouch->setSampleRate((unsigned int)(leftTrack->GetRate()+0.5)); WaveTrack* outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat()); WaveTrack* outputRightTrack = mFactory->NewWaveTrack(rightTrack->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. // Make soundTouchBuffer twice as big as MaxBlockSize for each channel, // because Soundtouch wants them interleaved, i.e., each // Soundtouch sample is left-right pair. sampleCount maxBlockSize = leftTrack->GetMaxBlockSize(); float* leftBuffer = new float[maxBlockSize]; float* rightBuffer = new float[maxBlockSize]; float* soundTouchBuffer = new float[maxBlockSize * 2]; // Go through the track one stereo buffer at a time. // sourceSampleCount counts the sample at which the current buffer starts, // per channel. longSampleCount sourceSampleCount = start; while (sourceSampleCount < end) { //Get a block of samples (smaller than the size of the buffer) sampleCount blockSize = leftTrack->GetBestBlockSize(sourceSampleCount); //Adjust the block size if it is the final block in the track if (sourceSampleCount + blockSize > end) blockSize = end - sourceSampleCount; // Get the samples from the tracks and put them in the buffers. leftTrack->Get((samplePtr)(leftBuffer), floatSample, sourceSampleCount, blockSize); rightTrack->Get((samplePtr)(rightBuffer), floatSample, sourceSampleCount, blockSize); // Interleave into soundTouchBuffer. for (int index = 0; index < blockSize; index++) { soundTouchBuffer[index*2] = leftBuffer[index]; soundTouchBuffer[(index*2)+1] = rightBuffer[index]; } //Add samples to SoundTouch mSoundTouch->putSamples(soundTouchBuffer, blockSize); //Get back samples from SoundTouch unsigned int outputCount = mSoundTouch->numSamples(); if (outputCount > 0) this->ProcessStereoResults(outputCount, outputLeftTrack, outputRightTrack); //Increment sourceSampleCount one blockfull of samples sourceSampleCount += blockSize; //Update the Progress meter if (TrackProgress(mCurTrackNum, (sourceSampleCount - start) / len)) return false; } // Tell SoundTouch to finish processing any remaining samples mSoundTouch->flush(); unsigned int outputCount = mSoundTouch->numSamples(); if (outputCount > 0) this->ProcessStereoResults(outputCount, outputLeftTrack, outputRightTrack); // Flush the output WaveTracks (since they're buffered, too) outputLeftTrack->Flush(); outputRightTrack->Flush(); // Clean up the buffers. delete [] leftBuffer; delete [] rightBuffer; delete [] soundTouchBuffer; // Take the output tracks and insert in place of the original // sample data. leftTrack->Clear(mT0, mT1); leftTrack->Paste(mT0, outputLeftTrack); rightTrack->Clear(mT0, mT1); rightTrack->Paste(mT0, outputRightTrack); double newLength = outputLeftTrack->GetEndTime(); if (newLength > m_maxNewLength) m_maxNewLength = newLength; // Delete the outputTracks now that their data are inserted in place. delete outputLeftTrack; delete outputRightTrack; //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; }
void ControlToolBar::OnRecord(wxCommandEvent &evt) { if (gAudioIO->IsBusy()) { mRecord->PopUp(); return; } AudacityProject *p = GetActiveProject(); if (p && p->GetCleanSpeechMode()) { size_t numProjects = gAudacityProjects.Count(); bool tracks = (p && !p->GetTracks()->IsEmpty()); if (tracks || (numProjects > 1)) { wxMessageBox(_("Recording in CleanSpeech mode is not possible when a track, or more than one project, is already open."), _("Recording not permitted"), wxOK | wxICON_INFORMATION, this); mRecord->PopUp(); mRecord->Disable(); return; } } if( evt.GetInt() == 1 ) // used when called by keyboard shortcut. Default (0) ignored. mRecord->SetShift(true); if( evt.GetInt() == 2 ) mRecord->SetShift(false); SetRecord(true); if (p) { TrackList *t = p->GetTracks(); TrackListIterator it(t); if(it.First() == NULL) mRecord->SetShift(false); double t0 = p->GetSel0(); double t1 = p->GetSel1(); if (t1 == t0) t1 = 1000000000.0; // record for a long, long time (tens of years) /* TODO: set up stereo tracks if that is how the user has set up * their preferences, and choose sample format based on prefs */ WaveTrackArray newRecordingTracks, playbackTracks; /* REQUIRES PORTMIDI */ // NoteTrackArray midiTracks; bool duplex; gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true); if(duplex){ playbackTracks = t->GetWaveTrackArray(false); /* REQUIRES PORTMIDI */ // midiTracks = t->GetNoteTrackArray(false); } else { playbackTracks = WaveTrackArray(); /* REQUIRES PORTMIDI */ // midiTracks = NoteTrackArray(); } // If SHIFT key was down, the user wants append to tracks int recordingChannels = 0; bool shifted = mRecord->WasShiftDown(); if (shifted) { TrackListIterator it(t); WaveTrack *wt; bool sel = false; double allt0 = t0; // Find the maximum end time of selected and all wave tracks for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave) { wt = (WaveTrack *)tt; if (wt->GetEndTime() > allt0) { allt0 = wt->GetEndTime(); } if (tt->GetSelected()) { sel = true; if (duplex) playbackTracks.Remove(wt); if (wt->GetEndTime() > t0) { t0 = wt->GetEndTime(); } } } } // Use end time of all wave tracks if none selected if (!sel) { t0 = allt0; } // Pad selected/all wave tracks to make them all the same length for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave && (tt->GetSelected() || !sel)) { wt = (WaveTrack *)tt; t1 = wt->GetEndTime(); if (t1 < t0) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->InsertSilence(0.0, t0 - t1); newTrack->Flush(); wt->Clear(t1, t0); wt->Paste(t1, newTrack); delete newTrack; } newRecordingTracks.Add(wt); } } t1 = 1000000000.0; // record for a long, long time (tens of years) } else { recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2); for (int c = 0; c < recordingChannels; c++) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); int initialheight = newTrack->GetHeight(); newTrack->SetOffset(t0); if (recordingChannels <= 2) { newTrack->SetHeight(initialheight/recordingChannels); } else { newTrack->SetMinimized(true); } if (recordingChannels == 2) { if (c == 0) { newTrack->SetChannel(Track::LeftChannel); newTrack->SetLinked(true); } else { newTrack->SetChannel(Track::RightChannel); newTrack->SetTeamed(true); } } else { newTrack->SetChannel( Track::MonoChannel ); } newRecordingTracks.Add(newTrack); } // msmeyer: StartStream calls a callback which triggers auto-save, so // we add the tracks where recording is done into now. We remove them // later if starting the stream fails for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) t->Add(newRecordingTracks[i]); } int token = gAudioIO->StartStream(playbackTracks, newRecordingTracks, /* REQUIRES PORTMIDI */ // midiTracks, t->GetTimeTrack(), p->GetRate(), t0, t1, p); bool success = (token != 0); if (success) { p->SetAudioIOToken(token); mBusyProject = p; SetVUMeters(p); } else { // msmeyer: Delete recently added tracks if opening stream fails if (!shifted) { for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) { t->Remove(newRecordingTracks[i]); delete newRecordingTracks[i]; } } // msmeyer: Show error message if stream could not be opened wxMessageBox(_("Error while opening sound device. " wxT("Please check the input device settings and the project sample rate.")), _("Error"), wxOK | wxICON_EXCLAMATION, this); SetPlay(false); SetStop(false); SetRecord(false); } } }