Siren 3.0 Outline

Sections


README

    Welcome to Siren 3.0

This window is an outline view--a simple multi-section document reader with code execution
capabilities. The list at the top of the view is the "sections" of the outline, and each section
has its own text (seen in this view).

The Siren outline (this text) contains the condensed on-line documentation and example code for
Siren. Users are strongly advised to read these summaries, step through the examples given here,
and consult the relevent source code and comments in a code browser for a tour/demo of what Siren
can do.

Each section starts with a short introductory text, which is followed by a series of executable
examples (enclosed in square brackets for easy selection). Thus you can give yourself a self-paced
tutorial and demo by working through the pages of the outline and "doing," "printing," or
"inspecting" the various examples (using the Smalltalk pop-up menus or command keys). Code examples
that are not enclosed in brackets are intended for reading rather than for execution, they may work
as well, or they may be "pseudo-code" for tutorial purposes only.

Warning

Siren is based on several earlier generations of Smalltalk-based music toolkits. There is still
much code in here that is "stale" meaning that it does not yet work in Squeak. Rather than strip it
all out, I'm leaving it here in the hope that some daring parties will help to port it. The
examples presented here are both the unit test suite and the release notes as to what has been
ported and tested to date.

Getting started

There's a simple demo script below in this outline that walks you through the basic facilities of
Siren. New users are advised to step through its expressions before proceeding with the detailed documentation.

For More Information

If you're new to Smalltalk, please locate and study one of the several excellent on-line or printed
tutorials. 

If you're new to Squeak, please look over the Squeak outline view.

If you're new to Siren, read on.

Return To Top


Introduction to Siren

What is Siren?

The Siren system is a general-purpose music and sound composition, processing, performance, and
analysis system; it is a re-implementation of the Musical Object Development Environment (MODE),
the software component of the Interim DynaPiano project. Siren is a collection of about 200
Smalltalk classes for building musical applications; the current version works on Squeak running on
Mac, Windows, and UNIX systems with MIDI drivers and CD-quality stereo audio I/O. The Siren release
is available via anonymous Internet ftp file transfer from the URL ftp://ftp.create.ucsb.edu/pub/Siren.

There are several elements to Siren:
    the Smoke music representation language
        (music magnitudes, events, event lists, generators, functions, and sounds);
    voices, schedulers and I/O drivers
        (real-time and file-based voices, sound, score, and MIDI I/O);
    user interface components for musical applications
        (UI framework, tools, and widgets); and
    several built-in applications 
        (editors and browsers for Smoke objects).

Each of these components is described below in its own section of the outline.

If you can read Smalltalk and want a quick tour before proceeding, read the condensed Siren Demo
that's at the end of this outline.

Where's More Documentation?

Various versions and components of Siren's predecessors (The HyperScore ToolKit and the MODE) are
documented in the book "The Well-Tempered Object: Musical Applications of Object-Oriented Software
Technology" (S. T. Pope, ed. MIT Press, 1991), in papers in the Proceedings of the 1986, 1987,
1989, 1991, 1992, 1994, 1996, and 1997 International Computer Music Conferences (ICMCs), in an
article on the "Interim DynaPiano" in "Computer Music Journal" 16:3, Fall, 1992 (heartily
recommended--it's also on the CMJ Web site), in the book "Musical Signal Processing (C. Roads, S.
T. Pope, G. DePoli, and A. Piccialli, eds. Swets & Zeitlinger, 1997), and in several documents
accessible via the Web page http://www.create.ucsb.edu/~stp/publ.html. There are more MODE- and
Smoke-related documents (including several of the above references) in the directory ftp://ftp.create.ucsb.edu/pub/Smalltalk/Music/Doc.

The official Siren home page is http://www.create.ucsb.edu/Siren.

An email discussion list called Squeakaudio is available; see the web page
http://www.create.ucsb.edu/mailman/listinfo/squeakaudio to sign up or to read the archives.

History

Siren and its predecessors stem from music systems that I've developed in the process of composing
and realizing my music. Of the early ancestors, ARA was an outgrowth of the Lisp system used for
"Bat out of Hell" (1983); DoubleTalk was based on the Smalltalk-80-based Petri net editing system
used for "Requiem Aeternam dona Eis" (1986); the HyperScore ToolKit's various versions were used
(among others) for "Day" (1988), and the MODE was developed to realize "Kombination XI" (1990) and
"Paragraph 31: All Gates are Open" (1993). In each of these cases, some amount of effort was
spent--after the completion of the composition--to make the tools more general-purpose. Siren--a
re-implementation of the MODE undertaken in 1997-8--is based on the representations and tools I'm
using in the realization of "Ywe Ye, Yi Jr Di" (in progress).

Current Status

This is the V3.0 release of Siren on Squeak. (For simplicity, Siren version numbers parallel the
versions of Squeak on which they run.)

Smoke: The Smoke representation is complete, so that the event list and event generator examples
run well. Microtonal pitches have been added.

Voices and Schedulers: The MIDI voices are designed to work with the portable Squeak MIDI
primitives. The scheduler has been tested and delivers acceptable real-time performance on a
high-end Macintosh system (G3 PowerBook). For slower systems, we may have to investigate adding a
level of queing in the output driver.

Sound I/O: Siren voices have been made to interface with John Maloney's sound synthesis classes.
Sound file I/O is implemented for AIFF and NeXT/Sun .snd files.

Graphics and GUI: The display list framework, Navigator MVC framework, and several of the MVC-based
GUI tools work in Squeak MVC projects but NOT in Morphic projects. See below and the on-line Siren
screen shot page.

The examples that follow in this outline are intended as a demonstration of the current working
state of Siren. Step through the sections, reading the documentation and executing the code
examples as appropriate.

Please note the category naming: MusicWIP means "work in progress," and "MusicOLD" means "old,
obsolete, or unmaintained." 

Squeak Siren is not supported, and most new features and extensions are to be found in the
VisualWorks version of Siren.

Return To Top


Siren Set-up

Setting up Siren

First, read and edit the path names to data storage
    Smalltalk browseAllImplementorsOf: #initializeSiren.

MIDI/Sound Configuration

Siren's MIDI support depends on a platform-independent interface class that talks to VM-side
primitives that talk to OS-level device drivers. The basic Squeak built-in MIDI can be used, but is
limited to toy examples. There are special virtual machine extensions and drivers for the Macintosh
and M$-Windows platforms.

To see which set of MIDI drivers is being used, look at
    Smalltalk browseAllImplementorsOf: #concreteClass.

If you have a Mac, you should grab the free Opcode MIDI System (OMS) and the SirenMIDI plug-in that
uses it. This includes a system extension, a control panel, and a configuration application.
There's also a stand-alone test application to demonstrate whether it's properly set up and
functioning. This app is called "TestOMSApp"; please use it to make sure that OMS MIDI is up and
running before trying out the Siren MIDI I/O examples. Note that you do not need any MIDI hardware
on a Mac; OMS is happy to play using the (software-only) QuickTime musical instruments.

For Windows, use Helge Horch's special virtual machine (included on the CD-ROM and Web site).

I'm not certain as to the ports of the Siren MIDI primitives to other platforms (any volunteers?).

Smalltalk Options

There are several configurable parts to Siren.

Class Siren is the general place to find utility messages related to Siren set-up and global
variables. Look at its class variables and initialization method.

Several of the voice classes have "default" methods that return a default instance. Look at
SynthVoice default, MIDIVoice default, and Voice default.

Siren looks in default directory for scores and sound files. By default this is called "TestData"
and is a sub-folder of the folder where the virtual image is executing. There are methods in class
Siren to change this. 

Siren House-keeping

To clear out temp. event lists, use,
    [EventList flushTemps]
or to flush all,
    [EventList initialize]

To flush and close down the scheduler,
    [Schedule interrupt; flush; release]

To send MIDI all notes off, flush ports, throw away open ports, clear out temp event lists, etc.
    [MIDIPort cleanUp]

To clean out garbage,
    [Smalltalk garbageCollect]

Check here to see if there's any cruft,
    [DependentsFields inspect]

Return To Top


The Siren Design

Siren Design Notes

There are several elements to Siren:
    the Smoke music representation language
        (music magnitudes, events, event lists, generators, functions, and sounds);
    voices, schedulers and I/O drivers
        (real-time and file-based voices, sound and MIDI I/O);
    user interface components for musical applications
        (extended graphics framework, layout managers, UI tools and widgets); and
    several built-in applications
        (editors and browsers for Siren objects).

These subsystems use a number of design patterns, implementing visitors, interceptors,
double-dispatching, MVC, and others.

The primary class hierarchies of Siren are given below grouped into categories. The text
indentation signifies sub/super-class  relationships, and instances variable names are shown.

Music Magnitude Models

    Magnitude
        MusicMagnitude -- value
            MusicModel
                Chroma
                    ModeMember
                    Pitch
                Chronos
                    Duration
                    Meter
                Ergon
                    Amplitude
                Positus
                    Directionality
                    Position
                    Spatialization

Music Magnitude Implementations

    Magnitude
        MusicMagnitude -- value
            ConditionalDuration
            NominalMagnitude
                SymbolicLoudness
                SymbolicPitch
            NumericalMagnitude
                HertzPitch
                IntervalMagnitude
                    MIDIPitch
                    MIDIVelocity
                MSecondDuration
                    SecondDuration
                RatioMagnitude -- relative
                    RatioDuration
                    RatioLoudness
                        DBLoudness
                    RatioPitch
            OrdinalMagnitude -- table
                Length
                Sharpness
            PField -- name  field

Events

    AbstractEvent -- properties
        DurationEvent -- duration
            ActionEvent -- action
            MusicEvent -- pitch  loudness  voice
                EventList -- events  index  startedAt
                MixElement

EventLists

    AbstractEvent -- properties
        DurationEvent -- duration
            MusicEvent -- pitch  loudness  voice
                EventList -- events  index  startedAt
                    EventGenerator
                        Cloud -- density
                            DynamicCloud
                            SelectionCloud
                                DynamicSelectionCloud
                        Cluster
                            Chord -- root  inversion
                                Arpeggio -- delay
                            Roll -- number  delta  noteDuration
                                Trill
                        Ostinato -- list  playing  process
                            MIDIEcho
                    Mix -- output  clipped  rate  channels

Functions

    AbstractEvent -- properties
        DurationEvent -- duration

            Function -- data  range  domain
                FourierSummation -- myForm  myArray
                LinearFunction
                    ExponentialFunction
                    SplineFunction -- linSeg
                Sound
                    GranularSound -- grains
                    StoredSound -- samplesInMemory  firstIndex  changed
                        FloatSound
                        Mu8Sound
                        VirtualSound -- source
                            CompositeSound -- components
                            GapSound -- cutList
                        WordSound

SoundFiles

    AbstractEvent -- properties
        SoundFile -- sound  fileName  file  headerSize  format  rate  channels  size
            AIFFSoundFile
            NeXTSoundFile
                EBICSFSoundFile

Voices

    Model -- dependents
        Voice -- name  instrument  stream
            MIDIFileVoice -- fileType  tracks  ppq  tempo
            MIDIVoice -- currentTime
            NotelistVoice -- parameterMap
                CmixVoice
                CmusicVoice
                CsoundVoice
            OldMIDIFileVoice -- tempo  time  command  byte2  byte3  eventList
            SoundVoice
            SynthVoice -- currentTime

MIDI

    Model -- dependents
        MIDIDevice -- port  debug
            MIDIFB01
            MIDIPF70

    MIDIPacket -- length  time  flags  duration  data
        MIDIAfterTouch
        MIDIControl
        MIDINoteOff
        MIDINoteOn
        MIDIPitchBend
        MIDIProgramChange

    Stream
        PositionableStream -- collection  position  readLimit
            MIDIPort -- readProcess  rData  hasDurs  hasBuffer  interfaces  timeOffset  readSemaphore 
readSemaphoreIndex  error  channelMap  defaultInterface

DisplayItems/Lists

    DisplayObject
        DisplayModel -- container  model
            DisplayItem -- origin  corner  color
                DisplayLine -- width
                    DisplayRectangle -- fill  border
                        DisplayArc -- angle
                            DisplayEllipse
                                DisplayCircle
                        DisplayPolyline -- vertices
                            DisplaySpline
                DisplayString -- dText
                DisplayVisual -- visual
            DisplayList -- components  origin  bounds

DisplayList Editors

    Model -- dependents
        DisplayListEditor -- selection  displayList  copyBuffer  view  controller
            EventListEditor -- eventList  ostinato  defaultEvent
                MixEditor -- selections
            TreeEditor -- root  leaves  layoutManager
                TRTreeEditor -- sequenceView

DisplayList Views

    View (...)
        DisplayListView -- displayList  form  background  scrollOffset  scaleX  scaleY  backgroundColor  foregroundColor
            FunctionView -- models  vRange  hRange  colors  width
            TimeSequenceView -- clefForm  xColor  yColor  headColor
                GroupingView
                PhraseView
                PitchTimeView -- pitchOffset
                    CMNView -- steps  sharps  staves  ledgers  xTable
                    HauerSteffensView
                    MixButtonPanel -- buttonOffsets  buttonFields  mainView
                    MixSliderPanel -- colorArray  mainView  maxVal  grid
                    MixView -- colorArray  textView
                    PositionTimeView

DisplayList Controllers

    Controller (...)
        MouseMenuController (...)
            DisplayListController -- selection  actions  keys
                FunctionController -- modelIndex
                TimeSequenceController
                    PitchTimeController
                        MixController
                            MixButtonController
                            MixSliderController
                TreeController

LayoutManagers

    LayoutManager -- view  orientation  itemAccessor
        HierarchyLayoutManager -- length  xStep  yStep  treeAccessor
            BinaryTreeLayoutManager
                LeafLinearTreeLayoutManager
                    TRTreeLayoutManager -- sequenceView
                TreeLayoutManager
            IndentedListLayoutManager
                IndentedTreeLayoutManager -- list
        TimeSequenceLayoutManager -- timeScale  timeOffset
            GroupingLayoutManager -- levelScale
            PitchTimeLayoutManager -- pitchScale  pitchOffset
                CMNLayoutManager -- stepArray  staffTop
                MixLayoutManager
                PositionTimeLayoutManager

Return To Top


The Smoke Music Representation

The Smoke Music Representation

The "kernel" of Siren is in the classes related to representing the basic musical magnitudes
(pitch, loudness, duration, etc.), and for creating and manipulating event and event list objects.
This package is known as the Smallmusic Object Kernel (Smoke--name suggested by Danny Oppenheim).

Smoke is an implementation-language-independent music representation, description language, and
interchange format that was developed by a group of researchers at CCRMA/Stanford and
CNMAT/Berkeley during 1990 and 1991. The basic design requirements are that the representation
support the following:
    --abstract models of the basic musical quantities (scalar magnitudes such as pitch, loudness, and
duration); 
    --flexible grain-size of "events" in terms of "notes," "grains," "elements," or "textures";
    --event, control, and sampled sound processing description levels;

    --nested/hierarchical event-tree structures for flexible description of "parts," "tracks," or
other parallel or sequential organizations;
    --annotation and marking of event tree structures supporting the creation of heterarchies
(lattices) and hypermedia networks;
    --annotation including common-practise notation possible;

    --instrument/note (voice/event, performer/music) abstractions;
    --separation of "data" from "interpretation" ("what" vs. "how" in terms of providing for
interpretation objects--voices);

    --abstractions for the description of "middle-level" musical structures (e.g., chords, clusters,
or trills);
    --sound functions, granular description, or other (non-note-oriented) description abstractions;
    --description of sampled sound synthesis and processing models such as sound file mixing or DSP;

    --possibility of building convertors for many common formats, such as MIDI data, Adagio, note
lists, DSP code, instrument definitions, or mixing scripts; and
    --possibility of parsing live performance into some rendition in the representation, and of
interpreting it (in some rendition) in real-time.

The "executive summary" of Smoke from (ICMC 1992) is as follows. Music (i.e., a musical surface or
structure), can be represented as a series of "events" (which generally last from tens of msec to
tens of sec). Events are simply property lists or dictionaries that are defined for some duration;
they can have named properties whose values are arbitrary. These properties may be music-specific
objects (such as pitches or spatial positions), and models of many common musical magnitudes are
provided. 

Events are grouped into "event lists" (AKA composite events or event collections) by their relative
start times. Event lists are events themselves and can therefore be nested into trees (i.e., an
event list can have another event list as one of its events); they can also map their properties
onto their component events. This means that an event can be "shared" by being in more than one
event list at different relative start times and with different properties mapped onto it.

Events and event lists are "performed" by the action of a scheduler passing them to an
interpretation object or voice. Voice objects and applications determine the interpretation of
events' properties, and may assume the existence of "standard" property names such as pitch,
loudness, voice, duration, or position. Voices map application-independent event properties onto
the specific parameters of I/O devices or formatted files. A scheduler expands and/or maps event
lists and sends their events to their voices in real time.

Sampled sounds can also be described as objects, by means of synthesis "patches," or signal
processing scripts involving a vocabulary of sound manipulation messages.

Examples

Move to the following sections for extensive examples of Smoke object creation and manipulation.

Return To Top


Music Magnitude Models

Siren Music Magnitude Models

MusicMagnitude objects are characterized by their identity, class, species, and value (e.g., the
pitch object that represents 'c3' has its object identity, the class SymbolicPitch, the species
Pitch, and the value 'c3' [a string]). MusicMagnitude behaviors distinguish between class
membership and species in a multiple-inheritance-like scheme that allows the object representing
"440.0 Hz" to have pitch-like and limited-precision-real-number-like behaviors. This means that its
behavior can depend on what it represents (a pitch), or how its value is stored (a floating-point
number). 

The mixed-mode music magnitude arithmetic is defined using the technique of species-based coercion,
i.e., class Pitch knows whether a note name or Hertz value is more general. This provides
capabilities similar to those of systems that use the techniques of multiple inheritance and
multiple polymorphism (such as C++ and the Common Lisp Object System), but in a much simpler and
scalable manner. All meaningful coercion messages (e.g., (440.0 Hz) asMIDIKeyNumber)), and
mixed-mode operations (e.g., (1/4 beat + 80 msec)) are defined.

The basic model classes include Pitch, Loudness, and Duration; exemplary extensions include Length,
Sharpness, Weight, and Breath for composition- or notation-specific magnitudes. The handling of
time as a parameter is finessed via the abstraction of duration. All times are durations of events
or delays, so that no "real" or "absolute" time object is needed. Duration objects can have simple
numerical or symbolic values, or they can be conditions (e.g., the duration until some event X
occurs), Boolean expressions of other durations, or arbitrary blocks of Smalltalk-80 code. 

Functions of one or more variables are yet another type of signal-like music magnitude. The MODE
Function class hierarchy includes line segment, exponential segment, spline segment and Fourier
summation functions. 

In the verbose SmOKe format music magnitudes, events and event lists are created by instance
creation messages sent to the appropriate classes. The first three expressions in the examples
below create various music magnitudes and coerce them into other representations. 

The terse form for music magnitude creation uses post-operators (unary messages) such as 440 hz or
250 msec, as shown in the subsequent examples.

Users can extend the music magnitude framework with their own classes that refine the existing
models of define totally new kinds of musical metrics.

Basic Models

Pitches
    HertzPitch -- 440.0 hz
    MIDIPitch -- 60 pitch -- can be non-integer for microtonal tunings (use #asFracMIDI)
    SymbolicPitch -- 'c#3' pitch -- can have a remainder for microtonal tunings
    RatioPitch -- 11/9 of: anotherPitch -- used for fraction-oriented tunings
    
Durations
    SecondDuration -- 1 sec
    MSecondDuration -- 100 msec
    RatioDuration -- 1/4 beat
    ConditionalDuration -- until: [ :t | block]

Amplitude/Loudness Objects
    DBLoudness -- -3 dB -- can be relative to 0 dB or positive-valued
    RatioLoudness -- 0.7071 ampl
    SymbolicLoudness -- 'fff' ampl
    MIDIVelocity -- 96 vel

Other Music Magnitudes
    OrdinalMagnitudes -- have order but no explicit value
    PField -- name/slot/value -- used for note lists

Examples

Verbose MusicMagnitude Creation and Coercion Messages

    (Duration value: 1/16) asMsec            "Answers Duration 62 msec." 
    (Pitch value: 60) asHertz                 "Answers Pitch 261.623 Hz." 
    (Amplitude value: 'ff') asMIDI            "Answers MIDI key velocity 100." 

Terse MusicMagnitude Creation using post-operators

    440 Hz                    "a HertzPitch"
    'c#3' pitch                "a SymbolicPitch"
    60 pitch                    "a MIDIPtch"
    250 msec                "a MSecondDuration"
    1/4 beat                    "a RatioDuration"

MusicMagnitude Coercion Examples

    440 Hz asSymbol            "--> 'a3' pitch"
    (1/4 beat) asMsec        "--> 250 msec"
    #mf ampl asMIDI        "--> 70 vel"

Duration Coercion Example--create a 1/8 beat duration and coerce it into msec, printing the result
to the Smalltalk transcript. (To execute this, double-click just inside the open-bracket to select
the entire expression and use the pop-up menu of command key to "do it.")

    [ | me |
    me := Duration value: 1/8.
    Transcript cr; show: me printString, ' = ',
        me asSec printString; cr]

Pitch Coercion Example--create a named pitch (middle C) and print it to the transcript as Hz and as
a MIDI key number.

    [ | me |
    me := Pitch value: 'c3'.
    Transcript show: me printString, ' = ',
        me asHertz printString, ' = ',
        me asMIDI printString; cr.
    "me inspect"]

Amplitude Coercion Example--create a named dynamic value and print it as an amplitude ratio and a
MIDI velocity.

    [ | me |
    me := Amplitude value: #mf.
    Transcript show: me printString, ' = ',
        me asRatio printString, ' = ',
        me asMIDI printString; cr.
    "me inspect"]

Mixed-mode Arithmetic--demonstrate adding beats and msec, or note names and Hertz values. Select
and print these.

    [(1/2 beat) + 100 msec]                " (0.6 beat")
    ['a4' pitch + 25 Hz]                    " (465.0 Hz)"
    [('a4' pitch + 100 Hz) asMIDI]        " (73 key)"
    ['mp' ampl + 3 dB]                    " (-4.6 dB)"

Alberto de Campo's microtonal extensions allow MIDI pitches to be floating-point numbers (e.g.,
MIDI key 60.25) and named pitches to have "remainder" values (e.g., c3 + 25 cents) as in the
following examples.

    [438 Hz asSymbol]          "rounds to nearest chromatic note, a3."
    [443.5 Hz asMIDI]        "ditto."
    [265 Hz asFracMIDI]        "converts to float chromatics; can be rounded, used
                             for MIDI pitch bend or for precise synthesis in Hz."
    [61.26 key asHertz]        "float chromatics can also be used directly; for
                             microtonal scales this is clearer than Hz (to me at least)."
    [260.0 Hz asFracSymbol]     "is rounded, but keeps track of offsets in
                              an inst var (fracPitch); survives conversions etc."

Note that asMIDI and asSymbol can now be used to round pitches to chromatics, while the messages
asFracMIDI and asFracSymbol keep the full microtonal precision.

Return To Top


Siren/Smoke Events

Smoke Events

The Event object in Smoke is modeled as a property-list dictionary with a duration. Events have no
notion of external time until their durations become active. Event behaviors include duration and
property accessing, and "performance," where the semantics of the operation depends on another
object--a voice or driver as described below. 

The primary messages that events understand are: (anEvent duration: someDurationObject)--to set the
duration time of the event (to some Duration-type music magnitude)--and property accessing messages
such as (anEvent color: #blue)--to set the "color" (an arbitrary property) to an arbitrary value
(the symbol #blue). 

The meaning of an event's properties is interpreted by voices and user interface objects; it is
obvious that (e.g.,) a pitch could be mapped differently by a MIDI output voice and a graphical
notation editor. It is common to have events with complex objects as properties (e.g., envelope
functions, real-time controller maps, DSP scripts, structural annotation, version history, or
compositional algorithms), or with more than one copy of some properties (e.g., one event with
enharmonic pitch name, key number, and frequency, each of which may be interpreted differently by
various voices or structure accessors). 

That there is no prescribed "level" or "grain size" for events in Smoke. There may be a one-to-one
or many-to-one relationship between events and "notes," or single event objects may be used to
represent long complex textures or surfaces.

Note the way that Smoke uses the Smalltalk concatenation message "," to denote the construction of
events and event lists;  (magnitude, magnitude) means to build an event with the two magnitudes as
properties, and (event, event) or ((duration -> event) , (duration -> event)) means to build an
event list with the given events as components.

There are classes for events are as follows.
    AbstractEvent -- just a property list
    DurationEvent -- adds duration
    MusicEvent -- adds pitch and voice
    ActionEvent -- has a block that it evaluates when scheduled

It is seldom necessary to extend the hierarchy of events.

Examples

Verbose Event Creation Messages -- Class messages
    
"Create a `generic' event."
    MusicEvent duration: 1/4 pitch: 'c3' ampl: 'mf'

"Create one with added properties."
    (MusicEvent dur: 1/4 pitch: 'c3') color: #green; accent: #sfz

Terse Event Creation using concatenation of music magnitudes--inspect these.

    [440 Hz, (1/4 beat), 44 dB]

    [490 Hz, (1/7 beat), 56 dB, (#voice -> #flute), (#embrochure -> #tight)]

    [(#c4 pitch, 0.21 sec, 64 velocity) voice: IOVoice default]

Return To Top


Event Lists and Event Hierarchy

Smoke Event Lists

EventList objects hold onto collections of events that are tagged and sorted by their start times
(represented as the duration between the start time of the event list and that of the event). The
event list classes are subclasses of Event themselves. This means that event lists can behave like
events and can therefore be arbitrarily deeply nested, i.e., one event list can contain another as
one of its events. 

The primary messages to which event lists respond (in addition to the behavior they inherit by
being events), are (anEventList add: anEvent at: aDuration)--to add an event to the
list--(anEventList play)--to play the event list on its voice (or a default one)--(anEventList
edit)--to open a graphical editor in the event list--and Smalltalk-80 collection iteration and
enumeration messages such as (anEventList select: [someBlock])--to select the events that satisfy
the given (Boolean) function block. 

Event lists can map their own properties onto their events in several ways. Properties can be
defined as lazy or eager, to signify whether they map themselves when created (eagerly) or when the
event list is performed (lazily). This makes it easy to create several event lists that have copies
of the same events and map their own properties onto the events at performance time under
interactive control. Voices handle mapping of event list properties via event modifiers, as
described below.

In a typical hierarchical Smoke score, data structure composition is used to manage the large
number of events, event generators and event modifiers necessary to describe a full performance.
The score is a tree--possibly a forest (i.e., with multiple roots) or a lattice (i.e., with
cross-branch links between the inner nodes)--of hierarchical event lists representing sections,
parts, tracks, phrases, chords, or whatever abstractions the user desires to define. Smoke does not
define any fixed event list subclasses for these types; they are all various compositions of
parallel or sequential event lists.

Note that events do not know their start times; this is always relative to some outer scope. This
means that events can be shared among many event lists, the extreme case being an entire
composition where one event is shared and mapped by many different event lists (as described in
[Scaletti 1989]). The fact that the Smoke text-based event and event list description format
consists of executable Smalltalk-80 message expressions (see examples below), means that it can be
seen as either a declarative or a procedural description language. The goal is to provide
"something of a cross between a music notation and a programming language" (Dannenberg 1989). 

Examples

The verbose way of creating an event list is to create a named instance and add events explicitly
as shown in the first example below, which creates a D-major chord.

    [(EventList newNamed: #Chord1)
        add: (1/2  beat, 'd3'  pitch,  'mf' ampl)  at: 0;
        add: (1/2  beat, 'fs3'  pitch, 'mf' ampl)  at: 0;
        add: (1/2  beat, 'a4'   pitch, 'mf' ampl)  at: 0]

This same chord could be defined more tersely as,

    [(0 => (1/2 beat, 'd3' pitch, 'mf' ampl)), 
      (0 => (1/2 beat, 'fs3' pitch, 'mf' ampl)), 
      (0 => (1/2 beat, 'a4' pitch, 'mf' ampl))]

This could be done even more compactly using a Chord object (see the discussion of event generators
below) as,

    [(Chord majorTriadOn: 'd3' inversion: 0) eventList]

Terse EventList creation using concatenation of events or (duration, event) asociations looks like,

    [(440 Hz, (1/2 beat), 44.7 dB),        "note the comma between events"
     (1 => ((1.396 sec, 0.714 ampl) phoneme: #xu))]

Bach Example--First measure of Fugue 2 from the Well-Tempered Klavier (ignoring the initial rest).

    [(((0 beat) => (1/16 beat,  'c3' pitch)),
      ((1/16 beat) => (1/16 beat, 'b2'  pitch)),
      ((1/8 beat)  => (1/8 beat,  'c3' pitch)),
    ((1/4 beat) => (1/8 beat, 'g2'  pitch)),
    ((3/8 beat) => (1/8 beat, 'a-flat2'  pitch)),
    ((1/2 beat) => (1/16 beat,  'c3' pitch)),
      ((1/16 beat) => (1/16 beat, 'b2'  pitch)),
      ((1/8 beat)  => (1/8 beat,  'c3' pitch)),
    ((3/4 beat) => (1/8 beat, 'd3'  pitch)),
    ((7/8 beat) => (1/8 beat, 'g2'  pitch)))]

There are more comfortable event list creation methods, such as the following examples.

Play a chromatic scale giving the initial and final pitches and total duration

    [(EventList scaleExampleFrom: 48 to: 60 in: 1500) play]

Create 64 random events with parameters in the given ranges and play them over MIDI

    [(EventList randomExample: 64 
        from: ((#duration: -> (50 to: 200)),
                (#pitch: -> (36 to: 60)),
                (#ampl: -> (48 to: 120)),
                (#voice: -> (1 to: 16)))) play]

Same with named instruments = play using built-in synthesis

    [(EventList randomExample: 64 
        from: ((#duration: -> (150 to: 400)),
                (#pitch: -> (36 to: 60)),
                (#ampl: -> (48 to: 120)),
                (#voice: -> #(organ1 flute2 clarinet bassoon1 marimba bass1)))) play]

Event lists don't have to have pitches at all, as in the word,

    [EventList named: 'phrase1'  
        fromSelectors: #(duration: loudness: phoneme:)    "3 parameters"
        values: (Array with: #(595 545 545 540 570 800 540)     "3 value arrays"
                with: #(0.8 0.4 0.5 0.3 0.2 0.7 0.1) 
                with: #(#dun #kel #kam #mer #ge #sprae #che)).
    (EventList named: 'phrase1') inspect]

Create a scale where the event property types are mixed.

    [EventList scaleExample2 inspect]

Note the use of the name for the event list in the above example. All named event lists are stored
in a global dictionary named EventLists. To look at all named event lists, execute the following

    [Siren eventLists inspect]

You can erase the temporary lists (those with names beginning with 'te') from the global EventList
dictionary with,

    [EventList flushTemps]

Create an event list of 20 notes with semi-random values and play it on a MIDI output voice.

    [(EventList randomExample: 20) playOn: MIDIVoice default]

Use the same random list creation method, but add three lists in parallel.

    [((EventList newNamed: #pRand)
            addAll: (EventList randomExample: 40);
            addAll: (EventList randomExample: 40);
            addAll: (EventList randomExample: 40))
        playOn: MIDIVoice default]

Play two-voice "counterpoint" on the software synthesis voices.

    [((EventList newNamed: #pRand)
            addAll: (EventList randomExample: 20);
            addAll: (EventList randomExample: 20))
        playOn: SynthVoice default]

Here's how to use the event scheduler explicitly.

    [Schedule addAppointment: (EventList randomExample: 20)
                in: (250 msec).
    Schedule runAppointments]

Action events have arbitrary blocks of Smalltalk code as their "actions." This example creates a
list of action events that flash random screen rectangles.

    [ActionEvent playExample]

You can, or course, mix event types and voices in an event list. As in the extended example given
in the section on voices.

EventList Bulk I/O

Inspect a dictionary of all known event lists.
    [EventList allLists]

Inspect the dictionary of named event lists.
    [EventList lists]

Write out all event lists to a file.
    [EventList storeAll]

To read in a stored file, simply,
    [(FileStream fileNamed: 'events.st') fileIn]

Load all event lists (.ev, .midi, and .gio files), from the given directory.
    [EventList loadDirectory: Siren scoreDir]

To clear out temp. event lists, use,
    [EventList flushTemps]
or to flush all,
    [EventList flush]

Return To Top


Siren Functions

Siren Functions

There are several classes that support functions of 1 variable that can be described in terms of
linear or exponential interpolation between break-points, Fourier sine summation, cubic splines, or
as raw sampled data.

Examples

Basic ramp up/down
    [(LinearFunction from: #((0 0) (0.5 1) (1 0))) atX: 0.25]
    [(ExponentialFunction from: #((0 0 5) (0.5 1 -5) (1 0))) atX: 0.25 ]

Do these in an MVC Project

ADSR-line envelopes
    [(LinearFunction from: #((0 0) (0.1 1) (0.2 0.7) (0.9 0.5) (1 0))) edit]
    [(ExponentialFunction from: #((0 0 5) (0.1 1 -3) (0.8 0.5 -2) (1 0))) edit]

Sine Summation
    [(FourierSummation from: #((1 1 0)  (3 0.3 0)  (5 0.2 0)  (7 0.15 0)  (9 0.11 0)  (11 0.09 0))) 
                    edit]

Others

    [(Function randomOfSize: 128 from: 0.2 to: 0.9) edit]
    [FunctionView onFunction: 
        (Function from: #( 0 1 0 0.5 1.0 0.5 0 1 0 0.3 0.6 0.9 1 0.5 0.25 0.125 0.0625 0 1 0))]

Spectra and Signal Analysis (OK in a morphic project)

Create a swept sine wave and take its fft.
    [Display restoreAfter: [Spectrum sweepExample]]

Read a file ("unbelichtet," etc. in German) and show the spectrogram
    [Display restoreAfter: [Spectrum fileExample]]

Return To Top


Siren Event Generators

Siren Event Generators

The EventGenerator and EventModifier packages provide for music description and performance using
generic or composition-specific middle-level objects. Event generators are used to represent the
common structures of the musical vocabulary such as chords, clusters, progressions, ostinati, or
algorithms. Each event generator subclass knows how it is described--e.g., a chord with a root and
an inversion, or an ostinato with an event list and repeat rate--and can perform itself once or
repeatedly, acting like a Smalltalk-80 control structure. 

EventModifier objects generally hold onto a function and a property name; they can be told to apply
their functions to the named property of an event list lazily or eagerly. Event generators and
modifiers are described in more detail in (Pope 1991a).

Examples

Chords are simple one-dimensional event genrators.

    [((Chord majorTetradOn: 'f4' inversion: 1) duration: 1.0) play]

Create and play a simple drum roll--another 1-D event generator.

    [((Roll length: 2000 rhythm: 50 note: 60) ampl: 80) play]

Create and edit a low 6 second stochastic cloud with 5 events per second.

    [ | c |
    c := (Cloud  dur: 6            "lasts 6 sec."
        pitch: (48 to: 60)          "with pitches in this range"
        ampl: (80 to: 120)         "and amplitudes in this range"
        voice: (1 to: 8)            "select from these voices"
        density: 5) eventList.    "play 5 notes per sec. and get the event list"
    c play 
    "c edit"]

Play a 6-second cloud that goes from low to high and soft to loud.

    [(DynamicCloud  dur: 6  
        pitch: #((30 to: 44) (60 to: 60))    "given starting and ending selection ranges"
        ampl: #((20 to: 40) (90 to: 120))
        voice: #(organ1 bass1 flute1)
        density: 6) eventList play]

Play a selection cloud that makes a transition from one triad to another.

    [(DynamicSelectionCloud dur: 6
        pitch: #( #(48 50 52) #(72 74 76) )    "starting and ending pitch sets"
        ampl: #(60 80 120)
        voice: #(marimba flute1)
        density: 6) eventList play]

Mark Lentczner's bell peals ring the changes.

    [(Peal upon: #(60 62 65)) playOn: SynthVoice default]

    [ | peal list |
    peal := Peal upon: #(60 62 65 67).
    list := EventList new.
    peal playOn: list durations: 240 meter: 100 at: 0.
    list voice: #marimba.
    list play]

Do these in an MVC Project

Edit a 6-second cloud that goes from low to high and soft to loud.

    [(DynamicCloud  dur: 6  
        pitch: #((30 to: 44) (60 to: 60))    "given starting and ending selection ranges"
        ampl: #((20 to: 40) (90 to: 120))
        voice: (1 to: 4)
        density: 15) eventList "play" edit ]

Edit a selection cloud that makes a transition from one triad to another.

    [(DynamicSelectionCloud dur: 4
        pitch: #( #(48 50 52) #(72 74 76) )    "starting and ending pitch sets"
        ampl: #(60 80 120)
        voice: #(1 3 5 7)
        density: 12) eventList "play" edit]

Return To Top


Scheduling and Performance

Schedulers and Real-time Performance

Event lists have events sorted by their relative start times. They can be "executed" by placing
them in a schedule for performance. A schedule can have one or more client objects (usually event
lists) that are assumed to be able to do something in response to the (scheduleAt: aTime) message.
The return value from this message is assumed to be the delay before calling the client again.
Event lists typically perform the next event (by passing it to its voice) and answer the relative
delta time to the next event (which may be 0 for simultaneous events).

The global EventScheduler instance can be accessed by a class message (schedule) to class Siren; it
can be used to sequence and synchronize event lists that may include a variety of events, event
lists, and voices.

The Scheduler messages for adding a new "client" and running the schedule are as follow.

    [Siren schedule addAppointment: (EventList randomExample: 20) at: 500 msec.
     Siren schedule runAppointments]
    [Siren schedule addAppointment: ActionEvent listExample in: (1000 msec); 
            runAppointments]
    [Siren schedule isRunning]
    [Siren schedule interrupt]
    [Siren schedule flush]

Test the MIDI driver by playing one note.

    [MIDIPort testANote]

Play 64 notes lasting 80 msec--a good test of real-time performance.
This is scheduled in the port, i.e., at the lowest-possible level.

    [MIDIPort testRandomPlay2: 64 dur: 80]

Test a roll--it's easier to hear scheduler jitter here. The first example uses the low-level port
delays. (try it several times to hear the changes in the jitter.)

    [MIDIPort testRoll: 40 dur: 60]

This should sound about the same, but plays an event generator over the the high-level scheduler.

    [((Roll length: 2400 rhythm: 60 note: 60) ampl: 96) play]

This example uses the high-level EventScheduler to play a scale. (Jitter is harder to hear here.)

    [MIDIDevice scheduleExample]

This example does the same using the port's low-level scheduler.

    [MIDIDevice directExample]

See also the extended code example at the end of the next section.

Return To Top


Voices, Ports, and Event I/O

Voices and Ports in Siren

The "performance" of events takes place via Voice objects. Event properties are assumed to be
independent of the parameters of any synthesis instrument or algorithm. A voice object is a
"property-to-parameter mapper" that knows about one or more output or input formats for Smoke data.
There are voice "device drivers" for common file storage formats--such as cmusic note lists, MIDI
file format, or phase vocoder scripts--or for use with real-time schedulers connected to MIDI or
sampled sound drivers. These classes can be refined to add new event and signal file formats or
multilevel mapping (e.g., for MIDI system exclusive messages) in an abstract way. 

Voice objects can also read input streams (e.g., real-time controller data or output from a
coprocess), and send messages to other voices, schedulers, event modifiers or event generators.
This is how one uses the system for real-time control of complex structures.

Voices and Schedulers

Some voices are "timeless" (e.g., MIDI file readers); they operate at full speed regardless of the
relative time of the event list they read or write. Others assume that some scheduler hands events
to their voices in real time during performance. The EventScheduler does just this; it can be used
to sequence and synchronize event lists that may include a variety of voices.

Examples

Create a random event list and write it out to a cmix-format notelist file. Edit the file.
    [CmixVoice randomExampleToFileNamed: 'rand.cm']

Create a random event list and write it out to a cmusic-format notelist file. Edit the file.
    [CmusicVoice randomExampleToFileNamed: 'rand.sc']

Test the MIDI driver by playing one note.

    [MIDIPort testANote]

Test playing Siren event lists using JohnM's synthesized voices

    [SynthVoice default play: (EventList randomExample: 20)]

Read MIDI, turn it into a Siren event list, and play it.

    [((MIDIFileReader scoreFromFileNamed: 'BWV775.MID') asEventList) play]

Complex Multimedia Example: Generate and play a mixed-voice event list; a cloud plays alternating
notes on MIDI and built-in synthesis, and a list of action events flashes screen rectangles in parallel.

    [ | el |
    el := (Cloud dur: 6            "Create a 6-second stochastic cloud"
        pitch: (48 to: 60)            "choose pitches in this range"
        ampl: (40 to: 70)             "choose amplitudes in this range"
                                        "select from these 2 voices"
        voice: (Array with: 1 with: (SynthVoice default))
        density: 5) eventList.        "play 5 notes per sec. and get the events"

                                    "add some animation events"
    el addAll: ActionEvent listExample2.
    el play]                        "and play the merged event list"

Return To Top


Physical Model-based Synthesis

STK Synthesis

The classes in the MusicApps-Synthesis-* categories constitute an almost complete port by Luciano
Notarfrancesco of Perry and Gary's C++ Synthesis ToolKit to Squeak. For details on STK, see the
CCRMA web site at http://ccrma-www.stanford.edu/CCRMA/Software/STK. There are several examples in
the 'examples-scores' and 'examples-scales' method categories of Instrument (see below; the method
comments have some shortcuts).

Known bugs: some instruments sound too loud and some of them too quiet.

Methods that would help a lot if primitivized: DLineL|tick:, BiQuad|tick:, MonoWvIn|informTick,
Noise|tick, LipFilter|tick:bore:, BowTable|lookup:, DLineA|tick:, OneZero|tick:.

The instrument hierarchy looks like this:
        Instrument
            BowedString
            Brass
            Clarinet
            Drone
            FM4Op
                FM4Alg5
                    Rhodey
                    TubularBell
                    Wurley
                FM4Alg8
                    BeeThree
            Flute
            Modal4
                AgogoBell
                Marimba
                Vibraphone
            PluckedString
                Mandolin
            Shaker
            Sitar

STK Examples

Note: these are very slow; be patient.

If you get a message about a rawwaves file not being found, point your Siren soundDir to the parent
directory of the folder named "rawwaves." See:
    [Smalltalk browseAllImplementorsOf: #initializeSiren]

    [Clarinet new majorScale play]
    [Flute new majorScale play]
    [Mandolin new majorScale play]

    [Vibraphone new majorChord play]
    [Marimba new majorChord play]
    [Brass new majorChord play]
    [Rhodey new majorChord play]
    [Wurley new majorChord play]
    [TubularBell new majorChord play]
    [BowedString new lowMajorScale play]

    [Sitar new duelingbScore play]
    [Clarinet new misaCriollaScore play]
    [BeeThree new organsScore play]
    [Brass new picturesScore play]
    [Mandolin new ridersonScore play]
    [Clarinet new simplgftScore play
    [Rhodey new spainScore play]

Return To Top


About MIDI

About Siren MIDI

Siren includes a portable MIDI I/O framework that consists of class MIDIPort, a list of primitives
in the (Dynamic) interpreter, and a C-language interface module that talks to the host platform's
native MIDI driver.

An instance of the MIDIPort class is used for the interface betweeen Squeak and external MIDI
drivers and devices. It implements both note-oriented (e.g., play: pitch at: aDelay dur: aDur amp:
anAmp voice: voice), and data-oriented (e.g., put: data at: delay length: size) behaviors for MIDI
I/O. There is an extensive test suite and demo in the class examples method and in the next section
of this Siren outline.

There is typically only one instance of MIDIPort, since it can handle up to 16 physical interfaces.
The messages new, default, and instance all answer the sole instance. The class also receives
shutDown/startUp messages on VI quit or start, so if you save a VI with a MIDIPort instance in it,
MIDI gets auto-started on image launch, and closed on image quit. MIDIPorts use dependency to
signal input data--objects wishing to receive input should register themselves as dependents of a
port. In the default Siren implementation, the scheduler is all in Smalltalk, and only the simplest
MIDI driver is assumed. Fancier versions (e.g., on top of the Mi_D library from CCRMA) leave the
scheduling up to C.

MIDI Implementation: This class has a set of low-level primitive methods that are tightly coupled
with the "glue" code in the (Dynamic)Interpreter class (protocol "Siren midi primitives"). That
glue code gets translated to C in the interpreter, and calls functions that are described by the C
header file sqMIDI.h (see below). There are several possible platform-dependent drivers that
implement the module described by sqMIDI.h, for example OMS/Mac,  Mi_D/Mac, Mi_D/SGI, and Windows.

Instance Variables:
    readProcess <Process> The loop process to read input data.
    rData <OrderedCollection of (Integer -> ByteArray)> The available data.
    hasDurs <Boolean> True iff the driver can support a 1-call noteOn/Off command.
    hasBuffer <Boolean> True iff the driver has a low-level output scheduler.
    interfaces <Dictionary> The registered I/O interfaces (name and direction).
    timeOffset <Integer> The difference between the Siren and  MIDI clocks.
    readSemaphore <Semaphore> The signal for input from the driver (handed to C).
    readSemaphoreIndex <Integer> The external object registry index of the semaphore.
    channelMap <Array> The mapping between virtual and real MIDI channels.
    defaultInterface <Integer> the default output interface for events with none.
    error <Value> The error value, or nil.

Class Variables:
    Instance <MIDIPort|nil> The sole instance, or nil.
    Debug <Boolean> Debug mode prints all I/O to the Transcript.

MIDI Commands Supported:
    0x9x pp vv -- note-on (x=channel, pp=pitch, vv=velocity)
    0x8x pp vv -- note-off (x=channel, pp=pitch, vv=velocity)
    0xCx cc -- program-change (x=channel, pp=pitch)
    0xEx ll hh -- program-change (x=channel, ll=low 7 bits, hh=high 7 bits)
    0xBx cc dd -- control change (x=channel, cc=controller, dd=data)

Primitive Interface:

(These instance methods in MIDIPort call primitives defined in class Interpreter; the Interpreter
is translated to C and makes up the "glue" code to get to the driver.)

int sqOpenMIDI(int callbackSemaphore, int readSocket);        (610)                                
int sqCloseMIDI(void);            (611)
int sqGetMIDIDeviceName(int index, int name);         (612)
int sqMIDIPlaySizeOn(int message, int length, int interface);        (613)
int sqReadMIDIPacket(int packet);            (614)
int sqReadMIDIControl(int which);        (615)
int sqMIDIioctl(int which, int onOff);        (616)

These entries get added to the interpreter's (class-side) primitive table:

(610 primitiveOpenMIDI)
(611 primitiveCloseMIDI)
(612 primitiveGetMIDIDeviceName)
(613 primitivePutMIDIOn)
(614 primitiveReadMIDIPacket)
(615 primitiveReadMIDIControl)
(616 primitiveMIDIioctl)

As defined in the C-language MIDI module header file (sqMIDI.h), this looks like:

// Primitive function prototypes
int sqOpenMIDI(int readSemaphore, int inputSocket);
int sqCloseMIDI(void);     
int sqGetMIDIDeviceName(int tableIndex, int deviceName);    
int sqMIDIPlaySizeOn(int MIDImessage, int length, int device);    
int sqReadMIDIPacket(int MIDIpacket, int maxSize, int dataBuffer);
int sqReadMIDIControl(int fromController, int toController, int into);
int sqMIDIioctl(int whichIoctl, int onOff);

// Ioctl commands
#define sqMIDIInstalled        1        // Is a MIDI driver installed?
#define sqMIDIVersion        2        // What driver version is this?
#define sqMIDIHasBuffer    3            // Is there a time-stamped output buffer?
#define sqMIDIHasDurs        4        // Is there a 1-call note command?
#define sqMIDIHasClock        5        // Does the driver have its own clock?
#define sqMIDIUseSemaphore    6    // Should the driver signal a semaphore in input?
#define sqMIDIEcho            7        // Should we echo in-coming events in the driver?
#define sqMIDIControllerCache 8        // Should we cache controller values for polling?
#define sqMIDIEventsAvailable 9        // How many events are in the input Q?
#define sqMIDIFlushDriver    10        // Flush driver I/O

#define sqMIDIQuery        0        // Query about the state of a feature
#define sqMIDITurnOn    1            // Turn it on
#define sqMIDITurnOff    2            // Turn it off

(The actual header file has copious comments of this.)

Return To Top


MIDI Tests

MIDI Tests and Examples

This text is also a method comment in MIDIPort examples.

Basic Tests

"Test for the existence of the MIDI driver and, if present, report its version and properties to
the transcript."
    [MIDIPort default hasMIDI  ifTrue: [MIDIPort testVersion]]
"Try to open and close the MIDI port (report to transcript)."
    [MIDIPort testOpenClose]
"Open MIDI, play a 1-sec. note."
    [MIDIPort testANote]
"Open MIDI, play a fast scale."
    [MIDIPort testAScale]
"Play some random notes."
    [MIDIPort default test]
"Open and leave open"
    [MIDIPort new]
"Open MIDI, play notes based on the mouse position (x --> voice; y --> pitch) until mouse down."
    [MIDIPort testMouseMIDI]
"Close down and clean up."
    [MIDIPort cleanUp]

General MIDI Maps and Program Changes

"Demonstrate program change by setting up an organ instrument to play on."
    [MIDIPort testProgramChange]
"Down-load a general MIDI patch for a 4-voice organ."
    [MIDIPort setupOrgan. MIDIPort default test]
"Down-load a general MIDI patch for a 16-voice percussion ensemble."
    [MIDIPort setupTunedPercussion. MIDIPort testAScale]

MIDI Input

"Open MIDI, try to read something--dump it to the transcript."
    [MIDIPort testInput]
"Execute this to end the input test"
    [MIDIPort testInputStop]
"Get the port's pending input."
    [MIDIPort default eventsAvailable]
    [MIDIPort default readAll]
    [MIDIPort default input]
    [MIDIPort default resetInput]
"Set up a MIDI dump object as a dependent of the input port. Dump for 30 seconds,
then turn off. The default update: method just dumps the MIDI packet into the transcript; 
customize this by writing your own update: method."
    [MIDIPort dumpExample]
"Set up uncached controller reading and dump input to the transcript."
    [MIDIPort testControllerInput]
    [MIDIPort testInputStop]
"Set up uncached controller reading--read controllers from lo to hi as an array and print it; 
stop on mouse press."
    [MIDIPort testControllerCachingFrom: 48 to: 52]

Real-time Performance Tests

"Play 'num' random pitches spaced 'dur' msec apart."
"This test creates the messages and does the scheduling right here."
    [MIDIPort testRandomPlayLowLevel: 64 dur: 80]
"Play a roll of 'num' notes spaced 'dur' msec apart."
"This version uses the 1-call note command and driver-side scheduler."
    [MIDIPort testRollHighLevel: 64 dur: 50]
"Play a roll of 'num' notes spaced 'dur' msec apart."
"This test creates the messages and does the scheduling right here."
    [MIDIPort testRollLowLevel: 64 dur: 50]

Continuous Control Tests

"Demonstrate control commands by playing a note and making a crescendo with the volume pedal."
    [MIDIPort testControlContinuous]
"Demonstrate pitch-bend by playing two notes and bending them."
    [MIDIPort testBend]

Utilities

"ANO"
    [MIDIPort allNotesOff]
"Close down and clean up."
    [MIDIPort cleanUp]

Return To Top


Sound and Sound Files

Sound File I/O

The objects that support sampled sound synthesis, recording, processing, and playback are grouped
into several packages that support applications in several of the current synthesis/DSP paradigms,
including Music-N-style synthesis, graphical interactive sample editing and arithmetic, tape
recorders, digital mixers, and spatial localizers.

The basic signal processing language is modeled after mshell and presents the model of a pocket
calculator with mixed mode arithmetic and graphical inspector/editors on scalar, function and (8-,
16-, 24- or 32-bit) sample array data types.

There are interfaces to higher-level analysis/synthesis packages in the form of Smalltalk user
primitives (also known as foreign function calls) to C-language routines from Dick Moore and Paul
Lansky that implement both phase and linear prediction-based vocoders, as well as sound I/O
interfaces to both the 8- and 16-bit audio worlds. The standard sound file formats (e.g., AIFF,
Sun/NeXT, IRCAM/BICSF) are supported for reading and writing different sample formats.

Examples

Create a sine wave sound
    [StoredSound sineDur: 5 rate: 16000 freq: 800 chans: 1]

View a pulse train
    [(StoredSound pulseTrainDur: 5.0 rate: 16000 freq: 250 width: 0.001 chans: 1) edit]

Store a swept sine to a file
    [(StoredSound sweepDur: 1.0 rate: 11025 from: 100 to: 1000 chans: 1)
        storeOnFileNamed: 'sweep.snd']

Read various file formats

    [StoredSound fromFile: 'Siren:unbelichtet.aiff']
    [StoredSound fromFile: 'Siren:unbelichtet.snd']

Sound View

Zoom these with [, ], {, and }. "+" to zoom to 1@1.

    [SoundViewMorph openOn: (StoredSound linearSweepDur: 1.0 rate: 8000 
                            from: 20 to: 2000 chans: 1)]

    [SoundView openOn: (StoredSound exponentialSweepDur: 1.0 rate: 8000 
                            from: 20 to: 2000 chans: 1)]

    [SoundView openOn: (StoredSound fromFile: 'Siren:unbelichtet.snd')]

SonogramView

Open a spectrum view; use a swept sine wave, or (if shiftPressed) a spoken word.
    [SonogramView example]

Return To Top


Graphics and GUI Framework

The Siren Graphics Framework

Do these in an MVC Project

The Siren graphical applications are based on the simple display list graphics framework in the
categories Graphics-DisplayLists and Graphics-DisplayListViews. This package includes display items
such as lines, polygons, curves, text items, and images, hierarchical display lists, and display
list views, editors, and controllers. Simple examples of the display list framework are given below.

This test method fills the screen with the various kinds of display items, then demonstrates
hierarchical display lists and list scaling. It refreshes the display after it's done. Look in the
upper-left of the screen for the test name.
    [DisplayList testAll]

The display list view/controller/editor are MVC components for viewing and manipulating display
lists. In the display list view, the following inputs are assumed:
    The red mouse botton selects, drags, and rubber-bands items in the display list.
    The yellow button brings up a simple menu of basic functions.
    The blue button is used for 'drag-scrolling' of the view--try it!
    The cursor keys, page up/down and home/end keys scroll in steps of 1/4 screen.
    The keys + and - zoom in and out; pressing '=' zooms to scale 1@1.

Try using the zooming, scrolling, and mouse selection on the following examples.

Display lines
    [DisplayListView example1]

Display rectangles
    [DisplayListView rectExample]
    
Display random strings
    [DisplayListView stringExample]

Display "stars"
    [DisplayListView exampleHierarchicalStars]

Open an example of a gridded-background display list view on the example hierarchical display list.
    [DisplayListView exampleGridded]
    
Display *lots* of random strings in a very large space.
    [DisplayListView hugeStringExample]

Edit a display list with lots of items (zoom out to see it all)
    [(DisplayLine linesX: Display width byY: Display height items: 4096) edit]

Edit a very large display list with lots of items (zoom out to see it all).
(Make sure you have enough memory to create the backup form.)
    [(DisplayRectangle rectanglesX: 2048 byY: 2048 items: 800) edit]

3D Graphics

    [WireModel exampleMenu]

    [WireModel screenSaver]

There are many more examples in the display item classes, and the view hierarchy.

Return To Top


Siren Music GUIs

Siren GUI Applications in Squeak

Do these in an MVC Project

Graphical Forms

Siren includes a hierarchical dictionary of images for use in musical notations. Execute the
following to display the various forms. The method steps through the form dictionaries and then
restores the display.

    [Siren displayMusicConstants]

To examine this dictionary, inspect,

    [Siren MusicConstantDictionary]

Layout Managers and Navigator MVC

The "Navigator MVC" framework is based on layout manager objects that can generate display lists
from structured objects. This enables, for example, a variety of musical notations. LayoutManagers
take data structures and generate display lists based on their layout policies. For example, to see
a class inheritance hierarchy as an indented list, use an IndentedListLayoutManager as in,

     [IndentedListLayoutManager colorListExample edit]

(Note that color denotes species in this example.)

To view the same structure as a tre-like layout, use an IndentedTreeLayoutManager, as in,

    [IndentedTreeLayoutManager listExample edit]

Music Notations

Layout managers serve as the basis for Siren's music notation applications. The basic
event-oriented layout manager uses the horizontal axis to denote time (flowing from left to right),
as in the next example, which opens a time sequence view on a random event list.

    [TimeSequenceView randomExample]

In the time sequence view, the "note head" signifies the event's voice, not the duration. 

A pitch/time view is an extension of this that uses the vertical dimension to display an event's
pitch, as in piano-roll notation; for example, to display a pitch/time view on a 3-stream event
list, try,

    [PitchTimeView randomExample]

In the above example, the note heads denote the events' voices, horizontal blue lines originating
at the note heads show the events' lengths, and red vertical lines show the events' amplitudes. To
see how this is done, look at class PitchTimeView's various implementation of the itemFor: method.

Open a pitch/time view on a *very long* 3-stream event list.

    [PitchTimeView randomExampleLong]

A more complete example is Hauer-Steffens notation, which has a clef and staff lines as in
common-practise notation.

    [HauerSteffensView randomExample]

Test panning and zooming these examples.

Common-practice music notation (CMN) (broken)
    [CMNView openOn: ((EventListEditor new) 
            eventList: (EventList scaleExampleFrom: 48 to: 60 in: 3000))]

MIDI GUIs

Open a view that displays the state of 8 MIDI faders.

    [MIDIFaderGUI openFaderView]

Open a view with 128 buttons for the GM instrument names.
See the setInstr: method for the behavior on mouse-down.

    [MIDIPatchEditor new openNamePopUp]

Sound View

The sound view is a simple waveform display. One can scroll, zoom, and edit.

Test the [, ], {. } and = keys. 

Use the pop-up menu to create sound objects based on a number of standard synthesis methods.

    [SoundView openOn: (StoredSound linearSweepDur: 1.0 rate: 8000
                    from: 20 to: 2000 chans: 1)]

    [SoundView openOn: (StoredSound exponentialSweepDur: 1.0 rate: 8000
                    from: 20 to: 2000 chans: 1)]

This example reads a sound from a file and edits it.

    [SoundView openOn: (StoredSound fromFile: 'unbelichtet.snd')]

Return To Top


Loading Score Data

Data Load/Store and the Paleo Database

"Note: The paleo test data is not on the CD-ROM, please go to the Siren web site to retrieve it if interested.


Flush the global event list dictionary

EventList flush

Load Event Lists from MIDI files

[    | num fn el |
    Cursor wait showWhile: 
        [1 to: 100 do: [ :ind |
            num := ind asZeroFilledString: 3.
            fn := Siren scoreDir, 'Scarlatti:K', num, '.MID'.
            (FileDirectory root fileExists: fn)
                ifTrue: [el := (MIDIFileReader scoreFromFileNamed: fn) asEventList.
                    el at: #name put: ('ScarlattiK', num) asSymbol.
                    el at: #composer put: 'Domenico Scarlatti'.
                    el at: #instrumentation put: #harpsichord.
                    el at: #style put: #Baroque.
                    Siren eventLists at: ('ScarlattiK', num) asSymbol put: el]]]]

Siren eventLists explore
EventList someInstance
EventList instanceCount
EventList allInstances

Loading Sound Files

[    | dir fn |
    dir := Siren soundDir, 'SimpleT:justin_b:'.
    Transcript cr.
    10 to: 14 do: [ :ind |
        fn := (FileDirectory on: dir) fileNamesMatching: (ind printString, '*').
        fn isEmpty
            ifFalse: [fn := fn first.
                Transcript show: 'Reading sound from: ', dir, fn; cr.
                StoredSound fromFile: dir, fn]]]

Siren sounds inspect
Siren initializeSoundDictionary

Loading Spectral Data Files

[    | dir fn |
    dir := Siren soundDir, 'sharc:tuba:'.
    Transcript cr.
    ((FileDirectory on: dir) fileNamesMatching: ('*.spect')) do:
        [ :fn |
        Transcript show: 'Reading sound from: ', dir, fn; cr.
        Spectrum fromFile: dir, fn]]]

Siren spectra inspect
Siren Siren initializeSpectrumDictionary

Return To Top


Storage Formats

Storing EventLists in Squeak Reference Streams

Siren supports compact and fast score I/O in the form of methods that work with the ReferenceStream
framework (system category System-Object Storage). See the storeDataOn: and readDataFrom: methods
in MusicEvent and EventList, and MusicMagnitude class methods for 'objects from disk.' Below is an
example of how to store and retrieve an event list, and an annotated hex dump.

Store an event list on a ReferenceStream

    | el rs name |
    el := EventList scaleExample2.
"    el := EventList named: #ScarlattiK004."
    el at: #composer put: 'Random'.    "add a property"
    name := 'test.elist.rs'.
    rs _ ReferenceStream fileNamed: name.
    rs nextPut: el.
    rs close.
    (FileStream named: name) edit.    "use the 'view as hex' menu item"

An EventList with 13 notes with mixed properties = about 560 bytes.

Read an event list from the file just created

    | el rs name |
    name := 'test.elist.rs'.
    rs _ ReferenceStream fileNamed: name.
    el := rs next.        "it's that easy"
    rs close.
    el inspect

Annotated binary dump of a Siren EventList

This is a hex dump of a simple event list representing a scale whereby the types of the properties
have been scrambled (see EventList scaleExample2), i.e., the pitches are mixed among MIDI key
numbers, note names, Hertz values, etc.
Note that the event and music magnitude keys (C0 and 80, respectively) are only used for debugging.
They make it much easier to debug I/O methods -- see the commented-out parts of the
storeDataOn:/readDataFrom: methods in MusicEvent and the class protocol for I/O in MusicMAgnitude.

09 00 00 00 06                             key 09 = normal object
                                        32-bit inst size 6 = 4 instVars + 1 property + 1
    06 09 45 76 65 6E 74 4C 69 73 74         class name, key 06 = String, size = 09, 
                                        value = 'EventList'
    80 58 04 00 00 08 6E                     (duration) key 80 = MusicMagnitude,
                                        key 58 = MSecondDuration, key 04 = smallInt,
                                        32-bit int value
    01 01 01                                 nil pitch, ampl, voice
    06 04 6E 61 6D 65                     property key = 'name'
    06 03 74 65 36                         property value = 'te6'
    00 00 00 0D                             # of events (13)
C0 80 58 04 00 00 00 00                     C0 = event key, (start time) 80 = music magnitude key
                                        58 = msec dur key, 04 = smallInt, 32-bit val = 0
    00 00 00 05                             obj size = 4 instVars + 0 properties + 1
    06 0A 4D 75 73 69 63 45 76 65 6E 74     class name, key 06 = String, size = 0A, 'MusicEvent'
    80 58 04 00 00 00 A6                 duration, key 58 = MSecondDuration, key 04 = smallInt,
                                        32-bit int value A6 = 166 msec
    80 5C 11 02 63 32                     pitch, key 5c = SymbolicPitch, key 11 = string, 
                                        length = 2, string = 'c2'
    80 47 04 00 00 00 48                      loudness, key 47 = MIDIVelocity
    01                                    voice = nil
C0 80 58 04 00 00 00 A6                     event, (start time) music magnitude, msec dur
    00 00 00 05                          obj size = 4 instVars + 0 properties + 1
    0A 00 00 00 35                         reference to class name
    80 48 0E 3F C5 3F 7C                     duration, key 48 = SecondDuration, key 0E = float
    80 5C 11 03 63 23 32                  pitch, key 5c = SymbolicPitch, key 11 = string...
    80 54 0E C0 13 B7 BB                     loudness, key 47 = DBLoudness, key 0E = float
    01                                      voice = nil
--more events follow, no footer

Sound Storage

    | snd rs name |
    snd := Sound named: '11.moon.1.snd'.
    name := 'test.snd.rs'.
    rs _ ReferenceStream fileNamed: name.
    rs nextPut: snd.
    rs close.
    (FileStream named: name) edit.    "use the 'view as hex' menu item"

Return To Top


Database testing

Testing MinneStore on Squeak

For advanced uers only...

    MSTestSuite runCompatibilityTest
    MSTestSuite runDiskObjectServiceTest
    MSTestSuite runMinneStoreTest1
    MSTestSuite runMinneStoreTest2
    MSTestSuite runMinneStoreTest3
    MSTestSuite runMinneStoreTest4

[    MinneStoreDB instanceCount]
[    MinneStoreDB allInstances]

Here is the way to use DataStream and ReferenceStream:

[    | obj rr |
    rr := ReferenceStream fileNamed: 'test.obj'.
    obj := StoredSound fileExample.
    obj freeData.
self halt.
    rr nextPut: obj.
    rr close]

To get it back:

[    | rr obj |
    rr := ReferenceStream fileNamed: 'test.obj'.
    obj := rr next.
    rr close.
    obj explore]

Return To Top


Paleo Database Creation

The Paleo Database

For advanced uers only...

Create a new database of event lists, store the entire event list dictionary to it.

[    | db |
"    Siren databaseDir: 'Content:Paleo'."
    Siren databaseDir: 'RAM:Database'.
    db := MinneStoreDB newOn: Siren databaseDir.
    Siren database: db.
    (db addObjectSetNamed: #Scores)
        objectsPerFile: 1;
        storesClass: EventList;
        indexOn: #style domain: Symbol;        "getter is a symbol"
        indexOn: #firstPitch
            domain: SmallInteger             "getter is a 'block' text"
            getter: 'item events first event pitch asMIDI value';
        indexOn: #composer domain: String.
    db save.
"    db storeAll: (Siren eventLists)
"    MessageTally spyOn: [db storeAll: (Siren eventLists)] toFileNamed: 'addAll.spy'  ]

Flush the in-memory event lists

    EventList flush

Run a block on all scores

[    Siren database forAll: #Scores 
            do: [ :item | Transcript show: 
                        item events first event pitch asMIDI value printString; cr] ]

Make a query

[    (Siren database getAll: #Scores) 
    "    where: #composer eq: 'Domenico Scarlatti';    "
        where: #firstPitch between: 62 and: 67;        
        execute ]

Add a new index to an existing database

[    | db |
    db := Siren database.
    (db objectSetNamed: #Scores) indexOn: #name domain: Symbol.
"    MessageTally spyOn: [(db objectSetNamed: #Scores) indexOn: #name domain: Symbol]
             toFileNamed: 'indexOn.spy'. "
    db save ]

Make a query on the newly created index

[    (Siren database getOne: #Scores) where: #name eq: #ScarlattiK005; execute ]


    "Object initializePropertiesFields. Smalltalk garbageCollect"

Return To Top


Setting up Demo Databases

Setting up the Demo Paleo Database

For advanced uers only...

[    YYYJD setupDB.                        "Create base DB"
    YYYJD setupScore.                    "Create a framework event list"
                                        "Load some voice snd files"
    YYYJD loadSndFiles: 'Content:Sound:YYYJD:EC:read3:#.snd' into: #VoiceSounds.
                                        "Load some bell sco files"
    YYYJD loadScoFiles: 'Content:Sound:YYYJD:Bells:B3:*.sc' into: #BellScores.
                                        "Load an LPC file"
    YYYJD doLPCFolder: 'Content:Sound:YYYJD:EC:read3' intoOSet: #LPC.
                                        "Print DB stats."
    PaleoDemo setupDB.                    "Add a few demo tables"
    PaleoDemo loadMikrokosmos.            "Read Mikrokosmos"
    PaleoDemo addSHARC.                "Read the SHARC timbres"
    PaleoDemo addGenerators.            "Add the demo generators"
    YYYJD printDBsizes                    "Print DB stats" ]

    SMSBrowser openOn: (PaleoDemo DB)        "open a browser"


    "PaleoUtil withAllSubclasses do: [ :c | c freeDB]"
    "MinneStoreDB instanceCount"
    "YYYJD setupDB"

Selection example: read 3 source sounds

[    YYYJD from: #VoiceSounds where: #folder eq: #read3 ]

Return To Top


Bibliography and References

Reference, Acknowledgments

The main Siren Web site is at http://www.create.ucsb.edu/Siren.

This outline is on-line at http://www.create.ucsb.edu/Siren/Siren3.0.outline.html.

To join the mailing list, see http://www.create.ucsb.edu/mailman/listinfo/squeakaudio.

Various versions and components of Siren's predecessors (esp. HyperScore ToolKit and MODE) are
documented in the book "The Well-Tempered Object: Musical Applications of Object-Oriented Software
Technology" (S. T. Pope, ed. MIT Press, 1991), in papers in the Proceedings of the 1986, 1987,
1989, 1991, 1992, 1994, 1996, and 1997 International Computer Music Conferences (ICMCs), in an
article on the "Interim DynaPiano" in "Computer Music Journal" 16:3, Fall, 1992 (heartily
recommended--available as PS on the CMJ Web site), in the book "Musical Signal Processing (C.
Roads, S. T. Pope, G. DePoli, and A. Piccialli, eds. Swets & Zeitlinger, 1997), and in several
documents on the Web page http://www.create.ucsb.edu/~stp/publ.html. There are more MODE- and
Smoke-related documents (including several of the above references) in the directory ftp://ftp.create.ucsb.edu/pub/Smalltalk/Music/Doc.

Acknowledgments

Siren incorporates the work of many people who have contributed ideas and/or code; they include:
    Paul Alderman
    Alberto de Campo
    Roger Dannenberg
    Lounette Dyer
    Adrian Freed
    Guy Garnett
    Kurt Hebel
    Helge Horch
    Craig Latta
    David Leibs
    Mark Lentczner
    Hitoshi Katta
    Alex Kouznetsov
    John Maloney
    James McCartney
    Hans-Martin Mosner
    Luciano Notarfrancesco
    Danny Oppenheim
    Nicola Orio
    Francois Pachet
    Pierre Roy
    Carla Scaletti
    Bill Schottstaedt
    John Tangney
    Bill Walker

I must also here acknowledge the generous support of my employers and the academic institutions
where this software was developed, including PCS/Cadmus GmbH in Munich, Xerox PARC, ParcPlace
Systems, Inc., CCRMA/Stanford, The STEIM Foundation in Amsterdam, The Swedish Institute for
Computer Science, CMNAT/Berkeley,  CREATE/UCSB, and the Technical University of Berlin.

Return To Top


Siren Look&Feel

The Siren Look-and-Feel

Set-up

"Set font 6 to be a 6-point bold sans-serif font for use on tiny buttons"
[TextStyle default fontAt: 6
        put: (StrikeFont new readMacFontHex: 'SS Bold 6')]

"Set font 6 to be a 6-point serif font for use on tiny buttons"
[TextStyle default fontAt: 6
        put: (StrikeFont new readMacFontHex: 'NewYork 6')]

"Increase the default font size"
[TextStyle changeDefaultFontSizeBy: 1]

"Pick a desktop pattern from the files named 'pattern*.GIF in the Siren data directory."
[Preferences pickDesktopPattern]

Preferences desktopFromGIFFile: 'Nomad:New:Patterns:Droplets.GIF'

Return To Top


Troubleshooting Siren

What to do if something crashes

Notes

This is beta-ware, but I've tested all the example expresisons in this outline.
Most of the advanced GUIs are not ported yet. (Wanna help?)

MIDI clean up

Look at the top group of items in the screen controller's "do" menu, especially the expressions

Flush and close down the scheduler

    [Schedule interrupt; flush]

All notes off, flush ports, throw away open ports, clear out temp event lists, etc.

    [MIDIPort cleanUp]

Try playing one note out over MIDI

    [MIDIPort testANote]

If you can't hear any MIDI output, try the TestOMSApp; it's a stand-alone Mac application that
should let you select a MIDI device with the lower of its two menus. After selecting a device, it
will play a scale on the chosen device. IF that isn't audible, your MIDI setup is misconfigured;
look at the OMS documentation.

Known Bugs

Too numerous to list (send me new ones...)

Return To Top


Siren Build Script

"How to File In Siren"

 "Do the STP12.3.0 and SMS 3.0 build scripts first!"

To get this outline, execute,

OutlineBrowser openFile: ('Siren3.0.outline') label: 'Siren Outline' 

[ | dir |
    "Edit this to be your local directory path."
    "Also, change the file separator below if necessary."

" dir := 'Nomad:Squeak:Siren:3.0:'. "
dir := '/home/stp/st/Siren3.0/'.

#(
"Minor system extensions"
    '5.System:TreeNode.st'
    '5.System:Graphics-DisplayLists.st'
    '5.System:Graphics-DisplayListViews.st'
    '5.System:Form-adds.st'
    '5.System:Interval-stop.st'
    '5.System:Object-Conversions.st'
    '5.System:SoundBuffer-shortAtput.st'
    '5.System:LauncherView-siren.st'
"Siren set-up: class pool dictionaries"
    '5.System:Music-Constants.st'
    '5.System:MIDI-Constants.st'
    '5.System:MIDI-GMMaps.st'
    '5.System:Sound-Constants.st'
    '5.System:MIDIScore-asEventList.st'
"Smoke Kernel"
    '1.Kernel:Music-Utility.st'
    '1.Kernel:Music-Models-Representation.st'
    '1.Kernel:Music-Models-Implementation.st'
    '1.Kernel:Music-Events.st'
    '1.Kernel:Music-EventGenerators.st'
    '1.Kernel:Music-EventModifiers.st'
    '1.Kernel:Music-Functions.st'
    '1.Kernel:Music-Voices.st'
    '1.Kernel:Music-Sound.st'
    '1.Kernel:Music-Support.st'
"Siren Apps"
    '2.Apps:MusicApps-HAT-Kernel.st'
    '2.Apps:MusicApps-HAT-Harmony.st'
    '2.Apps:MusicApps-LPC.st'
    '2.Apps:MusicApps-Pieces.st'
    '2.Apps:MusicApps-PVoc.st'
    '2.Apps:MusicApps-SHARC.st'
    '2.Apps:MusicApps-STK-Filters.st'
    '2.Apps:MusicApps-STK-Sources.st'
    '2.Apps:MusicApps-STK-Instruments.st'
"Siren MIDI/Sound I/O"
    '3.IO:MusicIO-MIDI.st'
    '3.IO:MusicIO-MIDI-Commands.st'
    '3.IO:MusicIO-MIDI-Patches.st'
    '3.IO:MusicIO-Sound.st'
"Siren GUI"
    '4.UI:MusicUI-EventLists.st'        
    '4.UI:MusicUI-Explorer.st'        
    '4.UI:MusicUI-Sound.st'        
    '4.UI:MusicUI-Functions.st'
    '4.UI:MusicUI-MIDI.st'
    '4.UI:MusicUI-Morphic.st'
"Work in Progress Categories"
    '6.WIP:MusicWIP-Effects.st'
    '6.WIP:MusicWIP-Spatial.st'
    '6.WIP:MusicWIP-PitchClasses.st'
    '6.WIP:MusicWIP-Mixing.st'
    '6.WIP:MusicWIP-Trees.st'
    '6.WIP:MusicWIP-OSC.st'
"Stale, MVC-based, or just plain old code"
    '7.OLD:MusicOLD-Editors.st'
    '7.OLD:MusicOLD-Layout.st'
    '7.OLD:MusicOLD-Morphic.st'
    '7.OLD:MusicOLD-Sound.st'
    '7.OLD:MusicOLD-UI-Functions.st'
    '7.OLD:MusicOLD-UI-HAT.st'
"This subclasses several other things, so do it last..."
    '2.Apps:MusicApps-OperaDB.st'

) do: [ :file |
    Transcript crtab; show: file; cr.
    file replaceAll: $: with: FileSep first.
    (FileStream readOnlyFileNamed: dir, file) fileIn].

    (Smalltalk at: #Siren) perform: #sourceDir: with: dir].


Now look in the Fixes directory and file in anything found there.

[    ((FileDirectory on: (Siren sourceDir, '8.Fixes')) fileNamesMatching: '*') do:
        [ :file | 
        Transcript cr; show: file.
        (FileStream readOnlyFileNamed: 
                    Siren sourceDir, '8.Fixes', FileSep, file) fileIn]]


"Now read and edit the path names to data storage"
    Smalltalk browseAllImplementorsOf: #initializeSiren.

"Do these"

[    Siren initializeSiren.
    Siren loadLogoFrom: Siren sourceDir, 'gifs', FileSep, 'firetruck2.small2.GIF'.
    Duration initialize.
    Pitch initialize.
    PitchClass initializeClass.
    Sound initialize.
    Amplitude initialize.
    SoundFile initializeClass.
    MusicMagnitude initializeTypeCodes.
    Smalltalk noChanges.
    Smalltalk cleanOutUndeclared.
    Symbol rehash]

"Open a Siren launcher."

[ (LauncherView openSiren: Transcript asMorphLabel: 'Siren Launcher') openInWorld ]

"Reorganize class categories, putting Music categories at the top"

"Make a snapshot"

Return To Top


Siren FileOut Script

File Out Music Categories

[ | block siz root strm |

"root := 'Nomad:Squeak:Siren:3.0:'."        "Edit this for your site"

root := '/home/stp/st/Siren3.0/'.            "Edit this for your site"

block := [ :token :dir | 
    (SystemOrganization categories select: 
        [ :cat | 
        siz := token size.
        (cat size >= siz) and: [(cat copyFrom: 1 to: siz) = token]])
    do: [ :catName |
        Transcript cr; cr; show: catName; cr.
        FileDirectory default deleteFileNamed: (dir, FileSep, catName, '.st') ifAbsent: [].
        strm := FileStream fileNamed: dir, FileSep, catName, '.st'.
        SystemOrganization fileOutCategory: catName on: strm.
        strm close]].

block value: 'Music-' value: root, '1.Kernel' .
block value: 'MusicApps-' value: root, '2.Apps'.
block value: 'MusicIO-' value: root, '3.IO'.
block value: 'MusicUI-' value: root, '4.UI'.
block value: 'MusicWIP-' value: root, '6.WIP'.
block value: 'MusicOLD-' value: root, '7.OLD'.
"
block value: 'Graphics-DisplayLists' value: root.
block value: 'Graphics-DisplayListViews' value: root.
block value: 'OODB-' value: 'Nomad:Squeak:SMS:2.9:'. " ]

Return To Top


Siren Demo Script

Minimal Siren Demo Script

Read through this text, selecting the blocks enclosed in square brackets. The single character
after the close-square-bracket (d,p, or i) denotes whether you should "do," "print," or "inspect"
the block.

Set-up

Test sound output by executing the following.
    [FMSound hiMajorScale play] d

Test the MIDI driver by playing one note.
    [MIDIPort testANote] d

See also the section above on "Siren Set-up."

Siren Example Views

(Optional) These expressions collect all the example methods from the Siren classes and present
them as as a list view or pop-up menu.

Open a message list browser on all of the example methods in the Siren categories."
    [Siren SirenExampleListView] d

MusicMagnitudes

    [440 Hz asSymbol] p            "--> 'a3' pitch"
    [(1/4 beat) asMsec] p        "--> 250 msec"
    [#mf ampl asMIDI] p        "--> 70 vel"

    ['a4' pitch asMIDI] p
    [('a4' pitch + 100 Hz) asMIDI] p
    ['mp' ampl + 3 dB] p

Event Creation Messages
    
"Create a `generic' event."
    MusicEvent duration: 1/4 pitch: 'c3' ampl: 'mf'

"Create one with added properties."
    (MusicEvent dur: 1/4 pitch: 'c3') color: #green; accent: #sfz

Event Lists

    [440 Hz, (1/4 beat), 44 dB] i
    (#c4 pitch, 0.21 sec, 64 velocity) voice: IOVoice default

    (EventList newNamed: #Chord1)
        add: ((1/2  beat), 'd3'  pitch,  'mf' ampl)  at: 0;
        add: ((1/2  beat), 'fs3'  pitch, 'mf' ampl)  at: 0;
        add: ((1/2  beat), 'a4'   pitch, 'mf' ampl)  at: 0

Play a scale created with a class message."
    [(EventList scaleExampleFrom: 48 to: 60 in: 1500) playOn: SynthVoice default] d

Create 64 random events with parameters in the given ranges, play it on MIDI
    [(EventList randomExample: 64 
        from: ((#duration: -> (50 to: 200)),
                (#pitch: -> (36 to: 60)),
                (#ampl: -> (48 to: 120)),
                (#voice: -> (1 to: 16)))) play] d

Create an event list of 20 notes with semi-random values and play it on a MIDI output voice.
    [(EventList randomExample: 20) playOn: MIDIVoice default] d

Play two-voice "counterpoint" on the software synthesis voices.
    [((EventList newNamed: #pRand)
            addAll: (EventList randomExample: 20);
            addAll: (EventList randomExample: 20))
        playOn: SynthVoice default] d

Siren Scheduler

Here's how to use the event scheduler explicitly.
    [EventScheduler instance addAppointment: (EventList randomExample: 20) in: (250 msec).
    EventScheduler instance runAppointments] d

Flush and close down the scheduler
    [EventScheduler instance interrupt; flush] d

Action events have arbitrary blocks of Smalltalk code as their "actions." This example creates a
list of action events that flash random screen rectangles.
    [ActionEvent playExample] d

Complex Multimedia Example
    [ | el |
    el := (Cloud  dur: 6            "Create a 6-second stochastic cloud"
        pitch: (48 to: 60)            "choose pitches in this range"
        ampl: (40 to: 70)             "choose amplitudes in this range"
        voice: nil                "leave the voice nil for now"
        density: 5) eventList.        "play 5 notes per sec. and get the events"
    1 to: el events size do:        "Now plug different voices in to the events"
        [ :ind |                     "ind is the counter"
        (el events at: ind) event voice: 
            (ind odd                 "alternate between two voices"
                ifTrue: [MIDIVoice default] 
                ifFalse: [SynthVoice default])].
                                    "add some animation events"
    el addAll: ActionEvent listExample2.
    el play] d                        "and play the merged event list"

EventGenerators

Chord object can give you an event list.
    (Chord majorTriadOn: 'd3' inversion: 0) eventList

Create and play a simple drum roll--another 1-D event generator.
    [((Roll length: 2000 rhythm: 50 note: 60) ampl: 80) play] d

Play a 6-second cloud that goes from low to high and soft to loud.
    [(DynamicCloud  dur: 6  
        pitch: #((30 to: 44) (50 to: 50))    "given starting and ending selection ranges"
        ampl: #((20 to: 40) (90 to: 120))
        voice: (1 to: 4)
        density: 15) eventList play "edit" "inspect"] d

Mark Lentczner's bell peals ring the changes.
    [(Peal upon: #(60 62 65)) play] d

MIDI Control

"Open MIDI, play notes based on the mouse position (x --> voice; y --> pitch) until mouse down."
    [MIDIPort testMouseMIDI] d

"Demonstrate program change by setting up an organ instrument to play on."
    [MIDIPort testProgramChange] d
"Down-load a general MIDI patch for a 4-voice organ."
    [MIDIPort setupOrgan. MIDIPort default test] d
"Down-load a general MIDI patch for a 16-voice percussion ensemble."
    [MIDIPort setupTunedPercussion. MIDIPort testAScale] d

"Demonstrate control commands by playing a note and making a crescendo with the volume pedal."
    [MIDIPort testControlContinuous] d
"Demonstrate pitch-bend by playing two notes and bending them."
    [MIDIPort testBend] d

The Siren Graphics Framework

Do these in an MVC Project

Display lines
    [DisplayListView example1] d

Display rectangles
    [DisplayListView example2] d

Open a sequence view on a random event list.
    [TimeSequenceView randomExample] d

Utilities

"ANO"
    "MIDIPort allNotesOff"
"Close down and clean up."
    "MIDIPort cleanUp"

Return To Top


Recent Changes

Recent Changes to Siren

None in this release; see http://www.create.ucsb.edu/Siren

Return To Top


Siren 3.0 Outline -- stp@create.ucsb.edu -- Generated #(19 February 2001 2:23:44 pm)