13 : A Musical Button

As we explored last month, csound can be used to create new instruments. How about creating instruments which sound like 'real' instruments? SoundFont is a trademark if E-mu. In 1990's E-mu came up with the idea of storing samples of sounds of real instruments and replaying them with the desired effects. The aim was that SoundBlaster cards from Creative Labs should be able to play MIDI files realistically.

A composer can compose music and explore it with an orchestra of a variety of instruments even if he is alone in his studio. We, however, are after a more limited goal.

It is one thing to look at the picture of a violin, a guitar, a sitar and quite another to hear what an instrument sounds like.

The TamTam activities included in OLPC/Sugar environment are really great tools for children to play with and explore music and sounds. We will try to explore and understand the underlying relationship between csound and SoundFonts.

On Fedora 10, the soundfonts are available in /usr/share/soundfonts. PCLite.sf2 is a part of the package, PersonalCopy-Lite-soundfont. Incidentally, this soundfont is also required by timidity++, a software player of MIDI files.

On Ubuntu 8.10, the soundfonts, /usr/share/sounds/sf2/FluidR3_GM.sf2, are installed by the fluid-soundfont packages.

The CSD File

Create the following sf2.csd file, with definition of an instrument which plays soundfont notes.

<CsoundSynthesizer>

<CsOptions>

</CsOptions>

<CsInstruments>

sr = 16000

ksmps = 160

nchnls = 2

gisf sfload "/usr/share/soundfonts/PCLite.sf2"

sfpassign 0, gisf

instr 1

ivel = p4

inotenum = p5

ipreindex = p6

iamp = p7

a1, a2 sfplay ivel, inotenum, iamp ,1, ipreindex, 0

outs a1, a2

endin

</CsInstruments>

<CsScore>

</CsScore>

</CsoundSynthesizer>

The above file structure is the same as discussed in last article. The soundfonts need to be loaded and presets(instruments) assigned. An orchestra instrument can play the soundfont notes using the sfplay opcode. You can find out more details in http://www.csounds.com/manual/html/sfplay.html.

The ivel parameter has a value between 0 and 127 and is the velocity or the vigour with which a musician plays the note – this translates into volume. You can use the iamp parameter as a factor to further adjust the volume.

The frequency is determined by inotenum, which also ranges between 0 and 127. The MIDI notes between 48 and 72 correspond to frequencies between 130.81Hz and 523.25Hz (http://www.phys.unsw.edu.au/jw/notes.html). This range shows the most noticeable difference between the various presets.

You may wish to experiment with various preset indexes, e.g. 0 is a grand piano, 24 is a guitar, 40 is a violin, 104 is a sitar, and 117 is a drum. The index is displayed when you will run csound for the above csd file (on Fedora but not on Ubuntu).

In the case of Ubuntu, you will need to replace the sfload command in the sf2.csd file by

gisf sfload "/usr/share/sounds/sf2/FluidR3_GM.sf2"

A better definition of an instrument to play soundfonts will be more complex, having an envelope as well.

The Python Code to Explore the Differences

Write the following code in play_sf2.py:

# Import the Csound API extension module.

import csnd

import sys

def add_score(csound,inst, dur, amp):

time = 0

vel = 127

for note in range(48,72):

csound.addNote(1, time, dur, vel, note, inst, amp)

time += dur

csound.addScoreLine('e 2')

# Command line parameters:

# Instrument Index, note duration, amplification

inst = int(sys.argv[1]) if len(sys.argv) > 1 else 0

note_duration = float(sys.argv[2]) if len(sys.argv) > 2 else 0.5

amp = float(sys.argv[3]) if len(sys.argv) > 3 else 1.0

print 'Instrument, note duration, amplification:', inst, note_duration, amp

# Create an instance of Csound.

csound = csnd.CppSound()

csound.setPythonMessageCallback()

csound.load('sf2.csd')

csound.setCommand('csound -dm0 -T -odac -+rtaudio=alsa temp.orc temp.sco')

add_score(csound,inst, note_duration, amp)

csound.exportForPerformance()

csound.perform()

The code is very similar to what you explored last month. The add_score method inserts notes to play on the first orchestra instrument.

You may pass command line parameters to specify the preset index, the note duration and the amplification. By default, program will play a piano with each note for half a second and amplification factor of 1.

The csound command in the code plays the notes directly and does not create an intermediary wave file.

Try the following options to compare a piano playing notes for a short and a long duration.

$ python play_sf2.py 0 0.3 2

$ python play_sf2.py 0 2 2

Now, compare a sitar with a violin

$ python play_sf2.py 104 2 2

$ python play_sf2.py 40 2 2

As you can imagine, you can easily spend a few hours studying the differences.

A One Button Music Player

So far, you have been writing a program and playing the notes. Can you program csound to play notes interactively, e.g. by pressing buttons or keys? The answer is, of course, yes!

The following command line option for csound will help

-L dnam read Line-oriented realtime score events from device 'dnam'

You will use the option to send the score in real time by using the device stdin!

For the minimal application, you will have 2 widgets to set the instrument index and note number. You can then use a Play button which will play the note as long as the button is pressed.

You can create a better version by replacing the note number text box by multiple buttons or map keys on the keyboard to note numbers.

First, you will write the code to start the csound server listening on the stdin. So, write the following code in tk_sf2.py, which uses the subprocess module. It will start the csound command and open a pipe for stdin.

import subprocess

def start_csound():

command = ['csound -dm0 -T -odac -+rtaudio=alsa -L stdin sf2.csd']

csd = subprocess.Popen(command, shell=True, stdin = subprocess.PIPE)

return csd

You will need to add a line to the score section of sf2.csd as follows:

<CsScore>

i 1 0 180

</CsScore>

The score duration of 180 seconds ensures that csound will listen on stdin for 3 minutes before shutting down. There must be a better solution but I could not find one so far.

Now, you can add the code to display a minimalist and very plain looking tkinter form. The form contains a list box to select the instrument, a text entry widget for the MIDI node number and a button which calls the method play_note when pressed and stop_note when released. You will fill in the code in these methods shortly.

def play_form():

def play_note(e):

pass

def stop_note(e):

pass

root = Tk()

instr_box = Listbox(root)

for instr in instruments:

instr_box.insert(END, instr)

instr_box.select_set(3)

instr_box.pack()

note_label=Label(root, text="Enter Note Number: 0-127")

note_label.pack()

note_box = Entry(root)

note_box.pack()

play = Button(root, text="Play")

play.bind("<Button-1>", play_note)

play.bind("<ButtonRelease-1>", stop_note)

play.pack()

return root

Now, add the code to start the csound server, fill in the instruments of interest in the list box and start the gui application.

csd = start_csound()

instruments = {'Piano':0, 'Guitar':24, 'Violin':40, 'Sitar':104, 'Drums':117}

play_form().mainloop()

Finally, you will replace the dummy play_note and stop_note methods with appropriate code:

def play_note(e):

notenum = note_box.get()

instr = instr_box.get(instr_box.curselection()[0])

preset_index = instruments[instr]

# i line: parameters p1..p7

# p1, p2, p3 : csound instrument, starting time, duration,

# p4..p7: velocity, Midi Note Num, Preset Index, Amp

csd.stdin.write('i 1 0 -1 127 ' + notenum + ' ' + str(preset_index) + ' 2\n')

def stop_note(e):

csd.stdin.write('i 1 0 0 \n')

For playing a note, you are picking up the preset index and note number. Then constructing the string and writing it on the stdin pipe of csound. A value of -1 for duration implies that you keep playing the note till another score command is received for the instrument. Since it is being played in realtime, the start time indicates the amount of delay after this score line has been received.

So, the stop_note method is simple. It just writes a score to play the piano for 0 time with 0 velocity; hence, stopping the note which was being played.

This leads to the issue what if you need to create an orchestra of multiple instruments? You will need to modify sf2.csd file to define additional instruments like the first one. Then, independent score commands can be given to each of the instruments.

Now, imagine programming each of the many instruments and, then, imagine that the effect is a delight when all these instruments play together. No wonder great composers are a rarity!




<Prev>  <Next>
Comments