[linux-audio-dev] Re: Quasimodo (Was: Re: LADSPA GUI)

New Message Reply About this list Date view Thread view Subject view Author view Other groups

Subject: [linux-audio-dev] Re: Quasimodo (Was: Re: LADSPA GUI)
From: Paul Barton-Davis (pbd_AT_Op.Net)
Date: la maalis 11 2000 - 07:56:25 EST


>such a package would ideally describe:
>
> 1. any low level building blocks
> that weren't in the "standard library"
>
> 2. the interconnection between
> low level building blocks.
>
> 3a. a way to coalesce the control
> signals into something intuitive
> to expose to the user.
>
> 3b. zero or more guis to bind to the
> coalesced controll signals which
> would be usefull for some hosts.
>
>my understanding is that quasimodo does
>2,3a, and 3b using interpreted scripting.
>how would you improve on this ?
>
>one solution::
>
>combine 2 and 3a rather than hiding
>3a inside a gui. this lets us feed
>the output of some blocks into the
>controll ports of others. it also
>provides the user with access to
>controll details rather than hardcoding
>them in the gui.

Hmm. I'm not entirely that I follow your thinking here, so forgive me
if I appear to be going backwards for a moment.

One of the reasons why the opcodes (the "low level building blocks")
are not trivially packaged as LADSPA plugins is that they are intended
for use in a programming language (this feature is inherited from
Csound). In the terminology of a plugin API, an opcode may have a
number of control ports, but these are intended to be utilized by a
language that connects them, rather than by simple static
connections. So, we can write (in that awful Csound syntax):

             aoutput oscil 1, kfrequency, kwavetable

which invokes the "oscil" opcode, connecting its 2 input ports to a
constant and two control variables, and its output port to an audio
variable.

Immediately, you should notice that a plugin API has no notion of
"variables". They do not correspond to anything in the plugin API
either, even though they need to be mapped to the API ports if they
are to be visible to the outside world in any way.

So, to use such a construction, you have to have a level "above" (or
is it below?) the plugin API that understands how to take the "script"
and convert it into something useful that *can* be a plugin. In the
example above, we'd end up with a plugin that set default values for the
variables aoutput, kfrequency and kwavetable during its initialize
routine, and when its run() routine was called, would invoke the
opcode in some way that led the output to be written to the current
assigned location of the port corresponding to `aoutput'.

Now, take a more complex module, with conditional control flow
constructs, possibly even function definition and use, and you end up
with something that seems to me to have specified your (2) (the
interconnections between the low level units) and created your (3a) in
the form of certain variables that when adjusted, alter the behaviour
of the system. Consider a somewhat trivial example, the 4 channel
mixer in Quasimodo (with non-constant-power panning, yuck :) :

<?xml version="1.0"?>
<!DOCTYPE quasi_module SYSTEM "qm.dtd">

<quasi_module name="4 Channel Slider Mixer" category="Audio/Mixers">

<definition language="csound_orchestra">
<![CDATA[
instr "4 Channel Mixer"

achn1 init 0
kamp1 init 1
kchn1_pan init 0.5
ac1_l init 0
ac1_r init 0

achn2 init 0
kamp2 init 1
kchn2_pan init 0.5
ac2_l init 0
ac2_r init 0

achn3 init 0
kamp3 init 1
kchn3_pan init 0.5
ac3_l init 0
ac3_r init 0

achn4 init 0
kamp4 init 1
kchn4_pan init 0.5
ac4_l init 0
ac4_r init 0

aout_left init 0
aout_right init 0

ac1_r = kchn1_pan * achn1 * kamp1
ac2_r = kchn2_pan * achn2 * kamp2
ac3_r = kchn3_pan * achn3 * kamp3
ac4_r = kchn4_pan * achn4 * kamp4

ac1_l = (1 - kchn1_pan) * achn1 * kamp1
ac2_l = (1 - kchn2_pan) * achn2 * kamp2
ac3_l = (1 - kchn3_pan) * achn3 * kamp3
ac4_l = (1 - kchn4_pan) * achn4 * kamp4

aout_left madd ac1_l, ac2_l, ac3_l, ac4_l
aout_right madd ac1_r, ac2_r, ac3_r, ac4_r

endin
]]>
</definition>

====================================================================
OK, its horrible code - thats Csound for you. But notice that in the
above setup, we've already implicitly created the things that
could/should be adjusted in the GUI. This is shown by the interface
definition, coming next. Check the "param" values for each controller
element.
====================================================================
<interface height="7">

<label xpos="2.0" ypos="5.0">4 Channel Slider Mixer</label>

<controller type="knob"
        xpos="5.0"
        ypos="65.0"
        label=""
        label_position="top"
        param="kchn1_pan"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<controller type="knob"
        xpos="20.0"
        ypos="65.0"
        label=""
        label_position="top"
        param="kchn2_pan"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<controller type="knob"
        xpos="35.0"
        ypos="65.0"
        label=""
        label_position="top"
        param="kchn3_pan"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<controller type="knob"
        xpos="50.0"
        ypos="65.0"
        label=""
        label_position="top"
        param="kchn4_pan"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<controller type="vslider"
        xpos="7.0"
        ypos="10.0"
        label=""
        label_position="top"
        param="kamp1"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<controller type="vslider"
        xpos="22.0"
        ypos="10.0"
        label=""
        label_position="top"
        param="kamp2"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<controller type="vslider"
        xpos="37.0"
        ypos="10.0"
        label=""
        label_position="top"
        param="kamp3"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<controller type="vslider"
        xpos="52.0"
        ypos="10.0"
        label=""
        label_position="top"
        param="kamp4"
        start="0.0"
        end="1.0"
        step="0.01"
        page="0.1"
/>

<socket
        xpos="85.0"
        ypos="80.0"
        label="Right"
        label_position="top"
        param="aout_right"
        direction="output"
        trigger="true"
>
</socket>

<socket
        xpos="75.0"
        ypos="80.0"
        label="Left"
        label_position="top"
        param="aout_left"
        direction="output"
        trigger="true"
>
</socket>

<socket
        xpos="5.0"
        ypos="80.0"
        label="1"
        label_position="top"
        param="achn1"
        direction="input"
>
</socket>

<socket
        xpos="20.0"
        ypos="80.0"
        label="2"
        label_position="top"
        param="achn2"
        direction="input"
>
</socket>

<socket
        xpos="35.0"
        ypos="80.0"
        label="3"
        label_position="top"
        param="achn3"
        direction="input"
>
</socket>

<socket
        xpos="50.0"
        ypos="80.0"
        label="4"
        label_position="top"
        param="achn4"
        direction="input"
>
</socket>
</interface>
</quasi_module>

=====================================================================

Notice that in Quasimodo, the GUI is optional. There is an
implemention, "server-quasimodo" which listens on a TCP/IP socket for
command-line instructions. It doesn't present any GUI at all. The
modules can be used with this version just as easily as the GTK one.

The only thing that the <interface></interface> block does for all
situations is to define the parameters that should be exposed to
external control. Thus, in the example above, the parameters ac[1-4]_{l,r}
are all completely internal to the module, and are not accessible to
anything else. This is deducible from the <interface> spec, which does
not reference them.

So, it feels to me as if this kind of organization: a definition of
the implementation of the higher-level plugin as a program involving
lower level plugins, combined with a specification for a GUI that
references parameters within the program, does in fact provide all
the things that you mention. Note that I am not advocating any
particular scripting language or any particular XML DTD for this, just
the way that they are used in the module definition show above.

>encode 2 and 3a in a standardized
>XML data structure rather than a
>scripting language.

step 2 *has* to be scripting language. It needs to contain conditional
control flow statements, parameters etc. Or am I missing something
here ?

--p


New Message Reply About this list Date view Thread view Subject view Author view Other groups

This archive was generated by hypermail 2b28 : su maalis 12 2000 - 09:14:06 EST