Sound-objects

 

Sound-objects are a specific type of time-objects handled by the Bol Processor. Whereas time-objects are designed to send timely commands to various devices, including robots, sound-objects produce sounds via MIDI and Csound drivers.
A new output format (event lists) makes it possible to connect the Bol Processor to the largest diversity of sound software devices.

🎶 The relocation or truncation of sound-objects, constraints on continuity, and the use of broken tempo (organum), bear similarities to the actions of human musicians during a performance. Virtuose performers need to break the rigidity of the beats in order to fully express the musicality of specific events.

All examples discussed here can be found in the Data project '-da.tryMiscObjects'. The time-setting of sound-objects uses an algorithm that has been documented in detail in previous publications.

Simple notes and sound-objects

In the Bol Processor, time-objects that produce (musical) sounds are of two types:

  • Simple notes are expressed in English, Italian-French or Indian conventional notation as a note name followed by an octave number. For example, C4 in English notation designates the note C in the fourth octave, which is usually the #60 key in the middle of a piano keyboard. C4 is equivalent to do3 in Italian-French notation and sa4 in Indian notation.
  • Sound-objects, which will be discussed here, are 'packages' of instructions (in MIDI or/and Csound formats) able to describe a set of instructions that may be likened to a 'musical gesture'.

The first version of the Bol Processor (BP1), released in 1980, dealt exclusively with sound objects representing the 'gestures' of Indian drummers and dancers. Within the current technical limitations, it produced and analysed musical items in text format, which the mussicologist could read or play on a drum. Read for example Analysis of tabla compositions.

Later, when the MIDI and Csound formats became available on small computers, handling simple notes and sound objects was introduced to expand the scope.

Editing sound-objects on BP3

Throughout this tutorial, we will become familiar with three types of sound object from a technological perspective:

  • MIDI objects produced by MIDI code
  • Csound objects produced by converting MIDI code to a Csound score
  • Pure Csound objects defined in Csound scores

Open the '-da.tryMiscObjects' project, which is distributed in the 'ctests' folder. A line of buttons at the bottom of the edit window gives access to the connected files. For example, since '-so.tryObjects' is written at the top of the project, a button with the same name is visible. Clicking this button displays the full set of sound objects used by this project:

👉 This tab must never be closed if sound objects are to be modified or created. A javascript scheduler checks for modifications to the sound objects every 30 seconds, and automatically saves the updated '-so.tryObjects' file. A message on top of the '-so.tryObjects' page confirms it:

If you are in a hurry, you can of course click the SAVE '-so.tryObjects' button.

Every sound-object is of the MIDI or Csound type, or both.

Click any name of sound-object, e.g. 'cycle1', to open the sound-object editor in a new tab:

This sound-object contains both MIDI and Csound code, producing identical sequences of events, as the Csound score at the bottom of the page was automatically generated from the MIDI content.

You can modify the Csound score, save it, then save the prototype and wait for less than 30 seconds for it to be updated in the '-so.tryObjects' file.

The advantage of using Csound scores for sound-objects is that they can represent much more complex instructions than MIDI messages, as they can control any number of parameters (arguments) in a Csound instrument. The instrument number 'x' or label can be set using the _ins(x) command in the Bol Processor score. Alternatively, it can be made specific to this sound object by entering the instrument number in the 'Force to instrument' field.

The MIDI sequence can be played by clicking the PLAY button, and it can be exported as a MIDI file. Conversely, a new MIDI sequence can be uploaded to the sound-object. This sequence, for example, was created using the MIDI file output format in the Bol Processor.

An image of the sound-object is available at the bottom of its page:

This image highlights a property of 'cycle1': as its name suggests, it is cyclic. This means that, if an extension is required but the 'Never rescale' option at the top of the page prevents this, it will fill the time interval with self-replications of its MIDI or Csound sequence. Replications are set to start at 34 % of its duration. This parameter can also be specified in milliseconds. Beware that if you enter a negative value or a value larger than the sound-object's duration, it will be set to 0. The same restrictions apply to parameters such as CONTINUITY, COVER, and TRUNCATE.

Creating a new sound-object

Let us create a sound-object labelled 'oscar' using a phrase of Oscar Peterson in the imported MusicXML score '-da.Watch_What_Happens_by_Oscar_Peterson'.

First click the EXPLODE button to split measures. Find one that you feel eligible as a sound-object. We selected measure #6:

This is the Bol Processor score of this measure:

{_vel(64) _chan(1){4, {{3,D5&}{1,&D5 G5 Bb5 D6 C6 Bb5}, {3/2,- F4 Gb4}{1/2,A4 Gb4}{2,G4}}}, _vel(64) _chan(2){4,{{3/2,- F3 Gb3}{1/2,A3 Gb3}G3&{1,&G3 G3 Bb3 D4 C4 Bb3}, 1/2{3/2,Bb2}--}}}

To export this fragment as a MIDI file, first set the Fade-out time to zero in the settings, otherwise it will end with 2 seconds of silence. Then select the MIDI file output format and click 'PLAY' near the 6th item. Click 'download' to get a file named 'Watch_What_Happens_by_Oscar_Peterson.mid' which contains only the chosen measure.

Then go back to the '-so.tryObjects' page and create an object called 'oscar'. Add 'oscar' to the '-al.tryObjects' alphabet which can be accessed via the link near the bottom of the '-da.tryMiscObjects' page. As 'oscar' belongs to the terminal alphabet, it should start with a lowercase character. However, you can bypass this rule by typing it between single quotation marks.

Open the 'oscar' tab and click the choose file button near 'Create or replace MIDI codes loading a MIDI file'. This will allow you to upload the MIDI file. Then click 'SAVE THIS PROTOTYPE'.

The duration of this sound-object is now set to 1820 ms, which is 1.82 beats because its time reference Tref is set to 1000 ms (by default). All default properties are fit for this demo, we'll change a few of them later.

Don't forget to wait for at least 30 seconds so that the new object is saved along with the '-so.tryObjects' file. The return to '-da.tryMiscObjects', type 'oscar' in the edit field and play it in real-time MIDI or MIDI file. You will get the following pianoroll and sound:

Now, you can also create the equivalent Csound score of this object by clicking the 'CREATE Csound CODE from MIDI codes' button. Wait a few seconds for the processing to complete. You will get:

t 0.000 60
i1 0.227 0.228 8.05 90.000 90.000 0.000 0.000 0.000 0.000 ; F4
i1 0.227 0.228 7.05 90.000 90.000 0.000 0.000 0.000 0.000 ; F3
i1 0.455 0.227 8.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#4
i1 0.455 0.227 7.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#3
i1 0.682 0.114 8.09 90.000 90.000 0.000 0.000 0.000 0.000 ; A4
i1 0.682 0.114 7.09 90.000 90.000 0.000 0.000 0.000 0.000 ; A3
i1 0.796 0.114 8.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#4
i1 0.796 0.114 7.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#3
i1 0.227 0.683 6.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#2
i1 0.000 1.365 9.02 90.000 90.000 0.000 0.000 0.000 0.000 ; D5
i1 0.910 0.455 7.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G3
i1 1.441 0.076 9.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G5
i1 1.441 0.076 7.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G3
i1 1.517 0.075 9.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#5
i1 1.517 0.075 7.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#3
i1 1.592 0.076 10.02 90.000 90.000 0.000 0.000 0.000 0.000 ; D6
i1 1.592 0.076 8.02 90.000 90.000 0.000 0.000 0.000 0.000 ; D4
i1 1.668 0.076 10.00 90.000 90.000 0.000 0.000 0.000 0.000 ; C6
i1 1.668 0.076 8.00 90.000 90.000 0.000 0.000 0.000 0.000 ; C4
i1 1.744 0.076 9.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#5
i1 0.910 0.910 8.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G4
i1 1.744 0.076 7.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#3
;

Click the 'SAVE THIS CODE' button, then 'SAVE THIS PROTOTYPE'.

The Csound code is only used when the output format is Csound. Real-time MIDI and MIDI files use the MIDI codes of sound-objects. For this reason, if a sound-object contains only Csound code, it won't produce any sound in MIDI.

Csound

Select the Csound score output format and play 'oscar'. You get the following:

No MIDI output, as the Bol Processor produced a Csound score output which the Csound console converted to a sound file. The notes are drawn in green on the pianoroll instead of brown, indicating that Csound is being used instead of MIDI.

The MIDI code stored in 'oscar' was not used. Instead, the Csound score was used.

If the sound-object contains MIDI code and no Csound score, using it with the Csound output option will convert MIDI to Csound for this particular task.

What happens if a sound-object contains Csound score and no MIDI code? Try it by duplicating 'oscar' to 'oscar-purecsound' and click the 'SUPPRESS all MIDI codes' button on the 'oscar-purecsound' page. Don't forget to add 'oscar-purecsound' to the '-al.tryObjects' alphabet!

Now, playing 'oscar-purecsound' will produce the same output as 'oscar' with the 'Csound score output' option selected. However, you will not hear any sound if you play 'oscar-purecsound' with the real-time MIDI or MIDI file output option selected.

The Bol Processor can convert streams of MIDI codes into Csound scores. However, it cannot perform the reverse conversion for a simple reason: Csound uses events that are too complex for straightforward conversion to MIDI.

MIDI channels and Csound instruments

Looking back at the Bol Processor score used to create 'oscar' reveals that it was calling both MIDI channels 1 and 2:

{_vel(64) _chan(1){4,{{3,D5&}{1,&D5 G5 Bb5 D6 C6 Bb5},{3/2,- F4 Gb4}{1/2,A4 Gb4}{2,G4}}},_vel(64) _chan(2){4,{{3/2,- F3 Gb3}{1/2,A3 Gb3}G3&{1,&G3 G3 Bb3 D4 C4 Bb3}, 1/2{3/2,Bb2}--}}}

This probably went unnoticed when the sound object was played on a MIDI device set to mix all 16 channels by default. However, MIDI channels can be used to redirect MIDI event streams to different devices. The _part() instruction also does this on the Bol Processor. Anyway, we need options for dealing with channels contained in a sound-object's MIDI code:

The 'Do not change…' option is declared as follows in 'tryObjects.json' (created when the output option is Event list):

"Default MIDI channel": {
"key": "DefaultChannel",
"value": "LOCAL_CH",
"unit": "enum"
},

MIDI channels are preserved as found in the sound object.

If the 'Force events to the current MIDI channel' option is selected, the MIDI channel will match the context of the event. The JSON declaration is:

"Default MIDI channel": {
"key": "DefaultChannel",
"value": "GLOBAL_CH",
"unit": "enum"
},

If the 'Force events to MIDI channel #' option is selected, the specified channel number is used. For instance, with a channel number of 6:

"Default MIDI channel": {
"key": "DefaultChannel",
"value": 6
},

The same process applies to Csound instruments. These can be specified using the _inst() command in the Bol Processor score, but instruments can also be set in each sound object:

Here, for instance, instruments specified in the Csound score will be used. This is declared as follows in 'tryObjects.json':

Csound instrument mode": {
"key": "CsoundInstrumentMode",
"value": "LOCAL_CS",
"unit": "enum"
}

If the option 'Force to instrument used in context' is selected, it is declared as follows:

Csound instrument mode": {
"key": "CsoundInstrumentMode",
"value": "GLOBAL_CS",
"unit": "enum"
}

If an instrument is specified (and used for all events), it is declared as follows:

"Csound instrument #": {
"key": "CsoundInstr",
"value": 3
}

In the produced Csound score, instrument numbers appear in the first parameter. For instance, this line

i3 0.000 1.000 8.00 90.000 90.000 0.000 0.012 0.012 0.000 ; C4

sends a note 'C4' to a Csound instrument #3. This will work on three conditions:

  1. A Csound instrument file (such as '-cs.tryTunings') has been declared on top of the project;
  2. It contains an instrument #3;
  3. The arguments of instrument #3 match parameters found in the line.

Try for instance playing the same object with three different instruments:

_ins(1) oscar-purecsound -- _ins(2) oscar-purecsound -- _ins(3) oscar-purecsound

👉 Instrument #3 cannot play short notes. However, these are present in the output score, as shown below:

t 0.000 60
i1 0.000 1.365 9.02 90.000 90.000 0.000 0.024 0.024 0.000 ; D5
i1 0.227 0.228 8.05 90.000 90.000 0.000 0.024 0.024 0.000 ; F4
i1 0.227 0.228 7.05 90.000 90.000 0.000 0.024 0.024 0.000 ; F3
i1 0.227 0.683 6.10 90.000 90.000 0.000 0.024 0.024 0.000 ; A#2
i1 0.455 0.227 8.06 90.000 90.000 0.000 0.024 0.024 0.000 ; F#4
i1 0.455 0.227 7.06 90.000 90.000 0.000 0.024 0.024 0.000 ; F#3
i1 0.682 0.114 8.09 90.000 90.000 0.000 0.024 0.024 0.000 ; A4
i1 0.682 0.114 7.09 90.000 90.000 0.000 0.024 0.024 0.000 ; A3
i1 0.796 0.114 8.06 90.000 90.000 0.000 0.024 0.024 0.000 ; F#4
i1 0.796 0.114 7.06 90.000 90.000 0.000 0.024 0.024 0.000 ; F#3
i1 0.910 0.455 7.07 90.000 90.000 0.000 0.024 0.024 0.000 ; G3
i1 0.910 0.910 8.07 90.000 90.000 0.000 0.024 0.024 0.000 ; G4
i1 1.441 0.076 9.07 90.000 90.000 0.000 0.024 0.024 0.000 ; G5
i1 1.441 0.076 7.07 90.000 90.000 0.000 0.024 0.024 0.000 ; G3
i1 1.517 0.075 9.10 90.000 90.000 0.000 0.024 0.024 0.000 ; A#5
i1 1.517 0.075 7.10 90.000 90.000 0.000 0.024 0.024 0.000 ; A#3
i1 1.592 0.076 10.02 90.000 90.000 0.000 0.024 0.024 0.000 ; D6
i1 1.592 0.076 8.02 90.000 90.000 0.000 0.024 0.024 0.000 ; D4
i1 1.668 0.076 10.00 90.000 90.000 0.000 0.024 0.024 0.000 ; C6
i1 1.668 0.076 8.00 90.000 90.000 0.000 0.024 0.024 0.000 ; C4
i1 1.744 0.076 9.10 90.000 90.000 0.000 0.024 0.024 0.000 ; A#5
i1 1.744 0.076 7.10 90.000 90.000 0.000 0.024 0.024 0.000 ; A#3
i2 3.000 1.365 9.02 90.000 90.000 0.000 0.012 0.012 0.000 ; D5
i2 3.227 0.228 8.05 90.000 90.000 0.000 0.012 0.012 0.000 ; F4
i2 3.227 0.228 7.05 90.000 90.000 0.000 0.012 0.012 0.000 ; F3
i2 3.227 0.683 6.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#2
i2 3.455 0.227 8.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#4
i2 3.455 0.227 7.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#3
i2 3.682 0.114 8.09 90.000 90.000 0.000 0.012 0.012 0.000 ; A4
i2 3.682 0.114 7.09 90.000 90.000 0.000 0.012 0.012 0.000 ; A3
i2 3.796 0.114 8.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#4
i2 3.796 0.114 7.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#3
i2 3.910 0.455 7.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G3
i2 3.910 0.910 8.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G4
i2 4.441 0.076 9.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G5
i2 4.441 0.076 7.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G3
i2 4.517 0.075 9.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#5
i2 4.517 0.075 7.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#3
i2 4.592 0.076 10.02 90.000 90.000 0.000 0.012 0.012 0.000 ; D6
i2 4.592 0.076 8.02 90.000 90.000 0.000 0.012 0.012 0.000 ; D4
i2 4.668 0.076 10.00 90.000 90.000 0.000 0.012 0.012 0.000 ; C6
i2 4.668 0.076 8.00 90.000 90.000 0.000 0.012 0.012 0.000 ; C4
i2 4.744 0.076 9.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#5
i2 4.744 0.076 7.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#3
i3 6.000 1.365 9.02 90.000 90.000 0.000 0.012 0.012 0.000 ; D5
i3 6.227 0.228 8.05 90.000 90.000 0.000 0.012 0.012 0.000 ; F4
i3 6.227 0.228 7.05 90.000 90.000 0.000 0.012 0.012 0.000 ; F3
i3 6.227 0.683 6.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#2
i3 6.455 0.227 8.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#4
i3 6.455 0.227 7.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#3
i3 6.682 0.114 8.09 90.000 90.000 0.000 0.012 0.012 0.000 ; A4
i3 6.682 0.114 7.09 90.000 90.000 0.000 0.012 0.012 0.000 ; A3
i3 6.796 0.114 8.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#4
i3 6.796 0.114 7.06 90.000 90.000 0.000 0.012 0.012 0.000 ; F#3
i3 6.910 0.455 7.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G3
i3 6.910 0.910 8.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G4
i3 7.441 0.076 9.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G5
i3 7.441 0.076 7.07 90.000 90.000 0.000 0.012 0.012 0.000 ; G3
i3 7.517 0.075 9.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#5
i3 7.517 0.075 7.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#3
i3 7.592 0.076 10.02 90.000 90.000 0.000 0.012 0.012 0.000 ; D6
i3 7.592 0.076 8.02 90.000 90.000 0.000 0.012 0.012 0.000 ; D4
i3 7.668 0.076 10.00 90.000 90.000 0.000 0.012 0.012 0.000 ; C6
i3 7.668 0.076 8.00 90.000 90.000 0.000 0.012 0.012 0.000 ; C4
i3 7.744 0.076 9.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#5
i3 7.744 0.076 7.10 90.000 90.000 0.000 0.012 0.012 0.000 ; A#3
s

A Csound sound-object can also use several instruments specified on eaach line of its Csound score. Duplicate 'oscar-purecsound' to 'oscar-2instruments', select the 'Do not change instrument', then modify the Csound score as follows:

i1 0.227 0.228 8.05 90.000 90.000 0.000 0.000 0.000 0.000 ; F4
i1 0.227 0.228 7.05 90.000 90.000 0.000 0.000 0.000 0.000 ; F3
i3 0.455 0.227 8.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#4
i3 0.455 0.227 7.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#3
i3 0.682 0.114 8.09 90.000 90.000 0.000 0.000 0.000 0.000 ; A4
i1 0.682 0.114 7.09 90.000 90.000 0.000 0.000 0.000 0.000 ; A3
i1 0.796 0.114 8.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#4
i1 0.796 0.114 7.06 90.000 90.000 0.000 0.000 0.000 0.000 ; F#3
i1 0.227 0.683 6.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#2
i3 0.000 1.365 9.02 90.000 90.000 0.000 0.000 0.000 0.000 ; D5
i1 0.910 0.455 7.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G3
i1 1.441 0.076 9.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G5
i1 1.441 0.076 7.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G3
i1 1.517 0.075 9.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#5
i1 1.517 0.075 7.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#3
i1 1.592 0.076 10.02 90.000 90.000 0.000 0.000 0.000 0.000 ; D6
i1 1.592 0.076 8.02 90.000 90.000 0.000 0.000 0.000 0.000 ; D4
i1 1.668 0.076 10.00 90.000 90.000 0.000 0.000 0.000 0.000 ; C6
i1 1.668 0.076 8.00 90.000 90.000 0.000 0.000 0.000 0.000 ; C4
i1 1.744 0.076 9.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#5
i1 0.910 0.910 8.07 90.000 90.000 0.000 0.000 0.000 0.000 ; G4
i1 1.744 0.076 7.10 90.000 90.000 0.000 0.000 0.000 0.000 ; A#3

Notes F#4, F#3 A4 and D5 will be played with instrument #3, and other notes with instrument #1. Save this score, save the 'oscar-2instruments', click SAVE on the -'so.tryObjects' page, add 'oscar-2instruments' to the '-al.tryObjects' alphabet, and play:

oscar-2instruments

Isn't it beautiful? 😀 

In this demo, we used instruments #1, #2 and #3 from the Csound instrument file named '-cs.tryTunings' because they each have 10 arguments that match the 10 parameters contained in the Csound scores. More complex instruments use more parameters.

This is still a small part of the sophistication of Csound, as Csound scores can handle an infinity of instruments with their specific parameters (example). You only need to design them…

Event list

Sound-objects are listed in event lists along with the parameters required for their instantiation. Indeed, the exported JSON file, e.g. 'tryObjects.json', is also used to this effect.

For example, the performance of

_ins(3) C4 - oscar-2instruments

produces an event list starting like this:

Here, the Csound instrument column displays instrument 3 for the 'C4' note. However, it states that 'oscar-2instruments' should use its own internal instrument assignments. These will be find in the Csound score stored in 'tryObjects.json'.

Duration of a sound-object

Type and play:

oscar - _tempo(1/3) oscar

The first instance of 'oscar' will play at the same speed as the captured fragment. The second instance will play at a third of the speed. This is possible because the property 'OK rescale' is selected. If you don't want to change the speed, regardless of the tempo of the performance, select 'Never rescale'.

When 'OK rescale' is selected, you can still prohibit the expansion or contraction of the duration, owing to the 'Expand' and 'Compress' options. Try to play the preceding example after unchecking 'Expand' or 'Compress', ot selecting 'Never rescale'.

A finer control of durations is possible. Select 'Dilation ratio range from' and set acceptable variations in range 0.5 to 2. Then play:

oscar - _tempo(4) oscar ---- _tempo(1/3) oscar

Speeds 4 and 1/3 won't be accepted and will be limited to 2 and 1/2:

Cover property

Now try this:

_tempo(0.5) oscar _tempo(1) oscar

Oops!

The two occurrences overlap because the second one starts on the second beat (2.00 seconds). The small red triangle at the start of the object indicates its pivot, which we will discuss later. Its shape indicates that the object is relocatable. So, how can we make the second occurrence play after the first has finished?

The solution is to set the 'COVER END' property to 'Never cover':

The same can be achieved by setting the 'COVER BEGINNING' property to 'Never cover'.

Pre-roll and post-roll

Note that the second occurrence in the preceding example moved exactly to the end of the first. Perhaps we need to set a safe silence of, say, 300 ms after each occurrence of 'oscar'. This can be achieved by setting the post-roll to 300 ms. Post-roll is the additional time given to a time object to finish its effect:

A similar effect can be achieved by setting a negative pre-roll, which introduces a silence at the beginning of the object. Two buttons at the bottom of the 'oscar' page make it easier to understand the concept of 'pre-roll' and 'post-roll' in terms of silences:

The pre-roll and post-roll of a sound-object can be positive or negative and are intended to frame a time interval outside the limits set by the object's first and last events. A positive post-roll means that there are 'things happening' at a given delay after the last event. A positive pre-roll indicates that 'things will happen' after a given delay following the first event. If the object has a 'Non-cover' property, the new limits will be used to calculate its location.

These delays are expressed in physical time (milliseconds), as it would make no sense for them to differ if the sound-object were played at a different speed.

Smooth time, smooth sound-object

Play the following on striated time (the default setting on '-da.trMiscObjects'):

oscar - _tempo(4/3) oscar - _tempo(2) oscar

The open the settings, uncheck the 'Striated time' option, save the settings, and play the same in smooth time.

The two results are shown below:

In striated time (top image), the pivot of each sound-object is set on a time streak (blue vertical line), and the time streaks are arranged according to the rhythmic structure.

In smooth time (bottom image), sound-objects and silences are located on the sole basis of their durations. For example, the first silence lasts for 1 second (the default setting), whereas the second silence, at tempo 4/3, lasts for 0.75 seconds.

A typical example of using smooth time is the arrangement of time patterns (empty time-objects) as shown in '-gr.tryTimePatterns' (read more):

{10,t1 t2,{t1 t3 t4,C4 D4 E4 F4 - A4}{t3 t1,B4 C5 _ E5}}

A smooth time-object is one whose Tref parameter is set to 0 ms. By default, this value is 1000 ms. This will be explained and demonstrated in future…

Silent sound-object

A silent sound-object is one in which both the MIDI and Csound contents are empty. It may be called a time-object since it does not deal with sound in its raw form. Since it does not deal with sound in its raw form, it may be called a time-object. However, once it is displayed in an event list, it may be used by other devices to produce any sequence of events.

Silent sound-objects can be created in three ways:

  1. A left-over variable
  2. A terminal symbol unrelated to a sound-object description
  3. A sound-object declared with empty MIDI and Csound contents

Check the three types in this example:

Thisvariable C4 gold gold h D4

Here, 'Thisvariable' is a left-over variable and 'h' is a terminal symbol not declared in '-so.tryObjects'. Both are played with a one-beat duration. However, 'gold' is declared in '-so.tryObjects' with a 'Tref' duration of 700 ms that cannot be rescaled, plus the 'force continuity in the beginning' property and 'relocate' properties.

The 'gold' silent sound-object can be given the same metrical and topological properties as other sound objects, creating a wide variety of situations.

Read the Silent sound-objects page for more details.

Smooth sound-object

The 'chik-smooth' silent sound-object has a 'Tref' duration of 0 ms. We call it a smooth sound-object. It 'adapts' to the current rhythmic structure by occupying 1 beat. Let us place the 'chik-smooth' pivot in the middle, allow it to be truncated and prevent its beginning from being covered. Now we play:

_tempo(2) chik C4 chik-smooth - _tempo(1) chik-smooth

The duration of 'chik-smooth' is one beat, that is 0.5 s when the tempo is 2 and 1 s when the tempo is 1.

The 'gold2' silent sound-object has a 'Tref' duration of 0 ms. We call it a smooth silent sound-object. Let us play:

_tempo(2) C4 gold gold2 gold2 D4

Here, the 'gold2' sound-object has a duration of one beat, or 0.5 seconds. The 'gold' sound-object, instead, is played at half its nominal duration, or 350 ms.

Out-time sound-object

Play:

C4 << chik >> C5

The 'chik' sound-object, normally lasting 250 ms, is played with duration zero if written between <<>>:

This is visible in the Csound score of this performance. Notes in 'chik' are {C3, F3, C4} :

i1 0.000 1.000 8.00 90.000 90.000 0.000 0.024 0.024 0.000 ; C4
i1 1.000 0.000 7.00 90.000 90.000 0.000 0.024 0.024 0.000 ; C3
i1 1.000 0.000 7.05 90.000 90.000 0.000 0.024 0.024 0.000 ; F3
i1 1.000 0.000 8.00 90.000 90.000 0.000 0.024 0.024 0.000 ; C4
i1 1.000 1.000 9.00 90.000 90.000 0.000 0.024 0.024 0.000 ; C5
s

Pivot

The concept of pivot was first introduced in the late 1980s by the Italian composer Marco Stroppa. It is discussed in detail in Bel's paper Two algorithms for the instantiation of structures of musical objects. The pivot is a specific point in time that indicates where a sound object should be placed.

By default, the pivot is located at the beginning of its sound-object. However, it can be set at various locations: beginning (pre-roll excluded), end (post-roll excluded), middle (pre-roll and post-roll excluded), first NoteOn, last NoteOff, middle of the NoteOn/NoteOff sequence. It can also be set to start at a given distance from the beginning, either as a percentage of the total duration (excluding pre-roll and post-roll) or as a fixed number of milliseconds.

This level of sophistication is necessary given that sound-objects — or, more generally, time-objects — are intended to drive all kinds of devices, including robots.

Try for instance:

C3 F2 pivotplus

The 'pivotplus' sound-object has its pivot located at 150 % duration from its beginning. The pivot is displayed as a full red arrow, meaning that the object cannot be relocated: the time-setting algorithm will place it on a time streak according to the rhythmic structure wherever possible.

Now try:

C3 F2 pivotminus

The 'pivotminus' sound-object has its pivot located at -50 % duration from its beginning.

Now, let's take another look at the eighteenth measure of 'Watch_What_Happens_by_Oscar_Peterson.mid' by Oscar Peterson, which has been used to create the 'oscar2' sound-object:

_chan(1){{4,{{1,F5 Bb3}{1/2,G4 F4}{1/2,A4 C5 E5}Db5{1,Bb4 Ab4 -}, 2{F4,Ab4}{1,G4 F4 -}}},_chan(2){4,{{1/2,F4}{1/2,Db3 D3 Eb3}{1/2,E3 D3}{1/2,- Cb4 C4}{1,Db4 G2 Eb4}{2/3,Eb4}{1/3,Ab2},-- 2/3{1/3,F3,Cb4}{2/3,F3,Cb4}{1/3,Db2}}}}

Note that it contains notes in MIDI channels 1 and 2. The image looks like this:

The numerous red lines from 4 to 6 seconds represent a series of MIDI messages that gradually decrease the volume. These can be deleted by a single click of the 'SUPPRESS volume control' button.

A pre-roll of -1000 ms has been added to introduce silence at the beginning of the object. However, the actual start of the object is at time 0, as indicated by its pivot (the red triangle).

The terminal symbol 'oscar2' has been added to the '-al.tryObjects' alphabet. If this had not been done, the machine would read "oscar2" as "oscar 2" and play 'oscar' followed by a two-beat silence!

The pivot is used to position 'oscar2' correctly in time. We will play for instance:

C4 _ _ oscar2

Let us set the position of the pivot at 800 ms of the beginning. This sets the first NoteOn (the first event of this object) to start 800 ms before the pivot location on the third beat:

👉 Be warned that the volume decreases at the end of this example. Your sound device may remain silent after the first playback. To avoid this, check the 'Reset controllers' option in the settings.

Cyclic sound-object

Let's take another look at the 'cycle1' sound object, which loops from 34% of its starting point. Play:

cycle1 _

We are calling 'cycle1' on 2 beats, but its option 'Never rescale' is checked. It will reach the expected duration by repeating its sequence of events, starting from 34% of its duration, as instructed:

The notes in this sound object are 'E4, G4, G4, A4, G4, Bb5'. The first two notes, 'E4 G4', are in the 34% area and are not repeated. The repetition therefore starts at 1.00 s with 'G4 A4 G4 Bb5'. However, to keep the total duration at 2.00 s, the second repetition is incomplete.

Let us create a copy of 'cycle1', called 'cycle1-force', which should extend itself with an integer number of repetitions.

(Don't forget to add it to the '-al.tryObjects' alphabet!)

Now play:

cycle1-force _

The duration now exceeds 2 beats in order to complete the 'G4, A4, G4, Bb5' repetition.

Cyclic sound objects play identically in both MIDI and Csound. All properties, such as pivot, pre-roll and post-roll, and effects, such as _transpose, _keyxpand and _keymap, are supported.

Below is an instance of cycle1 with a pre-roll of -100 ms and a post-roll of 300 ms. The grey parts are additional time segments that do not contain any event.

cycle1 _

Truncate the end of an object

Play this in MIDI and Csound:

C4 oscar2-trunc

Now let us add an 'f' sound-object which is not relocatable. As per the following score, it should be located with its pivot exactly on the fourth beat:

C4 oscar2-trunc -- f

However, the 'oscar2-trunc' sound-object is set to the 'Never cover end' option. It also has a 'Truncate end' option, set to accepting a truncation up to 50% of the sound's duration. This is an acceptable set of constraints:

A Trace file can be displayed, giving explanations about the time-setting process.

Read below the trace file for setting up 'C4 oscar2-trunc -- f':

Placing objects…
Col#5 side 1 Ts=5000 t1=3875 t2=4125 "f" should spend 1125 milliseconds, but no solution.
<
Col 3 NewTs=5000 ts1= 2000 ts2=2000 Tsm=5000 "-" This object is not the one concerned.<<
Col#2 nseq = 0 side = 2 ts = 1000 t1 = 1000 t2 = 5000 "oscar2-trunc"
We must save 1125 milliseconds
solution 1 ---------- SEQUENCE 1 ---------------------
#1 "C4" [0,1000] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
#2 "oscar2-trunc" [1000,3875] TruncBeg=0 TruncEnd=1125 alpha=1.000000 delta=0 DELTA=0
#3 "-" [2000,2000] TruncBeg=0 TruncEnd=0 alpha=2.000000 delta=0 DELTA=0
#5 "f" [3875,4125] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
---------- (time resolution = 1 ms) ------------

The trace indicates that 'oscar2-trunc' was truncated by 1125 ms at its end.

Now, let us try:

C4 oscar2-trunc f

The 'f' sound object needs to start on the second beat, which would cut off more than 50% of the end of 'oscar2-trunc'. There is no solution to this set of constraints. Therefore, the machine breaks the 'Non cover' limitation, yielding the following:

This is also explained in the Trace file:

Placing objects…
Col#3 side 1 Ts=5000 t1=1875 t2=2125 "f" should spend 3125 milliseconds, but no solution.
<
Col#2 nseq = 0 side = 2 ts = 1000 t1 = 1000 t2 = 5000 "oscar2-trunc"
We must save 3125 milliseconds
➡ We must release time constraint(s)!
• Releasing overlapping

Placing objects…
solution 1 ---------- SEQUENCE 1 ---------------------
#1 "C4" [0,1000] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
#2 "oscar2-trunc" [1000,5000] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
#3 "f" [1875,2125] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
---------- (time resolution = 1 ms) ------------

Since both the 'C4' note and the 'oscar2-trunc' sound-object are relocatable, we can figure out another solution that would not break an constraint:

This was generated by a modified version of the time-setting algorithm. In the correct version, this solution is not acceptable because it creates negative dates.

Truncate the beginning of an object

Let us now try to truncate the beginning of 'oscar2-trunc' by setting it no 'No relocate' and putting a 'crac' sound-object whose duration is slightly longer than 1 beat. The score of 'crac' is '{B3,D4}{C4,E4}'. We play:

crac oscar2-trunc

The picture is self-explanatory. The transition between 'crac' and 'oscar2-trunc' is smooth, because 'oscar2-trunc' starts sounding at the date of the first NoteOn(s) following the truncated part.

Try this longer truncation:

crac crac crac crac crac oscar2-trunc

Same result using 'oscar2-trunc-csound' which is a pure Csound object:

An out-time sound-object is never truncated:

crac << oscar2-trunc >>>

Truncating both ends of a sound-object

Try the following:

crac oscar2-trunc -- f 

If you are using the Event list, be aware that the start and end times displayed are those after truncation. To locate events correctly, you will need the 'trunc beg' and 'trunc end' values:

Let us truncate on both sides a pure Csound object playing 2 instruments:

crac oscar-2instruments - _tempo(1/4) f

Break tempo

Another solution to avoid issues with timings or sound objects being truncated is to authorise 'oscar2-trunc' to break the tempo. This technique is known as 'organum' in classical music performance. When this option is set on the 'oscar2-trunc' sound-object, we get:

The broken tempo is noticeable by the delayed position of the fifth streak (or beat).

This broken tempo will impose itself on any other sequences of events played on top of it. Play for instance:

{C4 oscar2-trunc f, D4 E4 B3 F3, A4 B4}

At first glance, it is surprising that the two sequences do not end on the same date. Note that 'F3' appears to be out of the picture. However, this bizarre result can be explained by counting beats. An expansion of the polymetric expression is the following:

_tempo(4) {C4_ _ _ oscar2-trunc_ _ _ f_ _ _ , D4_ _ E4_ _ B3_ _ F3_ _ , A4 _ _ _ _ _ B4_ _ _ _ _ }

The confusing factor is that the physical duration of 'f' is 0.25 s., with its pivot in the middle sitting on the beat labelled '3' (blue line). Notes in the 'D4 E4 B3 F3' sequence occupy 3 beats and their symbolic duration is 3/4 beat, whereas 'A4' and 'B4' occupy 1.5 beat. This is a typical example of the difference between symbolic duration (beats) and physical duration (seconds).

The process is summarized in the trace. The displacement of the third beat is marked as 'DELTA=3125':

Placing objects…
Col#9 nseq = 0 side = 1 ts = 5000 t1 = 1875 t2 = 2125 "f"
We must spend 3125 milliseconds
solution 1 ---------- SEQUENCE 1 ---------------------
#1 "C4" [0,1000] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
#5 "oscar2-trunc" [1000,5000] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
#9 "f" [5000,5250] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=3125
---------- (time resolution = 1 ms) ------------
Placing objects…
solution 1 ---------- SEQUENCE 1 ---------------------
#1 "C4" [0,1000] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
#5 "oscar2-trunc" [1000,5000] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
#9 "f" [5000,5250] TruncBeg=0 TruncEnd=0 alpha=1.000000 delta=0 DELTA=0
---------- (time resolution = 1 ms) ------------
Placing objects…
solution 1 ---------- SEQUENCE 2 ---------------------
#1 "D4" [0,750] TruncBeg=0 TruncEnd=0 alpha=0.750000 delta=0 DELTA=0
#4 "E4" [750,1500] TruncBeg=0 TruncEnd=0 alpha=0.750000 delta=0 DELTA=0
#7 "B3" [1500,5375] TruncBeg=0 TruncEnd=0 alpha=3.875000 delta=0 DELTA=0
#10 "F3" [5375,6125] TruncBeg=0 TruncEnd=0 alpha=0.750000 delta=0 DELTA=0
---------- (time resolution = 1 ms) ------------
Placing objects…
solution 1 ---------- SEQUENCE 3 ---------------------
#1 "A4" [0,1500] TruncBeg=0 TruncEnd=0 alpha=1.500000 delta=0 DELTA=0
#7 "B4" [1500,6125] TruncBeg=0 TruncEnd=0 alpha=4.625000 delta=0 DELTA=0
---------- (time resolution = 1 ms) ------------

Continuity property

A sound-object can be instructed to 'stick' to the preceding and/or the next event in the sequence. These properties are known as 'Continuity at the beginning' and 'Continuity at the end'. The 'glue' sound-event is of this kind. Play:

cycle1-force _ - glue

The 'glue' sound object was supposed to be placed on the fourth beat, but it ended up at the end of 'cycle1-force'.

A refinement of this process is the allowance for a maximum gap at the beginning or the end of a sound-object. These properties are known as 'Allow gap'.

Tonal scale

A detailed presentation of this topic is on this page. The tonality can be controlled in both Csound and real-time MIDI productions (read more). The good news is that (micro)tonal adjustements can be applied to both MIDI and Csound sound-objects.

Let's consider an example where the difference is obvious: the Bohlen-Pierce scale, in which the octave is replaced with a tritave (ratio 3/1) and it is divided in 13 intervals. Let us play the following in both MIDI and Csound:

_tempo(0.66) G3 C4 oscar - _scale(Bohlen-Pierce,60) G3 C4 oscar

The '60' in the _scale instruction means that the block key is the middle of a standard keyboard (more details). So, 'C4' is rendered at the same frequency in both the standard equal-tempered scale and the Bohlen-Pierce scale.

The event list of this performance can be downloaded here.

Note that the pianorolls of both parts are identical, because tonal corrections are done after drawing the pictures.

Serial tools

Serial tools such as _retro, _rotate, etc., modifying the order of sound-objects (read documentation) do not modify the order of events inside each sound-object.

Other serial tools are accepted by 'oscar', as shown by its settings.

The same effects are applied to Csound productions, whether converted from MIDI or using the object's Csound score.

Play this in both MIDI and Csound:

oscar - _transpose(3) oscar

Play in Csound:

oscar-purecsound - _transpose(3) oscar-purecsound

Check this in both MIDI and Csound:

oscar - _keyxpand(F#4,-1) oscar

More complex:

oscar - _keymap(C4,C2,C6,C7) oscar

Various effects

The _pitchbend() command has no effect on sound-objects. This is on the agenda.

Play this superposition of two occurrences of 'oscar' displaced by a few milliseconds, both in MIDI and Csound:

{oscar, 2/10 oscar}

Event lists

 

The Bol Processor can produce detailed lists of sound events as text files. This will facilitate connections with a great diversity of sound devices in replacement of the MIDI and Csound outputs.

Set it up

When 'Event list' is selected on a Data or Grammar page, sound events are saved to a CSV file. The full description of the sound object prototypes used in production is saved to a JSON file. All used tonal scales are exported in Scala (SCL) and keyboard mapping (KBM) formats.

The event list file

Click PRODUCE ITEM(s) on a grammar page, here for instance '-gr.koto3'. This yields:

No sound output (MIDI or Csound) has been produced. Instead, a file called 'koto3.csv' is available in the 'my_output' folder (by default). You can visualise this file by clicking the 'output file' link, or download it (get it here) for later use.

The event list contains many columns, a number which is likely to increase with new developments. It looks like this:

Beginning of the koto3.csv event list file

Each line of the event list is assigned to a sound-event with start and end times in milliseconds, as shown on the graphic display:

The labels 'a' and 'b' designate sound-objects, with respective prototype identifiers 3 and 5, as defined in the related '-so.abc1' file. The item labelled 'Y' is a silent sound-object created to represent the residual variable 'Y'.

The timings of sound-objects and simple notes in the event list are the accurate ones computed by the polymetric expansion and time-setting algorithms. Serial tools (e.g. _retro) modifying the order of events have been applied. However, serial tools modifying pitches (e.g. _transpose) have not been applied; therefore, their parameters are given in the event list. The second-to-last column contains the name of the tonal scale, if any (see below).

The sound-object file

The '-so.abc1' file, which is used to define all the sound objects used in the '-gr.koto3' project, is in an unfriendly format produced by the PHP interface. Therefore, to facilitate further use, it is exported to a more readable JSON format ('abc1.json'):

Top of the 'abc1.json' file

Please note that each sound-object is identified by both a label and a numeric identifier. These parameters are displayed in columns 4 and 5 of the event list. Numbers make it easier to match objects in the event list with their descriptions in 'abc1.json'.

👉 Since numeric identifiers are created when the alphabet is compiled, the values appearing in 'abc1.json' are specific to the '-gr.koto3' project.

The tonal (scale) files

SCL and KBM files are produced wherever tonal scales are used in the production (read MIDI microtonality).

Let's try the '-da.Ombres_errantes' project. Check the 'Event list' output format and click 'PLAY'. Links for downloading the rameau_en_sib.scl and rameau_en_sib.kbm files are displayed:

If several scales have been used in the production, several download lines will be displayed.

👉 These SCL and KBM file can also be exported via the tonality editor in the interface. The 'rameau_en_sib' scale is defined in the '-to.tryTunings' tonality framework which is displayed as follows:

(Click the EDIT button to reach the export to KBM)

Using command lines

Event lists and related files can be produced by calling the console via (Unix) command lines. For example,

./bp produce -se ./ctests/-se.koto3 -gr ./ctests/-gr.koto3 -al ./ctests/-al.abc1 -so ./ctests/-so.abc1 --eventlistout ./my_output/koto3.csv --traceout ./temp_bolprocessor/trace_my_session_my_project.txt

This will produce 'koto3.csv' and 'abc1.json' in the 'my_output' folder. (If the --traceout option is not specified, no image will be created.)

./bp play -se ./ctests/Imported_MusicXML/-se.Ombres_errantes -da ./[your_path]/0.bpda -to ./tonality_resources/-to.tryTunings --eventlistout ./my_output/Ombres_errantes.csv --traceout ./temp_bolprocessor/trace_8510fe8339_-da.Ombres_errantes.txt --traceout ./temp_bolprocessor/trace_my_session_my_project.txt --english

This will produce 'Ombres_errantes.csv', 'rameau_en_sib.scl' and 'rameau_en_sib.kbm' in the 'my_output' folder. (If the --traceout option is not specified, no image will be created.)

When calling this production via a command line, we need to find out the names of scale files. Since 'rameau_en_sib' is not found in the command, a scan of the data or grammar is necessary to detect _scale(rameau_en_sib, …) instruction(s). If several scales are used in the production, each of them will export a pair of SCL and KBM files that can be identified in the same manner.

Designed by Bernard Bel, version 3.5.4 (September 2026)

Serial tools

 

Examples cited on this page belong to the -da.trySerialTools project.

Geeks can check the "Trace serial tools" option in the settings if they wish to monitor processes in Zouleb.c.

The serial tool approach makes it possible to carve "shapes" in the time and pitch dimensions of a musical work containing simple notes and/or sound-objects. These tools were first implemented in the late 1990s, following suggestions from the Dutch composer Harm Visser.

Serial tools are applied immediately after the production process for a musical piece, and before the expansion of its polymetric structures. Therefore, they are ignored during the production.

All tools in the current implementation (version 3.5.2 and later) are applied recursively to the fields of polymetric structures.

Tools modifying the order of sound-objects

_retro

_retro C0 C2
_retro {C0 C2}

both produce:

C2 C0

 

Note the propagation in polymetric structures:

_retro {a b {c d, C4 D4}} 1/4

produces:

1/4 { { d c,D4 C4 } b a }

Here, sound objects a, b, c and d were mixed with the simple notes C4 and D4. Consequently, the timing of the final structure is influenced by the metrical and topological properties of these sound objects.

The cascading effect of _retro can be difficult to figure out. For example,

_retro a b _retro c d _retro A4 B4

will produce:

 c d b a B4 A4

whereas

_retro {a b _retro {c d _retro {A4 B4}}}

will produce:

c d B4 A4 b a

_ordseq

This tool restores the order of the following sequence (or polymetric structure) if it has been subject to change by _retro, _rndseq or _rotate. For example,

_retro {A3 B3, C5 D5 _ordseq C4 D4} F4

will produce:

F4 {B3 A3, C4 D4 D5 C5} F4

_rndseq

_rndseq a b c d

will put the sequence in an unpredictable order, for instance:

a d b c

If a positive integer is set as the seed for randomisation in the project settings, the same order will appear each time the production is launched. In our comparative tests, we use seed = 1, which produces the same "random series" in all systems.

Beware that

_rndseq A4 B4 C4 _rndseq A5 B5 C5 D5

is interpreted as:

_rndseq A4 B4 C4 {_rndseq A5 B5 C5 D5}

where the "{_rndseq A5 B5 C5 D5}" expression is a fifth unit, which may yield (depending on the seed):

B4 B5 D5 A5 C5 C4 A4

and not:

_rndseq{A4 B4 C4} _rndseq{A5 B5 C5 D5}

which may yield:

C5 B5 D5 A5 B4 C4 A4

Using curled brackets to mark out sequences is a good idea!

_randomize

This instruction cancels the fixed randomisation setting. In fact, it shuffles the cards and sets the seed to 0. Example of proper use:

_randomize _rndseq a b c d

_rotate

_rotate(x) rotates the following sequence by x units. If x = 2, the rotated sequence will start after the first 2 units, which will be sent to the end. Thus,

_rotate(2) C4 D4 E4 F4

will produce:

E4 F4 C4 D4

and

_rotate(-1) C4 D4 E4 F4

will produce:

F4 C4 D4 E4

This one:

_rotate(-1) {_rotate(2) _rotate(-1) a b c d}

will produce "a b c d" because -1 + 2 -1 = 0.

The _rotate instruction is propagated to all fields of polymetric structures. Thus,

_rotate(-1) {A4 B4 C4 D4, A2 B2 C2}

will produce:

 {D4 A4 B4 C4, C2 A2 B2}

Be aware that "units" may be simple notes, sound-objects, polymetric structures or structures that are preceded by another serial tool. For example,

_rotate(2) {A4 B4 _rotate(-1) C4 {D4 E4} F4, A3 B3 C3} F5 G5

produces:

G5 { { E4 D4 } F4 C4 A4 B4, C3 A3 B3 } F5

Explanation: The "C4 {D4 E4} F4" and "D4 E4" sequences rotate by 1 unit because 2 - 1 = 1. The "A3 B3 C3" sequence rotates by 2 units. The "_rotate(-1) C4 {D4 E4} F4" expression is treated as a single unit in the rotation by 2 units of "A4 B4 _rotate(-1) C4 {D4 E4} F4".

Tempo and speed markers are treated as "units" in the rotation. Thus,

_rotate(-1) C4 D4 _tempo(3/2) E4 F4 G4

produces:

G4 C4 D4 _tempo(3/2) E4 F4

Units moved by _rotate, _retro or _rndseq can also be silences or silent sound-objects, for instance:

_rotate(-1) {- b a c d, a b c'} d'

which produces:

 d' { d - b a c, c' a b }

Upgrade of the Zouleb() procedure (geeks only)

'Zouleb' is an anagram of 'Boulez', in honour of Pierre Boulez, who championed the composition of serial music.

Until version 3.5.2 was released in August 2026, the Zouleb() procedure in Zouleb.c used a different syntax for tools such as _rotate, which ignored the field separator ','.

If you wish to try the old procedure, check the Ignore field separators option in the SERIAL TOOLS section of the settings. This option is set on the old projects -gr.Visser.Shapes and -gr.Visser.Waves (read page).

For example,

_rotate(1) {A4 B4 _rotate(1) C4 F4 G4, A3 B3 C3}

produces

{B4 G4 C4 F4 A4, B3 C3 A3}

with the current procedure, whereas the old procedure produced:

{B4 G4 C4 F4,A3 B3 C3 A4}

Tools modifying pitches

_transpose

_transpose(n) shifts the following sequence up by n steps (not necessarily semitones). For example,

_transpose(4) C4 D4 E4

produces:

E4 F#4 G#4

Sound-objects can also be transposed if their "Accept transposition" option is set. For example,

c a b - _transpose(6) c a b

This and other pitch-modifying tools deal with steps of the scale. These are semitones only in 12-tone scales. Try for example a 13-tone scale:

-to.tryScales
_scale(Bohlen-Pierce,0) _transpose(1) C4 Db4 D4 E4 F4 Gb4 G4 H4 Jb4 J4 A4 Bb4 B4 C5

which produces:

Db4 D4 E4 F4 Gb4 G4 H4 Jb4 J4 A4 Bb4 B4 C5 Db5

_keyxpand

_keyxpand(basenote, ratio) multiplies melodic intervals by ratio (positive or negative) relative to the basenote. For example,

_keyxpand(C4, 2) D4 E4 F4 

produces:

E4 G#4 Bb4

The basenote parameter can be an explicit note, as shown above, or a key number ranging from 0 to 127. For instance, if C4 is mapped to the value 60 in the settings, it can be replaced by 60:

_keyxpand(60, 2) D4 E4 F4

_keyxpand is applied recursively to the fields of a polymetric expression. The values of ratio are cumulated multiplicatively if the centre note is the same. For instance,

_keyxpand(C4,-2) B3 C4 { _keyxpand(C4,-1) D4 E4 }

produces:

D4 C4 E4 G#4

i.e. the same as:

_keyxpand(C4,-2) B3 C4 _keyxpand(C4,2) { D4 E4 }
or _keyxpand(C4,-2) B3 C4 _keyxpand(C4,2) D4 E4

 

See -gr.tryKeyXpand for a typical example containing notes and sound-objects. The beginning of the piano roll produced by this grammar provides an approximate overview of the process:

The "ratio" parameter can be any floating-point number, positive or negative. For example,

_keyxpand(C4,-1.80) D4 E4

will produce:

G#3 F3

The two-semitone distance between C4 and D4 has been transformed to 2 × -1.8 = -3.6, which has been rounded down to -4, yielding G♯3. Similarly, the four semitones between C4 and E4 have been transformed to 4 × -1.8 = -7.2, which has been rounded up to -7, yielding F3.

Algebra

Let 'k' be the key number of 'basenote'. The following equation is used to change a note whose key number is 'x' to a note whose key number is 'y':

y = x + ratio . (x - k)

Key expand interpolation

👉   This feature is not yet implemented in BP3, but we plan to offer it in consistent with key mapping interpolation (see below).

_keymap

Syntax: _keymap(p1,q1,p2,q2)

In the sequence, this tool modifies pitches in range (p1,p2) according to a linear mapping in which q1 is the image of p1 and q2 the image of p2.

The tool makes changes on "simple notes" and on "sound-objects" in which the property "Accept key changes" has been set to true.

p1, q1, p2, q2 may be written as integers (key numbers) in range 0..127, or simple notes using the current note convention. The _keymap(p1,q1,p2,q2) syntax demands that p2 > p2, or the equivalent notes are in ascending order. For instance, both expressions:

_keymap(C3,E4,C5,C6) C4 B3 A3
and _keymap(48,64,72,84) C4 B3 A3

produce:

D5 C#5 C5

Algebra

Given _keymap(p1,q1,p2,q2) where p1, q1, p2 and q2 are key numbers, the following equation is used to change a note whose key number is 'x' to a note whose key number is 'y':

y = a x + b
with a = (q2 - q1) / (p2 - p1)
and b = q1 - a p1

In the example above, a = 5/6 and b = 24. Thus, C4 (60) is converted to D5 (74), B3 (59) to C#5 (73 after rounding from 73.16), and A3 (57) to C5 (72 after rounding from 71.5).

Changes to sound objects can be visualised using the -gr.tryKeyMap grammar by clicking the 'TryThis' button at the bottom of the page. The following sequence is played:

TryThis --> _keymap(52,86,86,52) a -- b

Sound-objects 'a' and 'b' (defined in -so.tryKeyMap) are identical except that "Accept key changes" is checked for 'a' and unchecked for 'b'. Therefore, 'b' is not modified:

Keymap interpolation

Changes can be made continuous using _mapcont and _mapstep instructions, and discrete again with _mapfixed. The parameters p1, q1, p2 and q2 are interpolated between two _keymap instructions.

Instructions _mapcont and _mapstep are equivalent when dealing with simple notes. However, _mapcont also modifies pitches inside the sound-objects whose option "Accept key changes" is checked.

Let's see an example easy to visualise because of the repeated pattern "C4 C#4 D4 G4":

_tempo(6) _mapcont _keymap(C3,C3,C5,C5) C4 C#4 D4 G4 C4 C#4 D4 G4 C4 C#4 D4 G4 C4 C#4 D4 G4 _keymap(C3,C2,C5,C6)

Same process using sound-objects: click the 'TryThis2' button at the bottom of the -gr.tryKeyMap grammar. The following sequence is played:

TryThis2 --> _mapcont _keymap(52,52,86,86) a a a _keymap(52,86,86,52)

Since the 'a' sound-object accepts key changes, it is modified by _keymap with parameters p1, q1, p2 and q2 interpolated between the two values. The interpolation is computed again for each note. This produces:

Grammar -gr.tryKeyMap is a more elaborated example containing notes and sound-objects. The beginning of the piano roll produced by this grammar provides an approximate overview of the process:

Enter notes

 

Notes can be entered from a connected MIDI device to the Grammar or Data projects of BolProcessor BP3. This is a monophonic input with no timing parameters. For time-tagged MIDI capture, refer to this page.

If you are having problems connecting to MIDI devices, check out the instructions on the Real-time MIDI page. We will remind you of the minimum procedure here. The same applies to Grammar and Data projects.

Let us assume that you connected a MIDI piano keyboard to a USB port of the machine running BP3. My keyboard's name is "Pocket Key 25". The only step required is to declare the keyboard as an input device.

Select Real-time MIDI and click SAVE format. Then click Add an input and SAVE MIDI ports. You will then see the following:

This means that BP3 will attempt to connect to the next available MIDI input and output. Every system has its own built-in MIDI devices, including those that you are currently running on the same machine. Therefore, it is unlikely that your external keyboard will be selected immediately. Never mind; click on the location in your data or grammar where you want to write notes, then click the MIDI enter notes button, which should now be visible since you declared a MIDI input. A process window will appear:

If the notes you play on the MIDI keyboard are immediately displayed on your Data or Grammar page, it means that BP3 has found your external keyboard. This is generally not the case. Clicking STOP will take you here:

On this example you can see that the selected input was "Bus 2", which is not the external keyboard. Click Show process to read details.

This the MacOS version; the others are very similar:

Bol Processor console app
Version 3.4.5 (May 23 2026 - 16:16:40)
Reading MIDI port settings: ../temp_bolprocessor/trace_f51acb6321_-da.tryEnterNotes_midiport
🎹 Your real-time MIDI settings:
MIDI output = 0: “new output” -
MIDI input = -1: “new input” -
🎹 Setting up MacOS MIDI system
Trying to assign ports to 1 output(s) without names but possibly with numbers
MIDI output = 0: “Bus 1” 👉 the number of your choice
Trying to assign ports to 1 input(s) without names but possibly with numbers
MIDI input = 1: “Bus 2” 👉 choice by default
MIDI input 1 makes BP3 interactive
🎶 More MIDI output options are available:
MIDI output = 1: “Bus 2”
MIDI output = 2: “Pocket Key 25”
🎶 More MIDI input options are available:
MIDI input = 0: “Bus 1”
MIDI input = 2: “Pocket Key 25”

MIDI settings saved to ../temp_bolprocessor/trace_f51acb6321_-da.tryEnterNotes_midiport
🎹 Name(s) of MIDI input or/and output changed and will be updated when saving the page of your project

BP3 selected "Bus 2" as the input, but it also says that "Pocket Key 25” is another option. This is the one we need. Let us copy and paste the name, then SAVE MIDI ports:

Now, clicking again the MIDI enter notes button will effectively type the names of notes played (in sequence) on the keyboard.

👉 For now, chords will be interpreted as note sequences rather than polymetric constructions. This could be addressed later.

If it still does not work, check the input filter:

NoteOn should be at least in column 1 (treat). If NoteOn and NoteOff are in column 2 (transmit), the notes will also be heard on the MIDI output device.

You can change the note convention from English to Italian/French or to Indian. Open your project settings and select the desired convention.

Note that you can use the computer keyboard mapping and MIDI keyboard capture of notes at the same time.

For geeks: As of 26 July 2026 (v3.4.7), we switched from SSE design to a polling design, which makes it compatible with Apache, FastCGI, CGI, MAMP, XAMPP, and PHP Desktop.

Install MAMP or XAMPP on MacOS

   

This page complements the Quick install MacOS guide. However, it addresses the more general issue of setting up a local Apache server on a Mac.

You will not be able to run both MAMP and XAMPP Apache servers at the same time if they use the same ports. This wouldn't be a good idea anyway… So, you need to opt for the device of your choice.

Choosing between MAMP and XAMPP

Both XAMPP and the basic version of MAMP are free of charge and fully meet the need to run an Apache server on the Bol Processor. A MAMP PRO version is also available for a charge, in case you need to run projects with more requirements.

As of July 2026, XAMPP 8.2.4 includes manager-osx.app, but this utility was compiled for Intel-based Macs and therefore required Rosetta on Macs with Apple silicon, which won't be available after MacOS version 27. Apple recommends replacing Intel applications with Apple-silicon or Universal alternatives where possible (read Apple: Using Intel-based apps on a Mac with Apple silicon). The automatic startup service described here makes manager-osx.app unnecessary.

The latest XAMPP release announced by Apache Friends is version 8.2.12. However, the latest version actually available for macOS remains XAMPP 8.2.4 (see the download page). Its components are Intel (x86_64) binaries and therefore still require Rosetta on Macs with Apple silicon. A native ARM64 version has not yet been announced.

Installing MAMP

Free MAMP on MacOS. The top right icon indicates that the Apache server is running.

if you choose the (free) MAMP version, both MAMP and (commercial) MAMP PRO will be installed. The interface will occasionally prompt you to "upgrade" to MAMP PRO (see picture), but you can ignore this recommendation.

In the preferences of MAMP, do not check "Start servers". Also do not start Apache clicking the Start button. For geeks: this is to make sure that Apache will be launched with your own identifier.

It appears that the free version of MAMP has a script execution time of 30 seconds. This is sufficient for most small Bol Processor projects. However, there is a way to overcome this limitation without having to run MAMP PRO (for a charge). Do the following:

  1. Select the highest available version of PHP, e.g. 8.3.14 as shown on the picture.
  2. In Applications/MAMP/bin/php/ you will see a folder named "php8.3.14". Open it, open the "conf" folder and edit the "php.ini" file (with TextEdit).
  3. Replace max_input_time = 60 with
    max_input_time = 300
  4. Replace max_execution_time = 60 with
    max_execution_time = 300
  5. Save the "php.ini" file.
  6. Edit the Applications/MAMP/conf/apache/httpd.conf file. Look for the line:

FastCgiServer /Applications/MAMP/fcgi-bin/php.fcgi -socket httpdFastCGI.sock

and replace it with:

FastCgiServer /Applications/MAMP/fcgi-bin/php.fcgi -socket httpdFastCGI.sock -idle-timeout 3600 -flush

To start Apache, got to the Terminal and type:

/Applications/MAMP/Library/bin/apachectl start

To stop it, type:

/Applications/MAMP/Library/bin/apachectl stop

We will see below how the start instruction can be sent when the computer starts up.

Installing MAMP PRO

(For rich people)

After the (free) downloading MAMP, you will find MAMP PRO in the Applications folder, whereas MAMP (free) is located in Applications/MAMP. Also note that this version of MAMP runs on port "8888" by default, as we will see below.

The MAMP PRO main page on MacOS (version 5.7)
  1. Launch MAMP PRO from the Applications folder.
  2. In the MAMP main window, click the Apache Enable button (see image). No need for MySQL.
  3. The image shows the default settings for PHP, which is started with Apache.
  4. In case of trouble, check the settings for ports (see image) and of hosts (general  and  Apache).

Installing XAMPP

Your Mac may refuse to run the XAMPP installer because it is from an "unknown developer". You can override this by allowing the application in the Privacy & Security section of the Mac's System Settings. Unpacking the files takes several minutes, so be patient and wait for it to finish!

👉 Don't try the virtual machine version of XAMPP! It won't work on Macs with M1 chips (and above). Use the native installer.

Start Apache automatically at login

For ordinary web pages, Apache may be started in several ways. However, the Bol Processor’s real-time MIDI functions require Apache to be started in the graphical session of the logged-in user. This gives the "bp" console access to the MacOS CoreMIDI server.

Therefore Apache should start automatically at login, not at boot time. We do not use a LaunchDaemon system for this purpose. Instead, we use a LaunchAgent.

MAMP Apache at login

The MAMP application should NOT be added to the Login items in your System Settings. Also deactivate MAMP GmbH in the Login items.

Make sure that Apache is stopped. You can do this either by clicking the Stop button in the MAMP application window, or by running the following command in the Terminal:

/Applications/MAMP/Library/bin/apachectl stop

Now, run the following commands in the Terminal:

mkdir -p ~/Library/LaunchAgents
nano ~/Library/LaunchAgents/org.bolprocessor.mamp-apache.plist

Paste the following text into the nano editor:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.bolprocessor.mamp-apache</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/MAMP/Library/bin/apachectl</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

Save the file by pressing Control-O, followed by Return. Then press Control-X to exit the editor.

Check that the file is valid:

plutil -lint ~/Library/LaunchAgents/org.bolprocessor.mamp-apache.plist

The result should be:

…org.bolprocessor.mamp-apache.plist: OK

Activate the service immediately:

launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/org.bolprocessor.mamp-apache.plist

Do not use sudo for any of these commands. Apache will start immediately and subsequently each time you log in to your Mac.

Make sure you quit the MAMP application before shutting down your computer; otherwise, it will reopen if the "Reopen windows when logging back in" option is selected.

To disable the automatic MAMP Apache start later, enter:

launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/org.bolprocessor.mamp-apache.plist

After a MacOS upgrade, it may happen that MAMP no longer starts at login. If this happens, simply run the following:

launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/org.bolprocessor.mamp-apache.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/org.bolprocessor.mamp-apache.plist

XAMPP Apache at login

(For geeks) The XAMPP procedure below also uses a LaunchAgent, so that Apache starts in the graphical session of the logged-in user. This is required for the Bol Processor’s real-time MIDI functions.

These instructions assume that XAMPP is installed in its default location:

/Applications/XAMPP

1. Find your macOS username

Open Terminal and enter:

whoami

Make a note of the result. It is your short macOS username.

2. Authorise the Apache startup command

The LaunchAgent must be allowed to start Apache without displaying a password request.

Enter:

sudo EDITOR=nano visudo -f /etc/sudoers.d/xampp-startapache

Enter your administrator password, then add the following line, replacing your_username with the result of the whoami command:

your_username ALL=(root) NOPASSWD: /Applications/XAMPP/xamppfiles/xampp startapache

For example, if whoami returned mary, the line would be:

mary ALL=(root) NOPASSWD: /Applications/XAMPP/xamppfiles/xampp startapache

Press Control-O, followed by Return, to save the file. Then press Control-X to leave the editor.

Test the authorisation:

sudo -k
sudo -n /Applications/XAMPP/xamppfiles/xampp startapache

Apache should start without requesting a password.

3. Create the user LaunchAgent

Enter:

mkdir -p "$HOME/Library/LaunchAgents"
nano "$HOME/Library/LaunchAgents/org.xampp.server.plist"

Paste the following text into the nano editor:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.xampp.server</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/sudo</string>
<string>-n</string>
<string>/Applications/XAMPP/xamppfiles/xampp</string>
<string>startapache</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ProcessType</key>
<string>Interactive</string>
<key>StandardOutPath</key>
<string>/tmp/xampp-startup.log</string>
<key>StandardErrorPath</key>
<string>/tmp/xampp-startup-error.log</string>
</dict>
</plist>

Save with Control-O, followed by Return, then leave the editor with Control-X.

Validate the file:

plutil -lint "$HOME/Library/LaunchAgents/org.xampp.server.plist"

The result should end with:

OK

4. Activate the LaunchAgent

Enter:

launchctl bootstrap gui/$(id -u) \
"$HOME/Library/LaunchAgents/org.xampp.server.plist"

Check that Apache is responding:

curl -I http://localhost

You may also inspect the LaunchAgent with:

launchctl print gui/$(id -u)/org.xampp.server

If it shows state = not running together with last exit code = 0, this is normal. The startup command has finished, while Apache continues to run separately.

Apache will subsequently start automatically after you log in to your Mac. Verify that Apache is running by opening:

http://localhost

Be patient, as it may take two to three minutes to take effect…

Removing an older XAMPP startup service

If you previously installed the system-level XAMPP startup script, remove it before using the new method. First determine whether it is loaded:

sudo launchctl print system/org.xampp.server

The displayed path identifies the old configuration file. A previously supplied script may have created:

/Library/LaunchDaemons/xampp.startapache.plist

Unload the service:

sudo launchctl bootout system/org.xampp.server

Then move its configuration outside the active directory:

sudo mkdir -p /Library/LaunchDaemons-disabled
sudo mv /Library/LaunchDaemons/xampp.startapache.plist \
/Library/LaunchDaemons-disabled/

Afterwards, the following command should report that the system service cannot be found:

sudo launchctl print system/org.xampp.server

Only the user service should remain:

launchctl print gui/$(id -u)/org.xampp.server

You can also remove manager-osx.app from System Settings → General → Login Items. The new LaunchAgent replaces it.

Keyboard mapping

 

The first version of the Bol Processor (BP1) was an advanced word processor designed to store text representations of musical variations created by Indian drum players. This work required a mapping system that could associate a word with a single key on the computer keyboard.

Keyboard mapping
on Bol Processor BP1

Although the keyboard mapping was relatively easy to program in the Apple II's 6502 assembly language, its implementation in BP2 and then BP3 was delayed until version 3.4.4 (May 2026). Thus, after many years, the entire technical environment for the study of drum improvisation has been revived, alongside the restored procedures of item parsing and learning rule weights from examples.

Checking keyboard mapping on BP3

Open "-gr.dhadhatite" (in the latest distribution of the "ctests" folder). On top of the grammar you can read:

-se.dhadhatite
-al.dhadhatite
-wg.dhadhatite
-da.dhadhatite
-kb.dhadhatite

The "-kb.dhadhatite" declaration points to a file containing the keyboard mapping. If this file is present in the "ctests" folder, a button appears below the grammar to open it. If no file with this name is found, you will be offered the option of creating a new one.

The display is self-explanatory:

The mapping involves the association of alphabetic keys with any word (or sequence of words). Here we use only the terminal alphabet of the grammar — 'bols' in the language of drum players. But we could map other keys to frequently used variables or expressions in this work environment.

When working with the tabla, Jim Kippen managed to position the words at keyboard locations that facilitated typing at the same speed at which he would play them on the tabla. For this reason, "dha" is located at the far left.

To activate the mapping, click on the desired position in the grammar and then press the 'Escape' key. Now, if you type "qcqqcrd" — or "QcQQCrd", etc. — you will get:

dhatitedhadhatitedheena

This works in MacOS, Windows and Linux environments.

Alphabetic keys that are not mapped, such as 'A' and 'N', will be inactive. Non-alphabetic keys, such as digits and typographic symbols, will function as normal.

If the "Add space after each word" option is checked, and the mapping is saved, typing "qcqqcrd" will produce:

dha tite dha dha tite dhee na

The Bol Processor can segment strings without spaces if they are made of words from the terminal alphabet, here defined in "-al.dhadhatite". However, adding spaces makes it easier to read for untrained humans.

The same keyboard mapping feature can be used on a Data page such as "-da.dhadhatite".

At the bottom of the keyboard form, there is a button labelled "COPY data from this file". This allows you to select a different keyboard file to use for the settings.

Grammars for the production and recognition of rhythmic sentences

Grammaires de génération et de reconnaissance de phrases rythmiques
Grammars for the production and recognition of rhythmic sentences

Bernard Bel

Actes du 6e congrès AFCET/INRIA, Antibes, 1987b: 353-366.

This paper details a cognitive anthropology project that uses formal generative grammars to model and analyze the rhythmic structures of North Indian tabla music.

Résumé :
Un projet d'anthropologie cognitive sur les langages de percussion du nord de l'Inde met en interaction des musiciens experts, un analyste et un automate simulant la génération et la reconnaissance de compositions rythmiques. Les connaissances et les hypothèses sur chaque type de composition sont formulées à l'aide de règles de production. Les grammaires sont ensuite modifiées jusqu'à ce que l'adéquation entre l'ensemble des phrases générées et la pratique musicale soit jugée satisfaisante. Cet exposé décrit le formalisme de représentation et les algorithmes utilisés pour la synthèse et l'analyse des phrases, en s'appuyant sur des exemples tirés du répertoire musical.

Abstract:
In a cognitive anthropological project dealing with drumming languages in North India, an interaction has been created between expert musicians, an analyst and an automaton simulating the generation and recognition of rhythmic compositions. Production rules are used to represent knowledge and hypotheses on each compositional type. Grammars are then modified in order to match the set of generated sentences with musical practice. This paper describes data representation and algorithms utilized for the synthesis and parsing of sentences, with the support of examples drawn from the musical repertoire.

More details are found in chapter 4 of Bel's (1990) thesis: French and English versions.

Télécharger cet article (français) ➡ Version texte

Download this paper (approx. English)

Skip to PDF content

Learning rule weights

 

The "learning rule weights" feature was implemented in the first version of the Bol Processor (BP1) a long time ago. It was designed in response to a problem that arose during fieldwork sessions while Jim Kippen was developing grammars capable of identifying the "language" conveyed by qa‘ida compositions of tabla. Citing Jim (Kippen & Bel, 1992):

[…] sometimes a grammar would reach a point of stagnation where computer-generated variations were judged to be neither very good nor incorrect. Consequently there was no simple way of refining or improving the model. We felt that a solution lay in attributing to each production rule a coefficient of likelihood (or weight) where the probability that certain generative paths would be chosen in preference to others could be examined.

The probabilistic model that has been implemented on the BP is derived from probabilistic grammars/automata as defined by Booth & Thompson (1973), the difference being that a weight rather than a probability is attached to every rule. The rule probability is computed as follows: if the weight is zero then the probability is zero; if the weight is positive then the inference engine calculates the sum of weights of all candidate rules, and the rule probability is the ratio of its own weight to the sum.

Rule weights can be inferred from a set of sentences (Maryanski & Booth 1977:525). The algorithm implemented in the BP is more powerful than the one devised by Maryanski and Booth, since the latter required the choice of a sample set in which all rules had been used. Given a grammar and a subset of the language that this grammar generates (for instance a sample sequence taken from a performance of an expert musician), rule weights may be inferred as follows: first, reset all weights to zero. Then, analyse every sentence and increment the weights of all rules used in the derivation by one unit.

We will demonstrate the inference of rule weights using grammars and data distributed in the ‘ctests’ folder. These grammars are real examples from field work.

Infer weights in the "-gr.dhadhatite" grammar

The ‘ctests’ folder contains both "-gr.dhahatite" and "-dha.dhahatite". The latter is a set of 15 examples designed for the demonstration. They are not of great interest because they were all produced by the grammar and will therefore be parsed successfully. However, you can try modifying details to observe failed parsing.

In addition, the first eight ones are identical, except for their notation. Items can be typed without spaces, given that the terminal alphabet "-al.dhahatite" will create a correct segmentation. They can be set out on several lines separated by single line feeds. Beats can be marked with periods (the period notation) which the polymetric algorithm interprets as identical symbolic durations (see this page).

Let us have a look at the "-gr.dhahatite" grammar which we already used for parsing variations. First create its templates as explained earlier.

Settings of the "-gr.dhahatite" grammar

Open the settings and check the LEARN option for parsing data files (see image). The option "Start from latest weights file instead of grammar" is selected by default and will be explained further.

Then add "-dha.dhahatite" and "-wg.dhahatite" in declarations on top of the grammar, and save the page.

This modifies the display of the top of the grammar:

Clicking the LEARN weights button will launch the inference of weights from the sample set in "-dha.dhahatite":

The production window shows that all 15 items were successfully parsed although
"13 item(s) failed in the parsing after matching a template"… We knew that this grammar would accept all items, so we need an explanation for the failed parsing.

Click the Show process button to get more details:

🔎 Analyzing new selection…
dhadhatitedhadhadheenadheenatitedheenateenadhadhatitedhadhadheenadhadhatitedhadhateenatatatitetatateenateenatiteteenateenadhadhatitedhadhadheenadhadhatitedhadhadheena
▶︎ Analyzing item [Single line]
👉 Item [Single line] matched template [1]
• Subgrammar 6/6
• Subgrammar 5/6
• Subgrammar 4/6
• Subgrammar 3/6
• Subgrammar 2/6
• Subgrammar 1/6
Item [Single line] matching template [1] rejected by grammar… ❌
Result of failed analysis:
S1F +V8(= S1F) +S2F *(= S1F ++A2 V6)(: S1F) S1F
👉 Item [Single line] matched template [2]
• Subgrammar 6/6
• Subgrammar 5/6
• Subgrammar 4/6
• Subgrammar 3/6
• Subgrammar 2/6
• Subgrammar 1/6
Item [Single line] matching template [2] rejected by grammar… ❌
Result of failed analysis:
(= ++S2F) +V8 S1F +S2F *(: ++S2F) *(= ++A2 V6) S1F S1F
👉 Item [Single line] matched template [3]
• Subgrammar 6/6
• Subgrammar 5/6
• Subgrammar 4/6
• Subgrammar 3/6
• Subgrammar 2/6
• Subgrammar 1/6
Item [Single line] matching template [3] accepted by grammar… ✅
Etc.

This report shows that the first item failed to match two templates but was later correctly parsed after matching template [3]. Other items displayed similar behaviour. At the top of the page, there is a link to a trace of the parsing because the "Trace production or parsing" option is selected in the settings. There is also a "Detailed trace" option available for automated analysis, which is not discussed here.

Now, move down to the bottom of the grammar and click the "-wg.dhadhatite" button. This file was created by the BP3 console upon completion of its analysis. It contains the new weights of the grammar, i.e. for each rule, the original weight (usually 100) plus the number of times the rule has been used in the parsing process.

For example, the weight of rule [1] of gram#1 would be raised from 100 to 115, since it was used for all 15 items. However, the weight of rule [5] of gram#1 would stay at 100, since it was not used in this analysis. Changes are easy to check comparing the content of "-wg.dhadhatite" with weights in the grammar.

The "-wg.dhadhatite" display includes several buttons that are easy to understand:

  • The COPY current weights button will copy all the weights in the grammar to the file "-wg.dhadhatite";
  • The SET rule weights and RESET rule weights buttons will set or reset all weights in the file "-wg.dhadhatite", yet not in the grammar.
  • The COPY BACK rule weights button will copy weights from "-wg.dhadhatite" to the "-gr.dhadhatite" grammar. Then you are offered the option to save the grammar if these weights are correct, or to reload the grammar otherwise:
  • The "-wg.dhadhatite" page also has buttons that allow you to save a copy of its weights to a new '-wg' file, or copy weights from another file.

In real life…

This "-wg.dhadhatite" layout shows how to proceed in real-life situations. You have a working Bol Processor grammar, as well as large sets of examples produced by the grammar and validated (or provided) by the expert you are working with. Proceed as follows:

  1. Open the "-wg" file and RESET all weights to 0 (in that file);
  2. Click the LEARN weights button;
  3. Keep an eye on items that are rejected. These may indicate an incomplete grammar. They may also be incorrect. If so, make changes and go back to step 1;
  4. If the option "Start from latest weights file instead of grammar" is selected, you can repeat this process with more sets of examples, as rule weights will add up.

Although rule weights can be very large integers, we find it more practical to keep them within a small range, such as 0 to 127. The 127 limit is just a convention that can be changed in the PHP interface. Currently, if the weight of a rule is not displayed, its value is 127, and vice versa.

References

Kippen, Jim, & Bernard Bel (1992) Modelling music with grammars: formal language representation in the Bol Processor. In A. Marsden & A. Pople (eds.): Computer Representations and Models in Music, London, Academic Press, 1992, p. 207-238.

Booth, T.L. and R.A. Thompson (1973). Applying Probability Measures to Abstract Languages, IEEE Transactions on Computers, Vol. C-22, n°5, p. 442-450.

Maryanski, F.J., and T.L. Booth (1977). Inference of Finite-State Probabilistic Grammars, IEEE Transactions on Computers, Vol. C-26, n°6, p. 521-536.
[Some anomalies of this paper are corrected in B.R. Gaine's paper Maryanski's Grammatical Inferencer, IEEE Transactions on Computers, Vol. C-27, n°1, 1979: 62-64]

Parsing music

The Bol Processor can analyse musical variations (strings of terminal symbols) using a grammar. A successful parse (membership test) indicates that the variation was produced by, or could be produced by, the grammar. This process was used extensively for modelling improvisation and composition in north Indian tabla drumming — read Jim Kippen's interview.

On this page we will introduce the real data sets "-da.dhahatite" versus "-gr.dhahatite", and "-da.dhin--" versus "-gr.dhin--". However, let us first deal with a very simple example.

A simple true Bol Processor grammar

Grammar "-gr.tryAllItems0" in the "ctests" folder:

RND
gram#1[1] S <-> (= X) X (: X)
gram#1[2] S <-> X X
-----
RND
gram#2[1] X <-> a
gram#2[2] X <-> b

This grammar is a "true Bol Processor grammar":

  • It does not contain erase rules such as X --> lambda;
  • Rules can be used for producing and parsing items, as indicated by the "<->" derivation sign;
  • Rules do not contain any /flag/;
  • Rules do not have decreasing weights such as "<K3-20>", "<100-50>", etc.;
  • Rules do not contain any procedure such as "_goto", "_repeat", "_destru", etc.;
  • Note, however, that rules can contain left/right contexts, including remote ones.

This can be summarised by the following, less technical yet highly relevant statement: a true Bol Processor grammar will deterministically parse every item it can produce. The "deterministic" property means that the algorithm will not backtrack if there are no available candidate rules. This is a fundamental feature of recognising formal languages that represent sets of musical variations.

When a grammar is read, the interface checks whether it is "true BP". If so, a "Create templates" button is displayed.

(For geeks)The interface uses the is_true_bp() function to check the grammar.

This grammar uses the terminal alphabet "-al.abc" which is linked to a set of sound-objects, but we won't use this feature here. Set the output mode to "BP data file".

Let us first produce all items, i.e. the language of this grammar. In the settings, check "Produce all items" in the PRODUCTION section. You can set "Max items produced" to a large number, for instance 500, as we assume that the actual size of the language is much smaller.

Click "PRODUCE". The result is:

a a a
a b a
b a b
b b b
a a
a b
b a
b b

The first four items have been created by gram#1[1], the first rule of the first subgrammar. The rule contains a pattern (=X)…(:X) meaning that the first and last parts must be identical.

The gram#1[2] rule produces two instances of variable X that are rewritten as 'a' or 'b' in gram#2. All possibilities are displayed in the last four items.

These 8 items have been copied to "-da.tryAnalyse", with additional empty lines to separate them.

Parse "-da.tryAnalyse"

In order to parse items in "-da.tryAnalyse", we need to write the name of the grammar on top of the Data page, along with links to the alphabet and settings:

-se.tryAnalyse
-al.abc
-gr.tryAllItems0

Now, "Analyse" buttons are shown for each item. However, if you click on any of these, the parsing will fail for the first four. The reason is that these should be recognised by gram#1[1], which is a pattern rule. In other words, we need to tell the engine that the pattern has been recognised.

👉  This is why we need to create templates in true BP grammars that contain pattern rules.

Create templates for "-gr.tryAllItems0"

Click the "Create templates" button, then click the "output file" link. We get:

----------
TEMPLATES:
[1] (@0 _)_(@1 )
[2] _ _
---------

In this simple grammar, the two templates represent the pattern types of each rule in gram#1. Although no theoretical knowledge of templates is required to use them, let us have a little explanation:

Template [2] contains two occurrences of '_', which means it will match any item containing two terminal symbols.

Template [1] contains the structural markers "@0" indicating a master parenthesis, and "@1" its slave copy. Note that "@0" is followed by a single "_", indicating a single terminal symbol, but "@1" is alone in its parenthesis, as it is meant to be the exact copy of its master.

Copy the templates, paste them at the bottom of grammar "-gr.tryAllItems0", then save the grammar. Note that the template button now appears as "Update templates".

Parse "-da.tryAnalyse" using the templates in "-gr.tryAllItems0"

Return to the "-da.tryAnalyse" and click the "Analyse" buttons. Now, all items are parsed successfully. The trace of the analysis of the first item "a a a" make it clear:

Analysing this item
Compiling grammar…
Compiling subgrammar #1…
Compiling subgrammar #2…
Compiling subgrammar #3…
Parsing completed
Errors: 0
Template(s) found, position 333
👉 We will try all templates, as per your settings
Analyzing selection…
Item matched template [1]
• Subgrammar 3/3
• Subgrammar 2/3
• Subgrammar 1/3
👉 Item matching template [1] accepted by grammar… ✅

Evidently, the four first items will match the "template [1]", and the four last items will match the "template [2]". The trace is self-explanatory:

Selected: gram#2[1] X <-> a
(= a) X(: a)
Selected: gram#2[1] X <-> a
(= X) X(: X)
Selected: gram#1[1] S <-> (= X) X(: X)
S

A few words about the creation of templates. Firstly, this procedure is restricted to finite languages. A grammar containing recursive rules would produce items of unrestricted length, resulting in an infinite number of templates. The Bol Processor has grammar procedures, such as "_repeat()", that tell the number of times a rule can be applied, thereby limiting the length or duration of any production. However, these procedures are currently not applicable to the parsing of items.

Secondly, at first glance, creating templates for the simple grammar amounts to producing the entire language, replacing terminals with '_', and eliminating duplicate instances. This would be impractical for most real-world grammars because, although the language is finite, it can be very large. To avoid this, the machine first determines up to which subgrammar structural rules are found. Then it explores all derivations of these subgrammars, and for each derivation it produces only one item which is converted to its template.

Structural rules are the ones that contain syntactic structures (master-slave parentheses) or/and structural markers if they are not in the contexts of the rule.

Structural markers are the glyphs '+', ':', ';', '=' and '\'. We'll see their usage in grammars for tabla compositions.

Analysis of "-da.acceleration" (without templates)

The "-gr.acceleration" grammar has no template, but you can check that it is able to parse "-da.acceleration" which it had created. Note that all rules have '<->' derivation signs instead of '-->' as in the old version.

A particular feature of this grammar is that a rule contains a period (beat marker) :

gram#1[2] A <-> E2 •

The item produced by this grammar contains periods that create the accelerating tempo:

_transpose(12) _vel(60) E2 • D2 E2 • _vel(65) B2 D2 E2 • G2 B2 D2 E2 • _vel(70) F#2 G2 B2 D2 E2 • Bb2 F#2 G2 B2 D2 E2 • _vel(75) C2 Bb2 F#2 G2 B2 D2 E2 • _vel(77) G#2 C2 Bb2 F#2 G2 B2 D2 E2 • _vel(80) A2 G#2 C2 Bb2 F#2 G2 B2 D2 E2 • _vel(85) Eb2 A2 G#2 C2 Bb2 F#2 G2 B2 D2 E2 • _vel(87) C#2 Eb2 A2 G#2 C2 Bb2 F#2 G2 B2 D2 E2 • _vel(90) F2 C#2 Eb2 A2 G#2 C2 Bb2 F#2 G2 B2 D2 E2 •

These periods are taken into account for the analysis because they have been produced by the grammar. For example, if you erase the last one, the parsing will fail.

Performance controls such as "_transpose()" and "_vel()" are ignored. The trace of the (successful) parsing ends as follows:

…
Selected: gram#1[3] B <-> D2 A
E2 . B C D E F G H I J K L
Selected: gram#1[2] A <-> E2 .
A B C D E F G H I J K L
Selected: gram#1[1] S <-> A B C D E F G H I J K L
S

Remember that "." and "•" are identical glyphs on BP3.

Rule selection criteria

To ensure the deterministic analysis is successful, several rules and selection criteria must be observed.

When multiple rules are candidates for rewriting a string Wi​, the system selects based on:

  • (D1) Position: Preference for the rightmost possible derivation.
  • (D2) Context Length: Preference for the rule that maximizes the fulfillment of context conditions.
  • (D3) Pattern Length: "Priority to the largest aggregates of symbols." Longest patterns are recognized first.
  • (D4) Order: The reverse order of appearance in the grammar.

To prevent ambiguity and "checkmate" scenarios in analysis, the following rules are applied:

  • The Chunk Rule: The right-hand side of a rule fi​ cannot be a substring of the right-hand side of a rule fj​ where j < i.
  • The Context Rule: In a LIN subgrammar, a right-hand context can only contain symbols from the subgrammar's external alphabet. Every symbol in the external alphabet of a subgrammar Gi is a terminal of a subgrammar Gj with j < i.

Explanations and proofs are found in chapter 4 of Bel's (1990) thesis: French and (bad English) versions.

Analysis of tabla compositions

Grammars "-gr.dhahatite" and "-gr.dhin--" are authentic examples of qa‘ida, the basic composition/improvisation form in the Lucknow school of tabla, as taught to Jim Kippen by Ustad Afaq Husain Khan in the early 1980s. (Read our joint paper in Anthropological Quarterly, 1989.)

The methods described here were first implemented in 1981 on an Apple IIc computer with just 64 kilobytes of memory… The Bol Processor BP1 was programmed in 6502 assembly language. Still, it was efficient enough to be used as an expert system for field research with leading exponents of the tabla.

The Bol Processor grammar concept emerged from formulating hypotheses about the structures of a qa‘idas within a teaching context. The aim was to generate as many variations as possible that would be deemed correct by an expert. However, proper identification of the language implied that the machine would be able to recognise good and bad variations submitted by experts and students alike. For this reason, each grammar should work "in reverse": given a musical variation (a string of terminals typed on the keyboard), the rules are applied from bottom to top until no rule remains applicable. If the final symbol is "S", the starting symbol, then the musical variation is assessed as "correct".

The "-gr.dhadhatite" grammar

The "dhadhatite" qa‘ida is taught to tabla beginners. This is because it is technically easy while also following complex syntactic structures, which traditional musicians refer to as "qavaid" — a term meaning "grammar" in Urdu (and Arabic). Therefore, the idea of using formal grammars to describe this compositional type was very promising.

A keyboard mapping in the old BP1

The Bol Processor enables users to program the computer's keyboard to map words to keys instead of characters. For example, typing "q" on an English keyboard would type "dha" into the text.

This is possible in "-gr.dhahatite" and "-da.dhadhatite" because "-kb.dhadhatite" is declared at the top of the grammar or data. The mapping is effective after pressing the "escape" key — read details.

When you open the "-gr.dhahatite" grammar, you will see that it is recognised as a "true BP grammar". Click the "Create templates" button, click the "output file" link, and copy the templates at the bottom of the grammar:

TEMPLATES:
[1] ________+________(@0 ________)+________ * (@0 ________++________)(@2 )________
[2] (@0 ++________)+________________+________ * (@1 ) * (@0 ++________)________________
[3] ________(@0 ______+__)________+________ * (@0 ________) * (@2 )________________
[4] (@0 ++____________+____)________+________ * (@1 )________________
[5] (@0 ++______________+__)________+________ * (@1 )________________
[6] (@0 ++________________)________+________ * (@1 )________________

These six templates contain markers of master-slave parentheses created by subgramar #2, including markers of a homomorphism, notated "*", that modifies the content of the following parenthesis, applying a mapping defined in the "-al.dhadhatite" alphabet:

*
dha --> ta
ti
te
na
dhee --> tee
tr

The terminal alphabet of this grammar is made of sound-objects named "dha", "ta", etc. These are quasi-onomatopoeic mnemonics, in an oral notation system, that represent drum-strokes. The "*" homomorphism reflects the musical concept of replacing "open" (resonating) strokes, such as "dha" and "dhee", with their "closed" counterparts, here "ta" and "tee". So, for instance "*(dhadhatite)" should be played "tatatite". You can use any word you like instead of "*" to label the mapping.

The templates also contain structural markers "+" also produced by subgrammar #2, for example:

gram#2[1] S1F S2F S1V S2F E32 <-> S1F +S2F (= V8) +S2F * (= S1F ++ S2F) (: V8) S1F

These markers are used as (proximate) contexts in subgrammar #5, for example:

gram#5[5] ++ A2 <-> ++dheena
gram#5[6] #+ S1F <-> #+ dhadhatitedhadhadheena

In rule [5], the string "++" is a left context, but in rule [6] the string "#+" is a negative context, meaning anything but '+'.

The last rule of subrammar #5 deserves our attention:

gram#5[8] ++ S2F <-- ++ dhadhatitedhadhadheena

It is using the derivation sign "<--" instead of "<->". This means that this rule can only be used in the analysis. It is easy to guess that when the sequence of strokes "dhadhatitedhadhadheena" is found preceded by a "++" (picked up from the template), it is immediately identified as "S2F". Sequences S1F and S2F appear in typical rhythmic contexts shown in subgrammar #1, for example:

gram#1[3] S64 <-> S1V S2F S1F S2F E32

In subgrammar #2, some of these fixed units are replaced with variations:

gram#2[2] S1V S2F S1F S2F E32 <-> (=++ A1 V7 ) +S2F S1F +S2F * (:++ A1 V7 ) * (= ++ S2F ) S1F S1F

To make things easier, the analyst labelled the variables with numbers indicating their durations: 1 for A1, for example, and 7 for V7. This is not compulsory, however.

In subgrammar #3, variations are broken down into smaller units. For example:

gram#3[18] V7 <-> T1 V6
gram#3[19] V7 <-> T2 V5

Then in the following subgrammars, small units are rewritten as sequences of strokes, for instance:

gram#4[5] T2 <-> dheena
…
gram#5[2] + B4 <-> +dhadhateena

Experts familiar with this musical genre will be convinced by an in-depth analysis of this grammar and experiments of production that its construction is based on musical concepts that are embodied perfectly in the formal grammars of the Bol Processor.

There is little to say about the derivation modes of these subgrammars. Most of them could be set to "RND", but "LIN" produces equivalent output in less computation time. This was critical in the Apple II era… Subgrammar #5 is "ORD", the fastest option, but "LIN" or "RND" would also be acceptable.

Now, let us produce a few variations of the "-gr.dhahatite" grammar. In the settings, check the "Non-stop improvize" option and set "Maxitems produced" to a small number, e.g. "4"., which is safe in terms of computation time and disk usage. Set the output file to "BP data file", since no sounds are expected.

If the "Seed for randomization" is set to 0 in the settings, you will get a different sequence each time you click the "PRODUCE ITEM(s)" button, for example:

4+4+4+4/4 dha dha ti te • dha dha dhee na • dha dha dhee na • ti te tee na •
dha dha ti te • dha dha dhee na • dha dha ti te • dha dha tee na •
ta ta ti te • ta ta tee na • ta ta tee na • ti te tee na •
dha dha ti te • dha dha dhee na • dha dha ti te • dha dha dhee na

4+4+4+4/4 dha dha tee na • dha dha dhee na • dha dha ti te • dha dha tee na •
dha dha ti te • dha dha dhee na • dha dha ti te • dha dha tee na •
ta ta tee na • ta ta tee na • ta ta ti te • ta ta tee na •
dha dha ti te • dha dha dhee na • dha dha ti te • dha dha dhee na

4+4+4+4/4 dha dha ti te • dha dha dhee na • dhee na ti te • dhee na tee na •
dha dha ti te • dha dha dhee na • dha dha ti te • dha dha tee na •
ta ta ti te • ta ta tee na • tee na ti te • tee na tee na •
dha dha ti te • dha dha dhee na • dha dha ti te • dha dha dhee na

4+4+4+4/4 dha dha ti te • dha dha dhee na • dha dha ti te • dha dha tee na •
tee na dhee na • dha dha ti te • dha dha ti te • dha dha tee na •
ta ta ti te • ta ta tee na • ta ta ti te • ta ta tee na •
tee na dhee na • dha dha ti te • dha dha ti te • dha dha dhee na

The "4+4+4+4/4" sections marking at the start of each variation indicates a layout most suitable for musicians: the beats are separated by periods ("." or "•"), with each beat containing four strokes — hence the "/4". These periods will be ignored in the parsing because they are not created by any rule in the grammar.

The expression "4+4+4+4" indicates that there are four lines, each containing four beats. As expected, the total duration is 16 beats.

👉 An old, still-valid format for "4+4+4+4/4" is "4+4+4+4*1/4".

Note that if "Split terminal symbols" is unchecked, you get a more compact representation, for instance:

4+4+4+4/4 dhadhatite . dhadhadheena . dhadha-- . titeteena .
dhadhatite . dhadhadheena . dhadhatite . dhadhateena .
tatatite . tatateena . tata-- . titeteena .
dhadhatite . dhadhadheena . dhadhatite . dhadhadheena

There is no guarantee that all variations will be different. Another method that ensures this (at the cost of computation time) is to select "Produce all items" instead of "Non-stop improvize" in the settings. Now, the machine only keeps productions that have not been found in the list before.

Items produced by this grammar can be copied to a Data project, here "-da.dhadhatite". The file in "ctests" contains eight variations. Beware that items should be separated by empty lines, which is automatically the case if you checked "Add lines between items" in the settings of "-gr.dhadhatite". In the settings of "-da.dhadhatite", check "Trace production or parsing", then save the settings and data and click "Analyze" near the first item. The "trace file" displays the detailed process which we won't comment. The "Show process" button displays a summary:

4+4+4+4/4 dhadhatite • dhadhadheena • dheenatite • dheenateena •
dhadhatite • dhadhadheena • dhadhatite • dhadhateena •
tatatite • tatateena • teenatite • teenateena •
dhadhatite • dhadhadheena • dhadhatite • dhadhadheena

Template(s) found
👉 We will try all templates, as per your settings
Analyzing selection…
Interpreting structure…
Expanding polymetric expression…
Using quantization = 10 ms with compression rate = 1
Phase diagram contains 2 lines
👉 Item matched template [1]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [1] rejected by grammar… ❌
Result of failed analysis:
S1F +V8(= S1F) +S2F *(= S1F ++V8)(: S1F) S1F
👉 Item matched template [2]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [2] rejected by grammar… ❌
Result of failed analysis:
(= ++S2F) +V8 S1F +S2F *(: ++S2F) *(= ++V8) S1F S1F
👉 Item matched template [3]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [3] accepted by grammar… ✅
👉 Item matched template [4]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [4] rejected by grammar… ❌
Result of failed analysis:
(= ++S2F V4 +B4) S1F +S2F *(: ++S2F V4 +B4) S1F S1F
👉 Item matched template [5]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [5] rejected by grammar… ❌
Result of failed analysis:
(= ++S2F V6 +B2) S1F +S2F *(: ++S2F V6 +B2) S1F S1F
👉 Item matched template [6]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [6] rejected by grammar… ❌
Result of failed analysis:
(= ++S2F V8) S1F +S2F *(: ++S2F V8) S1F S1F

This summary shows that this variation matches all six templates. However, only one leads to successful parsing, ending with the start string  "S". This helps determine the syntactic structure of the musical example, in addition to telling that it is "correct".

The initial beat and tempo expression "4+4+4+4/4" can be ignored. The following versions will also be parsed successfully:

/4 dhadhatite • dhadhadheena • dheenatite • dheenateena •
dhadhatite • dhadhadheena • dhadhatite • dhadhateena •
tatatite • tatateena • teenatite • teenateena •
dhadhatite • dhadhadheena • dhadhatite • dhadhadheena

dhadhatite • dhadhadheena • dheenatite • dheenateena •
dhadhatite • dhadhadheena • dhadhatite • dhadhateena •
tatatite • tatateena • teenatite • teenateena •
dhadhatite • dhadhadheena • dhadhatite • dhadhadheena

dhadhatitedhadhadheenadheenatitedheenateena
dhadhatitedhadhadheenadhadhatitedhadhateena
tatatitetatateenateenatiteteenateena
dhadhatitedhadhadheenadhadhatitedhadhadheena

dhadhatitedhadhadheenadheenatitedheenateenadhadhatitedhadhadheenadhadhatitedhadhateenatatatitetatateenateenatiteteenateenadhadhatitedhadhadheenadhadhatitedhadhadheena

The "-gr.dhin--" grammar

This grammar describes two sets of variations associated with the qa'ida. The first set covers 16 beats, i.e. 96 strokes at a speed of 6 per beat. The second set covers 32 beats, i.e. 192 strokes.

The 16-beat version was taught to Jim Kippen by Ustad Afaq Husain Khan in the 1980s. Within weeks, Jim had learned to identify the "language" of this qa‘ida, meaning that the machine successfully parsed all examples provided by the expert musician. However, at the end of the training, when Afaq Husain Khan played the same qa‘ida in a concert, he deliberately expanded variations to 32 beats. None of these were recognised by the grammar. Therefore, the grammar was adapted to produce 32-beat variations, and its validity was verified by analysing the variations performed at the concert.

Each set is represented by a variable named "S96" or "S192", created by subgrammar #1:

RND
gram#1 [1] <0> S <-> 4+4/6 S96
gram#1 [2] <5> S <-> 4+4+4+4/6 S192

The weights of rules 0 and 5 indicate that the weights of the rules have been inferred from a set of examples, five of which belonged to the 'S192' variant and none of which belonged to the 'S96' variant. Weight inference is explained on this page.

The weights of rules 0 and 5 indicate that the weights of the rules have been inferred from a set of examples, five of which belonged to the 'S192' variant and none of which belonged to the 'S96' variant. We will explain weight inference later.

'S192' and 'S96' are broken down further into fixed or variable blocks in subgrammar #2, which also introduces master-slave parentheses and the open-closed homomorphism notated "*":

RND
gram#2 [1] <5> S192 <-> (= F48 ) (= V24 ) F'24 *(: F48 ) (: V24 ) F24
gram#2 [2] <0> S96 <-> (= V24 ) F'24 *(: V24 ) F24
gram#2 [3] <0> V24 <-> (= V12 ) (: V12 )
gram#2 [4] <1> V24 <-> (= V12 ) *(: V12 )
gram#2 [5] <0> V24 <-> Q24
gram#2 [6] <0> V24 <-> V12 V12
gram#2 [7] <4> V24 <-> B24
gram#2 [8] <0> V12 <-> (= B6 ) *(: B6 )
gram#2 [9] <1> V12 <-> B12

Once again, we see that several weight 0 rules have not been used to analyse the example set. No explanation is required for other subgrammars.

Let's ask the machine to produce randomly 5 variations using this grammar and rule weights to determine their probabilities:

4+4+4+4/6 dhin--dhagena . dha--dhagena . dhatigegenaka . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
dheenedheenagena . dhagenadhin-- . dheenedheenagena . dheenedha-dheene .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
tin--takena . ta--takena . tatikekenaka . teeneteenakena .
taketirakita . tin--takena . tatikekenaka . teeneteenakena .
dheenedheenagena . dhagenadhin-- . dheenedheenagena . dheenedha-dheene .
tagetirakita . dhin--dhagena . dhatigegenaka . dheenedheenagena

4+4+4+4/6 dhin--dhagena . dha--dhagena . dhatigegenaka . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
dhin--dhagena . dha--dhin-- . dheenedheenagena . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
tin--takena . ta--takena . tatikekenaka . teeneteenakena .
taketirakita . tin--takena . tatikekenaka . teeneteenakena .
dhin--dhagena . dha--dhin-- . dheenedheenagena . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . dheenedheenagena

4+4+4+4/6 dhin--dhagena . dha--dhagena . dhatigegenaka . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
dhagenadha-- . dheenedheenagena . dheenedheenagena . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
tin--takena . ta--takena . tatikekenaka . teeneteenakena .
taketirakita . tin--takena . tatikekenaka . teeneteenakena .
dhagenadha-- . dheenedheenagena . dheenedheenagena . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . dheenedheenagena

4+4+4+4/6 dhin--dhagena . dha--dhagena . dhatigegenaka . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
dhagenadha-- . dheenedheenagena . dheenedha-dheene . dhagenadhin-- .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
tin--takena . ta--takena . tatikekenaka . teeneteenakena .
taketirakita . tin--takena . tatikekenaka . teeneteenakena .
dhagenadha-- . dheenedheenagena . dheenedha-dheene . dhagenadhin-- .
tagetirakita . dhin--dhagena . dhatigegenaka . dheenedheenagena

4+4+4+4/6 dhin--dhagena . dha--dhagena . dhatigegenaka . dheenedheenagena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
dhagenadha-- . dheenedheenagena . dhatigegenaka . teeneteenakena .
tagetirakita . dhin--dhagena . dhatigegenaka . teeneteenakena .
tin--takena . ta--takena . tatikekenaka . teeneteenakena .
taketirakita . tin--takena . tatikekenaka . teeneteenakena .
dhagenadha-- . dheenedheenagena . dhatigegenaka . teeneteenakena .
tagetirakita . dhin--dhagena . dhatigegenaka . dheenedheenagena

The variations differ slightly. These subtle differences are intended to create a "poetic" effect for listeners familiar with the "language" of the tabla.

Create 16 templates in the "-gr.dhin--" grammar. You will notice that the first eight cover 16 beats and the next eight cover 32 beats. This indicates that the creation of templates disregards rule weights and takes all rules in order. The same is true when creating variations with the "Produce all items" option.

A set of three 32-beat variations is provided in the "-da.dhin--" Data project. You can check that all these variations are successfully parsed. The parsing of the third item deserves our attention:

dhin--dhagena • dha--dhagena • dhatigegenaka • dheenedheenagena •
tagetirakita • dhin--dhagena • dhatigegenaka • teeneteenakena •
dheenedheenagena • dheenedheenagena • teeneteenakena • teeneteenakena •
tagetirakita • dhin--dhagena • dhatigegenaka • teeneteenakena •
tin--takena • ta--takena • tatikekenaka • teeneteenakena •
taketirakita • tin--takena • tatikekenaka • teeneteenakena •
dheenedheenagena • dheenedheenagena • teeneteenakena • teeneteenakena •
tagetirakita • dhin--dhagena • dhatigegenaka • dheenedheenagena

👉 Item matched template [12]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [12] accepted by grammar… ✅
👉 Item matched template [13]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [13] accepted by grammar… ✅
👉 Item matched template [16]
Subgrammar 6/6
Subgrammar 5/6
Subgrammar 4/6
Subgrammar 3/6
Subgrammar 2/6
Subgrammar 1/6
Item matching template [16] rejected by grammar… ❌

In the settings of "-da.dhin--", the option "When parsing, check all templates" is selected. This means that even after a successful analysis, the machine will try all the other templates. In this example, templates [12] and [13] match the composition and both lead to successful parsing. Therefore, there is a syntactic ambiguity in this piece, which we may see as part of its "poetic" dimension.

These demos highlight an important feature of the Bol Processor which is summarised by the following:

  1. Every language produced by a true Bol Processor grammar is finite;
  2. The grammar produces a finite (hopefully small) number of templates;
  3. The parsing of an item matched against a template is done in a deterministic way;
  4. Consequently, a true Bol Processor grammar is an identification of its finite language.

These qa‘idas are discussed in detail in our paper Modelling music with grammars (Kippen & Bel, 1992). The paper demonstrates the ability of the model to handle complex structures by taking real examples from the repertoire. It also questions the relevance of attempting to model irregularities encountered in actual performance.

(For geeks) An appendix of our Kippen & Bel (1992) paper explains the process of context-sensitive canonic rightmost derivation  which is used for the parsing.
More details are found in Bel (1987) and in chapter 4 of Bel's (1990) thesis: French and (bad English) versions.

👉 We recommend continuing your reading with the "Learning rule weights" page.


References

Kippen, Jim, & Bernard Bel (1992) Modelling music with grammars: formal language representation in the Bol Processor. In A. Marsden & A. Pople (eds.): Computer Representations and Models in Music, London, Academic Press, 1992, p. 207-238.

Bel, Bernard (1987) Grammaires de génération et de reconnaissance de phrases rythmiques Grammars for the production and recognition of rhythmic sentences).
Actes du 6e congrès AFCET/INRIA, Antibes, 1987b: 353-366.

Derivation modes

 

Let us compare productions by the same grammar
“-gr.tryDerivationModes” in all derivation modes.

The grammar comprises two subgrammars. The second one (gram#2) uses the ORD derivation mode in all examples. We show the trace of a derivation and the resulting production.

'A' and 'B' are variables. 'a', 'b', 'e' and 'f' are terminal symbols that are used to label sound objects.

[Derivation mode]
gram#1[1] S --> A B B A B B A
gram#1[2] A B --> a B
gram#1[3] B A --> B b
gram#1[4] B B A --> e B A
gram#1[5] B B --> f B

ORD
gram#2[1] f B --> f f
gram#2[2] e B --> e e
gram#2[3] A e --> c e


ORD

[Step #1] Selected: [1] LEFT S --> A B B A B B A
A B B A B B A
[Step #2] Selected: [2] LEFT A B --> a B
a B B A B B A
[Step #3] Selected: [2] LEFT A B --> a B
a B B a B B A
[Step #4] Selected: [3] LEFT B A --> B b
a B B a B B b
[Step #5] Selected: [5] LEFT B B --> f B
a f B a B B b
[Step #6] Selected: [5] LEFT B B --> f B
a f B a f B b
[Step #7] Selected: gram#2[1] LEFT f B --> f f
a f f a f B b
[Step #8] Selected: gram#2[1] LEFT f B --> f f
a f f a f f b

Result:  a f f a f f b

In this mode, the rules are applied in order and the rewriting positions are searched from left to right. This process is the fastest way to find a unique solution.


RND  (default derivation mode)

[Step #1] Selected: [1] RND S --> A B B A B B A
A B B A B B A
[Step #2] Selected: [4] RND B B A --> e B A
A e B A B B A
[Step #3] Selected: [3] RND B A --> B b
A e B A B B b
[Step #4] Selected: [2] RND A B --> a B
A e B a B B b
[Step #5] Selected: [5] RND B B --> f B
A e B a f B b
[Step #6] Selected: gram#2[1] LEFT f B --> f f
A e B a f f b
[Step #7] Selected: gram#2[2] LEFT e B --> e e
A e e a f f b
[Step #8] Selected: gram#2[3] LEFT A e --> c e
c e e a f f b

Result:  c e e a f f b
More results:
a f f a f f b
a f f a e e b
a f f b f f b
a f f b e e b
a e e a f f b
a e e b f f b
a e e a e e b
a e e b e e b
a e e c e e b
a f f c e e b
c e e b f f b
c e e a e e b
c e e b e e b
c e e c e e b

Rules are selected at random, with probabilities depending on their weights. The rewriting position is also selected at random unless it is specified as "LEFT" or "RIGHT."


LIN

[Step #1] Selected: [1] LEFT S --> A B B A B B A
A B B A B B A
[Step #2] Selected: [2] LEFT A B --> a B
a B B A B B A
[Step #3] Selected: [4] LEFT B B A --> e B A
a e B A B B A
[Step #4] Selected: [3] LEFT B A --> B b
a e B b B B A
[Step #5] Selected: [4] LEFT B B A --> e B A
a e B b e B A
[Step #6] Selected: [3] LEFT B A --> B b
a e B b e B b
[Step #7] Selected: gram#2[2] LEFT e B --> e e
a e e b e B b
[Step #8] Selected: gram#2[2] LEFT e B --> e e
a e e b e e b

Result:  a e e b e e b
More results:
a e e a f f b
a e e b e e b
a e e b f f b
a f f a e e b
a f f a f f b
a f f b e e b
a f f b f f b

Here, rules are selected at random but the rewriting position is searched from left to right, unless it is specified as "RIGHT" or "RND".


SUB

[Step #1] Selected: [1] RND S --> A B B A B B A
A B B A B B A
[Step #2] Selected: [2] RND A B --> a B
[Step #3] Selected: [4] RND B B A --> e B A
[Step #4] Selected: [2] RND A B --> a B
[Step #5] Selected: [4] RND B B A --> e B A
[Step #6] Selected: [3] RND B A --> B b
[Step #7] Selected: gram#2[2] LEFT e B --> e e
[Step #8] Selected: gram#2[2] LEFT e B --> e e

Result:  a e e a e e b

Here, all eligible rules are applied at once. The process is repeated until no rule is eligible.


SUB1

[Step #1] Selected: [1] LEFT S --> A B B A B B A
[Step #2] Selected: [2] LEFT A B --> a B
[Step #3] Selected: [2] LEFT A B --> a B
[Step #4] Selected: [3] LEFT B A --> B b
[Step #5] Selected: [5] LEFT B B --> f B
[Step #6] Selected: [5] LEFT B B --> f B
[Step #7] Selected: gram#2[1] LEFT f B --> f f
[Step #8] Selected: gram#2[1] LEFT f B --> f f

Result:  a f f a f f b

This is similar to "SUB," except that rewriting positions are searched from left to right, and the set of eligible rules is applied only once.


POSLONG

[Step #1] Selected: [1] LEFT S --> A B B A B B A
A B B A B B A
[Step #2] Selected: [2] LEFT A B --> a B
a B B A B B A
[Step #3] Selected: [4] LEFT B B A --> e B A
a e B A B B A
[Step #4] Selected: [2] LEFT A B --> a B
a e B a B B A
[Step #5] Selected: [4] LEFT B B A --> e B A
a e B a e B A
[Step #6] Selected: [3] LEFT B A --> B b
a e B a e B b
[Step #7] Selected: gram#2[2] LEFT e B --> e e
a e e a e B b
[Step #8] Selected: gram#2[2] LEFT e B --> e e

Result:  a e e a e e b

This is similar to SUB1, except that rewritings only occur in the positions of the longest substrings matched by a rule.

« Translate