1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-03 14:13:11 +02:00

Encapsulate details of setting-up of channel groups in TrackList...

... 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.
This commit is contained in:
Paul Licameli 2018-09-30 11:29:37 -04:00
commit 8fcbbaeea9
23 changed files with 198 additions and 264 deletions

View File

@ -7819,6 +7819,7 @@ void MenuCommandHandler::HandleMixAndRender
WaveTrack::Holder uNewLeft, uNewRight; WaveTrack::Holder uNewLeft, uNewRight;
::MixAndRender( ::MixAndRender(
tracks, trackFactory, rate, defaultFormat, 0.0, 0.0, uNewLeft, uNewRight); tracks, trackFactory, rate, defaultFormat, 0.0, 0.0, uNewLeft, uNewRight);
tracks->GroupChannels(*uNewLeft, uNewRight ? 2 : 1);
if (uNewLeft) { if (uNewLeft) {
// Remove originals, get stats on what tracks were mixed // Remove originals, get stats on what tracks were mixed
@ -8769,22 +8770,20 @@ void MenuCommandHandler::OnNewStereoTrack(const CommandContext &context)
auto defaultFormat = project.GetDefaultFormat(); auto defaultFormat = project.GetDefaultFormat();
auto rate = project.GetRate(); auto rate = project.GetRate();
auto t = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate));
t->SetChannel(Track::LeftChannel);
project.SelectNone(); project.SelectNone();
t->SetSelected(true); auto left = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate));
t->SetLinked (true); left->SetSelected(true);
t = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate)); auto right = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate));
t->SetChannel(Track::RightChannel); right->SetSelected(true);
t->SetSelected(true); tracks->GroupChannels(*left, 2);
project.PushState(_("Created new stereo audio track"), _("New Track")); project.PushState(_("Created new stereo audio track"), _("New Track"));
project.RedrawProject(); project.RedrawProject();
trackPanel->EnsureVisible(t); trackPanel->EnsureVisible(left);
} }
void MenuCommandHandler::OnNewLabelTrack(const CommandContext &context) void MenuCommandHandler::OnNewLabelTrack(const CommandContext &context)

View File

@ -120,10 +120,7 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
// TODO: more-than-two-channels // TODO: more-than-two-channels
decltype(mixLeft) mixRight{}; decltype(mixLeft) mixRight{};
if (mono) { if ( !mono ) {
mixLeft->SetChannel(Track::MonoChannel);
}
else {
mixRight = trackFactory->NewWaveTrack(format, rate); mixRight = trackFactory->NewWaveTrack(format, rate);
if (oneinput) { if (oneinput) {
auto channels = TrackList::Channels(first); auto channels = TrackList::Channels(first);
@ -134,14 +131,10 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
} }
else else
mixRight->SetName(_("Mix")); mixRight->SetName(_("Mix"));
mixLeft->SetChannel(Track::LeftChannel);
mixRight->SetChannel(Track::RightChannel);
mixRight->SetOffset(mixStartTime); mixRight->SetOffset(mixStartTime);
mixLeft->SetLinked(true);
} }
auto maxBlockLen = mixLeft->GetIdealBlockSize(); auto maxBlockLen = mixLeft->GetIdealBlockSize();
// If the caller didn't specify a time range, use the whole range in which // If the caller didn't specify a time range, use the whole range in which

View File

@ -4168,7 +4168,6 @@ AudacityProject::AddImportedTracks(const wxString &fileName,
{ {
std::vector< std::shared_ptr< Track > > results; std::vector< std::shared_ptr< Track > > results;
const auto numTracks = newTracks.size();
SelectNone(); SelectNone();
bool initiallyEmpty = mTracks->empty(); bool initiallyEmpty = mTracks->empty();
@ -4177,9 +4176,18 @@ AudacityProject::AddImportedTracks(const wxString &fileName,
int i = -1; int i = -1;
// Must add all tracks first (before using Track::IsLeader) // Must add all tracks first (before using Track::IsLeader)
for (auto &uNewTrack : newTracks) { for (auto &group : newTracks) {
auto newTrack = mTracks->Add(std::move(uNewTrack)); if (group.empty()) {
results.push_back(Track::Pointer(newTrack)); wxASSERT(false);
continue;
}
auto first = group.begin()->get();
auto nChannels = group.size();
for (auto &uNewTrack : group) {
auto newTrack = mTracks->Add(std::move(uNewTrack));
results.push_back(Track::Pointer(newTrack));
}
mTracks->GroupChannels(*first, nChannels);
} }
newTracks.clear(); newTracks.clear();

View File

@ -38,6 +38,7 @@ and TimeTrack.
#include "DirManager.h" #include "DirManager.h"
#include "Experimental.h" #include "Experimental.h"
#include "InconsistencyException.h"
#include "TrackPanel.h" // for TrackInfo #include "TrackPanel.h" // for TrackInfo
@ -724,6 +725,58 @@ Track *TrackList::Add(std::shared_ptr<TrackKind> &&t)
template Track *TrackList::Add<Track>(std::shared_ptr<Track> &&); template Track *TrackList::Add<Track>(std::shared_ptr<Track> &&);
template Track *TrackList::Add<WaveTrack>(std::shared_ptr<WaveTrack> &&); template Track *TrackList::Add<WaveTrack>(std::shared_ptr<WaveTrack> &&);
void TrackList::GroupChannels(
Track &track, size_t groupSize, bool resetChannels )
{
// If group size is more than two, for now only the first two channels
// are grouped as stereo, and any others remain mono
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 )
;
if ( count == 0 ) {
auto unlink = [&] ( Track &tr ) {
if ( tr.GetLinked() ) {
if ( resetChannels ) {
auto link = tr.GetLink();
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( true );
channel->SetChannel( Track::LeftChannel );
(*iter++)->SetChannel( Track::RightChannel );
while (iter != after)
(*iter++)->SetChannel( Track::MonoChannel );
}
return;
}
}
// *this does not contain the track or sufficient following channels
// or group size is zero
throw InconsistencyException{};
}
auto TrackList::Replace(Track * t, ListOfTracks::value_type &&with) -> auto TrackList::Replace(Track * t, ListOfTracks::value_type &&with) ->
ListOfTracks::value_type ListOfTracks::value_type
{ {

View File

@ -327,8 +327,11 @@ private:
Track *GetLink() const; Track *GetLink() const;
bool GetLinked () const { return mLinked; } bool GetLinked () const { return mLinked; }
public:
friend WaveTrack; // WaveTrack needs to call SetLinked when reloading project
void SetLinked (bool l); void SetLinked (bool l);
void SetChannel(ChannelType c) { mChannel = c; }
private: private:
// No need yet to make this virtual // No need yet to make this virtual
void DoSetLinked(bool l); void DoSetLinked(bool l);
@ -380,7 +383,6 @@ public:
void Offset(double t) { SetOffset(GetOffset() + t); } void Offset(double t) { SetOffset(GetOffset() + t); }
virtual void SetOffset (double o) { mOffset = o; } virtual void SetOffset (double o) { mOffset = o; }
void SetChannel(ChannelType c) { mChannel = c; }
virtual void SetPan( float ){ ;} virtual void SetPan( float ){ ;}
virtual void SetPanFromChannelType(){ ;}; virtual void SetPanFromChannelType(){ ;};
@ -1318,6 +1320,16 @@ public:
template<typename TrackKind> template<typename TrackKind>
Track *Add(std::shared_ptr<TrackKind> &&t); Track *Add(std::shared_ptr<TrackKind> &&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.
*/
void GroupChannels(
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
ListOfTracks::value_type Replace(Track * t, ListOfTracks::value_type &&with); ListOfTracks::value_type Replace(Track * t, ListOfTracks::value_type &&with);

View File

@ -2543,6 +2543,7 @@ void Effect::Preview(bool dryOnly)
mixRight->SetSelected(true); mixRight->SetSelected(true);
mTracks->Add(std::move(mixRight)); mTracks->Add(std::move(mixRight));
} }
mTracks->GroupChannels(*mixLeft, mixRight ? 2 : 1);
} }
else { else {
for (auto src : saveTracks->Any< const WaveTrack >()) { for (auto src : saveTracks->Any< const WaveTrack >()) {

View File

@ -157,9 +157,7 @@ bool EffectStereoToMono::ProcessOne(int count)
mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime()); mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime());
outTrack->Flush(); outTrack->Flush();
mLeftTrack->Paste(minStart, outTrack.get()); mLeftTrack->Paste(minStart, outTrack.get());
mLeftTrack->SetLinked(false); mOutputTracks->GroupChannels( *mLeftTrack, 1 );
mRightTrack->SetLinked(false);
mLeftTrack->SetChannel(Track::MonoChannel);
mOutputTracks->Remove(mRightTrack); mOutputTracks->Remove(mRightTrack);
return bResult; return bResult;

View File

@ -49,6 +49,7 @@ and ImportLOF.cpp.
#include <wx/listimpl.cpp> #include <wx/listimpl.cpp>
#include "../ShuttleGui.h" #include "../ShuttleGui.h"
#include "../Project.h" #include "../Project.h"
#include "../WaveTrack.h"
#include "ImportPCM.h" #include "ImportPCM.h"
#include "ImportMP3.h" #include "ImportMP3.h"
@ -538,6 +539,15 @@ bool Importer::Import(const wxString &fName,
return true; return true;
} }
auto end = tracks.end();
auto iter = std::remove_if( tracks.begin(), end,
std::mem_fn( &NewChannelGroup::empty ) );
if ( iter != end ) {
// importer shouldn't give us empty groups of channels!
wxASSERT(false);
// But correct that and proceed anyway
tracks.erase( iter, end );
}
if (tracks.size() > 0) if (tracks.size() > 0)
{ {
// success! // success!

View File

@ -274,7 +274,10 @@ private:
bool mCancelled; //!< True if importing was canceled by user bool mCancelled; //!< True if importing was canceled by user
bool mStopped; //!< True if importing was stopped by user bool mStopped; //!< True if importing was stopped by user
wxString mName; wxString mName;
std::list<TrackHolders> mChannels; //!< 2-dimentional array of WaveTrack's. First dimention - streams, second - channels of a stream. Length is mNumStreams TrackHolders mChannels; //!< 2-dimensional array of WaveTrack's.
//!< First dimension - streams,
//!< second - channels of a stream.
//!< Length is mNumStreams
#ifdef EXPERIMENTAL_OD_FFMPEG #ifdef EXPERIMENTAL_OD_FFMPEG
bool mUsingOD; bool mUsingOD;
#endif #endif
@ -520,31 +523,8 @@ ProgressResult FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
// There is a possibility that number of channels will change over time, but we do not have WaveTracks for NEW channels. Remember the number of channels and stick to it. // There is a possibility that number of channels will change over time, but we do not have WaveTracks for NEW channels. Remember the number of channels and stick to it.
sc->m_initialchannels = sc->m_stream->codec->channels; sc->m_initialchannels = sc->m_stream->codec->channels;
stream.resize(sc->m_stream->codec->channels); stream.resize(sc->m_stream->codec->channels);
int c = -1;
for (auto &channel : stream) for (auto &channel : stream)
{
++c;
channel = trackFactory->NewWaveTrack(sc->m_osamplefmt, sc->m_stream->codec->sample_rate); channel = trackFactory->NewWaveTrack(sc->m_osamplefmt, sc->m_stream->codec->sample_rate);
if (sc->m_stream->codec->channels == 2)
{
switch (c)
{
case 0:
channel->SetChannel(Track::LeftChannel);
channel->SetLinked(true);
break;
case 1:
channel->SetChannel(Track::RightChannel);
break;
}
}
else
{
channel->SetChannel(Track::MonoChannel);
}
}
} }
// Handles the start_time by creating silence. This may or may not be correct. // Handles the start_time by creating silence. This may or may not be correct.
@ -688,13 +668,10 @@ ProgressResult FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
// Copy audio from mChannels to newly created tracks (destroying mChannels elements in process) // Copy audio from mChannels to newly created tracks (destroying mChannels elements in process)
for (auto &stream : mChannels) for (auto &stream : mChannels)
{
for(auto &channel : stream) for(auto &channel : stream)
{
channel->Flush(); channel->Flush();
outTracks.push_back(std::move(channel));
} outTracks.swap(mChannels);
}
// Save metadata // Save metadata
WriteMetadata(tags); WriteMetadata(tags);

View File

@ -181,7 +181,7 @@ private:
FLAC__uint64 mSamplesDone; FLAC__uint64 mSamplesDone;
bool mStreamInfoDone; bool mStreamInfoDone;
ProgressResult mUpdateResult; ProgressResult mUpdateResult;
TrackHolders mChannels; NewChannelGroup mChannels;
std::unique_ptr<ODDecodeFlacTask> mDecoderTask; std::unique_ptr<ODDecodeFlacTask> mDecoderTask;
}; };
@ -455,25 +455,9 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory,
mChannels.resize(mNumChannels); mChannels.resize(mNumChannels);
auto iter = mChannels.begin(); auto iter = mChannels.begin();
for (size_t c = 0; c < mNumChannels; ++iter, ++c) { for (size_t c = 0; c < mNumChannels; ++iter, ++c)
*iter = trackFactory->NewWaveTrack(mFormat, mSampleRate); *iter = trackFactory->NewWaveTrack(mFormat, mSampleRate);
if (mNumChannels == 2) {
switch (c) {
case 0:
iter->get()->SetChannel(Track::LeftChannel);
iter->get()->SetLinked(true);
break;
case 1:
iter->get()->SetChannel(Track::RightChannel);
break;
}
}
else {
iter->get()->SetChannel(Track::MonoChannel);
}
}
//Start OD //Start OD
bool useOD = false; bool useOD = false;
@ -536,10 +520,11 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory,
return mUpdateResult; return mUpdateResult;
} }
for (const auto &channel : mChannels) { for (const auto &channel : mChannels)
channel->Flush(); channel->Flush();
}
outTracks.swap(mChannels); if (!mChannels.empty())
outTracks.push_back(std::move(mChannels));
tags->Clear(); tags->Clear();
size_t cnt = mFile->mComments.GetCount(); size_t cnt = mFile->mComments.GetCount();

View File

@ -130,7 +130,7 @@ struct GStreamContext
GstElement *mConv{}; // Audio converter GstElement *mConv{}; // Audio converter
GstElement *mSink{}; // Application sink GstElement *mSink{}; // Application sink
bool mUse{}; // True if this stream should be imported bool mUse{}; // True if this stream should be imported
TrackHolders mChannels; // Array of WaveTrack pointers, one for each channel NewChannelGroup mChannels; // Array of WaveTrack pointers, one for each channel
unsigned mNumChannels{}; // Number of channels unsigned mNumChannels{}; // Number of channels
gdouble mSampleRate{}; // Sample rate gdouble mSampleRate{}; // Sample rate
GstString mType; // Audio type GstString mType; // Audio type
@ -789,14 +789,6 @@ GStreamerImportFileHandle::OnNewSample(GStreamContext *c, GstSample *sample)
return; return;
} }
} }
// Set to stereo if there's exactly 2 channels
if (c->mNumChannels == 2)
{
c->mChannels[0]->SetChannel(Track::LeftChannel);
c->mChannels[1]->SetChannel(Track::RightChannel);
c->mChannels[0]->SetLinked(true);
}
} }
// Get the buffer for the sample...no need to release // Get the buffer for the sample...no need to release
@ -1140,18 +1132,6 @@ GStreamerImportFileHandle::Import(TrackFactory *trackFactory,
// Grab the streams lock // Grab the streams lock
g_mutex_locker locker{ mStreamsLock }; g_mutex_locker locker{ mStreamsLock };
// Count the total number of tracks collected
unsigned outNumTracks = 0;
for (guint s = 0; s < mStreams.size(); s++)
{
GStreamContext *c = mStreams[s].get();
if (c)
outNumTracks += c->mNumChannels;
}
// Create NEW tracks
outTracks.resize(outNumTracks);
// Copy audio from mChannels to newly created tracks (destroying mChannels in process) // Copy audio from mChannels to newly created tracks (destroying mChannels in process)
int trackindex = 0; int trackindex = 0;
for (guint s = 0; s < mStreams.size(); s++) for (guint s = 0; s < mStreams.size(); s++)
@ -1160,12 +1140,8 @@ GStreamerImportFileHandle::Import(TrackFactory *trackFactory,
if (c->mNumChannels) if (c->mNumChannels)
{ {
for (int ch = 0; ch < c->mNumChannels; ch++) for (int ch = 0; ch < c->mNumChannels; ch++)
{
c->mChannels[ch]->Flush(); c->mChannels[ch]->Flush();
outTracks[trackindex++] = std::move(c->mChannels[ch]); outTracks.push_back(std::move(c->mChannels));
}
c->mChannels.clear();
} }
} }

View File

@ -223,8 +223,9 @@ auto LOFImportFileHandle::GetFileUncompressedBytes() -> ByteCount
return 0; return 0;
} }
ProgressResult LOFImportFileHandle::Import(TrackFactory * WXUNUSED(trackFactory), TrackHolders &outTracks, ProgressResult LOFImportFileHandle::Import(
Tags * WXUNUSED(tags)) TrackFactory * WXUNUSED(trackFactory), TrackHolders &outTracks,
Tags * WXUNUSED(tags))
{ {
// Unlike other ImportFileHandle subclasses, this one never gives any tracks // Unlike other ImportFileHandle subclasses, this one never gives any tracks
// back to the caller. // back to the caller.

View File

@ -98,7 +98,7 @@ struct private_data {
ArrayOf<unsigned char> inputBuffer{ static_cast<unsigned int>(INPUT_BUFFER_SIZE) }; ArrayOf<unsigned char> inputBuffer{ static_cast<unsigned int>(INPUT_BUFFER_SIZE) };
int inputBufferFill; /* amount of data in inputBuffer */ int inputBufferFill; /* amount of data in inputBuffer */
TrackFactory *trackFactory; TrackFactory *trackFactory;
TrackHolders channels; NewChannelGroup channels;
ProgressDialog *progress; ProgressDialog *progress;
unsigned numChannels; unsigned numChannels;
ProgressResult updateResult; ProgressResult updateResult;
@ -208,8 +208,9 @@ auto MP3ImportFileHandle::GetFileUncompressedBytes() -> ByteCount
return 0; return 0;
} }
ProgressResult MP3ImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks, ProgressResult MP3ImportFileHandle::Import(
Tags *tags) TrackFactory *trackFactory, TrackHolders &outTracks,
Tags *tags)
{ {
outTracks.clear(); outTracks.clear();
@ -252,7 +253,8 @@ ProgressResult MP3ImportFileHandle::Import(TrackFactory *trackFactory, TrackHold
for(const auto &channel : privateData.channels) { for(const auto &channel : privateData.channels) {
channel->Flush(); channel->Flush();
} }
outTracks.swap(privateData.channels); if (!privateData.channels.empty())
outTracks.push_back(std::move(privateData.channels));
/* Read in any metadata */ /* Read in any metadata */
ImportID3(tags); ImportID3(tags);
@ -501,17 +503,9 @@ enum mad_flow output_cb(void *_data,
auto format = QualityPrefs::SampleFormatChoice(); auto format = QualityPrefs::SampleFormatChoice();
for(auto &channel: data->channels) { for(auto &channel: data->channels)
channel = data->trackFactory->NewWaveTrack(format, samplerate); channel = data->trackFactory->NewWaveTrack(format, samplerate);
channel->SetChannel(Track::MonoChannel);
}
/* special case: 2 channels is understood to be stereo */
if(channels == 2) {
data->channels.begin()->get()->SetChannel(Track::LeftChannel);
data->channels.rbegin()->get()->SetChannel(Track::RightChannel);
data->channels.begin()->get()->SetLinked(true);
}
data->numChannels = channels; data->numChannels = channels;
} }
else { else {

View File

@ -155,7 +155,7 @@ private:
ArrayOf<int> mStreamUsage; ArrayOf<int> mStreamUsage;
wxArrayString mStreamInfo; wxArrayString mStreamInfo;
std::list<TrackHolders> mChannels; std::list<NewChannelGroup> mChannels;
sampleFormat mFormat; sampleFormat mFormat;
}; };
@ -228,7 +228,8 @@ auto OggImportFileHandle::GetFileUncompressedBytes() -> ByteCount
return 0; return 0;
} }
ProgressResult OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks, ProgressResult OggImportFileHandle::Import(
TrackFactory *trackFactory, TrackHolders &outTracks,
Tags *tags) Tags *tags)
{ {
outTracks.clear(); outTracks.clear();
@ -258,27 +259,8 @@ ProgressResult OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHold
link.resize(vi->channels); link.resize(vi->channels);
int c = -1; for (auto &channel : link)
for (auto &channel : link) {
++c;
channel = trackFactory->NewWaveTrack(mFormat, vi->rate); channel = trackFactory->NewWaveTrack(mFormat, vi->rate);
if (vi->channels == 2) {
switch (c) {
case 0:
channel->SetChannel(Track::LeftChannel);
channel->SetLinked(true);
break;
case 1:
channel->SetChannel(Track::RightChannel);
break;
}
}
else {
channel->SetChannel(Track::MonoChannel);
}
}
} }
/* The number of bytes to get from the codec in each run */ /* The number of bytes to get from the codec in each run */
@ -374,10 +356,9 @@ ProgressResult OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHold
for (auto &link : mChannels) for (auto &link : mChannels)
{ {
for (auto &channel : link) { for (auto &channel : link)
channel->Flush(); channel->Flush();
outTracks.push_back(std::move(channel)); outTracks.push_back(std::move(link));
}
} }
//\todo { Extract comments from each stream? } //\todo { Extract comments from each stream? }

View File

@ -364,29 +364,12 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory,
CreateProgress(); CreateProgress();
TrackHolders channels(mInfo.channels); NewChannelGroup channels(mInfo.channels);
auto iter = channels.begin(); auto iter = channels.begin();
for (int c = 0; c < mInfo.channels; ++iter, ++c) { for (int c = 0; c < mInfo.channels; ++iter, ++c)
*iter = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate); *iter = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate);
if (mInfo.channels > 1)
switch (c) {
case 0:
iter->get()->SetChannel(Track::LeftChannel);
break;
case 1:
iter->get()->SetChannel(Track::RightChannel);
break;
default:
iter->get()->SetChannel(Track::MonoChannel);
}
}
if (mInfo.channels == 2) {
channels.begin()->get()->SetLinked(true);
}
auto fileTotalFrames = auto fileTotalFrames =
(sampleCount)mInfo.frames; // convert from sf_count_t (sampleCount)mInfo.frames; // convert from sf_count_t
auto maxBlockSize = channels.begin()->get()->GetMaxBlockSize(); auto maxBlockSize = channels.begin()->get()->GetMaxBlockSize();
@ -529,10 +512,11 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory,
return updateResult; return updateResult;
} }
for(const auto &channel : channels) { for(const auto &channel : channels)
channel->Flush(); channel->Flush();
}
outTracks.swap(channels); if (!channels.empty())
outTracks.push_back(std::move(channels));
const char *str; const char *str;

View File

@ -157,6 +157,8 @@ public:
// The given Tags structure may also be modified. // The given Tags structure may also be modified.
// In case of errors or exceptions, it is not necessary to leave outTracks // In case of errors or exceptions, it is not necessary to leave outTracks
// or tags unmodified. // or tags unmodified.
// If resulting outTracks is not empty,
// then each member of it must be a nonempty vector.
virtual ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks, virtual ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks,
Tags *tags) = 0; Tags *tags) = 0;

View File

@ -336,7 +336,7 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
(sizeof(AudioBuffer) * numchan))) }; (sizeof(AudioBuffer) * numchan))) };
abl->mNumberBuffers = numchan; abl->mNumberBuffers = numchan;
TrackHolders channels{ numchan }; NewChannelGroup channels{ numchan };
const auto size = sizeof(float) * bufsize; const auto size = sizeof(float) * bufsize;
ArraysOf<unsigned char> holders{ numchan, size }; ArraysOf<unsigned char> holders{ numchan, size };
@ -352,16 +352,6 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
channel = trackFactory->NewWaveTrack( format ); channel = trackFactory->NewWaveTrack( format );
channel->SetRate( desc.mSampleRate ); channel->SetRate( desc.mSampleRate );
if (numchan == 2) {
if (c == 0) {
channel->SetChannel(Track::LeftChannel);
channel->SetLinked(true);
}
else if (c == 1) {
channel->SetChannel(Track::RightChannel);
}
}
} }
do { do {
@ -395,11 +385,10 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
res = (updateResult == ProgressResult::Success && err == noErr); res = (updateResult == ProgressResult::Success && err == noErr);
if (res) { if (res) {
for (const auto &channel: channels) { for (auto &channel: channels)
channel->Flush(); channel->Flush();
} if (!channels.empty())
outTracks.push_back(std::move(channels));
outTracks.swap(channels);
} }
// //

View File

@ -107,7 +107,7 @@ void ImportRaw(wxWindow *parent, const wxString &fileName,
sf_count_t offset = 0; sf_count_t offset = 0;
double rate = 44100.0; double rate = 44100.0;
double percent = 100.0; double percent = 100.0;
TrackHolders channels; TrackHolders results;
auto updateResult = ProgressResult::Success; auto updateResult = ProgressResult::Success;
{ {
@ -200,31 +200,15 @@ void ImportRaw(wxWindow *parent, const wxString &fileName,
sf_subtype_more_than_16_bits(encoding)) sf_subtype_more_than_16_bits(encoding))
format = floatSample; format = floatSample;
results.resize(1);
auto &channels = results[0];
channels.resize(numChannels); channels.resize(numChannels);
auto iter = channels.begin(); auto iter = channels.begin();
for (decltype(numChannels) c = 0; c < numChannels; ++iter, ++c) { for (decltype(numChannels) c = 0; c < numChannels; ++iter, ++c)
const auto channel = *iter = trackFactory->NewWaveTrack(format, rate);
(*iter = trackFactory->NewWaveTrack(format, rate)).get();
if (numChannels > 1)
switch (c) {
case 0:
channel->SetChannel(Track::LeftChannel);
break;
case 1:
channel->SetChannel(Track::RightChannel);
break;
default:
channel->SetChannel(Track::MonoChannel);
}
}
const auto firstChannel = channels.begin()->get(); const auto firstChannel = channels.begin()->get();
if (numChannels == 2) {
firstChannel->SetLinked(true);
}
auto maxBlockSize = firstChannel->GetMaxBlockSize(); auto maxBlockSize = firstChannel->GetMaxBlockSize();
SampleBuffer srcbuffer(maxBlockSize * numChannels, format); SampleBuffer srcbuffer(maxBlockSize * numChannels, format);
@ -295,9 +279,11 @@ void ImportRaw(wxWindow *parent, const wxString &fileName,
if (updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled) if (updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled)
throw UserException{}; throw UserException{};
for (const auto &channel : channels) if (!results.empty() && !results[0].empty()) {
channel->Flush(); for (const auto &channel : results[0])
outTracks.swap(channels); channel->Flush();
outTracks.swap(results);
}
} }
// //

View File

@ -21,7 +21,10 @@ class wxWindow;
#include <vector> #include <vector>
using TrackHolders = std::vector<std::unique_ptr<WaveTrack>>; // Newly constructed WaveTracks that are not yet owned by a TrackList
// are held in unique_ptr not shared_ptr
using NewChannelGroup = std::vector< std::unique_ptr<WaveTrack> >;
using TrackHolders = std::vector< NewChannelGroup >;
void ImportRaw(wxWindow *parent, const wxString &fileName, void ImportRaw(wxWindow *parent, const wxString &fileName,

View File

@ -109,17 +109,17 @@ private:
int mStreamIndex; int mStreamIndex;
}; };
auto ODDecodeFFmpegTask::FromList(const std::list<TrackHolders> &channels) -> Streams auto ODDecodeFFmpegTask::FromList( const TrackHolders &channels ) -> Streams
{ {
Streams streams; Streams streams;
streams.reserve(channels.size()); streams.reserve(channels.size());
using namespace std; using namespace std;
transform(channels.begin(), channels.end(), back_inserter(streams), transform(channels.begin(), channels.end(), back_inserter(streams),
[](const TrackHolders &holders) { [](const NewChannelGroup &holders) {
Channels channels; Channels channels;
channels.reserve(holders.size()); channels.reserve(holders.size());
transform(holders.begin(), holders.end(), back_inserter(channels), transform(holders.begin(), holders.end(), back_inserter(channels),
mem_fn(&TrackHolders::value_type::get) mem_fn(&NewChannelGroup::value_type::get)
); );
return channels; return channels;
} }

View File

@ -31,7 +31,8 @@ public:
using Channels = std::vector < WaveTrack* >; using Channels = std::vector < WaveTrack* >;
using Streams = std::vector < Channels >; using Streams = std::vector < Channels >;
static Streams FromList(const std::list<TrackHolders> &channels); static Streams FromList(
const std::vector< std::vector< std::unique_ptr<WaveTrack> > > &channels);
/// Constructs an ODTask /// Constructs an ODTask
ODDecodeFFmpegTask(const ScsPtr &scs, Streams &&channels, const std::shared_ptr<FFmpegContext> &context, int streamIndex); ODDecodeFFmpegTask(const ScsPtr &scs, Streams &&channels, const std::shared_ptr<FFmpegContext> &context, int streamIndex);

View File

@ -1164,10 +1164,13 @@ bool ControlToolBar::DoRecord(AudacityProject &project,
wxString baseTrackName = recordingNameCustom? defaultRecordingTrackName : defaultTrackName; wxString baseTrackName = recordingNameCustom? defaultRecordingTrackName : defaultTrackName;
Track *first {};
for (int c = 0; c < recordingChannels; c++) { for (int c = 0; c < recordingChannels; c++) {
std::shared_ptr<WaveTrack> newTrack{ std::shared_ptr<WaveTrack> newTrack{
p->GetTrackFactory()->NewWaveTrack().release() p->GetTrackFactory()->NewWaveTrack().release()
}; };
if (!first)
first = newTrack.get();
// Quantize bounds to the rate of the new track. // Quantize bounds to the rate of the new track.
if (c == 0) { if (c == 0) {
@ -1215,26 +1218,14 @@ bool ControlToolBar::DoRecord(AudacityProject &project,
newTrack->SetMinimized(true); newTrack->SetMinimized(true);
} }
if (recordingChannels == 2) {
if (c == 0) {
newTrack->SetChannel(Track::LeftChannel);
newTrack->SetLinked(true);
}
else {
newTrack->SetChannel(Track::RightChannel);
}
}
else {
newTrack->SetChannel( Track::MonoChannel );
}
p->GetTracks()->RegisterPendingNewTrack( newTrack ); p->GetTracks()->RegisterPendingNewTrack( newTrack );
transportTracks.captureTracks.push_back(newTrack); transportTracks.captureTracks.push_back(newTrack);
// Bug 1548. New track needs the focus. // Bug 1548. New track needs the focus.
p->GetTrackPanel()->SetFocusedTrack( newTrack.get() ); p->GetTrackPanel()->SetFocusedTrack( newTrack.get() );
} }
p->GetTracks()->GroupChannels(*first, recordingChannels);
} }
//Automated Input Level Adjustment Initialization //Automated Input Level Adjustment Initialization
#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
gAudioIO->AILAInitialize(); gAudioIO->AILAInitialize();

View File

@ -802,6 +802,7 @@ void WaveTrackMenuTable::OnSpectrogramSettings(wxCommandEvent &)
} }
} }
#if 0
void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event) void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event)
{ {
int id = event.GetId(); int id = event.GetId();
@ -834,6 +835,7 @@ void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event)
_("Channel")); _("Channel"));
mpData->result = RefreshCode::RefreshAll; mpData->result = RefreshCode::RefreshAll;
} }
#endif
/// Merge two tracks into one stereo track ?? /// Merge two tracks into one stereo track ??
void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
@ -847,46 +849,39 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
auto partner = static_cast< WaveTrack * > auto partner = static_cast< WaveTrack * >
( *tracks->Find( pTrack ).advance( 1 ) ); ( *tracks->Find( pTrack ).advance( 1 ) );
pTrack->SetLinked(true); tracks->GroupChannels( *pTrack, 2 );
if (partner) { // Set partner's parameters to match target.
// Set partner's parameters to match target. partner->Merge(*pTrack);
partner->Merge(*pTrack);
pTrack->SetPan( 0.0f ); pTrack->SetPan( 0.0f );
pTrack->SetChannel(Track::LeftChannel); partner->SetPan( 0.0f );
partner->SetPan( 0.0f );
partner->SetChannel(Track::RightChannel);
// Set NEW track heights and minimized state // Set NEW track heights and minimized state
bool bBothMinimizedp = ((pTrack->GetMinimized()) && (partner->GetMinimized())); bool bBothMinimizedp = ((pTrack->GetMinimized()) && (partner->GetMinimized()));
pTrack->SetMinimized(false); pTrack->SetMinimized(false);
partner->SetMinimized(false); partner->SetMinimized(false);
int AverageHeight = (pTrack->GetHeight() + partner->GetHeight()) / 2; int AverageHeight = (pTrack->GetHeight() + partner->GetHeight()) / 2;
pTrack->SetHeight(AverageHeight); pTrack->SetHeight(AverageHeight);
partner->SetHeight(AverageHeight); partner->SetHeight(AverageHeight);
pTrack->SetMinimized(bBothMinimizedp); pTrack->SetMinimized(bBothMinimizedp);
partner->SetMinimized(bBothMinimizedp); partner->SetMinimized(bBothMinimizedp);
//On Demand - join the queues together. //On Demand - join the queues together.
if (ODManager::IsInstanceCreated()) if (ODManager::IsInstanceCreated())
if (!ODManager::Instance()->MakeWaveTrackDependent(partner, pTrack)) if (!ODManager::Instance()->MakeWaveTrackDependent(partner, pTrack))
{ {
; ;
//TODO: in the future, we will have to check the return value of MakeWaveTrackDependent - //TODO: in the future, we will have to check the return value of MakeWaveTrackDependent -
//if the tracks cannot merge, it returns false, and in that case we should not allow a merging. //if the tracks cannot merge, it returns false, and in that case we should not allow a merging.
//for example it returns false when there are two different types of ODTasks on each track's queue. //for example it returns false when there are two different types of ODTasks on each track's queue.
//we will need to display this to the user. //we will need to display this to the user.
} }
AudacityProject *const project = ::GetActiveProject(); /* i18n-hint: The string names a track */
/* i18n-hint: The string names a track */ project->PushState(wxString::Format(_("Made '%s' a stereo track"),
project->PushState(wxString::Format(_("Made '%s' a stereo track"), pTrack->GetName()),
pTrack->GetName()), _("Make Stereo"));
_("Make Stereo"));
}
else
pTrack->SetLinked(false);
mpData->result = RefreshCode::RefreshAll; mpData->result = RefreshCode::RefreshAll;
} }
@ -899,7 +894,6 @@ void WaveTrackMenuTable::SplitStereo(bool stereo)
AudacityProject *const project = ::GetActiveProject(); AudacityProject *const project = ::GetActiveProject();
auto channels = TrackList::Channels( pTrack ); auto channels = TrackList::Channels( pTrack );
int totalHeight = 0; int totalHeight = 0;
int nChannels = 0; int nChannels = 0;
for (auto channel : channels) { for (auto channel : channels) {
@ -907,7 +901,6 @@ void WaveTrackMenuTable::SplitStereo(bool stereo)
channel->SetName(pTrack->GetName()); channel->SetName(pTrack->GetName());
if (stereo) if (stereo)
channel->SetPanFromChannelType(); channel->SetPanFromChannelType();
channel->SetChannel(Track::MonoChannel);
//On Demand - have each channel add its own. //On Demand - have each channel add its own.
if (ODManager::IsInstanceCreated()) if (ODManager::IsInstanceCreated())
@ -919,7 +912,7 @@ void WaveTrackMenuTable::SplitStereo(bool stereo)
++nChannels; ++nChannels;
} }
pTrack->SetLinked(false); project->GetTracks()->GroupChannels( *pTrack, 1 );
int averageHeight = totalHeight / nChannels; int averageHeight = totalHeight / nChannels;
for (auto channel : channels) for (auto channel : channels)
@ -947,12 +940,9 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
SplitStereo(false); SplitStereo(false);
first->SetChannel(Track::RightChannel);
partner->SetChannel(Track::LeftChannel);
TrackList *const tracks = project->GetTracks(); TrackList *const tracks = project->GetTracks();
(tracks->MoveUp(partner)); tracks->MoveUp( partner );
partner->SetLinked(true); tracks->GroupChannels( *partner, 2 );
MixerBoard* pMixerBoard = project->GetMixerBoard(); MixerBoard* pMixerBoard = project->GetMixerBoard();
if (pMixerBoard) if (pMixerBoard)