2012-06-10 16:08:43 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
Audacity: A Digital Audio Editor
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
Paulstretch.cpp
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
Nasca Octavian Paul (Paul Nasca)
|
|
|
|
Some GUI code was taken from the Echo effect
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
*******************************************************************//**
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
\class EffectPaulstretch
|
|
|
|
\brief An Extreme Time Stretch and Time Smear effect
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
*//*******************************************************************/
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
#include "../Audacity.h"
|
2015-06-18 14:24:36 +00:00
|
|
|
#include "Paulstretch.h"
|
2019-01-17 23:31:08 +00:00
|
|
|
#include "LoadEffects.h"
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-01-21 15:41:03 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
#include <math.h>
|
|
|
|
#include <float.h>
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
#include <wx/intl.h>
|
|
|
|
#include <wx/valgen.h>
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2019-02-06 18:44:52 +00:00
|
|
|
#include "../Shuttle.h"
|
2015-06-18 14:24:36 +00:00
|
|
|
#include "../ShuttleGui.h"
|
2015-04-17 03:53:42 +00:00
|
|
|
#include "../FFT.h"
|
|
|
|
#include "../widgets/valnum.h"
|
2019-05-20 18:27:11 +00:00
|
|
|
#include "../widgets/AudacityMessageBox.h"
|
2016-01-02 09:00:40 +00:00
|
|
|
#include "../Prefs.h"
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-07-03 04:20:21 +00:00
|
|
|
#include "../WaveTrack.h"
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
// Define keys, defaults, minimums, and maximums for the effect parameters
|
|
|
|
//
|
2015-04-19 03:49:05 +00:00
|
|
|
// Name Type Key Def Min Max Scale
|
2017-10-03 22:07:04 +00:00
|
|
|
Param( Amount, float, wxT("Stretch Factor"), 10.0, 1.0, FLT_MAX, 1 );
|
|
|
|
Param( Time, float, wxT("Time Resolution"), 0.25f, 0.00099f, FLT_MAX, 1 );
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2018-11-04 14:19:05 +00:00
|
|
|
/// \brief Class that helps EffectPaulStretch. It does the FFTs and inner loop
|
|
|
|
/// of the effect.
|
2015-04-17 03:53:42 +00:00
|
|
|
class PaulStretch
|
|
|
|
{
|
|
|
|
public:
|
2016-09-08 12:55:58 +00:00
|
|
|
PaulStretch(float rap_, size_t in_bufsize_, float samplerate_);
|
2015-04-17 03:53:42 +00:00
|
|
|
//in_bufsize is also a half of a FFT buffer (in samples)
|
|
|
|
virtual ~PaulStretch();
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
void process(float *smps, size_t nsmps);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
size_t get_nsamples();//how many samples are required to be added in the pool next time
|
|
|
|
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)
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
private:
|
|
|
|
void process_spectrum(float *WXUNUSED(freq)) {};
|
2015-04-17 03:53:42 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
const float samplerate;
|
|
|
|
const float rap;
|
|
|
|
const size_t in_bufsize;
|
2015-04-17 03:53:42 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
public:
|
|
|
|
const size_t out_bufsize;
|
2016-04-14 16:35:15 +00:00
|
|
|
const Floats out_buf;
|
2015-04-17 03:53:42 +00:00
|
|
|
|
|
|
|
private:
|
2016-04-14 16:35:15 +00:00
|
|
|
const Floats old_out_smp_buf;
|
2016-09-08 12:55:58 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
const size_t poolsize;//how many samples are inside the input_pool size (need to know how many samples to fill when seeking)
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
private:
|
2016-04-14 16:35:15 +00:00
|
|
|
const Floats in_pool;//de marimea in_bufsize
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
double remained_samples;//how many fraction of samples has remained (0..1)
|
2016-09-08 12:55:58 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
const Floats fft_smps, fft_c, fft_s, fft_freq, fft_tmp;
|
2012-06-10 16:08:43 +00:00
|
|
|
};
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
//
|
|
|
|
// EffectPaulstretch
|
|
|
|
//
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2019-01-17 22:33:49 +00:00
|
|
|
const ComponentInterfaceSymbol EffectPaulstretch::Symbol
|
|
|
|
{ XO("Paulstretch") };
|
|
|
|
|
2019-01-17 23:31:08 +00:00
|
|
|
namespace{ BuiltinEffectsModule::Registration< EffectPaulstretch > reg; }
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
BEGIN_EVENT_TABLE(EffectPaulstretch, wxEvtHandler)
|
|
|
|
EVT_TEXT(wxID_ANY, EffectPaulstretch::OnText)
|
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
|
|
EffectPaulstretch::EffectPaulstretch()
|
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
mAmount = DEF_Amount;
|
|
|
|
mTime_resolution = DEF_Time;
|
2015-05-15 11:47:51 +00:00
|
|
|
|
|
|
|
SetLinearEffectFlag(true);
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EffectPaulstretch::~EffectPaulstretch()
|
|
|
|
{
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2018-11-02 15:31:44 +00:00
|
|
|
// ComponentInterface implementation
|
2015-04-17 03:53:42 +00:00
|
|
|
|
2018-11-02 15:31:44 +00:00
|
|
|
ComponentInterfaceSymbol EffectPaulstretch::GetSymbol()
|
2015-04-17 03:53:42 +00:00
|
|
|
{
|
2019-01-17 22:33:49 +00:00
|
|
|
return Symbol;
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
|
2019-12-08 18:53:48 +00:00
|
|
|
TranslatableString EffectPaulstretch::GetDescription()
|
2015-04-17 03:53:42 +00:00
|
|
|
{
|
2019-12-08 18:53:48 +00:00
|
|
|
return XO("Paulstretch is only for an extreme time-stretch or \"stasis\" effect");
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
|
2017-05-20 13:40:09 +00:00
|
|
|
wxString EffectPaulstretch::ManualPage()
|
|
|
|
{
|
|
|
|
return wxT("Paulstretch");
|
|
|
|
}
|
|
|
|
|
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
|
|
|
// EffectDefinitionInterface implementation
|
2015-04-17 03:53:42 +00:00
|
|
|
|
|
|
|
EffectType EffectPaulstretch::GetType()
|
|
|
|
{
|
|
|
|
return EffectTypeProcess;
|
|
|
|
}
|
|
|
|
|
|
|
|
// EffectClientInterface implementation
|
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
|
|
|
bool EffectPaulstretch::DefineParams( ShuttleParams & S ){
|
|
|
|
S.SHUTTLE_PARAM( mAmount, Amount );
|
|
|
|
S.SHUTTLE_PARAM( mTime_resolution, Time );
|
|
|
|
return true;
|
|
|
|
}
|
2015-04-17 03:53:42 +00:00
|
|
|
|
2018-02-21 14:24:25 +00:00
|
|
|
bool EffectPaulstretch::GetAutomationParameters(CommandParameters & parms)
|
2015-04-17 03:53:42 +00:00
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
parms.WriteFloat(KEY_Amount, mAmount);
|
|
|
|
parms.WriteFloat(KEY_Time, mTime_resolution);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return true;
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 14:24:25 +00:00
|
|
|
bool EffectPaulstretch::SetAutomationParameters(CommandParameters & parms)
|
2015-04-17 03:53:42 +00:00
|
|
|
{
|
|
|
|
ReadAndVerifyFloat(Amount);
|
|
|
|
ReadAndVerifyFloat(Time);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
mAmount = Amount;
|
|
|
|
mTime_resolution = Time;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return true;
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
// Effect implementation
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
double EffectPaulstretch::CalcPreviewInputLength(double previewLength)
|
|
|
|
{
|
|
|
|
// FIXME: Preview is currently at the project rate, but should really be
|
|
|
|
// at the track rate (bugs 1284 and 852).
|
2016-09-08 12:55:58 +00:00
|
|
|
auto minDuration = GetBufferSize(mProjectRate) * 2 + 1;
|
2016-01-02 09:00:40 +00:00
|
|
|
|
|
|
|
// Preview playback may need to be trimmed but this is the smallest selection that we can use.
|
|
|
|
double minLength = std::max<double>(minDuration / mProjectRate, previewLength / mAmount);
|
|
|
|
|
|
|
|
return minLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::Process()
|
|
|
|
{
|
2014-06-03 20:30:19 +00:00
|
|
|
CopyInputTracks();
|
2012-06-14 22:46:55 +00:00
|
|
|
m_t1=mT1;
|
2012-06-10 16:08:43 +00:00
|
|
|
int count=0;
|
2018-09-11 17:07:32 +00:00
|
|
|
for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
|
2012-06-10 16:08:43 +00:00
|
|
|
double trackStart = track->GetStartTime();
|
|
|
|
double trackEnd = track->GetEndTime();
|
|
|
|
double t0 = mT0 < trackStart? trackStart: mT0;
|
|
|
|
double t1 = mT1 > trackEnd? trackEnd: mT1;
|
|
|
|
|
|
|
|
if (t1 > t0) {
|
|
|
|
if (!ProcessOne(track, t0,t1,count))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
mT1=m_t1;
|
|
|
|
|
|
|
|
ReplaceProcessedTracks(true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
void EffectPaulstretch::PopulateOrExchange(ShuttleGui & S)
|
|
|
|
{
|
|
|
|
S.StartMultiColumn(2, wxALIGN_CENTER);
|
|
|
|
{
|
2017-10-30 16:23:41 +00:00
|
|
|
S.Validator<FloatingPointValidator<float>>(
|
|
|
|
1, &mAmount, NumValidatorStyle::DEFAULT, MIN_Amount)
|
|
|
|
/* i18n-hint: This is how many times longer the sound will be, e.g. applying
|
|
|
|
* the effect to a 1-second sample, with the default Stretch Factor of 10.0
|
|
|
|
* will give an (approximately) 10 second sound
|
|
|
|
*/
|
2020-03-27 10:41:30 +00:00
|
|
|
.AddTextBox(XO("&Stretch Factor:"), wxT(""), 10);
|
2017-10-30 16:23:41 +00:00
|
|
|
|
|
|
|
S.Validator<FloatingPointValidator<float>>(
|
|
|
|
3, &mTime_resolution, NumValidatorStyle::ONE_TRAILING_ZERO, MIN_Time)
|
2020-03-27 10:41:30 +00:00
|
|
|
.AddTextBox(XO("&Time Resolution (seconds):"), wxT(""), 10);
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
S.EndMultiColumn();
|
|
|
|
};
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::TransferDataToWindow()
|
|
|
|
{
|
|
|
|
if (!mUIParent->TransferDataToWindow())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::TransferDataFromWindow()
|
|
|
|
{
|
|
|
|
if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
// EffectPaulstretch implementation
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
void EffectPaulstretch::OnText(wxCommandEvent & WXUNUSED(evt))
|
|
|
|
{
|
|
|
|
EnableApply(mUIParent->TransferDataFromWindow());
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-08-28 15:25:53 +00:00
|
|
|
size_t EffectPaulstretch::GetBufferSize(double rate)
|
2016-01-02 09:00:40 +00:00
|
|
|
{
|
|
|
|
// Audacity's fft requires a power of 2
|
|
|
|
float tmp = rate * mTime_resolution / 2.0;
|
|
|
|
tmp = log(tmp) / log(2.0);
|
|
|
|
tmp = pow(2.0, floor(tmp + 0.5));
|
|
|
|
|
2016-08-28 15:25:53 +00:00
|
|
|
auto stmp = size_t(tmp);
|
|
|
|
if (stmp != tmp)
|
|
|
|
// overflow
|
|
|
|
return 0;
|
|
|
|
if (stmp >= 2 * stmp)
|
|
|
|
// overflow
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return std::max<size_t>(stmp, 128);
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count)
|
|
|
|
{
|
2019-12-19 19:19:51 +00:00
|
|
|
const auto badAllocMessage =
|
|
|
|
XO("Requested value exceeds memory capacity.");
|
2016-08-28 15:25:53 +00:00
|
|
|
|
|
|
|
const auto stretch_buf_size = GetBufferSize(track->GetRate());
|
|
|
|
if (stretch_buf_size == 0) {
|
2017-09-10 14:42:33 +00:00
|
|
|
::Effect::MessageBox( badAllocMessage );
|
2016-08-28 15:25:53 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
double amount = this->mAmount;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-08-24 15:24:26 +00:00
|
|
|
auto start = track->TimeToLongSamples(t0);
|
|
|
|
auto end = track->TimeToLongSamples(t1);
|
|
|
|
auto len = end - start;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-08-28 15:25:53 +00:00
|
|
|
const auto minDuration = stretch_buf_size * 2 + 1;
|
|
|
|
if (minDuration < stretch_buf_size) {
|
|
|
|
// overflow!
|
2017-09-10 14:42:33 +00:00
|
|
|
::Effect::MessageBox( badAllocMessage );
|
2016-08-28 15:25:53 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
if (len < minDuration) { //error because the selection is too short
|
2016-01-02 09:00:40 +00:00
|
|
|
|
2016-08-25 12:53:59 +00:00
|
|
|
float maxTimeRes = log( len.as_double() ) / log(2.0);
|
2016-01-02 09:00:40 +00:00
|
|
|
maxTimeRes = pow(2.0, floor(maxTimeRes) + 0.5);
|
|
|
|
maxTimeRes = maxTimeRes / track->GetRate();
|
|
|
|
|
|
|
|
if (this->IsPreviewing()) {
|
|
|
|
double defaultPreviewLen;
|
|
|
|
gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &defaultPreviewLen, 6.0);
|
|
|
|
|
|
|
|
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
|
|
|
if ((minDuration / mProjectRate) < defaultPreviewLen) {
|
2019-12-19 19:19:51 +00:00
|
|
|
::Effect::MessageBox(
|
|
|
|
XO("Audio selection too short to preview.\n\n"
|
|
|
|
"Try increasing the audio selection to at least %.1f seconds,\n"
|
|
|
|
"or reducing the 'Time Resolution' to less than %.1f seconds.")
|
|
|
|
.Format(
|
|
|
|
(minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
|
|
|
|
floor(maxTimeRes * 10.0) / 10.0),
|
|
|
|
wxOK | wxICON_EXCLAMATION );
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
2019-12-19 19:19:51 +00:00
|
|
|
::Effect::MessageBox(
|
|
|
|
XO("Unable to Preview.\n\n"
|
|
|
|
"For the current audio selection, the maximum\n"
|
|
|
|
"'Time Resolution' is %.1f seconds.")
|
|
|
|
.Format( floor(maxTimeRes * 10.0) / 10.0 ),
|
|
|
|
wxOK | wxICON_EXCLAMATION );
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
2019-12-19 19:19:51 +00:00
|
|
|
::Effect::MessageBox(
|
|
|
|
XO("The 'Time Resolution' is too long for the selection.\n\n"
|
|
|
|
"Try increasing the audio selection to at least %.1f seconds,\n"
|
|
|
|
"or reducing the 'Time Resolution' to less than %.1f seconds.")
|
|
|
|
.Format(
|
|
|
|
(minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
|
|
|
|
floor(maxTimeRes * 10.0) / 10.0),
|
|
|
|
wxOK | wxICON_EXCLAMATION );
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return false;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
|
2016-08-25 12:53:59 +00:00
|
|
|
auto dlen = len.as_double();
|
|
|
|
double adjust_amount = dlen /
|
|
|
|
(dlen - ((double)stretch_buf_size * 2.0));
|
2016-09-08 12:55:58 +00:00
|
|
|
amount = 1.0 + (amount - 1.0) * adjust_amount;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
Bug2346: Complete fix...
... without making undesirable dependency cycles.
Eliminate calls to NewWaveTrack in effects, but in Edit>Copy too, which was
not mentioned in the bug report. (Copying a track, deselecting all, and pasting
preserved CLIP colors, but not the TRACK color setting which applies to newly
generated clips.)
Instead, always use the new function WaveTrack::EmptyCopy from the track to be
later replaced, getting color information.
NewWaveTrack is still used in benchmark test, import, the Track menu
commands that make new tracks, recording to new tracks, and generators without
a selection, where there is no track to copy from.
Also when deserializing tracks from the .aup file, in which case the saved
color is later retrieved from the file.
Also, in mix-and-render, where other logic decides whether to copy colors
afterward.
See commit a9658e6ef7f7eaefce4dc37a93d389cca7705f41
2020-03-11 01:40:14 +00:00
|
|
|
auto outputTrack = track->EmptyCopy();
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-08-28 15:25:53 +00:00
|
|
|
try {
|
|
|
|
// This encloses all the allocations of buffers, including those in
|
|
|
|
// the constructor of the PaulStretch object
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
PaulStretch stretch(amount, stretch_buf_size, track->GetRate());
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-08-28 15:25:53 +00:00
|
|
|
auto nget = stretch.get_nsamples_for_fill();
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
auto bufsize = stretch.poolsize;
|
2016-04-14 16:35:15 +00:00
|
|
|
Floats buffer0{ bufsize };
|
|
|
|
float *bufferptr0 = buffer0.get();
|
2016-09-08 12:55:58 +00:00
|
|
|
bool first_time = true;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
const auto fade_len = std::min<size_t>(100, bufsize / 2 - 1);
|
|
|
|
bool cancelled = false;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
{
|
|
|
|
Floats fade_track_smps{ fade_len };
|
|
|
|
decltype(len) s=0;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
while (s < len) {
|
|
|
|
track->Get((samplePtr)bufferptr0, floatSample, start + s, nget);
|
|
|
|
stretch.process(buffer0.get(), nget);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
if (first_time) {
|
|
|
|
stretch.process(buffer0.get(), 0);
|
2016-08-28 15:25:53 +00:00
|
|
|
};
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
s += nget;
|
|
|
|
|
|
|
|
if (first_time){//blend the the start of the selection
|
|
|
|
track->Get((samplePtr)fade_track_smps.get(), floatSample, start, fade_len);
|
|
|
|
first_time = false;
|
|
|
|
for (size_t i = 0; i < fade_len; i++){
|
|
|
|
float fi = (float)i / (float)fade_len;
|
|
|
|
stretch.out_buf[i] =
|
|
|
|
stretch.out_buf[i] * fi + (1.0 - fi) * fade_track_smps[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (s >= len){//blend the end of the selection
|
|
|
|
track->Get((samplePtr)fade_track_smps.get(), floatSample, end - fade_len, fade_len);
|
|
|
|
for (size_t i = 0; i < fade_len; i++){
|
|
|
|
float fi = (float)i / (float)fade_len;
|
|
|
|
auto i2 = bufsize / 2 - 1 - i;
|
|
|
|
stretch.out_buf[i2] =
|
|
|
|
stretch.out_buf[i2] * fi + (1.0 - fi) *
|
|
|
|
fade_track_smps[fade_len - 1 - i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
outputTrack->Append((samplePtr)stretch.out_buf.get(), floatSample, stretch.out_bufsize);
|
|
|
|
|
|
|
|
nget = stretch.get_nsamples();
|
|
|
|
if (TrackProgress(count,
|
|
|
|
s.as_double() / len.as_double()
|
|
|
|
)) {
|
|
|
|
cancelled = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-08-25 12:53:59 +00:00
|
|
|
}
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2017-03-23 15:10:14 +00:00
|
|
|
if (!cancelled){
|
|
|
|
outputTrack->Flush();
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2017-03-23 15:10:14 +00:00
|
|
|
track->Clear(t0,t1);
|
|
|
|
track->Paste(t0, outputTrack.get());
|
2016-08-28 15:25:53 +00:00
|
|
|
m_t1 = mT0 + outputTrack->GetEndTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
return !cancelled;
|
|
|
|
}
|
|
|
|
catch ( const std::bad_alloc& ) {
|
2017-09-10 14:42:33 +00:00
|
|
|
::Effect::MessageBox( badAllocMessage );
|
2016-08-28 15:25:53 +00:00
|
|
|
return false;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*************************************************************/
|
|
|
|
|
|
|
|
|
2016-09-12 11:51:58 +00:00
|
|
|
PaulStretch::PaulStretch(float rap_, size_t in_bufsize_, float samplerate_ )
|
|
|
|
: samplerate { samplerate_ }
|
2016-09-08 12:55:58 +00:00
|
|
|
, rap { std::max(1.0f, rap_) }
|
|
|
|
, in_bufsize { in_bufsize_ }
|
|
|
|
, out_bufsize { std::max(size_t{ 8 }, in_bufsize) }
|
2016-04-14 16:35:15 +00:00
|
|
|
, out_buf { out_bufsize }
|
|
|
|
, old_out_smp_buf { out_bufsize * 2, true }
|
2016-09-08 12:55:58 +00:00
|
|
|
, poolsize { in_bufsize_ * 2 }
|
2016-04-14 16:35:15 +00:00
|
|
|
, in_pool { poolsize, true }
|
2016-09-08 12:55:58 +00:00
|
|
|
, remained_samples { 0.0 }
|
2016-04-14 16:35:15 +00:00
|
|
|
, fft_smps { poolsize, true }
|
|
|
|
, fft_c { poolsize, true }
|
2018-02-15 14:16:30 +00:00
|
|
|
, fft_s { poolsize, true }
|
2016-04-14 16:35:15 +00:00
|
|
|
, fft_freq { poolsize, true }
|
|
|
|
, fft_tmp { poolsize }
|
2015-04-17 03:53:42 +00:00
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
PaulStretch::~PaulStretch()
|
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
void PaulStretch::process(float *smps, size_t nsmps)
|
2015-04-17 03:53:42 +00:00
|
|
|
{
|
2016-02-13 15:43:16 +00:00
|
|
|
//add NEW samples to the pool
|
2016-09-08 12:55:58 +00:00
|
|
|
if ((smps != NULL) && (nsmps != 0)) {
|
|
|
|
if (nsmps > poolsize) {
|
|
|
|
nsmps = poolsize;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2016-09-08 12:55:58 +00:00
|
|
|
int nleft = poolsize - nsmps;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
//move left the samples from the pool to make room for NEW samples
|
2016-09-08 12:55:58 +00:00
|
|
|
for (int i = 0; i < nleft; i++)
|
|
|
|
in_pool[i] = in_pool[i + nsmps];
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
//add NEW samples to the pool
|
2016-04-14 16:35:15 +00:00
|
|
|
for (size_t i = 0; i < nsmps; i++)
|
2016-09-08 12:55:58 +00:00
|
|
|
in_pool[i + nleft] = smps[i];
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
//get the samples from the pool
|
2016-09-08 12:55:58 +00:00
|
|
|
for (size_t i = 0; i < poolsize; i++)
|
|
|
|
fft_smps[i] = in_pool[i];
|
2016-04-14 16:35:15 +00:00
|
|
|
WindowFunc(eWinFuncHanning, poolsize, fft_smps.get());
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
RealFFT(poolsize, fft_smps.get(), fft_c.get(), fft_s.get());
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
for (size_t i = 0; i < poolsize / 2; i++)
|
|
|
|
fft_freq[i] = sqrt(fft_c[i] * fft_c[i] + fft_s[i] * fft_s[i]);
|
2016-04-14 16:35:15 +00:00
|
|
|
process_spectrum(fft_freq.get());
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
//put randomize phases to frequencies and do a IFFT
|
2016-09-08 12:55:58 +00:00
|
|
|
float inv_2p15_2pi = 1.0 / 16384.0 * (float)M_PI;
|
|
|
|
for (size_t i = 1; i < poolsize / 2; i++) {
|
|
|
|
unsigned int random = (rand()) & 0x7fff;
|
|
|
|
float phase = random * inv_2p15_2pi;
|
|
|
|
float s = fft_freq[i] * sin(phase);
|
|
|
|
float c = fft_freq[i] * cos(phase);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
fft_c[i] = fft_c[poolsize - i] = c;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
fft_s[i] = s; fft_s[poolsize - i] = -s;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2016-09-08 12:55:58 +00:00
|
|
|
fft_c[0] = fft_s[0] = 0.0;
|
|
|
|
fft_c[poolsize / 2] = fft_s[poolsize / 2] = 0.0;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-04-14 16:35:15 +00:00
|
|
|
FFT(poolsize, true, fft_c.get(), fft_s.get(), fft_smps.get(), fft_tmp.get());
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
float max = 0.0, max2 = 0.0;
|
|
|
|
for (size_t i = 0; i < poolsize; i++) {
|
|
|
|
max = std::max(max, fabsf(fft_tmp[i]));
|
|
|
|
max2 = std::max(max2, fabsf(fft_smps[i]));
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
//make the output buffer
|
2016-09-08 12:55:58 +00:00
|
|
|
float tmp = 1.0 / (float) out_bufsize * M_PI;
|
|
|
|
float hinv_sqrt2 = 0.853553390593f;//(1.0+1.0/sqrt(2))*0.5;
|
|
|
|
|
|
|
|
float ampfactor = 1.0;
|
|
|
|
if (rap < 1.0)
|
|
|
|
ampfactor = rap * 0.707;
|
|
|
|
else
|
|
|
|
ampfactor = (out_bufsize / (float)poolsize) * 4.0;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < out_bufsize; i++) {
|
|
|
|
float a = (0.5 + 0.5 * cos(i * tmp));
|
|
|
|
float out = fft_smps[i + out_bufsize] * (1.0 - a) + old_out_smp_buf[i] * a;
|
|
|
|
out_buf[i] =
|
|
|
|
out * (hinv_sqrt2 - (1.0 - hinv_sqrt2) * cos(i * 2.0 * tmp)) *
|
|
|
|
ampfactor;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
//copy the current output buffer to old buffer
|
2016-09-08 12:55:58 +00:00
|
|
|
for (size_t i = 0; i < out_bufsize * 2; i++)
|
|
|
|
old_out_smp_buf[i] = fft_smps[i];
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
size_t PaulStretch::get_nsamples()
|
2015-04-17 03:53:42 +00:00
|
|
|
{
|
2016-09-08 12:55:58 +00:00
|
|
|
double r = out_bufsize / rap;
|
|
|
|
auto ri = (size_t)floor(r);
|
|
|
|
double rf = r - floor(r);
|
|
|
|
|
|
|
|
remained_samples += rf;
|
|
|
|
if (remained_samples >= 1.0){
|
|
|
|
ri += (size_t)floor(remained_samples);
|
|
|
|
remained_samples = remained_samples - floor(remained_samples);
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
if (ri > poolsize) {
|
|
|
|
ri = poolsize;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return ri;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-09-08 12:55:58 +00:00
|
|
|
size_t PaulStretch::get_nsamples_for_fill()
|
2013-06-21 22:10:50 +00:00
|
|
|
{
|
2015-04-17 03:53:42 +00:00
|
|
|
return poolsize;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|