mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-29 23:29:41 +02:00
Add LinkType to Track
LinkType replaces old boolean 'linked', mostly for WaveTrack display purposes Track::GroupChannels splitted into Track::UnlinkChannels and Track::MakeMultiChannelTrack (which is also aware of new LinkType) LinkConsistensyCheck made virtual to allow Track subclasses to perform type-specific consistensy checks. Introduces some corner-cases with copy-pasting/old project importing/file importing..., which are handled (cherry picked from audacity commit 607961da70968c179b60a811ce084b55a94438ec) Signed-off-by: akleja <storspov@gmail.com>
This commit is contained in:
parent
39d49be5b4
commit
f969701756
@ -722,7 +722,7 @@ bool ProjectAudioManager::DoRecord(AudacityProject &project,
|
||||
|
||||
transportTracks.captureTracks.push_back(newTrack);
|
||||
}
|
||||
TrackList::Get( *p ).GroupChannels(*first, recordingChannels);
|
||||
TrackList::Get( *p ).MakeMultiChannelTrack(*first, recordingChannels, true);
|
||||
// Bug 1548. First of new tracks needs the focus.
|
||||
TrackFocus::Get(*p).Set(first);
|
||||
if (TrackList::Get(*p).back())
|
||||
|
@ -1113,7 +1113,7 @@ ProjectFileManager::AddImportedTracks(const FilePath &fileName,
|
||||
auto newTrack = tracks.Add( uNewTrack );
|
||||
results.push_back(newTrack->SharedPointer());
|
||||
}
|
||||
tracks.GroupChannels(*first, nChannels);
|
||||
tracks.MakeMultiChannelTrack(*first, nChannels, true);
|
||||
}
|
||||
newTracks.clear();
|
||||
|
||||
|
138
src/Track.cpp
138
src/Track.cpp
@ -50,7 +50,6 @@ Track::Track()
|
||||
: vrulerSize(36,0)
|
||||
{
|
||||
mSelected = false;
|
||||
mLinked = false;
|
||||
|
||||
mIndex = 0;
|
||||
|
||||
@ -76,7 +75,7 @@ void Track::Init(const Track &orig)
|
||||
mName = orig.mName;
|
||||
|
||||
mSelected = orig.mSelected;
|
||||
mLinked = orig.mLinked;
|
||||
mLinkType = orig.mLinkType;
|
||||
mChannel = orig.mChannel;
|
||||
}
|
||||
|
||||
@ -172,18 +171,18 @@ void Track::SetIndex(int index)
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
void Track::SetLinked(bool l)
|
||||
void Track::SetLinkType(LinkType linkType)
|
||||
{
|
||||
auto pList = mList.lock();
|
||||
if (pList && !pList->mPendingUpdates.empty()) {
|
||||
auto orig = pList->FindById( GetId() );
|
||||
if (orig && orig != this) {
|
||||
orig->SetLinked(l);
|
||||
orig->SetLinkType(linkType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DoSetLinked(l);
|
||||
DoSetLinkType(linkType);
|
||||
|
||||
if (pList) {
|
||||
pList->RecalcPositions(mNode);
|
||||
@ -191,9 +190,14 @@ void Track::SetLinked(bool l)
|
||||
}
|
||||
}
|
||||
|
||||
void Track::DoSetLinked(bool l)
|
||||
void Track::DoSetLinkType(LinkType linkType) noexcept
|
||||
{
|
||||
mLinked = l;
|
||||
mLinkType = linkType;
|
||||
}
|
||||
|
||||
void Track::SetChannel(ChannelType c) noexcept
|
||||
{
|
||||
mChannel = c;
|
||||
}
|
||||
|
||||
Track *Track::GetLinkedTrack() const
|
||||
@ -224,7 +228,7 @@ Track *Track::GetLinkedTrack() const
|
||||
|
||||
bool Track::HasLinkedTrack() const noexcept
|
||||
{
|
||||
return mLinked;
|
||||
return mLinkType != LinkType::None;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -385,7 +389,7 @@ void Track::FinishCopy
|
||||
{
|
||||
if (dest) {
|
||||
dest->SetChannel(n->GetChannel());
|
||||
dest->SetLinked(n->HasLinkedTrack());
|
||||
dest->SetLinkType(n->GetLinkType());
|
||||
dest->SetName(n->GetName());
|
||||
}
|
||||
}
|
||||
@ -408,7 +412,7 @@ bool Track::LinkConsistencyCheck()
|
||||
wxT("Left track %s had linked right track %s with extra right track link.\n Removing extra link from right track."),
|
||||
GetName(), link->GetName());
|
||||
err = true;
|
||||
link->SetLinked(false);
|
||||
link->SetLinkType(LinkType::None);
|
||||
}
|
||||
|
||||
// Channels should be left and right
|
||||
@ -421,7 +425,7 @@ bool Track::LinkConsistencyCheck()
|
||||
wxT("Track %s and %s had left/right track links out of order. Setting tracks to not be linked."),
|
||||
GetName(), link->GetName());
|
||||
err = true;
|
||||
SetLinked(false);
|
||||
SetLinkType(LinkType::None);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -430,7 +434,7 @@ bool Track::LinkConsistencyCheck()
|
||||
wxT("Track %s had link to NULL track. Setting it to not be linked."),
|
||||
GetName());
|
||||
err = true;
|
||||
SetLinked(false);
|
||||
SetLinkType(LinkType::None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -711,57 +715,6 @@ Track *TrackList::DoAdd(const std::shared_ptr<Track> &t)
|
||||
return back().get();
|
||||
}
|
||||
|
||||
void TrackList::GroupChannels(
|
||||
Track &track, size_t groupSize, bool resetChannels )
|
||||
{
|
||||
// If group size is exactly two, group as stereo, else mono (bug 2195).
|
||||
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, --count )
|
||||
;
|
||||
if ( count == 0 ) {
|
||||
auto unlink = [&] ( Track &tr ) {
|
||||
if ( tr.HasLinkedTrack() ) {
|
||||
if ( resetChannels ) {
|
||||
auto link = tr.GetLinkedTrack();
|
||||
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( groupSize == 2 );
|
||||
channel->SetChannel( groupSize == 2? Track::LeftChannel : Track::MonoChannel );
|
||||
(*iter++)->SetChannel( groupSize == 2? Track::RightChannel : Track::MonoChannel );
|
||||
while (iter != after)
|
||||
(*iter++)->SetChannel( Track::MonoChannel );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// *this does not contain the track or sufficient following channels
|
||||
// or group size is zero
|
||||
THROW_INCONSISTENCY_EXCEPTION;
|
||||
}
|
||||
|
||||
auto TrackList::Replace(Track * t, const ListOfTracks::value_type &with) ->
|
||||
ListOfTracks::value_type
|
||||
{
|
||||
@ -784,6 +737,58 @@ auto TrackList::Replace(Track * t, const ListOfTracks::value_type &with) ->
|
||||
return holder;
|
||||
}
|
||||
|
||||
void TrackList::UnlinkChannels(Track& track)
|
||||
{
|
||||
auto list = track.mList.lock();
|
||||
if (list.get() == this)
|
||||
{
|
||||
auto channels = TrackList::Channels(&track);
|
||||
for (auto c : channels)
|
||||
{
|
||||
c->SetLinkType(Track::LinkType::None);
|
||||
c->SetChannel(Track::ChannelType::MonoChannel);
|
||||
}
|
||||
}
|
||||
else
|
||||
THROW_INCONSISTENCY_EXCEPTION;
|
||||
}
|
||||
|
||||
bool TrackList::MakeMultiChannelTrack(Track& track, int nChannels, bool aligned)
|
||||
{
|
||||
if (nChannels != 2)
|
||||
return false;
|
||||
|
||||
auto list = track.mList.lock();
|
||||
if (list.get() == this)
|
||||
{
|
||||
if (*list->FindLeader(&track) != &track)
|
||||
return false;
|
||||
|
||||
auto first = list->Find(&track);
|
||||
auto canLink = [&]() -> bool {
|
||||
int count = nChannels;
|
||||
for (auto it = first, end = TrackList::end(); it != end && count; ++it)
|
||||
{
|
||||
if ((*it)->HasLinkedTrack())
|
||||
return false;
|
||||
--count;
|
||||
}
|
||||
return count == 0;
|
||||
}();
|
||||
|
||||
if (!canLink)
|
||||
return false;
|
||||
|
||||
(*first)->SetLinkType(aligned ? Track::LinkType::Aligned : Track::LinkType::Group);
|
||||
(*first)->SetChannel(Track::LeftChannel);
|
||||
auto second = std::next(first);
|
||||
(*second)->SetChannel(Track::RightChannel);
|
||||
}
|
||||
else
|
||||
THROW_INCONSISTENCY_EXCEPTION;
|
||||
return true;
|
||||
}
|
||||
|
||||
TrackNodePointer TrackList::Remove(Track *t)
|
||||
{
|
||||
auto result = getEnd();
|
||||
@ -1075,7 +1080,7 @@ void TrackList::UpdatePendingTracks()
|
||||
if (pendingTrack && src) {
|
||||
if (updater)
|
||||
updater( *pendingTrack, *src );
|
||||
pendingTrack->DoSetLinked(src->HasLinkedTrack());
|
||||
pendingTrack->DoSetLinkType(src->GetLinkType());
|
||||
}
|
||||
++pUpdater;
|
||||
}
|
||||
@ -1303,3 +1308,8 @@ bool TrackList::HasPendingTracks() const
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Track::LinkType Track::GetLinkType() const noexcept
|
||||
{
|
||||
return mLinkType;
|
||||
}
|
||||
|
49
src/Track.h
49
src/Track.h
@ -236,10 +236,22 @@ class TENACITY_DLL_API Track /* not final */
|
||||
, public AttachedTrackObjects
|
||||
, public std::enable_shared_from_this<Track> // see SharedPointer()
|
||||
{
|
||||
public:
|
||||
|
||||
//! For two tracks describes the type of the linkage
|
||||
enum class LinkType : int {
|
||||
None = 0, //< No linkage
|
||||
Group = 2, //< Tracks are grouped together
|
||||
Aligned, //< Tracks are grouped and changes should be synchronized
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
friend class TrackList;
|
||||
|
||||
private:
|
||||
TrackId mId; //!< Identifies the track only in-session, not persistently
|
||||
LinkType mLinkType{ LinkType::None };
|
||||
|
||||
protected:
|
||||
std::weak_ptr<TrackList> mList; //!< Back pointer to owning TrackList
|
||||
@ -253,9 +265,6 @@ class TENACITY_DLL_API Track /* not final */
|
||||
private:
|
||||
bool mSelected;
|
||||
|
||||
protected:
|
||||
bool mLinked;
|
||||
|
||||
public:
|
||||
|
||||
//! Alias for my base type
|
||||
@ -360,25 +369,25 @@ public:
|
||||
static void FinishCopy (const Track *n, Track *dest);
|
||||
|
||||
// For use when loading a file. Return true if ok, else make repair
|
||||
bool LinkConsistencyCheck();
|
||||
virtual bool LinkConsistencyCheck();
|
||||
|
||||
bool HasOwner() const { return static_cast<bool>(GetOwner());}
|
||||
|
||||
std::shared_ptr<TrackList> GetOwner() const { return mList.lock(); }
|
||||
|
||||
LinkType GetLinkType() const noexcept;
|
||||
protected:
|
||||
|
||||
void SetLinkType(LinkType linkType);
|
||||
void DoSetLinkType(LinkType linkType) noexcept;
|
||||
void SetChannel(ChannelType c) noexcept;
|
||||
private:
|
||||
|
||||
Track* GetLinkedTrack() const;
|
||||
//! Returns true for leaders of multichannel groups
|
||||
bool HasLinkedTrack() const noexcept;
|
||||
|
||||
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);
|
||||
|
||||
//! Retrieve mNode with debug checks
|
||||
TrackNodePointer GetNode() const;
|
||||
@ -1487,16 +1496,18 @@ public:
|
||||
template<typename TrackKind>
|
||||
TrackKind *Add( const std::shared_ptr< TrackKind > &t )
|
||||
{ return static_cast< TrackKind* >( DoAdd( 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.
|
||||
|
||||
//! Removes linkage if track belongs to a group
|
||||
void UnlinkChannels(Track& track);
|
||||
/** \brief Converts channels to a multichannel track.
|
||||
* @param first and the following must be in this list. Tracks should
|
||||
* not be a part of another group (not linked)
|
||||
* @param nChannels number of channels, for now only 2 channels supported
|
||||
* @param aligned if true, the link type will be set to Track::LinkType::Aligned,
|
||||
* or Track::LinkType::Group otherwise
|
||||
* @returns true on success, false if some prerequisites do not met
|
||||
*/
|
||||
void GroupChannels(
|
||||
Track &track, size_t groupSize, bool resetChannels = true );
|
||||
bool MakeMultiChannelTrack(Track& first, int nChannels, bool aligned);
|
||||
|
||||
/// Replace first track with second track, give back a holder
|
||||
/// Give the replacement the same id as the replaced
|
||||
|
@ -35,6 +35,7 @@ from the project that will own the track.
|
||||
#include <wx/defs.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/debug.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
@ -64,6 +65,33 @@ from the project that will own the track.
|
||||
|
||||
using std::max;
|
||||
|
||||
namespace {
|
||||
|
||||
bool AreAligned(const WaveClipPointers& a, const WaveClipPointers& b)
|
||||
{
|
||||
if (a.size() != b.size())
|
||||
return false;
|
||||
|
||||
const auto compare = [](const WaveClip* a, const WaveClip* b) {
|
||||
return a->GetStartTime() == b->GetStartTime() &&
|
||||
a->GetNumSamples() == b->GetNumSamples();
|
||||
};
|
||||
|
||||
return std::mismatch(a.begin(), a.end(), b.begin(), compare).first == a.end();
|
||||
}
|
||||
|
||||
//Handles possible future file values
|
||||
Track::LinkType ToLinkType(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return Track::LinkType::None;
|
||||
else if (value > 3)
|
||||
return Track::LinkType::Group;
|
||||
return static_cast<Track::LinkType>(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static ProjectFileIORegistry::Entry registerFactory{
|
||||
wxT( "wavetrack" ),
|
||||
[]( AudacityProject &project ){
|
||||
@ -240,7 +268,39 @@ void WaveTrack::SetPanFromChannelType()
|
||||
SetPan( -1.0f );
|
||||
else if( mChannel == Track::RightChannel )
|
||||
SetPan( 1.0f );
|
||||
};
|
||||
}
|
||||
|
||||
bool WaveTrack::LinkConsistencyCheck()
|
||||
{
|
||||
auto err = PlayableTrack::LinkConsistencyCheck();
|
||||
|
||||
auto linkType = GetLinkType();
|
||||
if (static_cast<int>(linkType) == 1 || //Comes from old audacity version
|
||||
linkType == LinkType::Aligned)
|
||||
{
|
||||
auto next = dynamic_cast<WaveTrack*>(*std::next(GetOwner()->Find(this)));
|
||||
if (next == nullptr)
|
||||
{
|
||||
//next track is not a wave track, fix and report error
|
||||
wxLogWarning(
|
||||
wxT("Right track %s is expected to be a WaveTrack.\n Removing link from left wave track %s."),
|
||||
next->GetName(), GetName());
|
||||
SetLinkType(LinkType::None);
|
||||
SetChannel(MonoChannel);
|
||||
err = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto newLinkType = AreAligned(SortedClipArray(), next->SortedClipArray())
|
||||
? LinkType::Aligned : LinkType::Group;
|
||||
//not an error
|
||||
if (newLinkType != linkType)
|
||||
SetLinkType(newLinkType);
|
||||
}
|
||||
}
|
||||
return !err;
|
||||
}
|
||||
|
||||
|
||||
void WaveTrack::SetLastScaleType() const
|
||||
{
|
||||
@ -1667,7 +1727,7 @@ bool WaveTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
|
||||
}
|
||||
else if (!wxStrcmp(attr, wxT("linked")) &&
|
||||
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
|
||||
SetLinked(nValue != 0);
|
||||
SetLinkType(ToLinkType(nValue));
|
||||
else if (!wxStrcmp(attr, wxT("colorindex")) &&
|
||||
XMLValueChecker::IsGoodString(strValue) &&
|
||||
strValue.ToLong(&nValue))
|
||||
@ -1734,7 +1794,7 @@ void WaveTrack::WriteXML(XMLWriter &xmlFile) const
|
||||
xmlFile.StartTag(wxT("wavetrack"));
|
||||
this->Track::WriteCommonXMLAttributes( xmlFile );
|
||||
xmlFile.WriteAttr(wxT("channel"), mChannel);
|
||||
xmlFile.WriteAttr(wxT("linked"), mLinked);
|
||||
xmlFile.WriteAttr(wxT("linked"), static_cast<int>(GetLinkType()));
|
||||
this->PlayableTrack::WriteXMLAttributes(xmlFile);
|
||||
xmlFile.WriteAttr(wxT("rate"), mRate);
|
||||
xmlFile.WriteAttr(wxT("gain"), (double)mGain);
|
||||
|
@ -100,6 +100,8 @@ private:
|
||||
ChannelType GetChannel() const override;
|
||||
virtual void SetPanFromChannelType() override;
|
||||
|
||||
bool LinkConsistencyCheck() override;
|
||||
|
||||
/** @brief Get the time at which the first clip in the track starts
|
||||
*
|
||||
* @return time in seconds, or zero if there are no clips in the track
|
||||
|
@ -2395,8 +2395,8 @@ void Effect::Preview(bool dryOnly)
|
||||
mixRight->Offset(-mixRight->GetStartTime());
|
||||
mixRight->SetSelected(true);
|
||||
pRight = mTracks->Add( mixRight );
|
||||
mTracks->MakeMultiChannelTrack(*pLeft, 2, true);
|
||||
}
|
||||
mTracks->GroupChannels(*pLeft, pRight ? 2 : 1);
|
||||
}
|
||||
else {
|
||||
for (auto src : saveTracks->Any< const WaveTrack >()) {
|
||||
|
@ -213,7 +213,7 @@ bool EffectStereoToMono::ProcessOne(sampleCount & curTime, sampleCount totalTime
|
||||
double minStart = wxMin(left->GetStartTime(), right->GetStartTime());
|
||||
left->Clear(left->GetStartTime(), left->GetEndTime());
|
||||
left->Paste(minStart, outTrack.get());
|
||||
mOutputTracks->GroupChannels(*left, 1);
|
||||
mOutputTracks->UnlinkChannels(*left);
|
||||
mOutputTracks->Remove(right);
|
||||
|
||||
return bResult;
|
||||
|
@ -607,6 +607,7 @@ void OnPaste(const CommandContext &context)
|
||||
if (ff) {
|
||||
TrackFocus::Get(project).Set(ff);
|
||||
ff->EnsureVisible();
|
||||
ff->LinkConsistencyCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,10 +87,10 @@ void DoMixAndRender
|
||||
auto pNewLeft = tracks.Add( uNewLeft );
|
||||
decltype(pNewLeft) pNewRight{};
|
||||
if (uNewRight)
|
||||
pNewRight = tracks.Add( uNewRight );
|
||||
|
||||
// Do this only after adding tracks to the list
|
||||
tracks.GroupChannels(*pNewLeft, pNewRight ? 2 : 1);
|
||||
{
|
||||
pNewRight = tracks.Add(uNewRight);
|
||||
tracks.MakeMultiChannelTrack(*pNewLeft, 2, true);
|
||||
}
|
||||
|
||||
// If we're just rendering (not mixing), keep the track name the same
|
||||
if (selectedCount==1) {
|
||||
@ -644,7 +644,7 @@ void OnNewStereoTrack(const CommandContext &context)
|
||||
auto right = tracks.Add( trackFactory.NewWaveTrack( defaultFormat, rate ) );
|
||||
right->SetSelected(true);
|
||||
|
||||
tracks.GroupChannels(*left, 2);
|
||||
tracks.MakeMultiChannelTrack(*left, 2, true);
|
||||
|
||||
ProjectHistory::Get( project )
|
||||
.PushState(XO("Created new stereo audio track"), XO("New Track"));
|
||||
|
@ -828,7 +828,7 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
|
||||
((TrackView::Get( *pTrack ).GetMinimized()) &&
|
||||
(TrackView::Get( *partner ).GetMinimized()));
|
||||
|
||||
tracks.GroupChannels( *pTrack, 2 );
|
||||
tracks.MakeMultiChannelTrack( *pTrack, 2, false );
|
||||
|
||||
// Set partner's parameters to match target.
|
||||
partner->Merge(*pTrack);
|
||||
@ -884,7 +884,7 @@ void WaveTrackMenuTable::SplitStereo(bool stereo)
|
||||
++nChannels;
|
||||
}
|
||||
|
||||
TrackList::Get( *project ).GroupChannels( *pTrack, 1 );
|
||||
TrackList::Get( *project ).UnlinkChannels( *pTrack );
|
||||
int averageHeight = totalHeight / nChannels;
|
||||
|
||||
for (auto channel : channels)
|
||||
@ -898,6 +898,7 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
|
||||
AudacityProject *const project = &mpData->project;
|
||||
|
||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||
const auto linkType = pTrack->GetLinkType();
|
||||
auto channels = TrackList::Channels( pTrack );
|
||||
if (channels.size() != 2)
|
||||
return;
|
||||
@ -911,9 +912,8 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
|
||||
SplitStereo(false);
|
||||
|
||||
auto &tracks = TrackList::Get( *project );
|
||||
tracks.MoveUp( partner );
|
||||
tracks.GroupChannels( *partner, 2 );
|
||||
|
||||
tracks.MoveUp(partner);
|
||||
tracks.MakeMultiChannelTrack(*partner, 2, linkType == Track::LinkType::Aligned);
|
||||
if (hasFocus)
|
||||
trackFocus.Set(partner);
|
||||
|
||||
|
@ -961,6 +961,8 @@ UIHandle::Result TimeShiftHandle::Release
|
||||
if (mDidSlideVertically) {
|
||||
msg = XO("Moved clips to another track");
|
||||
consolidate = false;
|
||||
for (auto& pair : mClipMoveState.shifters)
|
||||
pair.first->LinkConsistencyCheck();
|
||||
}
|
||||
else {
|
||||
msg = ( mClipMoveState.hSlideAmount > 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user