diff --git a/src/Menus.cpp b/src/Menus.cpp index b6ce4069c..9d3b12f90 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -7819,6 +7819,7 @@ void MenuCommandHandler::HandleMixAndRender WaveTrack::Holder uNewLeft, uNewRight; ::MixAndRender( tracks, trackFactory, rate, defaultFormat, 0.0, 0.0, uNewLeft, uNewRight); + tracks->GroupChannels(*uNewLeft, uNewRight ? 2 : 1); if (uNewLeft) { // Remove originals, get stats on what tracks were mixed @@ -8769,22 +8770,20 @@ void MenuCommandHandler::OnNewStereoTrack(const CommandContext &context) auto defaultFormat = project.GetDefaultFormat(); auto rate = project.GetRate(); - auto t = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate)); - t->SetChannel(Track::LeftChannel); project.SelectNone(); - t->SetSelected(true); - t->SetLinked (true); + auto left = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate)); + left->SetSelected(true); - t = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate)); - t->SetChannel(Track::RightChannel); + auto right = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate)); + right->SetSelected(true); - t->SetSelected(true); + tracks->GroupChannels(*left, 2); project.PushState(_("Created new stereo audio track"), _("New Track")); project.RedrawProject(); - trackPanel->EnsureVisible(t); + trackPanel->EnsureVisible(left); } void MenuCommandHandler::OnNewLabelTrack(const CommandContext &context) diff --git a/src/Mix.cpp b/src/Mix.cpp index 49b622164..1da6fc84a 100644 --- a/src/Mix.cpp +++ b/src/Mix.cpp @@ -120,10 +120,7 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory, // TODO: more-than-two-channels decltype(mixLeft) mixRight{}; - if (mono) { - mixLeft->SetChannel(Track::MonoChannel); - } - else { + if ( !mono ) { mixRight = trackFactory->NewWaveTrack(format, rate); if (oneinput) { auto channels = TrackList::Channels(first); @@ -134,14 +131,10 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory, } else mixRight->SetName(_("Mix")); - mixLeft->SetChannel(Track::LeftChannel); - mixRight->SetChannel(Track::RightChannel); mixRight->SetOffset(mixStartTime); - mixLeft->SetLinked(true); } - auto maxBlockLen = mixLeft->GetIdealBlockSize(); // If the caller didn't specify a time range, use the whole range in which diff --git a/src/Project.cpp b/src/Project.cpp index 16cb98efb..637dc7274 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -4168,7 +4168,6 @@ AudacityProject::AddImportedTracks(const wxString &fileName, { std::vector< std::shared_ptr< Track > > results; - const auto numTracks = newTracks.size(); SelectNone(); bool initiallyEmpty = mTracks->empty(); @@ -4177,9 +4176,18 @@ AudacityProject::AddImportedTracks(const wxString &fileName, int i = -1; // Must add all tracks first (before using Track::IsLeader) - for (auto &uNewTrack : newTracks) { - auto newTrack = mTracks->Add(std::move(uNewTrack)); - results.push_back(Track::Pointer(newTrack)); + for (auto &group : newTracks) { + if (group.empty()) { + wxASSERT(false); + continue; + } + auto first = group.begin()->get(); + auto nChannels = group.size(); + for (auto &uNewTrack : group) { + auto newTrack = mTracks->Add(std::move(uNewTrack)); + results.push_back(Track::Pointer(newTrack)); + } + mTracks->GroupChannels(*first, nChannels); } newTracks.clear(); diff --git a/src/Track.cpp b/src/Track.cpp index 674df9757..6e91606d1 100644 --- a/src/Track.cpp +++ b/src/Track.cpp @@ -38,6 +38,7 @@ and TimeTrack. #include "DirManager.h" #include "Experimental.h" +#include "InconsistencyException.h" #include "TrackPanel.h" // for TrackInfo @@ -724,6 +725,58 @@ Track *TrackList::Add(std::shared_ptr &&t) template Track *TrackList::Add(std::shared_ptr &&); template Track *TrackList::Add(std::shared_ptr &&); +void TrackList::GroupChannels( + Track &track, size_t groupSize, bool resetChannels ) +{ + // If group size is more than two, for now only the first two channels + // are grouped as stereo, and any others remain mono + auto list = track.mList.lock(); + if ( groupSize > 0 && list.get() == this ) { + auto iter = track.mNode.first; + auto after = iter; + auto end = this->ListOfTracks::end(); + auto count = groupSize; + for ( ; after != end && count--; ++after ) + ; + if ( count == 0 ) { + auto unlink = [&] ( Track &tr ) { + if ( tr.GetLinked() ) { + if ( resetChannels ) { + auto link = tr.GetLink(); + if ( link ) + link->SetChannel( Track::MonoChannel ); + } + tr.SetLinked( false ); + } + if ( resetChannels ) + tr.SetChannel( Track::MonoChannel ); + }; + + // Disassociate previous tracks -- at most one + auto pLeader = this->FindLeader( &track ); + if ( *pLeader && *pLeader != &track ) + unlink( **pLeader ); + + // First disassociate given and later tracks, then reassociate them + for ( auto iter2 = iter; iter2 != after; ++iter2 ) + unlink( **iter2 ); + + if ( groupSize > 1 ) { + const auto channel = *iter++; + channel->SetLinked( true ); + channel->SetChannel( Track::LeftChannel ); + (*iter++)->SetChannel( Track::RightChannel ); + while (iter != after) + (*iter++)->SetChannel( Track::MonoChannel ); + } + return; + } + } + // *this does not contain the track or sufficient following channels + // or group size is zero + throw InconsistencyException{}; +} + auto TrackList::Replace(Track * t, ListOfTracks::value_type &&with) -> ListOfTracks::value_type { diff --git a/src/Track.h b/src/Track.h index 3e2c6078c..03af7aaf9 100644 --- a/src/Track.h +++ b/src/Track.h @@ -327,8 +327,11 @@ private: Track *GetLink() const; bool GetLinked () const { return mLinked; } -public: + + friend WaveTrack; // WaveTrack needs to call SetLinked when reloading project void SetLinked (bool l); + + void SetChannel(ChannelType c) { mChannel = c; } private: // No need yet to make this virtual void DoSetLinked(bool l); @@ -380,7 +383,6 @@ public: void Offset(double t) { SetOffset(GetOffset() + t); } virtual void SetOffset (double o) { mOffset = o; } - void SetChannel(ChannelType c) { mChannel = c; } virtual void SetPan( float ){ ;} virtual void SetPanFromChannelType(){ ;}; @@ -1318,6 +1320,16 @@ public: template Track *Add(std::shared_ptr &&t); + /** \brief Define a group of channels starting at the given track + * + * @param track and (groupSize - 1) following tracks must be in this + * list. They will be disassociated from any groups they already belong to. + * @param groupSize must be at least 1. + * @param resetChannels if true, disassociated channels will be marked Mono. + */ + void GroupChannels( + Track &track, size_t groupSize, bool resetChannels = true ); + /// Replace first track with second track, give back a holder /// Give the replacement the same id as the replaced ListOfTracks::value_type Replace(Track * t, ListOfTracks::value_type &&with); diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index f340989ca..068e27e99 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -2543,6 +2543,7 @@ void Effect::Preview(bool dryOnly) mixRight->SetSelected(true); mTracks->Add(std::move(mixRight)); } + mTracks->GroupChannels(*mixLeft, mixRight ? 2 : 1); } else { for (auto src : saveTracks->Any< const WaveTrack >()) { diff --git a/src/effects/StereoToMono.cpp b/src/effects/StereoToMono.cpp index 3cff69ae9..c7397e12d 100644 --- a/src/effects/StereoToMono.cpp +++ b/src/effects/StereoToMono.cpp @@ -157,9 +157,7 @@ bool EffectStereoToMono::ProcessOne(int count) mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime()); outTrack->Flush(); mLeftTrack->Paste(minStart, outTrack.get()); - mLeftTrack->SetLinked(false); - mRightTrack->SetLinked(false); - mLeftTrack->SetChannel(Track::MonoChannel); + mOutputTracks->GroupChannels( *mLeftTrack, 1 ); mOutputTracks->Remove(mRightTrack); return bResult; diff --git a/src/import/Import.cpp b/src/import/Import.cpp index 53ad404a0..b0ef1b27f 100644 --- a/src/import/Import.cpp +++ b/src/import/Import.cpp @@ -49,6 +49,7 @@ and ImportLOF.cpp. #include #include "../ShuttleGui.h" #include "../Project.h" +#include "../WaveTrack.h" #include "ImportPCM.h" #include "ImportMP3.h" @@ -538,6 +539,15 @@ bool Importer::Import(const wxString &fName, return true; } + auto end = tracks.end(); + auto iter = std::remove_if( tracks.begin(), end, + std::mem_fn( &NewChannelGroup::empty ) ); + if ( iter != end ) { + // importer shouldn't give us empty groups of channels! + wxASSERT(false); + // But correct that and proceed anyway + tracks.erase( iter, end ); + } if (tracks.size() > 0) { // success! diff --git a/src/import/ImportFFmpeg.cpp b/src/import/ImportFFmpeg.cpp index 2b0c820bf..a5aaf8923 100644 --- a/src/import/ImportFFmpeg.cpp +++ b/src/import/ImportFFmpeg.cpp @@ -274,7 +274,10 @@ private: bool mCancelled; //!< True if importing was canceled by user bool mStopped; //!< True if importing was stopped by user wxString mName; - std::list mChannels; //!< 2-dimentional array of WaveTrack's. First dimention - streams, second - channels of a stream. Length is mNumStreams + TrackHolders mChannels; //!< 2-dimensional array of WaveTrack's. + //!< First dimension - streams, + //!< second - channels of a stream. + //!< Length is mNumStreams #ifdef EXPERIMENTAL_OD_FFMPEG bool mUsingOD; #endif @@ -520,31 +523,8 @@ ProgressResult FFmpegImportFileHandle::Import(TrackFactory *trackFactory, // There is a possibility that number of channels will change over time, but we do not have WaveTracks for NEW channels. Remember the number of channels and stick to it. sc->m_initialchannels = sc->m_stream->codec->channels; stream.resize(sc->m_stream->codec->channels); - int c = -1; for (auto &channel : stream) - { - ++c; - channel = trackFactory->NewWaveTrack(sc->m_osamplefmt, sc->m_stream->codec->sample_rate); - - if (sc->m_stream->codec->channels == 2) - { - switch (c) - { - case 0: - channel->SetChannel(Track::LeftChannel); - channel->SetLinked(true); - break; - case 1: - channel->SetChannel(Track::RightChannel); - break; - } - } - else - { - channel->SetChannel(Track::MonoChannel); - } - } } // Handles the start_time by creating silence. This may or may not be correct. @@ -688,13 +668,10 @@ ProgressResult FFmpegImportFileHandle::Import(TrackFactory *trackFactory, // Copy audio from mChannels to newly created tracks (destroying mChannels elements in process) for (auto &stream : mChannels) - { for(auto &channel : stream) - { channel->Flush(); - outTracks.push_back(std::move(channel)); - } - } + + outTracks.swap(mChannels); // Save metadata WriteMetadata(tags); diff --git a/src/import/ImportFLAC.cpp b/src/import/ImportFLAC.cpp index db92be087..739d56366 100644 --- a/src/import/ImportFLAC.cpp +++ b/src/import/ImportFLAC.cpp @@ -181,7 +181,7 @@ private: FLAC__uint64 mSamplesDone; bool mStreamInfoDone; ProgressResult mUpdateResult; - TrackHolders mChannels; + NewChannelGroup mChannels; std::unique_ptr mDecoderTask; }; @@ -455,25 +455,9 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory, mChannels.resize(mNumChannels); auto iter = mChannels.begin(); - for (size_t c = 0; c < mNumChannels; ++iter, ++c) { + for (size_t c = 0; c < mNumChannels; ++iter, ++c) *iter = trackFactory->NewWaveTrack(mFormat, mSampleRate); - if (mNumChannels == 2) { - switch (c) { - case 0: - iter->get()->SetChannel(Track::LeftChannel); - iter->get()->SetLinked(true); - break; - case 1: - iter->get()->SetChannel(Track::RightChannel); - break; - } - } - else { - iter->get()->SetChannel(Track::MonoChannel); - } - } - //Start OD bool useOD = false; @@ -536,10 +520,11 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory, return mUpdateResult; } - for (const auto &channel : mChannels) { + for (const auto &channel : mChannels) channel->Flush(); - } - outTracks.swap(mChannels); + + if (!mChannels.empty()) + outTracks.push_back(std::move(mChannels)); tags->Clear(); size_t cnt = mFile->mComments.GetCount(); diff --git a/src/import/ImportGStreamer.cpp b/src/import/ImportGStreamer.cpp index e3dd8f6c6..0a3a14110 100644 --- a/src/import/ImportGStreamer.cpp +++ b/src/import/ImportGStreamer.cpp @@ -130,7 +130,7 @@ struct GStreamContext GstElement *mConv{}; // Audio converter GstElement *mSink{}; // Application sink bool mUse{}; // True if this stream should be imported - TrackHolders mChannels; // Array of WaveTrack pointers, one for each channel + NewChannelGroup mChannels; // Array of WaveTrack pointers, one for each channel unsigned mNumChannels{}; // Number of channels gdouble mSampleRate{}; // Sample rate GstString mType; // Audio type @@ -789,14 +789,6 @@ GStreamerImportFileHandle::OnNewSample(GStreamContext *c, GstSample *sample) return; } } - - // Set to stereo if there's exactly 2 channels - if (c->mNumChannels == 2) - { - c->mChannels[0]->SetChannel(Track::LeftChannel); - c->mChannels[1]->SetChannel(Track::RightChannel); - c->mChannels[0]->SetLinked(true); - } } // Get the buffer for the sample...no need to release @@ -1140,18 +1132,6 @@ GStreamerImportFileHandle::Import(TrackFactory *trackFactory, // Grab the streams lock g_mutex_locker locker{ mStreamsLock }; - // Count the total number of tracks collected - unsigned outNumTracks = 0; - for (guint s = 0; s < mStreams.size(); s++) - { - GStreamContext *c = mStreams[s].get(); - if (c) - outNumTracks += c->mNumChannels; - } - - // Create NEW tracks - outTracks.resize(outNumTracks); - // Copy audio from mChannels to newly created tracks (destroying mChannels in process) int trackindex = 0; for (guint s = 0; s < mStreams.size(); s++) @@ -1160,12 +1140,8 @@ GStreamerImportFileHandle::Import(TrackFactory *trackFactory, if (c->mNumChannels) { for (int ch = 0; ch < c->mNumChannels; ch++) - { c->mChannels[ch]->Flush(); - outTracks[trackindex++] = std::move(c->mChannels[ch]); - } - - c->mChannels.clear(); + outTracks.push_back(std::move(c->mChannels)); } } diff --git a/src/import/ImportLOF.cpp b/src/import/ImportLOF.cpp index 2edd8d5e4..e33b9c74b 100644 --- a/src/import/ImportLOF.cpp +++ b/src/import/ImportLOF.cpp @@ -223,8 +223,9 @@ auto LOFImportFileHandle::GetFileUncompressedBytes() -> ByteCount return 0; } -ProgressResult LOFImportFileHandle::Import(TrackFactory * WXUNUSED(trackFactory), TrackHolders &outTracks, - Tags * WXUNUSED(tags)) +ProgressResult LOFImportFileHandle::Import( + TrackFactory * WXUNUSED(trackFactory), TrackHolders &outTracks, + Tags * WXUNUSED(tags)) { // Unlike other ImportFileHandle subclasses, this one never gives any tracks // back to the caller. diff --git a/src/import/ImportMP3.cpp b/src/import/ImportMP3.cpp index 7726a9a1e..1feb77fb9 100644 --- a/src/import/ImportMP3.cpp +++ b/src/import/ImportMP3.cpp @@ -98,7 +98,7 @@ struct private_data { ArrayOf inputBuffer{ static_cast(INPUT_BUFFER_SIZE) }; int inputBufferFill; /* amount of data in inputBuffer */ TrackFactory *trackFactory; - TrackHolders channels; + NewChannelGroup channels; ProgressDialog *progress; unsigned numChannels; ProgressResult updateResult; @@ -208,8 +208,9 @@ auto MP3ImportFileHandle::GetFileUncompressedBytes() -> ByteCount return 0; } -ProgressResult MP3ImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks, - Tags *tags) +ProgressResult MP3ImportFileHandle::Import( + TrackFactory *trackFactory, TrackHolders &outTracks, + Tags *tags) { outTracks.clear(); @@ -252,7 +253,8 @@ ProgressResult MP3ImportFileHandle::Import(TrackFactory *trackFactory, TrackHold for(const auto &channel : privateData.channels) { channel->Flush(); } - outTracks.swap(privateData.channels); + if (!privateData.channels.empty()) + outTracks.push_back(std::move(privateData.channels)); /* Read in any metadata */ ImportID3(tags); @@ -501,17 +503,9 @@ enum mad_flow output_cb(void *_data, auto format = QualityPrefs::SampleFormatChoice(); - for(auto &channel: data->channels) { + for(auto &channel: data->channels) channel = data->trackFactory->NewWaveTrack(format, samplerate); - channel->SetChannel(Track::MonoChannel); - } - /* special case: 2 channels is understood to be stereo */ - if(channels == 2) { - data->channels.begin()->get()->SetChannel(Track::LeftChannel); - data->channels.rbegin()->get()->SetChannel(Track::RightChannel); - data->channels.begin()->get()->SetLinked(true); - } data->numChannels = channels; } else { diff --git a/src/import/ImportOGG.cpp b/src/import/ImportOGG.cpp index f2a7b9179..e51c542ea 100644 --- a/src/import/ImportOGG.cpp +++ b/src/import/ImportOGG.cpp @@ -155,7 +155,7 @@ private: ArrayOf mStreamUsage; wxArrayString mStreamInfo; - std::list mChannels; + std::list mChannels; sampleFormat mFormat; }; @@ -228,7 +228,8 @@ auto OggImportFileHandle::GetFileUncompressedBytes() -> ByteCount return 0; } -ProgressResult OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks, +ProgressResult OggImportFileHandle::Import( + TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) { outTracks.clear(); @@ -258,27 +259,8 @@ ProgressResult OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHold link.resize(vi->channels); - int c = -1; - for (auto &channel : link) { - ++c; - + for (auto &channel : link) channel = trackFactory->NewWaveTrack(mFormat, vi->rate); - - if (vi->channels == 2) { - switch (c) { - case 0: - channel->SetChannel(Track::LeftChannel); - channel->SetLinked(true); - break; - case 1: - channel->SetChannel(Track::RightChannel); - break; - } - } - else { - channel->SetChannel(Track::MonoChannel); - } - } } /* The number of bytes to get from the codec in each run */ @@ -374,10 +356,9 @@ ProgressResult OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHold for (auto &link : mChannels) { - for (auto &channel : link) { + for (auto &channel : link) channel->Flush(); - outTracks.push_back(std::move(channel)); - } + outTracks.push_back(std::move(link)); } //\todo { Extract comments from each stream? } diff --git a/src/import/ImportPCM.cpp b/src/import/ImportPCM.cpp index 19762b6de..68de4ef90 100644 --- a/src/import/ImportPCM.cpp +++ b/src/import/ImportPCM.cpp @@ -364,29 +364,12 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory, CreateProgress(); - TrackHolders channels(mInfo.channels); + NewChannelGroup channels(mInfo.channels); auto iter = channels.begin(); - for (int c = 0; c < mInfo.channels; ++iter, ++c) { + for (int c = 0; c < mInfo.channels; ++iter, ++c) *iter = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate); - if (mInfo.channels > 1) - switch (c) { - case 0: - iter->get()->SetChannel(Track::LeftChannel); - break; - case 1: - iter->get()->SetChannel(Track::RightChannel); - break; - default: - iter->get()->SetChannel(Track::MonoChannel); - } - } - - if (mInfo.channels == 2) { - channels.begin()->get()->SetLinked(true); - } - auto fileTotalFrames = (sampleCount)mInfo.frames; // convert from sf_count_t auto maxBlockSize = channels.begin()->get()->GetMaxBlockSize(); @@ -529,10 +512,11 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory, return updateResult; } - for(const auto &channel : channels) { + for(const auto &channel : channels) channel->Flush(); - } - outTracks.swap(channels); + + if (!channels.empty()) + outTracks.push_back(std::move(channels)); const char *str; diff --git a/src/import/ImportPlugin.h b/src/import/ImportPlugin.h index 386d79c49..b9141275b 100644 --- a/src/import/ImportPlugin.h +++ b/src/import/ImportPlugin.h @@ -157,6 +157,8 @@ public: // The given Tags structure may also be modified. // In case of errors or exceptions, it is not necessary to leave outTracks // or tags unmodified. + // If resulting outTracks is not empty, + // then each member of it must be a nonempty vector. virtual ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) = 0; diff --git a/src/import/ImportQT.cpp b/src/import/ImportQT.cpp index badc5a193..34171ccd3 100644 --- a/src/import/ImportQT.cpp +++ b/src/import/ImportQT.cpp @@ -336,7 +336,7 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory, (sizeof(AudioBuffer) * numchan))) }; abl->mNumberBuffers = numchan; - TrackHolders channels{ numchan }; + NewChannelGroup channels{ numchan }; const auto size = sizeof(float) * bufsize; ArraysOf holders{ numchan, size }; @@ -352,16 +352,6 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory, channel = trackFactory->NewWaveTrack( format ); channel->SetRate( desc.mSampleRate ); - - if (numchan == 2) { - if (c == 0) { - channel->SetChannel(Track::LeftChannel); - channel->SetLinked(true); - } - else if (c == 1) { - channel->SetChannel(Track::RightChannel); - } - } } do { @@ -395,11 +385,10 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory, res = (updateResult == ProgressResult::Success && err == noErr); if (res) { - for (const auto &channel: channels) { + for (auto &channel: channels) channel->Flush(); - } - - outTracks.swap(channels); + if (!channels.empty()) + outTracks.push_back(std::move(channels)); } // diff --git a/src/import/ImportRaw.cpp b/src/import/ImportRaw.cpp index bd44214a5..3c52a3c8d 100644 --- a/src/import/ImportRaw.cpp +++ b/src/import/ImportRaw.cpp @@ -107,7 +107,7 @@ void ImportRaw(wxWindow *parent, const wxString &fileName, sf_count_t offset = 0; double rate = 44100.0; double percent = 100.0; - TrackHolders channels; + TrackHolders results; auto updateResult = ProgressResult::Success; { @@ -200,31 +200,15 @@ void ImportRaw(wxWindow *parent, const wxString &fileName, sf_subtype_more_than_16_bits(encoding)) format = floatSample; + results.resize(1); + auto &channels = results[0]; channels.resize(numChannels); auto iter = channels.begin(); - for (decltype(numChannels) c = 0; c < numChannels; ++iter, ++c) { - const auto channel = - (*iter = trackFactory->NewWaveTrack(format, rate)).get(); - - if (numChannels > 1) - switch (c) { - case 0: - channel->SetChannel(Track::LeftChannel); - break; - case 1: - channel->SetChannel(Track::RightChannel); - break; - default: - channel->SetChannel(Track::MonoChannel); - } - } + for (decltype(numChannels) c = 0; c < numChannels; ++iter, ++c) + *iter = trackFactory->NewWaveTrack(format, rate); const auto firstChannel = channels.begin()->get(); - if (numChannels == 2) { - firstChannel->SetLinked(true); - } - auto maxBlockSize = firstChannel->GetMaxBlockSize(); SampleBuffer srcbuffer(maxBlockSize * numChannels, format); @@ -295,9 +279,11 @@ void ImportRaw(wxWindow *parent, const wxString &fileName, if (updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled) throw UserException{}; - for (const auto &channel : channels) - channel->Flush(); - outTracks.swap(channels); + if (!results.empty() && !results[0].empty()) { + for (const auto &channel : results[0]) + channel->Flush(); + outTracks.swap(results); + } } // diff --git a/src/import/ImportRaw.h b/src/import/ImportRaw.h index a42375e49..f65f88ca0 100644 --- a/src/import/ImportRaw.h +++ b/src/import/ImportRaw.h @@ -21,7 +21,10 @@ class wxWindow; #include -using TrackHolders = std::vector>; +// Newly constructed WaveTracks that are not yet owned by a TrackList +// are held in unique_ptr not shared_ptr +using NewChannelGroup = std::vector< std::unique_ptr >; +using TrackHolders = std::vector< NewChannelGroup >; void ImportRaw(wxWindow *parent, const wxString &fileName, diff --git a/src/ondemand/ODDecodeFFmpegTask.cpp b/src/ondemand/ODDecodeFFmpegTask.cpp index 45c687cdd..00009864d 100644 --- a/src/ondemand/ODDecodeFFmpegTask.cpp +++ b/src/ondemand/ODDecodeFFmpegTask.cpp @@ -109,17 +109,17 @@ private: int mStreamIndex; }; -auto ODDecodeFFmpegTask::FromList(const std::list &channels) -> Streams +auto ODDecodeFFmpegTask::FromList( const TrackHolders &channels ) -> Streams { Streams streams; streams.reserve(channels.size()); using namespace std; transform(channels.begin(), channels.end(), back_inserter(streams), - [](const TrackHolders &holders) { + [](const NewChannelGroup &holders) { Channels channels; channels.reserve(holders.size()); transform(holders.begin(), holders.end(), back_inserter(channels), - mem_fn(&TrackHolders::value_type::get) + mem_fn(&NewChannelGroup::value_type::get) ); return channels; } diff --git a/src/ondemand/ODDecodeFFmpegTask.h b/src/ondemand/ODDecodeFFmpegTask.h index 404e0b58b..86955237b 100644 --- a/src/ondemand/ODDecodeFFmpegTask.h +++ b/src/ondemand/ODDecodeFFmpegTask.h @@ -31,7 +31,8 @@ public: using Channels = std::vector < WaveTrack* >; using Streams = std::vector < Channels >; - static Streams FromList(const std::list &channels); + static Streams FromList( + const std::vector< std::vector< std::unique_ptr > > &channels); /// Constructs an ODTask ODDecodeFFmpegTask(const ScsPtr &scs, Streams &&channels, const std::shared_ptr &context, int streamIndex); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 038fa4453..afcf9ca4d 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -1164,10 +1164,13 @@ bool ControlToolBar::DoRecord(AudacityProject &project, wxString baseTrackName = recordingNameCustom? defaultRecordingTrackName : defaultTrackName; + Track *first {}; for (int c = 0; c < recordingChannels; c++) { std::shared_ptr newTrack{ p->GetTrackFactory()->NewWaveTrack().release() }; + if (!first) + first = newTrack.get(); // Quantize bounds to the rate of the new track. if (c == 0) { @@ -1215,26 +1218,14 @@ bool ControlToolBar::DoRecord(AudacityProject &project, 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 ); - } - p->GetTracks()->RegisterPendingNewTrack( newTrack ); transportTracks.captureTracks.push_back(newTrack); // Bug 1548. New track needs the focus. p->GetTrackPanel()->SetFocusedTrack( newTrack.get() ); } + p->GetTracks()->GroupChannels(*first, recordingChannels); } - + //Automated Input Level Adjustment Initialization #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT gAudioIO->AILAInitialize(); diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index fa75476fe..7d0e224c4 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -802,6 +802,7 @@ void WaveTrackMenuTable::OnSpectrogramSettings(wxCommandEvent &) } } +#if 0 void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event) { int id = event.GetId(); @@ -834,6 +835,7 @@ void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event) _("Channel")); mpData->result = RefreshCode::RefreshAll; } +#endif /// Merge two tracks into one stereo track ?? void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) @@ -847,46 +849,39 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) auto partner = static_cast< WaveTrack * > ( *tracks->Find( pTrack ).advance( 1 ) ); - pTrack->SetLinked(true); + tracks->GroupChannels( *pTrack, 2 ); - if (partner) { - // Set partner's parameters to match target. - partner->Merge(*pTrack); + // Set partner's parameters to match target. + partner->Merge(*pTrack); - pTrack->SetPan( 0.0f ); - pTrack->SetChannel(Track::LeftChannel); - partner->SetPan( 0.0f ); - partner->SetChannel(Track::RightChannel); + pTrack->SetPan( 0.0f ); + partner->SetPan( 0.0f ); - // Set NEW track heights and minimized state - bool bBothMinimizedp = ((pTrack->GetMinimized()) && (partner->GetMinimized())); - pTrack->SetMinimized(false); - partner->SetMinimized(false); - int AverageHeight = (pTrack->GetHeight() + partner->GetHeight()) / 2; - pTrack->SetHeight(AverageHeight); - partner->SetHeight(AverageHeight); - pTrack->SetMinimized(bBothMinimizedp); - partner->SetMinimized(bBothMinimizedp); + // Set NEW track heights and minimized state + bool bBothMinimizedp = ((pTrack->GetMinimized()) && (partner->GetMinimized())); + pTrack->SetMinimized(false); + partner->SetMinimized(false); + int AverageHeight = (pTrack->GetHeight() + partner->GetHeight()) / 2; + pTrack->SetHeight(AverageHeight); + partner->SetHeight(AverageHeight); + pTrack->SetMinimized(bBothMinimizedp); + partner->SetMinimized(bBothMinimizedp); - //On Demand - join the queues together. - if (ODManager::IsInstanceCreated()) - if (!ODManager::Instance()->MakeWaveTrackDependent(partner, pTrack)) - { - ; - //TODO: in the future, we will have to check the return value of MakeWaveTrackDependent - - //if the tracks cannot merge, it returns false, and in that case we should not allow a merging. - //for example it returns false when there are two different types of ODTasks on each track's queue. - //we will need to display this to the user. - } + //On Demand - join the queues together. + if (ODManager::IsInstanceCreated()) + if (!ODManager::Instance()->MakeWaveTrackDependent(partner, pTrack)) + { + ; + //TODO: in the future, we will have to check the return value of MakeWaveTrackDependent - + //if the tracks cannot merge, it returns false, and in that case we should not allow a merging. + //for example it returns false when there are two different types of ODTasks on each track's queue. + //we will need to display this to the user. + } - AudacityProject *const project = ::GetActiveProject(); - /* i18n-hint: The string names a track */ - project->PushState(wxString::Format(_("Made '%s' a stereo track"), - pTrack->GetName()), - _("Make Stereo")); - } - else - pTrack->SetLinked(false); + /* i18n-hint: The string names a track */ + project->PushState(wxString::Format(_("Made '%s' a stereo track"), + pTrack->GetName()), + _("Make Stereo")); mpData->result = RefreshCode::RefreshAll; } @@ -899,7 +894,6 @@ void WaveTrackMenuTable::SplitStereo(bool stereo) AudacityProject *const project = ::GetActiveProject(); auto channels = TrackList::Channels( pTrack ); - int totalHeight = 0; int nChannels = 0; for (auto channel : channels) { @@ -907,7 +901,6 @@ void WaveTrackMenuTable::SplitStereo(bool stereo) channel->SetName(pTrack->GetName()); if (stereo) channel->SetPanFromChannelType(); - channel->SetChannel(Track::MonoChannel); //On Demand - have each channel add its own. if (ODManager::IsInstanceCreated()) @@ -919,7 +912,7 @@ void WaveTrackMenuTable::SplitStereo(bool stereo) ++nChannels; } - pTrack->SetLinked(false); + project->GetTracks()->GroupChannels( *pTrack, 1 ); int averageHeight = totalHeight / nChannels; for (auto channel : channels) @@ -947,12 +940,9 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &) SplitStereo(false); - first->SetChannel(Track::RightChannel); - partner->SetChannel(Track::LeftChannel); - TrackList *const tracks = project->GetTracks(); - (tracks->MoveUp(partner)); - partner->SetLinked(true); + tracks->MoveUp( partner ); + tracks->GroupChannels( *partner, 2 ); MixerBoard* pMixerBoard = project->GetMixerBoard(); if (pMixerBoard)