226 lines
5.8 KiB
C++
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;
|
|
}
|