diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 5a9b1f344..6ddd3a845 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -1861,11 +1861,7 @@ void AudioIO::StartMonitoring(double sampleRate) } } -int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks, - const WaveTrackArray &captureTracks, -#ifdef EXPERIMENTAL_MIDI_OUT - const NoteTrackArray &midiPlaybackTracks, -#endif +int AudioIO::StartStream(const TransportTracks &tracks, double t0, double t1, const AudioIOStartStreamOptions &options) { @@ -1921,7 +1917,7 @@ int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks, } mSilenceLevel = (silenceLevelDB + dBRange)/(double)dBRange; // meter goes -dBRange dB -> 0dB - if ( !captureTracks.empty() ) { + if ( !tracks.captureTracks.empty() ) { // It does not make sense to apply the time warp during overdub recording, // which defeats the purpose of making the recording synchronized with // the existing audio. (Unless we figured out the inverse warp of the @@ -1941,7 +1937,7 @@ int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks, DEFAULT_LATENCY_CORRECTION)) / 1000.0; mRecordingSchedule.mDuration = mT1 - mT0; - if (captureTracks.size() > 0) + if (tracks.captureTracks.size() > 0) // adjust mT1 so that we don't give paComplete too soon to fill up the // desired length of recording mT1 -= mRecordingSchedule.mLatencyCorrection; @@ -1951,10 +1947,10 @@ int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks, mTime = t0; mSeek = 0; mLastRecordingOffset = 0; - mCaptureTracks = captureTracks; - mPlaybackTracks = playbackTracks; + mCaptureTracks = tracks.captureTracks; + mPlaybackTracks = tracks.playbackTracks; #ifdef EXPERIMENTAL_MIDI_OUT - mMidiPlaybackTracks = midiPlaybackTracks; + mMidiPlaybackTracks = tracks.midiTracks; #endif bool commit = false; @@ -2057,9 +2053,9 @@ int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks, unsigned int captureChannels = 0; sampleFormat captureFormat = floatSample; - if (playbackTracks.size() > 0 + if (tracks.playbackTracks.size() > 0 #ifdef EXPERIMENTAL_MIDI_OUT - || midiPlaybackTracks.size() > 0 + || tracks.midiTracks.size() > 0 #endif ) playbackChannels = 2; @@ -2067,7 +2063,7 @@ int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks, if (mSoftwarePlaythrough) playbackChannels = 2; - if( captureTracks.size() > 0 ) + if (tracks.captureTracks.size() > 0) { // For capture, every input channel gets its own track captureChannels = mCaptureTracks.size(); @@ -2424,13 +2420,15 @@ void AudioIO::PrepareMidiIterator(bool send, double offset) mIterator = std::make_unique(nullptr, false); // Iterator not yet intialized, must add each track... for (i = 0; i < nTracks; i++) { - NoteTrack *t = mMidiPlaybackTracks[i].get(); + const auto t = mMidiPlaybackTracks[i].get(); Alg_seq_ptr seq = &t->GetSeq(); // mark sequence tracks as "in use" since we're handing this // off to another thread and want to make sure nothing happens // to the data until playback finishes. This is just a sanity check. seq->set_in_use(true); - mIterator->begin_seq(seq, t, t->GetOffset() + offset); + mIterator->begin_seq(seq, + // casting away const, but allegro just uses the pointer as an opaque "cookie" + (void*)t, t->GetOffset() + offset); } GetNextEvent(); // prime the pump for FillMidiBuffers @@ -2686,7 +2684,7 @@ void AudioIO::StopStream() // set in_use flags to false int nTracks = mMidiPlaybackTracks.size(); for (int i = 0; i < nTracks; i++) { - NoteTrack *t = mMidiPlaybackTracks[i].get(); + const auto t = mMidiPlaybackTracks[i].get(); Alg_seq_ptr seq = &t->GetSeq(); seq->set_in_use(false); } diff --git a/src/AudioIO.h b/src/AudioIO.h index fddc0ff63..bb2d8a450 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -35,6 +35,7 @@ class NoteTrack; using NoteTrackArray = std::vector < std::shared_ptr< NoteTrack > >; +using NoteTrackConstArray = std::vector < std::shared_ptr< const NoteTrack > >; #endif // EXPERIMENTAL_MIDI_OUT @@ -140,6 +141,14 @@ struct AudioIOStartStreamOptions #endif }; +struct TransportTracks { + WaveTrackConstArray playbackTracks; + WaveTrackArray captureTracks; +#ifdef EXPERIMENTAL_MIDI_OUT + NoteTrackConstArray midiTracks; +#endif +}; + // This workaround makes pause and stop work when output is to GarageBand, // which seems not to implement the notes-off message correctly. #define AUDIO_IO_GB_MIDI_WORKAROUND @@ -169,10 +178,7 @@ class AUDACITY_DLL_API AudioIO final { * If successful, returns a token identifying this particular stream * instance. For use with IsStreamActive() below */ - int StartStream(const WaveTrackConstArray &playbackTracks, const WaveTrackArray &captureTracks, -#ifdef EXPERIMENTAL_MIDI_OUT - const NoteTrackArray &midiTracks, -#endif + int StartStream(const TransportTracks &tracks, double t0, double t1, const AudioIOStartStreamOptions &options); @@ -611,7 +617,7 @@ private: /// when true, mSendMidiState means send only updates, not note-on's, /// used to send state changes that precede the selected notes bool mSendMidiState; - NoteTrackArray mMidiPlaybackTracks; + NoteTrackConstArray mMidiPlaybackTracks; #endif #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT diff --git a/src/Track.cpp b/src/Track.cpp index eb59fab95..64c07ceac 100644 --- a/src/Track.cpp +++ b/src/Track.cpp @@ -1354,14 +1354,14 @@ WaveTrackConstArray TrackList::GetWaveTrackConstArray(bool selectionOnly, bool i } #if defined(USE_MIDI) -NoteTrackArray TrackList::GetNoteTrackArray(bool selectionOnly) +NoteTrackConstArray TrackList::GetNoteTrackConstArray(bool selectionOnly) const { - NoteTrackArray noteTrackArray; + NoteTrackConstArray noteTrackArray; for(const auto &track : *this) { if (track->GetKind() == Track::Note && (track->GetSelected() || !selectionOnly)) { - noteTrackArray.push_back( Track::Pointer(track) ); + noteTrackArray.push_back( Track::Pointer(track) ); } } @@ -1573,3 +1573,17 @@ bool TrackList::HasPendingTracks() const return true; return false; } + +#include "AudioIO.h" +TransportTracks GetAllPlaybackTracks(const TrackList &trackList, bool selectedOnly, bool useMidi) +{ + TransportTracks result; + result.playbackTracks = trackList.GetWaveTrackConstArray(selectedOnly); +#ifdef EXPERIMENTAL_MIDI_OUT + if (useMidi) + result.midiTracks = trackList.GetNoteTrackConstArray(selectedOnly); +#else + WXUNUSED(useMidi); +#endif + return result; +} diff --git a/src/Track.h b/src/Track.h index 2f28843e5..f04220309 100644 --- a/src/Track.h +++ b/src/Track.h @@ -50,7 +50,8 @@ class TimeShiftHandle; WX_DEFINE_USER_EXPORTED_ARRAY(Track*, TrackArray, class AUDACITY_DLL_API); using WaveTrackArray = std::vector < std::shared_ptr< WaveTrack > > ; using WaveTrackConstArray = std::vector < std::shared_ptr < const WaveTrack > >; -using NoteTrackArray = std::vector < std::shared_ptr < NoteTrack > >; + +using NoteTrackConstArray = std::vector < std::shared_ptr< const NoteTrack > >; #if defined(USE_MIDI) class NoteTrack; @@ -721,7 +722,7 @@ class TrackList final : public wxEvtHandler, public ListOfTracks WaveTrackConstArray GetWaveTrackConstArray(bool selectionOnly, bool includeMuted = true) const; #if defined(USE_MIDI) - NoteTrackArray GetNoteTrackArray(bool selectionOnly); + NoteTrackConstArray GetNoteTrackConstArray(bool selectionOnly) const; #endif /// Mainly a test function. Uses a linear search, so could be slow. @@ -886,4 +887,8 @@ class AUDACITY_DLL_API TrackFactory #endif }; +// global functions +struct TransportTracks; +TransportTracks GetAllPlaybackTracks(const TrackList &trackList, bool selectedOnly, bool useMidi = false); + #endif diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index acb2d5fea..d5c60d413 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -2602,30 +2602,16 @@ void Effect::Preview(bool dryOnly) if (success) { - WaveTrackConstArray playbackTracks; - WaveTrackArray recordingTracks; + auto tracks = GetAllPlaybackTracks(*mTracks, true); - SelectedTrackListOfKindIterator iter(Track::Wave, mTracks); - WaveTrack *src = (WaveTrack *) iter.First(); - while (src) { - playbackTracks.push_back(Track::Pointer(src)); - src = (WaveTrack *) iter.Next(); - } // Some effects (Paulstretch) may need to generate more // than previewLen, so take the min. t1 = std::min(mT0 + previewLen, mT1); -#ifdef EXPERIMENTAL_MIDI_OUT - NoteTrackArray empty; -#endif // Start audio playing AudioIOStartStreamOptions options { rate }; int token = - gAudioIO->StartStream(playbackTracks, recordingTracks, -#ifdef EXPERIMENTAL_MIDI_OUT - empty, -#endif - mT0, t1, options); + gAudioIO->StartStream(tracks, mT0, t1, options); if (token) { auto previewing = ProgressResult::Success; diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index d9969d3b0..eb5e7b029 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -686,13 +686,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, myOptions.cutPreviewGapStart = t0; myOptions.cutPreviewGapLen = t1 - t0; token = gAudioIO->StartStream( - mCutPreviewTracks->GetWaveTrackConstArray(false), - WaveTrackArray(), -#ifdef EXPERIMENTAL_MIDI_OUT - useMidi - ? mCutPreviewTracks->GetNoteTrackArray(false) - : NoteTrackArray(), -#endif + GetAllPlaybackTracks(*mCutPreviewTracks, false, useMidi), tcp0, tcp1, myOptions); } else @@ -706,14 +700,9 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, timetrack = t->GetTimeTrack(); } */ - token = gAudioIO->StartStream(t->GetWaveTrackConstArray(false), - WaveTrackArray(), -#ifdef EXPERIMENTAL_MIDI_OUT - useMidi - ? t->GetNoteTrackArray(false) - : NoteTrackArray(), -#endif - t0, t1, options); + token = gAudioIO->StartStream( + GetAllPlaybackTracks(*t, false, useMidi), + t0, t1, options); } if (token != 0) { success = true; @@ -1079,22 +1068,29 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt) } } - success = DoRecord(*p, existingTracks, t0, t1); + TransportTracks transportTracks; + if (UseDuplex()) { + // Remove recording tracks from the list of tracks for duplex ("overdub") + // playback. + /* TODO: set up stereo tracks if that is how the user has set up + * their preferences, and choose sample format based on prefs */ + transportTracks = GetAllPlaybackTracks(*p->GetTracks(), false, true); + for (const auto &wt : existingTracks) { + auto end = transportTracks.playbackTracks.end(); + auto it = std::find(transportTracks.playbackTracks.begin(), end, wt); + if (it != end) + transportTracks.playbackTracks.erase(it); + } + } + + transportTracks.captureTracks = existingTracks; + AudioIOStartStreamOptions options(p->GetDefaultPlayOptions()); + success = DoRecord(*p, transportTracks, t0, t1, options); } } -bool ControlToolBar::DoRecord(AudacityProject &project, WaveTrackArray &existingTracks, double t0, double t1) +bool ControlToolBar::UseDuplex() { - const auto p = &project; - bool success = false; - - /* TODO: set up stereo tracks if that is how the user has set up - * their preferences, and choose sample format based on prefs */ - WaveTrackConstArray playbackTracks; -#ifdef EXPERIMENTAL_MIDI_OUT - NoteTrackArray midiTracks; -#endif - bool duplex; gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, #ifdef EXPERIMENTAL_DA @@ -1103,39 +1099,30 @@ bool ControlToolBar::DoRecord(AudacityProject &project, WaveTrackArray &existing true #endif ); + return duplex; +} - if (duplex){ - TrackList *trackList = p->GetTracks(); - playbackTracks = trackList->GetWaveTrackConstArray(false); -#ifdef EXPERIMENTAL_MIDI_OUT - midiTracks = trackList->GetNoteTrackArray(false); -#endif - } - else { - playbackTracks = WaveTrackConstArray(); -#ifdef EXPERIMENTAL_MIDI_OUT - midiTracks = NoteTrackArray(); -#endif - } +bool ControlToolBar::DoRecord(AudacityProject &project, + const TransportTracks &tracks, + double t0, double t1, + const AudioIOStartStreamOptions &options) +{ + auto transportTracks = tracks; - bool appendRecord = !existingTracks.empty(); + // Will replace any given capture tracks with temporaries + transportTracks.captureTracks.clear(); + + const auto p = &project; + bool success = false; + + bool appendRecord = !tracks.captureTracks.empty(); { - WaveTrackArray recordingTracks; - if (appendRecord) { // Append recording: // 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 (const auto &wt : existingTracks) + for (const auto &wt : tracks.captureTracks) { - if (duplex) { - auto end = playbackTracks.end(); - auto it = std::find(playbackTracks.begin(), end, wt); - if (it != end) - playbackTracks.erase(it); - } t1 = wt->GetEndTime(); // A function that copies all the non-sample data between @@ -1167,7 +1154,7 @@ bool ControlToolBar::DoRecord(AudacityProject &project, WaveTrackArray &existing pending->Clear(t1, t0); pending->Paste(t1, newTrack.get()); } - recordingTracks.push_back(pending); + transportTracks.captureTracks.push_back(pending); } if (t1 <= p->GetSel0() && p->GetSel1() > p->GetSel0()) { @@ -1177,7 +1164,7 @@ bool ControlToolBar::DoRecord(AudacityProject &project, WaveTrackArray &existing } } - if( recordingTracks.empty() ) + if( transportTracks.captureTracks.empty() ) { // recording to NEW track(s). bool recordingNameCustom, useTrackNumber, useDateStamp, useTimeStamp; wxString defaultTrackName, defaultRecordingTrackName; @@ -1268,7 +1255,7 @@ bool ControlToolBar::DoRecord(AudacityProject &project, WaveTrackArray &existing } p->GetTracks()->RegisterPendingNewTrack( newTrack ); - recordingTracks.push_back( newTrack ); + transportTracks.captureTracks.push_back(newTrack); // Bug 1548. New track needs the focus. p->GetTrackPanel()->SetFocusedTrack( newTrack.get() ); } @@ -1279,13 +1266,7 @@ bool ControlToolBar::DoRecord(AudacityProject &project, WaveTrackArray &existing gAudioIO->AILAInitialize(); #endif - AudioIOStartStreamOptions options(p->GetDefaultPlayOptions()); - int token = gAudioIO->StartStream(playbackTracks, - recordingTracks, -#ifdef EXPERIMENTAL_MIDI_OUT - midiTracks, -#endif - t0, t1, options); + int token = gAudioIO->StartStream(transportTracks, t0, t1, options); success = (token != 0); diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 33c2ab683..4daef123b 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -41,6 +41,8 @@ enum class PlayMode : int; class WaveTrack; using WaveTrackArray = std::vector < std::shared_ptr < WaveTrack > >; +struct TransportTracks; + // In the GUI, ControlToolBar appears as the "Transport Toolbar". "Control Toolbar" is historic. class ControlToolBar final : public ToolBar { @@ -55,7 +57,9 @@ class ControlToolBar final : public ToolBar { void OnKeyEvent(wxKeyEvent & event); // Find suitable tracks to record into, or return an empty array. - WaveTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly); + static WaveTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly); + + static bool UseDuplex(); // msmeyer: These are public, but it's far better to // call the "real" interface functions like PlayCurrentRegion() and @@ -64,7 +68,10 @@ class ControlToolBar final : public ToolBar { void OnPlay(wxCommandEvent & evt); void OnStop(wxCommandEvent & evt); void OnRecord(wxCommandEvent & evt); - bool DoRecord(AudacityProject &project, WaveTrackArray &existingTracks, double t0, double t1); + bool DoRecord(AudacityProject &project, + const TransportTracks &transportTracks, // If captureTracks is empty, then tracks are created + double t0, double t1, + const AudioIOStartStreamOptions &options); void OnFF(wxCommandEvent & evt); void OnPause(wxCommandEvent & evt);