Community Forums Archive

Go Back

Subject:Glue lots of WAV files together :)
Posted by: bejj
Date:11/30/2012 3:39:17 PM

Hello,

What's the fastest way for concatenating lots of WAV files together ?
For example, concatenate all WAV files that are in C:\TEMP\ ?

I'm sure such a tool / such a script already exists for Soundforge !
:)

Thanks!

Subject:RE: Glue lots of WAV files together :)
Reply by: roblesinge
Date:11/30/2012 6:14:44 PM

You'll be shocked to find out that there isn't one already available. I wrote a couple of different batch merge scripts. This one I call a Q and A merge. It takes all of the files in a user selected folder and then merges them together in a new file, with 500 milliseconds of silence between each file and then saves this Q/A file to the parent directory of the selected folder. It could be adapted to do just a simple merge with no silence.


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

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

//gets our output directory by going up one folder from the files.
DirectoryInfo dirNew = new DirectoryInfo(strFolder);
DirectoryInfo parFolder = dirNew.Parent;

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

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

int fileCount = fileHolder.Length;

//Sets amt of silence btw files.
double amtSilence = .5;

//Sets the target filename.
string targFileName = "_QA_File.wav";


//Here is the part where we start the Merge!

//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);
mergedFile.UndosAreEnabled = false;
syncStat.Close(CloseOptions.DiscardChanges);

for (int x = 0; x < fileCount; x++)
{
//paste to mergedFile with fileHolder[x]
ISfFileHost temp = app.OpenFile(fileHolder[x], true, true);
temp.Markers.Clear();
mergedFile.OverwriteAudio(mergedFile.Length, 0, temp, new SfAudioSelection(temp));

//adds a marker to label each part of the mergedFile with filename
long mPos = mergedFile.Length - temp.Length;
string mName = Path.GetFileNameWithoutExtension(fileHolder[x]);
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);
}


//save merged file
string strFileName = Path.Combine(parFolder.FullName, targFileName);
DPF("The filename is {0}", strFileName);

mergedFile.SaveAs(strFileName, mergedFile.SaveFormat.Guid, "Default Template", RenderOptions.WaitForDoneOrCancel);
mergedFile.Close(CloseOptions.SaveChanges);

app.OpenFile(strFileName, false, false);

}

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


The following is a more in-depth file merging script that lets the user select the folder containing the files to be merged, How many of those files to merge at a time, where the resulting merged files will output, how much (if any) silence to put in between the files and then what output format to save the files as.


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

//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, 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]);

//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, 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



Hopefully you can alter one of these to suit your needs. I'm hoping that Sony implements a merge function in a later version of SF.

Rob.

Subject:RE: Glue lots of WAV files together :)
Reply by: RTVOK
Date:7/11/2013 4:23:48 AM

Hey Rob, i tried to run both scripts and none of them worked... 2nd script is great, but when it come to creating mix, error pops out... here it is:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
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)

i don't have any idea what it is... im still using SF9.0e

Subject:RE: Glue lots of WAV files together :)
Reply by: roblesinge
Date:7/11/2013 7:30:32 AM

You're sure you selected a folder full of wav files, correct? It will only work on WAV format files.

I'll have to play around and see if I can replicate the error. I haven't run into this problem with either of the scripts.

Rob.

Message last edited on7/11/2013 7:36:17 AM byroblesinge.

Go Back