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)