I think this is ready for serious testing. I have also included another effect "Trim Silence" which trims silence from the front, back or both ends of an audio track. These images are from Audacity 2.0 & wxWidgets 2.8.
Those two odd buttons ("Pristine" & "Tight") are just there to play around with.
Code: Select all
/////////////////////////////////////////////////////////////////////
//Mark Silence Effect
EffectMarkSilence::EffectMarkSilence()
{
SetEffectFlags(BUILTIN_EFFECT | ANALYZE_EFFECT);
mbAddLeadingLabel = false;
mbAddNumericLabelTrack = false;
gPrefs->Read(wxT("/Effects/MarkSilence/RecentMinimumAmplitude"), &miMinimumAmplitude, 900);
gPrefs->Read(wxT("/Effects/MarkSilence/RecentMinimumDuration"), &mdMinimumDuration, 2.7);
gPrefs->Read(wxT("/Effects/MarkSilence/RecentMinimumWait"), &mdMinimumWait, 90.0);
}
wxString EffectMarkSilence::GetEffectDescription()
{
return wxString::Format(_("Adding label(s) to mark silence(s)"));
}
bool EffectMarkSilence::PromptUser()
{
MarkSilenceDialog dlg(this, mParent);
dlg.CentreOnParent();
if (dlg.ShowModal() == wxID_CANCEL) {
return false;
}
gPrefs->Write(wxT("/Effects/MarkSilence/RecentAddLeadingLabel"), mbAddLeadingLabel);
gPrefs->Write(wxT("/Effects/MarkSilence/RecentNumericLabelTrack"), mbAddNumericLabelTrack);
gPrefs->Write(wxT("/Effects/MarkSilence/RecentMinimumAmplitude"), miMinimumAmplitude);
gPrefs->Write(wxT("/Effects/MarkSilence/RecentMinimumDuration"), mdMinimumDuration);
gPrefs->Write(wxT("/Effects/MarkSilence/RecentMinimumWait"), mdMinimumWait);
return true;
}
bool EffectMarkSilence::TransferParameters(Shuttle & shuttle)
{
shuttle.TransferBool(wxT("Leading"), mbAddLeadingLabel, false);
shuttle.TransferBool(wxT("Numeric"), mbAddNumericLabelTrack, false);
shuttle.TransferInt(wxT("Amplitude"), miMinimumAmplitude, 900);
shuttle.TransferDouble(wxT("Duration"), mdMinimumDuration, 2.7);
shuttle.TransferDouble(wxT("Wait"), mdMinimumWait, 90.0);
return true;
}
bool EffectMarkSilence::Process()
{
if (miMinimumAmplitude < 0) {
miMinimumAmplitude = 0;
wxMessageBox(_("the Minimum Amplitude value must NOT be negative; setting to 0"));
}
else if (miMinimumAmplitude > USHRT_MAX) {
miMinimumAmplitude = USHRT_MAX;
wxMessageBox(_("the Minimum Amplitude value must NOT exceed 65535; setting to 65535"));
}
LabelTrack * labelTrack = NULL;
Track * original = NULL;
LabelTrack * numericTrack = NULL;
Track * originalNumeric = NULL;
TrackListOfKindIterator iter(Track::Label, mTracks);
for (Track * track = iter.First(); track; track = iter.Next()) {
if (track->GetName() == wxT("Silences")) {
labelTrack = (LabelTrack *)track;
// copy LabelTrack here, so it can be undone on cancel
labelTrack->Copy(labelTrack->GetStartTime(), labelTrack->GetEndTime(), &original);
original->SetOffset(labelTrack->GetStartTime());
original->SetName(wxT("Silences"));
break;
}
if (track->GetName() == wxT("Numeric")) {
numericTrack = (LabelTrack *)track;
// copy Numeric LabelTrack here, so it can be undone on cancel
numericTrack->Copy(numericTrack->GetStartTime(), numericTrack->GetEndTime(), &originalNumeric);
originalNumeric->SetOffset(numericTrack->GetStartTime());
originalNumeric->SetName(wxT("Numeric"));
break;
}
}
if (!labelTrack) {
labelTrack = mFactory->NewLabelTrack();
labelTrack->SetName(_("Silences"));
mTracks->Add((Track *)labelTrack);
}
if (mbAddNumericLabelTrack && !numericTrack) {
numericTrack = mFactory->NewLabelTrack();
numericTrack->SetName(_("Numeric"));
mTracks->Add((Track *)numericTrack);
}
// JC: Only process selected tracks.
SelectedTrackListOfKindIterator waves(Track::Wave, mTracks);
WaveTrack * waveTrack = (WaveTrack *)waves.First();
while (waveTrack) {
double trackStart = waveTrack->GetStartTime();
double trackEnd = waveTrack->GetEndTime();
double t0 = mT0 < trackStart ? trackStart : mT0;
double t1 = mT1 > trackEnd ? trackEnd : mT1;
if (t1 > t0) {
sampleCount start = waveTrack->TimeToLongSamples(t0);
sampleCount end = waveTrack->TimeToLongSamples(t1);
sampleCount len = (sampleCount)(end - start);
if (waveTrack->GetLinked()) {//Stereo track
WaveTrack * rightTrack = (WaveTrack *)(waves.Next());
if (!ProcessStereo(labelTrack, numericTrack, waveTrack, rightTrack, start, len)) {
//put it back how it was
mTracks->Remove((Track *)labelTrack);
if (original) {
mTracks->Add((Track *)original);
}
if (originalNumeric) {
mTracks->Remove((Track *)numericTrack);
if (originalNumeric) {
mTracks->Add((Track *)originalNumeric);
}
}
return false;
}
}
else {//Mono track
if (!ProcessOne(labelTrack, numericTrack, waveTrack, start, len)) {
//put it back how it was
mTracks->Remove((Track *)labelTrack);
if (original) {
mTracks->Add((Track *)original);
}
if (originalNumeric) {
mTracks->Remove((Track *)numericTrack);
if (originalNumeric) {
mTracks->Add((Track *)originalNumeric);
}
}
return false;
}
}
}
waveTrack = (WaveTrack *)waves.Next();
}
return true;
}
bool EffectMarkSilence::ProcessOne(LabelTrack * pLabelTrack, LabelTrack * pNumericLabelTrack, WaveTrack * pWaveTrack,
sampleCount pStart, sampleCount pLength)
{
double trackRate = pWaveTrack->GetRate();
sampleCount duration = mdMinimumDuration * trackRate, waittime = mdMinimumWait * trackRate;
if (duration >= pLength)
return true;
sampleCount lastUseful = pLength - duration;
int labelCount = 2;
wxString labelCountString;
bool goodResult = true;
float * buffer;
try {
buffer = new float[pLength];
}
catch (std::bad_alloc&) {
wxMessageBox(_("Insufficient RAM to process sample"));
return false;
}
if (mbAddLeadingLabel) {
pLabelTrack->AddLabel(0.0, 0.0);
if (pNumericLabelTrack) {
pNumericLabelTrack->AddLabel(0.0, 0.0, wxT("1"));
}
}
goodResult = pWaveTrack->Get((samplePtr)buffer, floatSample, pStart, pLength);
if (!goodResult) {
delete[] buffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
sampleCount startLocation = pStart;
if (pStart == 0)
startLocation = duration / 2;
for (sampleCount bufferLocation = startLocation; bufferLocation < pLength; bufferLocation++) {
if (fabs(buffer[bufferLocation]) <= miMinimumAmplitude) {
for (sampleCount endLocation = bufferLocation + 1; endLocation < lastUseful; endLocation++) {
if (fabs(buffer[endLocation]) >= miMinimumAmplitude) {
if ((endLocation - bufferLocation) >= duration) {
double centerSilence = pWaveTrack->LongSamplesToTime(bufferLocation + ((endLocation - bufferLocation) / 2));
pLabelTrack->AddLabel(centerSilence, centerSilence);
if (pNumericLabelTrack) {
labelCountString.Printf(wxT("%d"), labelCount++);
pNumericLabelTrack->AddLabel(centerSilence, centerSilence, labelCountString);
}
bufferLocation = endLocation + waittime;
}
else
bufferLocation = endLocation + 1;
break;
}
}
}
}
delete[] buffer;
return goodResult;
}
bool EffectMarkSilence::ProcessStereo(LabelTrack * pLabelTrack, LabelTrack * pNumericLabelTrack, WaveTrack * pLeftTrack, WaveTrack * pRightTrack,
sampleCount pStart, sampleCount pLength) {
double trackRate = pLeftTrack->GetRate();
sampleCount duration = mdMinimumDuration * trackRate, waittime = mdMinimumWait * trackRate;
if (duration >= pLength)
return true;
sampleCount lastUseful = pLength - duration;
int labelCount = 2;
wxString labelCountString;
bool goodResult = true;
unsigned short * buffer = NULL;
float * leftBuffer = NULL;
float * rightBuffer = NULL;
#define TEMPORARY_BUFFER_SIZE 2500000//Slightly less than one minute at 44,100 samples/second
try {
buffer = new unsigned short[pLength];
}
catch (std::bad_alloc&) {
wxMessageBox(_("buffer - Insufficient RAM to process sample"));
return false;
}
try {
leftBuffer = new float[TEMPORARY_BUFFER_SIZE];
}
catch (std::bad_alloc&) {
delete[] buffer;
wxMessageBox(_("leftBuffer - Insufficient RAM to process sample"));
return false;
}
try {
rightBuffer = new float[TEMPORARY_BUFFER_SIZE];
}
catch (std::bad_alloc&) {
delete[] buffer;
delete[] leftBuffer;
wxMessageBox(_("rightBuffer - Insufficient RAM to process sample"));
return false;
}
if (mbAddLeadingLabel) {
pLabelTrack->AddLabel(0.0, 0.0);
if (pNumericLabelTrack) {
pNumericLabelTrack->AddLabel(0.0, 0.0, wxT("1"));
}
}
sampleCount startLocation = pStart;
if (pStart == 0)
startLocation = duration / 2;
sampleCount fullBuffers = ((pLength - startLocation) / TEMPORARY_BUFFER_SIZE), bufferRemainder = ((pLength - startLocation) % TEMPORARY_BUFFER_SIZE);
for (sampleCount filler = 0; filler < fullBuffers; filler++) {
//Assuming Track->Get() will never fail
goodResult = pLeftTrack->Get((samplePtr)leftBuffer, floatSample, startLocation, TEMPORARY_BUFFER_SIZE);
if (!goodResult) {
delete[] buffer;
delete[] leftBuffer;
delete[] rightBuffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
goodResult = pRightTrack->Get((samplePtr)rightBuffer, floatSample, startLocation, TEMPORARY_BUFFER_SIZE);
if (!goodResult) {
delete[] buffer;
delete[] leftBuffer;
delete[] rightBuffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
for (sampleCount getAverage = 0; getAverage < TEMPORARY_BUFFER_SIZE; getAverage++) {
float sampleAverage = (fabs(leftBuffer[getAverage]) + fabs(rightBuffer[getAverage])) / 2;
unsigned short shortSampleAverage = sampleAverage * USHRT_MAX;//USHRT_MAX = 65535;
buffer[(filler * TEMPORARY_BUFFER_SIZE) + getAverage] = shortSampleAverage;
}
startLocation += TEMPORARY_BUFFER_SIZE;
}
goodResult = pLeftTrack->Get((samplePtr)leftBuffer, floatSample, startLocation, bufferRemainder);
if (!goodResult) {
delete[] buffer;
delete[] leftBuffer;
delete[] rightBuffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
goodResult = pRightTrack->Get((samplePtr)rightBuffer, floatSample, startLocation, bufferRemainder);
if (!goodResult) {
delete[] buffer;
delete[] leftBuffer;
delete[] rightBuffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
for (sampleCount getAverage = 0; getAverage < bufferRemainder; getAverage++) {
float sampleAverage = (fabs(leftBuffer[getAverage]) + fabs(rightBuffer[getAverage])) / 2;
unsigned short shortSampleAverage = sampleAverage * USHRT_MAX;//USHRT_MAX = 65535;
buffer[(fullBuffers * TEMPORARY_BUFFER_SIZE) + getAverage] = shortSampleAverage;
}
startLocation = pStart;
if (pStart == 0)
startLocation = duration / 2;
for (sampleCount bufferLocation = startLocation; bufferLocation < pLength; bufferLocation++) {
if (buffer[bufferLocation] <= miMinimumAmplitude) {
bool weedOut = true;
#define WEED_GRANULARITY 1000
for (sampleCount weedLocation = bufferLocation + 1; weedLocation < WEED_GRANULARITY; weedLocation++) {//Eliminate false positives
if (buffer[weedLocation] > miMinimumAmplitude) {
weedOut = false;
weedLocation = WEED_GRANULARITY;
bufferLocation += (WEED_GRANULARITY - 1);
}
}
if (weedOut) {
for (sampleCount endLocation = bufferLocation + 1; endLocation < lastUseful; endLocation++) {
if (buffer[endLocation] >= miMinimumAmplitude) {
if ((endLocation - bufferLocation) >= duration) {
double centerSilence = pLeftTrack->LongSamplesToTime(bufferLocation + ((endLocation - bufferLocation) / 2));
pLabelTrack->AddLabel(centerSilence, centerSilence);
if (pNumericLabelTrack) {
labelCountString.Printf(wxT("%d"), labelCount++);
pNumericLabelTrack->AddLabel(centerSilence, centerSilence, labelCountString);
}
bufferLocation = endLocation + waittime;
}
else
bufferLocation = endLocation + 1;
break;
}
}
}
}
}
delete[] rightBuffer;
delete[] leftBuffer;
delete[] buffer;
return goodResult;
}
//----------------------------------------------------------------------------
// MarkSilenceDialog
//----------------------------------------------------------------------------
#define ID_CLEAN_VINYL 9000
#define ID_DIRTY_VINYL 9001
#define ID_CD 9002
#define ID_PRISTINE 9003
#define ID_TIGHT 9004
#define ID_SAVE_DEFAULTS 9005
#define ID_RESTORE_DEFAULTS 9006
BEGIN_EVENT_TABLE(MarkSilenceDialog, EffectDialog)
EVT_BUTTON(ID_CLEAN_VINYL, MarkSilenceDialog::OnCleanVinylButton)
EVT_BUTTON(ID_DIRTY_VINYL, MarkSilenceDialog::OnDirtyVinylButton)
EVT_BUTTON(ID_CD, MarkSilenceDialog::OnCBButton)
EVT_BUTTON(ID_PRISTINE, MarkSilenceDialog::OnPristineButton)
EVT_BUTTON(ID_TIGHT, MarkSilenceDialog::OnTightButton)
EVT_BUTTON(ID_SAVE_DEFAULTS, MarkSilenceDialog::OnSaveDefaultsButton)
EVT_BUTTON(ID_RESTORE_DEFAULTS, MarkSilenceDialog::OnRestoreDefaultsButton)
END_EVENT_TABLE()
MarkSilenceDialog::MarkSilenceDialog(EffectMarkSilence * effect, wxWindow * parent)
: EffectDialog(parent, _("Mark Silence"), INSERT_EFFECT)
{
mEffect = effect;
mcbAddLeadingLabel = NULL;
mcbAddNumericLabels = NULL;
mtcMinimumAmplitude = NULL;
mtcMinimumDuration = NULL;
mtcMinimumWait = NULL;
mbCleanVinyl = NULL;
mbDirtyVinyl = NULL;
mbCB = NULL;
mbPristine = NULL;
mbTight = NULL;
mbSave = NULL;
mbRestore = NULL;
Init();
}
void MarkSilenceDialog::PopulateOrExchange(ShuttleGui & S)
{
S.StartScroller();
S.StartVerticalLay();
{
S.StartStatic(_("Extra Labels"), 0);
{
mcbAddLeadingLabel = S.TieCheckBox(_("Place label at the beginning of the track"), mEffect->mbAddLeadingLabel);
if (mEffect->mbAddLeadingLabel)
mcbAddLeadingLabel->SetValue(true);
mcbAddNumericLabels = S.TieCheckBox(_("Create second label track with numeric labels"), mEffect->mbAddNumericLabelTrack);
if (mEffect->mbAddNumericLabelTrack)
mcbAddNumericLabels->SetValue(true);
}
S.EndStatic();
S.StartStatic(_("Recorded Media Presets"), 0);
{
S.StartMultiColumn(5, wxALIGN_LEFT);
{
mbCleanVinyl = S.Id(ID_CLEAN_VINYL).AddButton(_("Clean Vinyl"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
mbDirtyVinyl = S.Id(ID_DIRTY_VINYL).AddButton(_("Dirty Vinyl"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
mbCB = S.Id(ID_CD).AddButton(_("CD"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
mbPristine = S.Id(ID_PRISTINE).AddButton(_("Pristine"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
mbTight = S.Id(ID_TIGHT).AddButton(_("Tight"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
}
S.EndMultiColumn();
}
S.EndStatic();
S.StartStatic(_("Variables"), 0);
{
S.StartMultiColumn(2, wxALIGN_LEFT);
{
mtcMinimumAmplitude = S.TieTextBox(_("Minimum amplitude (0 to 65535) to define as silence:"), mEffect->miMinimumAmplitude, 20);
mtcMinimumAmplitude->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
mtcMinimumDuration = S.TieTextBox(_("Minimum duration to define as silent gap (seconds):"), mEffect->mdMinimumDuration, 20);
mtcMinimumDuration->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
mtcMinimumWait = S.TieTextBox(_("Minimum wait time before looking for another silent gap (seconds):"), mEffect->mdMinimumWait, 20);
mtcMinimumWait->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
}
S.EndMultiColumn();
S.StartStatic(_("Defaults"), 0);
{
S.StartMultiColumn(2, wxALIGN_LEFT);
{
mbSave = S.Id(ID_SAVE_DEFAULTS).AddButton(_("Save As Defaults"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
mbRestore = S.Id(ID_RESTORE_DEFAULTS).AddButton(_("Restore Defaults"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
}
S.EndMultiColumn();
}
S.EndStatic();
}
S.EndStatic();
}
S.EndVerticalLay();
S.EndScroller();
}
bool MarkSilenceDialog::TransferDataFromWindow()
{
EffectDialog::TransferDataFromWindow();
return true;
}
void MarkSilenceDialog::OnCleanVinylButton(wxCommandEvent & WXUNUSED(anEvent)) {
wxString value;
value.Printf(wxT("%d"), 900);
mtcMinimumAmplitude->SetValue(value);
value.Printf(wxT("%.1f"), 2.7);
mtcMinimumDuration->SetValue(value);
value.Printf(wxT("%.1f"), 90.0);
mtcMinimumWait->SetValue(value);
}
void MarkSilenceDialog::OnDirtyVinylButton(wxCommandEvent & WXUNUSED(anEvent)) {
wxString value;
value.Printf(wxT("%d"), 1200);
mtcMinimumAmplitude->SetValue(value);
value.Printf(wxT("%.1f"), 1.0);
mtcMinimumDuration->SetValue(value);
value.Printf(wxT("%.1f"), 90.0);
mtcMinimumWait->SetValue(value);
}
void MarkSilenceDialog::OnCBButton(wxCommandEvent & WXUNUSED(anEvent)) {
wxString value;
value.Printf(wxT("%d"), 500);
mtcMinimumAmplitude->SetValue(value);
value.Printf(wxT("%.1f"), 1.0);
mtcMinimumDuration->SetValue(value);
value.Printf(wxT("%.1f"), 90.0);
mtcMinimumWait->SetValue(value);
}
void MarkSilenceDialog::OnPristineButton(wxCommandEvent & WXUNUSED(anEvent)) {
wxString value;
value.Printf(wxT("%d"), 100);
mtcMinimumAmplitude->SetValue(value);
value.Printf(wxT("%.1f"), 0.03);
mtcMinimumDuration->SetValue(value);
value.Printf(wxT("%.1f"), 90.0);
mtcMinimumWait->SetValue(value);
}
void MarkSilenceDialog::OnTightButton(wxCommandEvent & WXUNUSED(anEvent)) {
wxString value;
value.Printf(wxT("%d"), 200);
mtcMinimumAmplitude->SetValue(value);
value.Printf(wxT("%.1f"), 0.05);
mtcMinimumDuration->SetValue(value);
value.Printf(wxT("%.1f"), 90.0);
mtcMinimumWait->SetValue(value);
}
void MarkSilenceDialog::OnSaveDefaultsButton(wxCommandEvent & WXUNUSED(anEvent)) {
gPrefs->Write(wxT("/Effects/MarkSilence/AddLeadingLabel"), mcbAddLeadingLabel->GetValue());
gPrefs->Write(wxT("/Effects/MarkSilence/AddNumericLabelTrack"), mcbAddNumericLabels->GetValue());
gPrefs->Write(wxT("/Effects/MarkSilence/MinimumAmplitude"), mtcMinimumAmplitude->GetValue());
gPrefs->Write(wxT("/Effects/MarkSilence/MinimumDuration"), mtcMinimumDuration->GetValue());
gPrefs->Write(wxT("/Effects/MarkSilence/MinimumWait"), mtcMinimumWait->GetValue());
}
void MarkSilenceDialog::OnRestoreDefaultsButton(wxCommandEvent & WXUNUSED(anEvent)) {
gPrefs->Read(wxT("/Effects/MarkSilence/AddLeadingLabel"), &mEffect->mbAddLeadingLabel, false);
if (mEffect->mbAddLeadingLabel)
mcbAddLeadingLabel->SetValue(true);
gPrefs->Read(wxT("/Effects/MarkSilence/AddNumericLabelTrack"), &mEffect->mbAddNumericLabelTrack, false);
if (mEffect->mbAddNumericLabelTrack)
mcbAddNumericLabels->SetValue(true);
wxString value;
gPrefs->Read(wxT("/Effects/MarkSilence/MinimumAmplitude"), &mEffect->miMinimumAmplitude, 900);
value.Printf(wxT("%d"), mEffect->miMinimumAmplitude);
mtcMinimumAmplitude->SetValue(value);
gPrefs->Read(wxT("/Effects/MarkSilence/MinimumDuration"), &mEffect->mdMinimumDuration, 2.7);
value.Printf(wxT("%.1f"), mEffect->mdMinimumDuration);
mtcMinimumDuration->SetValue(value);
gPrefs->Read(wxT("/Effects/MarkSilence/MinimumWait"), &mEffect->mdMinimumWait, 90.0);
value.Printf(wxT("%.1f"), mEffect->mdMinimumWait);
mtcMinimumWait->SetValue(value);
}
/////////////////////////////////////////////////////////////////////
//Trim Silence Front &/or Back Effect
EffectTrimFrontBack::EffectTrimFrontBack()
{
miFrontBackBoth = TRIM_BOTH;
gPrefs->Read(wxT("/Effects/TrimFrontBack/FrontBackBoth"), &miFrontBackBoth, TRIM_BOTH);
gPrefs->Read(wxT("/Effects/TrimFrontBack/FrontPad"), &miFrontPad, 22000);
gPrefs->Read(wxT("/Effects/TrimFrontBack/BackPad"), &miBackPad, 22000);
gPrefs->Read(wxT("/Effects/TrimFrontBack/MinimumAmplitude"), &mdMinimumAmplitude, 0.005);
}
wxString EffectTrimFrontBack::GetEffectDescription()
{
return wxString::Format(_("Trim Silence Front and/or Back"));
}
bool EffectTrimFrontBack::PromptUser()
{
TrimFrontBackDialog dlg(this, mParent);
dlg.CentreOnParent();
if (dlg.ShowModal() == wxID_CANCEL) {
return false;
}
gPrefs->Write(wxT("/Effects/MarkSilence/FrontBackBoth"), miFrontBackBoth);
gPrefs->Write(wxT("/Effects/MarkSilence/BackPad"), miFrontPad);
gPrefs->Write(wxT("/Effects/MarkSilence/FrontPad"), miBackPad);
gPrefs->Write(wxT("/Effects/MarkSilence/MinimumAmplitude"), mdMinimumAmplitude);
return true;
}
bool EffectTrimFrontBack::TransferParameters(Shuttle & shuttle)
{
shuttle.TransferInt(wxT("FrontBackBoth"), miFrontBackBoth, TRIM_BOTH);
shuttle.TransferInt(wxT("FrontPad"), miFrontPad, 22000);
shuttle.TransferInt(wxT("BackPad"), miBackPad, 22000);
shuttle.TransferDouble(wxT("MinimumAmplitude"), mdMinimumAmplitude, 0.005);
return true;
}
bool EffectTrimFrontBack::Process()
{
if (mdMinimumAmplitude < 0.0) {
mdMinimumAmplitude = 0.0;
wxMessageBox(_("the Minimum Amplitude value must NOT be negative; setting to 0"));
}
else if (mdMinimumAmplitude > 1.0) {
mdMinimumAmplitude = 1.0;
wxMessageBox(_("the Minimum Amplitude value must NOT exceed 1.0; setting to 1"));
}
if (miFrontPad < 0) {
miFrontPad = 0;
wxMessageBox(_("the Front Pad value must NOT be negative; setting to 0"));
}
if (miBackPad < 0) {
miBackPad = 0;
wxMessageBox(_("the Back Pad value must NOT be negative; setting to 0"));
}
// JC: Only process selected tracks.
SelectedTrackListOfKindIterator waves(Track::Wave, mTracks);
WaveTrack * waveTrack = (WaveTrack *)waves.First();
while (waveTrack) {
double trackStart = waveTrack->GetStartTime();
double trackEnd = waveTrack->GetEndTime();
double t0 = mT0 < trackStart ? trackStart : mT0;
double t1 = mT1 > trackEnd ? trackEnd : mT1;
if (t1 > t0) {
sampleCount start = waveTrack->TimeToLongSamples(t0);
sampleCount end = waveTrack->TimeToLongSamples(t1);
sampleCount length = (sampleCount)(end - start);
if (waveTrack->GetLinked()) {//Stereo track
WaveTrack * rightTrack = (WaveTrack *)(waves.Next());
if (!ProcessStereo(waveTrack, rightTrack)) {
return false;
}
}
else {//Mono track
if (!ProcessOne(waveTrack)) {
return false;
}
}
}
waveTrack = (WaveTrack *)waves.Next();
}
return true;
}
bool EffectTrimFrontBack::ProcessOne(WaveTrack * pMonoTrack)
{
bool goodResult = true;
if ((miFrontBackBoth == TRIM_BOTH) || (miFrontBackBoth == TRIM_FRONT)) {
sampleCount trackEndSample = pMonoTrack->TimeToLongSamples(pMonoTrack->GetEndTime());
sampleCount trackStartSample = pMonoTrack->TimeToLongSamples(pMonoTrack->GetStartTime());
float * buffer;
try {
buffer = new float[trackEndSample];
}
catch (std::bad_alloc&) {
wxMessageBox(_("Insufficient RAM to process sample"));
return false;
}
goodResult = pMonoTrack->Get((samplePtr)buffer, floatSample, trackStartSample, trackEndSample);
if (!goodResult) {
delete[] buffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
for (sampleCount bufferLocation = 0; bufferLocation < trackEndSample; bufferLocation++) {
if (fabs(buffer[bufferLocation]) >= mdMinimumAmplitude) {
if (bufferLocation > miFrontPad) {
bufferLocation = bufferLocation - miFrontPad;
pMonoTrack->Clear(pMonoTrack->GetStartTime(), pMonoTrack->LongSamplesToTime(bufferLocation));
}
break;
}
}
delete[] buffer;
}
if ((miFrontBackBoth == TRIM_BOTH) || (miFrontBackBoth == TRIM_BACK)) {
sampleCount trackEndSample = pMonoTrack->TimeToLongSamples(pMonoTrack->GetEndTime());
sampleCount trackStartSample = pMonoTrack->TimeToLongSamples(pMonoTrack->GetStartTime());
float * buffer;
try {
buffer = new float[trackEndSample];
}
catch (std::bad_alloc&) {
wxMessageBox(_("Insufficient RAM to process sample"));
return false;
}
goodResult = pMonoTrack->Get((samplePtr)buffer, floatSample, trackStartSample, trackEndSample);
if (!goodResult) {
delete[] buffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
sampleCount bufferEnd = trackEndSample - 1;
for (sampleCount bufferLocation = bufferEnd; bufferLocation > trackStartSample; bufferLocation--) {
if (fabs(buffer[bufferLocation]) >= mdMinimumAmplitude) {
if ((bufferLocation + miBackPad) <= bufferEnd) {
bufferLocation = bufferLocation + miBackPad;
pMonoTrack->Clear(pMonoTrack->LongSamplesToTime(bufferLocation), pMonoTrack->GetEndTime());
}
break;
}
}
delete[] buffer;
}
return goodResult;
}
bool EffectTrimFrontBack::ProcessStereo(WaveTrack * pLeftTrack, WaveTrack * pRightTrack) {
bool goodResult = true;
sampleCount leftFront = 0, rightFront = 0, leftBack = 0, rightBack = 0;
if ((miFrontBackBoth == TRIM_BOTH) || (miFrontBackBoth == TRIM_FRONT)) {
//Left track
sampleCount trackEndSample = pLeftTrack->TimeToLongSamples(pLeftTrack->GetEndTime());
sampleCount trackStartSample = pLeftTrack->TimeToLongSamples(pLeftTrack->GetStartTime());
float * buffer;
try {
buffer = new float[trackEndSample];
}
catch (std::bad_alloc&) {
wxMessageBox(_("Insufficient RAM to process sample"));
return false;
}
goodResult = pLeftTrack->Get((samplePtr)buffer, floatSample, trackStartSample, trackEndSample);
if (!goodResult) {
delete[] buffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
for (sampleCount bufferLocation = 0; bufferLocation < trackEndSample; bufferLocation++) {
if (fabs(buffer[bufferLocation]) >= mdMinimumAmplitude) {
if (bufferLocation > miFrontPad) {
bufferLocation = bufferLocation - miFrontPad;
leftFront = bufferLocation;
}
break;
}
}
delete[] buffer;
//Right track
trackEndSample = pRightTrack->TimeToLongSamples(pRightTrack->GetEndTime());
trackStartSample = pRightTrack->TimeToLongSamples(pRightTrack->GetStartTime());
try {
buffer = new float[trackEndSample];
}
catch (std::bad_alloc&) {
wxMessageBox(_("Insufficient RAM to process sample"));
return false;
}
goodResult = pRightTrack->Get((samplePtr)buffer, floatSample, trackStartSample, trackEndSample);
if (!goodResult) {
delete[] buffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
for (sampleCount bufferLocation = 0; bufferLocation < trackEndSample; bufferLocation++) {
if (fabs(buffer[bufferLocation]) >= mdMinimumAmplitude) {
if (bufferLocation > miFrontPad) {
bufferLocation = bufferLocation - miFrontPad;
rightFront = bufferLocation;
}
break;
}
}
delete[] buffer;
}
if ((miFrontBackBoth == TRIM_BOTH) || (miFrontBackBoth == TRIM_FRONT)) {
if ((leftFront != 0) && (rightFront != 0)) {
sampleCount front = leftFront;
if (rightFront < front)
front = rightFront;
goodResult = pRightTrack->Clear(pRightTrack->GetStartTime(), pRightTrack->LongSamplesToTime(front));
if (goodResult)
goodResult = pLeftTrack->Clear(pLeftTrack->GetStartTime(), pLeftTrack->LongSamplesToTime(front));
}
}
if ((miFrontBackBoth == TRIM_BOTH) || (miFrontBackBoth == TRIM_BACK)) {
//Left track
sampleCount trackEndSample = pLeftTrack->TimeToLongSamples(pLeftTrack->GetEndTime());
sampleCount trackStartSample = pLeftTrack->TimeToLongSamples(pLeftTrack->GetStartTime());
float * buffer;
try {
buffer = new float[trackEndSample];
}
catch (std::bad_alloc&) {
wxMessageBox(_("Insufficient RAM to process sample"));
return false;
}
goodResult = pLeftTrack->Get((samplePtr)buffer, floatSample, trackStartSample, trackEndSample);
if (!goodResult) {
delete[] buffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
sampleCount bufferEnd = trackEndSample - 1;
for (sampleCount bufferLocation = bufferEnd; bufferLocation > trackStartSample; bufferLocation--) {
if (fabs(buffer[bufferLocation]) >= mdMinimumAmplitude) {
if ((bufferLocation + miBackPad) <= bufferEnd) {
bufferLocation = bufferLocation + miBackPad;
leftBack = bufferLocation;
}
break;
}
}
delete[] buffer;
//Right track
trackEndSample = pRightTrack->TimeToLongSamples(pRightTrack->GetEndTime());
trackStartSample = pRightTrack->TimeToLongSamples(pRightTrack->GetStartTime());
try {
buffer = new float[trackEndSample];
}
catch (std::bad_alloc&) {
wxMessageBox(_("Insufficient RAM to process sample"));
return false;
}
goodResult = pRightTrack->Get((samplePtr)buffer, floatSample, trackStartSample, trackEndSample);
if (!goodResult) {
delete[] buffer;
wxMessageBox(_("Unable to fill the track buffer"));
return false;
}
bufferEnd = trackEndSample - 1;
for (sampleCount bufferLocation = bufferEnd; bufferLocation > trackStartSample; bufferLocation--) {
if (fabs(buffer[bufferLocation]) >= mdMinimumAmplitude) {
if ((bufferLocation + miBackPad) <= bufferEnd) {
bufferLocation = bufferLocation + miBackPad;
rightBack = bufferLocation;
}
break;
}
}
delete[] buffer;
}
if (goodResult) {
if ((miFrontBackBoth == TRIM_BOTH) || (miFrontBackBoth == TRIM_BACK)) {
if ((leftBack != 0) && (rightBack != 0)) {
sampleCount back = leftBack;
if (rightBack > back)
back = rightBack;
sampleCount debugsc = pRightTrack->TimeToLongSamples(pRightTrack->GetEndTime());
goodResult = pRightTrack->Clear(pRightTrack->LongSamplesToTime(back), pRightTrack->GetEndTime());
if (goodResult)
goodResult = pLeftTrack->Clear(pLeftTrack->LongSamplesToTime(back), pLeftTrack->GetEndTime());
}
}
}
return goodResult;
}
//----------------------------------------------------------------------------
// TrimFrontBackDialog
//----------------------------------------------------------------------------
#define ID_TRIM_BOTH 9003
#define ID_TRIM_FRONT 9001
#define ID_TRIM_BACK 9002
BEGIN_EVENT_TABLE(TrimFrontBackDialog, EffectDialog)
EVT_BUTTON(ID_TRIM_FRONT, TrimFrontBackDialog::OnFrontButton)
EVT_BUTTON(ID_TRIM_BACK, TrimFrontBackDialog::OnBackButton)
EVT_BUTTON(ID_TRIM_BOTH, TrimFrontBackDialog::OnBothButton)
END_EVENT_TABLE()
TrimFrontBackDialog::TrimFrontBackDialog(EffectTrimFrontBack * effect, wxWindow * parent)
: EffectDialog(parent, _("Mark Silence"), INSERT_EFFECT)
{
mEffect = effect;
mtcFrontPad = NULL;
mtcBackPad = NULL;
mtcMinimumAmplitude = NULL;
mtcFrom = NULL;
mbBoth = NULL;
mbFront = NULL;
mbBack = NULL;
Init();
}
void TrimFrontBackDialog::PopulateOrExchange(ShuttleGui & S)
{
S.StartScroller();
S.StartVerticalLay();
{
mtcFrontPad = S.TieTextBox(_("Padding at front (in samples):"), mEffect->miFrontPad, 20);
mtcFrontPad->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
mtcBackPad = S.TieTextBox(_("Padding it back (in samples):"), mEffect->miBackPad, 20);
mtcBackPad->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
mtcMinimumAmplitude = S.TieTextBox(_("Minimum amplitude (0.0 to 1.0) to define as silence:"), mEffect->mdMinimumAmplitude, 20);
mtcMinimumAmplitude->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
S.StartStatic(_("Trim Silence From"), 0);
{
S.StartMultiColumn(3, wxALIGN_LEFT);
{
mbBoth = S.Id(ID_TRIM_BOTH).AddButton(_("Both"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
mbFront = S.Id(ID_TRIM_FRONT).AddButton(_("Front"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
mbBack = S.Id(ID_TRIM_BACK).AddButton(_("Back"), wxALL | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
}
S.EndMultiColumn();
wxString fromWhere;
if (mEffect->miFrontBackBoth == TRIM_FRONT)
fromWhere = _("trimming from front");
else if (mEffect->miFrontBackBoth == TRIM_BACK)
fromWhere = _("trimming from back");
else
fromWhere = _("trimming from both");
mtcFrom = S.AddTextBox(wxEmptyString, fromWhere, 20);
mtcFrom->SetEditable(false);
}
S.EndStatic();
}
S.EndVerticalLay();
S.EndScroller();
}
bool TrimFrontBackDialog::TransferDataFromWindow()
{
EffectDialog::TransferDataFromWindow();
return true;
}
void TrimFrontBackDialog::OnBothButton(wxCommandEvent & WXUNUSED(anEvent)) {
mEffect->miFrontBackBoth = 3;
mtcFrom->SetValue(_("trimming from both"));
}
void TrimFrontBackDialog::OnFrontButton(wxCommandEvent & WXUNUSED(anEvent)) {
mEffect->miFrontBackBoth = 1;
mtcFrom->SetValue(_("trimming from front"));
}
void TrimFrontBackDialog::OnBackButton(wxCommandEvent & WXUNUSED(anEvent)) {
mEffect->miFrontBackBoth = 2;
mtcFrom->SetValue(_("trimming from back"));
}