Jacktube v0.31 =============== Jacktube is an advanced audio/MIDI processing program. It uses LADSPA and DSSI plugins to process audio, MIDI events to control its operation and Jack is used for audio connections. The exact operation is governed by a set of customizable rules, which resembles a simple programming language. Even though Jacktube is primarily meant for audio work, it can be used in any signal processing application. Maintainer: Kristian Amlie Command line options ==================== I'm too lazy maintain documentation of options both here and in the program. This should do the trick: jacktube -h Examples ======== For some examples, see the scripts inside the examples/ subdirectory. Language ======== Jacktube operates by receiving audio from Jack input ports, using plugins to process the audio and handing the result to one or more output ports. In addition, MIDI events can be received from one or more sequencer ports and can either be used to control the operation of the audio path, sent to a DSSI synth plugin, or can be modified and sent back out on a MIDI output port. Number variables ---------------- The rule file consists of commands telling Jacktube what to do. A simple command may look like this: $variable = 1.5; This assigns the value 1.5 to the variable called "$variable". Variables have no direct effect on the signal path, but are useful to hold values in the program. All variables start with "$". Variables are created by simply assigning to them, but you can only create them outside an event scope (more on event scopes later). In other words, if you want to assign to a variable inside an event scope, you must have assigned to it at least once outside first. String variables ---------------- Variables can also hold strings. This is useful in the context of DSSI "configure" (more on that later). Note that strings are in a separate namespace, so you can have two variables, $myVar and $myVar, one containing a number and one containing a string. The context where it is used decides which one is chosen. String variables (and string operations in general) support simple concatenation using '+'. You can also concatenate numbers to the string this way, like: $variable = "mySoundFontNumber" + int($soundFontNumber) + ".sf2"; int() and float() are used to include numbers and numerical variables, and strings you can refer to directly. Audio paths ----------- An audio path can be constructed like this: >~output ~ <~input; This constructs a Jack output port ">~output" and an input port "<~input", and creates a path between them, so that the signal will flow from "<~input" to ">~output". All Jack output ports start with ">~" (think shell redirection, you write "into" the port so that it appears as an output port in the Jack port graph), and input ports start with "<~" (similar redirection mnemonic). Any port can only be written to by one other port, which means that the following command will replace the audio path we made above. >~output ~ <~anotherInput; A port can be read by as many ports as you like. To remove an audio path completely, use this command: >~output -; (Think "~" for audio wave, and "-" for flatline). Plugin loading -------------- Audio paths are useful, but they become much more powerful when combined with plugins. Before a plugin can be used, it must be loaded. You can obtain a list of available plugins by using "jacktube -l" (the normal LADSPA and DSSI environment variables apply). Once a plugin is selected, it is loaded like this: %plugin = "amp_mono"; You can identify a plugin using either its name or the name combined with the library it resides in (if there is a naming conflict). In the jacktube plugin listing, the name is the first word in each entry, and the library is the name after the comma. For example: amp_mono, amp.so Description: Mono Amplifier Unique ID: 1048 Real time: Yes Type: Filter In this listing, "amp_mono" is the name, and "amp.so" is the library it resides in. The unique ID can also be used to load LADSPA plugins, but this behavior is deprecated, and will only work for plugin variables with no array subscript. After the loading is complete, the plugin can be referred to by the name "%plugin". All plugin instances start with "%". Plugin ports ------------ LADSPA and DSSI plugins come with a number of ports, which are either control ports or audio ports (can be obtained with "jacktube -p"). Let's connect them: %plugin["gain"] = 0.5; %plugin["input"] ~ <~input; >~output ~ %plugin["output"]; So what happened here? We assigned the control parameter "gain" a value of 0.5. Then we connected the plugins input to the input from Jack, followed by connecting the Jack output to the output from the plugin. This audio path will modify the input signal so that the output signal has its amplitude cut in half. Note that you can use both variables and arbitrary arithmetics in the control port assignment. In addition the port name can be replaced by its numeric name if you like. So this is legal: %plugin[0] = $variable * 2 - 0.8; The operators are "+", "-", "*", "/" and "^" (raise). But the real magic is this: A control port can receive its input from a signal path as well, like this: %plugin["gain"] ~ <~anotherInput; The gain parameter will now vary with its input signal (see the -s option for controlling the granularity of this processing). You cannot use arithmetics on this path directly, but the signal can come from a plugin as well, so you can preprocess it there before it gets to the control port. Why is this useful, do you ask? Won't the control just fluctuate wildly and create noise? In most cases, yes, but remember that the input signal doesn't have to be audio. It could be a low frequency oscillator, for example like this: %gain = "amp_mono"; %oscillator = "sine_fcac"; %oscillator["freq"] = 1; %oscillator["amplitude"] = 0.5; %gain["gain"] ~ %oscillator["output"]; %gain["input"] ~ <~input; >~output ~ %gain["output"]; Here, the oscillator is set to oscillate at 1Hz, and send its output to the gain control port, causing the audio signal to rise and lower between 0 and 0.5 gain, once per second. Arrays ------ Variables and plugin handles support arrays, like this: $array[3] = 4.3; %myplugin[2] = "amp"; Any positive integer (and zero) can be used as a subscript and a variable or arithmetic expression can be used as well. All the elements are created on the fly, and the array does not need to be declared first, but arrays have the same restrictions as other variables regarding assignment within a scope. If you assign to a plugin port within an array, you will have to use two brackets: %myplugin[2]["gain"] = 0.5; Dynamic port names ------------------ Jack ports do not support arrays the way that internal variables do, however there is a different syntax which provides dynamic port names. In short, you have to create all ports by using them in an audio path, like described above, however after it is created you can refer to it by any string, including a variable or concatenated string. For example: >~out_channel_1 ~ <~in_channel_1; >~out_channel_2 ~ <~in_channel_2; >~{"out_channel_" + int($currentchannel)} ~ <~{"in_channel_" + int($currentchannel)}; This becomes very useful in event scopes, where the output port may depend on which channel the MIDI event came from. Event scopes ------------ As described before, MIDI events can be used to control the behavior of Jacktube. MIDI events take the shape of an event range followed by an event scope, which contains the commands that should be executed for that event: [<@seqInput, , , , ] { } The first variable there is the sequencer input port (the names start with "<@" and ">@", and have similar semantics like audio ports). After follow four specifiers, which are either "all" (match all possible values), a number or a range (two numbers separated by "-"). These specify for which event values this action should trigger. The order is channel, type, key/control, velocity/value. Type is the type of MIDI event, which is between 0 and 127. "type[xxx]" constants can be used to test for specific types of events, where xxx is either keyon, keyoff, keypress, pitch, program, control or channelpress. The order is important if you use them in a range, since everything between the two endpoints will be matched. The rest of the ranges should be self explanatory. For example: [<@seqInput, all, type[control], 1, 0-64] { %gain["gain"] = event[value]; } Note that arithmetics are allowed in ranges, but if they are anything more complicated than a simple variable or number you must put it in parenthesis. After the event range comes the event scope, where you can put any command. Inside the event scope, you can use "event[channel]" special variables to refer to the MIDI values that triggered the event. The types for "event[]" are again: channel, type, key/param and velocity/value. All the event ranges that match are executed; Jacktube does not stop on the first match. This can be changed, however, by putting the "final" keyword right before the event range. If the range matches, this will execute that scope, but no scope that follows it will be executed, even if it matches. For example: final [<@seqInput, all, type[control], 1, all] { $modulation = event[value]; } [<@seqInput, all, all, all, all] { # Forward the event. >@seqOutput = [event[channel], event[type], event[key], event[value]]; } In this example, we intercept MIDI control messages of the Modulation type (control message number 1), and store it in a variable. We don't want those control messages to propagate to the catch-all scope following it, so we put final in front of the first scope. In the second scope, we forward all other events (more on sending events below). Sending MIDI events ------------------- In addition to the already mentioned actions, MIDI events can be sent out too. Here is how that looks: [<@seqInput, all, type[control], 1, all] { >@seqOutput = [3, event[type], event[key], event[value]]; } This sends the same MIDI message back out on ">@seqOutput", except that it is always sent on channel 3. MIDI messages are only allowed inside an event scope. MIDI messages for DSSI synth plugins are sent in the exact same way, except that you replace the sequencer port name with a plugin name, such as: [<@seqInput, all, type[control], 1, all] { %plugin = [3, event[type], event[key], event[value]]; } "If" logic ---------- One can also put if statements inside the event scopes, and they look more or less exactly like a C/C++ if statement, with some exceptions: First, the braces are required, and second, you write "elseif" instead of "else if". For example: [<@seqInput, all, type[control], 1, all] { if (event[value] > 64) { >@seqOutput = [3, event[type], event[key], event[value]]; } else { >@seqOutput = [4, event[type], event[key], event[value]]; } } This will make events with values above 64 go to channel 3, while the rest go to channel 4. Like C/C++, the else clause is optional. Jacktube provides the <, <=, ==, >=, > and != operators for use in if statements. DSSI operations --------------- DSSI plugins come with some extra capabilities in addition to ports and accepting MIDI messages. "configure" is a call that is used to configure the plugin in some way. It consists of two strings, a key and a value. The meaning of these strings is completely plugin dependent (but see "show_gui" below). For example: %fluid = "FluidSynth-DSSI"; %fluid.configure("load", "mysoundfonts.sf2"); This loads the DSSI plugin of FluidSynth, and then tells it to load a certain soundfont using FluidSynth custom configure call. In accordance with the DSSI specification, if any configure key starts with "GLOBAL:", configure will propogate that key and value pair to all plugins of the same type. Another call is "show_gui", which, surprisingly, shows the GUI of the plugin, if it has one. It takes two parameters: The first is a label which the plugin can display in order to separate it from other instances of the same plugin. The other is an optional parameter, "configure_print", which causes it to print any configure calls received from the GUI to the console. This is a good way to find out which types of configure calls a plugin supports, for use with the configure call described above. The corresponding call to close the GUI is "hide_gui". For example: %fluid = "FluidSynth-DSSI"; [<@seqInput, all, type[control], 64, 64-127] { %fluid.show_gui("Fluid 1"); } [<@seqInput, all, type[control], 64, 0-63] { %fluid.hide_gui(); } [<@seqInput, all, type[control], 66, 64-127] { %fluid.show_gui("Fluid 1", configure_print); } [<@seqInput, all, type[control], 66, 0-63] { %fluid.hide_gui(); } In this example, we listen to the control messages 64 and 66, which are the sustain and sustenuto pedals. If we press the first pedal down we launch the GUI with no printouts, and we close it when releasing the pedal. If we press the second pedal down, we launch the GUI as well, but this time it will print all configure calls received from the GUI. Again, we close the GUI when releasing the pedal. The last DSSI specific call is programs(), which returns a string with a list of the programs (instruments) offered by the plugin. This is useful together with the "print" statement, which is described in the "Debugging" section. Comments -------- Comments in the rule file start with "#", continue to the next newline and are accepted everywhere. Debugging ========= For debugging scripts, Jacktube provides the "print" statement, which accepts any string as its argument, like so: [<@seqInput, all, all, all, all] { print("Event received on channel " + int(event[channel])); } Disclaimer ========== This program is provided as is, with no warranty whatsoever. Use the program at your own risk. The authors are not responsible for the malfunction of this program, nor any of its consequences.