mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-19 00:51:12 +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:
committed by
akleja
parent
39d49be5b4
commit
f969701756
@@ -722,7 +722,7 @@ bool ProjectAudioManager::DoRecord(AudacityProject &project,
|
|||||||
|
|
||||||
transportTracks.captureTracks.push_back(newTrack);
|
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.
|
// Bug 1548. First of new tracks needs the focus.
|
||||||
TrackFocus::Get(*p).Set(first);
|
TrackFocus::Get(*p).Set(first);
|
||||||
if (TrackList::Get(*p).back())
|
if (TrackList::Get(*p).back())
|
||||||
|
@@ -1113,7 +1113,7 @@ ProjectFileManager::AddImportedTracks(const FilePath &fileName,
|
|||||||
auto newTrack = tracks.Add( uNewTrack );
|
auto newTrack = tracks.Add( uNewTrack );
|
||||||
results.push_back(newTrack->SharedPointer());
|
results.push_back(newTrack->SharedPointer());
|
||||||
}
|
}
|
||||||
tracks.GroupChannels(*first, nChannels);
|
tracks.MakeMultiChannelTrack(*first, nChannels, true);
|
||||||
}
|
}
|
||||||
newTracks.clear();
|
newTracks.clear();
|
||||||
|
|
||||||
|
138
src/Track.cpp
138
src/Track.cpp
@@ -50,7 +50,6 @@ Track::Track()
|
|||||||
: vrulerSize(36,0)
|
: vrulerSize(36,0)
|
||||||
{
|
{
|
||||||
mSelected = false;
|
mSelected = false;
|
||||||
mLinked = false;
|
|
||||||
|
|
||||||
mIndex = 0;
|
mIndex = 0;
|
||||||
|
|
||||||
@@ -76,7 +75,7 @@ void Track::Init(const Track &orig)
|
|||||||
mName = orig.mName;
|
mName = orig.mName;
|
||||||
|
|
||||||
mSelected = orig.mSelected;
|
mSelected = orig.mSelected;
|
||||||
mLinked = orig.mLinked;
|
mLinkType = orig.mLinkType;
|
||||||
mChannel = orig.mChannel;
|
mChannel = orig.mChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,18 +171,18 @@ void Track::SetIndex(int index)
|
|||||||
mIndex = index;
|
mIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::SetLinked(bool l)
|
void Track::SetLinkType(LinkType linkType)
|
||||||
{
|
{
|
||||||
auto pList = mList.lock();
|
auto pList = mList.lock();
|
||||||
if (pList && !pList->mPendingUpdates.empty()) {
|
if (pList && !pList->mPendingUpdates.empty()) {
|
||||||
auto orig = pList->FindById( GetId() );
|
auto orig = pList->FindById( GetId() );
|
||||||
if (orig && orig != this) {
|
if (orig && orig != this) {
|
||||||
orig->SetLinked(l);
|
orig->SetLinkType(linkType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DoSetLinked(l);
|
DoSetLinkType(linkType);
|
||||||
|
|
||||||
if (pList) {
|
if (pList) {
|
||||||
pList->RecalcPositions(mNode);
|
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
|
Track *Track::GetLinkedTrack() const
|
||||||
@@ -224,7 +228,7 @@ Track *Track::GetLinkedTrack() const
|
|||||||
|
|
||||||
bool Track::HasLinkedTrack() const noexcept
|
bool Track::HasLinkedTrack() const noexcept
|
||||||
{
|
{
|
||||||
return mLinked;
|
return mLinkType != LinkType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -385,7 +389,7 @@ void Track::FinishCopy
|
|||||||
{
|
{
|
||||||
if (dest) {
|
if (dest) {
|
||||||
dest->SetChannel(n->GetChannel());
|
dest->SetChannel(n->GetChannel());
|
||||||
dest->SetLinked(n->HasLinkedTrack());
|
dest->SetLinkType(n->GetLinkType());
|
||||||
dest->SetName(n->GetName());
|
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."),
|
wxT("Left track %s had linked right track %s with extra right track link.\n Removing extra link from right track."),
|
||||||
GetName(), link->GetName());
|
GetName(), link->GetName());
|
||||||
err = true;
|
err = true;
|
||||||
link->SetLinked(false);
|
link->SetLinkType(LinkType::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Channels should be left and right
|
// 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."),
|
wxT("Track %s and %s had left/right track links out of order. Setting tracks to not be linked."),
|
||||||
GetName(), link->GetName());
|
GetName(), link->GetName());
|
||||||
err = true;
|
err = true;
|
||||||
SetLinked(false);
|
SetLinkType(LinkType::None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -430,7 +434,7 @@ bool Track::LinkConsistencyCheck()
|
|||||||
wxT("Track %s had link to NULL track. Setting it to not be linked."),
|
wxT("Track %s had link to NULL track. Setting it to not be linked."),
|
||||||
GetName());
|
GetName());
|
||||||
err = true;
|
err = true;
|
||||||
SetLinked(false);
|
SetLinkType(LinkType::None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,57 +715,6 @@ Track *TrackList::DoAdd(const std::shared_ptr<Track> &t)
|
|||||||
return back().get();
|
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) ->
|
auto TrackList::Replace(Track * t, const ListOfTracks::value_type &with) ->
|
||||||
ListOfTracks::value_type
|
ListOfTracks::value_type
|
||||||
{
|
{
|
||||||
@@ -784,6 +737,58 @@ auto TrackList::Replace(Track * t, const ListOfTracks::value_type &with) ->
|
|||||||
return holder;
|
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)
|
TrackNodePointer TrackList::Remove(Track *t)
|
||||||
{
|
{
|
||||||
auto result = getEnd();
|
auto result = getEnd();
|
||||||
@@ -1075,7 +1080,7 @@ void TrackList::UpdatePendingTracks()
|
|||||||
if (pendingTrack && src) {
|
if (pendingTrack && src) {
|
||||||
if (updater)
|
if (updater)
|
||||||
updater( *pendingTrack, *src );
|
updater( *pendingTrack, *src );
|
||||||
pendingTrack->DoSetLinked(src->HasLinkedTrack());
|
pendingTrack->DoSetLinkType(src->GetLinkType());
|
||||||
}
|
}
|
||||||
++pUpdater;
|
++pUpdater;
|
||||||
}
|
}
|
||||||
@@ -1303,3 +1308,8 @@ bool TrackList::HasPendingTracks() const
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
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 AttachedTrackObjects
|
||||||
, public std::enable_shared_from_this<Track> // see SharedPointer()
|
, 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;
|
friend class TrackList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TrackId mId; //!< Identifies the track only in-session, not persistently
|
TrackId mId; //!< Identifies the track only in-session, not persistently
|
||||||
|
LinkType mLinkType{ LinkType::None };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::weak_ptr<TrackList> mList; //!< Back pointer to owning TrackList
|
std::weak_ptr<TrackList> mList; //!< Back pointer to owning TrackList
|
||||||
@@ -253,9 +265,6 @@ class TENACITY_DLL_API Track /* not final */
|
|||||||
private:
|
private:
|
||||||
bool mSelected;
|
bool mSelected;
|
||||||
|
|
||||||
protected:
|
|
||||||
bool mLinked;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
//! Alias for my base type
|
//! Alias for my base type
|
||||||
@@ -360,25 +369,25 @@ public:
|
|||||||
static void FinishCopy (const Track *n, Track *dest);
|
static void FinishCopy (const Track *n, Track *dest);
|
||||||
|
|
||||||
// For use when loading a file. Return true if ok, else make repair
|
// 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());}
|
bool HasOwner() const { return static_cast<bool>(GetOwner());}
|
||||||
|
|
||||||
std::shared_ptr<TrackList> GetOwner() const { return mList.lock(); }
|
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:
|
private:
|
||||||
|
|
||||||
Track* GetLinkedTrack() const;
|
Track* GetLinkedTrack() const;
|
||||||
//! Returns true for leaders of multichannel groups
|
//! Returns true for leaders of multichannel groups
|
||||||
bool HasLinkedTrack() const noexcept;
|
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
|
//! Retrieve mNode with debug checks
|
||||||
TrackNodePointer GetNode() const;
|
TrackNodePointer GetNode() const;
|
||||||
@@ -1487,16 +1496,18 @@ public:
|
|||||||
template<typename TrackKind>
|
template<typename TrackKind>
|
||||||
TrackKind *Add( const std::shared_ptr< TrackKind > &t )
|
TrackKind *Add( const std::shared_ptr< TrackKind > &t )
|
||||||
{ return static_cast< TrackKind* >( DoAdd( t ) ); }
|
{ return static_cast< TrackKind* >( DoAdd( t ) ); }
|
||||||
|
|
||||||
/** \brief Define a group of channels starting at the given track
|
//! Removes linkage if track belongs to a group
|
||||||
*
|
void UnlinkChannels(Track& track);
|
||||||
* @param track and (groupSize - 1) following tracks must be in this
|
/** \brief Converts channels to a multichannel track.
|
||||||
* list. They will be disassociated from any groups they already belong to.
|
* @param first and the following must be in this list. Tracks should
|
||||||
* @param groupSize must be at least 1.
|
* not be a part of another group (not linked)
|
||||||
* @param resetChannels if true, disassociated channels will be marked Mono.
|
* @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(
|
bool MakeMultiChannelTrack(Track& first, int nChannels, bool aligned);
|
||||||
Track &track, size_t groupSize, bool resetChannels = true );
|
|
||||||
|
|
||||||
/// Replace first track with second track, give back a holder
|
/// Replace first track with second track, give back a holder
|
||||||
/// Give the replacement the same id as the replaced
|
/// 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/defs.h>
|
||||||
#include <wx/intl.h>
|
#include <wx/intl.h>
|
||||||
#include <wx/debug.h>
|
#include <wx/debug.h>
|
||||||
|
#include <wx/log.h>
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@@ -64,6 +65,33 @@ from the project that will own the track.
|
|||||||
|
|
||||||
using std::max;
|
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{
|
static ProjectFileIORegistry::Entry registerFactory{
|
||||||
wxT( "wavetrack" ),
|
wxT( "wavetrack" ),
|
||||||
[]( AudacityProject &project ){
|
[]( AudacityProject &project ){
|
||||||
@@ -240,7 +268,39 @@ void WaveTrack::SetPanFromChannelType()
|
|||||||
SetPan( -1.0f );
|
SetPan( -1.0f );
|
||||||
else if( mChannel == Track::RightChannel )
|
else if( mChannel == Track::RightChannel )
|
||||||
SetPan( 1.0f );
|
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
|
void WaveTrack::SetLastScaleType() const
|
||||||
{
|
{
|
||||||
@@ -1667,7 +1727,7 @@ bool WaveTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
|
|||||||
}
|
}
|
||||||
else if (!wxStrcmp(attr, wxT("linked")) &&
|
else if (!wxStrcmp(attr, wxT("linked")) &&
|
||||||
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
|
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
|
||||||
SetLinked(nValue != 0);
|
SetLinkType(ToLinkType(nValue));
|
||||||
else if (!wxStrcmp(attr, wxT("colorindex")) &&
|
else if (!wxStrcmp(attr, wxT("colorindex")) &&
|
||||||
XMLValueChecker::IsGoodString(strValue) &&
|
XMLValueChecker::IsGoodString(strValue) &&
|
||||||
strValue.ToLong(&nValue))
|
strValue.ToLong(&nValue))
|
||||||
@@ -1734,7 +1794,7 @@ void WaveTrack::WriteXML(XMLWriter &xmlFile) const
|
|||||||
xmlFile.StartTag(wxT("wavetrack"));
|
xmlFile.StartTag(wxT("wavetrack"));
|
||||||
this->Track::WriteCommonXMLAttributes( xmlFile );
|
this->Track::WriteCommonXMLAttributes( xmlFile );
|
||||||
xmlFile.WriteAttr(wxT("channel"), mChannel);
|
xmlFile.WriteAttr(wxT("channel"), mChannel);
|
||||||
xmlFile.WriteAttr(wxT("linked"), mLinked);
|
xmlFile.WriteAttr(wxT("linked"), static_cast<int>(GetLinkType()));
|
||||||
this->PlayableTrack::WriteXMLAttributes(xmlFile);
|
this->PlayableTrack::WriteXMLAttributes(xmlFile);
|
||||||
xmlFile.WriteAttr(wxT("rate"), mRate);
|
xmlFile.WriteAttr(wxT("rate"), mRate);
|
||||||
xmlFile.WriteAttr(wxT("gain"), (double)mGain);
|
xmlFile.WriteAttr(wxT("gain"), (double)mGain);
|
||||||
|
@@ -100,6 +100,8 @@ private:
|
|||||||
ChannelType GetChannel() const override;
|
ChannelType GetChannel() const override;
|
||||||
virtual void SetPanFromChannelType() override;
|
virtual void SetPanFromChannelType() override;
|
||||||
|
|
||||||
|
bool LinkConsistencyCheck() override;
|
||||||
|
|
||||||
/** @brief Get the time at which the first clip in the track starts
|
/** @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
|
* @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->Offset(-mixRight->GetStartTime());
|
||||||
mixRight->SetSelected(true);
|
mixRight->SetSelected(true);
|
||||||
pRight = mTracks->Add( mixRight );
|
pRight = mTracks->Add( mixRight );
|
||||||
|
mTracks->MakeMultiChannelTrack(*pLeft, 2, true);
|
||||||
}
|
}
|
||||||
mTracks->GroupChannels(*pLeft, pRight ? 2 : 1);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (auto src : saveTracks->Any< const WaveTrack >()) {
|
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());
|
double minStart = wxMin(left->GetStartTime(), right->GetStartTime());
|
||||||
left->Clear(left->GetStartTime(), left->GetEndTime());
|
left->Clear(left->GetStartTime(), left->GetEndTime());
|
||||||
left->Paste(minStart, outTrack.get());
|
left->Paste(minStart, outTrack.get());
|
||||||
mOutputTracks->GroupChannels(*left, 1);
|
mOutputTracks->UnlinkChannels(*left);
|
||||||
mOutputTracks->Remove(right);
|
mOutputTracks->Remove(right);
|
||||||
|
|
||||||
return bResult;
|
return bResult;
|
||||||
|
@@ -607,6 +607,7 @@ void OnPaste(const CommandContext &context)
|
|||||||
if (ff) {
|
if (ff) {
|
||||||
TrackFocus::Get(project).Set(ff);
|
TrackFocus::Get(project).Set(ff);
|
||||||
ff->EnsureVisible();
|
ff->EnsureVisible();
|
||||||
|
ff->LinkConsistencyCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -87,10 +87,10 @@ void DoMixAndRender
|
|||||||
auto pNewLeft = tracks.Add( uNewLeft );
|
auto pNewLeft = tracks.Add( uNewLeft );
|
||||||
decltype(pNewLeft) pNewRight{};
|
decltype(pNewLeft) pNewRight{};
|
||||||
if (uNewRight)
|
if (uNewRight)
|
||||||
pNewRight = tracks.Add( uNewRight );
|
{
|
||||||
|
pNewRight = tracks.Add(uNewRight);
|
||||||
// Do this only after adding tracks to the list
|
tracks.MakeMultiChannelTrack(*pNewLeft, 2, true);
|
||||||
tracks.GroupChannels(*pNewLeft, pNewRight ? 2 : 1);
|
}
|
||||||
|
|
||||||
// If we're just rendering (not mixing), keep the track name the same
|
// If we're just rendering (not mixing), keep the track name the same
|
||||||
if (selectedCount==1) {
|
if (selectedCount==1) {
|
||||||
@@ -644,7 +644,7 @@ void OnNewStereoTrack(const CommandContext &context)
|
|||||||
auto right = tracks.Add( trackFactory.NewWaveTrack( defaultFormat, rate ) );
|
auto right = tracks.Add( trackFactory.NewWaveTrack( defaultFormat, rate ) );
|
||||||
right->SetSelected(true);
|
right->SetSelected(true);
|
||||||
|
|
||||||
tracks.GroupChannels(*left, 2);
|
tracks.MakeMultiChannelTrack(*left, 2, true);
|
||||||
|
|
||||||
ProjectHistory::Get( project )
|
ProjectHistory::Get( project )
|
||||||
.PushState(XO("Created new stereo audio track"), XO("New Track"));
|
.PushState(XO("Created new stereo audio track"), XO("New Track"));
|
||||||
|
@@ -828,7 +828,7 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
|
|||||||
((TrackView::Get( *pTrack ).GetMinimized()) &&
|
((TrackView::Get( *pTrack ).GetMinimized()) &&
|
||||||
(TrackView::Get( *partner ).GetMinimized()));
|
(TrackView::Get( *partner ).GetMinimized()));
|
||||||
|
|
||||||
tracks.GroupChannels( *pTrack, 2 );
|
tracks.MakeMultiChannelTrack( *pTrack, 2, false );
|
||||||
|
|
||||||
// Set partner's parameters to match target.
|
// Set partner's parameters to match target.
|
||||||
partner->Merge(*pTrack);
|
partner->Merge(*pTrack);
|
||||||
@@ -884,7 +884,7 @@ void WaveTrackMenuTable::SplitStereo(bool stereo)
|
|||||||
++nChannels;
|
++nChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackList::Get( *project ).GroupChannels( *pTrack, 1 );
|
TrackList::Get( *project ).UnlinkChannels( *pTrack );
|
||||||
int averageHeight = totalHeight / nChannels;
|
int averageHeight = totalHeight / nChannels;
|
||||||
|
|
||||||
for (auto channel : channels)
|
for (auto channel : channels)
|
||||||
@@ -898,6 +898,7 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
|
|||||||
AudacityProject *const project = &mpData->project;
|
AudacityProject *const project = &mpData->project;
|
||||||
|
|
||||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||||
|
const auto linkType = pTrack->GetLinkType();
|
||||||
auto channels = TrackList::Channels( pTrack );
|
auto channels = TrackList::Channels( pTrack );
|
||||||
if (channels.size() != 2)
|
if (channels.size() != 2)
|
||||||
return;
|
return;
|
||||||
@@ -911,9 +912,8 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
|
|||||||
SplitStereo(false);
|
SplitStereo(false);
|
||||||
|
|
||||||
auto &tracks = TrackList::Get( *project );
|
auto &tracks = TrackList::Get( *project );
|
||||||
tracks.MoveUp( partner );
|
tracks.MoveUp(partner);
|
||||||
tracks.GroupChannels( *partner, 2 );
|
tracks.MakeMultiChannelTrack(*partner, 2, linkType == Track::LinkType::Aligned);
|
||||||
|
|
||||||
if (hasFocus)
|
if (hasFocus)
|
||||||
trackFocus.Set(partner);
|
trackFocus.Set(partner);
|
||||||
|
|
||||||
|
@@ -961,6 +961,8 @@ UIHandle::Result TimeShiftHandle::Release
|
|||||||
if (mDidSlideVertically) {
|
if (mDidSlideVertically) {
|
||||||
msg = XO("Moved clips to another track");
|
msg = XO("Moved clips to another track");
|
||||||
consolidate = false;
|
consolidate = false;
|
||||||
|
for (auto& pair : mClipMoveState.shifters)
|
||||||
|
pair.first->LinkConsistencyCheck();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
msg = ( mClipMoveState.hSlideAmount > 0
|
msg = ( mClipMoveState.hSlideAmount > 0
|
||||||
|
Reference in New Issue
Block a user