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 assert(first->IsLeader()); // precondition on trackRange
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 += wt->NChannels();
49 if (IsMono(*wt) && wt->GetPan() == 0)
50 numMono++;
51 }
52
53 if (numMono == numWaves)
54 mono = true;
55
56 /* the next loop will do two things at once:
57 * 1. build an array of all the wave tracks were are trying to process
58 * 2. determine when the set of WaveTracks starts and ends, in case we
59 * need to work out for ourselves when to start and stop rendering.
60 */
61
62 double mixStartTime = 0.0; /* start time of first track to start */
63 bool gotstart = false; // flag indicates we have found a start time
64 double mixEndTime = 0.0; /* end time of last track to end */
65 double tstart, tend; // start and end times for one track.
66
67 Mixer::Inputs waveArray;
68
69 for (auto wt : trackRange) {
70 const auto stretchingSequence =
71 StretchingSequence::Create(*wt, wt->GetClipInterfaces());
72 waveArray.emplace_back(stretchingSequence, 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::NChannels(*first))
95 oneinput = true;
96 // only one input track (either 1 mono or one linked stereo pair)
97
98 auto result = trackFactory->Create(mono ? 1 : 2, *first);
99 auto mix = static_cast<WaveTrack*>(*result->begin());
100 mix->SetPan(0);
101 mix->SetGain(1.0f);
102 mix->SetRate(rate);
103 mix->ConvertToSampleFormat(format);
104 if(!oneinput)
105 mix->SetName(newTrackName);
106 mix->MoveTo(mixStartTime);
107
108 auto maxBlockLen = mix->GetIdealBlockSize();
109
110 // If the caller didn't specify a time range, use the whole range in which
111 // any input track had clips in it.
112 if (startTime == endTime) {
113 startTime = mixStartTime;
114 endTime = mixEndTime;
115 }
116
117 Mixer mixer(move(waveArray),
118 // Throw to abort mix-and-render if read fails:
119 true, warpOptions,
120 startTime, endTime, mono ? 1 : 2, maxBlockLen, false,
121 rate, format);
122
123 using namespace BasicUI;
124 auto updateResult = ProgressResult::Success;
125 {
126 auto effectiveFormat = mixer.EffectiveFormat();
127 auto pProgress = MakeProgress(XO("Mix and Render"),
128 XO("Mixing and rendering tracks"));
129
130 while (updateResult == ProgressResult::Success) {
131 auto blockLen = mixer.Process();
132
133 if (blockLen == 0)
134 break;
135
136 for(auto channel : mix->Channels())
137 {
138 auto buffer = mixer.GetBuffer(channel->ReallyGetChannelIndex());
139 channel->AppendBuffer(buffer, format, blockLen, 1, effectiveFormat);
140 }
141
142 updateResult = pProgress->Poll(
143 mixer.MixGetCurrentTime() - startTime, endTime - startTime);
144 }
145 }
146 mix->Flush();
147 if (updateResult == ProgressResult::Cancelled ||
148 updateResult == ProgressResult::Failed)
149 return {};
150 else {
151#if 0
152 int elapsedMS = wxGetElapsedTime();
153 double elapsedTime = elapsedMS * 0.001;
154 double maxTracks = totalTime / (elapsedTime / numWaves);
155
156 // Note: these shouldn't be translated - they're for debugging
157 // and profiling only.
158 wxPrintf(" Tracks: %d\n", numWaves);
159 wxPrintf(" Mix length: %f sec\n", totalTime);
160 wxPrintf("Elapsed time: %f sec\n", elapsedTime);
161 wxPrintf("Max number of tracks to mix in real time: %f\n", maxTracks);
162#endif
164 }
165
166 return result;
167}
168
169#include "RealtimeEffectList.h"
170#include "RealtimeEffectState.h"
171
172std::vector<MixerOptions::StageSpecification>
174{
175 auto &effects = RealtimeEffectList::Get(track);
176 if (!effects.IsActive())
177 return {};
178 std::vector<MixerOptions::StageSpecification> result;
179 for (size_t i = 0, count = effects.GetStatesCount(); i < count; ++i) {
180 const auto pState = effects.GetStateAt(i);
181 if (!pState->IsEnabled())
182 continue;
183 const auto pEffect = pState->GetEffect();
184 if (!pEffect)
185 continue;
186 const auto &settings = pState->GetSettings();
187 if (!settings.has_value())
188 continue;
189 auto &stage = result.emplace_back(MixerOptions::StageSpecification{
190 [pEffect]{ return pEffect->MakeInstance(); },
191 settings });
192 }
193 return result;
194}
195
196/* The following registration objects need a home at a higher level to avoid
197 dependency either way between WaveTrack or RealtimeEffectList, which need to
198 be in different libraries that do not depend either on the other.
199
200 WaveTrack, like AudacityProject, has a registry for attachment of serializable
201 data. RealtimeEffectList exposes an interface for serialization. This is
202 where we connect them.
203
204 There is also registration for serialization of the project-wide master effect
205 stack (whether or not UI makes it available).
206 */
207#include "Project.h"
211};
212
214[](const AudacityProject &project, XMLWriter &xmlFile){
216} };
217
220 [](WaveTrack &track) { return &RealtimeEffectList::Get(track); }
221};
222
224[](const WaveTrack &track, auto &xmlFile) {
225 if (track.IsLeader())
226 RealtimeEffectList::Get(track).WriteXML(xmlFile);
227} };
Toolkit-neutral facade for basic user interface services.
XO("Cut/Copy/Paste")
static ProjectFileIORegistry::ObjectReaderEntry projectAccessor
static ProjectFileIORegistry::ObjectWriterEntry projectWriter
static WaveTrackIORegistry::ObjectWriterEntry waveTrackWriter
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
TrackListHolder 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...
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
std::shared_ptr< TrackList > TrackListHolder
Definition: Track.h:42
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:385
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
Definition: Mix.cpp:375
double MixGetCurrentTime()
Current time in seconds (unwarped, i.e. always between startTime and stopTime)
Definition: Mix.cpp:390
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)
size_t NChannels() const
Definition: Track.cpp:960
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:1251
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
Definition: WaveTrack.cpp:726
A Track that contains audio waveform data.
Definition: WaveTrack.h:222
bool IsLeader() const override
Definition: WaveTrack.cpp:2778
void SetPan(float newPan)
Definition: WaveTrack.cpp:1140
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:292
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:56
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:807
Typically statically constructed.