Strasheela's Music Representation

About this document
— Strasheela's Music Representation
Introduction
Score Creation
Introduction
Inspect score object
More Inspector features
Output Formats
Introduction
Output LilyPond
Output MusicXML, via Fomus
Output MIDI file
Output Csound file (no sound)
Output Csound file (with sound)
Output CSound file (file dialog)
Music Containers
Introduction
Containers
Specifying and Accessing Basic Score Object Information
intro
Setting/accessing a parameter value
Default parameter values
Type checking (1)
Type checking (2)
Identity test
Info tags (1)
Info tags (2)
The handle argument
Contained objects
Positional information (1)
Positional information (2)
The temporal parameters
The offsetTime parameter (1)
The offsetTime parameter (2)
Incrementally creating scores (1)
Incrementally creating scores (2)
Using extension classes
Using creator functions
Saving a score object as textual score
Finding init Documentation
Higher-Order Accessors
intro
filterItems
findItem
mapItems
forAllItems
countItems
Higher-Order Accessors for Indirectly Contained Objects
intro
collect (1)
collect (2)
collect (3)
collect (4)
collect (5)
map
find
Simultaneous items
User-defined accessors
Customising Output to Export Formats
intro
Output Csound Score
Output Lilypond
to be sorted
Implicit pattern matching in Strasheela

About this document

This file was automatically generated from the interactive Strasheela tutorial. Some aspects of the text only make sense in the original interactive tutorial application (e.g., buttons indicated to press, and positions specified on the screen), and not in this version of the text.

— Strasheela's Music Representation

Introduction

The following sections introduce various aspects of Strasheela's music representation. The representation is then later used for defining musical constraint satisfaction problems.

Please note that this part of the tutorial is still unfinished!

Score Creation

Introduction

This section creates a Strasheela score consisting of a single note. The score is specified textually, and this specification is then transformed into a score object instance.

Inspect score object

This subsection shows how to create a simple score and examine it in the Inspector The Inspector shows

<O: Note>

where the O says that this is an object, and Note is the name of its class. To look "inside" a score object, activate the context menu (typically right-click, but on a mac you must middle-click). Select 'Filter' -> 'Show Textual Score'. This shows the score in the textual form as entered in this example.

If you cannot activate the context menu (for example, having a mac laptop with only one mouse button), you can still see some info in the Inspector by adding `{... toInitRecord($)}'.

WARNING: the Inspector may appear behind this window.

local
   TextualScore = note(startTime:0
                       duration:1000
                       timeUnit:milliseconds
                       pitch:60
                       amplitude:64)
   %% second argument to Score.makeScore explained later
   ScoreInstance = {Score.makeScore TextualScore unit}
in
   {Inspect ScoreInstance}
   %% if difficulties arise with the Context Menu
   {Inspect {ScoreInstance toInitRecord($)}}
end

More Inspector features

There are a few other useful features of the Inspector.

When you select a Filter, the background color behind the score changed to indicate that we are now 'looking into' something. You can 'go back' to the original view by the context menu entry 'unmap' (you have to right-click on the top-level label note).

The Inspector also provides different views on the score via the other 'Filter' context menu entries. These provide more information on the internal structure of a score object. For example, 'Show Score Hierarchy Recursively' reveals that even a plain note is internally represented hiearchically: the note parameters (e.g. duration, pitch) are represented by their own objects.

WARNING: the Inspector may appear behind this window.

local
   TextualScore = note(startTime:0
                       duration:1000
                       timeUnit:milliseconds
                       pitch:60
                       amplitude:64)
   %% second argument to Score.makeScore explained later
   ScoreInstance = {Score.makeScore TextualScore unit}
in
   {Inspect ScoreInstance}
end

Output Formats

Introduction

This section creates a Strasheela score consisting of three notes and demonstrates various methods of outputting the music.

Please note that the shell (or Dos) where you started this tutorial provides feedback. It confirms the writing of the LilyPond or Csound score file, shows the call to the helper applications, etc. When you are using Strasheela from within the OPI, the Emacs buffer 'Oz Emulator' shows this information.

Output LilyPond

The first section outputs the music to a PDF file using LilyPond.

The {MyScore wait} requires Strasheela to wait until all score information not explicitly specified (e.g. the start time of the notes) is propagated in the background. Remember that Oz is a concurrent programming languages: propagation of such score information happens concurrently 'behind the scene'. The wait method blocks until all parameter values in the score are determined. We will not need it later in actual CSPs.

WARNING: you must specify (via the `Settings...' menu entry) where Strasheela can find lilypond, convert-ly (part of LilyPond), and a PDF viewer.

local
   TextualScore = sim(items:[note(offsetTime:0
                                  duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:1500
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0 timeUnit:milliseconds)
   MyScore = {Score.makeScore TextualScore unit}
in
   {MyScore wait}
   {Out.renderAndShowLilypond MyScore
    unit(file:{Tk.return tk_getSaveFile}
         dir:nil)}
end

Output MusicXML, via Fomus

This second section outputs to a MusicXML file (using Fomus), which can be opened, for section, by software like Finale and Sibelius. Fomus supports outputting into further formats and supports tweaking the MusicXML output specifically for Finale or Sibelius usage: just set the appropriate Fomus flag in the section below (see the Fomus documentation for the supported flags). Please note that for running these sections you must specify (via 'Settings...' menu entry) where Strasheela can find the applications lilypond, convert-ly (an application part of Lilypond), a PDF file viewer, and Fomus.

WARNING: you must specify (via the `Settings...' menu entry) where Strasheela can find Fomus.

local
   TextualScore = sim(items:[note(offsetTime:0
                                  duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:1500
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0 timeUnit:milliseconds)
   MyScore = {Score.makeScore TextualScore unit}
in
   {MyScore wait}
   {Out.renderFomus MyScore
    unit(file:{Tk.return tk_getSaveFile}
         dir:nil
         flags:['-x'])}
end

Output MIDI file

This section saves a MIDI file with the procedure `Out.renderMidiFile'.

The filename is given directly as "/tmp/test.midi" (the .midi is added automatically). You may change this by editing the argument csvDir and midiDir (the directory) and file (the file name without extension).

WARNING: Windows users will have to input a directory path like "C:\\Dokumente und Einstellungen\\" (German settings).

WARNING: you must specify (via the `Settings...' menu entry) where Strasheela can find a MIDI player.

local
   TextualScore = sim(items:[note(offsetTime:0
                                  duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:1500
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0 timeUnit:milliseconds)
   MyScore = {Score.makeScore TextualScore unit}
in
   {MyScore wait}
   {Out.midi.renderAndPlayMidiFile MyScore
   unit(file:'test'
         midiDir:'/tmp/'
         csvDir:'/tmp/')}
end

Output Csound file (no sound)

This section saves a Csound score file with the procedure `Out.outputCsoundScore'.

The filename is given directly as "/tmp/test.sco" (the .sco is added automatically). You may change this by editing the argument scoDir (the directory) and file (the file name without extension). Windows users will have to input a directory path like "C:\\Dokumente und Einstellungen\\" (German settings..).

Please have a look at the content of the resulting Csound score file (on UNIX, you may just uncomment the last line of the first section). You will notice that the Csound p-fields p2 and p3 (i.e. the second and third columns) correspond to the notes' offset time and duration, now measured in Csound beats (note the Csound tempo specification above, which can be changed with the procedure Init.setTempo, but defaults to 60.0). Moreover, p4 is the amplitude (measure as midi velocity value in the section and now normalised in 0-1) and p5 is the pitch (measured as midi keynumber). In strasheela/goodies/csound you will find a demo Csound orc file which works with these settings. This file is used in the second and third section.

WARNING: you must specify (via the `Settings...' menu entry) where Strasheela can find CSound.

local
   TextualScore = sim(items:[note(offsetTime:0
                                  duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:1500
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0 timeUnit:milliseconds)
   MyScore = {Score.makeScore TextualScore unit}
in
   {MyScore wait}
   {Out.outputCsoundScore MyScore
    unit(scoDir:"/tmp/"
         file:"test")}
   % UNIX: show content of resulting test.sco at terminal (uncomment next line)
   % {Out.exec 'cat' ["/tmp/test.sco"]}
end

Output Csound file (with sound)

This section additionally calls Csound in the background and open the resulting sound file with an editor with the procedure `Out.renderAndPlayCsound'.

There is one new file variable: soundDir (the directory for the resulting sound file).

WARNING: you must specify (via the `Settings...' menu entry) where Strasheela can find CSound.

local
   TextualScore = sim(items:[note(offsetTime:0
                                  duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:1500
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0 timeUnit:milliseconds)
   MyScore = {Score.makeScore TextualScore unit}
in
   {Init.setTempo 80.0}
   {MyScore wait}
   {Out.renderAndPlayCsound MyScore
    unit(file:"test"
         scoDir:"/tmp/"
         soundDir:"/tmp/")}
end

Output CSound file (file dialog)

In this example, the output file is specified with a file dialog for convenience.

Most directory arguments of output procedures (e.g., scoDir and soundDir in Out.renderAndPlayCsound) can be omitted; their default value are directories set in the Strasheela environment (cf. the _ozrc file in the Strasheela directory for an section how to set these default directories).

WARNING: you must specify (via the `Settings...' menu entry) where Strasheela can find CSound.

local
   TextualScore = sim(items:[note(offsetTime:0
                                  duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:1500
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0 timeUnit:milliseconds)
   MyScore = {Score.makeScore TextualScore unit}
in
   {Init.setTempo 40.0}
   {MyScore wait}
   %% Specify file with file dialog (full path without extension)
   {Out.renderAndPlayCsound MyScore
    unit(file:{Tk.return tk_getSaveFile}
         scoDir:nil soundDir:nil)}
end

Music Containers

Introduction

The following section introduces the overall construction and organization of Strasheela's music representation.

Containers

This example introduces Strasheela containers: notes are organized into sequential containers (seq) and simultaneous containers (sim).

In the previous section, the timeUnit was set to milliseconds. Here, it is set to beats(4), which means that the duration 4 indicates a quarter-note (a beat) and consequently duration 1 is a sixteenth-note. In general, all musical parameters support some unit of measurement which indicates how the numeric parameter value is interpreted (in particular for output into export formats). For example, the pitchUnit defaults to (MIDI) 'keynumber', but can also be set to midicent, frequency, or et72 (i. e. equal temperament with 72 steps per octave). These various parameter units of measurements are supported, because Strasheela parameter values are (at least presently) always integers. Whereas the timeUnit must be set only once in the score, other units can be set individually for every score object. The timeUnit is handled differently, because Strasheela implicitly applies some constraints to all temporal parameters (e.g., the temporal structure in sequential and simultaneous containers is enforced by constraints), and these constraints require a consistent timeUnit for all parameters.

local
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration:2
                                         pitch:64
                                         amplitude:64)
                                    note(duration:2
                                         pitch:65
                                         amplitude:64)
                                    note(duration:4
                                         pitch:67
                                         amplitude:64)
                                    note(duration:4
                                         pitch:62
                                         amplitude:64)])
                         seq(items:[sim(items:[note(duration:8
                                                    pitch:48
                                                    amplitude:64)
                                               note(duration:8
                                                    pitch:55
                                                    amplitude:64)])
                                    sim(items:[note(duration:4
                                                    pitch:50
                                                    amplitude:64)
                                               note(duration:4
                                                    pitch:54
                                                    amplitude:64)])])]
                  startTime:0
                  timeUnit:beats(4))
              unit}
in
   {MyScore wait}
   {Out.midi.renderAndPlayMidiFile MyScore
    unit(file:'test')}
%Uncomment if desired:
%   {Out.renderAndShowLilypond MyScore
%    unit(file:{Tk.return tk_getSaveFile}
%        dir:nil)}
end

Specifying and Accessing Basic Score Object Information

intro

Various information about score objects can be stored and retrieved from Strasheela's music representation. This information is used for formulating music theory models. For example, a musical CSP may specify a score where all note pitch variables are initialised to certain domains, and neighbouring melodic notes are then accessed and constrained.

The textual music representation allows for the convenient definition of complex scores, as already shown by the previous sections. The interface defined for Strasheela score objects, on the other hand, simplifies the access to score information. As shown before, the textual representation is transformed into a score object with the function Score.makeScore. Vice versa, a score object can be transformed into its textual representation (see below for an section). Using the textual representation and Score.makeScore is strongly recommended for creating Strasheela score objects, because it hides low-level details of the score object creation process.

This section introduces the textual music representation format in more detail. In general, a textual score is a (often nested) Oz record. Record labels correspond with score classes, and record features are initialisation arguments for the class creation. This was already demonstrated in previous sections. In the following, specific aspects are introduced in more detail by sections. For additional information and sections of the textual representation please see the Score.makeScore reference documentation (strasheela/doc/api/node6.html#entity225) and and the Score.makeScore sections in strasheela/testing/ScoreCore-test.oz.

The section also demonstrates the retrieval of various basic information. Basic information includes access to object parameter values (e.g. the pitch of a note), checking the type (class) of an object (e.g. checking whether object X is a note), checking the identity of two objects, and exploring the hierarchic structure of a score. Later sections will show how you can access more complex information.

The naming of many methods and functions accessing basic information follows common conventions. Such convensions are briefly summarised here. The name of accessor methods usually start with 'get' as in getSomething (e.g., getDuration, getPitch, getContainers). Type-checking methods usually start with 'is' (e.g., isNote, isPause). Methods which check a has-a relation often start with 'has' (hasSuccessor, hasThisInfo). Converters start often with 'to' or contain 'to' in their name (e.g. toInitRecord, but also Int.toFloat). The name of many constructors starts with 'make' (Score.makeScore, Score.makeClass).

Setting/accessing a parameter value

No information available.

/* A parameter value (e.g. the duration of a note, or of a
simultaneous container) is usually specified in the textual score with
the name of the parameter as a record feature (e.g. duration), and
accessed with a method get<MyParameter> (e.g. getDuration). The
following section initialises and accesses the duration of a
simultaneous container. */

local
  MySim = {Score.makeScore sim(duration:3) unit}
in
  {Inspect {MySim getDuration($)}}
end

Default parameter values

No information available.

/* The textual music representation specifies initialisation arguments
for score object to create (e.g., sets the pitch of a note, or the
duration of a sequential container). Most of these initialisation
arguments are optional, and Strasheela defines a default value for
these initialisation arguments. For example, parameter values such as
durations and pitches default to a constrained variable.

This section creates a single note and specifies no initialisation
arguments at all. You can inspect the note's parameters and their
default values (mostly undetermined constrained variables). In the
Inspector, use the note's context menu: 'Filter' -> 'Show Score
Hierarchy Recursively'.

An undetermined parameter value is accessed the same way as a
determined parameter. For example, a note's undetermined pitch is
accessed with the method getPitch. Accessing undetermined parameter
values is important for constraining these values. */

local
   MyNote = {Score.makeScore note unit}
in
   {Inspect pitch # {MyNote getPitch($)}}
   {Inspect note # MyNote}
end

Type checking (1)

No information available.

/* The type (class) of Strasheela objects can be checked. Methods such
as isNote or isPause return either true or false. This information can
be useful, for section, to decide to which object a constraint is
applied and which objects are skipped. */

local
  MyNote = {Score.makeScore note unit}
in
  %% MyNote is a note but note a pause
  {Inspect isNote # {MyNote isNote($)}}
  {Inspect isPause # {MyNote isPause($)}}
end

Type checking (2)

No information available.

/* Strasheela defines a hierarchy of types (class hierarchy). For
section, see strasheela/doc/api/class25.html for the superclasses of
the note class. Consequently, an object returns true for the
typechecker of its class and all its superclasses. This is useful, for
section, for being able to apply a constraint to all notes and pauses
but not to the containers these objects contained in (possibly deeply
nested).

Strasheela uses the following terminolody. Every Strasheela object is
a ScoreObject. Strasheela distinguishes between Parameters (e.g. the
pitch of a note), Elements (e.g. notes and pauses), and Containers
(e.g. simultaneous and sequential containers). An umbrella term
(superclass) for Elements and Containers is Items. For an even more
fine-grained terminology please refer to the reference documentation.

The list function Map was introduced in section "Higher-Order
Programming". The function Record.map is the same as map, but for
records. */

local
  MyNote = {Score.makeScore note unit}
  MyPause = {Score.makeScore pause unit}
  MySim = {Score.makeScore sim unit}
  MyObjects = unit(note:MyNote pause:MyPause sim:MySim)
in
  %% Both MyNote and MyPause are elements, and all three objects are items.
  {Inspect isElement # {Record.map MyObjects
                        fun {$ X} {X isElement($)} end}}
  {Inspect isItem # {Record.map MyObjects
                     fun {$ X} {X isItem($)} end}}
end

Identity test

No information available.

/* Sometimes we want to check the identity of score objects, for
section, to prevent that constraints between identical objects are
applied. The identity of score objects can be tested with the ==
operator (and its opposite, the \= operator). Please note that the
operator == checks for the identity of objects, not for their
equality. */

local
  Note1 = {Score.makeScore note unit}
  Note2 = {Score.makeScore note unit}
in
  {Inspect identicalObjects # (Note1 == Note1)}
  {Inspect differentObjects # (Note1 == Note2)}
end

Info tags (1)

No information available.

/* It is often useful to add various additional information to certain
score objects (e.g. for naming a container representing a voice, or
for tagging notes with a specific purpose in the music). It is
convenient to have a general info attribute for all such information,
instead of defining some special attribute for each information tag
which might occur.

Information tags can be specified via the info argument for a score
object. The method hasThisInfo returns true for a specific info tag,
in the case the score object was tagged with it. */

local
  MyNote = {Score.makeScore note(info:myTag) unit}
in
  {Inspect {MyNote hasThisInfo($ myTag)}}
end

Info tags (2)

No information available.

/* A score object get be given multiple information tags at the info
argument, and further info tags can be added later with the method
addInfo. All these tags can be checked with the method hasThisInfo, as
shown above. The method getInfo returns a list of all information tags
of a given object. */

local
  MyNote = {Score.makeScore note(info:[hi there]) unit}
in
  {MyNote addInfo(test)}
  {Inspect {MyNote getInfo($)}}
end

The handle argument

No information available.

/* The handle argument makes it easy to directly access some object
within a nested score. The argument expects a variable. After score
creation, this variable is bound to the score object to which the
handle argument was given. In this section, the first and the second
note in a sequential container are bound to the variables Note1 and
Note2 using their handle argument (the method toInitRecord returns the
textual representation of a score item). Watch the different note
pitches to confirm them. Every score object in the textual
representation supports the handle argument. */

local
   Note1 Note2
   MyScore = {Score.makeScore seq(items:[note(handle:Note1
                                              duration:2
                                              pitch:60
                                              amplitude:64)
                                         note(handle:Note2
                                              duration:2
                                              pitch:62
                                              amplitude:64)
                                         note(duration:4
                                              pitch:64
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats(4))
              unit}
in
  {Inspect note1 # {Note1 toInitRecord($)}}
  {Inspect note2 # {Note2 toInitRecord($)}}
end

Contained objects

No information available.

/* The method getItems returns the items contained in a
container. Similarily, the containers of an item are accessed with the
method getContainers. This section makes again use of the handle
argument (see above). */

local
   MyNote
   MyScore = {Score.makeScore seq(items:[note(duration:2
                                              pitch:60
                                              amplitude:64)
                                         note(handle:MyNote
                                              duration:2
                                              pitch:62
                                              amplitude:64)
                                         note(duration:4
                                              pitch:64
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats(4))
              unit}
in
  {Inspect items # {MyScore getItems($)}}
  {Inspect containers # {MyNote getContainers($)}}
end

Positional information (1)

No information available.

/* Sometimes it is useful to access positionally related
information. For example, you may want to access the successor of a
note in a voice in order to constrain the melodic interval between
these two notes.

A Strasheela item can be contained in multiple containers (e.g. a
temporal container and a container expressing the motific or harmonic
structure). See the documentation of Score.makeScore for details. This
approach may be seldomly used because it is often too much restrictive
(see my Thesis Sec. 5.4.3.3 for a discussion). Nevertheless, the
access to positionally related information must take into account that
a score object may be contained in multiple containers.

Therefore, positional accessor methods expect a specification which
container they must refers to. For example, the container may be given
as an argument or a method tailored for a specific container type such
as getTemporalSuccessor is used (an item can only be contained in a
single temporal container at maximum). */

local
   MyNote
   TextualScore = sim(items:[note(duration:2
                                  pitch:60
                                  amplitude:64)
                             note(handle:MyNote
                                  duration:2
                                  pitch:62
                                  amplitude:64)
                             note(duration:4
                                  pitch:64
                                  amplitude:64)]
                      startTime:0
                      timeUnit:beats(4))
   %% container can be acccessed from MyNote with getTemporalContainer
   _/*Ignore container*/ = {Score.makeScore TextualScore unit}
   MyContainer = {MyNote getTemporalContainer($)}
in
  {Inspect position # {MyNote getPosition($ MyContainer)}}
  {Inspect successor # {{MyNote getSuccessor($ MyContainer)} toInitRecord($)}}
end

Positional information (2)

No information available.

/* The hierarchy of temporal items must be a tree. Consequently, if
you are interested in positional information from this hierarchy, then
you do not need to specify a container. Instead, use specialised
methods such as getTemporalSuccessor or getTemporalPredecessor. Please
note that getTemporalSuccessor returns the positional successor in a
TemporalContainer (not necessarily the temporal successor!). */

local
   MyNote
   TextualScore = sim(items:[note(duration:2
                                  pitch:60
                                  amplitude:64)
                             note(handle:MyNote
                                  duration:2
                                  pitch:62
                                  amplitude:64)
                             note(duration:4
                                  pitch:64
                                  amplitude:64)]
                      startTime:0
                      timeUnit:beats(4))
   _/*Ignore container*/ = {Score.makeScore TextualScore unit}
in
  {Inspect {{MyNote getTemporalSuccessor($)} toInitRecord($)}}
end

The temporal parameters

No information available.

/* All temporal objects (e.g., notes, and temporal containers like the
sequential and the simultaneous container) define the three temporal
parameters startTime, duration and endTime (they also define a
temporal parameter offsetTime, discussed below). Three parameters are
initially constrained to the obvious relation.

  StartTime + Duration = EndTime

Consequently, you can choose which parameter(s) you want to specify
during the initialisation, and all three parameters will always be
consistent. You even can leave all three parameters unspecified (as we
saw above), and can only further constrain their value and thus define
a rhythmical CSP. The following section demonstrates this by
specifying the startTime and the endTime of a note. You can examine
all three parameters with the Inspector (Show Score Hierarchy
Recursively). */

local
   MyNote = {Score.makeScore note(startTime:2
                                  endTime:5
                                  timeUnit: beats)
             unit}
in
   {Inspect MyNote}
end

The offsetTime parameter (1)

No information available.

/* All temporal score objects (e.g., notes, simultaneous and
sequential containers) support the parameter offsetTime. The meaning
of this parameter depends on the class of the temporal container in
which an object is contained in. For objects in a simultaneous
container, the offsetTime delays the start of its contained objects
with respect to the startTime of this container. The Csound section
shown previously is reproduced here. */

local
   TextualScore = sim(items:[note(offsetTime:0
                                  duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:2000
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0
                      timeUnit:milliseconds)
   ScoreInstance = {Score.makeScore TextualScore unit}
in
   {Init.setTempo 40.0}
   {ScoreInstance wait}
   {Out.renderAndPlayCsound ScoreInstance
    unit(file:{Tk.return tk_getSaveFile}
         scoDir:nil soundDir:nil)}
end

The offsetTime parameter (2)

No information available.

/* For objects in a sequential container, the offsetTime specifies a
pause between the object and its preceeding score object in this
container.

he offsetTime parameter defaults to 0 (this is the only parameter in
the Strasheela core which defaults to a determined value). Please note
that (like all FD integers) the offsetTime can *not* be negative (i.e.,
it cannot be used for expressing an overlap of objects in a
sequential container). */

local
   TextualScore = seq(items:[note(duration:1000
                                  pitch:60
                                  amplitude:64)
                             note(offsetTime:1500
                                  duration:1000
                                  pitch:62
                                  amplitude:64)
                             note(offsetTime:500
                                  duration:1000
                                  pitch:64
                                  amplitude:64)]
                      startTime:0
                      timeUnit:milliseconds)
   ScoreInstance = {Score.makeScore TextualScore unit}
in
   {Init.setTempo 40.0}
   {ScoreInstance wait}
   {Out.renderAndPlayCsound ScoreInstance
    unit(file:{Tk.return tk_getSaveFile}
         scoDir:nil soundDir:nil)}
end

Incrementally creating scores (1)

No information available.

/* The function Score.makeScore creates a fully initialised score. The
hierarchic structure of such a fully initialised score is fixed (at
least for a CSP it should be regarded as fixed). Sometimes, however,
it is convenient to create a score incrementally by creating parts
independently and combining them later. One option is, that you simply
create independent textual scores and combine them later. */

local
   Voice1 = seq(info:voice1
                items:[note(duration:4
                            pitch:59
                            amplitude:64)
                       note(duration:4
                            pitch:60
                            amplitude:64)])
   Voice2 = seq(info:voice2
                items:[note(duration:4
                            pitch:67
                            amplitude:64)
                       note(duration:4
                            pitch:67
                            amplitude:64)])
   MyScore = {Score.makeScore
              sim(items:[Voice1 Voice2]
                  startTime:0
                  timeUnit:beats(4))
              unit}
in
   {Inspect {MyScore toInitRecord($)}}
end

Incrementally creating scores (2)

No information available.

/* In certain cases, you want to create your score incrementally, but
you need access to the score objects (i.e. not only their textual
representation). You could use the approach shown in the previous
section and additionally use the handle argument introduced
before. With that approach, the score object is available after the
full score is created. Another option is to use the function
Score.makeScore2 instead of Score.makeScore. Score.makeScore2
immediately outputs a score which is not yet fully initialised and can
still be inserted in other Strasheela containers. Such partially
initialised scores are later fully initialised simply by using these
score objects within a call of Score.makeScore (or by explitictly
calling it with the procedure Score.initScore). This section only
reproduces the previous section with Score.makeScore2. A later section
below will show a use of Score.makeScore2 which cannot be reproduced
with independent textual score objects nor with the handle
argument. */

local
   Voice1 = {Score.makeScore2 seq(info:voice1
                                  items:[note(duration:4
                                              pitch:59
                                              amplitude:64)
                                         note(duration:4
                                              pitch:60
                                              amplitude:64)])
             unit}
   Voice2 = {Score.makeScore2 seq(info:voice2
                                  items:[note(duration:4
                                              pitch:67
                                              amplitude:64)
                                         note(duration:67
                                              pitch:55
                                              amplitude:64)])
             unit}
   MyScore = {Score.makeScore
              sim(items:[Voice1 Voice2]
                  startTime:0
                  timeUnit:beats(4))
              unit}
in
   {Inspect {MyScore toInitRecord($)}}
end

Using extension classes

No information available.

/* A Strasheela music representation created with Score.makeScore
consists of objects, that is class instances. Per default,
Score.makeScore uses the classes defined by the Strasheela core -- as
did all sections so far.

Strasheela extensions, however, often define their own classes which
extend the classes of the Strasheela core. For example, Strasheela's
harmony model -- an extension provided in the strasheela/contribtion
folder -- provides the class HS.score.note (the harmony extention --
i.e. its functor -- is usually bound to the variable HS). The class
HS.score.note extends the standard note class Score.note by the notion
of pitch classes (among many other things).

This section shows how you can specify which classes Score.makeScore
should use when creating a score. The clases are specified in the
second argument of Score.makeScore (which so far was alway only
unit). This argument expects a record whose features match the score
object labels in the textual music representation, and whose values
are the classes which should be used for objects with this label.

Every Strasheela score class defines a method init, which is called
internally when an instance of this class is created (cf. Strasheela's
reference documentation for a init method definition of various
classes). The feature/value-pairs of an object's textual
representation correspond to these init method arguments. For example,
the init method of the class HS.score.note expects the arguments of
the standard note class Score.note (e.g., duration and pitch) with
additional arguments (e.g., pitchClass). The textual representation
output of this sections shows further arguments, which are irrelevant
here. */

local
   MyNote = {Score.makeScore note(startTime:0
                                  duration:4
                                  pitchClass:7
                                  pitch:{FD.int 48#72})
             unit(note:HS.score.note)}
in
   {Inspect {MyNote toInitRecord($)}}
end

Using creator functions

No information available.

/* You can customise further the meaning of a textual music
representation. Score object labels in the textual representation can
also be mapped to creator functions via the second argument of
Score.makeScore (instead of classes). Using this feature, a single
score object in the textual music representation can express a complex
subscore.

In this section, the meaning of the textual score object with the
label mySection is defined by the creator function MakeMySection. Such
a function expects the record (namely the fully textual score object)
and returns a score object (not fully initialised, i.e., created with
Score.makeScore2 as described above). */

local
   fun {MakeMySection Args}
      {Score.makeScore2 seq(items:{LUtils.collectN Args.n
                                   fun {$} note(duration:{FD.int 1#8}) end}
                            duration:Args.dur)
       unit}
   end
   MyScore = {Score.makeScore sim(items:[mySection(n:2 dur:4)
                                        mySection(n:4 dur:4)]
                                 startTime:0
                                 timeUnit:beats)
             unit(mySection:MakeMySection
                  sim:Score.simultaneous)}
in
   {Inspect {MyScore toInitRecord($)}}
end

Saving a score object as textual score

No information available.

/* A score object can be transformed 'back' into its textual
representation, for section, to hand-edit the score directly. This
works even for score objects which are not fully determined, as the
section below demonstrates (its pitches are variables). The procedure
Out.saveScore saves a given score object as Oz code into a text file,
and the function Out.loadScore loads the (possibly edited) score from
a file and returns a score object. */

local
   MyScore = {Score.makeScore seq(items:[note(duration:2
                                              pitch:{FD.int 60#67}
                                              amplitude:64)
                                         note(duration:1
                                              pitch:{FD.int 60#67}
                                              amplitude:64)
                                         note(duration:3
                                              pitch:{FD.int 60#67}
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats)
              unit}
in
   {Out.saveScore MyScore unit(file:{Tk.return tk_getSaveFile}
                               %% use file name as given by GUI
                               dir:nil
                               extension:nil)}
end

%% Look at the textual score to compare with the original
{Inspect
 {Out.loadScore unit(file:{Tk.return tk_getOpenFile}
                     dir:nil
                     extension:nil)}}

Finding init Documentation

No information available.

/* The arguments of score objects in the textual score representation
are the arguments for the init method of their class. So, the init
method documentation also serves as documentation fo the textual score
representation.

However, many init method arguments are inherited from superclasses
and it can be hard to find which superclass defines and documents
them. In such a situation, you can ask the score object itself. The
method getInitArgSources returns the classes which define an init
arguments and the method getInitArgDefaults returns their default
values (_ indicates no default value). */

% the values at the record features are the classes which define this argument
{Browse initArgs # {{Score.makeScore note unit} getInitArgSources($)}}

{Browse initArgDefaults # {{Score.makeScore note unit} getInitArgDefaults($)}}

Higher-Order Accessors

intro

Strasheela provides various means for accessing information about multiple score objects contained in a container. Many of these means are higher-order methods, that is, the user specifies the desired information with a method or function as argument (see the introduction to higher order programming previously in this tutorial).

The section shows shows how a contained can be queried about its directly contained score items (all containers, notes and pauses belong to the superclass item — in contrast to score parameters). You will notice a consistent naming scheme of these methods. Their name usually end in 'Items' (as in mapItems).

filterItems

No information available.

/* The method filterItems returns all items directly contained in a
container for which a given unary function or method returns true. Try
using a Boolean method (e.g., isNote). */

local
   MyScore = {Score.makeScore seq(items:[note(duration:2
                                              pitch:60
                                              amplitude:64)
                                         note(duration:2
                                              pitch:62
                                              amplitude:64)
                                         note(duration:4
                                              pitch:64
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats(4))
              unit}
in
   {Inspect {MyScore filterItems($ fun {$ MyNote}
                                      {MyNote getDuration($)} < 4
                                   end)}}
end

findItem

No information available.

/* The method findItem returns the first item for which a test (method
or function) returns true. */

local
   MyScore = {Score.makeScore seq(items:[note(duration:2
                                              pitch:60
                                              amplitude:64)
                                         note(duration:2
                                              pitch:62
                                              amplitude:64)
                                         note(duration:4
                                              pitch:64
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats(4))
              unit}
in
   {Inspect {MyScore findItem($ fun {$ MyNote}
                                   {MyNote getDuration($)} < 4
                                 end)}}
end

mapItems

No information available.

/* The method mapItems applies a unary function or method to every
object which fulfills a test. Here, the pitchs of all notes in MyScore
are collected with the method getPitch. Try and replace getPitch with
another accessor method introduced before (e.g., getDuration). Also,
try and replace the test (e.g., using isNote), or leave the test out
altogether. */

local
   MyScore = {Score.makeScore seq(items:[note(duration:2
                                              pitch:60
                                              amplitude:64)
                                         note(duration:2
                                              pitch:62
                                              amplitude:64)
                                         note(duration:4
                                              pitch:64
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats(4))
              unit}
in
   {Inspect {MyScore mapItems($ getPitch
                              test: fun {$ MyNote}
                                       {MyNote getDuration($)} < 4
                                     end)}}
end

forAllItems

No information available.

/* The method forAllItems is very similar to mapItems. forAllItems
applies a unary procedure or method to every object which fulfills a
test -- in contrast to mapItems this procedure/method returns no value
(cf. the Oz procedures Map and ForAll introduced before). */

local
   MyScore = {Score.makeScore seq(items:[note(duration:2
                                              pitch:60
                                              amplitude:64)
                                         pause(duration:2)
                                         note(duration:4
                                              pitch:64
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats(4))
              unit}
in
   {MyScore forAllItems(proc {$ X} {Inspect X} end
                        test: isNote)}
end

countItems

No information available.

/* The method countItems returns the number of score objects directly
contained in a container which fulfill a given test. This test can be
a method or a function (try isNote, isPause or isItem instead). */

local
   MyScore = {Score.makeScore seq(items:[note(duration:2
                                              pitch:60
                                              amplitude:64)
                                         pause(duration:2)
                                         note(duration:4
                                              pitch:64
                                              amplitude:64)]
                                  startTime:0
                                  timeUnit:beats(4))
              unit}
in
   {Inspect {MyScore countItems($ test:fun {$ X}
                                         {X getDuration($)} < 4
                                       end)}}
end

Higher-Order Accessors for Indirectly Contained Objects

intro

The previous section discussed methods for accessing (information on) directly contained score objects. This sections shows methods which traverse the whole score hierarchy for accessing information. For example, the method collect returns a list of directly and indirectly contained score objects.

These methods support a few additional arguments. You saw the argument test already in the section before. The argument 'level' expects an integer which limits the depth the method decends during its traversal (its default is all, i.e., full recursive traversal). The argument 'mode' specifies whether only the subtree below the given object is traversed (mode: tree) or whether the full score graph is traversed (mode: graph).

collect (1)

No information available.

/* When no further arguments are specified, the method collect returns
all objects directly or indirectly contained in MyScore, even the
containers and parameters. It does not, however, include MyScore
itself in the output. */

local
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration:1 pitch:60)
                                    note(duration:2 pitch:60)])
                         seq(items:[note(duration:2 pitch:64)
                                    note(duration:1 pitch:64)])]
                  startTime:0)
              unit}
in
   {Inspect {MyScore collect($)}}
end

collect (2)

No information available.

/* With the the argument test, you filter which objects are
collected. Again, this argument expects either a Boolean function or
method. This section returns all notes whose duration is > 1.*/

local
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration:1 pitch:60)
                                    note(duration:2 pitch:60)])
                         seq(items:[note(duration:2 pitch:64)
                                    note(duration:1 pitch:64)])]
                  startTime:0)
              unit}
in
   {Inspect {MyScore collect($ test:fun {$ X}
                                       {X isNote($)} andthen
                                       {X getDuration($)} > 1
                                    end)}}
end

collect (3)

No information available.

/* This section also filters score objects with the test argument of
collect. Here, all sequential objects in MyScore are contained. */

local
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration:1 pitch:60)
                                    note(duration:2 pitch:60)])
                         seq(items:[note(duration:2 pitch:64)
                                    note(duration:1 pitch:64)])]
                  startTime:0)
              unit}
in
   {Inspect {MyScore collect($ test:isSequential)}}
end

collect (4)

No information available.

/* This section collects all items (i.e. notes and containers)
directly contained in MyScore (level 1). Try increasing the level to
2. Please note that the level only specifies the nesting of items, but
not parameters (i.e., there is no third nesting level in this
section). Thus, if you also allow for parameters in the output (by
removing the test isItem), then the parameters from the items of the
levels specified are included. */

local
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration:1 pitch:60)
                                    note(duration:2 pitch:60)])
                         seq(items:[note(duration:2 pitch:64)
                                    note(duration:1 pitch:64)])]
                  startTime:0)
              unit}
in
   {Inspect {MyScore collect($ level:1 test:isItem)}}
end

collect (5)

No information available.

/* This section demonstrates the graph traversal of collect, which
considers not only score items contained in a given container, but
also containers in which a given item is contained in.

This section collects all items in the score (argument test:isItem)
which are either contained in the container MyItem or in which MyItem
is contained (argument mode:graph). Please note, however, that MyItem
itself is not included: as you already have access to it you can
easily add it to the list returned (how?). MyItem is again bound with
the handle argument, see above. */

local
   MyItem
   _ /* Ignore */ = {Score.makeScore
                     sim(items:[seq(items:[note(duration:1 pitch:59)
                                           note(duration:2 pitch:60)])
                                seq(handle:MyItem
                                    items:[note(duration:2 pitch:67)
                                           note(duration:1 pitch:67)])]
                         startTime:0)
                     unit}
in
   {Inspect {MyItem collect($ mode:graph test:isItem)}}
end

map

No information available.

/* The method map is like mapItems, but recursively traverses a score
hierarchy. Like the method collect, it supports the arguments test,
level, and mode. There also exists a method forAll, which corresponds
to forAllItems but works with score hierarchies. */

local
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration:1 pitch:59)
                                    note(duration:2 pitch:60)])
                         seq(items:[note(duration:2 pitch:67)
                                    note(duration:1 pitch:67)])]
                  startTime:0)
              unit}
in
   {Inspect {MyScore map($ getPitch test:isNote)}}
end

find

No information available.

/* The method find is like the method findItems, but recursively
traverses a score hierarchy. This section returns the first
simultaneous object which directly contains only notes.

Again, find also supports the arguments test, level, and
mode. Moreover, there exists a method filter which corresponds to
filterItems. */

local
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration:2
                                         pitch:64
                                         amplitude:64)
                                    note(duration:2
                                         pitch:65
                                         amplitude:64)
                                    note(duration:4
                                         pitch:67
                                         amplitude:64)
                                    note(duration:4
                                         pitch:62
                                         amplitude:64)])
                         seq(items:[sim(items:[note(duration:8
                                                    pitch:48
                                                    amplitude:64)
                                               note(duration:8
                                                    pitch:55
                                                    amplitude:64)])
                                    sim(items:[note(duration:4
                                                    pitch:50
                                                    amplitude:64)
                                               note(duration:4
                                                    pitch:54
                                                    amplitude:64)])])]
                  startTime:0
                  timeUnit:beats(4))
              unit}
in
   {Inspect {MyScore find($ fun {$ X}
                               {X isSimultaneous($)} andthen
                               {All {X getItems($)}
                                fun {$ MyItem} {MyItem isNote($)} end}
                            end)}}
end

Simultaneous items

No information available.

/* The previous sections demonstrated various generic means for
accessing information. In addition, Strasheela provides predefined
accessors for more specific information. For example, the method
getSimultaneousItems returns all score in the whole score items which
are simultaneous to a given item (i.e. overlap in time). Like many
methods shown before, getSimultaneousItems also supports an argument
test.

The score topology of this section is a simultaneous container,
containing two sequential containers which in turn contain notes. Such
a topology can express polyphony where multiple voices run in
parallel. MyNote is the first note of the upper voice (accessed with
the handle argument). Two notes of the lower voice sound simultaneous
with MyNote, and these are returned by getSimultaneousItems (check
their textual representation to confirm they are correct). The
procedure Wait delays the inspecting until the notes are found. Try
what happens if you use another function/method as test for
getSimultaneousItems.  */

local
   MyNote
   _ /*IgnoreScore*/ = {Score.makeScore
                        sim(items:[seq(items:[note(handle:MyNote
                                                   duration:2 pitch:72)
                                              note(duration:1 pitch:71)
                                              note(duration:3 pitch:72)])
                                   seq(items:[note(duration:1 pitch:60)
                                              note(duration:2 pitch:62)
                                              note(duration:3 pitch:64)])
                                   ]
                            startTime:0)
                        unit}
   Result = {MyNote getSimultaneousItems($ test:isNote)}
in
   {Wait Result}
   {Inspect Result}
end

User-defined accessors

No information available.

/* Strasheela provides access to various information, but chances are
that some accessor for your particular requirements is not available
yet. However, Strasheela makes it easy for you to define your own. As
a demonstration, this section defines an accessor similar to
getSimultaneousItems used in the previous section.

This section accesses the relevant information using generic accessors
such as the method filter. Moreover, it encapsulates our new accessors
in functions, so the accessors are modular and can be easily reused
later.

More specifically, two functions are defined. The function
IsSimultaneousItem expects two score items and checks whether their
temporal position overlaps. The function GetSimultaneousNotes expects
a single score item, retrieves its top-level temporal container and
then filters out all score objects directly or indirectly contained
within this container which meet a specific condition. The condition
is a conjunction of three tests: (i) a selected object Y must not be
the argument X given to GetSimultaneousNotes, (ii) Y must be a note,
and (iii) the two objects X and Y must be simultaneous -- which is
tested with the function IsSimultaneousItem defined before.

Finally, the function IsSimultaneousItem is used in an section
virtually idential to the previous section. */

local
   fun {IsSimultaneousItem X Y}
         StartX = {X getStartTime($)}
         StartY = {Y getStartTime($)}
         EndX = {X getEndTime($)}
         EndY = {Y getEndTime($)}
      in
         (StartX < EndY) andthen (StartY < EndX)
      end
   fun {GetSimultaneousNotes X}
      TopLevel = {X getTopLevels($ test:Score.isTemporalContainer)}.1
   in
      {TopLevel filter($ fun {$ Y}
                            %% thread because IsSimultaneousItem would block
                            %% when temporal params of X or Y are undetermined
                            thread
                               Y \= X andthen
                               {Y isNote($)} andthen
                               {IsSimultaneousItem X Y}
                            end
                         end)}
   end
   MyNote
   _ /*IgnoreScore*/ = {Score.makeScore
                        sim(items:[seq(items:[note(handle:MyNote
                                                   duration:2 pitch:72)
                                              note(duration:1 pitch:71)
                                              note(duration:3 pitch:72)])
                                   seq(items:[note(duration:1 pitch:60)
                                              note(duration:2 pitch:62)
                                              note(duration:3 pitch:64)])
                                   ]
                            startTime:0)
                        unit}   Result = {GetSimultaneousNotes MyNote}
in
   {Wait Result}
   {Inspect Result}
end

Customising Output to Export Formats

intro

Strasheela's music representation has been designed to make the definition of new output formats and the extension of existing formats relatively simple without necessarily touching the original Strasheela code. This section demonstrates the definition of Csound and Liliput output for a different temperament. The temperament is expressed in the score by a new pitch unit et31, denoting 31-tone equal-temperament. For more information on this interesting temperament visit http://www.tonalsoft.com/enc/number/31edo.aspx or http://en.wikipedia.org/wiki/31_equal_temperament.

Please note that the output for these sections again requires the correct settings for Csound, a sound file editor, lilypond, convert-ly (an application part of Lilypond), and a PDF file viewer (via 'Settings...' menu entry).

Output Csound Score

No information available.

/* This section defines Csound score file format export from
scratch. The 31-tone equal-temperament pitch value is transformed into
a MIDI pitch float in the Csound score. This definition is simplified
by procedures predefined in Strasheela such as Out.scoreToEvents and
Out.writeToFile (see the Strasheela reference for more details on
these procedures). A test demonstrates this definition. */

local
   %% Returns true if X is a note whose pitch unit is et31
   fun {IsEt31Note X}
      {X isNote($)} andthen
      {X getPitchUnit($)} == et31
   end

   %% Transforms MyScore into a list of VS in Csound note syntax
   fun {ScoreToCsoundNotes MyScore}
      %% Out.scoreToEvents expects a list of clauses
      %% Test#Transformation: when the function Test matches, then the
      %% function Transformation is called with the matching score
      %% object.
      {Out.scoreToEvents MyScore
       [%% transform note with pitch unit et31 into Csound note of the
        %% format [i1 StartTime Duration Amplitude Pitch], where
        %% Amplitude is in the interval [0,1] and Pitch is a MIDI
        %% number float. This format corresponds with the default
        %% Csound orc provided by Strasheela.
        IsEt31Note#fun {$ MyNote}
                      [{Out.listToVS
                        [i1
                         {MyNote getStartTimeInSeconds($)}
                         {MyNote getDurationInSeconds($)}
                         %% transform MIDI velocity into interval [0,1]
                         {IntToFloat {MyNote getAmplitude($)}} / 128.0
                         %% transform et31 to MIDI float
                         {IntToFloat {MyNote getPitch($)}} * 12.0 / 31.0]
                        " "}]
                   end
        %% raise error for every other event in MyScore
        isScoreObject#fun {$ X} raise unsupported(X) end end]
       %% only output fully determined events with a duration > 0, ignore
       %% everything else
       unit(test:fun {$ X}
                    {X isEvent($)} andthen {X isDet($)} andthen
                    ({X getDuration($)} > 0)
                 end)}
   end

   %% Output MyScore as Csound score file at Path
   proc {OutputCsoundEt31 MyScore Path}
      Extension = ".sco" in
      {Out.writeToFile {Out.listToVS {ScoreToCsoundNotes MyScore}
                        "\n"}
       Path#Extension}
   end

   %% A testscore: a simple cadence C-min, F-min, G-maj, C-min
   %% pitch notation: 31 * Octave  + ET 31 pitch class
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration: 4
                                         pitch:31*5 + 18
                                         pitchUnit:et31
                                         amplitude:64) % MIDI velo
                                    note(duration: 4
                                         pitch:31*5 + 21
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 18
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 18
                                         pitchUnit:et31
                                         amplitude:64)])
                         seq(items:[note(duration: 4
                                         pitch:31*5 + 8
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 13
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 5
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 8
                                         pitchUnit:et31
                                         amplitude:64)])
                         seq(items:[note(duration: 4
                                         pitch:31*5 + 0
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 0
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*4 + 28
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 0
                                         pitchUnit:et31
                                         amplitude:64)])]
                  startTime:0
                  timeUnit:beats(4))
              unit}
   Path = {Tk.return tk_getSaveFile}
in
   {MyScore wait}
   %% output Csound score file to Path
   {OutputCsoundEt31 MyScore Path}
   {Out.callCsound unit(file:Path
                        %% use Path as is
                        scoDir:nil
                        soundDir:nil)}
   {Out.playSound unit(file:Path
                       %% use Path as is
                       scoDir:nil
                       soundDir:nil)}
end

Output Lilypond

No information available.

/* This section extends Strasheela's lilypond export to support
31-tone equal temperament. The pitch notation uses a mapping from et31
pitch classes to notated pitches as shown at
http://www.tonalsoft.com/enc/number/31edo.aspx. Please note that the
notation of this temperament correctly supports enharmonic notation
(in contrast to Strasheela's default Lilypond output which only
supports sharps). Moreover, interval transpositions in this
temperament result in the correct enharmonic spelling up to two sharps
and flats. Again, a test demonstrates this definition (only flats are
used in the test for simplicity). */

local
   fun {IsEt31Note X}
      {X isNote($)} andthen
      {X getPitchUnit($)} == et31
   end

   %% This 31-tone equal temperament pitch class mapping follows
   %% http://www.tonalsoft.com/enc/number/31edo.aspx
   LilyEt31PCs = pcs(c deses cis des cisis
                     d eses 'dis' es disis
                     e fes eis
                     f geses fis ges fisis
                     g aeses gis aes gisis
                     a beses ais bes aisis
                     b ces bis)
   LilyOctaves = octs(",,,," ",,," ",," "," "" "'" "''" "'''" "''''")
   %% Transform a Pitch (an int) into the corresponding Lily code (a VS)
   fun {ET31PitchToLily MyPitch}
      PC = {Int.'mod' MyPitch 31} + 1
      Oct = {Int.'div' MyPitch 31} + 1
   in
      LilyEt31PCs.PC # LilyOctaves.Oct
   end

   %% Expects a Strasheela note object and returns the corresponding
   %% Lilypond code (a VS). For simplicity, this transformation does not
   %% support any expessions (e.g. fingering marks, or articulation
   %% marks).
   fun {NoteEt31ToLily MyNote}
      Rhythms = {Out.lilyMakeRhythms {MyNote getDurationParameter($)}}
   in
      %% if MyNote is shorter than 64th then skip it (Out.lilyMakeRhythms
      %% then returns nil)
      if Rhythms == nil
      then ''
      else
         Pitch = {ET31PitchToLily {MyNote getPitch($)}}
         FirstNote = Pitch#Rhythms.1
      in
         %% handle tied notes
         if {Length Rhythms} == 1
            %% no tied notes
         then FirstNote
            %% all values in Rhythm.2 are tied to predecessor
         else FirstNote#{Out.listToVS {Map Rhythms.2
                                       fun {$ R} " ~ "#Pitch#R end}
                         " "}
         end
      end
   end

   %% A testscore: a simple cadence C-min, F-min, G-maj, C-min
   %% pitch notation: 31 * Octave  + ET 31 pitch class
   MyScore = {Score.makeScore
              sim(items:[seq(items:[note(duration: 4
                                         pitch:31*5 + 18
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 21
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 18
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 18
                                         pitchUnit:et31
                                         amplitude:64)])
                         seq(items:[note(duration: 4
                                         pitch:31*5 + 8
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 13
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 5
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 8
                                         pitchUnit:et31
                                         amplitude:64)])
                         seq(items:[note(duration: 4
                                         pitch:31*5 + 0
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 0
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*4 + 28
                                         pitchUnit:et31
                                         amplitude:64)
                                    note(duration: 4
                                         pitch:31*5 + 0
                                         pitchUnit:et31
                                         amplitude:64)])]
                  startTime:0
                  timeUnit:beats(4)
                 )
              unit}
in
   {MyScore wait}

   %% Strasheela's standard Lilypond output procedure
   %% Out.renderAndShowLilypond provides an argument clauses which
   %% expects a list of clauses Test#Transformation. When the function
   %% Test matches an object, then the function Transformation is called
   %% with the matching score object to generate Lilypond
   %% code. Otherwise, the score object is processed by the default
   %% clauses of Out.renderAndShowLilypond.
   {Out.renderAndShowLilypond MyScore
    unit(file:"test"
         clauses:[IsEt31Note#NoteEt31ToLily])}
end

to be sorted

Implicit pattern matching in Strasheela

The following function GetPitch expects a record as argument which must match the record note(pitch:Pitch ...). The variable Pitch is implicitly declared and bound to the value at the feature 'pitch' of the record given as argument to the function.

Please note that the record in the header of the function GetPitch is not even complete but contains three dots (...) to indicate that further record features are possible.

local
   fun {GetPitch note(pitch:Pitch ...)}
      Pitch
   end
in
   {Inspect {GetPitch note(duration:4 pitch:60)}}
end