diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 67116ccf7..a0a310d5d 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -1148,13 +1148,8 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, #ifdef EXPERIMENTAL_MIDI_OUT NoteTrackArray midiPlaybackTracks, #endif - TimeTrack *timeTrack, double sampleRate, - double t0, double t1, - AudioIOListener* listener, - bool playLooped /* = false */, - double cutPreviewGapStart /* = 0.0 */, - double cutPreviewGapLen, /* = 0.0 */ - const double * /* pStartTime */ /* = 0 */) + double sampleRate, double t0, double t1, + const AudioIOStartStreamOptions &options) { if( IsBusy() ) return 0; @@ -1194,8 +1189,8 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, } mSilenceLevel = (silenceLevelDB + dBRange)/(double)dBRange; // meter goes -dBRange dB -> 0dB - mTimeTrack = timeTrack; - mListener = listener; + mTimeTrack = options.timeTrack; + mListener = options.listener; mRate = sampleRate; mT0 = t0; mT1 = t1; @@ -1207,9 +1202,9 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, #ifdef EXPERIMENTAL_MIDI_OUT mMidiPlaybackTracks = midiPlaybackTracks; #endif - mPlayLooped = playLooped; - mCutPreviewGapStart = cutPreviewGapStart; - mCutPreviewGapLen = cutPreviewGapLen; + mPlayLooped = options.playLooped; + mCutPreviewGapStart = options.cutPreviewGapStart; + mCutPreviewGapLen = options.cutPreviewGapLen; mPlaybackBuffers = NULL; mPlaybackMixers = NULL; mCaptureBuffers = NULL; @@ -1326,13 +1321,17 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, memset(mPlaybackBuffers, 0, sizeof(RingBuffer*)*mPlaybackTracks.GetCount()); memset(mPlaybackMixers, 0, sizeof(Mixer*)*mPlaybackTracks.GetCount()); - for( unsigned int i = 0; i < mPlaybackTracks.GetCount(); i++ ) + const Mixer::WarpOptions &warpOptions = + Mixer::WarpOptions(mTimeTrack); + + for (unsigned int i = 0; i < mPlaybackTracks.GetCount(); i++) { mPlaybackBuffers[i] = new RingBuffer(floatSample, playbackBufferSize); // MB: use normal time for the end time, not warped time! mPlaybackMixers[i] = new Mixer(1, &mPlaybackTracks[i], - mTimeTrack, mT0, mT1, 1, + warpOptions, + mT0, mT1, 1, playbackMixBufferSize, false, mRate, floatSample, false); mPlaybackMixers[i]->ApplyTrackGains(false); diff --git a/src/AudioIO.h b/src/AudioIO.h index 6d5bd8db8..39af256a1 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -42,6 +42,7 @@ class Resample; class TimeTrack; class AudioThread; class Meter; +class SelectedRegion; class TimeTrack; class wxDialog; @@ -74,6 +75,25 @@ DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_AUDIOIO_PLAYBACK, -1); DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_AUDIOIO_CAPTURE, -1); DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_AUDIOIO_MONITOR, -1); +// To avoid growing the argument list of StartStream, add fields here +struct AudioIOStartStreamOptions +{ + AudioIOStartStreamOptions() + : timeTrack(NULL) + , listener(NULL) + , playLooped(false) + , cutPreviewGapStart(0.0) + , cutPreviewGapLen(0.0) + {} + + TimeTrack *timeTrack; + AudioIOListener* listener; + bool playLooped; + double cutPreviewGapStart; + double cutPreviewGapLen; + +}; + class AUDACITY_DLL_API AudioIO { public: @@ -103,15 +123,9 @@ class AUDACITY_DLL_API AudioIO { #ifdef EXPERIMENTAL_MIDI_OUT NoteTrackArray midiTracks, #endif - TimeTrack *timeTrack, double sampleRate, - double t0, double t1, - AudioIOListener* listener, - bool playLooped = false, - double cutPreviewGapStart = 0.0, - double cutPreviewGapLen = 0.0, - // May be other than t0, - // but will be constrained between t0 and t1 - const double *pStartTime = 0); + double sampleRate, double t0, double t1, + const AudioIOStartStreamOptions &options = + AudioIOStartStreamOptions()); /** \brief Stop recording, playback or input monitoring. * @@ -281,9 +295,8 @@ class AUDACITY_DLL_API AudioIO { */ static int GetOptimalSupportedSampleRate(); - /** \brief The time the stream has been playing for + /** \brief During playback, the (unwarped) track time most recently played * - * This is given in seconds based on starting at t0 * When playing looped, this will start from t0 again, * too. So the returned time should be always between * t0 and t1 @@ -375,10 +388,10 @@ private: #endif /** \brief Get the number of audio samples free in all of the playback - * buffers. - * - * Returns the smallest of the buffer free space values in the event that - * they are different. */ + * buffers. + * + * Returns the smallest of the buffer free space values in the event that + * they are different. */ int GetCommonlyAvailPlayback(); /** \brief Get the number of audio samples ready in all of the recording @@ -535,7 +548,7 @@ private: Meter *mInputMeter; Meter *mOutputMeter; bool mUpdateMeters; - bool mUpdatingMeters; + volatile bool mUpdatingMeters; #if USE_PORTMIXER PxMixer *mPortMixer; diff --git a/src/Menus.cpp b/src/Menus.cpp index e0e34119a..b247cf3a1 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -2013,7 +2013,8 @@ void AudacityProject::OnPlayOneSecond() double pos = mTrackPanel->GetMostRecentXPos(); mLastPlayMode = oneSecondPlay; - GetControlToolBar()->PlayPlayRegion(pos - 0.5, pos + 0.5); + GetControlToolBar()->PlayPlayRegion + (SelectedRegion(pos - 0.5, pos + 0.5), GetDefaultPlayOptions()); } @@ -2056,7 +2057,8 @@ void AudacityProject::OnPlayToSelection() // only when playing a short region, less than or equal to a second. // mLastPlayMode = ((t1-t0) > 1.0) ? normalPlay : oneSecondPlay; - GetControlToolBar()->PlayPlayRegion(t0, t1); + GetControlToolBar()->PlayPlayRegion + (SelectedRegion(t0, t1), GetDefaultPlayOptions()); } // The next 4 functions provide a limited version of the @@ -2073,7 +2075,7 @@ void AudacityProject::OnPlayBeforeSelectionStart() mLastPlayMode = oneSecondPlay; // this disables auto scrolling, as in OnPlayToSelection() - GetControlToolBar()->PlayPlayRegion(t0 - beforeLen, t0); + GetControlToolBar()->PlayPlayRegion(SelectedRegion(t0 - beforeLen, t0), GetDefaultPlayOptions()); } void AudacityProject::OnPlayAfterSelectionStart() @@ -2089,9 +2091,9 @@ void AudacityProject::OnPlayAfterSelectionStart() mLastPlayMode = oneSecondPlay; // this disables auto scrolling, as in OnPlayToSelection() if ( t1 - t0 > 0.0 && t1 - t0 < afterLen ) - GetControlToolBar()->PlayPlayRegion(t0, t1); + GetControlToolBar()->PlayPlayRegion(SelectedRegion(t0, t1), GetDefaultPlayOptions()); else - GetControlToolBar()->PlayPlayRegion(t0, t0 + afterLen); + GetControlToolBar()->PlayPlayRegion(SelectedRegion(t0, t0 + afterLen), GetDefaultPlayOptions()); } void AudacityProject::OnPlayBeforeSelectionEnd() @@ -2107,9 +2109,9 @@ void AudacityProject::OnPlayBeforeSelectionEnd() mLastPlayMode = oneSecondPlay; // this disables auto scrolling, as in OnPlayToSelection() if ( t1 - t0 > 0.0 && t1 - t0 < beforeLen ) - GetControlToolBar()->PlayPlayRegion(t0, t1); + GetControlToolBar()->PlayPlayRegion(SelectedRegion(t0, t1), GetDefaultPlayOptions()); else - GetControlToolBar()->PlayPlayRegion(t1 - beforeLen, t1); + GetControlToolBar()->PlayPlayRegion(SelectedRegion(t1 - beforeLen, t1), GetDefaultPlayOptions()); } @@ -2124,7 +2126,7 @@ void AudacityProject::OnPlayAfterSelectionEnd() mLastPlayMode = oneSecondPlay; // this disables auto scrolling, as in OnPlayToSelection() - GetControlToolBar()->PlayPlayRegion(t1, t1 + afterLen); + GetControlToolBar()->PlayPlayRegion(SelectedRegion(t1, t1 + afterLen), GetDefaultPlayOptions()); } void AudacityProject::OnPlayLooped() diff --git a/src/Mix.cpp b/src/Mix.cpp index a1cdc5853..cad92c516 100644 --- a/src/Mix.cpp +++ b/src/Mix.cpp @@ -161,7 +161,8 @@ bool MixAndRender(TrackList *tracks, TrackFactory *trackFactory, endTime = mixEndTime; } - Mixer *mixer = new Mixer(numWaves, waveArray, tracks->GetTimeTrack(), + Mixer *mixer = new Mixer(numWaves, waveArray, + Mixer::WarpOptions(tracks->GetTimeTrack()), startTime, endTime, mono ? 1 : 2, maxBlockLen, false, rate, format); @@ -226,8 +227,28 @@ bool MixAndRender(TrackList *tracks, TrackFactory *trackFactory, return (updateResult == eProgressSuccess || updateResult == eProgressStopped); } +Mixer::WarpOptions::WarpOptions(double min, double max) + : timeTrack(0), minSpeed(min), maxSpeed(max) +{ + if (minSpeed < 0) + { + wxASSERT(false); + minSpeed = 0; + } + if (maxSpeed < 0) + { + wxASSERT(false); + maxSpeed = 0; + } + if (minSpeed > maxSpeed) + { + wxASSERT(false); + std::swap(minSpeed, maxSpeed); + } +} + Mixer::Mixer(int numInputTracks, WaveTrack **inputTracks, - TimeTrack *timeTrack, + const WarpOptions &warpOptions, double startTime, double stopTime, int numOutChannels, int outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, @@ -238,12 +259,15 @@ Mixer::Mixer(int numInputTracks, WaveTrack **inputTracks, mHighQuality = highQuality; mNumInputTracks = numInputTracks; mInputTrack = new WaveTrack*[mNumInputTracks]; + + // mSamplePos holds for each track the next sample position not + // yet processed. mSamplePos = new sampleCount[mNumInputTracks]; for(i=0; iTimeToLongSamples(startTime); } - mTimeTrack = timeTrack; + mTimeTrack = warpOptions.timeTrack; mT0 = startTime; mT1 = stopTime; mTime = startTime; @@ -277,23 +301,45 @@ Mixer::Mixer(int numInputTracks, WaveTrack **inputTracks, } mFloatBuffer = new float[mInterleavedBufferSize]; + // This is the number of samples grabbed in one go from a track + // and placed in a queue, when mixing with resampling. + // (Should we use WaveTrack::GetBestBlockSize instead?) mQueueMaxLen = 65536; + + // But cut the queue into blocks of this finer size + // for variable rate resampling. Each block is resampled at some + // constant rate. mProcessLen = 1024; + // Position in each queue of the start of the next block to resample. mQueueStart = new int[mNumInputTracks]; + + // For each queue, the number of available samples after the queue start. mQueueLen = new int[mNumInputTracks]; mSampleQueue = new float *[mNumInputTracks]; mResample = new Resample*[mNumInputTracks]; for(i=0; iGetRate()); - if (timeTrack) { + double minFactor, maxFactor; + if (mTimeTrack) { // variable rate resampling - mResample[i] = new Resample(mHighQuality, - factor / timeTrack->GetRangeUpper(), - factor / timeTrack->GetRangeLower()); - } else { - mResample[i] = new Resample(mHighQuality, factor, factor); // constant rate resampling + mbVariableRates = true; + minFactor = factor / mTimeTrack->GetRangeUpper(); + maxFactor = factor / mTimeTrack->GetRangeLower(); } + else if (warpOptions.minSpeed > 0.0 && warpOptions.maxSpeed > 0.0) { + // variable rate resampling + mbVariableRates = true; + minFactor = factor / warpOptions.maxSpeed; + maxFactor = factor / warpOptions.minSpeed; + } + else { + // constant rate resampling + mbVariableRates = false; + minFactor = maxFactor = factor; + } + + mResample[i] = new Resample(mHighQuality, minFactor, maxFactor); mSampleQueue[i] = new float[mQueueMaxLen]; mQueueStart[i] = 0; mQueueLen[i] = 0; @@ -408,6 +454,7 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track, while (out < mMaxOut) { if (*queueLen < mProcessLen) { + // Shift pending portion to start of the buffer memmove(queue, &queue[*queueStart], (*queueLen) * sampleSize); *queueStart = 0; @@ -581,7 +628,7 @@ sampleCount Mixer::Process(sampleCount maxToProcess) } } - if (mTimeTrack || track->GetRate() != mRate) + if (mbVariableRates || track->GetRate() != mRate) out = MixVariableRates(channelFlags, track, &mSamplePos[i], mSampleQueue[i], &mQueueStart[i], &mQueueLen[i], mResample[i]); @@ -594,28 +641,27 @@ sampleCount Mixer::Process(sampleCount maxToProcess) double t = (double)mSamplePos[i] / (double)track->GetRate(); if(t > mTime) mTime = std::min(t, mT1); - } if(mInterleaved) { for(int c=0; cRemoveMenu(mRecentFilesMenu); } +AudioIOStartStreamOptions AudacityProject::GetDefaultPlayOptions() +{ + AudioIOStartStreamOptions options; + options.timeTrack = GetTracks()->GetTimeTrack(); + options.listener = this; + return options; +} + void AudacityProject::UpdatePrefsVariables() { gPrefs->Read(wxT("/AudioFiles/ShowId3Dialog"), &mShowId3Dialog, true); diff --git a/src/Project.h b/src/Project.h index 587564f01..b8166838d 100644 --- a/src/Project.h +++ b/src/Project.h @@ -82,6 +82,7 @@ class LyricsWindow; class MixerBoard; class MixerBoardFrame; +struct AudioIOStartStreamOptions; AudacityProject *CreateNewAudacityProject(); AUDACITY_DLL_API AudacityProject *GetActiveProject(); @@ -135,6 +136,8 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, const wxPoint & pos, const wxSize & size); virtual ~AudacityProject(); + AudioIOStartStreamOptions GetDefaultPlayOptions(); + TrackList *GetTracks() { return mTracks; } UndoManager *GetUndoManager() { return &mUndoManager; } diff --git a/src/Sequence.cpp b/src/Sequence.cpp index a995911d2..293610de6 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -1248,7 +1248,7 @@ bool Sequence::Set(samplePtr buffer, sampleFormat format, } bool Sequence::GetWaveDisplay(float *min, float *max, float *rms,int* bl, - int len, sampleCount *where, + int len, const sampleCount *where, double samplesPerPixel) { sampleCount s0 = where[0]; diff --git a/src/Sequence.h b/src/Sequence.h index eca3cb3d9..8e73921d8 100644 --- a/src/Sequence.h +++ b/src/Sequence.h @@ -80,7 +80,7 @@ class Sequence: public XMLTagHandler { sampleCount start, sampleCount len); bool GetWaveDisplay(float *min, float *max, float *rms,int* bl, - int len, sampleCount *where, + int len, const sampleCount *where, double samplesPerPixel); bool Copy(sampleCount s0, sampleCount s1, Sequence **dest); diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index cdc98ba93..73c26548b 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -973,7 +973,7 @@ void TrackPanel::OnTimer() AudacityProject *p = GetProject(); if ((p->GetAudioIOToken() > 0) && - gAudioIO->IsStreamActive(p->GetAudioIOToken())) + gAudioIO->IsStreamActive(p->GetAudioIOToken())) { // Update lyrics display. LyricsWindow* pLyricsWindow = p->GetLyricsWindow(); @@ -991,11 +991,11 @@ void TrackPanel::OnTimer() // audacityAudioCallback where it calls gAudioIO->mOutputMeter->UpdateDisplay(). MixerBoard* pMixerBoard = this->GetMixerBoard(); if (pMixerBoard && - (p->GetAudioIOToken() > 0) && - gAudioIO->IsStreamActive(p->GetAudioIOToken())) + (p->GetAudioIOToken() > 0) && + gAudioIO->IsStreamActive(p->GetAudioIOToken())) { pMixerBoard->UpdateMeters(gAudioIO->GetStreamTime(), - (p->mLastPlayMode == loopedPlay)); + (p->mLastPlayMode == loopedPlay)); } // Check whether we were playing or recording, but the stream has stopped. @@ -1379,27 +1379,29 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */) mRefreshBacking = false; // Redraw the backing bitmap - DrawTracks( &mBackingDC ); + DrawTracks(&mBackingDC); // Copy it to the display - dc->Blit( 0, 0, mBacking->GetWidth(), mBacking->GetHeight(), &mBackingDC, 0, 0 ); + dc->Blit(0, 0, mBacking->GetWidth(), mBacking->GetHeight(), &mBackingDC, 0, 0); } else { // Copy full, possibly clipped, damage rectange - dc->Blit( box.x, box.y, box.width, box.height, &mBackingDC, box.x, box.y ); + dc->Blit(box.x, box.y, box.width, box.height, &mBackingDC, box.x, box.y); } // Done with the clipped DC delete dc; // Drawing now goes directly to the client area - wxClientDC cdc( this ); + wxClientDC cdc(this); // Update the indicator in case it was damaged if this project is playing + + // PRL: mIndicatorShowing never becomes true! AudacityProject* p = GetProject(); if (!gAudioIO->IsPaused() && - ( mIndicatorShowing || gAudioIO->IsStreamActive(p->GetAudioIOToken()))) + (mIndicatorShowing || gAudioIO->IsStreamActive(p->GetAudioIOToken()))) { // We just want to repair, not update the old, so set the second param to true. // This is important because this onPaint could be for just some of the tracks. @@ -1407,8 +1409,8 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */) } // Draw the cursor - if( mViewInfo->selectedRegion.isPoint()) - DoDrawCursor( cdc ); + if (mViewInfo->selectedRegion.isPoint()) + DoDrawCursor(cdc); #if DEBUG_DRAW_TIMING sw.Pause(); @@ -2119,7 +2121,8 @@ void TrackPanel::StartOrJumpPlayback(wxMouseEvent &event) //the clicked point ControlToolBar * ctb = p->GetControlToolBar(); //ctb->SetPlay(true);// Not needed as done in PlayPlayRegion - ctb->PlayPlayRegion(clicktime, endtime,false) ; + ctb->PlayPlayRegion + (SelectedRegion(clicktime, endtime), p->GetDefaultPlayOptions()); } else { @@ -2130,7 +2133,7 @@ void TrackPanel::StartOrJumpPlayback(wxMouseEvent &event) //require a new method in ControlToolBar: SetPause(); ControlToolBar * ctb = p->GetControlToolBar(); ctb->StopPlaying(); - ctb->PlayPlayRegion(clicktime,endtime,false) ; + ctb->PlayPlayRegion(SelectedRegion(clicktime, endtime), p->GetDefaultPlayOptions()); } } } diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 804455dfa..75bb92bb6 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -2265,7 +2265,7 @@ void Effect::Preview(bool dryOnly) #ifdef EXPERIMENTAL_MIDI_OUT empty, #endif - NULL, rate, t0, t1, NULL); + rate, t0, t1); if (token) { int previewing = eProgressSuccess; @@ -2959,7 +2959,9 @@ void EffectUIHost::OnPlay(wxCommandEvent & WXUNUSED(evt)) mPlayPos = mRegion.t1(); } - mProject->GetControlToolBar()->PlayPlayRegion(mPlayPos, mRegion.t1()); + mProject->GetControlToolBar()->PlayPlayRegion + (SelectedRegion(mPlayPos, mRegion.t1()), + mProject->GetDefaultPlayOptions()); } } diff --git a/src/export/Export.cpp b/src/export/Export.cpp index d974d9f23..0ac5c4e82 100644 --- a/src/export/Export.cpp +++ b/src/export/Export.cpp @@ -277,7 +277,7 @@ Mixer* ExportPlugin::CreateMixer(int numInputTracks, WaveTrack **inputTracks, { // MB: the stop time should not be warped, this was a bug. return new Mixer(numInputTracks, inputTracks, - timeTrack, + Mixer::WarpOptions(timeTrack), startTime, stopTime, numOutChannels, outBufferSize, outInterleaved, outRate, outFormat, diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 9f0b0c4f6..641869266 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -467,34 +467,42 @@ bool ControlToolBar::IsRecordDown() { return mRecord->IsDown(); } -void ControlToolBar::PlayPlayRegion(double t0, double t1, - bool looped /* = false */, - bool cutpreview /* = false */, - TimeTrack *timetrack /* = NULL */, - const double *pStartTime /* = NULL */) + +int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, + const AudioIOStartStreamOptions &options, + bool cutpreview, /* = false */ + bool backwards /* = false */) { + double t0 = selectedRegion.t0(); + double t1 = selectedRegion.t1(); + // SelectedRegion guarantees t0 <= t1, so we need another boolean argument + // to indicate backwards play. + const bool looped = options.playLooped; + + wxASSERT(! backwards); + SetPlay(true, looped, cutpreview); if (gAudioIO->IsBusy()) { SetPlay(false); - return; + return -1; } if (cutpreview && t0==t1) { SetPlay(false); - return; /* msmeyer: makes no sense */ + return -1; /* msmeyer: makes no sense */ } AudacityProject *p = GetActiveProject(); if (!p) { SetPlay(false); - return; // Should never happen, but... + return -1; // Should never happen, but... } TrackList *t = p->GetTracks(); if (!t) { mPlay->PopUp(); - return; // Should never happen, but... + return -1; // Should never happen, but... } bool hasaudio = false; @@ -512,7 +520,7 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1, if (!hasaudio) { SetPlay(false); - return; // No need to continue without audio tracks + return -1; // No need to continue without audio tracks } double maxofmins,minofmaxs; @@ -565,7 +573,7 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1, // we test if the intersection has no volume if (minofmaxs <= maxofmins) { // no volume; play nothing - return; + return -1; } else { t0 = maxofmins; @@ -573,14 +581,15 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1, } } - // Can't play before 0...either shifted or latencey corrected tracks - if (t0 < 0.0) { + // Can't play before 0...either shifted or latency corrected tracks + if (t0 < 0.0) t0 = 0.0; - } + if (t1 < 0.0) + t1 = 0.0; + int token = -1; bool success = false; if (t1 > t0) { - int token; if (cutpreview) { double beforeLen, afterLen; gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0); @@ -590,36 +599,37 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1, SetupCutPreviewTracks(tcp0, t0, t1, tcp1); if (mCutPreviewTracks) { + AudioIOStartStreamOptions myOptions = options; + myOptions.cutPreviewGapStart = t0; + myOptions.cutPreviewGapLen = t1 - t0; token = gAudioIO->StartStream( mCutPreviewTracks->GetWaveTrackArray(false), WaveTrackArray(), #ifdef EXPERIMENTAL_MIDI_OUT NoteTrackArray(), #endif - timetrack, p->GetRate(), tcp0, tcp1, p, false, - t0, t1-t0, - pStartTime); + p->GetRate(), tcp0, tcp1, myOptions); } else { // Cannot create cut preview tracks, clean up and exit SetPlay(false); SetStop(false); SetRecord(false); - return; + return -1; } } else { + // Lifted the following into AudacityProject::GetDefaultPlayOptions() + /* if (!timetrack) { timetrack = t->GetTimeTrack(); } + */ token = gAudioIO->StartStream(t->GetWaveTrackArray(false), WaveTrackArray(), #ifdef EXPERIMENTAL_MIDI_OUT t->GetNoteTrackArray(false), #endif - timetrack, - p->GetRate(), t0, t1, p, looped, - 0, 0, - pStartTime); + p->GetRate(), t0, t1, options); } if (token != 0) { success = true; @@ -648,7 +658,10 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1, SetPlay(false); SetStop(false); SetRecord(false); + return -1; } + + return token; } void ControlToolBar::PlayCurrentRegion(bool looped /* = false */, @@ -666,9 +679,12 @@ void ControlToolBar::PlayCurrentRegion(bool looped /* = false */, double playRegionStart, playRegionEnd; p->GetPlayRegion(&playRegionStart, &playRegionEnd); - PlayPlayRegion(playRegionStart, - playRegionEnd, - looped, cutpreview); + AudioIOStartStreamOptions options(p->GetDefaultPlayOptions()); + options.playLooped = looped; + if (cutpreview) + options.timeTrack = NULL; + PlayPlayRegion(SelectedRegion(playRegionStart, playRegionEnd), + options, cutpreview); } } @@ -898,14 +914,14 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt) #ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT gAudioIO->AILAInitialize(); #endif - + + AudioIOStartStreamOptions options(p->GetDefaultPlayOptions()); int token = gAudioIO->StartStream(playbackTracks, newRecordingTracks, #ifdef EXPERIMENTAL_MIDI_OUT midiTracks, #endif - t->GetTimeTrack(), - p->GetRate(), t0, t1, p); + p->GetRate(), t0, t1, options); bool success = (token != 0); diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index a2ee3935b..847752cf1 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -30,6 +30,9 @@ class AudacityProject; class TrackList; class TimeTrack; +struct AudioIOStartStreamOptions; +class SelectedRegion; + // In the GUI, ControlToolBar appears as the "Transport Toolbar". "Control Toolbar" is historic. class ControlToolBar:public ToolBar { @@ -64,13 +67,10 @@ class ControlToolBar:public ToolBar { // play from current cursor. void PlayCurrentRegion(bool looped = false, bool cutpreview = false); // Play the region [t0,t1] - void PlayPlayRegion(double t0, double t1, - bool looped = false, - bool cutpreview = false, - TimeTrack *timetrack = NULL, - // May be other than t0, - // but will be constrained between t0 and t1 - const double *pStartTime = NULL); + // Return the Audio IO token or -1 for failure + int PlayPlayRegion(const SelectedRegion &selectedRegion, + const AudioIOStartStreamOptions &options, + bool cutpreview = false, bool backwards = false); void PlayDefault(); // Stop playing diff --git a/src/toolbars/TranscriptionToolBar.cpp b/src/toolbars/TranscriptionToolBar.cpp index 0e603bcdb..9e71f691c 100644 --- a/src/toolbars/TranscriptionToolBar.cpp +++ b/src/toolbars/TranscriptionToolBar.cpp @@ -439,11 +439,13 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview) #ifdef EXPERIMENTAL_MIDI_OUT gAudioIO->SetMidiPlaySpeed(mPlaySpeed); #endif - p->GetControlToolBar()->PlayPlayRegion(playRegionStart, - playRegionEnd, - looped, - cutPreview, - mTimeTrack); + AudioIOStartStreamOptions options(p->GetDefaultPlayOptions()); + options.playLooped = looped; + options.timeTrack = mTimeTrack; + p->GetControlToolBar()->PlayPlayRegion + (SelectedRegion(playRegionStart, playRegionEnd), + options, + cutPreview); } }