A very extensive rewrite.
No longer:
1 Test a type field of a track and then do a C-style pointer down-cast
2 Use verbose old iterators over tracks that required those down-casts
3 Detect that a track has a stereo partner by testing Track::GetLink();
Detect that a track is the left stereo channel by testing Track::GetLinked()
4 Do something to one channel of a pair, then repeat on the other channel
5 Set up new or imported stereo tracks with Track::SetLinked() and setting of
channel types
Instead:
1 -- Use track_cast, which won't cast const away, to convert a pointer, getting
null if the track is not of the specified type
-- Or use Track::TypeSwitch which takes lambda expressions with arguments of
desired subtypes, entering the lambda body only if the track has the
right type
-- Or use TrackIterRange<...>::Visit() or VisitWhile() to apply a
type-switch over each of several tracks.
2 -- Use TrackList::Any() or Selected() or other new functions, which return
TrackIterRange<...> objects that represent ranges of tracks to be visited.
-- Use the template type parameter to restrict visitation to a subtype and
cause the pointer down-cast to be built in to the iterator deferences.
-- Use C++11 range-for or stl algorithms with TrackIterRanges for brevity
-- Or even avoid loops by using size() or min() or similar member functions
of TrackIterRange.
-- Use StartingWith(), EndingAfter(), Excluding(), and operators + and -
(which can take arbitrary predicates on tracks) to filter the range object
and simplify loop bodies even further.
3 -- Use Track::IsLeader() to test whether a track is first in a channel group
(which also returns true when it is not grouped with any other track, and
maybe not even a WaveTrack)
-- Or use TrackList::FindLeader() to get the first member of its group
-- Or use TrackList::Leaders() to get a track range that includes leaders
only, then visit that range as in (2).
4 -- Use TrackList::Channels() to get a TrackIterRange that includes only
the members of the group of a given track and visit that as in (2).
-- Do not needlessly assume or assert that the channels are at most two.
5 -- After all the channels have been added at the end of the TrackList, just
call TrackList::GroupChannels() passing the leader track and number of
channels.
Most rewrites covered by (3) and (4) generalize without any complications to
possible future more-than-stereo track groupings. In other places, there may
be some difficulty about such generalization, and those are marked with the
comments:
// TODO: more-than-two-channels
or else
// TODO: more-than-two-channels-message
where it was merely a matter of more appropriate wording for the user. This
branch makes no changes to user-visible messages.
What isn't changed in the interface exposed by TrackList and Track:
-- Grouped channels are always a contiguous sub-range of all tracks, and
client code may rely on that assumption.
What isn't changed in the implementaion:
-- Channel groups are never yet actually more than two.
-- The grouping of channels is still represented by a boolean flag in each
track that is true only for the first channel of each stereo pair.
-- The persistent representation of channel groups in .aup files.
... except that WaveTrack needs to be a friend, just for reloading from disk.
Nothing yet is changed in the internal or persistent representation of channel
groups.
... grouping the channels; rather than one flat vector.
OGG, GStreamer and FFmpeg import were written to allow multiple multi-channel
tracks; others always imported one group of channels.
All of that is reflected in the results returned by the importers, though it
makes no difference yet in AudacityProject::AddImportedTracks (where the
results are used).
... requiring at least some message rewrites.
All these places have a comment:
// TODO: more-than-two-channels
or in simpler cases just
// TODO: more-than-two-channels-message
... I don't know whether these libraries support more than two channels with
no problems.
All these places have the comment:
// TODO: more-than-two-channels