Contents

kaseq samples runing together
 

Introduction

ALSA MIDI Kommander is a DCOP [1] interface exposing many ALSA Sequencer [2] features for shell scripts, Kommander scripts, or KDE programs requiring MIDI Sequencer services. A few MIDI utilities have been developed with this tool, which can be used both as programming examples and as real work tools.

Motivations for writing these things were the complexity of the ALSA Sequencer API programming, and the need for an extensible set of MIDI tools, built from a common set of reusable pieces.

This document tries to explain the behavior and main features of the system. This is a work in progress, and some changes are expected as long as the system is maturing and growing.

Copyright (C) 2005, Pedro Lopez-Cabanillas <plcl AT users.sourceforge.net>

 

Components

The main program, "kaseq" is a single executable, used as a DCOP service program. It runs as a daemon, allowing only a single running instance, and providing a system tray icon to give visual feedback for MIDI activity, and a way to control the program execution itself. It depends on KDE 3.x and the Qt library.

You can write DCOP [1] clients in C++, or use a ready-to-use one:

  • dcop CLI utility, it is used mainly for Bash scripts or directly in the command line.
  • kdcop, is a GUI utility, with a DCOP services browser and executor. Very useful to explore and learn.
  • Kommander [3]. It is a two part application. The first part is the editor, based on "Qt-designer", where you visually build dialogs and applications and edit the scripting elements. The second part is the executor which processes the generated XML file. With Kommander, you can write scripts with rich Graphic User Interfaces.

ALSA [2] is an audio and MIDI system infrastructure for Linux. It is included in Linux 2.6.x kernels as a set of drivers and modules, and has a user-space library providing a powerful and complex API. The sequencer part is the high level MIDI engine providing support for input and output MIDI streams, time-stamped events, and routing the streams between clients which can be hardware (MIDI physical ports) or software clients (applications). There is a lot of documentation [4] around.

 

DCOP Interface Functions

Following is a brief enumeration of the current functions available as DCOP calls. The first parameter (QString name) is common to all functions, and identifies each Sequencer client. The client instance is created on the open() call, and it is destroyed on a corresponding close(). All other functions for this client must be invoked with the same client name.

Currently, there are only functions for sending non scheduled events. Future plans are to provide functions for incoming (recording) events with- and without timestamp, queue management, and a set of easy String-based syntax commands.

Sequencer Client Management

These functions manage the creation and destruction of the Sequencer client instance, and the associated Port.

void open( QString name, QString ports )
     Creates one sequencer client instance and one sequencer port;
     "ports" argument can be: "IN", "OUT", or "IO".
void openInput( QString name )
     Same as open( name, "IN" )
void openOutput( QString name )
     Same as open( name, "OUT" )
void openDuplex( QString name )
     Same as open( name, "IO" )
void open( QString name )
     Same as openOutput( name )
void close( QString name )
     Destroys one Sequencer client instance

Connections Management

The following functions provide the means to establish port subscriptions between the internal Sequencer port and another external one, or between two arbitrary sequencer ports.

int connectTo( QString name, QString port )
int connectFrom( QString name, QString port )
int disconnectTo( QString name, QString port )
int disconnectFrom( QString name, QString port )
int connectTwo( QString name, QString sender, QString dest )
int disconnectTwo( QString name, QString orig, QString dest )
void disconnectAll( QString name )

The following functions provide a list of ALSA Sequencer ports available in the system.       

QStringList availableInputs( QString name )
QStringList availableOutputs( QString name )

Sending Events

These functions can be used to send any MIDI event to all the subscribed destinations.

int sendNoteOn( QString name, int chan, int pitch, int vel )
int sendNoteOff( QString name, int chan, int pitch, int vel )
int sendCtlChange( QString name, int chan, int control, int value )
int sendPgmChange( QString name, int chan, int program )
int sendPolyAft( QString name, int chan, int pitch, int value )
int sendChanAft( QString name, int chan, int value )
int sendBender( QString name, int chan, int value )
int sendSystemRealTime( QString name, int msg )
int sendSysex( QString name, QByteArray data )
int sendSysex( QString name, QString data )

Miscellany functions

These are convenient functions with self-explained names.

QString getLastError( QString name )
void exit()

 

Programming Examples

In the directory examples/ of the distribution package you can find several shell and Kommander script examples.

All of them require the "kaseq" program. It can be executed at the command line prompt or invoked by the scripts in the initialization stage. It is a daemon-like program, allowing only a single running instance, and also providing a system tray icon. The icon shows a simulated green LED lighting when there is some outgoing MIDI activity. Another red LED is provided for incoming MIDI, which will be more useful in the future.

Shell Scripts

#!/bin/sh
CLIENTNAME="Example1"
DESTINATION="KMidimon:0"
dcop kaseq kaseqIface openOutput    "$CLIENTNAME"
dcop kaseq kaseqIface connectTo     "$CLIENTNAME" "$DESTINATION"
dcop kaseq kaseqIface sendPgmChange "$CLIENTNAME" 0 55
dcop kaseq kaseqIface sendCtlChange "$CLIENTNAME" 0 7 127
dcop kaseq kaseqIface sendNoteOn    "$CLIENTNAME" 0 66 127
dcop kaseq kaseqIface sendNoteOff   "$CLIENTNAME" 0 66 127
dcop kaseq kaseqIface close         "$CLIENTNAME"

This example shows some basic functionality. First, two variables are defined for the client name (Example1) and the destination port (KMidimon:0). The flow begins with an open() call, then a connectTo() to subscribe the created port with the input port of the KMidimon application, which should be started previously. You can find KMidimon in the resources [5]. After that, you can send some events to the subscribed ports, and finally call close() to release resources.

Example2 is very similar, using a different way to call dcop with error checking.

Example3 shows how to detect if some sequencer client is already running, examining the list returned by availableOutputs().

Example4 introduces a Kommander dialog, invoked from a shell script. This is not the best way to do it, as you will see in the following section.

Kommander Scripts

To run the dialogs, you can use a command like with the following syntax:

$ kmdr-executor example5.kmdr

Or simply, double click over the .kmdr file name in Konqueror.

To learn about Kommander, go to the program home page [3] and read the documentation. It works basically with associated text holding script snippets. Every example has an initialization section (as associated text) with the following lines:

@exec(kaseq)
@setGlobal(CLIENT_NAME,Example5)
@dcop(kaseq,kaseqIface,open(QString),@global(CLIENT_NAME))
@Connections.insertItem("No Connection",0)
@Connections.insertItems(@dcop(kaseq,kaseqIface,availableOutputs(QString),@global(CLIENT_NAME)),1)

The first line executes "kaseq" if it is not running already. Then, a global variable CLIENT_NAME is created, with the name that will be used for all the following dcop calls. Also, a combo box component is populated with the available output ports. There is also an associated text invoked before the dialog is closed:

@dcop(kaseq,kaseqIface,close(QString),@global(CLIENT_NAME))

When the user selects some item with the combo box, a ScriptObject.execute() method is invoked, which holds the following snippet with shell script syntax:

dcop kaseq kaseqIface disconnectAll @global(CLIENT_NAME)
if [ @Connections.currentItem -ne 0 ]; then
   dcop kaseq kaseqIface connectTo @global(CLIENT_NAME) @Connections.selection
fi

StatusBar object has a population text, executing:

@dcop(kaseq,kaseqIface,getLastError(QString),@global(CLIENT_NAME))

The signal activated(int) of the Combo Box object is connected to both slots ScriptObject.execute() and also StatusBar.populate().

The example5 has also two buttons sending "Note On" and "Note Off" MIDI events. You can imagine how to do that. For instance:

@dcop(kaseq,kaseqIface,sendNoteOn(QString,int,int,int),@global(CLIENT_NAME),0,64,127)

There are three more examples, not much more technically complex, but closer to real world applications. Example6 has a set of MMC Transport buttons. Example7 has eight faders with variable controller and channel numbers, and Example8 is a controller for GM synthesizers featuring several goodies.

MMC MIDI Transport

MMC Transport image

(example6.kmdr)

Guess how this gadget can be used. There are some hardware devices that can understand MMC commands and can be controlled using this dialog. You can try also some interesting Linux MIDI programs, among others:

MIDI Controls

MIDI Controls image

(example7.kmdr)

There are several programs with similar functionality:

  • QMidiControl [9]
  • MidiControl [10]

The second one is interesting because the GUI is designed using a visual tool (Glade) and the resulting definition is stored on a XML file. It depends on libglademm and gtkmm. It sends MIDI Control Change messages only, but the main technical differences are the signal/slot connections not being stored in the XML file (this is an exclusive Qt characteristic), and doesn't use scripts so the widgets must be named using a fixed pattern scheme to trigger different channel and controller number values.

GM Kommander

GM Kommander image

(example8.kmdr)

The most interesting parts of this example are the implementations of the buttons "Panic" and "GM Reset". The first one executes a for loop:

@for(CHAN, 0, 15, 1)
@dcop(kaseq,kaseqIface,sendCtlChange(QString,int,int,int),@global(CLIENT_NAME),@CHAN,120,0)
@dcop(kaseq,kaseqIface,sendCtlChange(QString,int,int,int),@global(CLIENT_NAME),@CHAN,121,0)
@dcop(kaseq,kaseqIface,sendCtlChange(QString,int,int,int),@global(CLIENT_NAME),@CHAN,123,0)
@end

The "GM Reset" button sends a System Exclusive message:

@dcop(kaseq,kaseqIface,sendSysex(QString,QString),@global(CLIENT_NAME),"f0 7e 7f 09 01 f7")

 

 

Download

You can download the latest released sources from SourceForge.
There are ready to install packages for:


Licensed under the GNU General Public License (GPL).
 

Requirements

In order to successfully use this program, you need KDE 3.3 and ALSA 1.0 drivers and library.
ALSA library, drivers and utilities can be found at ALSA home page.

 

Resources

[1] DCOP
http://developer.kde.org/documentation/other/dcop.html

[2] ALSA Sequencer API
http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html

[3] Kommander
http://kommander.kdewebdev.org/

[4] ALSA Wiki
http://alsa.opensrc.org/AlsaMidi

[5] KMidimon
http://kmetronome.sourceforge.net/kmidimon

[6] Ardour
http://www.ardour.org/

[7] Rosegarden
http://www.rosegardenmusic.com

[8] KMetronome
http://kmetronome.sourceforge.net/

[9] QMidiControl
http://sourceforge.net/projects/alsamodular

[10] MidiControl
http://sourceforge.net/projects/midicontrol

 

This page was modified on April 19, 2010

Communities