Living in a shell

Since I started working for my new employer the amount of time I’m spending inside a terminal window is rapidly increasing. And I like it. I’m learning more in a few months than I did in the past 5 years. I’m discovering superhandy commands and utilities that I had never used or even heard of before. Utilities like w, last and the various *stat utilities. And I’m becoming better and better in using utilities that I already know but that always remained hard to grasp simply because I didn’t use them extensively. Think of Vim, screen and sed. Or on a lower level, the Bash shell itself.

I’m particularly starting to develop a fondness for Vim. I’ve worked myself through a complete Vim book and when I finished it I was like, Vim is not arcane at all, it’s actually quite simple. It’s all about terseness, doing things in the fastest, most efficient way, memorizing the most important commands and forcing myself to use it for things I would normally do in say, gedit. So now I find myself easily copying whole blocks of text, commenting and uncommenting multiple lines with just a few keystrokes, using markers and buffers and browsing faster through files than possible with a mouse.

Currently I’m reading a book on Bash and it already provided me with a lot of new insights and ideas that I could use in my daily work. My scripting skills are a bit feeble so hopefully this book will help me to improve these. Luckily I have great colleagues that are very knowledgeable when it come to things like Bash and Vim so I’m coming along just fine. But I want to be able to purge a Sendmail mail queue filled with spam like I saw one of my colleagues do recently. What he did on top of his head was just amazing:

for i in `ls | grep qfr`; do w=`grep example.org $i | wc -l`; if [ "$w" != "0" ]; 
then echo $i | sed -e 's/^qfr//'; fi;  done | sed -e 's/^/*/' | xargs -n 50000;

This allowed him to create a list of all spam messages which he simply ran through rm. It’s no rocket science but I’d really like to be that proficient too. Another colleague of mine is just awesome with regular expressions and Vi. For example, I recently asked him how I could delete all text between parentheses including the parentheses, for example a list of packages I copied from /var/log/apt/history.log, and he immediately replied %s/([^)]*)//g. Yes, I’m blessed that I can work in such an inspiring environment.

Living in a shell

Using a Raspberry Pi as a piano

Recently I posted about my successful attempt to get LinuxSampler running on the Raspberry Pi. I’ve taken this a bit further and produced a script that turns the Raspberry Pi into a fully fledged piano. Don’t expect miracles, the sample library I used is good quality so the RPi might choke on it every now and then with regard to disk IO. But it’s usable if you don’t play too many notes at once or make extensive use of a sustain pedal. I’ve tested the script with a Class 4 SD though so a faster SD card could improve stability.

Edit: finally got around buying a better SD card and the difference is huge! I bought a SanDisk Extreme Class 10 SD card and with this SD card I can run LinuxSampler at lower latencies and I can play more notes at once.

Before you can run the script on your Raspberry Pi you will need to tweak your Raspbian installation so you can do low latency audio. How to achieve this is all described in the Raspberry Pi wiki article I’ve put up on wiki.linuxaudio.org. After you’ve set up your RPi you will need to install JACK and LinuxSampler with sudo apt-get install jackd1 linuxsampler. Next step is to get the Salamander Grand Piano sample pack on your RPi:

cd
mkdir LinuxSampler
cd LinuxSampler
wget -c http://download.linuxaudio.org/lau/SalamanderGrandPianoV2
/SalamanderGrandPianoV2_44.1khz16bit.tar.bz2
wget -c http://dl.dropbox.com/u/16547648/sgp44.1khz_V2toV3.tar.bz2
tar jxvf SalamanderGrandPianoV2/SalamanderGrandPianoV2_44.1khz16bit.tar.bz2
tar jxvf sgp44.1khz_V2toV3.tar.bz2 -C SalamanderGrandPianoV2_44.1khz16bit
--strip-components=1

Please note that decompressing the tarballs on the RPi could take some time. Now that you’ve set up the Salamander Grand Piano sample library you can download the script and the LinuxSampler config file:

cd
mkdir bin
wget -c https://raw.github.com/AutoStatic/scripts/rpi/piano -O /home/pi/bin/piano
chmod +x bin/piano
wget -c https://raw.github.com/AutoStatic/configs/rpi/home/pi/LinuxSampler
/SalamanderGrandPianoV3.lscp -O
/home/pi/LinuxSampler/SalamanderGrandPianoV3.lscp

Almost there. We’ve installed the necessary software and downloaded the sample library, LinuxSampler config and piano script. Now we need to dot the i’s and cross the t’s because the script assumes some defaults that might be different in your setup. Let’s dissect the script:

#!/bin/bash

if ! pidof jackd &> /dev/null
then
  sudo killall ifplugd &> /dev/null
  sudo killall dhclient-bin &> /dev/null
  sudo service ntp stop &> /dev/null
  sudo service triggerhappy stop &> /dev/null
  sudo service ifplugd stop &> /dev/null
  sudo service dbus stop &> /dev/null
  sudo killall console-kit-daemon &> /dev/null
  sudo killall polkitd &> /dev/null
  killall gvfsd &> /dev/null
  killall dbus-daemon &> /dev/null
  killall dbus-launch &> /dev/null
  sudo mount -o remount,size=128M /dev/shm &> /dev/null
  echo -n performance
| sudo tee /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor &> /dev/null
  if ip addr | grep wlan &> /dev/null
  then
    echo -n "1-1.1:1.0" | sudo tee /sys/bus/usb/drivers/smsc95xx/unbind &> /dev/null
  fi
  jackd -P84 -p128 -t2000 -d alsa -dhw:UA25 -p512 -n2 -r44100 -s -P -Xseq
&> /dev/null &
fi

This is the first section of the script. An if clause that checks if JACK is already running and if that’s not the case the system gets set up for low latency use, a simple check is done if there is an active WiFi adapter and if so the ethernet interface is disabled and then on the last line JACK is invoked. Notice the ALSA name used, hw:UA25, this could be different on your RPi, you can check with aplay -l.

jack_wait -w &> /dev/null

jack_wait is a simple app that does nothing else but checking if JACK is active, the -w option means to wait for JACK to become active.

if ! pidof linuxsampler &> /dev/null
then
  linuxsampler --instruments-db-location $HOME/LinuxSampler/instruments.db
&> /dev/null &
  sleep 5
netcat -q 3 localhost 8888
< $HOME/LinuxSampler/SalamanderGrandPianoV3.lscp &> /dev/null &
fi

This stanza checks if LinuxSampler is running, if not LinuxSampler is started and 5 seconds later the config file is pushed to the LinuxSampler backend with the help of netcat.

while [ "$STATUS" != "100" ]
do
  STATUS=$(echo "GET CHANNEL INFO 0" | netcat -q 3 localhost 8888
| grep INSTRUMENT_STATUS | cut -d " " -f 2 | tr -d 'rn')
done

A simple while loop that checks the load status of LinuxSampler. When the load status has reached 100% the script will move on.

jack_connect LinuxSampler:0 system:playback_1 &> /dev/null
jack_connect LinuxSampler:1 system:playback_2 &> /dev/null
#jack_connect alsa_pcm:MPK-mini/midi_capture_1 LinuxSampler:midi_in_0 &> /dev/null
jack_connect alsa_pcm:USB-Keystation-61es/midi_capture_1 LinuxSampler:midi_in_0
&> /dev/null

This part sets up the necessary JACK connections. The portnames of the MIDI devices can be different on your system, you can look them up with jack_lsp which will list all available JACK ports.

jack_midiseq Sequencer 176400 0 69 20000 22050 57 20000 44100 64 20000 66150 67 20000 &
sleep 4
jack_connect Sequencer:out LinuxSampler:midi_in_0
sleep 3.5
jack_disconnect Sequencer:out LinuxSampler:midi_in_0
killall jack_midiseq

This is the notification part of the script that will play four notes. It’s based on jack_midiseq, another JACK example tool that does nothing more but looping a sequence of notes. It’s an undocumented utility so I’ll explain how it is invoked:

jack_midiseq

<command> <JACK port name> <loop length> <start value> <MIDI note value> <length value>

Example:
jack_midiseq Sequencer 176400 0 69 20000 22050 57 20000 44100 64 20000 66150 67 20000

JACK port name: Sequencer
Loop length: 4 seconds at 44.1 KHz (176400/44100)
Start value of first note: 0
MIDI note value of first note: 69 (A4)
Length value: 20000 samples, so that's almost half a second
Start value of second note: 22050 (so half a second after the first note)
MIDI note value of second note: 57 (A3)
Length value: 20000 samples
Start value of third note: 44100 (so a second after the first note)
MIDI note value of second note: 64 (E4)
Length value: 20000 samples
Start value of third note: 66150 (so one second and a half after the first note)
MIDI note value of second note: 67 (G4)
Length value: 20000 samples

Now the script is finished, the last line calls exit with a status value of 0 which means the script was run successfully.

exit 0

After making the script executable with chmod +x ~/bin/piano and running it you can start playing piano with your Raspberry Pi! Again, bear in mind that the RPi is not made for this specific purpose so it could happen that audio starts to stutter every now and then, especially when you play busy parts or play more than 4 notes at once.


Using a Raspberry Pi as a piano: quick demo

Using a Raspberry Pi as a piano

Nog meer mididings

Mididings script voor 29 oktober is klaar:

#!/usr/bin/python

from mididings import *
from mididings.extra import *

config(
    backend='jack',
    client_name='sodap-live',
    data_offset=0,
)

run(
    Filter(PROGRAM) >> [
        [
            ProgramFilter(0) >> NoteOn(64,50),
            ProgramFilter(7) >> NoteOn(69,86),
            ProgramFilter(0) >> NoteOn(72,60),
            ProgramFilter(1) >> NoteOn(74,62),
            ProgramFilter(2) >> NoteOn(76,64),
            ProgramFilter(3) >> NoteOn(71,30),
            ProgramFilter(7) >> NoteOn(77,88),
            ProgramFilter(8) >> NoteOn(79,96),
            ProgramFilter(9) >> NoteOn(81,84),
        ] >>
        LatchNotes() >> [
            KeyFilter(64) % NoteOff(69,0),
            KeyFilter(69) % NoteOff(64,0),
        ],
        ProgramFilter(4) >> Panic(),
        ProgramFilter(10) >>
        Channel(2) >>
        NoteOn(36,64),
    ]
)

Met dit script kan ik nu de strijkers spelen in Soda P – Icon of your Time en heb een Vibraslap sample van Freesound.org in een soundfontje gezet met Swami zodat ik deze met de FluidSynth DSSI plug-in ook vanuit Qtractor kan triggeren met behulp van de Boss FC-50. De strijkers gaan via het eerste paralelle ‘blok’ waarin Program Changes worden omgezet in Note Ons, daarna gaan ze door de LatchNotes Unit en daarna worden in een genest parallel blok nog twee Note Offs gedefinieerd. Stuurt de FC-50 Program Change 4 uit dan worden alle noten uitgezet (‘Panic’), bij Program Change 10 stuurt mididings een MIDI noot 36 op MIDI kanaal 2 naar Qtractor voor de Vibraslap. Het config deel aan het begin van het script vertelt mididings dat er JACK MIDI poorten aangemaakt moeten worden met als naam ‘sodap-live’. data_offset=0 is nodig omdat de FC-50 bij 0 begint met tellen en niet bij 1.

Nog meer mididings

Bash Session Management

Zo af en toe krijg ik de vraag wat voor session manager ik gebruik om mijn projecten en de daarbijbehorende applicaties en JACK connecties te beheren. Mijn antwoord is dan: Bash. Dus géén Ladish, géén JACK Session en ook géén aj-snapshot maar simpele Bash scripts. Waarom? Omdat ik Bash begrijp en momenteel geen tijd en zin heb om me te verdiepen in nóg een extra tool. Daarnaast is het aantal applicaties dat Ladish en JACK Session ondersteunt echt minimaal.

Maar hoe ziet zo’n Bash script er dan uit? Nou zo:

#!/bin/bash
export SESSION=$HOME/Sessions/moneyorlove.d

hydrogen -s $SESSION/moneyorlove.h2song &
seq24 -j $SESSION/seq24-moneyorlove.midi &
sleep 2
yoshimi -N analoguebass --state=$SESSION/yoshimi-analoguebass.state &
yoshimi -N dubstep --state=$SESSION/yoshimi-dubstep.state &
sleep 2
guitarix -r default -f $SESSION/guitarix_moneyorlove_bass_rc &
sleep 2
amsynth -b $SESSION/moneyorlove.amSynth.presets &
sleep 2
qtractor $SESSION/moneyorlove.qtr &
sleep 3
export SEQ24_BASS_OUT=$(jack_lsp | grep "(capture): Yoshimi Analogue")
export SEQ24_DUBSTEP_OUT=$(jack_lsp | grep "(capture): Yoshimi Dubstep")
export DRONE1=$(jack_lsp | grep "Feedback Drone/out_1")
export DRONE2=$(jack_lsp | grep "Feedback Drone/out_2")
aconnect Hydrogen:1 MPK
jack_connect "$SEQ24_BASS_OUT" yoshimi-analoguebass:midi in
jack_connect "$SEQ24_DUBSTEP_OUT" yoshimi-dubstep:midi in
jack_disconnect "$DRONE1" "system:playback_1"
jack_disconnect "$DRONE2" "system:playback_2"

Ok, wat gebeurt hier?

export SESSION=$HOME/Sessions/moneyorlove.d

Ik maak als eerste een SESSION variable aan met als waarde de directory waar alle benodigde bestanden voor mijn sessie in staan. In mijn home staat een Sessions directory en daar staan alle sessie directories en bijbehorende Bash scripts in. Vervolgens start ik Hydrogen en seq24 op met de parameters om de bijbehorende project bestanden in te laden:

hydrogen -s $SESSION/moneyorlove.h2song &
seq24 -j $SESSION/seq24-moneyorlove.midi &

Daarna laat ik Bash even twee tellen uitrusten

sleep 2

om vervolgens twee instances van Yoshimi (respectievelijke JACK poortnamen analoguebass en dubstep, kun je opgeven met de -N optie) op te starten met de bijbehorende .state bestanden. Je kunt met Yoshimi ook .xmz (parameter) bestanden opslaan, komt bijna op hetzelfde neer.

yoshimi -N analoguebass --state=$SESSION/yoshimi-analoguebass.state &
yoshimi -N dubstep --state=$SESSION/yoshimi-dubstep.state &

Weer even pas op de plaats:

sleep 2

Guitarix opstarten met de standaard instellingen (kun je aanpassen in Guitarix) en een Guitarix state file inladen:

guitarix -r default -f $SESSION/guitarix_moneyorlove_bass_rc &

Bash weer uit laten puffen:

sleep 2

amSynth starten met de juiste .presets file:

amsynth -b $SESSION/moneyorlove.amSynth.presets &

Even wachten…

sleep 2

Qtractor laden en een .qtr sessie file laten openen:

qtractor $SESSION/moneyorlove.qtr &

Hier speel ik een beetje vals want in feite gebruik ik Qtractor voor een deel als session manager. Qtractor slaat in de .qtr sessie file namelijk alle JACK connecties op die naar en van Qtractor gemaakt worden. Nadat Qtractor is gestart laat ik Bash weer even bijkomen:

sleep 3

Aangezien Qtractor dus alleen JACK connecties opslaat van en naar Qtractor moet ik de resterende JACK connecties handmatig aanmaken. Dit doe ik met jack_connect. De benodigde ALSA MIDI connecties die Qtractor niet kan opslaan zet ik op mbv aconnect. Maar eerst moet ik wat poortnamen toewijzen aan variabelen om er zeker van te zijn dat de juiste poorten met elkaar verbonden worden. Hier gebruik ik jack_lsp voor, de CLI JACK tool om een lijstje te genereren van beschikbare JACK poorten, en hier laat ik grep op los om de juiste poorten eruit te pikken.

export SEQ24_BASS_OUT=$(jack_lsp | grep "(capture): Yoshimi Analogue")
export SEQ24_DUBSTEP_OUT=$(jack_lsp | grep "(capture): Yoshimi Dubstep")
export DRONE1=$(jack_lsp | grep "Feedback Drone/out_1")
export DRONE2=$(jack_lsp | grep "Feedback Drone/out_2")

Lest best de JACK connecties die Qtractor niet kan maken:

aconnect Hydrogen:1 MPK
jack_connect "$SEQ24_BASS_OUT" yoshimi-analoguebass:midi in
jack_connect "$SEQ24_DUBSTEP_OUT" yoshimi-dubstep:midi in

En op de valreep nog even wat onnodige poorten disconnecten:

jack_disconnect "$DRONE1" "system:playback_1"
jack_disconnect "$DRONE2" "system:playback_2"

En nu is mijn volledige sessie opgestart en klaar om mee te werken. Twee dingen vallen waarschijnlijk al meteen op: 1. de ampersands (&) en 2. de vele sleep commando’s. Waarom? 1. een ampersand toevoegen aan een commando zorgt ervoor dat het commando op de achtergrond gaat draaien en het script door kan gaan met de volgende regel, oftewel, het script stopt niet om het commando af te wachten, 2. sommige applicaties hebben even tijd nodig om op te starten en het is cruciaal dat als de sessie file van Qtractor ingeladen wordt alle applicaties zijn opgestart zodat Qtractor de JACK connecties kan aanmaken.

Ja, en wat nu als je je sessie af wilt sluiten? Moet je dan alle applicaties handmatig afsluiten? Ja. Maar zo af en toe gebruik ik ook een ‘end-session’ script die alle openstaande audio applicaties killt. Dit kun je ook netjes scripten in je sessie script zelf, zo ver ben echter nog niet en vooralsnog red ik me prima zo. Als ik klaar ben zet ik toch meestal mijn PC uit en het komt zelden voor dat ik tijdens het maken van muziek meerdere sessies na elkaar moet openen.

Bash Session Management