audacia/src/effects/StereoToMono.cpp
Paul Licameli c2feee6cea Bug2346: Complete fix...
... without making undesirable dependency cycles.

Eliminate calls to NewWaveTrack in effects, but in Edit>Copy too, which was
not mentioned in the bug report.  (Copying a track, deselecting all, and pasting
preserved CLIP colors, but not the TRACK color setting which applies to newly
generated clips.)

Instead, always use the new function WaveTrack::EmptyCopy from the track to be
later replaced, getting color information.

NewWaveTrack is still used in benchmark test, import, the Track menu
commands that make new tracks, recording to new tracks, and generators without
a selection, where there is no track to copy from.

Also when deserializing tracks from the .aup file, in which case the saved
color is later retrieved from the file.

Also, in mix-and-render, where other logic decides whether to copy colors
afterward.

See commit a9658e6ef7
2020-03-10 22:32:23 -04:00

176 lines
4.6 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
StereoToMono.cpp
Lynn Allan
*******************************************************************//**
\class EffectStereoToMono
\brief An Effect to convert stereo to mono.
*//*******************************************************************/
#include "../Audacity.h"
#include "StereoToMono.h"
#include "LoadEffects.h"
#include <wx/intl.h>
#include "../Project.h"
#include "../WaveTrack.h"
const ComponentInterfaceSymbol EffectStereoToMono::Symbol
{ XO("Stereo To Mono") };
namespace{ BuiltinEffectsModule::Registration< EffectStereoToMono > reg; }
EffectStereoToMono::EffectStereoToMono()
{
}
EffectStereoToMono::~EffectStereoToMono()
{
}
// ComponentInterface implementation
ComponentInterfaceSymbol EffectStereoToMono::GetSymbol()
{
return Symbol;
}
TranslatableString EffectStereoToMono::GetDescription()
{
return XO("Converts stereo tracks to mono");
}
// EffectDefinitionInterface implementation
EffectType EffectStereoToMono::GetType()
{
// Really EffectTypeProcess, but this prevents it from showing in the Effect Menu
return EffectTypeHidden;
}
bool EffectStereoToMono::IsInteractive()
{
return false;
}
// EffectClientInterface implementation
unsigned EffectStereoToMono::GetAudioInCount()
{
return 2;
}
unsigned EffectStereoToMono::GetAudioOutCount()
{
return 1;
}
// Effect implementation
bool EffectStereoToMono::Process()
{
// Do not use mWaveTracks here. We will possibly DELETE tracks,
// so we must use the "real" tracklist.
this->CopyInputTracks(); // Set up mOutputTracks.
bool bGoodResult = true;
auto trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
bool refreshIter = false;
int count = 0;
while ( trackRange.first != trackRange.second ) {
mLeftTrack = *trackRange.first;
auto channels = TrackList::Channels( mLeftTrack );
if (channels.size() != 2) {
// TODO: more-than-two-channels
++ trackRange.first;
continue;
}
mRightTrack = * channels.rbegin();
if ((mLeftTrack->GetRate() == mRightTrack->GetRate())) {
auto leftTrackStart = mLeftTrack->TimeToLongSamples(mLeftTrack->GetStartTime());
auto rightTrackStart = mRightTrack->TimeToLongSamples(mRightTrack->GetStartTime());
mStart = wxMin(leftTrackStart, rightTrackStart);
auto leftTrackEnd = mLeftTrack->TimeToLongSamples(mLeftTrack->GetEndTime());
auto rightTrackEnd = mRightTrack->TimeToLongSamples(mRightTrack->GetEndTime());
mEnd = wxMax(leftTrackEnd, rightTrackEnd);
bGoodResult = ProcessOne(count);
if (!bGoodResult)
break;
// The right channel has been deleted, so we must restart from the beginning
refreshIter = true;
}
if (refreshIter) {
trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
refreshIter = false;
}
else
++trackRange.first;
count++;
}
this->ReplaceProcessedTracks(bGoodResult);
return bGoodResult;
}
bool EffectStereoToMono::ProcessOne(int count)
{
float curLeftFrame;
float curRightFrame;
float curMonoFrame;
auto idealBlockLen = mLeftTrack->GetMaxBlockSize() * 2;
auto index = mStart;
Floats leftBuffer { idealBlockLen };
Floats rightBuffer{ idealBlockLen };
bool bResult = true;
auto outTrack = mLeftTrack->EmptyCopy();
outTrack->ConvertToSampleFormat( floatSample );
while (index < mEnd) {
bResult &= mLeftTrack->Get((samplePtr)leftBuffer.get(), floatSample, index, idealBlockLen);
bResult &= mRightTrack->Get((samplePtr)rightBuffer.get(), floatSample, index, idealBlockLen);
auto limit = limitSampleBufferSize( idealBlockLen, mEnd - index );
for (decltype(limit) i = 0; i < limit; ++i) {
index++;
curLeftFrame = leftBuffer[i];
curRightFrame = rightBuffer[i];
curMonoFrame = (curLeftFrame + curRightFrame) / 2.0;
leftBuffer[i] = curMonoFrame;
}
outTrack->Append((samplePtr)leftBuffer.get(), floatSample, limit);
if (TrackProgress(count, 2.*(index.as_double() / (mEnd - mStart).as_double())))
return false;
}
double minStart = wxMin(mLeftTrack->GetStartTime(), mRightTrack->GetStartTime());
mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime());
outTrack->Flush();
mLeftTrack->Paste(minStart, outTrack.get());
mOutputTracks->GroupChannels( *mLeftTrack, 1 );
mOutputTracks->Remove(mRightTrack);
return bResult;
}
bool EffectStereoToMono::IsHidden()
{
return true;
}