Hi,
I wrote a script some time ago which fixes glitches on wav files when the start and width of the glitches are known. Recently we've noticed a few problems with crashing and once in a while with a directory path being corrupted. The program works by passing in 4 parameters which are:
the directory path
the wav filename
the start position in samples of an audio glitch
the width of the audio glitch
when this program is run in a loop on a multiprocessor (2 CPU or 4 CPU Pentium xeons) then the program crashes (it completes the glitch fixing/interpolation operation but generates a soundforge crash window). When the exact same code is run on a single CPU machine with no hyperthreading (3GHz pentium) then there are no problems with crashing or (so far) path corruption. I have also run this on Soundforge 8 and 9 with the same results.
The program generates log files from soundforge and the logs are all fine (the glitch fix script is fine and we also pass in a close script. to close soundforge.exe down. See the scripts below.
The glitch script code using external parameters is:
//*****************************************************************************
//* Start of Code. *
//*****************************************************************************
using System;
using System.IO;
using System.Windows.Forms;
using SoundForge;
public class EntryPoint {
public void Begin(IScriptableApp app) {
string szPath = GETARG("Dir", ""); //Directory input argument. Type "" to leave blank
string szFilename = GETARG("File", "");//Filename input argument
int szStart = GETARG("glitchstart",1);//Input parameter 1 that defines the start point of the glitch
int szWidth = GETARG("glitchwidth",2);//Input parameter 2 that defines the width of the glitch to be processed
SfStatus result = SfStatus.Success;
//Add in a check that Dir has been set (if not set a default value)
if (null == szPath)
{
szPath = @"C:\";
DPF("NO DIRECTORY PARAMETER HAS BEEN SET AS AN INPUT PARAMETER. USE /Script:<path_to_script?Dir=<dir_path> WHEN CALLING SF");
}
string szWavFile = Path.Combine(szPath, szFilename) + ".wav";//concatenated filename based on the path, filename and .wav
DPF("{0} check the WAV File variable contents", szWavFile);
//#######################################################################
//Create the output file path and name to write the WAV output and log file
//#######################################################################
string szWavOutFile = szFilename;//output filename
string szOPDir = szPath + "\\dr\\";
if ( ! Directory.Exists(szOPDir))
{
Directory.CreateDirectory(szOPDir); // make sure that the directory exists...
}
//####################################################################
//check that the output file & log file doesn't already exist.
//If it does then add a number "_<ccFileExt>" to the name
//create the full path to the output directory
//####################################################################
szWavOutFile = Path.Combine(szOPDir, szWavOutFile) + ".wav";//adds extra output directory to each file
string szWavOutLogFile = Path.GetFileNameWithoutExtension(szWavOutFile);
if (File.Exists(szWavOutFile))
{
szWavOutFile = (Path.Combine(szOPDir, (Path.GetFileNameWithoutExtension(szWavFile))) + ".wav");
szWavOutLogFile = Path.GetFileNameWithoutExtension(szWavOutFile);
}
else
{
result = SfStatus.Fail;
}
DPF("Output filename and directory debug {0}", szWavOutFile);
szWavOutLogFile = Path.Combine(szOPDir, szWavOutLogFile) + ".log";//adds extra output directory (\\dr\\ to each file
StreamWriter sw = new StreamWriter(szWavOutLogFile);
if ( ! File.Exists(szWavFile))
{
sw.WriteLine("####################################################################################");
sw.WriteLine("Error");
sw.WriteLine("Warning: Input File Does Not Exist with name : \n {0}", szWavFile);
sw.WriteLine("####################################################################################");
result = SfStatus.Fail;
}
//###########################################################################################
// Write out the Input Directory path that is passed to Soundforge to check it's ok
//###########################################################################################
sw.WriteLine(" Input Directory Path from DIR parameter = {0}", szPath);
//############################################################################################
//check that the start and width parameters are not 0 (if they are then generate an exception)
//############################################################################################
if (szStart == 0)
{
sw.WriteLine("####################################################################################");
sw.WriteLine("Error: Glitch Removal Process Failed");
sw.WriteLine("fatal: An Error has occurred due to the start parameter having a value of 0");
sw.WriteLine("####################################################################################");
result = SfStatus.Fail;
sw.Close();//close the log file
}
else
{
sw.WriteLine("glitch start parameter has a valid value of {0}", szStart);
}
//###########################################################################################
//check that the glitch width parameter is valid
//###########################################################################################
if (szWidth == 0)
{
sw.WriteLine("####################################################################################");
sw.WriteLine("Error: Glitch Removal Process Failed");
sw.WriteLine("fatal: An Error has occurred due to the Width parameter having a value of 0");
sw.WriteLine("####################################################################################");
result = SfStatus.Fail;
sw.Close();//close the log file
}
else
{
sw.WriteLine("Glitch width parameter has a valid value of {0}", szWidth);
}
//process the file using the audio restoration effect
result = ProcessFile(app, szWavFile, szWavOutFile, szWavOutLogFile, sw, szStart, szWidth);
DPF("~ - {0}", result);
if (result != SfStatus.Success)
{
sw.WriteLine("####################################################################################");
sw.WriteLine("Error: Glitch Removal Process Failed");
sw.WriteLine("fatal: An Error has occurred whilst processing the audio file");
sw.WriteLine("####################################################################################");
sw.Close();//close the log file
}
else
{
sw.WriteLine(" ");
sw.WriteLine("####################################################################################");
sw.WriteLine("Glitch Removal Completed successfully");
sw.WriteLine("####################################################################################");
}
sw.Close();//close the log file
} // Begin
//Function used to process the WAV input file and call the audio glitch removal function
public SfStatus ProcessFile(IScriptableApp app, string szWavFile, string szWavOutFile, string szWavOutLogFile, StreamWriter sw,int szStart, int szWidth)
{
SfStatus result = SfStatus.Success;
try
{
ISfFileHost fileIn = app.OpenFile(szWavFile, false, true);//not read-only, do not allow soundforge window
//Check that the file is not empty, if it is then set the status to fail
//Check that the file data length is 8 or more samples (minimum window size for glitch removal)
if ((null == fileIn) | (fileIn.Length <= 8)) {
sw.WriteLine("####################################################################################");
sw.WriteLine("Error: ");
sw.WriteLine("fatal: Input file is Empty");
sw.WriteLine("####################################################################################");
return SfStatus.Fail;
}
//Restore the present file using the built in glitch removal function
Int64 ccLength = fileIn.Length;
Int64 ccGlitchPos = 0;
Int64 ccPosDiff = 1;//Sample Position Difference between glitches
Int64 ccEnd = 0;//End of Interpolation window sample value
ccEnd = szStart + szWidth;
//Set the audio selection region
SfAudioSelection AllAudio = new SfAudioSelection(szStart,ccEnd);//Select the whole audio waveform (0 = start, -1 = end)
DPF("Value of szStart = {0} ", szStart);
DPF("Value of ccEnd = {0} ", ccEnd);
//undertake glitch removal
ccGlitchPos = szStart;//set the start glitch position
DPF("RUNNING FIRST PASS GLITCH REMOVAL");
sw.WriteLine("RUNNING FIRST PASS GLITCH REMOVAL");
//FIRST PASS GLITCH REMOVAL = loop until no more glitches are found
for (Int64 ccPos = szStart; ccPos < ccLength; ccPos += ccPosDiff)
{
if (result == SfStatus.Success)
{
SfAudioSelection GlitchReg = new SfAudioSelection(szStart, szWidth);//Create the Interpolation window
DPF("Value of szStart in First Pass = {0} ", szStart);
DPF("Value of ccEnd in first Pass= {0} ", ccEnd);
sw.WriteLine("Valud of szStart = {0}", szStart);
sw.WriteLine("Valud of ccEnd = {0}", ccEnd);
//select the audio selection from the start sample point of the glitch and the end point based on the glitch width + start
SfAudioSelection asel = new SfAudioSelection(szStart, ccEnd);
fileIn.Window.SetSelectionAndScroll(szStart, szWidth, DataWndScrollTo.Nearest);//added by TPA to move window
app.DoMenuAndWait ("Tools: Repair Interpolate", true);
//remove 1 from posDiff (this allows looping based on the default value of ccPosDiff
ccPosDiff = ccPosDiff - 1;
if (ccPosDiff == 0)//Loop until no further glitches found
{
DPF("PARAMETER BASED GLITCH REMOVAL COMPLETED");
break;
}
}
else
{
DPF("ERROR HAS OCCURRED DURING FIRST PASS LOOP. SFStatus is not .Success. Either File Loading Error or Glitch Detection Error");
sw.WriteLine("####################################################################################");
sw.WriteLine("Error: ");
sw.WriteLine("Warning: Problem has occurred during FIRST PASS LOOP. SFStatus is not .Success. Either File Loading Error or Glitch Detection Error");
sw.WriteLine("####################################################################################");
result = SfStatus.Fail;
break;
}
}//end of for loop
//##############################################################################
//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.RenderAs(szWavOutFile, fileOut.SaveFormat.Guid, 0, new SfAudioSelection(fileOut), RenderOptions.RenderOnly);
result = fileOut.WaitForDoneOrCancel();
//#############################################################################
//Write out the audio file statistics and the audio restore info
//#############################################################################
//####################################################################
//Get the Statistics for the input and output files
//####################################################################
sw.WriteLine("####################################################################################");
sw.WriteLine("Input File Data Format & File Length");
sw.WriteLine(" Sample Rate, SampleType, Number of Channels = (" + fileIn.SampleRate + "," + fileIn.SampleType + "," + fileIn.Channels + ")");
sw.WriteLine("Input File Length (samples) = {0}", fileIn.Length);
sw.WriteLine("####################################################################################");
sw.WriteLine("Output File Data Format & File Length");
sw.WriteLine(" Sample Rate, SampleType, Number of Channels = (" + fileOut.SampleRate + "," + fileOut.SampleType + "," + fileOut.Channels + ")");
sw.WriteLine("Output File Length (samples) = {0}", fileOut.Length);
sw.WriteLine("####################################################################################");
fileIn.Close(CloseOptions.DiscardChanges);//close the input file
fileOut.Close(CloseOptions.DiscardChanges);//close the output file
}
catch(Exception e)
{
sw.WriteLine("####################################################################################");
sw.WriteLine("Error:");
sw.WriteLine("The following exception was caught:\n{0}", e);
sw.WriteLine("####################################################################################");
return SfStatus.Fail;
}
return result;
}
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
The close process script uses the method described in this forum to close a process:
Process curr = Process.GetCurrentProcess();
curr.CloseMainWindow();:
As an example to show the problem (the exact code is all built as part of a C++ project ) but the same problem occurs using a dos batch file. The code below shows how the parameters are passed in to soundforge:
"c:\Program Files\Sony\Sound Forge 8.0\Forge80.exe" /NoDefSession /NoLogo /Script:"<path_to_glitchfixscript>\Audio_Glitch_removal_extparam_.cs"?dir="<path_audio_arc>\audio_src&File="wav_input_filename"&glitchstart=18&glitchwidth=18" /Script:"<path_to_close_script>\close_sf.cs"
The actual batch file run the above soundforge call in a loop multiple times.
Are there any known issues with Hyperthreading? Do you have any ideas as to whether path corruption is a known problem or can occur when passing external paths in to soundforge? Is soundforge 8 or 9 designed to work on hyperthreading enabled machines?
Thanks for any help
Regards
Tim
Some addition details for the path corruption problem:
I've been running tests and the soundforge debug info that appears when the path gets corrupted is enclosed as below:
System.TypeInitializationException: The type initializer for "Microsoft.CSharp.CSharpCodeGenerator" threw an exception. ---> System.InvalidProgramException: Common Language Runtime detected an invalid program.
at Microsoft.CSharp.CSharpCodeGenerator..cctor()
--- End of inner exception stack trace ---
at Microsoft.CSharp.CSharpCodeGenerator..ctor()
at Microsoft.CSharp.CSharpCodeProvider..ctor()
at SoundForge.CodeDomScriptManager..ctor(EngineType eType)
at SoundForge.ScriptHost.RunScript(String szFile, String szSourceText, EngineType eType, Boolean fCompileOnly, Boolean fDebug)
System.TypeInitializationException: The type initializer for "Microsoft.CSharp.CSharpCodeGenerator" threw an exception. ---> System.InvalidProgramException: Common Language Runtime detected an invalid program.
at Microsoft.CSharp.CSharpCodeGenerator..cctor()
--- End of inner exception stack trace ---
at Microsoft.CSharp.CSharpCodeGenerator..ctor()
at Microsoft.CSharp.CSharpCodeProvider..ctor()
at SoundForge.CodeDomScriptManager..ctor(EngineType eType)
at SoundForge.ScriptHost.RunScript(String szFile, String szSourceText, EngineType eType, Boolean fCompileOnly, Boolean fDebug)
The file C:\Apps_local\V303\Files\Sony\Sound could not be opened.
The file C:\Apps_local\V303\Forge could not be opened.
The file C:\Apps_local\V303\8.0\Forge80.exe could not be opened.
The input file path was not C:\Apps_local\V303\Files\Sony\Sound when set by the input parameter.
This problem is urgent so if someone could have a look at the message above that would be greatly appreciated.
Regards
Tim Addy