Community Forums Archive

Go Back

Subject:Setting specific parameters through scripting
Posted by: D-Slam
Date:1/26/2006 2:07:49 PM

Is it possible to override a preset parameter in C#. For example can a script set the "Attack Threshold" or "ReleaseThreshold" of an AutoTrimCrop preset? If not, can I set all of the parameters in code?

I currently use...
app.DoEffect("Process.AutoTrimCrop","MyPreset_TrimWav", EffectOptions.EffectOnly);
but I'd like to use an "ini" file to pull in some custom values.

thx

Subject:RE: Setting specific parameters through scrip
Reply by: _TJ
Date:1/26/2006 7:23:04 PM

Yes, you can do this - and as luck would have it. Auto Trim/Crop is one of the few effects that are enabled for direct creation of presets.

But first of all, some background.

app.DoEffect(...) and ISfFileHost.DoEffect(...) both accept the preset in multiple forms. Here are the definitions for the DoEffect(...) methods.

ISfGenericPreset app.DoEffect(string strNameOrGuid,
object pPresetNameOrData,
EffectOptions opt);

ISfGenericPreset file.DoEffect(string strEffectNameOrGUID,
object vPresetNameOrData,
SfAudioSelection asel,
EffectOptions opt);

Notice that the second argument is of type object. This is so you can pass the preset in one of multiple forms.

The most commonly used form is to use a string. This causes Sound Forge to search for that preset by name, and then use the settings for that named preset.

You could also pass an int or uint as the preset, in which case, Sound Forge would just that number as an index into the list of available presets. This is not very useful most of the time because you have no way of knowing for sure that the 3rd preset (for instance) is the settings you want unless you enumerate all of the presets and check, and if you do that, you might as well just keep the 3rd preset object and give that instead. The exception to this is the number 0, because the preset at index 0 is always the default settings (by definition).

The 3rd way to pass a preset is to pass an object of type ISfGenericPreset, which you can get by calling various functions, or which you can make from scratch.

The last way is to pass an array of bytes containing the actual preset data. This is most useful if you want to save a preset off to a file, then read it back later and use it. But you could also use this as a way to create presets from scratch.

In order to create a preset from scratch, then you would need to know the structure and size of that array of bytes. And here we get to the problem: That structure is typically secret. And furthermore, it usually changes with every new release of the Plug-In.

So in the general case, you would have to contact the Plug-In manufacturer, and ask them to tell you the structure of the preset bytes and then use that information to create or modify a preset. And in most cases, the Plug-In manufacturer will refuse to tell you what you need to know. But assuming that you can get the information you need, creating a preset from scratch is pretty easy.


// an Auto Trim/Crop preset is 28 bytes in size. We determine this
// by getting a preset and querying its size in a previous script.
// the bytes were also determined by saving off a previously created preset
//
byte[] abData = new byte[28] {
2,0,0,0,124,243,255,255,184,242,255,255,20,0,0,0,20,0,0,0,1,0,0,0,0,0,0,0,
};

ISfGenericEffect fx = app.FindEffect("Auto Trim/Crop");
ISfGenericPreset preset = new SoundForge.SfGenericPreset("Preset Name", fx, abData);

// we now have a preset for Auto Trim/Crop created from scratch. Now, to use it.
//
app.DoEffect("Process.AutoTrimCrop", preset, EffectOptions.EffectOnly);


So the only trick here is that you have to create an array of bytes with the correct size and contents to be a settings for the given Plug-in. Then create a new object of type SoundForge.SfGenericPreset. That's it.

In my next post. I'll show how (for a few of the built-in effects). you can make use of the Fields property of the preset object to modify a preset using reasonable names for the various settings fields.

tj

Subject:RE: Setting specific parameters through scrip
Reply by: _TJ
Date:1/26/2006 7:25:50 PM

Here's a script that will ask the user to choose setttings for Auto Trim/Crop and then will dump the contents of the returned preset as an array of bytes.


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

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

ISfGenericEffect fx = app.FindEffect("Auto Trim/Crop");

ISfGenericPreset preset0 = fx.ChoosePreset(IntPtr.Zero, 0);
DPF("preset Name='{0}'\r\n byte[] abData = new byte[{1}] {{\r\n ", preset0.Name, preset0.Size);
byte[] abData = preset0.Bytes;
for (int ii = 0; ii < abData.Length; ++ii)
{
DPF("~`{0:d},", abData[ii]);
}
DPF(" };");

}

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: Setting specific parameters through scrip
Reply by: _TJ
Date:1/26/2006 7:59:52 PM

And here's how you use the Fields property. Unfortunately, there appears to be a bug in Fields for Auto Trim/Crop. You need to use values for the the Time members that are 10 or 1000 times larger than you want to correct for a scaling error in the Fields class for Auto Trim/Crop.


ISfGenericEffect fx = app.FindEffect("Auto Trim/Crop");
byte[] abData = new byte[28] {
2,0,0,0,124,243,255,255,184,242,255,255,20,0,0,0,20,0,0,0,1,0,0,0,0,0,0,0,
};

// make a preset
ISfGenericPreset preset1 = new SoundForge.SfGenericPreset("Test Preset", fx, abData);

// get an object that will let us set fields in the preset.
//
Fields_AutoTrimCrop fields1 = Fields_AutoTrimCrop.FromPreset(preset1);

// set values for a few of the fields.
//
fields1.StartThreshold = SfHelpers.dBToRatio(-31); // set to -31 db
fields1.InTime = 30 * 1000.0; // set to 30 ms (the * 1000 is to work around a bug)

// write changes back into the preset object.
//
fields1.ToPreset(preset1);

// use the preset.
//
app.DoEffect("Process.AutoTrimCrop", preset1, EffectOptions.EffectOnly);


Subject:RE: Setting specific parameters through scrip
Reply by: D-Slam
Date:1/27/2006 4:40:04 AM

You ROCK as always!

Next question is: where is a good place to search for documentation on which generics are supported and which fields come in which order with what ranges.

The dump utitility is super useful, but it's not obvious which fields correspond to which items in the array. Though I suppose they's in 'tabbed' order and I could figure it out.

Unfortunately, I'm still waiting on my copy of VS2003, so I'm doing most of this blind.

thanks again!

Subject:RE: Setting specific parameters through scrip
Reply by: D-Slam
Date:1/27/2006 8:58:55 AM

OK. So I've been comparing values in the array by changing various parameters, and I'm guesing that some have to do with the Field that is currently highlighted and others are the actual values. Some appear to be binary bit values (like 184 or 242) while others are 0-255.

preset Name='MyTrimWav'
byte[] abData = new byte[28] {
1,0,0,0,184,242,255,255,184,242,255,255,30,0,0,0,30,0,0,0,1,0,0,0,0,0,0,0,
};

Is there something on this in the Sound Forge Scripting API help file? I can't seem to locate documentation on which effects contain which fileds. I only see something similar to..."For most owners, attempting to use this field with throw a NotImplemented exception, but for a limited subset, it will return an object that allows settings to be examined and/or changed."

The presets where I'm hoping to dynamically change field values are...
AutoTrimCrop
InsertSilence
Render or SaveAs (I think you've already talked about this elsewhere)

Is there a spec for what the various bytes in the array represent and how to convert to or derive appropriate values?




Message last edited on1/27/2006 8:59:55 AM byD-Slam.
Subject:RE: Setting specific parameters through scrip
Reply by: _TJ
Date:1/27/2006 12:58:06 PM

Next question is: where is a good place to search for documentation on which generics are supported and which fields come in which order with what ranges.

If I understand the question, you want to know which Plug-Ins support the Fields parameter that I wrote about in my 3rd response in this thread?

Basically, look through the help file for the Script API. Classes whose names begin wth Fields_ are the ones that are supported. There's about 6 of them as I recall.
Details about parameters and ranges can be determined from looing at the Plug-In UI, but I don't beleve that this information is documented in a way that Scripts can get acess to the information. They just have to know...

tj

Subject:RE: Setting specific parameters through scrip
Reply by: _TJ
Date:1/27/2006 1:59:44 PM

."For most owners, attempting to use this field with throw a NotImplemented exception, but for a limited subset, it will return an object that allows settings to be examined and/or changed."

This doesn't seem to be documented. And the most reliable way to know would be to simply TRY it. but I beleve that the current list. is

GraphicFade, TimeStretch, Normalize, Resample, BitDepthConverter, ChannelConverter, DCOffset, and AutoTrimCrop.

The presets where I'm hoping to dynamically change field values are...
AutoTrimCrop
InsertSilence
Render or SaveAs (I think you've already talked about this elsewhere)

For Auto Trim/Crop you can use fields.

For InsertSilence, you can just use the ISfFileHost.InsertSilence(...) method. This gives you direct control of the parameters at each call, no need to build a preset at all. just pass the insertion point and amount of silence as a parameter to this method.

for RenderAs() or SaveAs(), there is no support for field access. You will just have to build and save off a bunch of presets. Now you CAN get the bytes for a render preset. but each preset is about 1Kb worth of data with a complex structure. It would be a huge amount of work to reverse engineer. And we aren't going to document it.

You can, however, save the bytes off to a file and reload them later. So you don't have to worry about having the right presets with the right names loaded on a particular machine in order for your script to run. This, incidently, is how the Batch Converter does it. So you might want to have a look at it's source code (included in the SDK).

Is there a spec for what the various bytes in the array represent and how to convert to or derive appropriate values?

No. And there probably won't be. If we published the structure then we wouldn't be allowed to change it.


tj

Message last edited on1/27/2006 2:02:13 PM by_TJ.
Subject:RE: Setting specific parameters through scrip
Reply by: ghova
Date:2/2/2006 8:09:11 AM

You know, I was just thinking about how I could do this today, and presto, here's the answer! Many thanks for an informative thread.

Go Back