bool EffectSoundTouch::ProcessStereo(WaveTrack* leftTrack, WaveTrack* rightTrack, sampleCount start, sampleCount end) { mSoundTouch->setSampleRate((unsigned int)(leftTrack->GetRate()+0.5)); WaveTrack* outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat(), leftTrack->GetRate()); WaveTrack* outputRightTrack = mFactory->NewWaveTrack(rightTrack->GetSampleFormat(), rightTrack->GetRate()); //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. sampleCount 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 // mCurTrackNum is left track. Include right track. int nWhichTrack = mCurTrackNum; double frac = (sourceSampleCount - start) / len; 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; } // 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->ClearAndPaste(mCurT0, mCurT1, outputLeftTrack, true, false, GetTimeWarper()); rightTrack->ClearAndPaste(mCurT0, mCurT1, outputRightTrack, true, false, GetTimeWarper()); // Track the longest result length double newLength = outputLeftTrack->GetEndTime(); m_maxNewLength = wxMax(m_maxNewLength, newLength); newLength = outputRightTrack->GetEndTime(); m_maxNewLength = wxMax(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; }
//ProcessOne() takes a track, transforms it to bunch of buffer-blocks, //and executes ProcessSoundTouch on these blocks bool EffectSoundTouch::ProcessOne(WaveTrack *track, sampleCount start, sampleCount end) { WaveTrack *outputTrack; sampleCount s; mSoundTouch->setSampleRate((unsigned int)(track->GetRate()+0.5)); outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); //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(); // this should only be used for changeTempo - it dumps data otherwise with pRateTransposer->clear(); 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->ClearAndPaste(mCurT0, mCurT1, outputTrack, true, false, GetTimeWarper()); double newLength = outputTrack->GetEndTime(); m_maxNewLength = wxMax(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; }
// ProcessOne() takes a track, transforms it to bunch of buffer-blocks, // and calls libsamplerate code on these blocks. bool EffectChangeSpeed::ProcessOne(WaveTrack * track, longSampleCount start, longSampleCount 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]; double factor = 100.0 / (100.0 + m_PercentChange); sampleCount outBufferSize = (sampleCount)((factor * inBufferSize) + 10); float * outBuffer = new float[outBufferSize]; // Set up the resampling stuff for this track. Resample resample(true, factor, factor); //Go through the track one buffer at a time. samplePos counts which //sample the current buffer starts at. bool bLoopSuccess = true; sampleCount blockSize; longSampleCount 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(factor, inBuffer, blockSize, ((samplePos + blockSize) >= end), &inUsed, outBuffer, outBufferSize); if (outgen < 0) { bLoopSuccess = 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)) { bLoopSuccess = 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 if (bLoopSuccess) { track->Clear(mCurT0, mCurT1); track->Paste(mCurT0, 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 bLoopSuccess; }
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; }
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); } } }