Community Forums Archive

Go Back

Subject:File Merging Script
Posted by: roblesinge
Date:7/7/2010 6:40:09 PM

I'm working on a file merger script, and thought I would start a new post about it. I had bumped a very old thread, and I'm thinking a new thread would be more appropriate.

I'm trying to open a specific number of files (wav) in a folder and merge them into one MP3 file. At this point, I just need to be able to do 50 files at a time, but ideally I'd like to be able to choose how many files to merge via user input. The next step would be to add a user specified amount of silence in between each file. This is a lot for one batch, but I think it's all do-able.

So, I thought I would start by pulling parts of the "access all files in a folder" script, but I'm not sure how to morph that into "just a certain number of files in the folder." Is it possible to do only a certain number of files at a time? To make sure I'm clear, I have thousands of files in a folder, I want to take them 50 at a time and merge those 50 into an MP3.

I did find some code for a file merger on the forums, but it only works on specific files (hard-coded into the script). I'm digging through the SDK, trying to figure out what I need to make this work. Can someone point me toward some concepts I should be considering?


Message last edited on7/23/2010 2:52:24 PM byroblesinge.
Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/22/2010 1:20:37 PM

Okay, it's obvious to me that I'm way out of my depth here. I've been hacking on this for a while and I've run out of time for this afternoon. I thought I'd post my code and see if anyone can shed some light. I'm getting a divide by zero exception somehow. I'm sure there is a TON more wrong with this script. I didn't do a whole lot of error handling at all. I basically adapted code from other scripts I've made, so there is a bunch of stuff in here that probably doesn't need to be there (I've commented out a lot of it). I'm sure this is more of a mess than I think, but here goes.

Again, the objective: I want to take all of the wav files in a folder and merge 50 of them at a time into an MP3 file. I want to try and insert a user determined amount of silence in between each file as I'm merging them and then save the whole thing to an MP3 file.

I've tried to use an array and some nested loops to accomplish the 50 at a time thing. It compiles okay, and runs through the user input stage, but then I get the error.



edited to save space in post


Thanks for the help!
--Rob.

Message last edited on7/23/2010 2:54:02 PM byroblesinge.
Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/22/2010 5:44:42 PM

Okay, so I'm not crazy. After putting in some break points, I've debugged it down to this. Turns out my initial string to int conversions weren't working. Had to change them up to Convert.ToInt16().

Now, I'm having trouble with my RenderAs.

Fixed this part too... keep reading.



Thanks!
--Rob.

Message last edited on7/23/2010 2:55:01 PM byroblesinge.
Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/23/2010 2:06:22 PM

Okay, I figured out my problems. I've gotta stop using <= when I'm dealing with arrays that start at 0. Anyway, I've now added in the part that deals with the leftovers of my batches of 50 (or whatever is chosen). Now, I've got a whole new error to work through.





using System;

using System.IO;

using System.Windows.Forms;

using SoundForge;



public class EntryPoint
{

public void Begin(IScriptableApp app)
{
//Gets number of files to merge at a time, and amt of silence btw them.

string strMergeNum = SfHelpers.WaitForInputString("How many files do you want to merge at a time?", "50");
int mergeNum = Convert.ToInt16(strMergeNum);

if (mergeNum == 0)
{

DPF("You must specify a number greater than 0");

return;

}

string strAmtSilence = SfHelpers.WaitForInputString("How much silence btw files (in SECONDS) use 0 for no silence", "2");
double amtSilence = Convert.ToDouble(strAmtSilence);



//--------------
//Gets the folder with the merge files and counts them to int fileCount
//Then sets the loopCount by dividing the fileCount by the mergeNum
//Then takes care of remainder for final paste-to with leftOver

string strFolder = SfHelpers.ChooseDirectory("Choose a folder to process files from", @"S:\Audio");
string targFolder = SfHelpers.ChooseDirectory("Where do you want to save the merged MP3?", @"S:\Audio");
string targFileName = SfHelpers.WaitForInputString("Enter the prefix for your MP3 filename (i.e. PR# or Preview", "Preview_");

System.IO.DirectoryInfo d = new System.IO.DirectoryInfo(strFolder);
int fileCount;
fileCount = d.GetFiles().Length;

int loopCount = (int)(fileCount / mergeNum);
int leftOver = fileCount % mergeNum;

ISfRenderer rend = app.FindRenderer("MP3Audio", "MP3");
ISfGenericPreset preset = rend.GetTemplate("Default Template");

//Create a blank merge file for pasting
//ISfFileHost mergedFile = app.NewFile(null, true);
//moved this inside the loop so a new file would open each time through


//Creates an array to hold filenames for the paste operation
string[] fileHolder;
fileHolder = Directory.GetFiles(strFolder);

int carryO = 0;
int y = 0;
int z = mergeNum;
for (int x = 0; x < loopCount; x++)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost syncStat = app.OpenFile(fileHolder[0], true, true);
if (null == syncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

//creates a new file (mergedFile) for us to paste into.
ISfFileHost mergedFile = app.NewFile(syncStat.DataFormat, true);

while (y < z)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
mergedFile.OverwriteAudio(mergedFile.Length, 0, temp, new SfAudioSelection(temp));

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = mergedFile.SecondsToPosition(amtSilence);
mergedFile.InsertSilence(mergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

syncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string fileNumber = x.ToString();
string fileName1 = (targFileName + fileNumber + ".mp3");
string strFileName = Path.Combine(targFolder, fileName1);
DPF("The filename is {0}", strFileName);

mergedFile.RenderAs(strFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);
//mergedFile.Close(CloseOptions.DiscardChanges);
z = z + mergeNum;
carryO = x + 1;
}

for (int ii = 0; ii < fileCount; ii++)
{
fileHolder[ii] = null;
}



//Next section will eventually handle the leftOver files
z = z + leftOver;

//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost lastsyncStat = app.OpenFile(fileHolder[0], true, true);
if (null == lastsyncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}
ISfFileHost lastmergedFile = app.NewFile(lastsyncStat.DataFormat, true);

while (y < z)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
lastmergedFile.OverwriteAudio(lastmergedFile.Length, 0, temp, new SfAudioSelection(temp));

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = lastmergedFile.SecondsToPosition(amtSilence);
lastmergedFile.InsertSilence(lastmergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

lastsyncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string lastfileNumber = carryO.ToString();
string lastfileName1 = (targFileName + lastfileNumber + ".mp3");
string laststrFileName = Path.Combine(targFolder, lastfileName1);
DPF("The filename is {0}", laststrFileName);

lastmergedFile.RenderAs(laststrFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);
//mergedFile.Close(CloseOptions.DiscardChanges);
z = z + mergeNum;

}






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)); }

} //EntryPoint


New Error:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at SoundForge.IScriptableApp.OpenFile(String pszFileName, Boolean fReadOnly, Boolean fNoWindow)
at EntryPoint.Begin(IScriptableApp app)
at EntryPoint.FromSoundForge(IScriptableApp app)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at SoundForge.ScriptHost.ScriptManager.Run(Assembly asm, String className, String methodName)
at SoundForge.ScriptHost.CodeDomScriptManager.Run(Assembly assy)
at SoundForge.ScriptHost.RunScript(String szFile, String szSourceText, EngineType eType, Boolean fCompileOnly, Boolean fDebug)


Also, I'm Still having the issue where each time through the loop it gradually slows down. But, it's workable. Any ideas on what could be causing the slowdown, or how to fix it? I'm assuming it's because I'm not dealing with garbage correctly. Any thoughts?

Thanks,
R.

Subject:RE: File Merging Script
Reply by: ExSonyTJ
Date:7/24/2010 7:32:07 PM

I don't see any code that would prevent you from calling app.OpenFile on something that isn't an audio file. try printing out the filenames using DPF(strFile) and make sure that everything that you pass to app.OpenFile is actually an audio file.

Also, you call Directory.GetFiles twice, it's kind of an expensive function, so you should only call it once.

Move this one up and use it instead of the first one.

string[] fileHolder;
fileHolder = Directory.GetFiles(strFolder);

then add this to get the file count

int fileCount = fileHolder.Length;




Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/25/2010 6:59:50 PM

Excellent. Thanks TJ, that definitely makes more sense. Script is now completely functional. I was doing too much looping. I just adjusted how I ran that "leftOver" loop. Since the leftover file amount will always be a known quantity, I don't have to use the same if-->while structure. Just ran a test on a folder with 557 files and it ran like a charm. It's still a bit slow as it rolls through, but that is probably just to be expected. Now that it's working, I'm going to try and break it with some more improvements. I'd like to make render format user selectable, which should be relatively simple. I'm also going to work on making it a bit more robust.

To that end? TJ, would you have a recommendation on how to ensure only wav files are read into the array? I mean, this is pretty specialized, so I can just tell my people to ALWAYS ensure there aren't any stray files sitting in that folder. However, I'm always interested in making things better.

This should lay the base for any kind of file merger I would ever need to build in the future. Thanks for letting me vent about it here, and for the pointers.

Final working code, in case anyone needs a file merger:



using System;

using System.IO;

using System.Windows.Forms;

using SoundForge;



public class EntryPoint
{

public void Begin(IScriptableApp app)
{
//Gets number of files to merge at a time, and amt of silence btw them.

string strMergeNum = SfHelpers.WaitForInputString("How many files do you want to merge at a time?", "50");
int mergeNum = Convert.ToInt16(strMergeNum);

if (mergeNum == 0)
{

DPF("You must specify a number greater than 0");

return;

}

string strAmtSilence = SfHelpers.WaitForInputString("How much silence btw files (in SECONDS) use 0 for no silence", "2");
double amtSilence = Convert.ToDouble(strAmtSilence);



//--------------
//Gets the folder with the merge files and counts them to int fileCount
//Then sets the loopCount by dividing the fileCount by the mergeNum
//Then takes care of remainder for final paste-to with leftOver

string strFolder = SfHelpers.ChooseDirectory("Choose a folder to process files from", @"S:\Audio");
string targFolder = SfHelpers.ChooseDirectory("Where do you want to save the merged MP3?", @"S:\Audio");
string targFileName = SfHelpers.WaitForInputString("Enter the prefix for your MP3 filename (i.e. PR# or Preview", "Preview_");


//Creates an array to hold filenames for the paste operation
string[] fileHolder;
fileHolder = Directory.GetFiles(strFolder);

int fileCount = fileHolder.Length;


//Checks to make sure the mergeNum is less than the fileCount
if (fileCount < mergeNum)
{
DPF("You do not have " + mergeNum + " files to merge. Please choose a smaller number");
return;
}


//Math to determine the number of loops to go through
int loopCount = (int)(fileCount / mergeNum);
int leftOver = fileCount - (mergeNum * loopCount);


//Gets the renderer template for our MP3 file
ISfRenderer rend = app.FindRenderer("MP3Audio", "MP3");
ISfGenericPreset preset = rend.GetTemplate("Default Template");

//Create a blank merge file for pasting
//ISfFileHost mergedFile = app.NewFile(null, true);
//moved this inside the loop so a new file would open each time through


int carryO = 0;
int y = 0;
int z = mergeNum;
for (int x = 0; x < loopCount; x++)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost syncStat = app.OpenFile(fileHolder[0], true, true);
if (null == syncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

//creates a new file (mergedFile) for us to paste into.
ISfFileHost mergedFile = app.NewFile(syncStat.DataFormat, true);

while (y < z)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
mergedFile.OverwriteAudio(mergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = mergedFile.SecondsToPosition(amtSilence);
mergedFile.InsertSilence(mergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

syncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string fileNumber = x.ToString();
string fileName1 = (targFileName + fileNumber + ".mp3");
string strFileName = Path.Combine(targFolder, fileName1);
DPF("The filename is {0}", strFileName);

mergedFile.RenderAs(strFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);

z = z + mergeNum;
carryO = x + 1;
}


//Next section deals with leftover files


//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost lastsyncStat = app.OpenFile(fileHolder[0], true, true);
if (null == lastsyncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

ISfFileHost lastmergedFile = app.NewFile(lastsyncStat.DataFormat, true);

for (int ii=leftOver; ii > 0; ii--)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
lastmergedFile.OverwriteAudio(lastmergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = lastmergedFile.SecondsToPosition(amtSilence);
lastmergedFile.InsertSilence(lastmergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

lastsyncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string lastfileNumber = carryO.ToString();
string lastfileName1 = (targFileName + lastfileNumber + ".mp3");
string laststrFileName = Path.Combine(targFolder, lastfileName1);
DPF("The filename is {0}", laststrFileName);

lastmergedFile.RenderAs(laststrFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);

}


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)); }

} //EntryPoint


Thanks,
--Rob.

Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/26/2010 1:44:35 PM

Well, I knew it wouldn't last. I've got a new problem. The current script relies on files being in the correct order in the directory. However, Windows lists numbered files differently than I need. If I'm naming by row number as I edit (i.e. 1, 2, 3, 4... 10, 11, 12... 100, 101, 102, etc). They will show up in the directory as 1, 10, 100, 1000, 1001, etc.). So, it won't paste the files in order, because they won't be read into the array in order. The cheap/dirty way around it is to just use a rename program and add zeros to the beginning of the filenames. Is there a way around this inside the script though? Any thoughts?

Thanks,
R.

Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/26/2010 5:08:08 PM

I've come across the Array.Sort() method, and I'm trying to figure out how to sort my array of filenames (string fileHolder[]) by the length of each string inside of it. that should sort them the way I want. I'll keep hacking at it, but if anyone knows how to use Array.Sort, I'd appreciate the pointers.

Thanks,
R.

Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/26/2010 8:10:04 PM

Done. Wrote (well copied from MSDN library website) a method to sort the array of strings by length and then number. I think that should be the last of it.

Final, final, final working code:



using System;

using System.IO;

using System.Windows.Forms;

using SoundForge;
using System.Collections.Generic;


public class EntryPoint
{
//Method to evaluate and sort our string array of filenames
private static int CompareStringLengths(string m, string n)
{
if (m == null)
{
if (n == null)
{
return 0;
}
else
{
return -1;
}
}
else
{
if (n == null)
{
return 1;
}
else
{
int retval = m.Length.CompareTo(n.Length);

if (retval != 0)
{
return retval;
}
else
{
return m.CompareTo(n);
}
}
}
}



public void Begin(IScriptableApp app)
{
//Gets number of files to merge at a time, and amt of silence btw them.

string strMergeNum = SfHelpers.WaitForInputString("How many files do you want to merge at a time?", "50");
int mergeNum = Convert.ToInt16(strMergeNum);

if (mergeNum == 0)
{

DPF("You must specify a number greater than 0");

return;

}

string strAmtSilence = SfHelpers.WaitForInputString("How much silence btw files (in SECONDS) use 0 for no silence", "2");
double amtSilence = Convert.ToDouble(strAmtSilence);


//Gets the folder with the merge files and counts them to int fileCount
//Then sets the loopCount by dividing the fileCount by the mergeNum
//Then takes care of remainder for final paste-to with leftOver

string strFolder = SfHelpers.ChooseDirectory("Choose a folder to process files from", @"S:\Audio");
string targFolder = SfHelpers.ChooseDirectory("Where do you want to save the merged MP3?", @"S:\Audio");
string targFileName = SfHelpers.WaitForInputString("Enter the prefix for your MP3 filename (i.e. PR# or Preview", "Preview_");


//Creates an array to hold filenames for the paste operation
string[] fileHolder;
fileHolder = Directory.GetFiles(strFolder);
foreach (string prntOut in fileHolder)
DPF(prntOut);

DPF("BREAK");
DPF("_____");


//Let's sort the array so our files are pasted in the correct order
Array.Sort(fileHolder, CompareStringLengths);

//Print out our Array to check
foreach (string strTest in fileHolder)
DPF(strTest);

int fileCount = fileHolder.Length;


//Checks to make sure the mergeNum is less than the fileCount
if (fileCount < mergeNum)
{
DPF("You do not have " + mergeNum + " files to merge. Please choose a smaller number");
return;
}


//Math to determine the number of loops to go through
int loopCount = (int)(fileCount / mergeNum);
int leftOver = fileCount - (mergeNum * loopCount);


//Gets the renderer template for our MP3 file
ISfRenderer rend = app.FindRenderer("MP3Audio", "MP3");
ISfGenericPreset preset = rend.GetTemplate("Default Template");


int carryO = 0;
int y = 0;
int z = mergeNum;
for (int x = 0; x < loopCount; x++)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost syncStat = app.OpenFile(fileHolder[0], true, true);
if (null == syncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

//creates a new file (mergedFile) for us to paste into.
ISfFileHost mergedFile = app.NewFile(syncStat.DataFormat, true);

while (y < z)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
mergedFile.OverwriteAudio(mergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = mergedFile.SecondsToPosition(amtSilence);
mergedFile.InsertSilence(mergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

syncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string fileNumber = x.ToString();
string fileName1 = (targFileName + fileNumber + ".mp3");
string strFileName = Path.Combine(targFolder, fileName1);
DPF("The filename is {0}", strFileName);

mergedFile.RenderAs(strFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);

z = z + mergeNum;
carryO = x + 1;
}


//Next section deals with leftover files


//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost lastsyncStat = app.OpenFile(fileHolder[0], true, true);
if (null == lastsyncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

ISfFileHost lastmergedFile = app.NewFile(lastsyncStat.DataFormat, true);

for (int ii=leftOver; ii > 0; ii--)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
lastmergedFile.OverwriteAudio(lastmergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = lastmergedFile.SecondsToPosition(amtSilence);
lastmergedFile.InsertSilence(lastmergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

lastsyncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string lastfileNumber = carryO.ToString();
string lastfileName1 = (targFileName + lastfileNumber + ".mp3");
string laststrFileName = Path.Combine(targFolder, lastfileName1);
DPF("The filename is {0}", laststrFileName);

lastmergedFile.RenderAs(laststrFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);

}


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)); }

} //EntryPoint

Subject:RE: File Merging Script
Reply by: roblesinge
Date:7/29/2010 6:25:02 AM

I'm still not completely happy with the way my Array gets sorted. Does anyone know of a way to make it sort my array fileHolder the way the Windows gui does. For example, when I look at a folder with wav files in it that have been named by row number or an ID number, the files are listed like this:

1, 2, 3, 4, 4_alt, 5, 6, 6_alt...

The sort method that I have does not account for the alternate files that we sometimes have, as they would be sorted to the bottom first with all of those characters.

Any thoughts?

Thanks,
Rob.

Subject:RE: File Merging Script
Reply by: ExSonyTJ
Date:7/31/2010 10:22:56 AM

What you want to do is to compare the filenames by first converting them to numbers, something like this


//Method to evaluate and sort our string array of filenames
private static int CompareStringLengths(string m, string n)
{
if (m == null)
{
if (n == null)
{
return 0;
}
else
{
return -1;
}
}
else
{
if (n == null)
{
return 1;
}
else
{
int mval = Convert.ToInt16(m);
int nval = Convert.ToInt16(n);
int retval = m - n;

if (retval != 0)
{
return retval;
}
else
{
return m.CompareTo(n);
}
}
}
}



Of course, this code works only if all the filenames start with a number.

Subject:RE: File Merging Script
Reply by: roblesinge
Date:8/10/2010 2:39:08 PM

I had to change a line, I think you meant this:


int retval = mval - nval;



I get an error when using this on a folder that contains a file with the filename "10-alt.wav." I think it's choking on the "-alt" part, since those aren't integers. Your post seemed to make it sound like as long as the first part of the filename consisted of numbers, it would work. Any thoughts? Error is as follows:

---------------------------------

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Failed to compare two elements in the array. ---> System.FormatException: Input string was not in a correct format.
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.Int16.Parse(String s, NumberStyles style, NumberFormatInfo info)
at System.Convert.ToInt16(String value)
at EntryPoint.CompareStringLengths(String m, String n)
at System.Array.FunctorComparer`1.Compare(T x, T y)
at System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, IComparer`1 comparer, Int32 a, Int32 b)
at System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer)
at System.Collections.Generic.GenericArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
--- End of inner exception stack trace ---
at System.Collections.Generic.GenericArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
at System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer)
at System.Array.Sort[T](T[] array, IComparer`1 comparer)
at System.Array.Sort[T](T[] array, Comparison`1 comparison)
at EntryPoint.Begin(IScriptableApp app)
at EntryPoint.FromSoundForge(IScriptableApp app)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at SoundForge.ScriptHost.ScriptManager.Run(Assembly asm, String className, String methodName)
at SoundForge.ScriptHost.CodeDomScriptManager.Run(Assembly assy)
at SoundForge.ScriptHost.RunScript(String szFile, String szSourceText, EngineType eType, Boolean fCompileOnly, Boolean fDebug)

------------------------------------

We've been using the previous version of the merge without issue, but I've been careful to avoid any filenames that have letters in them.

Thanks, as always.
Rob.

Subject:RE: File Merging Script
Reply by: ExSonyTJ
Date:8/11/2010 10:14:53 AM

Sorry. What's happening is that Convert.ToInt16 is throwing an exception when it gets handled something that isn't just a number. That's really dumb behavior on the part of MS, (parsers shouldn't throw exeptions). but it is what it is, so you need to work around it.

You can switch to using Int32.TryParse instead. That won't throw exceptions, but i'm not sure that it will give you back the right answer either, a little experiment would tell you. The safest solution would be to split the number part of the string into a separate string and then convert that to an int.

Subject:RE: File Merging Script
Reply by: roblesinge
Date:8/12/2010 12:48:43 PM

Figured it out. I'm calling this a fully functioning version 1.0 of this merger. I ran across an alphanumeric sorting method online, and replaced my original sort method completely.

Final working code:



using System;

using System.IO;

using System.Windows.Forms;

using SoundForge;

using System.Collections;

using System.Collections.Generic;


public class EntryPoint
{
//Method to evaluate and sort our string array of filenames
public class AlphanumComparatorFast : IComparer
{
public int Compare(object x, object y)
{
string s1 = x as string;
if (s1 == null)
{
return 0;
}
string s2 = y as string;
if (s2 == null)
{
return 0;
}

int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;

// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];

// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;

// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;

if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

do
{
space2[loc2++] = ch2;
marker2++;

if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));

// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1);
string str2 = new string(space2);

int result;

if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
int thisNumericChunk = int.Parse(str1);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}

if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}



public void Begin(IScriptableApp app)
{

//Gets the folder with the merge files and counts them to int fileCount
//Then sets the loopCount by dividing the fileCount by the mergeNum
//Then takes care of remainder for final paste-to with leftOver

string strFolder = SfHelpers.ChooseDirectory("Choose a folder to process files from", @"S:\Audio");


//Creates an array to hold filenames for the paste operation
string[] fileHolder;
fileHolder = Directory.GetFiles(strFolder);

//Let's sort the array so our files are pasted in the correct order
Array.Sort(fileHolder, new AlphanumComparatorFast());

//Print out our Array to check
foreach (string strTest in fileHolder)
DPF(strTest);

int fileCount = fileHolder.Length;


//Gets number of files to merge at a time.
string strMergeNum = SfHelpers.WaitForInputString("How many of these " + fileCount + " files do you want per MP3?", "50");
int mergeNum = Convert.ToInt16(strMergeNum);

//Checks to make sure mergeNum is not 0. If it is, it asks for mergeNum again.
if (mergeNum == 0)
{
int mm = 0;
while (mm == 0)
{
string strErrMergeNum = SfHelpers.WaitForInputString("You must merge more than 0 files, try again", "50");
int ErrmergeNum = Convert.ToInt16(strErrMergeNum);
mm = ErrmergeNum;
}

mergeNum = mm;
return;
}
else if (fileCount < mergeNum)
mergeNum = fileCount;


//Gets amt of silence btw files.
string strAmtSilence = SfHelpers.WaitForInputString("How much silence btw files (in SECONDS) use 0 for no silence", "2");
double amtSilence = Convert.ToDouble(strAmtSilence);


//Gets the target folder and the prefix for the merged filenames.
string targFolder = SfHelpers.ChooseDirectory("Where do you want to save the merged MP3?", strFolder);
string targFileName = SfHelpers.WaitForInputString("Enter the prefix for your MP3 filename (i.e. PR# or Preview", "Preview_");


//Math to determine the number of loops to go through
int loopCount = 0;
int leftOver = 0;

if (fileCount == mergeNum)
loopCount = 1;

else
{
loopCount = (int)(fileCount / mergeNum);
leftOver = fileCount - (mergeNum * loopCount);
}


//Gets the renderer template for our MP3 file
ISfRenderer rend = app.FindRenderer("MP3Audio", "MP3");
ISfGenericPreset preset = rend.GetTemplate("Default Template");


int carryO = 0;
int y = 0;
int z = mergeNum;
for (int x = 0; x < loopCount; x++)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost syncStat = app.OpenFile(fileHolder[0], true, true);
if (null == syncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

//creates a new file (mergedFile) for us to paste into.
ISfFileHost mergedFile = app.NewFile(syncStat.DataFormat, true);

while (y < z)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
mergedFile.OverwriteAudio(mergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = mergedFile.SecondsToPosition(amtSilence);
mergedFile.InsertSilence(mergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

syncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string fileNumber = x.ToString();
string fileName1 = (targFileName + fileNumber + ".mp3");
string strFileName = Path.Combine(targFolder, fileName1);
DPF("The filename is {0}", strFileName);

mergedFile.RenderAs(strFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);

z = z + mergeNum;
carryO = x + 1;
}


//Next section deals with leftover files

if (leftOver != 0)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost lastsyncStat = app.OpenFile(fileHolder[0], true, true);
if (null == lastsyncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

ISfFileHost lastmergedFile = app.NewFile(lastsyncStat.DataFormat, true);

for (int ii = leftOver; ii > 0; ii--)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
lastmergedFile.OverwriteAudio(lastmergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = lastmergedFile.SecondsToPosition(amtSilence);
lastmergedFile.InsertSilence(lastmergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

lastsyncStat.Close(CloseOptions.DiscardChanges);

//save mp3 file
string lastfileNumber = carryO.ToString();
string lastfileName1 = (targFileName + lastfileNumber + ".mp3");
string laststrFileName = Path.Combine(targFolder, lastfileName1);
DPF("The filename is {0}", laststrFileName);

lastmergedFile.RenderAs(laststrFileName, "MP3 Audio", preset, null, RenderOptions.AndClose);
}
}


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)); }

} //EntryPoint


Thanks for all of your help with this. I couldn't have done it by myself.

For future versions, I'd love to be able to have the user decide output format. But that will have to wait until I have more spare time.

Thanks,
--Rob.

Subject:RE: File Merging Script
Reply by: wowie
Date:10/29/2014 12:20:59 PM

First of all, THANKS for this awesome script!!

i have no experience with scripting so i just ask,
is there any "easy" way to include regions???

greets from ffm

Subject:RE: File Merging Script
Reply by: roblesinge
Date:10/30/2014 7:36:12 AM

Do you mean have the files save with the region information saved? Just make sure that you have the "Save Metadata" box checked whenever you save a file. That should keep region information. It's been a while since I wrote this script, but I don't see anything in it that would clear regions or markers.

-R.

Subject:RE: File Merging Script
Reply by: Semajnosk
Date:2/5/2015 3:46:30 PM

Just saw that you said you would work on the part of selectable output as you had the time so editing out my original redundant question. Interest is definitely there and with my ever so limited knowledge I'll chip away at it as I can as well. If I come up with anything I'll update.

Thanks for the great work!

James

Message last edited on2/6/2015 3:20:43 PM bySemajnosk.
Subject:RE: File Merging Script
Reply by: roblesinge
Date:2/10/2015 6:36:53 AM

I did finally work that problem out, but it's been a few years and a few machines ago. I believe the code below will allow you to select the output format from a list of renderers. As always, use it with caution until you know what it does.


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

public class EntryPoint
{
//Method to evaluate and sort our string array of filenames
public class AlphanumComparatorFast : IComparer
{
public int Compare(object x, object y)
{
string s1 = x as string;
if (s1 == null)
{
return 0;
}
string s2 = y as string;
if (s2 == null)
{
return 0;
}

int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;

// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];

// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;

// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;

if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

do
{
space2[loc2++] = ch2;
marker2++;

if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));

// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1);
string str2 = new string(space2);

int result;

if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
int thisNumericChunk = int.Parse(str1);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}

if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}

public void Begin(IScriptableApp app)
{
//Gets the folder with the merge files and counts them to int fileCount
//Then sets the loopCount by dividing the fileCount by the mergeNum
//Then takes care of remainder for final paste-to with leftOver

string strFolder = SfHelpers.ChooseDirectory("Choose a folder to process files from", @"S:\Audio");

//Creates an array to hold filenames for the paste operation
string[] fileHolder;
fileHolder = Directory.GetFiles(strFolder, "*.wav");

List<string> filePaths = new List<string>();

foreach (string s in fileHolder)
{
if (s.ToLower().EndsWith("wav"))
filePaths.Add(s);
}

filePaths.Sort(new AlphanumComparatorFast fastSort);

//Let's sort the array so our files are pasted in the correct order
Array.Sort(fileHolder, new AlphanumComparatorFast());

//Print out our Array to check
foreach (string strTest in fileHolder)
DPF(strTest);

int fileCount = fileHolder.Length;

//Gets number of files to merge at a time.
string strMergeNum = SfHelpers.WaitForInputString("How many of these " + fileCount + " files do you want per MP3?", "50");
int mergeNum = Convert.ToInt16(strMergeNum);

//Checks to make sure mergeNum is not 0. If it is, it asks for mergeNum again.
if (mergeNum == 0)
{
int mm = 0;
while (mm == 0)
{
string strErrMergeNum = SfHelpers.WaitForInputString("You must merge more than 0 files, try again", "50");
int ErrmergeNum = Convert.ToInt16(strErrMergeNum);
mm = ErrmergeNum;
}

mergeNum = mm;
return;
}
else if (fileCount < mergeNum)
mergeNum = fileCount;

//Gets amt of silence btw files.
string strAmtSilence = SfHelpers.WaitForInputString("How much silence btw files (in SECONDS) use 0 for no silence", "2");
double amtSilence = Convert.ToDouble(strAmtSilence);

// get renderer and template
int cRend = app.Renderers.Count;
string[] renderers = new string[cRend];
for (int ix = 0; ix < cRend; ix++)
renderers[ix] = app.Renderers[ix].Name;
object item = SfHelpers.ChooseItemFromList("Select destination file type:", renderers);

ISfRenderer rend = app.FindRenderer(item.ToString(), null);
if (null == rend)
DPF("No renderer.");

ISfGenericPreset tpl = rend.ChooseTemplate(IntPtr.Zero, 0);
if (null == tpl)
DPF("No template.");

//Gets the target folder and the prefix for the merged filenames.
string targFolder = SfHelpers.ChooseDirectory("Where do you want to save the merged " + rend.Extension + " file?", strFolder);
string targFileName = SfHelpers.WaitForInputString("Enter the prefix for your " + rend.Extension + " filename (i.e. PR# or Preview", "Preview_");

//Math to determine the number of loops to go through
int loopCount = 0;
int leftOver = 0;

if (fileCount == mergeNum)
loopCount = 1;

else
{
loopCount = (int)(fileCount / mergeNum);
leftOver = fileCount - (mergeNum * loopCount);
}

//Here is the part where we start the Merge!
int carryO = 0;
int y = 0;
int z = mergeNum;
for (int x = 0; x < loopCount; x++)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost syncStat = app.OpenFile(fileHolder[0], true, false);
if (null == syncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

//creates a new file (mergedFile) for us to paste into.
ISfFileHost mergedFile = app.NewFile(syncStat.DataFormat, true);

while (y < z)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
mergedFile.OverwriteAudio(mergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

//adds a marker to label each part of the mergedFile with filename
long mPos = mergedFile.Length - temp.Length;
string mName = Path.GetFileNameWithoutExtension(fileHolder[y]);
mergedFile.Markers.AddMarker(mPos, mName);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = mergedFile.SecondsToPosition(amtSilence);
mergedFile.InsertSilence(mergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

syncStat.Close(CloseOptions.DiscardChanges);

//save merged file
string fileNumber = x.ToString();
string fileName1 = (targFileName + fileNumber + "." + rend.Extension);
string strFileName = Path.Combine(targFolder, fileName1);
DPF("The filename is {0}", strFileName);

mergedFile.RenderAs(strFileName, null, tpl, null, RenderOptions.AndClose);

z = z + mergeNum;
carryO = x + 1;
}

//Next section deals with leftover files

if (leftOver != 0)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost lastsyncStat = app.OpenFile(fileHolder[0], true, true);
if (null == lastsyncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

ISfFileHost lastmergedFile = app.NewFile(lastsyncStat.DataFormat, true);

for (int ii = leftOver; ii > 0; ii--)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
lastmergedFile.OverwriteAudio(lastmergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

//adds a marker to label each part of the mergedFile with filename
long mPos = lastmergedFile.Length - temp.Length;
string lName = Path.GetFileNameWithoutExtension(fileHolder[y]);
lastmergedFile.Markers.AddMarker(mPos, lName);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = lastmergedFile.SecondsToPosition(amtSilence);
lastmergedFile.InsertSilence(lastmergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

lastsyncStat.Close(CloseOptions.DiscardChanges);

//save merged file
string lastfileNumber = carryO.ToString();
string lastfileName1 = (targFileName + lastfileNumber + "." + rend.Extension);
string laststrFileName = Path.Combine(targFolder, lastfileName1);
DPF("The filename is {0}", laststrFileName);

lastmergedFile.RenderAs(laststrFileName, null, tpl, null, RenderOptions.AndClose);
}
}

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, params object[] args) { ForgeApp.OutputText(String.Format(fmt, args)); }
} //EntryPoint


R.

Subject:RE: File Merging Script
Reply by: Semajnosk
Date:2/10/2015 11:04:39 AM

I tried the script this morning and it wasn't working. I assumed (dangerous with scripts.. i know) the script should be pretty similar to the working script but for the rendering part. I compared the two and noticed that the section to 'create an array to hold filenames for the paste operation' were significantly different. Tried my luck and patching that code up with the corresponding code from the working script and it seems to work.

Worked for me but use with caution as I'm very noobish with this whole scripting bit. Thanks again Rob, couldn't have put this throw rug down if you hadn't built the whole house first. If anyone sees I've done something foolish... please let me know, I want to learn!

"Working" script:


using System;

using System.IO;

using System.Windows.Forms;

using SoundForge;

using System.Collections;

using System.Collections.Generic;


public class EntryPoint
{
//Method to evaluate and sort our string array of filenames
public class AlphanumComparatorFast : IComparer
{
public int Compare(object x, object y)
{
string s1 = x as string;
if (s1 == null)
{
return 0;
}
string s2 = y as string;
if (s2 == null)
{
return 0;
}

int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;

// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];

// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;

// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;

if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

do
{
space2[loc2++] = ch2;
marker2++;

if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));

// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1);
string str2 = new string(space2);

int result;

if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
int thisNumericChunk = int.Parse(str1);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}

if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}

public void Begin(IScriptableApp app)
{
//Gets the folder with the merge files and counts them to int fileCount
//Then sets the loopCount by dividing the fileCount by the mergeNum
//Then takes care of remainder for final paste-to with leftOver

string strFolder = SfHelpers.ChooseDirectory("Choose a folder to process files from", @"S:\Audio");

//Creates an array to hold filenames for the paste operation
string[] fileHolder;
fileHolder = Directory.GetFiles(strFolder);

//Let's sort the array so our files are pasted in the correct order
Array.Sort(fileHolder, new AlphanumComparatorFast());

//Print out our Array to check
foreach (string strTest in fileHolder)
DPF(strTest);

int fileCount = fileHolder.Length;

//Gets number of files to merge at a time.
string strMergeNum = SfHelpers.WaitForInputString("How many of these " + fileCount + " files do you want per MP3?", "50");
int mergeNum = Convert.ToInt16(strMergeNum);

//Checks to make sure mergeNum is not 0. If it is, it asks for mergeNum again.
if (mergeNum == 0)
{
int mm = 0;
while (mm == 0)
{
string strErrMergeNum = SfHelpers.WaitForInputString("You must merge more than 0 files, try again", "50");
int ErrmergeNum = Convert.ToInt16(strErrMergeNum);
mm = ErrmergeNum;
}

mergeNum = mm;
return;
}
else if (fileCount < mergeNum)
mergeNum = fileCount;

//Gets amt of silence btw files.
string strAmtSilence = SfHelpers.WaitForInputString("How much silence btw files (in SECONDS) use 0 for no silence", "2");
double amtSilence = Convert.ToDouble(strAmtSilence);

// get renderer and template
int cRend = app.Renderers.Count;
string[] renderers = new string[cRend];
for (int ix = 0; ix < cRend; ix++)
renderers[ix] = app.Renderers[ix].Name;
object item = SfHelpers.ChooseItemFromList("Select destination file type:", renderers);

ISfRenderer rend = app.FindRenderer(item.ToString(), null);
if (null == rend)
DPF("No renderer.");

ISfGenericPreset tpl = rend.ChooseTemplate(IntPtr.Zero, 0);
if (null == tpl)
DPF("No template.");

//Gets the target folder and the prefix for the merged filenames.
string targFolder = SfHelpers.ChooseDirectory("Where do you want to save the merged " + rend.Extension + " file?", strFolder);
string targFileName = SfHelpers.WaitForInputString("Enter the prefix for your " + rend.Extension + " filename (i.e. PR# or Preview", "Preview_");

//Math to determine the number of loops to go through
int loopCount = 0;
int leftOver = 0;

if (fileCount == mergeNum)
loopCount = 1;

else
{
loopCount = (int)(fileCount / mergeNum);
leftOver = fileCount - (mergeNum * loopCount);
}

//Here is the part where we start the Merge!
int carryO = 0;
int y = 0;
int z = mergeNum;
for (int x = 0; x < loopCount; x++)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost syncStat = app.OpenFile(fileHolder[0], true, false);
if (null == syncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

//creates a new file (mergedFile) for us to paste into.
ISfFileHost mergedFile = app.NewFile(syncStat.DataFormat, true);

while (y < z)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
mergedFile.OverwriteAudio(mergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

//adds a marker to label each part of the mergedFile with filename
long mPos = mergedFile.Length - temp.Length;
string mName = Path.GetFileNameWithoutExtension(fileHolder[y]);
mergedFile.Markers.AddMarker(mPos, mName);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = mergedFile.SecondsToPosition(amtSilence);
mergedFile.InsertSilence(mergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

syncStat.Close(CloseOptions.DiscardChanges);

//save merged file
string fileNumber = x.ToString();
string fileName1 = (targFileName + fileNumber + "." + rend.Extension);
string strFileName = Path.Combine(targFolder, fileName1);
DPF("The filename is {0}", strFileName);

mergedFile.RenderAs(strFileName, null, tpl, null, RenderOptions.AndClose);

z = z + mergeNum;
carryO = x + 1;
}

//Next section deals with leftover files

if (leftOver != 0)
{
//get a reference (syncStat) for the specs of new file (mergedFile).
ISfFileHost lastsyncStat = app.OpenFile(fileHolder[0], true, true);
if (null == lastsyncStat)
{

DPF("Could not open {0}", fileHolder[0]);

return;

}

ISfFileHost lastmergedFile = app.NewFile(lastsyncStat.DataFormat, true);

for (int ii = leftOver; ii > 0; ii--)
{
//paste to mergedFile with fileHolder[y]
ISfFileHost temp = app.OpenFile(fileHolder[y], true, false);
lastmergedFile.OverwriteAudio(lastmergedFile.Length, 0, temp, new SfAudioSelection(temp));
DPF(fileHolder[y]);

//adds a marker to label each part of the mergedFile with filename
long mPos = lastmergedFile.Length - temp.Length;
string lName = Path.GetFileNameWithoutExtension(fileHolder[y]);
lastmergedFile.Markers.AddMarker(mPos, lName);

if (amtSilence != 0)
{
//Inserts requested silence (amtSilence) onto the end of mergedFile
long secToSamp = lastmergedFile.SecondsToPosition(amtSilence);
lastmergedFile.InsertSilence(lastmergedFile.Length, secToSamp);
}

temp.Close(CloseOptions.DiscardChanges);
y++;
}

lastsyncStat.Close(CloseOptions.DiscardChanges);

//save merged file
string lastfileNumber = carryO.ToString();
string lastfileName1 = (targFileName + lastfileNumber + "." + rend.Extension);
string laststrFileName = Path.Combine(targFolder, lastfileName1);
DPF("The filename is {0}", laststrFileName);

lastmergedFile.RenderAs(laststrFileName, null, tpl, null, RenderOptions.AndClose);
}
}

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, params object[] args) { ForgeApp.OutputText(String.Format(fmt, args)); }
} //EntryPoint

Message last edited on2/11/2015 9:14:52 AM bySemajnosk.

Go Back