Csound's ability to output midi data in realtime can open up many possibilities. We can relay the Csound score to a hardware synthesizer so that it plays the notes in our score instead of a Csound instrument. We can algorthmically generate streams of notes within the orchestra and have these played by the external device. We could even route midi data internally to another piece of software. Csound could be used as a device to transform incoming midi data, transforming, transposing or arpeggiating incoming notes before they are output again. Midi output could also be used to preset faders on a motorized fader box (such as the Behringer BCF 2000) to their correct initial locations.
The command line flag for realtime midi output is -Q. Just as when setting up an audio input or output device or a midi input device we must define the desired device number after the flag. When in doubt what midi output devices we have on our system we can always specify an 'out of range' device number (e.g. -Q999) in which case Csound will not run but will instead give an error and provide us with a list of available devices and their corresponding numbers. We can then insert an appropriate device number.
The analog of the opcode for the input of raw midi data, midiin, is midiout. midiout will output a midi note with its given input arguments once every k period - this could very quickly lead to clogging of incoming midi data in the device to which midi is begin sent unless measures are taken to prevent the midiout code from begin executed on every k pass. In the following example this is dealt with by turning off the instrument as soon as the midiout line has been executed just once by using the turnoff opcode. Alternative approaches would be to set the status byte to zero after the first k pass or to embed the midiout within a conditional (if... then...) so that its rate of execution can be controlled in some way.
Another thing to be aware of is that midi notes do not contain any information about note duration; instead the device playing the note waits until it receives a corresonding note-off instruction on the same midi channel and with the same note number before stopping the note. When working with midiout we must also be aware of this. The status byte for a midi note-off is 128 but it is more common for note-offs to be expressed as a note-on (status byte 144) with zero velocity. In the following example two notes (and corresonding note offs) are send to the midi output - the first note-off makes use of the zero velocity convention whereas the second makes use of the note-off status byte. Hardware and software synths should respond similarly to both. One advantage of the note-off message using status byte 128 is that we can also send a note-off velocity, i.e. how forcefully we release the key. Only more expensive midi keyboards actually sense and send note-off velocity and it is even rarer for hardware to respond to received note-off velocities in a meaningful way. Using Csound as a sound engine we could respond to this data in a creative way however.
In order for the following example to work you must connect a midi sound module or keyboard receiving on channel 1 to the midi output of your computer. You will also need to set the appropriate device number after the '-Q' flag.
EXAMPLE 07E01.csd
<CsoundSynthesizer> <CsOptions> ; amend device number accordingly -Q999 </CsOptions> <CsInstruments> ;Example by Iain McCurdy ksmps = 32 ;no audio so sr and nchnls omitted instr 1 ;arguments for midiout are read from p-fields istatus init p4 ichan init p5 idata1 init p6 idata2 init p7 midiout istatus, ichan, idata1, idata2; send raw midi data turnoff; turn this instrument off to prevent repeated iterations of midiout endin </CsInstruments> <CsScore> ;p1 p2 p3 p4 p5 p6 p7 i 1 0 0.01 144 1 60 100; note on i 1 2 0.01 144 1 60 0; note off (using velocity zero) i 1 3 0.01 144 1 60 100; note on i 1 5 0.01 128 1 60 100; note off (using 'note off' status byte) </CsScore> </CsoundSynthesizer>
The use of separate score events for note-ons and note-offs is rather a hassle. It would be more sensible to use the Csound note duration (p3) to define when the midi note-off is sent. The next example does this by utilizing a release flag generated by the release opcode whenever a note ends and sending the note-off then.
EXAMPLE 07E02.csd
<CsoundSynthesizer> <CsOptions> ; amend device number accordingly -Q999 </CsOptions> <CsInstruments> ;Example by Iain McCurdy ksmps = 32 ;no audio so sr and nchnls omitted instr 1 ;arguments for midiout are read from p-fields istatus init p4 ichan init p5 idata1 init p6 idata2 init p7 kskip init 0 if kskip=0 then midiout istatus, ichan, idata1, idata2; send raw midi data (note on) kskip = 1; ensure that the note on will only be executed once endif krelease release; normally output is zero, on final k pass output is 1 if krelease=1 then; i.e. if we are on the final k pass... midiout istatus, ichan, idata1, 0; send raw midi data (note off) endif endin </CsInstruments> <CsScore> ;p1 p2 p3 p4 p5 p6 p7 i 1 0 4 144 1 60 100 i 1 1 3 144 1 64 100 i 1 2 2 144 1 67 100 f 0 5; extending performance time prevents note-offs from being lost </CsScore> </CsoundSynthesizer>
Obviously midiout is not limited to only sending only midi note information but instead this information could include continuous controller information, pitch bend, system exclusive data and so on. The next example, as well as playing a note, sends controller 1 (modulation) data which rises from zero to maximum (127) across the duration of the note. To ensure that unnessessary midi data is not sent out, the output of the line function is first converted into integers, and midiout for the continuous controller data is only executed whenever this integer value changes. The function that creates this stream of data goes slightly above this maximum value (it finishes at a value of 127.1) to ensure that a rounded value of 127 is actually achieved.
In practice it may be necessary to start sending the continuous controller data slightly before the note-on to allow the hardware time to respond.
EXAMPLE 07E03.csd
<CsoundSynthesizer> <CsOptions> ; amend device number accordingly -Q999 </CsOptions> <CsInstruments> ;Example by Iain McCurdy ksmps = 32 ;no audio so sr and nchnls omitted instr 1 ; play a midi note ; read in values from p-fields ichan init p4 inote init p5 iveloc init p6 kskip init 0; 'skip' flag will ensure that note-on is executed just once if kskip=0 then midiout 144, ichan, inote, iveloc; send raw midi data (note on) kskip = 1; ensure that the note on will only be executed once by flipping flag endif krelease release; normally output is zero, on final k pass output is 1 if krelease=1 then; i.e. if we are on the final k pass... midiout 144, ichan, inote, 0; send raw midi data (note off) endif ; send continuous controller data iCCnum = p7 kCCval line 0, p3, 127.1; continuous controller data function kCCval = int(kCCval); convert data function to integers ktrig changed kCCval; generate a trigger each time kCCval (integers) changes if ktrig=1 then; if kCCval has changed midiout 176, ichan, iCCnum, kCCval; send a continuous controller message endif endin </CsInstruments> <CsScore> ;p1 p2 p3 p4 p5 p6 p7 i 1 0 5 1 60 100 1 f 0 7; extending performance time prevents note-offs from being lost </CsScore> </CsoundSynthesizer>
midiout is the most powerful opcode for midi output but if we are only interested in sending out midi notes from an instrument then the midion opcode simplifies the procedure as the following example demonstrates by playing a simple major arpeggio.
EXAMPLE 07E04.csd
<CsoundSynthesizer> <CsOptions> ; amend device number accordingly -Q999 </CsOptions> <CsInstruments> ;Example by Iain McCurdy ksmps = 32 ;no audio so sr and nchnls omitted instr 1 ; read values in from p-fields kchn = p4 knum = p5 kvel = p6 midion kchn, knum, kvel; send a midi note endin </CsInstruments> <CsScore> ;p1 p2 p3 p4 p5 p6 i 1 0 2.5 1 60 100 i 1 0.5 2 1 64 100 i 1 1 1.5 1 67 100 i 1 1.5 1 1 72 100 f 0 30; extending performance time prevents note-offs from being lost </CsScore> </CsoundSynthesizer>
Changing any of midion's k-rate input arguments in realtime will force it to stop the current midi note and send out a new one with the new parameters.
midion2 allows us to control when new notes are sent (and the current note is stopped) through the use of a trigger input. The next example uses midion2 to algorithmically generate a melodic line. New note generation is controlled by a metro, the rate of which undulates slowly through the use of a randomi function.
EXAMPLE 07E05.csd
<CsoundSynthesizer> <CsOptions> ; amend device number accordingly -Q999 </CsOptions> <CsInstruments> ;Example by Iain McCurdy ksmps = 32 ;no audio so sr and nchnls omitted instr 1 ; read values in from p-fields kchn = p4 knum random 48,72.99; note numbers will be chosen randomly across a 2 octave range kvel random 40, 115; velocities are chosen randomly krate randomi 1,2,1; rate at which new notes will be output ktrig metro krate^2; 'new note' trigger midion2 kchn, int(knum), int(kvel), ktrig; send a midi note whenever ktrig=1 endin </CsInstruments> <CsScore> i 1 0 20 1 f 0 21; extending performance time prevents the final note-off from being lost </CsScore> </CsoundSynthesizer>
midion and midion2 generate monophonic melody lines with no gaps between notes.
moscil works in a slightly different way and allows us to explicitly define note durations as well as the pauses between notes thereby permitting the generation of more staccato melodic lines. Like midion and midion2, moscil will not generate overlapping notes (unless two or more instances of it are concurrent). The next example algorithmically generates a melodic line using moscil.
EXAMPLE 07E06.csd
<CsoundSynthesizer> <CsOptions> ; amend device number accordingly -Q999 </CsOptions> <CsInstruments> ;Example by Iain McCurdy ksmps = 32 ;no audio so sr and nchnls omitted seed 0; random number generators seeded by system clock instr 1 ; read value in from p-field kchn = p4 knum random 48,72.99; note numbers will be chosen randomly across a 2 octave range kvel random 40, 115; velocities are chosen randomly kdur random 0.2, 1; note durations will chosen randomly from between the given limits kpause random 0, 0.4; pauses between notes will be chosen randomly from between the given limits moscil kchn, knum, kvel, kdur, kpause; send a stream of midi notes endin </CsInstruments> <CsScore> ;p1 p2 p3 p4 i 1 0 20 1 f 0 21; extending performance time prevents the final note-off from being lost </CsScore> </CsoundSynthesizer>
As well as (or instead of) outputting midi in realtime, Csound can render data from all of its midi output opcodes to a midi file. To do this we use the '--midioutfile=' flag followed by the desired name for our file. For example:
<CsOptions> -Q2 --midioutfile=midiout.mid </CsOptions>
will simultaneously stream realtime midi to midi output device number 2 and render to a file named 'midiout.mid' which will be saved in our home directory.