1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 23:59:37 +02:00

Check in new Truncate Silence routine as Experimental

This commit is contained in:
BusinessmanProgrammerSteve 2010-03-13 15:21:38 +00:00
parent 3d76f55cad
commit 6860fca483
3 changed files with 308 additions and 0 deletions

View File

@ -148,6 +148,8 @@ extern void AddPages( AudacityProject * pProj, GuiFactory & Factory, wxNotebo
//#define AUTOMATED_INPUT_LEVEL_ADJUSTMENT
#endif
// AWD: new Truncate Silence code
//#define EXPERIMENTAL_TRUNC_SILENCE
#endif

View File

@ -26,12 +26,19 @@
#include "../Audacity.h"
#include <wx/wx.h>
#include <wx/list.h>
#include <wx/listimpl.cpp>
#include <limits>
#include <math.h>
#include "../Experimental.h"
#include "../Prefs.h"
#include "../Project.h"
#include "../WaveTrack.h"
#include "TruncSilence.h"
WX_DEFINE_LIST(RegionList);
EffectTruncSilence::EffectTruncSilence()
{
Init();
@ -110,6 +117,7 @@ bool EffectTruncSilence::TransferParameters( Shuttle & shuttle )
return true;
}
#ifndef EXPERIMENTAL_TRUNC_SILENCE
#define QUARTER_SECOND_MS 250
bool EffectTruncSilence::Process()
{
@ -442,6 +450,293 @@ bool EffectTruncSilence::Process()
return !cancelled;
}
#else // defined(EXPERIMENTAL_TRUNC_SILENCE)
// AWD: this is the new version!
bool EffectTruncSilence::Process()
{
// Copy tracks
this->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 float minTruncMs = 1.0f;
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;
silences.DeleteContents(true);
// Start with the whole selection silent
Region *sel = new Region;
sel->start = mT0;
sel->end = mT1;
silences.push_back(sel);
// Remove non-silent regions in each track
SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
for (Track *t = iter.First(); t; t = iter.Next())
{
WaveTrack *wt = (WaveTrack *)t;
// Smallest silent region to detect in frames
sampleCount minSilenceFrames =
sampleCount((wxMax( mTruncInitialAllowedSilentMs, minTruncMs) *
wt->GetRate()) / 1000.0);
//
// 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 = start;
sampleCount silentFrames = 0;
while (index < end) {
// 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;
}
if (silentFrames >= minSilenceFrames)
{
// Track ended in silence -- record region
Region *r = new Region;
r->start = wt->LongSamplesToTime(index - silentFrames);
r->end = wt->LongSamplesToTime(index);
trackSilences.push_back(r);
}
// Intersect with the overall silent region list
Intersect(silences, trackSilences);
delete [] buffer;
}
//
// Now remove the silent regions from all selected/sync-seletcted tracks
//
// Loop over detected regions in reverse (so cuts don't change time values
// down the line)
RegionList::reverse_iterator rit;
double totalCutLen = 0.0; // For cutting selection at the end
for (rit = silences.rbegin(); rit != silences.rend(); ++rit) {
Region *r = *rit;
// Intersection may create regions smaller than allowed; ignore them
if (r->end - r->start < mTruncInitialAllowedSilentMs / 1000.0)
continue;
// Find new silence length as requested
double inLength = r->end - r->start;
double outLength = wxMin(
mTruncInitialAllowedSilentMs / 1000.0 + (inLength - mTruncInitialAllowedSilentMs / 1000.0) / mSilenceCompressRatio,
mTruncLongestAllowedSilentMs);
double cutLen = inLength - outLength;
totalCutLen += cutLen;
TrackListIterator iterOut(mOutputTracks);
for (Track *t = iterOut.First(); t; t = iterOut.Next())
{
if (t->GetKind() == Track::Wave && (
t->GetSelected() || t->IsSynchroSelected()))
{
// In WaveTracks, clear with a cross-fade
WaveTrack *wt = (WaveTrack *)t;
sampleCount blendFrames = mBlendFrameCount;
double cutStart = (r->start + r->end - cutLen) / 2;
double cutEnd = cutStart + cutLen;
// Round start/end times to frame boundaries
cutStart = wt->LongSamplesToTime(wt->TimeToLongSamples(cutStart));
cutEnd = wt->LongSamplesToTime(wt->TimeToLongSamples(cutEnd));
// Make sure the cross-fade does not affect non-silent frames
if (wt->LongSamplesToTime(blendFrames) > inLength) {
blendFrames = wt->TimeToLongSamples(inLength);
}
// Perform cross-fade in memory
float *buf1 = new float[blendFrames];
float *buf2 = new float[blendFrames];
sampleCount t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2;
sampleCount t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2;
wt->Get((samplePtr)buf1, floatSample, t1, blendFrames);
wt->Get((samplePtr)buf2, floatSample, t2, blendFrames);
for (sampleCount i = 0; i < blendFrames; ++i) {
buf1[i] = ((blendFrames-i) * buf1[i] + i * buf2[i]) /
(double)blendFrames;
}
// Perform the cut
wt->Clear(cutStart, cutEnd);
// Write cross-faded data
wt->Set((samplePtr)buf1, floatSample, t1, blendFrames);
delete [] buf1;
delete [] buf2;
}
else if (t->GetSelected() || t->IsSynchroSelected())
{
// Non-wave tracks: just do a sync adjust
double cutStart = (r->start + r->end - cutLen) / 2;
double cutEnd = cutStart + cutLen;
t->SyncAdjust(cutStart, cutEnd);
}
}
}
mT1 -= totalCutLen;
ReplaceProcessedTracks(true);
return true;
}
// Finds the intersection of the ordered region lists, stores in dest
void EffectTruncSilence::Intersect(RegionList &dest, const RegionList &src)
{
RegionList::iterator destIter;
destIter = dest.begin();
// Any time we reach the end of the dest list we're finished
if (destIter == dest.end())
return;
Region *curDest = *destIter;
// Operation: find non-silent regions in src, remove them from dest.
double nsStart = curDest->start;
double nsEnd;
bool lastRun = false; // must run the loop one extra time
RegionList::const_iterator srcIter = src.begin();
while (srcIter != src.end() || lastRun)
{
// Don't use curSrc unless lastRun is false!
Region *curSrc;
if (lastRun)
{
// The last non-silent region extends as far as possible
curSrc = NULL;
nsEnd = std::numeric_limits<double>::max();
}
else
{
curSrc = *srcIter;
nsEnd = curSrc->start;
}
if (nsEnd > nsStart)
{
// Increment through dest until we have a region that could be affected
while (curDest->end <= nsStart) {
++destIter;
if (destIter == dest.end())
return;
curDest = *destIter;
}
// Check for splitting dest region in two
if (nsStart > curDest->start && nsEnd < curDest->end) {
// The second region
Region *r = new Region;
r->start = nsEnd;
r->end = curDest->end;
// The first region
curDest->end = nsStart;
// Insert second
// AWD: wxList::insert() has a bug causing it to return the wrong
// value; this is a workaround.
RegionList::iterator nextIt(destIter);
++nextIt;
dest.insert(nextIt, r);
++destIter; // (now points at the newly-inserted region)
curDest = *destIter;
}
// Check for truncating the end of dest region
if (nsStart > curDest->start && nsStart < curDest->end &&
nsEnd >= curDest->end)
{
curDest->end = nsStart;
++destIter;
if (destIter == dest.end())
return;
curDest = *destIter;
}
// Check for all dest regions that need to be removed completely
while (nsStart <= curDest->start && nsEnd >= curDest->end) {
destIter = dest.erase(destIter);
if (destIter == dest.end())
return;
curDest = *destIter;
}
// Check for truncating the beginning of dest region
if (nsStart <= curDest->start &&
nsEnd > curDest->start && nsEnd < curDest->end)
{
curDest->start = nsEnd;
}
}
if (lastRun) {
// done
lastRun = false;
}
else {
// Next non-silent region starts at the end of this silent region
nsStart = curSrc->end;
++srcIter;
if (srcIter == src.end()) {
lastRun = true;
}
}
}
}
#endif // EXPERIMENTAL_TRUNC_SILENCE
void EffectTruncSilence::BlendFrames(float* buffer, int blendFrameCount, int leftIndex, int rightIndex)
{
float* bufOutput = &buffer[leftIndex];

View File

@ -18,6 +18,14 @@
#define __AUDACITY_EFFECT_TRUNC_SILENCE__
#include "Effect.h"
#include "../Experimental.h"
#include <wx/list.h>
// Declaration of RegionList
struct REGION;
typedef struct REGION Region;
WX_DECLARE_LIST(Region, RegionList);
class EffectTruncSilence: public Effect {
@ -53,6 +61,9 @@ public:
private:
//ToDo ... put BlendFrames in Effects, Project, or other class
void BlendFrames(float* buffer, int leftIndex, int rightIndex, int blendFrameCount);
#ifdef EXPERIMENTAL_TRUNC_SILENCE
void Intersect(RegionList &dest, const RegionList &src);
#endif
private:
sampleCount mBlendFrameCount;