audacia/src/effects/StereoToMono.cpp

226 lines
5.8 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
StereoToMono.cpp
Lynn Allan
*******************************************************************//**
\class EffectStereoToMono
\brief An Effect to convert stereo to mono.
*//*******************************************************************/
#include "StereoToMono.h"
#include "LoadEffects.h"
#include <wx/intl.h>
#include "../Mix.h"
#include "../Project.h"
#include "../WaveTrack.h"
#include "../widgets/ProgressDialog.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;
// Determine the total time (in samples) used by all of the target tracks
sampleCount totalTime = 0;
auto trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
while (trackRange.first != trackRange.second)
{
auto left = *trackRange.first;
auto channels = TrackList::Channels(left);
if (channels.size() > 1)
{
auto right = *channels.rbegin();
auto leftRate = left->GetRate();
auto rightRate = right->GetRate();
if (leftRate != rightRate)
{
if (leftRate != mProjectRate)
{
mProgress->SetMessage(XO("Resampling left channel"));
left->Resample(mProjectRate, mProgress);
leftRate = mProjectRate;
}
if (rightRate != mProjectRate)
{
mProgress->SetMessage(XO("Resampling right channel"));
right->Resample(mProjectRate, mProgress);
rightRate = mProjectRate;
}
}
{
auto start = wxMin(left->TimeToLongSamples(left->GetStartTime()),
right->TimeToLongSamples(right->GetStartTime()));
auto end = wxMax(left->TimeToLongSamples(left->GetEndTime()),
right->TimeToLongSamples(right->GetEndTime()));
totalTime += (end - start);
}
}
++trackRange.first;
}
// Process each stereo track
sampleCount curTime = 0;
bool refreshIter = false;
mProgress->SetMessage(XO("Mixing down to mono"));
trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
while (trackRange.first != trackRange.second)
{
auto left = *trackRange.first;
auto channels = TrackList::Channels(left);
if (channels.size() > 1)
{
auto right = *channels.rbegin();
bGoodResult = ProcessOne(curTime, totalTime, left, right);
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;
}
}
this->ReplaceProcessedTracks(bGoodResult);
return bGoodResult;
}
bool EffectStereoToMono::ProcessOne(sampleCount & curTime, sampleCount totalTime, WaveTrack *left, WaveTrack *right)
{
auto idealBlockLen = left->GetMaxBlockSize() * 2;
bool bResult = true;
sampleCount processed = 0;
auto start = wxMin(left->GetStartTime(), right->GetStartTime());
auto end = wxMax(left->GetEndTime(), right->GetEndTime());
WaveTrackConstArray tracks;
tracks.push_back(left->SharedPointer< const WaveTrack >());
tracks.push_back(right->SharedPointer< const WaveTrack >());
Mixer mixer(tracks,
true, // Throw to abort mix-and-render if read fails:
Mixer::WarpOptions{*inputTracks()},
start,
end,
1,
idealBlockLen,
false, // Not interleaved
left->GetRate(), // Process() checks that left and right
// rates are the same
floatSample);
auto outTrack = left->EmptyCopy();
outTrack->ConvertToSampleFormat(floatSample);
while (auto blockLen = mixer.Process(idealBlockLen))
{
auto buffer = mixer.GetBuffer();
for (auto i = 0; i < blockLen; i++)
{
((float *)buffer)[i] /= 2.0;
}
outTrack->Append(buffer, floatSample, blockLen);
curTime += blockLen;
if (TotalProgress(curTime.as_double() / totalTime.as_double()))
{
return false;
}
}
outTrack->Flush();
double minStart = wxMin(left->GetStartTime(), right->GetStartTime());
left->Clear(left->GetStartTime(), left->GetEndTime());
left->Paste(minStart, outTrack.get());
mOutputTracks->GroupChannels(*left, 1);
mOutputTracks->Remove(right);
return bResult;
}
bool EffectStereoToMono::IsHidden()
{
return true;
}