Community Forums Archive

Go Back

Subject:Glitch Removal Script
Posted by: timaddy
Date:11/1/2006 9:10:12 AM

Enclosed is a glitch removal script that uses the internal glitch tool with soundforge (it runs quite a bit slower than the Audio Restoration plugin). One thing that i've not been able to do is work out how to set anything but a fixed interpolation window size (i've tried to select a window using the start point where the glitch is find - ccAfter and set the window range using ccrange but it always seems to set the same window size)? Any help/improvements would be great.



//NOTE: This script requires Sound Forge 8.0a or higher to run.
// Requires IScriptableApp.Win32Window, which is not available in Sound Forge 8.0

//This script loads in all the wav files from a directory (presently set by a prompt) and then removes any audio glitches
//from these files and writes the processed files out to a new directory(also selected via a prompt)


using System;
using System.IO;
using System.Windows.Forms;
using SoundForge;

public class EntryPoint {
public void Begin(IScriptableApp app) {

string szPath = PromptForPath(app.Win32Window, @"h:\");
string szFilter = "*.wav";


//Loop to the audio restoration plugin on each WAV file
foreach (string szWAVFile in Directory.GetFiles(szPath, szFilter))
{
string szWavFile = Path.GetFileNameWithoutExtension(szWAVFile);
szWavFile = Path.Combine(Path.GetDirectoryName(szWAVFile),szWavFile) + ".wav";//could remove this line if the extension is included in the szWavFile
DPF("{0} check the WAV File variable contents", szWavFile);

if ( ! File.Exists(szWavFile))
{
DPF("{0} does not exist, it will be skipped.", szWavFile);
continue;
}

//process the file using the audio restoration effect
SfStatus result = ProcessFile(app, szWavFile);//call the process file function

DPF("Completed the audio de-glitch process");

DPF("~ - {0}", result);
if (result != SfStatus.Success)
break;
}
} // Begin

//Function used to process the WAV input file and call the audio de-clicker function

public SfStatus ProcessFile(IScriptableApp app, string szWavFile)
{
SfStatus result = SfStatus.Success;

try
{
ISfFileHost fileIn = app.OpenFile(szWavFile, false, false);//not read-only, allow soundforge window

//Restore the present file using the built in glitch removal function
//DPF("Find the audio glitches in each file using the soundforge built in glitch removal");

Int64 ccLength = fileIn.Length;
Int64 ccGlitchPos = 0;
Int64 ccPosDiff = 0;//Sample Position Difference between glitches
Int64 ccRange = 192;//set the sample width to be 96
Int64 ccEnd = 0;//End of Interpolation window sample value

ISfDataWnd wnd = app.ActiveWindow;

// create an undo wrapper, so we don't end up with an undo for every marker that we create...

for (Int64 ccPos = ccGlitchPos; ccPos < ccLength; ccPos += ccPosDiff)
{
if (result == SfStatus.Success)
{
SfAudioSelection aselAll = new SfAudioSelection(ccGlitchPos,-1);//Select the whole audio waveform (0 = start, -1 = end)
Int64 ccAfter = fileIn.FindAudioGlitch(aselAll, 0.1, 5);//these settings can be adjusted dependent on type of glitch
result = fileIn.WaitForDoneOrCancel();
ccEnd = ccAfter + ccRange;//set the interpolation window
SfAudioSelection GlitchReg = new SfAudioSelection(ccAfter, ccEnd);//Create the Interpolation window
app.DoMenuAndWait ("Tools: Repair Interpolate", true);
ccGlitchPos = ccAfter;//hold the present glitch position to use as the start of the next FindAudio Glitch
ccPosDiff = ccAfter - ccPos;//Calculate the difference between the new glitch and the previous one
if (ccPosDiff == 0)//Loop until no further glitches found
{
break;
}
fileIn.Window.SetCursorAndScroll(ccAfter, DataWndScrollTo.Nearest);

DPF("after {0} ccPos {1} PosDiff {2}", ccAfter, ccPos, ccPosDiff);
}
else
{
break;
}
}

//Write out the new audio file
ISfFileHost fileOut = fileIn.NewFile(new SfAudioSelection(0,-1));//select all of the current AudioSelection? Check this

//Select all of the audio from the present file, then overwrite the fileOut with this from fileIn
SfAudioSelection aselAllOut = new SfAudioSelection(0,-1);
fileOut.OverwriteAudio(0, 0, fileIn, aselAllOut);

fileOut.Save(SaveOptions.PromptIfNoFilename);//if a prompt is required to choose the output directory **use this***
result = fileOut.WaitForDoneOrCancel();
fileIn.Close(CloseOptions.DiscardChanges);//close the input file
fileOut.Close(CloseOptions.SaveChanges);//close the output file

if (fileOut.IsZombie)//checks to make sure that the file is dead and not yet released
{
DPF(" the file is closed");
}
else
{
DPF(" the file is NOT closed");
}

}
catch(FileNotFoundException ex)
{
result = SfStatus.Fail;
//write the error message to the console
Console.Write("Error: " + ex.Message);
Console.WriteLine(" May be the file doesn't exist or it was incorrectly typed in!");
}

return result;
}

public string PromptForPath(IWin32Window hParent, string szDir)
{
FolderBrowserDialog dlg = new FolderBrowserDialog();

dlg.Description = "Select folder for rendered files:";
if (null != szDir && "" != szDir)
dlg.SelectedPath = szDir;
else
dlg.SelectedPath = @"C:\";
dlg.ShowNewFolderButton = true;
DialogResult res = dlg.ShowDialog(hParent);
if (res == DialogResult.OK)
return dlg.SelectedPath;
return null;
}

public void FromSoundForge(IScriptableApp app) {
ForgeApp = app; //execution begins here
app.SetStatusText(String.Format("Script '{0}' is running.", Script.Name));
Begin(app);
app.SetStatusText(String.Format("Script '{0}' is done.", Script.Name));
}
public static IScriptableApp ForgeApp = null;
public static void DPF(string sz) { ForgeApp.OutputText(sz); }
public static void DPF(string fmt, object o) { ForgeApp.OutputText(String.Format(fmt,o)); }
public static void DPF(string fmt, object o, object o2) { ForgeApp.OutputText(String.Format(fmt,o,o2)); }
public static void DPF(string fmt, object o, object o2, object o3) { ForgeApp.OutputText(String.Format(fmt,o,o2,o3)); }
public static string GETARG(string k, string d) { string val = Script.Args.ValueOf(k); if (val == null || val.Length == 0) val = d; return val; }
public static int GETARG(string k, int d) { string s = Script.Args.ValueOf(k); if (s == null || s.Length == 0) return d; else return Script.Args.AsInt(k); }
public static bool GETARG(string k, bool d) { string s = Script.Args.ValueOf(k); if (s == null || s.Length == 0) return d; else return Script.Args.AsBool(k); }
} //EntryPoint


Message last edited on11/2/2006 1:45:12 AM bytimaddy.
Subject:RE: Glitch Removal Script
Reply by: _TJ
Date:11/1/2006 2:09:49 PM

Would you edit your posts and add <pre> before the code and </pre> after it?
This will make the code much easier to read.

I'll be back later with more a more substantive reply. But it would really help to have properly indented code to look at.

tj

Subject:RE: Glitch Removal Script
Reply by: _TJ
Date:11/2/2006 1:08:04 PM

Thank you for re-formatting. This is much better.

Now, this is a pretty good script, but it doesn't quite do what you think it does. The main body of the code is this:


for (Int64 ccPos = ccGlitchPos; ccPos < ccLength; ccPos += ccPosDiff)
{
if (result == SfStatus.Success)
{
SfAudioSelection aselAll = new SfAudioSelection(ccGlitchPos,-1);//Select the whole audio waveform (0 = start, -1 = end)
Int64 ccAfter = fileIn.FindAudioGlitch(aselAll, 0.1, 5);//these settings can be adjusted dependent on type of glitch
result = fileIn.WaitForDoneOrCancel();
ccEnd = ccAfter + ccRange;//set the interpolation window
SfAudioSelection GlitchReg = new SfAudioSelection(ccAfter, ccEnd);//Create the Interpolation window
app.DoMenuAndWait ("Tools: Repair Interpolate", true);
ccGlitchPos = ccAfter;//hold the present glitch position to use as the start of the next FindAudio Glitch
ccPosDiff = ccAfter - ccPos;//Calculate the difference between the new glitch and the previous one
if (ccPosDiff == 0)//Loop until no further glitches found
{
break;
}
fileIn.Window.SetCursorAndScroll(ccAfter, DataWndScrollTo.Nearest);

DPF("after {0} ccPos {1} PosDiff {2}", ccAfter, ccPos, ccPosDiff);
}
else
{
break;
}
}


First find the glitch, then make a selection containing the glitch, then invoke DoMenu("Tools: Repair Interpolate"), advance past the glitch and loop back to find the next one.

But this line sequence:

SfAudioSelection GlitchReg = new SfAudioSelection(ccAfter, ccEnd);//Create the Interpolation window
app.DoMenuAndWait ("Tools: Repair Interpolate", true);


Doesn't do what you seem to think. The SfAudioSelection called GlitchReg contains the interpolation window, but that selection is never passed to "Repair Interpolate", Instead "Interpolate" uses the current cursor postion and/or selection from the file.Window object. (All DoMenu/DoMenuAndWait calls use the current time selection from the Window object.).

But this script doesnt get around to changing the cursor position in the file.Window object until AFTER it has called DoMenuAndWait, so what this script ends up doing is find the first glitch, fix nothing (unless the first glitch happes to be where the cursor position starts out), then move the cursor to the first glitch, find the second glitch, fix the first glitch, etc...

There are two ways to solve this. The simplest is to just move the fileIn.Window.SetCursorAndScroll(ccAfter, DataWndScrollTo.Nearest); call up until it is before the DoMenuAndWait call.

A better solution is to use file.DoEffect("Interpolate", ...) which lets you pass in the selection range for the glitch. Then there is no need to move the cursor at all.

If you do the second, I recommend that you use a smaller window, the default window for Interpolate looks to be about 0.3 millisec, or about 17 samples at 44.1Khz. Also the window shouldn't be centered on the glitch, but should instead be about 1/4 before and 3/4 after. That will give you behavior equivalent to what you are getting now using DoMenuAndWait("Tools: Repair Interpolate", ...);

tj

Subject:RE: Glitch Removal Script
Reply by: timaddy
Date:11/7/2006 6:25:10 AM

Hi TJ,

Thanks for the info. Very useful have updated the script as below and it works well for the first method you suggested.


for (Int64 ccPos = ccGlitchPos; ccPos < ccLength; ccPos += ccPosDiff)
{
if (result == SfStatus.Success)
{
SfAudioSelection aselAll = new SfAudioSelection(ccGlitchPos,-1);//Select the whole audio waveform (0 = start, -1 = end)
Int64 ccAfter = fileIn.FindAudioGlitch(aselAll, 0.1, 5);
result = fileIn.WaitForDoneOrCancel();
ccStart = ccAfter - (ccRange/4);
ccEnd = ccAfter + (ccRange*(3/4));//set the interpolation window

SfAudioSelection GlitchReg = new SfAudioSelection(ccStart, ccEnd);//Create the Interpolation window
fileIn.Window.SetSelectionAndScroll(ccStart, ccRange, DataWndScrollTo.Nearest);
//fileIn.DoEffect("Interpolate", "[Sys] Find Clipping" );
app.DoMenuAndWait ("Tools: Repair Interpolate", true);
ccGlitchPos = ccAfter;//hold the present glitch position to use as the start of the next FindAudio Glitch
ccPosDiff = ccAfter - ccPos;//Calculate the difference between the new glitch and the previous one
if (ccPosDiff == 0)//Loop until no further glitches found
{
break;
}

DPF("Glitch Detected at {0} Glitch Window Start {1} Glitch Window End {2}", ccAfter, ccStart, ccEnd);
}
else
{
break;
}
}


Tim

Message last edited on11/8/2006 6:50:21 AM bytimaddy.
Subject:RE: Glitch Removal Script
Reply by: timaddy
Date:11/8/2006 3:54:04 AM

Hi TJ,

I've tried the methods that you mentioned. The method using fileIn.Window.SetCursorAndScroll(ccAfter, DataWndScrollTo.Nearest); works correctly but using the file.DoEffect("Interpolate", ....) method I cannot get a range passed into the Interpolate effect.

Could you explain exactly how to pass a range into the Interpolate function? I've tried passing GlitchReg and a number of other options but I keep getting the following error:

"No overload for method 'DoEffect' takes '2' arguments

From the documentation it appears that all you can pass to Interpolate is a preset name?

Tim

Subject:RE: Glitch Removal Script
Reply by: _TJ
Date:11/10/2006 2:33:05 PM

Actually DoEffect() takes 4 arguments, the effect name (in this case "Interpolate"); the preset name (in this case 0, since Interpolate has no presets), the selection, and a set of flags that control operation.

try this


fileIn.DoEffect("Interpolate", 0, GlitchReg, EffectOptions.EffectOnly);
result = fileIn.WaitForDoneOrCancel();


tj


Message last edited on11/10/2006 2:33:59 PM by_TJ.

Go Back