From 86320838de936b2eec618d3abbb65aee586b22ae Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 5 Jun 2019 11:46:07 -0400 Subject: [PATCH] WaveTrack.cpp does not depend on ODManager.cpp ... ... Breaking up an s.c.c. of 6 into 3 components: ODManager, ODTask, ODWaveTrackTaskQueue UndoManager WaveClip, WaveTrack Rewrite the OD tasks and queues to hold weak pointers to tracks, so the track destructor need not notify them. --- src/ProjectFileManager.cpp | 4 +- src/ProjectHistory.cpp | 3 +- src/WaveTrack.cpp | 6 -- src/effects/Effect.cpp | 2 +- src/import/ImportFFmpeg.cpp | 2 +- src/import/ImportFLAC.cpp | 2 +- src/import/ImportPCM.cpp | 2 +- src/ondemand/ODComputeSummaryTask.cpp | 10 ++- src/ondemand/ODDecodeTask.cpp | 10 ++- src/ondemand/ODManager.cpp | 29 +++---- src/ondemand/ODManager.h | 13 +-- src/ondemand/ODTask.cpp | 23 +++--- src/ondemand/ODTask.h | 9 ++- src/ondemand/ODWaveTrackTaskQueue.cpp | 80 ++++++++----------- src/ondemand/ODWaveTrackTaskQueue.h | 20 +++-- .../wavetrack/ui/WaveTrackControls.cpp | 6 +- 16 files changed, 101 insertions(+), 120 deletions(-) diff --git a/src/ProjectFileManager.cpp b/src/ProjectFileManager.cpp index eff8a6d54..fcd345d7f 100644 --- a/src/ProjectFileManager.cpp +++ b/src/ProjectFileManager.cpp @@ -210,7 +210,7 @@ void ProjectFileManager::EnqueueODTasks() //add the track to the already created tasks that correspond to the od flags in the wavetrack. for(unsigned int i=0;iGetODType() & odFlags) - newTasks[i]->AddWaveTrack(wt); + newTasks[i]->AddWaveTrack(wt->SharedPointer< WaveTrack >()); } //create whatever NEW tasks we need to. @@ -236,7 +236,7 @@ void ProjectFileManager::EnqueueODTasks() } if(newTask) { - newTask->AddWaveTrack(wt); + newTask->AddWaveTrack(wt->SharedPointer< WaveTrack >()); newTasks.push_back(std::move(newTask)); } } diff --git a/src/ProjectHistory.cpp b/src/ProjectHistory.cpp index f5674dda3..619f0771a 100644 --- a/src/ProjectHistory.cpp +++ b/src/ProjectHistory.cpp @@ -17,6 +17,7 @@ Paul Licameli split from ProjectManager.cpp #include "Track.h" #include "UndoManager.h" #include "ViewInfo.h" +#include "WaveTrack.h" #include "ondemand/ODComputeSummaryTask.h" #include "ondemand/ODManager.h" @@ -167,7 +168,7 @@ void ProjectHistory::PopState(const UndoState &state) // PRL: Is it correct to add all tracks to one task, even if they // are not partnered channels? Rather than // make one task for each? - computeTask->AddWaveTrack(wt); + computeTask->AddWaveTrack(wt->SharedPointer< WaveTrack >()); } }); } diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index 45492f1df..8cc4b1d04 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -53,8 +53,6 @@ Track classes. #include "Prefs.h" -#include "ondemand/ODManager.h" - #include "effects/TimeWarper.h" #include "prefs/SpectrogramSettings.h" #include "prefs/TracksPrefs.h" @@ -213,10 +211,6 @@ void WaveTrack::Merge(const Track &orig) WaveTrack::~WaveTrack() { - //Let the ODManager know this WaveTrack is disappearing. - //Deschedules tasks associated with this track. - if(ODManager::IsInstanceCreated()) - ODManager::Instance()->RemoveWaveTrack(this); } double WaveTrack::GetOffset() const diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 0c5af8544..0e23425f9 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -2256,7 +2256,7 @@ void Effect::ReplaceProcessedTracks(const bool bGoodResult) // Swap the wavecache track the ondemand task uses, since now the NEW // one will be kept in the project if (ODManager::IsInstanceCreated()) { - ODManager::Instance()->ReplaceWaveTrack( t, o.get() ); + ODManager::Instance()->ReplaceWaveTrack( t, o ); } } } diff --git a/src/import/ImportFFmpeg.cpp b/src/import/ImportFFmpeg.cpp index cf80028dd..445a52ba3 100644 --- a/src/import/ImportFFmpeg.cpp +++ b/src/import/ImportFFmpeg.cpp @@ -588,7 +588,7 @@ ProgressResult FFmpegImportFileHandle::Import(TrackFactory *trackFactory, //for each wavetrack within the stream add coded blockfiles for (int c = 0; c < sc->m_stream->codec->channels; c++) { - WaveTrack *t = stream[c].get(); + auto t = stream[c]; odTask->AddWaveTrack(t); auto maxBlockSize = t->GetMaxBlockSize(); diff --git a/src/import/ImportFLAC.cpp b/src/import/ImportFLAC.cpp index 38df02601..7c6b3398c 100644 --- a/src/import/ImportFLAC.cpp +++ b/src/import/ImportFLAC.cpp @@ -505,7 +505,7 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory, bool moreThanStereo = mNumChannels>2; for (const auto &channel : mChannels) { - mDecoderTask->AddWaveTrack(channel.get()); + mDecoderTask->AddWaveTrack(channel); if(moreThanStereo) { //if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each. diff --git a/src/import/ImportPCM.cpp b/src/import/ImportPCM.cpp index d97ca1318..6d2a3192a 100644 --- a/src/import/ImportPCM.cpp +++ b/src/import/ImportPCM.cpp @@ -445,7 +445,7 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory, bool moreThanStereo = mInfo.channels>2; for (const auto &channel : channels) { - computeTask->AddWaveTrack(channel.get()); + computeTask->AddWaveTrack(channel); if(moreThanStereo) { //if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each. diff --git a/src/ondemand/ODComputeSummaryTask.cpp b/src/ondemand/ODComputeSummaryTask.cpp index d35a50794..1685a8091 100644 --- a/src/ondemand/ODComputeSummaryTask.cpp +++ b/src/ondemand/ODComputeSummaryTask.cpp @@ -116,8 +116,9 @@ void ODComputeSummaryTask::DoSomeInternal() mWaveTrackMutex.Lock(); for(size_t i=0;iAddInvalidRegion(blockStartSample,blockEndSample); + auto waveTrack = mWaveTracks[i].lock(); + if(success && waveTrack) + waveTrack->AddInvalidRegion(blockStartSample,blockEndSample); } mWaveTrackMutex.Unlock(); } @@ -182,13 +183,14 @@ void ODComputeSummaryTask::Update() for(size_t j=0;jGetAllClips()) { + for (const auto &clip : waveTrack->GetAllClips()) { seq = clip->GetSequence(); //This lock may be way too big since the whole file is one sequence. //TODO: test for large files and find a way to break it down. diff --git a/src/ondemand/ODDecodeTask.cpp b/src/ondemand/ODDecodeTask.cpp index c7b7db591..409693fc9 100644 --- a/src/ondemand/ODDecodeTask.cpp +++ b/src/ondemand/ODDecodeTask.cpp @@ -102,8 +102,9 @@ void ODDecodeTask::DoSomeInternal() mWaveTrackMutex.Lock(); for(size_t i=0;iAddInvalidRegion(blockStartSample,blockEndSample); + auto waveTrack = mWaveTracks[i].lock(); + if(waveTrack) + waveTrack->AddInvalidRegion(blockStartSample,blockEndSample); } mWaveTrackMutex.Unlock(); } @@ -138,13 +139,14 @@ void ODDecodeTask::Update() for(size_t j=0;jGetAllClips()) { + for (const auto &clip : waveTrack->GetAllClips()) { seq = clip->GetSequence(); //TODO:this lock is way to big since the whole file is one sequence. find a way to break it down. seq->LockDeleteUpdateMutex(); diff --git a/src/ondemand/ODManager.cpp b/src/ondemand/ODManager.cpp index b6ae83e6a..a92a69b95 100644 --- a/src/ondemand/ODManager.cpp +++ b/src/ondemand/ODManager.cpp @@ -257,7 +257,7 @@ void ODManager::AddNewTask(std::unique_ptr &&mtask, bool lockMutex) { //search for a task containing the lead track. wavetrack removal is threadsafe and bound to the mQueuesMutex //note that GetWaveTrack is not threadsafe, but we are assuming task is not running on a different thread yet. - if(mQueues[i]->ContainsWaveTrack(task->GetWaveTrack(0))) + if(mQueues[i]->ContainsWaveTrack(task->GetWaveTrack(0).get())) queue = mQueues[i].get(); } @@ -472,20 +472,9 @@ void ODManager::Quit() } } -///removes a wavetrack and notifies its associated tasks to stop using its reference. -void ODManager::RemoveWaveTrack(WaveTrack* track) -{ - mQueuesMutex.Lock(); - for(unsigned int i=0;iContainsWaveTrack(track)) - mQueues[i]->RemoveWaveTrack(track); - } - mQueuesMutex.Unlock(); -} - ///replace the wavetrack whose wavecache the gui watches for updates -void ODManager::ReplaceWaveTrack(Track *oldTrack, Track *newTrack) +void ODManager::ReplaceWaveTrack(Track *oldTrack, + const std::shared_ptr &newTrack) { mQueuesMutex.Lock(); for(unsigned int i=0;i &track) { ODWaveTrackTaskQueue* owner=NULL; mQueuesMutex.Lock(); for(unsigned int i=0;iContainsWaveTrack(track)) + if(mQueues[i]->ContainsWaveTrack(track.get())) { owner = mQueues[i].get(); break; @@ -519,7 +509,10 @@ void ODManager::MakeWaveTrackIndependent(WaveTrack* track) ///better design in the future. ///@return returns success. Some ODTask conditions require that the tasks finish before merging. ///e.g. they have different effects being processed at the same time. -bool ODManager::MakeWaveTrackDependent(WaveTrack* dependentTrack,WaveTrack* masterTrack) +bool ODManager::MakeWaveTrackDependent( + const std::shared_ptr< WaveTrack > &dependentTrack, + WaveTrack* masterTrack +) { //First, check to see if the task lists are mergeable. If so, we can simply add this track to the other task and queue, //then DELETE this one. @@ -535,7 +528,7 @@ bool ODManager::MakeWaveTrackDependent(WaveTrack* dependentTrack,WaveTrack* mast { masterQueue = mQueues[i].get(); } - else if(mQueues[i]->ContainsWaveTrack(dependentTrack)) + else if(mQueues[i]->ContainsWaveTrack(dependentTrack.get())) { dependentQueue = mQueues[i].get(); dependentIndex = i; diff --git a/src/ondemand/ODManager.h b/src/ondemand/ODManager.h index 28d50f6a6..5e4467b4a 100644 --- a/src/ondemand/ODManager.h +++ b/src/ondemand/ODManager.h @@ -67,19 +67,20 @@ class ODManager final ///Wakes the queue loop up by signalling its condition variable. void SignalTaskQueueLoop(); - ///removes a wavetrack and notifies its associated tasks to stop using its reference. - void RemoveWaveTrack(WaveTrack* track); - ///if it shares a queue/task, creates a NEW queue/task for the track, and removes it from any previously existing tasks. - void MakeWaveTrackIndependent(WaveTrack* track); + void MakeWaveTrackIndependent( const std::shared_ptr< WaveTrack > &track); ///attach the track in question to another, already existing track's queues and tasks. Remove the task/tracks. ///Returns success if it was possible.. Some ODTask conditions make it impossible until the Tasks finish. - bool MakeWaveTrackDependent(WaveTrack* dependentTrack,WaveTrack* masterTrack); + bool MakeWaveTrackDependent( + const std::shared_ptr< WaveTrack > &dependentTrack, + WaveTrack* masterTrack + ); ///if oldTrack is being watched, ///replace the wavetrack whose wavecache the gui watches for updates - void ReplaceWaveTrack(Track *oldTrack, Track *newTrack); + void ReplaceWaveTrack(Track *oldTrack, + const std::shared_ptr< Track > &newTrack); ///Adds a task to the running queue. Threas-safe. void AddTask(ODTask* task); diff --git a/src/ondemand/ODTask.cpp b/src/ondemand/ODTask.cpp index 418e03643..5cc35e688 100644 --- a/src/ondemand/ODTask.cpp +++ b/src/ondemand/ODTask.cpp @@ -182,7 +182,7 @@ bool ODTask::IsTaskAssociatedWithProject(AudacityProject* proj) mWaveTrackMutex.Lock(); for(int i=0;i<(int)mWaveTracks.size();i++) { - if(mWaveTracks[i]==tr) + if ( mWaveTracks[i].lock().get() == tr ) { //if we find one, then the project is associated with us;return true mWaveTrackMutex.Unlock(); @@ -252,19 +252,19 @@ bool ODTask::IsComplete() } -WaveTrack* ODTask::GetWaveTrack(int i) +std::shared_ptr< WaveTrack > ODTask::GetWaveTrack(int i) { - WaveTrack* track = NULL; + std::shared_ptr< WaveTrack > track; mWaveTrackMutex.Lock(); if(i<(int)mWaveTracks.size()) - track = mWaveTracks[i]; + track = mWaveTracks[i].lock(); mWaveTrackMutex.Unlock(); return track; } ///Sets the wavetrack that will be analyzed for ODPCMAliasBlockFiles that will ///have their summaries computed and written to disk. -void ODTask::AddWaveTrack(WaveTrack* track) +void ODTask::AddWaveTrack( const std::shared_ptr< WaveTrack > &track) { mWaveTracks.push_back(track); } @@ -321,7 +321,7 @@ void ODTask::DemandTrackUpdate(WaveTrack* track, double seconds) mWaveTrackMutex.Lock(); for(size_t i=0;iGetRate()); demandSampleChanged = newDemandSample != GetDemandSample(); @@ -342,21 +342,22 @@ void ODTask::StopUsingWaveTrack(WaveTrack* track) mWaveTrackMutex.Lock(); for(size_t i=0;i &newTrack) { mWaveTrackMutex.Lock(); for(size_t i=0;i( newTrack ); + mWaveTracks[i] = std::static_pointer_cast( newTrack ); } } mWaveTrackMutex.Unlock(); diff --git a/src/ondemand/ODTask.h b/src/ondemand/ODTask.h index a74a6e6d8..338f60d58 100644 --- a/src/ondemand/ODTask.h +++ b/src/ondemand/ODTask.h @@ -84,12 +84,13 @@ class ODTask /* not final */ ///Replaces all instances to a wavetrack with a NEW one, effectively transferring the task. ///ODTask has no wavetrack, so it does nothing. But subclasses that do should override this. - virtual void ReplaceWaveTrack(Track *oldTrack, Track *newTrack); + virtual void ReplaceWaveTrack(Track *oldTrack, + const std::shared_ptr< Track > &newTrack); ///Adds a WaveTrack to do the task for - void AddWaveTrack(WaveTrack* track); + void AddWaveTrack( const std::shared_ptr< WaveTrack > &track); virtual int GetNumWaveTracks(); - virtual WaveTrack* GetWaveTrack(int i); + virtual std::shared_ptr< WaveTrack > GetWaveTrack(int i); ///changes the tasks associated with this Waveform to process the task from a different point in the track virtual void DemandTrackUpdate(WaveTrack* track, double seconds); @@ -156,7 +157,7 @@ class ODTask /* not final */ //for a function not a member var. ODLock mBlockUntilTerminateMutex; - std::vector mWaveTracks; + std::vector< std::weak_ptr< WaveTrack > > mWaveTracks; ODLock mWaveTrackMutex; sampleCount mDemandSample; diff --git a/src/ondemand/ODWaveTrackTaskQueue.cpp b/src/ondemand/ODWaveTrackTaskQueue.cpp index dbc8a4441..02622bc1b 100644 --- a/src/ondemand/ODWaveTrackTaskQueue.cpp +++ b/src/ondemand/ODWaveTrackTaskQueue.cpp @@ -64,7 +64,8 @@ bool ODWaveTrackTaskQueue::CanMergeWith(ODWaveTrackTaskQueue* otherQueue) /// sets the NeedODUpdateFlag since we don't want the head task to finish without haven't dealt with the depednent /// ///@param track the track to bring into the tasks AND tracklist for this queue -void ODWaveTrackTaskQueue::MergeWaveTrack(WaveTrack* track) +void ODWaveTrackTaskQueue::MergeWaveTrack( + const std::shared_ptr< WaveTrack > &track) { AddWaveTrack(track); mTasksMutex.Lock(); @@ -83,7 +84,7 @@ bool ODWaveTrackTaskQueue::ContainsWaveTrack(const WaveTrack* track) mTracksMutex.Lock(); for(unsigned int i=0;i &track) { mTracksMutex.Lock(); @@ -117,8 +119,7 @@ void ODWaveTrackTaskQueue::AddTask(std::unique_ptr &&mtask) { //task->GetWaveTrack(i) may return NULL, but we handle it by checking before using. //The other worry that the WaveTrack returned and was deleted in the meantime is also - //handled since mQueuesMutex is locked one up in the stack from here, - //and WaveTrack deletion is bound to that. + //handled by keeping standard weak pointers to tracks, which give thread safety. mTracks.push_back(task->GetWaveTrack(i)); } @@ -126,32 +127,15 @@ void ODWaveTrackTaskQueue::AddTask(std::unique_ptr &&mtask) } -///Removes a track from the list. Also notifies mTasks to stop using references -///to the instance in a thread-safe manner (may block) -void ODWaveTrackTaskQueue::RemoveWaveTrack(WaveTrack* track) -{ - if(track) - { - - mTasksMutex.Lock(); - for(unsigned int i=0;iStopUsingWaveTrack(track); - mTasksMutex.Unlock(); - - mTracksMutex.Lock(); - for(unsigned int i=0;i &track) { - + // First remove expired weak pointers + Compress(); + mTracksMutex.Lock(); + if(mTracks.size()<2) { //if there is only one track, it is already independent. @@ -161,17 +145,16 @@ void ODWaveTrackTaskQueue::MakeWaveTrackIndependent(WaveTrack* track) for(unsigned int i=0;iClone(); - task->AddWaveTrack(track); + mTasks[j]->StopUsingWaveTrack( track.get() ); //AddNewTask requires us to relinquish this lock. However, it is safe because ODManager::MakeWaveTrackIndependent //has already locked the m_queuesMutex. mTasksMutex.Unlock(); @@ -181,7 +164,6 @@ void ODWaveTrackTaskQueue::MakeWaveTrackIndependent(WaveTrack* track) mTasksMutex.Lock(); } mTasksMutex.Unlock(); - mTracksMutex.Lock(); break; } } @@ -207,7 +189,8 @@ void ODWaveTrackTaskQueue::DemandTrackUpdate(WaveTrack* track, double seconds) //Replaces all instances of a wavetracck with a NEW one (effectively transferes the task.) -void ODWaveTrackTaskQueue::ReplaceWaveTrack(Track *oldTrack, Track *newTrack) +void ODWaveTrackTaskQueue::ReplaceWaveTrack(Track *oldTrack, + const std::shared_ptr &newTrack) { if(oldTrack) { @@ -218,26 +201,17 @@ void ODWaveTrackTaskQueue::ReplaceWaveTrack(Track *oldTrack, Track *newTrack) mTracksMutex.Lock(); for(unsigned int i=0;i( newTrack ); + if ( mTracks[i].lock().get() == oldTrack ) + mTracks[i] = std::static_pointer_cast( newTrack ); mTracksMutex.Unlock(); } } -//returns the wavetrack at position x. -WaveTrack* ODWaveTrackTaskQueue::GetWaveTrack(size_t x) -{ - WaveTrack* ret = NULL; - mTracksMutex.Lock(); - if (x < mTracks.size()) - ret = mTracks[x]; - mTracksMutex.Unlock(); - return ret; -} - ///returns the number of wavetracks in this queue. int ODWaveTrackTaskQueue::GetNumWaveTracks() { + Compress(); + int ret = 0; mTracksMutex.Lock(); ret=mTracks.size(); @@ -271,6 +245,8 @@ ODTask* ODWaveTrackTaskQueue::GetTask(size_t x) //returns true if either tracks or tasks are empty bool ODWaveTrackTaskQueue::IsEmpty() { + Compress(); + bool isEmpty; mTracksMutex.Lock(); isEmpty = mTracks.size()<=0; @@ -342,3 +318,13 @@ void ODWaveTrackTaskQueue::FillTipForWaveTrack( const WaveTrack * t, wxString &t } } + +void ODWaveTrackTaskQueue::Compress() +{ + mTracksMutex.Lock(); + auto begin = mTracks.begin(), end = mTracks.end(), + new_end = std::remove_if( begin, end, + []( const std::weak_ptr &ptr ){ return ptr.expired(); } ); + mTracks.erase( new_end, end ); + mTracksMutex.Unlock(); +} diff --git a/src/ondemand/ODWaveTrackTaskQueue.h b/src/ondemand/ODWaveTrackTaskQueue.h index 5f22ae7d3..68bda5b34 100644 --- a/src/ondemand/ODWaveTrackTaskQueue.h +++ b/src/ondemand/ODWaveTrackTaskQueue.h @@ -42,31 +42,26 @@ class ODWaveTrackTaskQueue final ///Adds a track to the associated list. - void AddWaveTrack(WaveTrack* track); - ///Removes a track from the list. Also notifies mTasks to stop using references - ///to the instance in a thread-safe manner (may block) - void RemoveWaveTrack(WaveTrack* track); + void AddWaveTrack( const std::shared_ptr< WaveTrack > &track); ///changes the tasks associated with this Waveform to process the task from a different point in the track void DemandTrackUpdate(WaveTrack* track, double seconds); ///replaces all instances of a WaveTrack within this task with another. - void ReplaceWaveTrack(Track *oldTrack, Track *newTrack); + void ReplaceWaveTrack(Track *oldTrack, + const std::shared_ptr &newTrack); //if the wavetrack is in this queue, and is not the only wavetrack, clones the tasks and schedules it. - void MakeWaveTrackIndependent(WaveTrack* track); + void MakeWaveTrackIndependent( const std::shared_ptr< WaveTrack > &track); ///returns whether or not this queue's task list and another's can merge together, as when we make two mono tracks stereo. bool CanMergeWith(ODWaveTrackTaskQueue* otherQueue); - void MergeWaveTrack(WaveTrack* track); + void MergeWaveTrack( const std::shared_ptr< WaveTrack > &track); //returns true if the agrument is in the WaveTrack list. bool ContainsWaveTrack(const WaveTrack* track); - //returns the wavetrack at position x. - WaveTrack* GetWaveTrack(size_t x); - ///returns the number of wavetracks in this queue. int GetNumWaveTracks(); @@ -96,12 +91,15 @@ class ODWaveTrackTaskQueue final protected: + // Remove expired weak pointers to tracks + void Compress(); + //because we need to save this around for the tool tip. wxString mTipMsg; ///the list of tracks associated with this queue. - std::vector mTracks; + std::vector< std::weak_ptr< WaveTrack > > mTracks; ODLock mTracksMutex; ///the list of tasks associated with the tracks. This class owns these tasks. diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index 877c655dd..f52d805bc 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -889,7 +889,8 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) //On Demand - join the queues together. if (ODManager::IsInstanceCreated()) - if (!ODManager::Instance()->MakeWaveTrackDependent(partner, pTrack)) + if (!ODManager::Instance() + ->MakeWaveTrackDependent(partner->SharedPointer(), pTrack)) { ; //TODO: in the future, we will have to check the return value of MakeWaveTrackDependent - @@ -927,7 +928,8 @@ void WaveTrackMenuTable::SplitStereo(bool stereo) //On Demand - have each channel add its own. if (ODManager::IsInstanceCreated()) - ODManager::Instance()->MakeWaveTrackIndependent(channel); + ODManager::Instance()->MakeWaveTrackIndependent( + channel->SharedPointer() ); //make sure no channel is smaller than its minimum height if (view.GetHeight() < view.GetMinimizedHeight()) view.SetHeight(view.GetMinimizedHeight());