Fix effect preview issues
Fixes bug 1274 and unblocks bug 995.
This commit is contained in:
parent
33477fd5d1
commit
46055cde25
|
@ -2431,11 +2431,6 @@ bool Effect::IsHidden()
|
|||
|
||||
void Effect::Preview(bool dryOnly)
|
||||
{
|
||||
if (mIsLinearEffect)
|
||||
wxLogDebug(wxT("Linear Effect"));
|
||||
else
|
||||
wxLogDebug(wxT("Non-linear Effect"));
|
||||
|
||||
if (mNumTracks == 0) { // nothing to preview
|
||||
return;
|
||||
}
|
||||
|
@ -2553,8 +2548,9 @@ void Effect::Preview(bool dryOnly)
|
|||
playbackTracks.Add(src);
|
||||
src = (WaveTrack *) iter.Next();
|
||||
}
|
||||
if (isNyquist && isGenerator)
|
||||
t1 = mT1;
|
||||
// Some effects (Paulstretch) may need to generate more
|
||||
// than previewLen, so take the min.
|
||||
t1 = std::min(mT0 + previewLen, mT1);
|
||||
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
NoteTrackArray empty;
|
||||
|
@ -2569,7 +2565,6 @@ void Effect::Preview(bool dryOnly)
|
|||
|
||||
if (token) {
|
||||
int previewing = eProgressSuccess;
|
||||
wxLogDebug(wxT("mT0 %.3f t1 %.3f"),mT0,t1);
|
||||
// The progress dialog must be deleted before stopping the stream
|
||||
// to allow events to flow to the app during StopStream processing.
|
||||
// The progress dialog blocks these events.
|
||||
|
|
|
@ -334,6 +334,9 @@ protected:
|
|||
// (such as fade effects) need to know the full selection length.
|
||||
void SetPreviewFullSelectionFlag(bool previewDurationFlag);
|
||||
|
||||
// Use this if the effect needs to know if it is previewing
|
||||
bool IsPreviewing() { return mIsPreview; }
|
||||
|
||||
// Most effects only require selected tracks to be copied for Preview.
|
||||
// If IncludeNotSelectedPreviewTracks(true), then non-linear effects have
|
||||
// preview copies of all wave tracks.
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "../ShuttleGui.h"
|
||||
#include "../FFT.h"
|
||||
#include "../widgets/valnum.h"
|
||||
#include "../Prefs.h"
|
||||
|
||||
#include "../WaveTrack.h"
|
||||
|
||||
|
@ -79,8 +80,8 @@ END_EVENT_TABLE()
|
|||
|
||||
EffectPaulstretch::EffectPaulstretch()
|
||||
{
|
||||
amount = DEF_Amount;
|
||||
time_resolution = DEF_Time;
|
||||
mAmount = DEF_Amount;
|
||||
mTime_resolution = DEF_Time;
|
||||
|
||||
SetLinearEffectFlag(true);
|
||||
}
|
||||
|
@ -112,8 +113,8 @@ EffectType EffectPaulstretch::GetType()
|
|||
|
||||
bool EffectPaulstretch::GetAutomationParameters(EffectAutomationParameters & parms)
|
||||
{
|
||||
parms.WriteFloat(KEY_Amount, amount);
|
||||
parms.WriteFloat(KEY_Time, time_resolution);
|
||||
parms.WriteFloat(KEY_Amount, mAmount);
|
||||
parms.WriteFloat(KEY_Time, mTime_resolution);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -123,14 +124,27 @@ bool EffectPaulstretch::SetAutomationParameters(EffectAutomationParameters & par
|
|||
ReadAndVerifyFloat(Amount);
|
||||
ReadAndVerifyFloat(Time);
|
||||
|
||||
amount = Amount;
|
||||
time_resolution = Time;
|
||||
mAmount = Amount;
|
||||
mTime_resolution = Time;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Effect implementation
|
||||
|
||||
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).
|
||||
int minDuration = GetBufferSize(mProjectRate) * 2 + 1;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
bool EffectPaulstretch::Process()
|
||||
{
|
||||
CopyInputTracks();
|
||||
|
@ -159,11 +173,12 @@ bool EffectPaulstretch::Process()
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void EffectPaulstretch::PopulateOrExchange(ShuttleGui & S)
|
||||
{
|
||||
S.StartMultiColumn(2, wxALIGN_CENTER);
|
||||
{
|
||||
FloatingPointValidator<float> vldAmount(1, &amount);
|
||||
FloatingPointValidator<float> vldAmount(1, &mAmount);
|
||||
vldAmount.SetMin(MIN_Amount);
|
||||
|
||||
/* i18n-hint: This is how many times longer the sound will be, e.g. applying
|
||||
|
@ -172,7 +187,7 @@ void EffectPaulstretch::PopulateOrExchange(ShuttleGui & S)
|
|||
*/
|
||||
S.AddTextBox(_("Stretch Factor:"), wxT(""), 10)->SetValidator(vldAmount);
|
||||
|
||||
FloatingPointValidator<float> vldTime(3, &time_resolution, NUM_VAL_ONE_TRAILING_ZERO);
|
||||
FloatingPointValidator<float> vldTime(3, &mTime_resolution, NUM_VAL_ONE_TRAILING_ZERO);
|
||||
vldTime.SetMin(MIN_Time);
|
||||
S.AddTextBox(_("Time Resolution (seconds):"), wxT(""), 10)->SetValidator(vldTime);
|
||||
}
|
||||
|
@ -206,33 +221,66 @@ void EffectPaulstretch::OnText(wxCommandEvent & WXUNUSED(evt))
|
|||
EnableApply(mUIParent->TransferDataFromWindow());
|
||||
}
|
||||
|
||||
int EffectPaulstretch::GetBufferSize(double rate)
|
||||
{
|
||||
// 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));
|
||||
|
||||
return std::max<int>((int)tmp, 128);
|
||||
}
|
||||
|
||||
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count)
|
||||
{
|
||||
int stretch_buf_size;//must be power of 2 (because Audacity's fft requires it)
|
||||
if (time_resolution<MIN_Time) time_resolution=MIN_Time;
|
||||
{
|
||||
float tmp=track->GetRate()*time_resolution*0.5;
|
||||
tmp=log(tmp)/log(2.0);
|
||||
tmp=pow(2.0,floor(tmp+0.5));
|
||||
stretch_buf_size=(int)tmp;
|
||||
};
|
||||
if (stretch_buf_size<128) stretch_buf_size=128;
|
||||
double amount=this->amount;
|
||||
if (amount<MIN_Amount) amount=MIN_Amount;
|
||||
int stretch_buf_size = GetBufferSize(track->GetRate());
|
||||
double amount = this->mAmount;
|
||||
|
||||
sampleCount start = track->TimeToLongSamples(t0);
|
||||
sampleCount end = track->TimeToLongSamples(t1);
|
||||
sampleCount len = (sampleCount)(end - start);
|
||||
|
||||
m_t1=mT1;
|
||||
int minDuration = stretch_buf_size * 2 + 1;
|
||||
if (len < minDuration){ //error because the selection is too short
|
||||
|
||||
float maxTimeRes = log(len) / log(2.0);
|
||||
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) {
|
||||
::wxMessageBox (wxString::Format(_("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."),
|
||||
(minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
|
||||
floor(maxTimeRes * 10.0) / 10.0),
|
||||
GetName(), wxOK | wxICON_EXCLAMATION);
|
||||
}
|
||||
else {
|
||||
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
||||
::wxMessageBox (wxString::Format(_("Unable to Preview.\n\n"
|
||||
"For the current audio selection, the maximum\n"
|
||||
"'Time Resolution' is %.1f seconds."),
|
||||
floor(maxTimeRes * 10.0) / 10.0),
|
||||
GetName(), wxOK | wxICON_EXCLAMATION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
||||
::wxMessageBox (wxString::Format(_("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."),
|
||||
(minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
|
||||
floor(maxTimeRes * 10.0) / 10.0),
|
||||
GetName(), wxOK | wxICON_EXCLAMATION);
|
||||
}
|
||||
|
||||
if (len<=(stretch_buf_size*2+1)){//error because the selection is too short
|
||||
/* i18n-hint: This is an effect error message, for the effect named Paulstretch.
|
||||
* Time Resolution is a parameter of the effect, the translation should match
|
||||
*/
|
||||
::wxMessageBox(_("Error in Paulstretch:\nThe selection is too short.\n It must be much longer than the Time Resolution."));
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
double adjust_amount=(double)len/((double)len-((double)stretch_buf_size*2.0));
|
||||
|
@ -296,15 +344,11 @@ bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int coun
|
|||
delete [] fade_track_smps;
|
||||
outputTrack->Flush();
|
||||
|
||||
|
||||
|
||||
track->Clear(t0,t1);
|
||||
track->Paste(t0,outputTrack);
|
||||
if (!cancelled){
|
||||
double flen=t1-t0;
|
||||
if (s>0) m_t1=t0+flen*(double)outs/(double)(s);
|
||||
};
|
||||
|
||||
bool success = track->Paste(t0,outputTrack);
|
||||
if (!cancelled && success){
|
||||
m_t1 = mT0 + outputTrack->GetEndTime();
|
||||
}
|
||||
|
||||
delete stretch;
|
||||
delete []buffer0;
|
||||
|
@ -343,9 +387,8 @@ PaulStretch::PaulStretch(float rap_,int in_bufsize_,float samplerate_)
|
|||
fft_c[i]=0.0;
|
||||
fft_s[i]=0.0;
|
||||
fft_freq[i]=0.0;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
PaulStretch::~PaulStretch()
|
||||
{
|
||||
|
@ -357,13 +400,13 @@ PaulStretch::~PaulStretch()
|
|||
delete [] fft_s;
|
||||
delete [] fft_freq;
|
||||
delete [] fft_tmp;
|
||||
};
|
||||
}
|
||||
|
||||
void PaulStretch::set_rap(float newrap)
|
||||
{
|
||||
if (rap>=1.0) rap=newrap;
|
||||
else rap=1.0;
|
||||
};
|
||||
}
|
||||
|
||||
void PaulStretch::process(float *smps,int nsmps)
|
||||
{
|
||||
|
@ -371,7 +414,7 @@ void PaulStretch::process(float *smps,int nsmps)
|
|||
if ((smps!=NULL)&&(nsmps!=0)){
|
||||
if (nsmps>poolsize){
|
||||
nsmps=poolsize;
|
||||
};
|
||||
}
|
||||
int nleft=poolsize-nsmps;
|
||||
|
||||
//move left the samples from the pool to make room for new samples
|
||||
|
@ -379,7 +422,7 @@ void PaulStretch::process(float *smps,int nsmps)
|
|||
|
||||
//add new samples to the pool
|
||||
for (int i=0;i<nsmps;i++) in_pool[i+nleft]=smps[i];
|
||||
};
|
||||
}
|
||||
|
||||
//get the samples from the pool
|
||||
for (int i=0;i<poolsize;i++) fft_smps[i]=in_pool[i];
|
||||
|
@ -399,11 +442,10 @@ void PaulStretch::process(float *smps,int nsmps)
|
|||
float s=fft_freq[i]*sin(phase);
|
||||
float c=fft_freq[i]*cos(phase);
|
||||
|
||||
|
||||
fft_c[i]=fft_c[poolsize-i]=c;
|
||||
|
||||
fft_s[i]=s;fft_s[poolsize-i]=-s;
|
||||
};
|
||||
}
|
||||
fft_c[0]=fft_s[0]=0.0;
|
||||
fft_c[poolsize/2]=fft_s[poolsize/2]=0.0;
|
||||
|
||||
|
@ -415,7 +457,7 @@ void PaulStretch::process(float *smps,int nsmps)
|
|||
if (a>max) max=a;
|
||||
float b=fabs(fft_smps[i]);
|
||||
if (b>max2) max2=b;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//make the output buffer
|
||||
|
@ -430,12 +472,11 @@ void PaulStretch::process(float *smps,int nsmps)
|
|||
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;
|
||||
};
|
||||
}
|
||||
|
||||
//copy the current output buffer to old buffer
|
||||
for (int i=0;i<out_bufsize*2;i++) old_out_smp_buf[i]=fft_smps[i];
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
int PaulStretch::get_nsamples()
|
||||
{
|
||||
|
@ -447,16 +488,16 @@ int PaulStretch::get_nsamples()
|
|||
if (remained_samples>=1.0){
|
||||
ri+=(int)floor(remained_samples);
|
||||
remained_samples=remained_samples-floor(remained_samples);
|
||||
};
|
||||
}
|
||||
|
||||
if (ri>poolsize){
|
||||
ri=poolsize;
|
||||
};
|
||||
}
|
||||
|
||||
return ri;
|
||||
};
|
||||
}
|
||||
|
||||
int PaulStretch::get_nsamples_for_fill()
|
||||
{
|
||||
return poolsize;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
|
||||
// Effect implementation
|
||||
|
||||
virtual double CalcPreviewInputLength(double previewLength);
|
||||
virtual bool Process();
|
||||
virtual void PopulateOrExchange(ShuttleGui & S);
|
||||
virtual bool TransferDataToWindow();
|
||||
|
@ -49,12 +50,13 @@ private:
|
|||
// EffectPaulstretch implementation
|
||||
|
||||
void OnText(wxCommandEvent & evt);
|
||||
int GetBufferSize(double rate);
|
||||
|
||||
bool ProcessOne(WaveTrack *track, double t0, double t1, int count);
|
||||
|
||||
private:
|
||||
float amount;
|
||||
float time_resolution; //seconds
|
||||
float mAmount;
|
||||
float mTime_resolution; //seconds
|
||||
double m_t1;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
|
|
@ -58,6 +58,13 @@ Param( Compress, double, XO("Compress"), 50.0, 0.0, 99.9,
|
|||
|
||||
static const sampleCount DEF_BlendFrameCount = 100;
|
||||
|
||||
// Lower bound on the amount of silence to find at a time -- this avoids
|
||||
// detecting silence repeatedly in low-frequency sounds.
|
||||
static const double DEF_MinTruncMs = 0.001;
|
||||
|
||||
// Typical fraction of total time taken by detection (better to guess low)
|
||||
const double detectFrac = 0.4;
|
||||
|
||||
#include <wx/listimpl.cpp>
|
||||
WX_DEFINE_LIST(RegionList);
|
||||
|
||||
|
@ -154,6 +161,41 @@ bool EffectTruncSilence::SetAutomationParameters(EffectAutomationParameters & pa
|
|||
|
||||
// Effect implementation
|
||||
|
||||
double EffectTruncSilence::CalcPreviewInputLength(double previewLength)
|
||||
{
|
||||
double inputLength = mT1 - mT0;
|
||||
double minInputLength = inputLength;
|
||||
|
||||
// Master list of silent regions
|
||||
RegionList silences;
|
||||
silences.DeleteContents(true);
|
||||
|
||||
// Start with the whole selection silent
|
||||
Region *sel = new Region;
|
||||
sel->start = mT0;
|
||||
sel->end = mT1;
|
||||
silences.push_back(sel);
|
||||
|
||||
SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
|
||||
int whichTrack = 0;
|
||||
|
||||
for (Track *t = iter.First(); t; t = iter.Next()) {
|
||||
WaveTrack *wt = (WaveTrack *)t;
|
||||
|
||||
RegionList trackSilences;
|
||||
trackSilences.DeleteContents(true);
|
||||
|
||||
sampleCount index = wt->TimeToLongSamples(mT0);
|
||||
sampleCount silentFrame = 0; // length of the current silence
|
||||
|
||||
Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack, &inputLength, &minInputLength);
|
||||
|
||||
whichTrack++;
|
||||
}
|
||||
return inputLength;
|
||||
}
|
||||
|
||||
|
||||
bool EffectTruncSilence::Startup()
|
||||
{
|
||||
wxString base = wxT("/Effects/TruncateSilence/");
|
||||
|
@ -206,17 +248,9 @@ bool EffectTruncSilence::Startup()
|
|||
|
||||
bool EffectTruncSilence::Process()
|
||||
{
|
||||
// Typical fraction of total time taken by detection (better to guess low)
|
||||
const double detectFrac = .4;
|
||||
|
||||
// Copy tracks
|
||||
CopyInputTracks(Track::All);
|
||||
|
||||
// Lower bound on the amount of silence to find at a time -- this avoids
|
||||
// detecting silence repeatedly in low-frequency sounds.
|
||||
const double minTruncMs = 0.001;
|
||||
double truncDbSilenceThreshold = Enums::Db2Signal[mTruncDbChoiceIndex];
|
||||
|
||||
// Master list of silent regions; it is responsible for deleting them.
|
||||
// This list should always be kept in order.
|
||||
RegionList silences;
|
||||
|
@ -237,111 +271,19 @@ bool EffectTruncSilence::Process()
|
|||
|
||||
// Smallest silent region to detect in frames
|
||||
sampleCount minSilenceFrames =
|
||||
sampleCount(wxMax( mInitialAllowedSilence, minTruncMs) *
|
||||
wt->GetRate());
|
||||
sampleCount(wxMax( mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
|
||||
|
||||
//
|
||||
// Scan the track for silences
|
||||
//
|
||||
RegionList trackSilences;
|
||||
trackSilences.DeleteContents(true);
|
||||
sampleCount blockLen = wt->GetMaxBlockSize();
|
||||
sampleCount start = wt->TimeToLongSamples(mT0);
|
||||
sampleCount end = wt->TimeToLongSamples(mT1);
|
||||
|
||||
// Allocate buffer
|
||||
float *buffer = new float[blockLen];
|
||||
sampleCount index = wt->TimeToLongSamples(mT0);
|
||||
sampleCount silentFrame = 0;
|
||||
|
||||
sampleCount index = start;
|
||||
sampleCount silentFrames = 0;
|
||||
bool cancelled = false;
|
||||
|
||||
// Keep position in overall silences list for optimization
|
||||
RegionList::iterator rit(silences.begin());
|
||||
|
||||
while (index < end)
|
||||
{
|
||||
// Show progress dialog, test for cancellation
|
||||
cancelled = TotalProgress(
|
||||
detectFrac * (whichTrack + index / (double)end) /
|
||||
(double)GetNumWaveTracks());
|
||||
if (cancelled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Optimization: if not in a silent region skip ahead to the next one
|
||||
//
|
||||
double curTime = wt->LongSamplesToTime(index);
|
||||
for ( ; rit != silences.end(); ++rit)
|
||||
{
|
||||
// Find the first silent region ending after current time
|
||||
if ((*rit)->end >= curTime)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rit == silences.end())
|
||||
{
|
||||
// No more regions -- no need to process the rest of the track
|
||||
break;
|
||||
}
|
||||
else if ((*rit)->start > curTime)
|
||||
{
|
||||
// End current silent region, skip ahead
|
||||
if (silentFrames >= minSilenceFrames)
|
||||
{
|
||||
Region *r = new Region;
|
||||
r->start = wt->LongSamplesToTime(index - silentFrames);
|
||||
r->end = wt->LongSamplesToTime(index);
|
||||
trackSilences.push_back(r);
|
||||
}
|
||||
silentFrames = 0;
|
||||
|
||||
index = wt->TimeToLongSamples((*rit)->start);
|
||||
}
|
||||
//
|
||||
// End of optimization
|
||||
//
|
||||
|
||||
// Limit size of current block if we've reached the end
|
||||
sampleCount count = blockLen;
|
||||
if ((index + count) > end)
|
||||
{
|
||||
count = end - index;
|
||||
}
|
||||
|
||||
// Fill buffer
|
||||
wt->Get((samplePtr)(buffer), floatSample, index, count);
|
||||
|
||||
// Look for silences in current block
|
||||
for (sampleCount i = 0; i < count; ++i)
|
||||
{
|
||||
if (fabs(buffer[i]) < truncDbSilenceThreshold)
|
||||
{
|
||||
++silentFrames;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (silentFrames >= minSilenceFrames)
|
||||
{
|
||||
// Record the silent region
|
||||
Region *r = new Region;
|
||||
r->start = wt->LongSamplesToTime(index + i - silentFrames);
|
||||
r->end = wt->LongSamplesToTime(index + i);
|
||||
trackSilences.push_back(r);
|
||||
}
|
||||
silentFrames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Next block
|
||||
index += count;
|
||||
}
|
||||
|
||||
delete [] buffer;
|
||||
// Detect silences
|
||||
bool cancelled = !(Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack));
|
||||
|
||||
// Buffer has been freed, so we're OK to return if cancelled
|
||||
if (cancelled)
|
||||
|
@ -350,11 +292,11 @@ bool EffectTruncSilence::Process()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (silentFrames >= minSilenceFrames)
|
||||
if (silentFrame >= minSilenceFrames)
|
||||
{
|
||||
// Track ended in silence -- record region
|
||||
Region *r = new Region;
|
||||
r->start = wt->LongSamplesToTime(index - silentFrames);
|
||||
r->start = wt->LongSamplesToTime(index - silentFrame);
|
||||
r->end = wt->LongSamplesToTime(index);
|
||||
trackSilences.push_back(r);
|
||||
}
|
||||
|
@ -480,6 +422,164 @@ bool EffectTruncSilence::Process()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EffectTruncSilence::Analyze(RegionList& silenceList,
|
||||
RegionList& trackSilences,
|
||||
WaveTrack* wt,
|
||||
sampleCount* silentFrame,
|
||||
sampleCount* index,
|
||||
int whichTrack,
|
||||
double* inputLength /*= NULL*/,
|
||||
double* minInputLength /*= NULL*/)
|
||||
{
|
||||
// Smallest silent region to detect in frames
|
||||
sampleCount minSilenceFrames = sampleCount(wxMax( mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
|
||||
|
||||
double truncDbSilenceThreshold = Enums::Db2Signal[mTruncDbChoiceIndex];
|
||||
sampleCount blockLen = wt->GetMaxBlockSize();
|
||||
sampleCount start = wt->TimeToLongSamples(mT0);
|
||||
sampleCount end = wt->TimeToLongSamples(mT1);
|
||||
sampleCount outLength = 0;
|
||||
|
||||
double previewLength;
|
||||
gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLength, 6.0);
|
||||
// Minimum required length in samples.
|
||||
const sampleCount previewLen = previewLength * wt->GetRate();
|
||||
|
||||
// Keep position in overall silences list for optimization
|
||||
RegionList::iterator rit(silenceList.begin());
|
||||
|
||||
// Allocate buffer
|
||||
float *buffer = new float[blockLen];
|
||||
|
||||
// Loop through current track
|
||||
while (*index < end) {
|
||||
if (inputLength && ((outLength >= previewLen) || (*index - start > wt->TimeToLongSamples(*minInputLength)))) {
|
||||
*inputLength = std::min<double>(*inputLength, *minInputLength);
|
||||
if (outLength >= previewLen) {
|
||||
*minInputLength = *inputLength;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!inputLength) {
|
||||
// Show progress dialog, test for cancellation
|
||||
bool cancelled = TotalProgress(
|
||||
detectFrac * (whichTrack + (*index - start) / (double)(end - start)) /
|
||||
(double)GetNumWaveTracks());
|
||||
if (cancelled) {
|
||||
delete [] buffer;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Optimization: if not in a silent region skip ahead to the next one
|
||||
|
||||
double curTime = wt->LongSamplesToTime(*index);
|
||||
for ( ; rit != silenceList.end(); ++rit) {
|
||||
// Find the first silent region ending after current time
|
||||
if ((*rit)->end >= curTime) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rit == silenceList.end()) {
|
||||
// No more regions -- no need to process the rest of the track
|
||||
if (inputLength) {
|
||||
// Add available samples up to previewLength.
|
||||
sampleCount remainingTrackSamples = wt->TimeToLongSamples(wt->GetEndTime()) - *index;
|
||||
sampleCount requiredTrackSamples = previewLen - outLength;
|
||||
outLength += (remainingTrackSamples > requiredTrackSamples)? requiredTrackSamples : remainingTrackSamples;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if ((*rit)->start > curTime) {
|
||||
// End current silent region, skip ahead
|
||||
if (*silentFrame >= minSilenceFrames) {
|
||||
Region *r = new Region;
|
||||
r->start = wt->LongSamplesToTime(*index - *silentFrame);
|
||||
r->end = wt->LongSamplesToTime(*index);
|
||||
trackSilences.push_back(r);
|
||||
}
|
||||
*silentFrame = 0;
|
||||
sampleCount newIndex = wt->TimeToLongSamples((*rit)->start);
|
||||
if (inputLength) {
|
||||
sampleCount requiredTrackSamples = previewLen - outLength;
|
||||
// Add non-silent sample to outLength
|
||||
outLength += ((newIndex - *index) > requiredTrackSamples)? requiredTrackSamples : newIndex - *index;
|
||||
}
|
||||
|
||||
*index = newIndex;
|
||||
}
|
||||
// End of optimization
|
||||
|
||||
// Limit size of current block if we've reached the end
|
||||
sampleCount count = blockLen;
|
||||
if ((*index + count) > end) {
|
||||
count = end - *index;
|
||||
}
|
||||
|
||||
// Fill buffer
|
||||
wt->Get((samplePtr)(buffer), floatSample, *index, count);
|
||||
|
||||
// Look for silenceList in current block
|
||||
for (sampleCount i = 0; i < count; ++i) {
|
||||
if (inputLength && ((outLength >= previewLen) || (outLength > wt->TimeToLongSamples(*minInputLength)))) {
|
||||
*inputLength = wt->LongSamplesToTime(*index + i) - wt->LongSamplesToTime(start);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fabs(buffer[i]) < truncDbSilenceThreshold) {
|
||||
(*silentFrame)++;
|
||||
}
|
||||
else {
|
||||
sampleCount allowed = 0;
|
||||
if (*silentFrame >= minSilenceFrames) {
|
||||
if (inputLength) {
|
||||
switch (mActionIndex) {
|
||||
case kTruncate:
|
||||
outLength += wt->TimeToLongSamples(mTruncLongestAllowedSilence);
|
||||
break;
|
||||
case kCompress:
|
||||
allowed = wt->TimeToLongSamples(mInitialAllowedSilence);
|
||||
outLength += allowed +
|
||||
(*silentFrame - allowed) * mSilenceCompressPercent / 100.0;
|
||||
break;
|
||||
// default: // Not currently used.
|
||||
}
|
||||
}
|
||||
|
||||
// Record the silent region
|
||||
Region *r = new Region;
|
||||
r->start = wt->LongSamplesToTime(*index + i - *silentFrame);
|
||||
r->end = wt->LongSamplesToTime(*index + i);
|
||||
trackSilences.push_back(r);
|
||||
}
|
||||
else if (inputLength) { // included as part of non-silence
|
||||
outLength += *silentFrame;
|
||||
}
|
||||
*silentFrame = 0;
|
||||
if (inputLength) {
|
||||
++outLength; // Add non-silent sample to outLength
|
||||
}
|
||||
}
|
||||
}
|
||||
// Next block
|
||||
*index += count;
|
||||
}
|
||||
delete [] buffer;
|
||||
|
||||
if (inputLength) {
|
||||
*inputLength = std::min<double>(*inputLength, *minInputLength);
|
||||
if (outLength >= previewLen) {
|
||||
*minInputLength = *inputLength;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void EffectTruncSilence::PopulateOrExchange(ShuttleGui & S)
|
||||
{
|
||||
wxASSERT(kNumActions == WXSIZEOF(kActionStrings));
|
||||
|
|
|
@ -57,7 +57,21 @@ public:
|
|||
|
||||
// Effect implementation
|
||||
|
||||
virtual double CalcPreviewInputLength(double previewLength);
|
||||
virtual bool Startup();
|
||||
|
||||
// Analyze a single track to find silences
|
||||
// If inputLength is not NULL we are calculating the minimum
|
||||
// amount of input for previewing.
|
||||
virtual bool Analyze(RegionList &silenceList,
|
||||
RegionList &trackSilences,
|
||||
WaveTrack* wt,
|
||||
sampleCount* silentFrame,
|
||||
sampleCount* index,
|
||||
int whichTrack,
|
||||
double* inputLength = NULL,
|
||||
double* minInputLength = NULL);
|
||||
|
||||
virtual bool Process();
|
||||
virtual void PopulateOrExchange(ShuttleGui & S);
|
||||
virtual bool TransferDataToWindow();
|
||||
|
@ -68,7 +82,7 @@ private:
|
|||
|
||||
//ToDo ... put BlendFrames in Effects, Project, or other class
|
||||
void BlendFrames(float* buffer, int leftIndex, int rightIndex, int blendFrameCount);
|
||||
void Intersect(RegionList &dest, const RegionList &src);
|
||||
void Intersect(RegionList &dest, const RegionList & src);
|
||||
|
||||
void OnControlChange(wxCommandEvent & evt);
|
||||
void UpdateUI();
|
||||
|
|
|
@ -571,7 +571,9 @@ bool NyquistEffect::Process()
|
|||
mProps += wxString::Format(wxT("(putprop '*PROJECT* (float %s) 'PREVIEW-DURATION)\n"),
|
||||
Internat::ToString(previewLen).c_str());
|
||||
|
||||
|
||||
// *PREVIEWP* is true when previewing (better than relying on track view).
|
||||
wxString isPreviewing = (this->IsPreviewing())? wxT("T") : wxT("NIL");
|
||||
mProps += wxString::Format(wxT("(setf *PREVIEWP* %s)\n"), isPreviewing.c_str());
|
||||
|
||||
mProps += wxString::Format(wxT("(putprop '*SELECTION* (float %s) 'START)\n"),
|
||||
Internat::ToString(mT0).c_str());
|
||||
|
|
Loading…
Reference in New Issue