JACK engine for ipyclam

Here you have a cute alternative to QJackCtl if you have it plenty of ardour multichannel ports connected in fancy ways. Auto-completion, broadcasting and Python slices on ports will be your friends.

ipyclam design enables other modular systems than CLAM to be controlled with the same interactive API just by reimplementing the internal engine. This could be exploited to provide interactive consoles for system such as Galan, Patchage… But a JACK engine is not just a proof of concept but also something that is useful within the CLAM NetworkEditor work-flow. You can explore and handle external JACK connections from the new NetworkEditor console in a similar way you can handle internal CLAM connections.

The screenshot is an independent ipyclam qtconsole with a simple session. Notice how quick is to get a description of the system status and how visual and replicable it is. Unit creation, deletion and renaming has no sense (maybe for testing), so I decide to ignore those messages. Transport control is not implemented either regardless it has sense.

Other changes on ipyclam

JACK engine has been a milestone of a set of changes I did this week to wrap-up ipyclam for the Linux Audio Conference 2013. Let me summarize them here:

Separate UI module

Now ui related methods are no more part of the Engine. They have been moved away and separated in independent modules for PyQt4 and PySide. Besides making the engines independent of Qt/Clam specifics splitting PyQt4 and PySide in two modules finally achieves the goal of having code which is independent of the Python binding, just by changing the imports.

if usePyQt :
    import ipyclam.ui.PyQt4 as ui
    from PyQt4 import QtGui, QtCore
else:
    import ipyclam.ui.PySide as ui
    from PySide import QtGui, QtCore

# using ui, QtGui and QtCore freely
app = QtGui.QApplication([])
w1 = ui.createWidget("Oscilloscope")
w2 = ui.loadUi("file.ui")
...

Engines are engines

Let’s forget about terms like ‘back-end’ and ‘network proxy’. ‘Back-end’ was a ambiguous term because we used it in CLAM for other purposes: Audio back-ends for JACK, PortAudio, LASDPA… Moreover, the corresponding classes had a nasty legacy name: XXX_NetworkProxy. So I started calling them engines and the classes XXX_Engine. Now we have Clam_Engine, Dummy_Engine and the brand new Jack_Engine.

The class naming is not definitive. I would like to make a submodule per engine with the same classes inside, including the Engine and the configuration wrapper. The goal should be having code like this:

from ipyclam.engines.clam import Engine as Clam_Engine, Config as Clam_Config
from ipyclam.engines.dummy import Engine as Dummy_Engine, Config as Dummy_Config

But I still I have to figure out how to implement it and it is not a priority.

CLAM and Dummy engines have been unified

They were meant to be analogous but it was clear they weren’t as I tried to pass one engine tests with other’s. It has been the harder task of this week but the good news is that once they were unified, implementing the JACK engine has been a one evening task.

Now all the tests are shared with some required divergences explicitly extracted. Still most about configuration is to be done.

Consistent connection broadcasting

There were incoherences, even within a single engine, about the behaviour of connections when one of the sides was not a single connector.

There are three kind of connectible sides:

  • A simple connector
  • A connector set: _inports,_outports… and slices
  • A processing

The behaviour has been defined as follows:

set1 > set2
# is equivalent to ordered one to one connection
for connector1,connector2 in zip(set1,set2) :
    connector1 > connector2

connector > set2
# is equivalent to one to many connection
for connector2 in set2 :
    connector > connector2

processing1 > processing2
# is equivalent to
processing1._outcontrols > processing2._incontrols
processing1._outports > processing2._inports

# for cases like
processing > set
# or
processing > connector
# the processing is substituted by the matching set
# _inports, _outports, _incontrols or _outcontrols

Angle operator now always check connector directions. connect method doesn’t, although when sides are processing units forward direction is implied.

a > b
b < a
a.connect(b)

Comments

2 comments.

  • Pau Arumi
    slices
    2013-02-23 19:47:50

    This is very nice!

    One question, you mention slices as connectable entity. How does it works? How does a user connect a slice/subrange of a processing ports? Thanks

    • David García Garzón
      Re: slices
      2013-02-24 11:36:48

      You are right, I forgot an example on the more interesting feature. If ports are named InPort1, InPort2...

      n.proc1._outports[2:7:2] > p.proc2._inports[3:]
      

      should be equivalent to

      n.proc1.OutPort3 > p.proc2.InPort4
      n.proc1.OutPort5 > p.proc2.InPort5
      n.proc1.OutPort7 > p.proc2.InPort6
      

      Broadcasting rules for connector sets apply to them.

      Maybe you tried something more intuitive like:

      n.proc1[2:7:2] > p.proc2[3:]
      

      This is NOT implemented but it could have sense, by taking the direction from the operator, and, either implicitly understanding that we are connecting ports, not controls, or using some heuristic I can not figure out right now.

      What do you think? Should we implement that?

Leave a comment

 (optional)
 (optional)