Skip to content

Commit

Permalink
Merge pull request #2805 from Paul-Licameli/Non-display-channel-group…
Browse files Browse the repository at this point in the history
…-data

Non display channel group data

Move many things into ChannelGropuAttachments, so they are not duplicated between channels.

This simplifies code that formerly had to maintain consistency of channels. There are fewer uses of TrackList::Channels
and fewer iterations over all tracks when iteration over leaders only is enough.

Remove some other calls to TrackList::Channels() that were needed only to count the channels.

Also fixes an issue related to correctness of Mute and Solo settings when copying and pasting or duplicating MIDI or
audio tracks.

Also a new small library lib-playable-track to lift the mute and solo logic out of lib-track.
  • Loading branch information
Paul-Licameli committed May 24, 2023
2 parents b8501b2 + f04177f commit 7a258a0
Show file tree
Hide file tree
Showing 60 changed files with 741 additions and 753 deletions.
1 change: 1 addition & 0 deletions libraries/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ set( LIBRARIES
lib-lv2
lib-ladspa
lib-audio-unit
lib-playable-track
)

if ( ${_OPT}has_networking )
Expand Down
6 changes: 3 additions & 3 deletions libraries/lib-audio-io/AudioIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ struct AudioIoCallback::TransportState {
wxASSERT(false);
continue;
}
unsigned chanCnt = TrackList::Channels(vt).size();
unsigned chanCnt = TrackList::NChannels(*vt);
i += chanCnt; // Visit leaders only
mpRealtimeInitialization
->AddTrack(*vt, numPlaybackChannels, sampleRate);
Expand Down Expand Up @@ -1981,7 +1981,7 @@ bool AudioIO::ProcessPlaybackSlices(
produced = mixer->Process( toProduce );
//wxASSERT(produced <= toProduce);
for(size_t j = 0, nChannels =
TrackList::Channels(mPlaybackTracks[i].get()).size();
TrackList::NChannels(*mPlaybackTracks[i]);
j < nChannels; ++i, ++j
) {
auto warpedSamples = mixer->GetBuffer(j);
Expand Down Expand Up @@ -2028,7 +2028,7 @@ void AudioIO::TransformPlayBuffers(
continue;
// vt is mono, or is the first of its group of channels
const auto nChannels = std::min<size_t>(
mNumPlaybackChannels, TrackList::Channels(vt).size());
mNumPlaybackChannels, TrackList::NChannels(*vt));

// Loop over the blocks of unflushed data, at most two
for (unsigned iBlock : {0, 1}) {
Expand Down
5 changes: 2 additions & 3 deletions libraries/lib-effects/EffectBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,22 +427,21 @@ void EffectBase::Preview(
return;

mixLeft->Offset(-mixLeft->GetStartTime());
mixLeft->SetSelected(true);
auto pLeft = mTracks->Add( mixLeft );
Track *pRight{};
if (mixRight) {
mixRight->Offset(-mixRight->GetStartTime());
mixRight->SetSelected(true);
pRight = mTracks->Add( mixRight );
mTracks->MakeMultiChannelTrack(*pLeft, 2, true);
}
mixLeft->SetSelected(true);
}
else {
for (auto src : saveTracks->Any< const WaveTrack >()) {
if (src->GetSelected() || mPreviewWithNotSelected) {
auto dest = src->Copy(mT0, t1);
dest->SetSelected(src->GetSelected());
mTracks->Add( dest );
dest->SetSelected(src->GetSelected());
}
}
}
Expand Down
16 changes: 3 additions & 13 deletions libraries/lib-effects/MixAndRender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ void MixAndRender(const TrackIterRange<const WaveTrack> &trackRange,
int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/
for(auto wt : trackRange) {
numWaves++;
float pan = wt->GetPan();
if (wt->GetChannel() == Track::MonoChannel && pan == 0)
if (wt->GetChannel() == Track::MonoChannel && wt->GetPan() == 0)
numMono++;
}

Expand Down Expand Up @@ -91,7 +90,7 @@ void MixAndRender(const TrackIterRange<const WaveTrack> &trackRange,
}

/* create the destination track (NEW track) */
if (numWaves == (int)TrackList::Channels(first).size())
if (numWaves == (int)TrackList::NChannels(*first))
oneinput = true;
// only one input track (either 1 mono or one linked stereo pair)

Expand All @@ -113,17 +112,8 @@ void MixAndRender(const TrackIterRange<const WaveTrack> &trackRange,

// TODO: more-than-two-channels
decltype(mixLeft) mixRight{};
if ( !mono ) {
if (!mono) {
mixRight = trackFactory->Create(format, rate);
if (oneinput) {
auto channels = TrackList::Channels(first);
if (channels.size() > 1)
mixRight->SetName((*channels.begin().advance(1))->GetName()); /* set name to match input track's right channel!*/
else
mixRight->SetName(first->GetName()); /* set name to that of sole input channel */
}
else
mixRight->SetName(newTrackName);
mixRight->SetOffset(mixStartTime);
}

Expand Down
14 changes: 14 additions & 0 deletions libraries/lib-playable-track/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[[
Extends Track with subclasses defining the notions of mute and solo.
]]

set( SOURCES
PlayableTrack.cpp
PlayableTrack.h
)
set( LIBRARIES
lib-track-interface
)
audacity_library( lib-playable-track "${SOURCES}" "${LIBRARIES}"
"" ""
)
197 changes: 197 additions & 0 deletions libraries/lib-playable-track/PlayableTrack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*!********************************************************************
Audacity: A Digital Audio Editor
PlayableTrack.cpp
Dominic Mazzoni
Paul Licameli split from Track.cpp
*******************************************************************//**
\class AudioTrack
\brief A Track that can load/save audio data to/from XML.
\class PlayableTrack
\brief An AudioTrack that can be played and stopped.
*//*******************************************************************/
#include "PlayableTrack.h"

namespace {
struct MuteAndSolo : ClientData::Cloneable<> {
MuteAndSolo() = default;
MuteAndSolo(const MuteAndSolo &);
MuteAndSolo& operator=(const MuteAndSolo &) = delete;
~MuteAndSolo() override;
std::unique_ptr<ClientData::Cloneable<>> Clone() const override;

static MuteAndSolo &Get(PlayableTrack &track);
static const MuteAndSolo &Get(const PlayableTrack &track);

bool GetMute() const;
void SetMute(bool value);
bool GetSolo() const;
void SetSolo(bool value);

private:
//! Atomic because it may be read by worker threads in playback
std::atomic<bool> mMute{ false };
//! Atomic because it may be read by worker threads in playback
std::atomic<bool> mSolo{ false };
};

static const Track::ChannelGroupAttachments::RegisteredFactory
muteAndSoloFactory{ [](auto &) { return std::make_unique<MuteAndSolo>(); } };

//! Copy can't be generated by default because of mutable members
MuteAndSolo::MuteAndSolo(const MuteAndSolo &other) {
SetMute(other.GetMute());
SetSolo(other.GetSolo());
}

MuteAndSolo::~MuteAndSolo() = default;

std::unique_ptr<ClientData::Cloneable<>> MuteAndSolo::Clone() const {
return std::make_unique<MuteAndSolo>(*this);
}

MuteAndSolo &MuteAndSolo::Get(PlayableTrack &track) {
return track.GetGroupData().Track::ChannelGroupAttachments
::Get<MuteAndSolo>(muteAndSoloFactory);
}

const MuteAndSolo &MuteAndSolo::Get(const PlayableTrack &track)
{
return Get(const_cast<PlayableTrack &>(track));
}

bool MuteAndSolo::GetMute() const
{
return mMute.load(std::memory_order_relaxed);
}

void MuteAndSolo::SetMute(bool value)
{
mMute.store(value, std::memory_order_relaxed);
}

bool MuteAndSolo::GetSolo() const
{
return mSolo.load(std::memory_order_relaxed);
}

void MuteAndSolo::SetSolo(bool value)
{
mSolo.store(value, std::memory_order_relaxed);
}
}


AudioTrack::AudioTrack() : Track{}
{
}

AudioTrack::AudioTrack(const Track &orig, ProtectedCreationArg &&a)
: Track{ orig, std::move(a) }
{
}

PlayableTrack::PlayableTrack() : AudioTrack{}
{
}

PlayableTrack::PlayableTrack(
const PlayableTrack &orig, ProtectedCreationArg &&a
) : AudioTrack{ orig, std::move(a) }
{
}

void PlayableTrack::SetMute(bool m)
{
if (DoGetMute() != m) {
DoSetMute(m);
Notify(true);
}
}

void PlayableTrack::SetSolo(bool s)
{
if (DoGetSolo() != s) {
DoSetSolo(s);
Notify(true);
}
}

bool PlayableTrack::DoGetMute() const
{
return MuteAndSolo::Get(*this).GetMute();
}

void PlayableTrack::DoSetMute(bool value)
{
MuteAndSolo::Get(*this).SetMute(value);
}

bool PlayableTrack::DoGetSolo() const
{
return MuteAndSolo::Get(*this).GetSolo();
}

void PlayableTrack::DoSetSolo(bool value)
{
MuteAndSolo::Get(*this).SetSolo(value);
}

// Serialize, not with tags of its own, but as attributes within a tag.
void PlayableTrack::WriteXMLAttributes(XMLWriter &xmlFile) const
{
xmlFile.WriteAttr(wxT("mute"), DoGetMute());
xmlFile.WriteAttr(wxT("solo"), DoGetSolo());
AudioTrack::WriteXMLAttributes(xmlFile);
}

// Return true iff the attribute is recognized.
bool PlayableTrack::HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
{
long nValue;

if (attr == "mute" && value.TryGet(nValue)) {
DoSetMute(nValue != 0);
return true;
}
else if (attr == "solo" && value.TryGet(nValue)) {
DoSetSolo(nValue != 0);
return true;
}

return AudioTrack::HandleXMLAttribute(attr, value);
}

auto AudioTrack::ClassTypeInfo() -> const TypeInfo &
{
static Track::TypeInfo info{
{ "audio", "audio", XO("Audio Track") },
false, &Track::ClassTypeInfo() };
return info;
}

auto PlayableTrack::ClassTypeInfo() -> const TypeInfo &
{
static Track::TypeInfo info{
{ "playable", "playable", XO("Playable Track") },
false, &AudioTrack::ClassTypeInfo() };
return info;
}

EnumSetting<SoloBehavior> TracksBehaviorsSolo{
wxT("/GUI/Solo"),
{
ByColumns,
{ XO("Simple"), XO("Multi-track"), XO("None") },
{ wxT("Simple"), wxT("Multi"), wxT("None") }
},
0, // "Simple"
{ SoloBehaviorSimple, SoloBehaviorMulti, SoloBehaviorNone },
};
77 changes: 77 additions & 0 deletions libraries/lib-playable-track/PlayableTrack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*!********************************************************************
Audacity: A Digital Audio Editor
@file PlayableTrack.h
@brief Extends Track with notions of mute and solo setting
Dominic Mazzoni
Paul Licameli split from Track.h
**********************************************************************/
#ifndef __AUDACITY_PLAYABLE_TRACK__
#define __AUDACITY_PLAYABLE_TRACK__

#include "Prefs.h"
#include "Track.h"

//! Track subclass holding data representing sound (as notes, or samples, or ...)
class PLAYABLE_TRACK_API AudioTrack /* not final */ : public Track
{
public:
AudioTrack();
AudioTrack(const Track &orig, ProtectedCreationArg &&a);

static const TypeInfo &ClassTypeInfo();

// Serialize, not with tags of its own, but as attributes within a tag.
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const {}

// Return true iff the attribute is recognized.
bool HandleXMLAttribute(const std::string_view & /*attr*/, const XMLAttributeValueView &/*value*/)
{ return false; }
};

ENUMERATE_TRACK_TYPE(AudioTrack);

//! AudioTrack subclass that can also be audibly replayed by the program
class PLAYABLE_TRACK_API PlayableTrack /* not final */ : public AudioTrack
{
public:
PlayableTrack();
PlayableTrack(const PlayableTrack &orig, ProtectedCreationArg&&);

static const TypeInfo &ClassTypeInfo();

bool GetMute () const { return DoGetMute(); }
bool GetSolo () const { return DoGetSolo(); }
bool GetNotMute () const { return !DoGetMute(); }
bool GetNotSolo () const { return !DoGetSolo(); }
void SetMute (bool m);
void SetSolo (bool s);

// Serialize, not with tags of its own, but as attributes within a tag.
void WriteXMLAttributes(XMLWriter &xmlFile) const;

// Return true iff the attribute is recognized.
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value);

protected:
bool DoGetMute() const;
void DoSetMute(bool value);
bool DoGetSolo() const;
void DoSetSolo(bool value);
};

ENUMERATE_TRACK_TYPE(PlayableTrack);

enum SoloBehavior {
SoloBehaviorSimple,
SoloBehaviorMulti,
SoloBehaviorNone,
};

extern PLAYABLE_TRACK_API EnumSetting<SoloBehavior> TracksBehaviorsSolo;

#endif
2 changes: 1 addition & 1 deletion libraries/lib-realtime-effects/RealtimeEffectList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const RealtimeEffectList &RealtimeEffectList::Get(const AudacityProject &project
}

static const Track::ChannelGroupAttachments::RegisteredFactory trackEffects
{
{
[](Track::ChannelGroupData &)
{
return std::make_unique<RealtimeEffectList>();
Expand Down

0 comments on commit 7a258a0

Please sign in to comment.