drumstick 2.9.0
C++ MIDI libraries using Qt objects, idioms, and style.
drumstick Documentation
Author
Copyright © 2009-2023 Pedro López-Cabanillas <plcl AT users.sf.net>
Date
December 23, 2023
Version
2.9.0

This document is licensed under the Creative Commons Attribution-Share Alike 4.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/

Abstract

This is the reference documentation for drumstick. These libraries are a set of C++ MIDI related classes, using Qt objects, idioms and style.

Currently, there are four libraries designed to work together if/when needed:

  • Drumstick::ALSA is a Linux only C++/Qt wrapper around the ALSA Sequencer API. ALSA sequencer provides software support for MIDI technology on Linux.
  • Drumstick::File provides easy multiplatform file I/O for Standard MIDI Files (.mid), RIFF MIDI (.rmi) and Cakewalk (.wrk) file formats.
  • Drumstick::RT is a realtime MIDI I/O library with pluggable backends. It uses Drumstick::ALSA on Linux, and other native frameworks on macOS and Windows.
  • Drumstick::Widgets contains MIDI widgets, including a Virtual Piano used by VMPK among other programs
See also
https://doc.qt.io/qt-5/index.html
https://www.alsa-project.org/alsa-doc/alsa-lib/seq.html
https://www.ics.com/intro-design-patterns-c-qt-2nd-edition
https://www.midi.org/articles/tutorials

Disclaimer

This document is a work in progress and it will be always in development. Please visit the drumstick web site to read the latest version.

See also
https://drumstick.sourceforge.io

Introduction

For an introduction to design and programming with C++ and Qt, see the book "An Introduction to Design Patterns in C++ with Qt" by by Alan Ezust and Paul Ezust. It is available published on dead trees, and also online.

Drumstick::ALSA was the first library developed under the Drumstick umbrella, and is available only on Linux, because ALSA Sequencer is an exclusive Linux technology. Here is how a simple program playing notes using Drumstick::ALSA looks like:

#include <QCoreApplication>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
// create a client object on the heap
client->open();
client->setClientName( "MyClient" );
// create the port. The pointer is owned by the client instance
port->setPortName( "MyPort" );
port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ );
port->setPortType( SND_SEQ_PORT_TYPE_MIDI_GENERIC );
// subscribe the port to some other client:port
port->subscribeTo( "128:0" ); // or "name:port", like in "FluidSynth:0"
QList<int> notelist{ 60, 62, 64, 65, 67, 69, 71, 72 };
for(auto note : notelist)
{
// create event objects on the stack, to send note on/off messages
drumstick::ALSA::NoteOnEvent ev1( 0, note, 100 ); // (channel, note number, velocity)
ev1.setSource( port->getPortId() );
ev1.setSubscribers(); // deliver to all the connected ports
ev1.setDirect(); // not scheduled, deliver immediately
client->output( &ev1 ); // or outputDirect() if you prefer not buffered
client->drainOutput(); // flush the buffer
QThread::msleep(250); // wait a quarter second
drumstick::ALSA::NoteOffEvent ev2( 0, note, 0 ); // (channel, note number, velocity)
ev2.setSource( port->getPortId() );
ev2.setSubscribers(); // deliver to all the connected ports
ev2.setDirect(); // not scheduled, deliver immediately
client->output( &ev2 ); // or outputDirect() if you prefer not buffered
client->drainOutput(); // flush the buffer
}
// close and clean
client->close();
// it also deletes the port and other owned objects
delete client;
return 0;
}
Classes managing ALSA Sequencer clients.
Classes managing ALSA Sequencer events.
Client management.
Definition: alsaclient.h:219
Port management.
Definition: alsaport.h:125
void subscribeTo(PortInfo *port)
Subscribe to another port destination.
Definition: alsaport.cpp:679
void setPortName(QString const &newName)
Sets the port name.
Definition: alsaport.cpp:928
void setPortType(unsigned int newValue)
Sets the port type bitmap.
Definition: alsaport.cpp:984
void setCapability(unsigned int newValue)
Sets the port capabilities.
Definition: alsaport.cpp:961
int getPortId()
Gets the port number.
Definition: alsaport.cpp:939
Event representing a note-off MIDI event.
Definition: alsaevent.h:285
Event representing a note-on MIDI event.
Definition: alsaevent.h:265
void close()
Close the sequencer device.
Definition: alsaclient.cpp:452
void setClientName(QString const &newName)
Changes the public name of the ALSA sequencer client.
Definition: alsaclient.cpp:842
void output(SequencerEvent *ev, bool async=false, int timeout=-1)
Output an event using the library output buffer.
Definition: alsaclient.cpp:996
MidiPort * createPort()
Create and attach a new MidiPort instance to this client.
Definition: alsaclient.cpp:865
void open(const QString deviceName="default", const int openMode=SND_SEQ_OPEN_DUPLEX, const bool blockMode=false)
Open the sequencer device.
Definition: alsaclient.cpp:395
void drainOutput(bool async=false, int timeout=-1)
Drain the library output buffer.

MIDI is a real time protocol, so it is not a surprise that many applications using MIDI require only real time functionality. In this case, you may use the Drumstick::RT library, which is multiplatform. An example equivalent to the former one, but implemented using the Drumstick::RT library looks like this:

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
drumstick::rt::MIDIOutput* output = man.outputBackendByName("SonivoxEAS");
if (output != 0) {
qDebug() << "testing backend: " << output->backendName();
qDebug() << "public name " << output->publicName();
auto conn = output->connections().first();
qDebug() << "port " << conn.first;
output->open(conn);
QList<int> note_list{ 60, 62, 64, 65, 67, 69, 71, 72 };
for(auto midi_note : note_list)
{
output->sendNoteOn(0, midi_note, 100);
QThread::msleep(250); // wait a quarter second
output->sendNoteOff(0, midi_note, 0);
}
output->close();
}
return 0;
}
BackendManager class declaration.
The BackendManager class manages lists of dynamic and static backends for applications based on drums...
MIDIOutput * outputBackendByName(const QString name)
outputBackendByName
MIDI OUT interface.
Definition: rtmidioutput.h:122
virtual QString backendName()=0
backendName
virtual void sendNoteOn(int chan, int note, int vel)=0
sendNoteOn 0x9
virtual QList< MIDIConnection > connections(bool advanced=false)=0
connections
virtual QString publicName()=0
publicName
virtual void open(const MIDIConnection &conn)=0
open the MIDI port by name
virtual void sendNoteOff(int chan, int note, int vel)=0
sendNoteOff 0x8
virtual void close()=0
close the MIDI port
Realtime MIDI output interface.

A common pattern on both implementations is QThread::msleep(250) to do the rhythm. If you are targeting only Linux, you may be interested on another (better) way to do the same, using Drumstick::ALSA again, because ALSA Sequencer is capable of event scheduling (that is why it is called a Sequencer).

#include <QCoreApplication>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
client->open();
client->setClientName( "MyClient" );
port->setPortName( "MyPort" );
port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ );
port->setPortType( SND_SEQ_PORT_TYPE_MIDI_GENERIC );
port->subscribeTo( "FLUID Synth (qsynth):0" );
drumstick::ALSA::MidiQueue *queue = client->createQueue( "MyQueue" );
tempo.setPPQ( 120 );
tempo.setNominalBPM( 120 );
queue->setTempo(tempo);
client->drainOutput();
queue->start();
int tick = 0;
QList<int> notelist{ 60, 62, 64, 65, 67, 69, 71, 72 };
for(auto midinote : notelist)
{
drumstick::ALSA::NoteOnEvent ev1( 0, midinote, 100 );
ev1.setSource( port->getPortId() );
ev1.setSubscribers();
ev1.scheduleTick(queue->getId(), tick, false);
client->output( &ev1 );
tick += 60;
drumstick::ALSA::NoteOffEvent ev2( 0, midinote, 0 );
ev2.setSource( port->getPortId() );
ev2.setSubscribers();
ev2.scheduleTick(queue->getId(), tick, false);
client->output( &ev2 );
}
client->drainOutput();
client->synchronizeOutput();
queue->stop();
// close and clean
client->close();
delete client;
return 0;
}
Classes managing ALSA Sequencer ports.
Classes managing ALSA Sequencer queues.
Queue management.
Definition: alsaqueue.h:201
void start()
Start the queue.
Definition: alsaqueue.cpp:862
void stop()
Stop the queue.
Definition: alsaqueue.cpp:873
QueueTempo & getTempo()
Gets a QueueTempo object reference.
Definition: alsaqueue.cpp:791
void setTempo(const QueueTempo &value)
Applies a QueueTempo object to the queue.
Definition: alsaqueue.cpp:821
Queue tempo container.
Definition: alsaqueue.h:130
void setPPQ(int value)
Sets the queue resolution in parts per quarter note.
Definition: alsaqueue.cpp:455
void setNominalBPM(float value)
Sets the queue's nominal tempo in BPM (beats per minute).
Definition: alsaqueue.cpp:529
MidiQueue * createQueue()
Create and return a new MidiQueue associated to this client.
void synchronizeOutput()
Wait until all sent events are processed.

To build a program using Drumstick, you may use CMake or Qmake. Using CMake you need first to build or install Drumstick on your development machine, and create a project file like the following, with the name: "CMakeLists.txt"

cmake_minimum_required(VERSION 3.16)
project(example LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Core REQUIRED)
find_package(Drumstick COMPONENTS ALSA REQUIRED)
add_executable(example main.cpp)
target_link_libraries( example
Qt5::Core
Drumstick::ALSA
)

Assuming that you have Qt 5.12.9 installed at your $HOME/Qt directory, and Drumstick is installed at $HOME/Drumstick, then you can configure and build your project with these commands: (your current directory is your project's)

mkdir build
cmake -S . -B build -DCMAKE_PREFIX_PATH="$HOME/Qt/5.12.9/gcc_64;$HOME/Drumstick"
cmake --build build

If you prefer to download the Drumstick sources and build it, you can also build your project without needing to install Drumstick. In this case:

mkdir build
cmake -S . -B build -DCMAKE_PREFIX_PATH=$HOME/Qt/5.12.9/gcc_64 -DDrumstick_DIR=$HOME/Source/Drumstick/build
cmake --build .

To run your Drumstick::RT programs without installing Drumstick and your program, you may need to use an environment variable to indicate the location of the plugins, like this:

export DRUMSTICKRT=$HOME/Source/Drumstick/build/lib/drumstick2/
./example

There are more examples in the source tree, under the utils/ directory, and you can also see applications using this library, like kmetronome, kmidimon and VMPK.

See also
https://dmidiplayer.sourceforge.io
https://kmetronome.sourceforge.io
https://kmidimon.sourceforge.io
https://vmpk.sourceforge.io
https://wrk2mid.sourceforge.io

Acknowledgments

Parts of this documentation are copied from the ALSA library documentation, whose authors are:

  • Jaroslav Kysela <perex AT perex.cz>
  • Abramo Bagnara <abramo AT alsa-project.org>
  • Takashi Iwai <tiwai AT suse.de>
  • Frank van de Pol <fvdpol AT coil.demon.nl>