/********************************************************************** 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 #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; }