Audacity 3.2.0
Paulstretch.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Paulstretch.cpp
6
7 Nasca Octavian Paul (Paul Nasca)
8 Some GUI code was taken from the Echo effect
9
10*******************************************************************//*******************************************************************/
16#include "Paulstretch.h"
17#include "EffectEditor.h"
18#include "LoadEffects.h"
19
20#include <algorithm>
21
22#include <math.h>
23
24#include <wx/valgen.h>
25
26#include "ShuttleGui.h"
27#include "FFT.h"
28#include "../widgets/valnum.h"
29#include "AudacityMessageBox.h"
30#include "Prefs.h"
31
32#include "WaveTrack.h"
33
35{
38 > parameters;
39 return parameters;
40}
41
45{
46public:
47 PaulStretch(float rap_, size_t in_bufsize_, float samplerate_);
48 //in_bufsize is also a half of a FFT buffer (in samples)
49 virtual ~PaulStretch();
50
51 void process(float *smps, size_t nsmps);
52
53 size_t get_nsamples();//how many samples are required to be added in the pool next time
54 size_t get_nsamples_for_fill();//how many samples are required to be added for a complete buffer refill (at start of the song or after seek)
55
56private:
57 void process_spectrum(float *WXUNUSED(freq)) {};
58
59 const float samplerate;
60 const float rap;
61 const size_t in_bufsize;
62
63public:
64 const size_t out_bufsize;
66
67private:
69
70public:
71 const size_t poolsize;//how many samples are inside the input_pool size (need to know how many samples to fill when seeking)
72
73private:
74 const Floats in_pool;//de marimea in_bufsize
75
76 double remained_samples;//how many fraction of samples has remained (0..1)
77
79};
80
81//
82// EffectPaulstretch
83//
84
86{ XO("Paulstretch") };
87
89
90BEGIN_EVENT_TABLE(EffectPaulstretch, wxEvtHandler)
91 EVT_TEXT(wxID_ANY, EffectPaulstretch::OnText)
93
95{
96 Parameters().Reset(*this);
97
98 SetLinearEffectFlag(true);
99}
100
102{
103}
104
105// ComponentInterface implementation
106
108{
109 return Symbol;
110}
111
113{
114 return XO("Paulstretch is only for an extreme time-stretch or \"stasis\" effect");
115}
116
118{
119 return L"Paulstretch";
120}
121
122// EffectDefinitionInterface implementation
123
125{
126 return EffectTypeProcess;
127}
128
129// Effect implementation
130
132 const EffectSettings &, double previewLength) const
133{
134 // FIXME: Preview is currently at the project rate, but should really be
135 // at the track rate (bugs 1284 and 852).
136 auto minDuration = GetBufferSize(mProjectRate) * 2 + 1;
137
138 // Preview playback may need to be trimmed but this is the smallest selection that we can use.
139 double minLength = std::max<double>(minDuration / mProjectRate, previewLength / mAmount);
140
141 return minLength;
142}
143
144
146{
148 m_t1=mT1;
149 int count=0;
150 for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
151 double trackStart = track->GetStartTime();
152 double trackEnd = track->GetEndTime();
153 double t0 = mT0 < trackStart? trackStart: mT0;
154 double t1 = mT1 > trackEnd? trackEnd: mT1;
155
156 if (t1 > t0) {
157 if (!ProcessOne(track, t0,t1,count))
158 return false;
159 }
160
161 count++;
162 }
163 mT1=m_t1;
164
166
167 return true;
168}
169
170
171std::unique_ptr<EffectEditor> EffectPaulstretch::PopulateOrExchange(
173 const EffectOutputs *)
174{
175 mUIParent = S.GetParent();
176 S.StartMultiColumn(2, wxALIGN_CENTER);
177 {
178 S
179 .Validator<FloatingPointValidator<float>>(
180 1, &mAmount, NumValidatorStyle::DEFAULT, Amount.min)
181 /* i18n-hint: This is how many times longer the sound will be, e.g. applying
182 * the effect to a 1-second sample, with the default Stretch Factor of 10.0
183 * will give an (approximately) 10 second sound
184 */
185 .AddTextBox(XXO("&Stretch Factor:"), wxT(""), 10);
186
187 S
188 .Validator<FloatingPointValidator<float>>(
189 3, &mTime_resolution, NumValidatorStyle::ONE_TRAILING_ZERO, Time.min)
190 .AddTextBox(XXO("&Time Resolution (seconds):"), L"", 10);
191 }
192 S.EndMultiColumn();
193 return nullptr;
194};
195
197{
198 if (!mUIParent->TransferDataToWindow())
199 {
200 return false;
201 }
202
203 return true;
204}
205
207{
208 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
209 {
210 return false;
211 }
212
213 return true;
214}
215
216// EffectPaulstretch implementation
217
218void EffectPaulstretch::OnText(wxCommandEvent & WXUNUSED(evt))
219{
221 mUIParent, mUIParent->TransferDataFromWindow());
222}
223
224size_t EffectPaulstretch::GetBufferSize(double rate) const
225{
226 // Audacity's fft requires a power of 2
227 float tmp = rate * mTime_resolution / 2.0;
228 tmp = log(tmp) / log(2.0);
229 tmp = pow(2.0, floor(tmp + 0.5));
230
231 auto stmp = size_t(tmp);
232 if (stmp != tmp)
233 // overflow
234 return 0;
235 if (stmp >= 2 * stmp)
236 // overflow
237 return 0;
238
239 return std::max<size_t>(stmp, 128);
240}
241
242bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count)
243{
244 const auto badAllocMessage =
245 XO("Requested value exceeds memory capacity.");
246
247 const auto stretch_buf_size = GetBufferSize(track->GetRate());
248 if (stretch_buf_size == 0) {
249 EffectUIServices::DoMessageBox(*this, badAllocMessage);
250 return false;
251 }
252
253 double amount = this->mAmount;
254
255 auto start = track->TimeToLongSamples(t0);
256 auto end = track->TimeToLongSamples(t1);
257 auto len = end - start;
258
259 const auto minDuration = stretch_buf_size * 2 + 1;
260 if (minDuration < stretch_buf_size) {
261 // overflow!
262 EffectUIServices::DoMessageBox(*this, badAllocMessage);
263 return false;
264 }
265
266 if (len < minDuration) { //error because the selection is too short
267
268 float maxTimeRes = log( len.as_double() ) / log(2.0);
269 maxTimeRes = pow(2.0, floor(maxTimeRes) + 0.5);
270 maxTimeRes = maxTimeRes / track->GetRate();
271
272 if (this->IsPreviewing()) {
273 double defaultPreviewLen;
274 gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &defaultPreviewLen, 6.0);
275
276 if ((minDuration / mProjectRate) < defaultPreviewLen) {
278 /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
279 XO("Audio selection too short to preview.\n\n"
280 "Try increasing the audio selection to at least %.1f seconds,\n"
281 "or reducing the 'Time Resolution' to less than %.1f seconds.")
282 .Format(
283 (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
284 floor(maxTimeRes * 10.0) / 10.0),
285 wxOK | wxICON_EXCLAMATION );
286 }
287 else {
289 /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
290 XO("Unable to Preview.\n\n"
291 "For the current audio selection, the maximum\n"
292 "'Time Resolution' is %.1f seconds.")
293 .Format( floor(maxTimeRes * 10.0) / 10.0 ),
294 wxOK | wxICON_EXCLAMATION );
295 }
296 }
297 else {
299 /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
300 XO("The 'Time Resolution' is too long for the selection.\n\n"
301 "Try increasing the audio selection to at least %.1f seconds,\n"
302 "or reducing the 'Time Resolution' to less than %.1f seconds.")
303 .Format(
304 (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
305 floor(maxTimeRes * 10.0) / 10.0),
306 wxOK | wxICON_EXCLAMATION );
307 }
308
309 return false;
310 }
311
312
313 auto dlen = len.as_double();
314 double adjust_amount = dlen /
315 (dlen - ((double)stretch_buf_size * 2.0));
316 amount = 1.0 + (amount - 1.0) * adjust_amount;
317
318 auto outputTrack = track->EmptyCopy();
319
320 try {
321 // This encloses all the allocations of buffers, including those in
322 // the constructor of the PaulStretch object
323
324 PaulStretch stretch(amount, stretch_buf_size, track->GetRate());
325
326 auto nget = stretch.get_nsamples_for_fill();
327
328 auto bufsize = stretch.poolsize;
329 Floats buffer0{ bufsize };
330 float *bufferptr0 = buffer0.get();
331 bool first_time = true;
332
333 const auto fade_len = std::min<size_t>(100, bufsize / 2 - 1);
334 bool cancelled = false;
335
336 {
337 Floats fade_track_smps{ fade_len };
338 decltype(len) s=0;
339
340 while (s < len) {
341 track->GetFloats(bufferptr0, start + s, nget);
342 stretch.process(buffer0.get(), nget);
343
344 if (first_time) {
345 stretch.process(buffer0.get(), 0);
346 };
347
348 s += nget;
349
350 if (first_time){//blend the start of the selection
351 track->GetFloats(fade_track_smps.get(), start, fade_len);
352 first_time = false;
353 for (size_t i = 0; i < fade_len; i++){
354 float fi = (float)i / (float)fade_len;
355 stretch.out_buf[i] =
356 stretch.out_buf[i] * fi + (1.0 - fi) * fade_track_smps[i];
357 }
358 }
359 if (s >= len){//blend the end of the selection
360 track->GetFloats(fade_track_smps.get(), end - fade_len, fade_len);
361 for (size_t i = 0; i < fade_len; i++){
362 float fi = (float)i / (float)fade_len;
363 auto i2 = bufsize / 2 - 1 - i;
364 stretch.out_buf[i2] =
365 stretch.out_buf[i2] * fi + (1.0 - fi) *
366 fade_track_smps[fade_len - 1 - i];
367 }
368 }
369
370 outputTrack->Append((samplePtr)stretch.out_buf.get(), floatSample, stretch.out_bufsize);
371
372 nget = stretch.get_nsamples();
373 if (TrackProgress(count,
374 s.as_double() / len.as_double()
375 )) {
376 cancelled = true;
377 break;
378 }
379 }
380 }
381
382 if (!cancelled){
383 outputTrack->Flush();
384
385 track->Clear(t0,t1);
386 track->Paste(t0, outputTrack.get());
387 m_t1 = mT0 + outputTrack->GetEndTime();
388 }
389
390 return !cancelled;
391 }
392 catch ( const std::bad_alloc& ) {
393 EffectUIServices::DoMessageBox(*this, badAllocMessage);
394 return false;
395 }
396};
397
398/*************************************************************/
399
400
401PaulStretch::PaulStretch(float rap_, size_t in_bufsize_, float samplerate_ )
402 : samplerate { samplerate_ }
403 , rap { std::max(1.0f, rap_) }
404 , in_bufsize { in_bufsize_ }
405 , out_bufsize { std::max(size_t{ 8 }, in_bufsize) }
406 , out_buf { out_bufsize }
407 , old_out_smp_buf { out_bufsize * 2, true }
408 , poolsize { in_bufsize_ * 2 }
409 , in_pool { poolsize, true }
410 , remained_samples { 0.0 }
411 , fft_smps { poolsize, true }
412 , fft_c { poolsize, true }
413 , fft_s { poolsize, true }
414 , fft_freq { poolsize, true }
415 , fft_tmp { poolsize }
416{
417}
418
420{
421}
422
423void PaulStretch::process(float *smps, size_t nsmps)
424{
425 //add NEW samples to the pool
426 if ((smps != NULL) && (nsmps != 0)) {
427 if (nsmps > poolsize) {
428 nsmps = poolsize;
429 }
430 int nleft = poolsize - nsmps;
431
432 //move left the samples from the pool to make room for NEW samples
433 for (int i = 0; i < nleft; i++)
434 in_pool[i] = in_pool[i + nsmps];
435
436 //add NEW samples to the pool
437 for (size_t i = 0; i < nsmps; i++)
438 in_pool[i + nleft] = smps[i];
439 }
440
441 //get the samples from the pool
442 for (size_t i = 0; i < poolsize; i++)
443 fft_smps[i] = in_pool[i];
445
446 RealFFT(poolsize, fft_smps.get(), fft_c.get(), fft_s.get());
447
448 for (size_t i = 0; i < poolsize / 2; i++)
449 fft_freq[i] = sqrt(fft_c[i] * fft_c[i] + fft_s[i] * fft_s[i]);
451
452
453 //put randomize phases to frequencies and do a IFFT
454 float inv_2p15_2pi = 1.0 / 16384.0 * (float)M_PI;
455 for (size_t i = 1; i < poolsize / 2; i++) {
456 unsigned int random = (rand()) & 0x7fff;
457 float phase = random * inv_2p15_2pi;
458 float s = fft_freq[i] * sin(phase);
459 float c = fft_freq[i] * cos(phase);
460
461 fft_c[i] = fft_c[poolsize - i] = c;
462
463 fft_s[i] = s; fft_s[poolsize - i] = -s;
464 }
465 fft_c[0] = fft_s[0] = 0.0;
466 fft_c[poolsize / 2] = fft_s[poolsize / 2] = 0.0;
467
468 FFT(poolsize, true, fft_c.get(), fft_s.get(), fft_smps.get(), fft_tmp.get());
469
470 float max = 0.0, max2 = 0.0;
471 for (size_t i = 0; i < poolsize; i++) {
472 max = std::max(max, fabsf(fft_tmp[i]));
473 max2 = std::max(max2, fabsf(fft_smps[i]));
474 }
475
476
477 //make the output buffer
478 float tmp = 1.0 / (float) out_bufsize * M_PI;
479 float hinv_sqrt2 = 0.853553390593f;//(1.0+1.0/sqrt(2))*0.5;
480
481 float ampfactor = 1.0;
482 if (rap < 1.0)
483 ampfactor = rap * 0.707;
484 else
485 ampfactor = (out_bufsize / (float)poolsize) * 4.0;
486
487 for (size_t i = 0; i < out_bufsize; i++) {
488 float a = (0.5 + 0.5 * cos(i * tmp));
489 float out = fft_smps[i + out_bufsize] * (1.0 - a) + old_out_smp_buf[i] * a;
490 out_buf[i] =
491 out * (hinv_sqrt2 - (1.0 - hinv_sqrt2) * cos(i * 2.0 * tmp)) *
492 ampfactor;
493 }
494
495 //copy the current output buffer to old buffer
496 for (size_t i = 0; i < out_bufsize * 2; i++)
498}
499
501{
502 double r = out_bufsize / rap;
503 auto ri = (size_t)floor(r);
504 double rf = r - floor(r);
505
506 remained_samples += rf;
507 if (remained_samples >= 1.0){
508 ri += (size_t)floor(remained_samples);
510 }
511
512 if (ri > poolsize) {
513 ri = poolsize;
514 }
515
516 return ri;
517}
518
520{
521 return poolsize;
522}
wxT("CloseDown"))
END_EVENT_TABLE()
#define M_PI
Definition: Distortion.cpp:30
EffectType
@ EffectTypeProcess
void WindowFunc(int whichFunction, size_t NumSamples, float *in)
Definition: FFT.cpp:513
void RealFFT(size_t NumSamples, const float *RealIn, float *RealOut, float *ImagOut)
Definition: FFT.cpp:228
void FFT(size_t NumSamples, bool InverseTransform, const float *RealIn, const float *ImagIn, float *RealOut, float *ImagOut)
Definition: FFT.cpp:129
@ eWinFuncHann
Definition: FFT.h:114
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
FileConfig * gPrefs
Definition: Prefs.cpp:70
char * samplePtr
Definition: SampleFormat.h:55
#define S(N)
Definition: ToChars.cpp:64
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mT1
Definition: EffectBase.h:113
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:111
bool IsPreviewing() const
Definition: EffectBase.h:89
double mProjectRate
Definition: EffectBase.h:105
double mT0
Definition: EffectBase.h:112
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: EffectBase.cpp:232
static bool EnableApply(wxWindow *parent, bool enable=true)
Enable or disable the Apply button of the dialog that contains parent.
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:396
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:346
Performs effect computation.
Hold values to send to effect output meters.
Interface for manipulations of an Effect's settings.
An Extreme Time Stretch and Time Smear effect.
Definition: Paulstretch.h:21
bool TransferDataToWindow(const EffectSettings &settings) override
static constexpr EffectParameter Time
Definition: Paulstretch.h:70
float mTime_resolution
Definition: Paulstretch.h:62
const EffectParameterMethods & Parameters() const override
Definition: Paulstretch.cpp:34
virtual ~EffectPaulstretch()
bool TransferDataFromWindow(EffectSettings &settings) override
static const ComponentInterfaceSymbol Symbol
Definition: Paulstretch.h:25
TranslatableString GetDescription() const override
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
double CalcPreviewInputLength(const EffectSettings &settings, double previewLength) const override
EffectType GetType() const override
Type determines how it behaves.
wxWeakRef< wxWindow > mUIParent
Definition: Paulstretch.h:59
bool ProcessOne(WaveTrack *track, double t0, double t1, int count)
std::unique_ptr< EffectEditor > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
void OnText(wxCommandEvent &evt)
bool Process(EffectInstance &instance, EffectSettings &settings) override
size_t GetBufferSize(double rate) const
ComponentInterfaceSymbol GetSymbol() const override
static constexpr EffectParameter Amount
Definition: Paulstretch.h:68
static int DoMessageBox(const EffectPlugin &plugin, const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
Abstract base class used in importing a file.
Class that helps EffectPaulStretch. It does the FFTs and inner loop of the effect.
Definition: Paulstretch.cpp:45
const float rap
Definition: Paulstretch.cpp:60
void process(float *smps, size_t nsmps)
const Floats in_pool
Definition: Paulstretch.cpp:74
virtual ~PaulStretch()
double remained_samples
Definition: Paulstretch.cpp:76
const size_t out_bufsize
Definition: Paulstretch.cpp:64
const Floats old_out_smp_buf
Definition: Paulstretch.cpp:68
const Floats fft_tmp
Definition: Paulstretch.cpp:78
size_t get_nsamples_for_fill()
const float samplerate
Definition: Paulstretch.cpp:57
const Floats fft_freq
Definition: Paulstretch.cpp:78
PaulStretch(float rap_, size_t in_bufsize_, float samplerate_)
const Floats fft_smps
Definition: Paulstretch.cpp:78
const size_t in_bufsize
Definition: Paulstretch.cpp:61
const size_t poolsize
Definition: Paulstretch.cpp:71
const Floats out_buf
Definition: Paulstretch.cpp:65
size_t get_nsamples()
void process_spectrum(float *WXUNUSED(freq))
Definition: Paulstretch.cpp:57
const Floats fft_s
Definition: Paulstretch.cpp:78
const Floats fft_c
Definition: Paulstretch.cpp:78
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Retrieve samples from a track in floating-point format, regardless of the storage format.
Definition: SampleTrack.h:82
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: SampleTrack.cpp:43
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
void Paste(double t0, const Track *src) override
Definition: WaveTrack.cpp:1367
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:652
double GetRate() const override
Definition: WaveTrack.cpp:360
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}, bool keepLink=true) const
Definition: WaveTrack.cpp:574
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
BuiltinEffectsModule::Registration< EffectPaulstretch > reg
Definition: Paulstretch.cpp:88
STL namespace.
const Type min
Minimum value.
Externalized state of a plug-in.