Your Basic Siren Demo Script

To use this demo script, read through the text selecting the blocks enclosed in square brackets. The single character after the close-square-bracket (d,p, i, or db) denotes whether you should "do," "print," "inspect," or "debug" the block. (Typically, CTRL-D means do-it, CTRL-P means print-it, and CTRL-Q means inspect-it.) To look at the code for the complex examples below, simply "debug-it" and single-step into the demo method.

For the function and event list examples below, note that the "open" method edits the receiver by default; if you hold down while executing it, it will play the receiver, and if you hold down is will inspect the receiver.

If you're new to reading Smalltalk, look at the workbook section on "Learning to Read Smalltalk." If you're setting up Siren for the first time, see the section above on "Siren Set-up and Testing" and make certain you know how to use the Siren Utility panel and the Siren Transport view.

Set-up

Configure and test the MIDI and sound I/O drivers using the utility panel.
   [SirenSession openUtility] d

Load the test data with the "Load All" button in the utility pane.
You can browse the test data with the left-side menu buttons in the transport panel, do,
   [SirenSession openTransport] d

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

MusicMagnitudes

Print these to see what kinds of music magnitude representations and operations are supported.
   [440 Hz asSymbol] p      "--> 'a3' pitch"
   [(1/4 beat) asMsec] p      "--> 250 msec"
   [#mf ampl asMIDI] p      "--> 70 vel"
   [-16 dB asRatio value] p   "--> 0.158489"

   ['a4' pitch asMIDI] p
   [('a4' pitch + 100 Hz) asMIDI] p
   [('a4' pitch + 100 Hz) asFracMIDI] p
   ['mp' ampl + 3 dB] p
   [('mp' ampl + 3 dB) asMIDI] p
   [(1/2 beat) + 100 msec] p

Pitch expressions ("N" is short-hand for PitchClass)
   [N C sharp sharp] p
   [N C augmentedFourth] p
   [N C diminishedThirteenth] p
   [N do flat intervalBetween: N sol] p
   [PitchClass D flat melodicMinorScale notes] p
   [(PitchClassChord new fromString: 'C aug9 dim5') notes] p
   [(HungarianMinor root: N fa) asPitchesInOctave: 2] p

Event Creation Messages

Create a `generic' event using a class instance creation message.
   MusicEvent duration: 1/4 pitch: 'c3' ampl: 'mf'

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

Terse format: concatenation (with ',') of music magnitudes
   [440 Hz, (1/4 beat), 44 dB] i
   (#c4 pitch, 0.21 sec, 64 velocity) voice: IOVoice default

Event Lists

Verbose form using a class instance creation message;
a chord is simply a set of events at the same time.

   (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) open] d

Create 64 random events with parameters in the given ranges, play it on the default output voice, or edit it.
   [(EventList randomExample: 64          "make 64 notes"
      from: ((#duration: -> (50 to: 200)),      "duration range in msec"
            (#pitch: -> (36 to: 60)),         "pitch range in MIDI keys"
            (#ampl: -> (48 to: 120)),      "amplitude range in MIDI velocities"
            (#voice: -> (1 to: 1)))         "play all on voice 1"
      ) open] 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

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]

Play two-voice "counterpoint" on the note list score file voices.
   [ | vox list |
   vox := CsoundVoice onFileNamed: 'test.cs'.
   list := (EventList newNamed: #pRand)
         addAll: (EventList randomExample: 20);
         addAll: (EventList randomExample: 20).
   vox play: list.
   vox close.
   (Filename named: 'test.cs') open] d

Here's another example of creating a simple melody
   [(EventList named: 'melody'
      fromSelectors: #(pitch: duration: ampl:)
      values: (Array with: #(c d e f g)
               with: #(4 8 8 4 4) reciprocal
               with: #(1))) open] d

You can also create event lists with snippets of code such as the following whole-tone scale.
   [ | elist |
   elist := EventList newAnonymous.
   1 to: 12 do:
      [ :index |
      elist add: (1/4 beat, (index * 2 + 36) key, 'mf' ampl)].
   elist open ] d

Event lists can also be nested into arbitrary structures, as in the following group of four sub-groups
   [ (EventList newNamed: 'Hierarchical/4Groups')
      add: (EventList randomExample: 8
         from: ((#duration: -> (60 to: 120)), (#pitch: -> (36 to: 40)), (#ampl: -> #(110)))) at: 0;
      add: (EventList randomExample: 8
         from: ((#duration: -> (60 to: 120)), (#pitch: -> (40 to: 44)), (#ampl: -> #(100)))) at: 1;
      add: (EventList randomExample: 8
         from: ((#duration: -> (60 to: 120)), (#pitch: -> (44 to: 48)), (#ampl: -> #(80)))) at: 2;
      add: (EventList randomExample: 8
         from: ((#duration: -> (60 to: 120)), (#pitch: -> (48 to: 52)), (#ampl: -> #(70)))) at: 3;
      open ] d

Smalltalk methods can also process event lists, as in this code to increase the durations of the last notes in each of the groups from the previous example.
   [ (EventList named: 'Hierarchical/4Groups') eventsDo:
      [ :sublist | | evnt |   "Remember: this is hierarchical, to the events are the sub-groups"
      evnt := sublist events last event.      "get the first note of each group"
      evnt duration: evnt duration * 8].      "multiply the duration by 4"
   (EventList named: #groups) open ] d

...or the following to take the scale and make it slow down
   [ | elist |
   elist := EventList scaleExampleFrom: 60 to: 36 in: 1500.
   1 to: elist size do:
      [ :index | | assoc |
      assoc := elist events at: index.
      assoc key: (assoc key * (1 + (index / elist events size)))].
   elist open ] d

Storage and Persistency
   "SirenSession eventList: 'piece1/mvmnt1/part1' put: EventList new"
   "SirenSession eventList: 'piece1/mvmnt1/part1'"

Siren Scheduler

Reset
   [EventScheduler initialize]

Here's how to use the event scheduler explicitly.
   [EventScheduler instance addClient: (EventList randomExample: 20) in: (500 msec).
   EventScheduler instance run] 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: #(1)            "leave the 1 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: [OSCVoice default])].
                        "add some animation events"
   el addAll: ActionEvent listExample.
   el open] d               "and play the merged event list"

Functions and Control

If you like thicker function plots, do this,
   [FunctionView lineWidth: 2]

Basic ramp up/down (linear and exponential flavors)
   [(LinearFunction from: #((0 0) (0.5 1) (1 0))) at: 0.25] p
   [(ExponentialFunction from: #((0 0 5) (0.5 1 -5) (1 0))) at: 0.25 ] p

ADSR-like envelopes
   [(LinearFunction from: #((0 0) (0.1 1) (0.16 0.7) (0.8 0.4) (1 0))) edit]
   [(ExponentialFunction from: #((0 0 5) (0.1 1 -3) (0.8 0.4 -2) (1 0))) edit]

Open a view with a linear envelope, an exponential envelope, a spline curve, and a sum-of-sines function
   [FunctionView multiFunctionExample]

One can apply a function to any property of an event list, as in the example below, which makes a crescendo/decrescendo using an exponential triangle function.

   [ | list fcn |
   list := EventList newNamed: #test3.
   (0 to: 4000 by: 100) do:    "4 seconds, 10 notes per second"
         [ :index |         "add the same note"
         list add: (MusicEvent dur: 100 pitch: 36 ampl: 120) at: index].
   fcn := ExponentialFunction from: #((0 0.05 2) (0.5 1 -2) (1 0.05)).
   list applyFunction: fcn to: #loudness.
   list inspect] d

Send function data values out as regular OSC messages
   [OSCVoice functionExample] db

Load a SHARC sample and create a wave table from it.
   [(Function from: (((SHARCInstrument fromDir: 'tuba') samples at: #c3) asWavetable: 1024)) edit: 1024] d

EventGenerators

A cluster is the simplest event generator.
   [(Cluster dur: 2.0
      pitchSet: #(48 50 52 54 56)
      ampl: 100
      voice: 1) open] d

Chord object can give you an event list.
   [((Chord majorTetradOn: 'f4' inversion: 0) duration: 1.0) open] d

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

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: 1)         "select from these voices"
      density: 5) eventList.   "play 5 notes per sec. and get the event list"
   c open] 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) open] d

Select notes frmo a given scale
   [(SelectionCloud dur: 4
      pitch: ((NeapolitanMinor root: N do) asPitchesInOctave: 4)
      ampl: #(80 40 120)
      voice: #(1)
      density: 12) open] d

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: #(1)
      density: 12) open] d

The extended DynamicSelectionCloud uses a multi-part pitch set of the format (time -> chord) (time -> chord) ... as in the following example.
   [ | score chords list |         "generate the tetrads from the selected scale; scramble the order"
   chords := ((NeapolitanMinor root: N do) generateChordsPoly: 4 inOctave: 2) scrambled.
   list := OrderedCollection new.
   1 to: 7 do:
      [ :ind |                              "shift every other one up 2 octaves"
      ind even ifTrue: [list add: ((ind - 1) * 3 -> ((chords at: ind) collect: [ :no | no + 24]))]
         ifFalse: [list add: ((ind - 1) * 3 -> (chords at: ind))]].
   score := (ExtDynamicSelectionCloud dur: 8   "now make a cloud from these"
      pitch: list
      ampl: 60
      voice: nil
      density: 10) eventList.
   score eventsDo: [ :ev |                     "plug in the properties for FM"
      ev inst: '/i1/pn'.
      ev modIndex: 2.0.
      ev ratio: 1.02.
      ev pos: 0.0].
   SirenSession eventList: 'EvGens/dsCloud1' put: score.
   score open] d

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

EventModifiers

One can apply functions to the properties of event lists, as in the following example, which creates a drum roll and applies a crescendo modifier to it.
   [ | roll decresc |
   roll := ((Roll length: 3000 rhythm: 150 note: 60) ampl: 120) eventList.
   decresc := Swell new function: (ExponentialFunction from: #((0 1 4) (1 0))).
   decresc applyTo: roll.
   roll open]

MIDI Control

Open MIDI, play notes based on the mouse position (x --> dur; 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 16-voice percussion ensemble.
   [MIDIPort setupTunedPercussion. MIDIPort testAScale] d

Reset the GM map
   [MIDIPort resetEnsemble]

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

ANO
   [MIDIPort allNotesOff]

Close down and clean up.
   [MIDIPort cleanUp]

Voices and I/O

   [CsoundVoice randomExampleToFileAndEdit]
   [SuperColliderVoice randomExampleToFileAndEdit]

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

These use the CSL OSC servers
   [OSCVoice fmExample1]
   [OSCVoice sndExample1]

These examples loop endlessly, so you have to interrupt or flush the scheduler to stop them
   [OSCVoice fmExample2.       5 wait.
   OSCVoice sndExample2.      5 wait.
   OSCVoice fmExample4]
   [EventScheduler flush]

As an example that mixes styles, the following expression plays a long low FM note and then uses Siren function objects to send continuous controls to make the note glissando down and pan from left to right.
   [OSCVoice fmExample3]

The Siren Graphics Framework

Display rectangles in a display list view -- test zoom and scroll.
   [DisplayList rectangleExample]

An alternative layout (which I prefer) places the zoom bars on the top and right. look at,
   [DisplayListView open4SquareOn: (DisplayList rectanglesX: 2000 byY: 2000)]

Display random strings
   [DisplayList stringExample]

Show the result of the IndentedListLayoutManager
   [DisplayListView colorClassListExample]
   [DisplayListView classTreeExample]

Music Notations

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

Try the picth-time layout
   [PitchTimeView randomExample] d
   [PitchTimeView openOnEventList: (EventList scaleExampleFrom: 48 to: 84 in: 5)] d

Open a pitch/time view on a *very long* 3-stream event list.
   [PitchTimeView randomExampleLong] d

A more complete example is Hauer-Steffens notation, which has a clef and staff lines as in common-practise notation.
   [HauerSteffensView randomExample] d
   [(EventList scaleExampleFrom: 40 to: 66 in: 5) edit] d

Sound Views

Create and view some example sounds
   [SoundView openOn: SampledSound sawtooth] d
   [(SampledSound sweepDur: 10.0 rate: 44100 from: 10 to: 400 chans: 1) edit] d

Read in a sound from a file
   [(SampledSound fromFile: 'unbelichtet.aiff') edit] d

Now try the external interface examples, then the SWIG Loris and CSL APIs.

Create a swept sine wave and take its fft.
   [Spectrum sweepExample display] d

Read a file (T'ang dynasty speech) and show the spectrogram
   [Spectrum fileExample display] db

Low-level sample processing: sum a sine and a sawtooth
   [ | sin saw |
   sin := SampledSound sineDur: 1.0 rate: 44100 freq: 10 chans: 1.
   saw := SampledSound sawtoothDur: 1.0 rate: 44100 freq: 100 chans: 1.
   sin scaleBy: 0.8.
   saw scaleBy: 0.1.
   1 to: sin size do:         "loop to do vector math on sound samples"
      [ :index |
      sin at: index put: ((sin at: index) + (saw at: index))].
   sin edit] d

As a final example, one can apply a function to a sound as an envelope, as in this block,

   [((SampledSound sineDur: 1.0 rate: 44100 freq: 220 chans: 1)
            scaledByFunction: (ExponentialFunction default)) edit] d

Using the Loris Interface and Tools

For normal usage, set-up with this
   [Siren.Loris.Loris initializeModule] d

Test with this (prints the Loris version string)
   [Siren.Loris.Loris version] p

To load a sound file and run it, saving the result as SDIF and opening a display (read this method to see how to use the Loris API)
   [LorisSound example: '1.2a1.aiff' resolution: 70] db
   [LorisSound fromSDIF: 'horns1c.sdif'] d

Using the CSL Models

Set-up
   [Siren.CSL.CSL initializeModule] d

First test; print a random number
   [Siren.CSL.CSL fRand    ] p

To oest a CSL graph; play a simple note
   [CSLGraph testSimpleGraph] d

Test FM
   [CSLGraph testFM]

Test processors: filtered noise
   [CSLGraph testStaticFilter]
   [CSLGraph testDynamicFilter]

Play a sound sample
   [CSLGraph testSoundFile]

Play 25 oscillators with random-walk freq and position.
   [CSLGraph testOscillatorBank]

If you have a MIDI fader box, try the following demo
   [CSLGraph testMIDIOscillatorBank]