Audacity 3.2.0
MixAndRender.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5MixAndRender.cpp
6
7Paul Licameli split from Mix.cpp
8
9**********************************************************************/
10
11#include "MixAndRender.h"
12
13#include "BasicUI.h"
14#include "Mix.h"
15#include "RealtimeEffectList.h"
16#include "WaveTrack.h"
17
18using WaveTrackConstArray = std::vector < std::shared_ptr < const WaveTrack > >;
19
20//TODO-MB: wouldn't it make more sense to DELETE the time track after 'mix and render'?
22 const Mixer::WarpOptions &warpOptions,
23 const wxString &newTrackName,
24 WaveTrackFactory *trackFactory,
25 double rate, sampleFormat format,
26 double startTime, double endTime,
28{
29 uLeft.reset(), uRight.reset();
30 if (trackRange.empty())
31 return;
32
33 // This function was formerly known as "Quick Mix".
34 bool mono = false; /* flag if output can be mono without losing anything*/
35 bool oneinput = false; /* flag set to true if there is only one input track
36 (mono or stereo) */
37
38 auto first = *trackRange.begin();
39 assert(first); // because the range is known to be nonempty
40
41 // this only iterates tracks which are relevant to this function, i.e.
42 // selected WaveTracks. The tracklist is (confusingly) the list of all
43 // tracks in the project
44
45 int numWaves = 0; /* number of wave tracks in the selection */
46 int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/
47 for(auto wt : trackRange) {
48 numWaves++;
49 float pan = wt->GetPan();
50 if (wt->GetChannel() == Track::MonoChannel && pan == 0)
51 numMono++;
52 }
53
54 if (numMono == numWaves)
55 mono = true;
56
57 /* the next loop will do two things at once:
58 * 1. build an array of all the wave tracks were are trying to process
59 * 2. determine when the set of WaveTracks starts and ends, in case we
60 * need to work out for ourselves when to start and stop rendering.
61 */
62
63 double mixStartTime = 0.0; /* start time of first track to start */
64 bool gotstart = false; // flag indicates we have found a start time
65 double mixEndTime = 0.0; /* end time of last track to end */
66 double tstart, tend; // start and end times for one track.
67
68 Mixer::Inputs waveArray;
69
70 for(auto wt : trackRange) {
71 waveArray.emplace_back(
72 wt->SharedPointer<const SampleTrack>(), GetEffectStages(*wt));
73 tstart = wt->GetStartTime();
74 tend = wt->GetEndTime();
75 if (tend > mixEndTime)
76 mixEndTime = tend;
77 // try and get the start time. If the track is empty we will get 0,
78 // which is ambiguous because it could just mean the track starts at
79 // the beginning of the project, as well as empty track. The give-away
80 // is that an empty track also ends at zero.
81
82 if (tstart != tend) {
83 // we don't get empty tracks here
84 if (!gotstart) {
85 // no previous start, use this one unconditionally
86 mixStartTime = tstart;
87 gotstart = true;
88 } else if (tstart < mixStartTime)
89 mixStartTime = tstart; // have a start, only make it smaller
90 } // end if start and end are different
91 }
92
93 /* create the destination track (NEW track) */
94 if (numWaves == (int)TrackList::Channels(first).size())
95 oneinput = true;
96 // only one input track (either 1 mono or one linked stereo pair)
97
98 // EmptyCopy carries over any interesting channel group information
99 // But make sure the left is unlinked before we re-link
100 // And reset pan and gain
101 auto mixLeft =
102 first->EmptyCopy(trackFactory->GetSampleBlockFactory(), false);
103 mixLeft->SetPan(0);
104 mixLeft->SetGain(1);
105 mixLeft->SetRate(rate);
106 mixLeft->ConvertToSampleFormat(format);
107 if (oneinput)
108 mixLeft->SetName(first->GetName()); /* set name of output track to be the same as the sole input track */
109 else
110 /* i18n-hint: noun, means a track, made by mixing other tracks */
111 mixLeft->SetName(newTrackName);
112 mixLeft->SetOffset(mixStartTime);
113
114 // TODO: more-than-two-channels
115 decltype(mixLeft) mixRight{};
116 if ( !mono ) {
117 mixRight = trackFactory->Create(format, rate);
118 if (oneinput) {
119 auto channels = TrackList::Channels(first);
120 if (channels.size() > 1)
121 mixRight->SetName((*channels.begin().advance(1))->GetName()); /* set name to match input track's right channel!*/
122 else
123 mixRight->SetName(first->GetName()); /* set name to that of sole input channel */
124 }
125 else
126 mixRight->SetName(newTrackName);
127 mixRight->SetOffset(mixStartTime);
128 }
129
130
131 auto maxBlockLen = mixLeft->GetIdealBlockSize();
132
133 // If the caller didn't specify a time range, use the whole range in which
134 // any input track had clips in it.
135 if (startTime == endTime) {
136 startTime = mixStartTime;
137 endTime = mixEndTime;
138 }
139
140 Mixer mixer(move(waveArray),
141 // Throw to abort mix-and-render if read fails:
142 true, warpOptions,
143 startTime, endTime, mono ? 1 : 2, maxBlockLen, false,
144 rate, format);
145
146 using namespace BasicUI;
147 auto updateResult = ProgressResult::Success;
148 {
149 auto effectiveFormat = mixer.EffectiveFormat();
150 auto pProgress = MakeProgress(XO("Mix and Render"),
151 XO("Mixing and rendering tracks"));
152
153 while (updateResult == ProgressResult::Success) {
154 auto blockLen = mixer.Process();
155
156 if (blockLen == 0)
157 break;
158
159 if (mono) {
160 auto buffer = mixer.GetBuffer();
161 mixLeft->Append(buffer, format, blockLen, 1, effectiveFormat);
162 }
163 else {
164 auto buffer = mixer.GetBuffer(0);
165 mixLeft->Append(buffer, format, blockLen, 1, effectiveFormat);
166 buffer = mixer.GetBuffer(1);
167 mixRight->Append(buffer, format, blockLen, 1, effectiveFormat);
168 }
169
170 updateResult = pProgress->Poll(
171 mixer.MixGetCurrentTime() - startTime, endTime - startTime);
172 }
173 }
174
175 mixLeft->Flush();
176 if (!mono)
177 mixRight->Flush();
178 if (updateResult == ProgressResult::Cancelled || updateResult == ProgressResult::Failed)
179 {
180 return;
181 }
182 else {
183 uLeft = mixLeft, uRight = mixRight;
184#if 0
185 int elapsedMS = wxGetElapsedTime();
186 double elapsedTime = elapsedMS * 0.001;
187 double maxTracks = totalTime / (elapsedTime / numWaves);
188
189 // Note: these shouldn't be translated - they're for debugging
190 // and profiling only.
191 wxPrintf(" Tracks: %d\n", numWaves);
192 wxPrintf(" Mix length: %f sec\n", totalTime);
193 wxPrintf("Elapsed time: %f sec\n", elapsedTime);
194 wxPrintf("Max number of tracks to mix in real time: %f\n", maxTracks);
195#endif
196
197 for (auto pTrack : { uLeft.get(), uRight.get() })
198 if (pTrack)
200 }
201}
202
203#include "RealtimeEffectList.h"
204#include "RealtimeEffectState.h"
205
206std::vector<MixerOptions::StageSpecification>
208{
209 auto &effects = RealtimeEffectList::Get(track);
210 if (!effects.IsActive())
211 return {};
212 std::vector<MixerOptions::StageSpecification> result;
213 for (size_t i = 0, count = effects.GetStatesCount(); i < count; ++i) {
214 const auto pState = effects.GetStateAt(i);
215 if (!pState->IsEnabled())
216 continue;
217 const auto pEffect = pState->GetEffect();
218 if (!pEffect)
219 continue;
220 const auto &settings = pState->GetSettings();
221 if (!settings.has_value())
222 continue;
223 auto &stage = result.emplace_back(MixerOptions::StageSpecification{
224 [pEffect]{ return pEffect->MakeInstance(); },
225 settings });
226 }
227 return result;
228}
229
230/* The following registration objects need a home at a higher level to avoid
231 dependency either way between WaveTrack or RealtimeEffectList, which need to
232 be in different libraries that do not depend either on the other.
233
234 WaveTrack, like AudacityProject, has a registry for attachment of serializable
235 data. RealtimeEffectList exposes an interface for serialization. This is
236 where we connect them.
237
238 There is also registration for serialization of the project-wide master effect
239 stack (whether or not UI makes it available).
240 */
241#include "Project.h"
244 [](AudacityProject &project) { return &RealtimeEffectList::Get(project); }
245};
246
248[](const AudacityProject &project, XMLWriter &xmlFile){
249 RealtimeEffectList::Get(project).WriteXML(xmlFile);
250} };
251
254 [](WaveTrack &track) { return &RealtimeEffectList::Get(track); }
255};
256
258[](const WaveTrack &track, auto &xmlFile) {
259 if (track.IsLeader())
260 RealtimeEffectList::Get(track).WriteXML(xmlFile);
261} };
Toolkit-neutral facade for basic user interface services.
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
Definition: Export.h:39
int format
Definition: ExportPCM.cpp:53
XO("Cut/Copy/Paste")
static ProjectFileIORegistry::ObjectReaderEntry projectAccessor
static ProjectFileIORegistry::ObjectWriterEntry projectWriter
void MixAndRender(const TrackIterRange< const WaveTrack > &trackRange, const Mixer::WarpOptions &warpOptions, const wxString &newTrackName, WaveTrackFactory *trackFactory, double rate, sampleFormat format, double startTime, double endTime, WaveTrack::Holder &uLeft, WaveTrack::Holder &uRight)
Mixes together all input tracks, applying any envelopes, amplitude gain, panning, and real-time effec...
static WaveTrackIORegistry::ObjectWriterEntry waveTrackWriter
static WaveTrackIORegistry::ObjectReaderEntry waveTrackAccessor
std::vector< MixerOptions::StageSpecification > GetEffectStages(const WaveTrack &track)
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
static Settings & settings()
Definition: TrackInfo.cpp:87
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Functions for doing the mixdown of the tracks.
Definition: Mix.h:26
size_t Process(size_t maxSamples)
Definition: Mix.cpp:272
std::vector< Input > Inputs
Definition: Mix.h:43
sampleFormat EffectiveFormat() const
Deduce the effective width of the output, which may be narrower than the stored format.
Definition: Mix.cpp:386
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
Definition: Mix.cpp:376
double MixGetCurrentTime()
Current time in seconds (unwarped, i.e. always between startTime and stopTime)
Definition: Mix.cpp:391
static RealtimeEffectList & Get(AudacityProject &project)
void Clear()
Use only in the main thread. Sends Remove messages.
static const std::string & XMLTag()
void WriteXML(XMLWriter &xmlFile) const
Use only in the main thread, to avoid races.
@ MonoChannel
Definition: Track.h:285
bool IsLeader() const
Definition: Track.cpp:406
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1544
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:565
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
Definition: WaveTrack.cpp:116
const SampleBlockFactoryPtr & GetSampleBlockFactory() const
Definition: WaveTrack.h:582
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
std::shared_ptr< WaveTrack > Holder
Definition: WaveTrack.h:99
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
std::unique_ptr< ProgressDialog > MakeProgress(const TranslatableString &title, const TranslatableString &message, unsigned flags=(ProgressShowStop|ProgressShowCancel), const TranslatableString &remainingLabelText={})
Create and display a progress dialog.
Definition: BasicUI.h:289
bool empty() const
Definition: MemoryX.h:296
Iterator begin() const
Definition: MemoryX.h:290
Immutable structure is an argument to Mixer's constructor.
Definition: MixerOptions.h:54
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:1168
Typically statically constructed.