Score Following and Event Queueing in MAX

The patch "SCORE_FOLLOWING.DEMO" uses two patches (abstractions located in the same folder as "SCORE_FOLLOWING.DEMO) which are:"QLIST.CONTROL"and "EXP.FOLLOW". They are loaded automatically when "SCORE_FOLLOWING.DEMO" is loaded.

"SCORE_FOLLOWING.DEMO" is an application of score following using the objects qlist and explode, and will only make sense after the Help Windows for qlist and explode have been studied.

"SCORE_FOLLOWING.DEMO" has a patcher entitled "README" on its front panel. It explains how to use "SCORE_FOLLOWING.DEMO".

- Cort Lippe

README PATCHER CONTENTS

Introduction

Here are some directions for experimenting with this score following demonstration. The first suggestion would be to study the two help windows: "explode.pat" and "qlist.pat". Once you have an understanding of the two objects, explode and qlist, you should continue with this README and try this demonstration patch SCORE_FOLLOWING.DEMO.

open these patchers to continue:

The Basic Idea

Basically, the idea here is to divide up a "musical composition" into sections. Each section has its own explode/qlist configuration. Each explode contains the "score" (your music) for a specific section. The explode also contains control information which acts as "event" markers. These event markers are numbered in ascending order. They serve as cues which are used to trigger the events in your qlist. The qlist contains your "electronic score". As you, or another musician, play your score, the explode can follow your playing, comparing your incoming notes to the score in the explode (which you should have previously recorded with event markers). As you arrive at an event marker, it advances the qlist and triggers your electronic events.

Take a look at the explode below. Double-click on it to open it, then click on "show ranges" and put the pitch parameter in the musical notation grid for ease of viewing. Select the "#" cell of the velo parameter. While you are at it click on the up/down arrow of time and shrink the score. Now,

you should be able to see the entire score at a glance. At the top of the score is a line of ascending numbers from 0 through 10. These are event markers. They are recorded on a high pitch which is not usually used (MIDI note-number 125) and on MIDI channel 2. Verify these statements and you will also notice that the "musical" score is recorded on MIDI channel 1.

Recording a Score

Assuming that you have a MIDI keyboard hooked up, and that it is sending notein values on channel 1, you are almost ready to start recording. (You can check MIDI first by putting an X in the check MIDI toggle on the front panel of this demo patch and playing something.) The demo has two sections, numbered 1 and 2. The first thing to do is record a score which you can later try to follow.

The patcher "midi-in" takes apart incoming MIDI to send to the patch EXP.FOLLOW. Inside patcher "midi-in", on the top left-hand side is a notein object. On the right-hand side is a ctlin 64. The controller can be changed to anything, but each 'bang' from it INCREMENTS a counter which writes incrementing event markers into your score. These event markers serve as the event numbers. (With a ctlin 64, a simple footswitch can be used to mark the events, but you can even send a 'bang' with the mouse if you wish.) As you record your musical score, you must hit the footpedal BEFORE the note which you have decided will trigger an event. (If you do not have any notes before event #1, hit the footpedal twice before you start playing, since the event numbers should start at zero.) As you alternate between your score input and event number input, do not worry about rhythm. For score following, the tempo and rhythm of the score which is recorded in an explode is not important, so you can input the score at any speed.

Before recording the score, decide where your event markers will be. The method for putting an explode into "record" mode is to initialize a section on the front panel of SCORE_FOLLOWING.DEMO by clicking the number box 1 or 2 in the top left-hand corner. Initialize section 1 for this explanation, and then hit the record button. The record button is safely hidden away in the patcher "work". Open this patcher and you will find the record button on the right-hand side of the patcher. Do not forget that hitting this button after initializing a section will erase any existing score which is already in the explode. Once you have finished recording your score and events, hit a zero on the front panel. This general reset takes the explode out of "record" mode. Afterwards you might want to open the explode and check your work. To see what you have recorded, hit the GET button which is found at the right of the parameter box. A copy of what you recorded into the explode will be sent up to the the explode object which exists on the NeXT. (See the "editing" patcher of the help window for explode if this is not clear.) If everything looks correct, you can try following the score. Go and open the next patcher in this README now.

Score Following

You should re-initialize your section number and this time: DO NOT HIT THE RECORD BUTTON! Initializing a section automatically puts the explode for the appropriate section in "follow" mode if you do not hit the record button afterwards. Now try playing your score. (You can forget about the footpedal now, it is only used for recording the events.) As you play the score, watch the front panel of this demo patch. You should see the "note_from_score" receive printing note numbers as matches between the incoming notes and the recorded explode notes are made. When an event arrives, the "event_number" receive will display the event in its number box. As you play, the qlist receives the event numbers and advances through its event list. This can be verified by looking at the MAX window to see if anything is being printed while you play the score.

The Electronic Score

Open the qlist object of section 1. You will see events numbered 1 to 11. After studying the rest of this README, feel free to add your own actions into the score, or to use this qlist as a template and try inputting your own electronic score in place of the events which are there now.

EXP.FOLLOW

EXP.FOLLOW takes 3 arguments: the section number, the section number to succeed, and the number of events in the section. Using this information, a counter automatically initializes the succeeding section when a section is finished. (Section 2, being the last section of this example, goes to section 0, which is just a convenient manner to stop.) Opening EXP.FOLLOW, you can see that the "pitch_to_explode" receive gets the pitches being played from the patcher "midi_in" and sends them to the explode object. The 'followat' message to explode enables the explode to be advanced by hand, or with the metronome, to a particular event, and then followed starting with that event. The incoming velocities and channels on the right-hand side are used to filter the notes which are not on channel 2 and send out the event numbers (velocities) to the qlist control patch QLIST.CONTROL for notes found on channel 2. A check is also done to see if the last event has been reached. If yes, then the next section is initialized.

QLIST.CONTROL

The basic concepts and operation of QLIST.CONTROL are explained in EXAMPLE 3 of the qlist Help Window "qlist.pat" in the MAX help folder. The explanation is repeated here:

QLIST.CONTROL takes one argument which specifies the section number. (This argument is not used except as a convenient label.) The output from EXP.FOLLOW going into QLIST.CONTROL is event numbers. If the event number is 0, the qlist object is sent a 'rewind' and a 'next' by QLIST.CONTROL. Successive event numbers advance the qlist through its queue of events. The syntax created via QLIST.CONTROL is the following: in the qlist object connected to QLIST.CONTROL, if there are two numbers at the beginning of a line which are not related to a variable send, they represent a delay time in milliseconds and an event number. A single number at the beginning of a line, before any variable send on the same line, represents a delay time in milliseconds. Thus, event 2 starts at time 0 with a send of a message to var1 and then the value 7500 to var1. After a delay of 2000 milliseconds, var2 is sent a message. Click successively from event 0, which rewinds the qlist, through event 10 and compare the values in the MAX print window with the values in the qlist. Note that events 2 and 5 have time-delayed sends after time zero.

Note also that you can click through the events at any speed you like. If you advance to a succeeding event before all the timed delays have been triggered in the present event, the delay times will automatically be set to zero and the variable sends will be done immediately. In this way, variable 'state' is always kept up to date. For instance: in event 2 there is a time delay of 2 seconds before var2 will be sent a message. If event 3 is triggered after event 2, but BEFORE 2 seconds have elapsed, var2 will immediately be sent the message with a delay time of zero, ignoring the original delay time of 2 seconds. Note that this example, unlike the file "qlist.help" does not have the possibility of a nonexecutable 'next' message. Feel free to add that option if you wish. Note also that there is an extra dummy event at the end of qlist. QLIST.CONTROL expects an extra event in order to stop looping at the end of the events. The last "real" event in this example is event number 10. Event number 11 is a dummy event with no actions. One last detail: the dashed lines: (-----------------------) with the event number following, are simply there to act as visual separators for events in the queue and serve no other purpose.

Explode Help Patcher

Arguments: none

Inlet: integers and messages - time, pitch, velocity, duration, channel

Outlet: integers - time, pitch, velocity, duration, channel

Explode (version 0.06), by Miller Puckette, is a powerful graphical editor which allows you to input sequences of numbers via inlets, and to input and edit sequences graphically via the mouse and keyboard of your computer. (See "EXPLODE: A User Interface for Sequencing and Score Following" by Puckette in the Proceedings, International Computer Music Conference, 1990, Glasgow). The parameter names for input and output from right to left are: channel, duration, velocity, pitch, and time. For MIDI purposes, these parameter names are useful, but the explode object can be used with values of any nature as a general-purpose object for the storing and sequencing of numbers. (Note that a patch must be saved in order to save any changes made to the contents of an explode.)

Editing

Double-click on the explode object above to open it. You will see a collection of events, represented by straight-line segments, and a control panel. From left to right, the control panel contains three input/edit mode boxes, three indicators for record/follow/play, two toggles, and a "parameter box".

In the upper left-hand corner of the control panel, the three input/edit boxes can be selected to activate the "input tool", the "length tool", and the "edit tool".

The input tool (represented by a pencil icon) is used to create new events in the explode. Each new event is drawn as a horizontal straight-line segment on the xy grid by clicking and dragging the mouse.

The length tool (represented by a horizontal-arrow icon) is used to modify the duration of existing events. To modify the duration of an event, select the event by clicking it, then drag the mouse to shorten or lengthen the event's line segment on the grid. Several events can be modified simultaneously by making a multiple selection of the events (depress the "shift" key while selecting), then dragging the mouse.

The edit tool (represented by the upward-pointing arrow) allows you to select events for various types of editing operations. With the edit tool selected, events which have been selected can be moved via the keyboard arrows. Selected events can also be moved, depending on the starting direction, on the x or on the y axis with the mouse. They can be cut with a "control-x" or "delete", and the MAX "Edit" menu can be used as well. They can be duplicated by holding down the "option" key (also called "alt" key) while dragging the selected events in a horizontal or vertical direction. You can also cut and paste from another explode or from a text editor.

The parameter box contains cells for showing and editing the numerical values of each parameter, and switches for choosing which parameter to show as "y" (the vertical location of an event), "w" (the horzontal width), and "#" (a number printed above the event). Note that if you select an event by clicking on it with the mouse, the parameter box will print the parameter values for the event in the long rectangular cells next to each parameter name. (Selecting a group of events with differing parameter values will show a "*" in the parameter box.) The "y, w, #" cells allow you to view parameters in different ways, the "#" cell lets you see the values of parameters as you edit them.

With the edit tool selected, you can toggle "show grid" and "show ranges". If "show ranges" is not highlighted (not selected), you can also edit specific parameters of events by clicking in the long rectangular cell to the right of the parameter in the parameter box. Note that a black dot appears in the cell. Now you can select an event or a group of events and type a new value for the parameter which has a black dot by its name, and immediately follow with a "return" on the keyboard.

If the "show ranges" toggle is highlighted (selected), the scale of the five explode parameters on the xy axis is printed in the parameter box. You can change (zoom) the scale of any parameter by clicking on the small up/down arrows or by clicking in the long rectangular cell of any parameter (noting the black dot that appears) and typing the desired scale in the rectangle. By clicking the cell just to the right of the little up/down arrows of any parameter while the "y" cell is highlighted for the parameter, the parameter can be viewed in 10-grid, 12-grid, and in a simple standard musical notation grid.

Since in early versions, Explode has no scroll bar, a feature was introduced which is unavailable on the MacIntosh: if no items are selected, you may type a time into the "time" slot of the parameter box and type in a new scroll position. BE SURE NOTHING IS SELECTED -- otherwise instead of scrolling you will move the selected notes to the time you typed.

Explode has another special feature which is unavailable on the MacIntosh: since there is a copy of MAX running on the NeXT as well as on the IRCAM board (ISPW), it is necessary to SEND a copy of anything you edit in an explode object (via the mouse and NeXT keyboard) down to the ISPW. Likewise, anything you have recorded into the explode object (via MIDI or internally in a patch) on the ISPW needs to be sent up to the NeXT so that you can see it! Thus, while editing or recording an explode object, you will see two grey buttons appear just to the right of the parameter box. The button "SEND" sends down the copy you were editing on the NeXT to the ISPW. The button "GET" sends anything you have recorded into the explode object on the ISPW to the explode object on the NeXT.

Messages

Explode accepts the following messages:

'start' puts explode into the "play" mode and outputs the delay before the first event without outputting the event itself.

'startat' <pitch> <velocity> <channel> searches from the beginning of a sequence until if finds the first event with a matching pitch, velocity, and channel, and then explode enters into the "play" mode pointing to the next event following the described event. The time difference between the matched event and the next one is output.

'next' sends out the first event and the next delay time before the following event. Thus 'next' messages step through a sequence event-by-event when in the "play" mode.

'nth' <event number> outputs the ordinal nth event in a list when in the "play" mode. The left-most outlet gives the CUMULATIVE time up to that event.

'stop' returns explode to the editing mode. You can also click on a tool in the control panel of explode to stop.

'record' completely erases any existing event list and puts explode in the "record" mode.

'follow' puts explode in the "follow" mode pointing to the first event.

'followat' <pitch> <velocity> <channel> searches from the beginning of a sequence until if finds the first event with a matching pitch, velocity, and channel, and then explode enters into the "follow" mode pointing to the next event coming after the described event.

'params' <nskip> <timeskip> <octave> sets score following parameters for use with 'follow' or 'followat'. The performer is allowed to miss <nskip> notes and then is allowed to blank out for <timeskip> milliseconds in the score. <Octave>, if nonzero, directs the follower to accept events with an octave error, either up or down, for the pitch parameter. Thus "params 2 200 0", which is the default for 'params', will allow up to two events successively as errors and then skip over 200 milliseconds in the score in looking for the next notes to follow.

'import'and 'export' allow you to read and write disk files to and from an explode object. <DO NOT EXIST YET ON THE ISPW!>

Play/Record/Follow

play/record/follow modes

The play/record/follow modes cause the respective indicators on the explode control panel to highlight.

"play" mode: in this mode, which is set by a 'start' or a 'startat' message, the 'next' message puts the next event out outlets 2 through 5 of the explode object and if there is another event afterwards it puts the time delay for it out the first outlet (the left-most outlet). Incoming notes are ignored in this mode.

"record" mode: incoming events are recorded into the event list. If the incoming event has a duration specified, the note gets that duration. If nothing has been put in the duration inlet since the previous event, the event is matched with a later "note off" (a zero-velocity note of the same pitch and channel).

"follow" mode: incoming events are compared with the event list and matches are sent out the outlets 2 through 5 (nothing comes out of the first outlet). If the incoming event has no velocity specified, a note-on is assumed. (A note-off attempts to match a previously matched note-on and if successful, re-sends the matched note with the velocity set to zero.)

Example

an explode example interfaced with MAX

To make use of its play/record/follow modes, explode can be interfaced with MAX. One possible MAX interface is shown in the patcher below:

Open the patcher and you will see that you can input events via MIDI, and then playback or follow your score also via MIDI. There are message boxes for playback, recording, following, writing data into the explode, and reading and writing files from/to the disk.

Qlist Help Patcher

Arguments: none;

Inlet: 'next' and 'rewind', as messages Outlet: lists of ints and floatsArguments: none;

Inlet: 'next' and 'rewind', as messages Outlet: lists of ints and floats

Qlist (version 0.02), by Miller Puckette, outputs and executes sequences of events found in its queue. Qlist accepts three messages: 'next' and 'rewind'. Qlist outputs integers and floating-point numbers as a list. Qlist recognizes the semicolon as a separator between lists of numbers. Variable names can also be included in qlist, and qlist sends values associated with a variable, that is to say, any values following a variable name and succeeded by a semicolon, to corresponding receives.

Double-click on the qlist object box above and the qlist text subwindow will open. This text window can be edited, but must be closed before any changes take effect. Note that a patch must be saved in order to save any changes made to the contents of its qlist. After clicking on 'rewind', advance through the qlist by clicking successively on 'next'. The values separated by a semicolon are output as lists and printed in the MAX window.

EXAMPLE 2

In example 2, variables are used. Their corresponding receives print their values in the MAX window. The lists of numbers not associated with a variable are also printed as in the first example. (Note that qlist itelf does not output the values associated with a variable, but that the corresponding receive is connected to a print object which prints the values.)

EXAMPLE 3

A final example shows one possible MAX interface to qlist for sending variable values to corresponding receives and for separating groups of variables and their values into successive events. This example also allows for time-delayed variable sends within an event. The syntax created via the patcher 'qlist_control' is the following: in the qlist object connected to 'qlist_control', if there are two numbers at the beginning of a line which are not related to a variable send, they represent a delay time in milliseconds and an event number. A single number at the beginning of a line, before any variable send on the same line, represents a delay time in milliseconds. Thus, event 2 starts at time 0 with a send of the value 7500 to var1. After a delay of 2000 milliseconds, var2 is sent the value 20. Click successively from event 0, which rewinds the qlist, through event 10 and compare the values in the MAX print window with the values in the qlist. Note that events 2, 5, and 9 all have time-delayed sends after time zero.

Note also that you can click through the events at any speed you like. If you advance to a succeeding event before all the timed delays have been triggered in the present event, the delay times will automatically be set to zero and the variable sends will be done immediately. In this way, variable 'state' is always kept up to date. For instance: in event 2 there is a time delay of 2 seconds before var2 will be sent the value 20. If event 3 is triggered after event 2, but BEFORE 2 seconds have elapsed, var2 will immediately be sent the value of 20 with a delay time of zero, ignoring the original delay time of 2 seconds. Note the extra dummy event at the end of qlist. The patcher 'qlist_control' expects an extra event in order to stop looping at the end of the events. The last "real" event in this example is event number 10. Event number 11 is a dummy event. The dashed lines: (-----------------------) with the event number following, simply act as visual separators for events in the queue.

Nota Bene: The next message can act as a "goto" in two ways. The first way is to send 'next' or 'next 0' to qlist, and qlist will execute everything in its queue up through the event clicked upon, thereby keeping state. 'Next' can also be used to jump or 'goto' an event without executing the events preceding the designated event. To use 'next' in a non-executable fashion, send 'next' followed by any nonzero number, e.g. 'next 1', and qlist will not send values to the corresponding receives.

EXAMPLE 4

Qlist was modified in 0.23 to be able to record and recall a sequence of Max messages. If you want to use this in real time, it would be best to keep the qlists short since the memory allocation scheme isn't optimized for adding to big ones. (This will get improved later...)

This is the only easy way to play a chord on a keyboard and get it in text form, for instance.