1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Group arguments to StartStream, and a bit less of #ifdef for MIDI play...

... And pull choice of tracks and options out of DoRecord
This commit is contained in:
Paul Licameli 2018-05-28 20:44:37 -04:00
parent 0635f8802b
commit 35a97e09e7
7 changed files with 103 additions and 106 deletions

View File

@ -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<Alg_iterator>(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);
}

View File

@ -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

View File

@ -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<NoteTrack>(track) );
noteTrackArray.push_back( Track::Pointer<const NoteTrack>(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;
}

View File

@ -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

View File

@ -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<WaveTrack>(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;

View File

@ -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);

View File

@ -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);