Audacity 3.2.0
StereoToMono.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 StereoToMono.cpp
6
7 Lynn Allan
8
9*******************************************************************//*******************************************************************/
15
16
17#include "StereoToMono.h"
18#include "EffectOutputTracks.h"
19#include "LoadEffects.h"
20
21#include "Mix.h"
22#include "MixAndRender.h"
23#include "Project.h"
24#include "RealtimeEffectList.h"
25#include "WaveTrack.h"
26#include "ProgressDialog.h"
27
29{ XO("Stereo To Mono") };
30
32
34{
35}
36
38{
39}
40
41// ComponentInterface implementation
42
44{
45 return Symbol;
46}
47
49{
50 return XO("Converts stereo tracks to mono");
51}
52
53// EffectDefinitionInterface implementation
54
56{
57 // Really EffectTypeProcess, but this prevents it from showing in the Effect Menu
58 return EffectTypeHidden;
59}
60
62{
63 return false;
64}
65
67{
68 return 2;
69}
70
72{
73 return 1;
74}
75
76// Effect implementation
77
79{
80 // Do not use mWaveTracks here. We will possibly DELETE tracks,
81 // so we must use the "real" tracklist.
82 EffectOutputTracks outputs {
83 *mTracks,
84 GetType(),
85 // This effect ignores mT0 and mT1 but always mixes the entire tracks.
86 { { mTracks->GetStartTime(), mTracks->GetEndTime() } }
87 };
88 bool bGoodResult = true;
89
90 // Determine the total time (in samples) used by all of the target tracks
91 // only for progress dialog
92 sampleCount totalTime = 0;
93
94 auto trackRange = outputs.Get().Selected<WaveTrack>();
95 for (const auto left : trackRange) {
96 if (left->Channels().size() > 1) {
97 auto start = left->TimeToLongSamples(left->GetStartTime());
98 auto end = left->TimeToLongSamples(left->GetEndTime());
99 totalTime += (end - start);
100 }
101 }
102
103 // Process each stereo track
104 sampleCount curTime = 0;
105 bool refreshIter = false;
106
107 mProgress->SetMessage(XO("Mixing down to mono"));
108
109 // Don't use range-for, because iterators may be invalidated by erasure from
110 // the track list
111 while (trackRange.first != trackRange.second) {
112 auto track = *trackRange.first;
113 if (track->Channels().size() > 1) {
114 if (!ProcessOne(outputs.Get(), curTime, totalTime, *track))
115 break;
116 // The right channel has been deleted, so we must restart from the beginning
117 refreshIter = true;
118 }
119
120 if (refreshIter) {
121 trackRange = outputs.Get().Selected<WaveTrack>();
122 refreshIter = false;
123 }
124 else
125 ++trackRange.first;
126 }
127
128 if (bGoodResult)
129 outputs.Commit();
130
131 return bGoodResult;
132}
133
135 sampleCount & curTime, sampleCount totalTime, WaveTrack &track)
136{
137 auto idealBlockLen = track.GetMaxBlockSize() * 2;
138 bool bResult = true;
139 sampleCount processed = 0;
140
141 const auto start = track.GetStartTime();
142 const auto end = track.GetEndTime();
143
145 tracks.emplace_back(
146 track.SharedPointer<const SampleTrack>(), GetEffectStages(track));
147
148 Mixer mixer(move(tracks),
149 true, // Throw to abort mix-and-render if read fails:
151 start,
152 end,
153 1,
154 idealBlockLen,
155 false, // Not interleaved
156 track.GetRate(),
158
159 // Always make mono output; don't use WideEmptyCopy
160 auto outTrack = track.EmptyCopy();
161 auto tempList = TrackList::Temporary(nullptr, outTrack, nullptr);
162 assert(outTrack->IsLeader());
163 outTrack->ConvertToSampleFormat(floatSample);
164
165 double denominator = track.GetChannelGain(0) + track.GetChannelGain(1);
166 while (auto blockLen = mixer.Process()) {
167 auto buffer = mixer.GetBuffer();
168 for (auto i = 0; i < blockLen; i++)
169 ((float *)buffer)[i] /= denominator;
170
171 // If mixing channels that both had only 16 bit effective format
172 // (for example), and no gains or envelopes, still there should be
173 // dithering because of the averaging above, which may introduce samples
174 // lying between the quantization levels. So use widestSampleFormat.
175 outTrack->Append(buffer, floatSample, blockLen, 1, widestSampleFormat);
176
177 curTime += blockLen;
178 if (TotalProgress(curTime.as_double() / totalTime.as_double()))
179 return false;
180 }
181 outTrack->Flush();
182
183 const auto unlinkedTracks = outputs.UnlinkChannels(track);
184 assert(unlinkedTracks.size() == 2);
185 outputs.Remove(*unlinkedTracks[1]);
186
187 track.Clear(start, end);
188 track.Paste(start, *outTrack);
190
191 return bResult;
192}
193
195{
196 return true;
197}
EffectType
@ EffectTypeHidden
XO("Cut/Copy/Paste")
std::vector< MixerOptions::StageSpecification > GetEffectStages(const WaveTrack &track)
const auto tracks
virtual void SetMessage(const TranslatableString &message)=0
Change an existing dialog's message.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
const TrackList * inputTracks() const
Definition: EffectBase.h:91
BasicUI::ProgressDialog * mProgress
Definition: EffectBase.h:111
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:109
bool TotalProgress(double frac, const TranslatableString &={}) const
Definition: Effect.cpp:335
Performs effect computation.
Use this object to copy the input tracks to tentative outputTracks.
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
bool IsInteractive() const override
Whether the effect needs a dialog for entry of settings.
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
ComponentInterfaceSymbol GetSymbol() const override
static const ComponentInterfaceSymbol Symbol
Definition: StereoToMono.h:19
bool Process(EffectInstance &instance, EffectSettings &settings) override
TranslatableString GetDescription() const override
bool ProcessOne(TrackList &outputs, sampleCount &curTime, sampleCount totalTime, WaveTrack &track)
bool IsHiddenFromMenus() const override
Default is false.
EffectType GetType() const override
Type determines how it behaves.
virtual ~EffectStereoToMono()
Functions for doing the mixdown of the tracks.
Definition: Mix.h:27
std::vector< Input > Inputs
Definition: Mix.h:45
static RealtimeEffectList & Get(AudacityProject &project)
void Clear()
Use only in the main thread. Sends Remove messages.
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:160
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:975
void Remove(Track &track)
Remove a channel group, given the leader.
Definition: Track.cpp:745
std::vector< Track * > UnlinkChannels(Track &track)
Removes linkage if track belongs to a group.
Definition: Track.cpp:691
AudacityProject * GetOwner()
Definition: Track.h:1014
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &left={}, const Track::Holder &right={})
Definition: Track.cpp:1418
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:222
double GetStartTime() const override
Implement WideSampleSequence.
Definition: WaveTrack.cpp:3045
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:1436
void Paste(double t0, const Track &src) override
Definition: WaveTrack.cpp:2393
double GetEndTime() const override
Implement WideSampleSequence.
Definition: WaveTrack.cpp:3050
double GetRate() const override
Definition: WaveTrack.cpp:1085
float GetChannelGain(int channel) const override
Takes gain and pan into account.
Definition: WaveTrack.cpp:1153
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2699
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}, bool keepLink=true) const
Definition: WaveTrack.cpp:1323
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
double as_double() const
Definition: SampleCount.h:46
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
BuiltinEffectsModule::Registration< EffectStereoToMono > reg
Externalized state of a plug-in.
Immutable structure is an argument to Mixer's constructor.
Definition: MixerOptions.h:56