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 "StretchingSequence.h"
17#include "WaveTrack.h"
18
19using WaveTrackConstArray = std::vector < std::shared_ptr < const WaveTrack > >;
20
21//TODO-MB: wouldn't it make more sense to DELETE the time track after 'mix and render'?
23 const Mixer::WarpOptions &warpOptions,
24 const wxString &newTrackName,
25 WaveTrackFactory *trackFactory,
26 double rate, sampleFormat format,
27 double startTime, double endTime)
28{
29 if (trackRange.empty())
30 return {};
31
32 // This function was formerly known as "Quick Mix".
33 bool mono = false; /* flag if output can be mono without losing anything*/
34 bool oneinput = false; /* flag set to true if there is only one input track
35 (mono or stereo) */
36
37 auto first = *trackRange.begin();
38 assert(first); // because the range is known to be nonempty
39
40 // this only iterates tracks which are relevant to this function, i.e.
41 // selected WaveTracks. The tracklist is (confusingly) the list of all
42 // tracks in the project
43
44 size_t numWaves = 0; /* number of wave tracks in the selection */
45 size_t numMono = 0; /* number of mono, centre-panned wave tracks in selection*/
46 for (auto wt : trackRange) {
47 numWaves += wt->NChannels();
48 if (IsMono(*wt) && wt->GetPan() == 0)
49 numMono++;
50 }
51
52 if (numMono == numWaves)
53 mono = true;
54
55 /* the next loop will do two things at once:
56 * 1. build an array of all the wave tracks were are trying to process
57 * 2. determine when the set of WaveTracks starts and ends, in case we
58 * need to work out for ourselves when to start and stop rendering.
59 */
60
61 double mixStartTime = 0.0; /* start time of first track to start */
62 bool gotstart = false; // flag indicates we have found a start time
63 double mixEndTime = 0.0; /* end time of last track to end */
64 double tstart, tend; // start and end times for one track.
65
66 Mixer::Inputs waveArray;
67
68 for (auto wt : trackRange) {
69 const auto stretchingSequence =
70 StretchingSequence::Create(*wt, wt->GetClipInterfaces());
71 waveArray.emplace_back(stretchingSequence, GetEffectStages(*wt));
72 tstart = wt->GetStartTime();
73 tend = wt->GetEndTime();
74 if (tend > mixEndTime)
75 mixEndTime = tend;
76 // try and get the start time. If the track is empty we will get 0,
77 // which is ambiguous because it could just mean the track starts at
78 // the beginning of the project, as well as empty track. The give-away
79 // is that an empty track also ends at zero.
80
81 if (tstart != tend) {
82 // we don't get empty tracks here
83 if (!gotstart) {
84 // no previous start, use this one unconditionally
85 mixStartTime = tstart;
86 gotstart = true;
87 } else if (tstart < mixStartTime)
88 mixStartTime = tstart; // have a start, only make it smaller
89 } // end if start and end are different
90 }
91
92 /* create the destination track (NEW track) */
93 if (numWaves == first->NChannels())
94 oneinput = true;
95 // only one input track (either 1 mono or one linked stereo pair)
96
97 auto mix = trackFactory->Create(mono ? 1 : 2, *first);
98 mix->SetPan(0);
99 mix->SetGain(1.0f);
100 mix->SetRate(rate);
101 mix->ConvertToSampleFormat(format);
102 if(!oneinput)
103 mix->SetName(newTrackName);
104 mix->MoveTo(mixStartTime);
105
106 auto maxBlockLen = mix->GetIdealBlockSize();
107
108 // If the caller didn't specify a time range, use the whole range in which
109 // any input track had clips in it.
110 if (startTime == endTime) {
111 startTime = mixStartTime;
112 endTime = mixEndTime;
113 }
114
115 Mixer mixer(move(waveArray),
116 // Throw to abort mix-and-render if read fails:
117 true, warpOptions,
118 startTime, endTime, mono ? 1 : 2, maxBlockLen, false,
119 rate, format);
120
121 using namespace BasicUI;
122 auto updateResult = ProgressResult::Success;
123 {
124 auto effectiveFormat = mixer.EffectiveFormat();
125 auto pProgress = MakeProgress(XO("Mix and Render"),
126 XO("Mixing and rendering tracks"));
127
128 while (updateResult == ProgressResult::Success) {
129 auto blockLen = mixer.Process();
130
131 if (blockLen == 0)
132 break;
133
134 for(auto channel : mix->Channels())
135 {
136 auto buffer = mixer.GetBuffer(channel->GetChannelIndex());
137 channel->AppendBuffer(buffer, format, blockLen, 1, effectiveFormat);
138 }
139
140 updateResult = pProgress->Poll(
141 mixer.MixGetCurrentTime() - startTime, endTime - startTime);
142 }
143 }
144 mix->Flush();
145 if (updateResult == ProgressResult::Cancelled ||
146 updateResult == ProgressResult::Failed)
147 return {};
148 else {
149#if 0
150 int elapsedMS = wxGetElapsedTime();
151 double elapsedTime = elapsedMS * 0.001;
152 double maxTracks = totalTime / (elapsedTime / numWaves);
153
154 // Note: these shouldn't be translated - they're for debugging
155 // and profiling only.
156 wxPrintf(" Tracks: %d\n", numWaves);
157 wxPrintf(" Mix length: %f sec\n", totalTime);
158 wxPrintf("Elapsed time: %f sec\n", elapsedTime);
159 wxPrintf("Max number of tracks to mix in real time: %f\n", maxTracks);
160#endif
162 }
163
164 return mix;
165}
166
167#include "RealtimeEffectList.h"
168#include "RealtimeEffectState.h"
169
170std::vector<MixerOptions::StageSpecification>
172{
173 auto &effects = RealtimeEffectList::Get(track);
174 if (!effects.IsActive())
175 return {};
176 std::vector<MixerOptions::StageSpecification> result;
177 for (size_t i = 0, count = effects.GetStatesCount(); i < count; ++i) {
178 const auto pState = effects.GetStateAt(i);
179 if (!pState->IsEnabled())
180 continue;
181 const auto pEffect = pState->GetEffect();
182 if (!pEffect)
183 continue;
184 const auto &settings = pState->GetSettings();
185 if (!settings.has_value())
186 continue;
187 auto &stage = result.emplace_back(MixerOptions::StageSpecification{
188 [pEffect]{ return pEffect->MakeInstance(); },
189 settings });
190 }
191 return result;
192}
193
194/* The following registration objects need a home at a higher level to avoid
195 dependency either way between WaveTrack or RealtimeEffectList, which need to
196 be in different libraries that do not depend either on the other.
197
198 WaveTrack, like AudacityProject, has a registry for attachment of serializable
199 data. RealtimeEffectList exposes an interface for serialization. This is
200 where we connect them.
201
202 There is also registration for serialization of the project-wide master effect
203 stack (whether or not UI makes it available).
204 */
205#include "Project.h"
209};
210
212[](const AudacityProject &project, XMLWriter &xmlFile){
214} };
215
218 [](WaveTrack &track) { return &RealtimeEffectList::Get(track); }
219};
220
222[](const WaveTrack &track, auto &xmlFile) {
223 RealtimeEffectList::Get(track).WriteXML(xmlFile);
224} };
Toolkit-neutral facade for basic user interface services.
XO("Cut/Copy/Paste")
static ProjectFileIORegistry::ObjectReaderEntry projectAccessor
static ProjectFileIORegistry::ObjectWriterEntry projectWriter
static WaveTrackIORegistry::ObjectWriterEntry waveTrackWriter
Track::Holder MixAndRender(const TrackIterRange< const WaveTrack > &trackRange, const Mixer::WarpOptions &warpOptions, const wxString &newTrackName, WaveTrackFactory *trackFactory, double rate, sampleFormat format, double startTime, double endTime)
Mixes together all input tracks, applying any envelopes, amplitude gain, panning, and real-time effec...
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
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
const auto project
static Settings & settings()
Definition: TrackInfo.cpp:69
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:27
size_t Process(size_t maxSamples)
Definition: Mix.cpp:271
std::vector< Input > Inputs
Definition: Mix.h:45
sampleFormat EffectiveFormat() const
Deduce the effective width of the output, which may be narrower than the stored format.
Definition: Mix.cpp:388
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
Definition: Mix.cpp:378
double MixGetCurrentTime()
Current time in seconds (unwarped, i.e. always between startTime and stopTime)
Definition: Mix.cpp:393
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.
static std::shared_ptr< StretchingSequence > Create(const PlayableSequence &, const ClipConstHolders &clips)
std::shared_ptr< Track > Holder
Definition: Track.h:202
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:870
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
Definition: WaveTrack.cpp:393
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
bool IsMono(const Channel &channel)
Whether the channel is mono.
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:294
bool empty() const
Definition: IteratorX.h:58
Iterator begin() const
Definition: IteratorX.h:52
Immutable structure is an argument to Mixer's constructor.
Definition: MixerOptions.h:56
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:682
Typically statically constructed.