"
Name: Siren
Comment: This is the Siren sound/music package version 7.2.
DevelopmentPrerequisites: #(#(#parcel 'DLLCC' '') #(#parcel 'ComposedTextEditor' ''))
HideSource: false
PackageName: Siren
Parcel: #('Siren7_2a')
ParcelName: Siren
PrerequisiteParcels: #(#('DLLCC' '') #('ComposedTextEditor' ''))
SaveSource: true
Date: 5:59:04 am December 23, 2003
"!
'From VisualWorks® NonCommercial, 7.2 of November 3, 2003 on December 23, 2003 at 5:59:04 am'!


(Dialog confirm: 'You are filing-in a Parcel source file!!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned.  Choose terminate or close.']!

Smalltalk defineNameSpace: #Siren
	private: false
	imports: '
			private Smalltalk.*
			'
	category: 'Music-Support'!

Smalltalk.Siren defineClass: #SirenUtility
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'oscPort defaultVoiceClass inChannels blockSize oscIP sampleRate outChannels '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Support'!

Siren.SirenUtility comment:
'The class SirenUtility represents the package-level state of the Siren Framework.

Its class methods answer a variety of Siren system variables such as default file directories and shared event list dictionaries.

the class is also the imlpementor of the Siren configuration panel, see the interface window spec, and the instance methods.

Class Variables:
	DataDir <String> the default sound/score directory.
	EventLists <Dictionary> Event lists
	FileSep <String> the platforms file name separator string
	ScoreDir <String> the score directory.
	SoundDir <String> the sound directory.
	Sounds <Dictionary> Sou nds
	Voices <Dictionary>  Voices
'!

Siren.SirenUtility class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #AbstractOSCPacket
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'oscBytes '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-OSC'!

Siren.AbstractOSCPacket class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventListTreeItem
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'lists '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.EventListTreeItem class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventListTreeEditor
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'scoreView listTree '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.EventListTreeEditor class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #LayoutManager
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'view orientation itemAccessor '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.LayoutManager comment:
'Instances of the LayoutManager classes are used to generate display lists given random data structures and possibly hints in terms of structure accessors.
This is David''s miraculous idea and the heart of the Navigator architecture.

Instance Variables:
	view		<DisplayListView> the view--May provide the displayItems
	orientation	<Symbol or nil> my ''base'' symbol (e.g., #top)
	itemAccessor	<StructureAccessor> my referencing protocol translator

Look for class references to the LayoutManager classes (mostly from DisplayListView classes), for lots of examples of the use of LayoutManagers.

This implementation is Topaz 0, STEIM, Amsterdam, May/June 1990.

The entirety of this software is Copyright (c) 1990, Stephen Travis Pope, Nomad Object Design/Nomad Computer Music Research. 
All Rights Reserved.'!

Siren.LayoutManager class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #OSCMessage
	superclass: #{Siren.AbstractOSCPacket}
	indexedType: #none
	private: false
	instanceVariableNames: 'address arguments '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-OSC'!

Siren.OSCMessage class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayListTracker
	superclass: #{UI.SelectionTracker}
	indexedType: #none
	private: false
	instanceVariableNames: 'inside '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayListViews'!

Siren.DisplayListTracker class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventListText
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.EventListText class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #AbstractEvent
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'properties '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Events'!

Siren.AbstractEvent comment:
'AbstractEvent is the base class in the event/event-list hierarchy (although it can be instantiated). Instances of AbstractEvent are objects that can be used as dictionaries or property lists. Their properties can be accessed either with at: and at:put: or by using the new property names themselves as selectors (e.g., (anAEventInstance at: #color put: #green) or (anAEventInstance color: #green)).

Instance variables:
	properties	 <Dictionary or nil> property list dictionary

The global dictionary called Events can be used for sharing event instances.
'!

Siren.AbstractEvent class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DurationEvent
	superclass: #{Siren.AbstractEvent}
	indexedType: #none
	private: false
	instanceVariableNames: 'duration '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Events'!

Siren.DurationEvent comment:
'Instances of DurationEvent are events that have special slots for their duration and voice properties.

Instance variables:
	duration <Duration species or nil> duration--a relative time
	voice <Voice, Symbol or Number> voice--a voice or key
'!

Siren.DurationEvent class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MusicEvent
	superclass: #{Siren.DurationEvent}
	indexedType: #none
	private: false
	instanceVariableNames: 'pitch loudness voice '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Events'!

Siren.MusicEvent comment:
'Instances of class MusicEvent are concrete musical note event objects used for eventLists and eventGenerators.

Instance variables:
	pitch <Pitch-species MusicMagnitude> the pitch/frequency
	loudness <Loudness-species MusicMagnitude> the loudness/amplitude
'!

Siren.MusicEvent class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #ActionEvent
	superclass: #{Siren.DurationEvent}
	indexedType: #objects
	private: false
	instanceVariableNames: 'action '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Events'!

Siren.ActionEvent comment:
'An instance of ActionEvent evaluates a Smalltalk block when scheduled.

Instance variables:
	action  <Block or Value> Something to do
'!

Siren.ActionEvent class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SoundFile
	superclass: #{Siren.AbstractEvent}
	indexedType: #objects
	private: false
	instanceVariableNames: 'name mode fileFormat sampleFormat rate channels size index position sound '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Sound'!

Siren.SoundFile comment:
'Instances of SoundFile are used for reading and writing sound objects to/from files.
They handle formatting, headers and I/O.
Currently implemented formats include the NeXT, SPARC, and BICSF/IRCAM soundfile formats. Special code is implemented for reading IRCAM headers with comments and max-amp records. Ths C-code for managing these sound files is part of cmix, available free from Princeton.edu.
Note that it might be cleaner to have subclasses for the different header types. I''m not sure where the trade-off is...

Instance variables:
	sound		<SampledSound> the sound''s samples
	rate		<Number> the sound''s sampling rate
	channels	<Number> the number of channels
	fileName	<String> the file''s name
	file		<ExternalStream> the file''s stream
	position	<Number> the current position
	headerSize	<Number> the size of the soundfile header
	properties	<Dictionary> the property list dictionary of the receiver
	format		<Symbol> the format--currently only #linear16Bit is handled
	size		<Number> the size in samples
'!

Siren.SoundFile class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #StructureAccessor
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'subject itemGenerator '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.StructureAccessor comment:
'Instances of StructureAccessor and its subclasses are ''protocol convertors'' which provide predictable interfaces (e.g., tree-speak), for diverse data structures.
The basic display item generating protocol is: 
	(aStructureAccessor itemFor: aModelNode).

InstanceVariableNames:
	subject			<Object> the model or victim
	itemGenerator   	<Symbol or Block> the DisplayItem generator
						Model items are passed to it (if it''s a Block),
						or have to perform: it (if it''s a Symbol).

'!

Siren.StructureAccessor class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #LibSndFileInterface
	superclass: #{External.ExternalInterface}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: '
			private Siren.LibSndFileInterfaceDictionary.*
			'
	category: 'MusicIO-External'
	attributes: #(
			#(#includeFiles #('sndfile_lite.h'))
			#(#includeDirectories #('DLLCC'))
			#(#libraryFiles #('sndfile_lite.dylib' 'libsndfile.a'))
			#(#libraryDirectories #('DLLCC' '/usr/local/lib'))
			#(#beVirtual false)
			#(#optimizationLevel #full))!

Siren.LibSndFileInterface class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayItem
	superclass: #{Graphics.DependentPart}
	indexedType: #none
	private: false
	instanceVariableNames: 'offset color '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayLists'!

Siren.DisplayItem comment:
'Instances of the subclasses of the abstract class DisplayItem are used as the items in display lists. They can generally display themselves on graphics contexts.
These are done this way because I don''t think wrappers are right for use in display lists.

Instance Variables:
	offset		<Point> the object''s offset relative to its container (display list)
	color		<ColorValue> the object''s display color (or nil)

The subclasses add special display-related state and behavior such as an extent point and display method, or a visual item such as a string or image to display.
They generally implement displayOn: aGraphicsContext and bounds accessing methods.

This implementation is MODE 1.1, STEIM, Amsterdam, May/June 1990; updated at the Lagoon in Palo Alto, July, 1991-May, 1992.

The entirety of this software is Copyright (c) 1990, Stephen Travis Pope, Nomad Object Design/Nomad Computer Music Research. 
All Rights Reserved.
'!

Siren.DisplayItem class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayLine
	superclass: #{Siren.DisplayItem}
	indexedType: #none
	private: false
	instanceVariableNames: 'width extent '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayLists'!

Siren.DisplayLine comment:
'Instances of DisplayLine are used for visual lines in display lists.

Instance Variables:
	width		<Number> the object''s line width
	extent		<Point> the object''s visual extent'!

Siren.DisplayLine class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayVisual
	superclass: #{Siren.DisplayItem}
	indexedType: #none
	private: false
	instanceVariableNames: 'visual '
	classInstanceVariableNames: ''
	imports: '
			MusicConstants
			'
	category: 'MusicUI-DisplayLists'!

Siren.DisplayVisual comment:
'Instances of DisplayVisual are used for displaying arbitrary visual objects (e.g., cached images) in display lists.

Instance Variables:
	visual		<VisualComponent> the object''s visual object, typically an image'!

Siren.DisplayVisual class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventModifier
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'selector function scale start stop index '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventModifiers'!

Siren.EventModifier class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #TimeSequenceLayoutManager
	superclass: #{Siren.LayoutManager}
	indexedType: #none
	private: false
	instanceVariableNames: 'timeScale timeOffset '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.TimeSequenceLayoutManager comment:
'Instances of the SequenceLayoutManager classes are used to generate display lists with "x = time" from event lists.

Instance Variables:
	timeScale		<Integer> the msec./pixel scale
	timeOffset		<Integer> the start of the layout in msec.
'!

Siren.TimeSequenceLayoutManager class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Peal
	superclass: #{Core.SequenceableCollection}
	indexedType: #objects
	private: false
	instanceVariableNames: 'base directions position finished '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Peal comment:
'Peals are repetitive note-generating sequences.  
This implementation was written by Mark Lentczner in 1986.

Instance variables:
	base <Collection of Numbers> the pitches i use
	directions <Array of +-1> the direction of the current sub-sequence
	position <Number> the current index
	finished <Boolean> an i done?'!

Siren.Peal class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PortMidiInterface
	superclass: #{External.ExternalInterface}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: '
			private Siren.PortMidiInterfaceDictionary.*
			'
	category: 'MusicIO-External'
	attributes: #(
			#(#includeFiles #('portmidi_lite.h'))
			#(#includeDirectories #('DLLCC'))
			#(#libraryFiles #('portmidi_lite.dylib' 'portmidi.a' 'CoreMIDI'))
			#(#libraryDirectories #('DLLCC' '/usr/local/lib' '/System/Library/Frameworks/CoreMIDI.framework/Versions/Current'))
			#(#beVirtual false)
			#(#optimizationLevel #full))!

Siren.PortMidiInterface class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #ScheduleRecord
	superclass: #{Core.Association}
	indexedType: #none
	private: false
	instanceVariableNames: 'next previous '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Support'!

Siren.ScheduleRecord comment:
'A ScheduleRecord is used as an entry in the SEventQueue. 
It''s a doubly-linked record with a key (the start time) and value (the scheduled event).

Instance Variables
	next <ScheduleRecord | nil> the next record in the Q (nil for the last)
	previous <ScheduleRecord | nil> the previous record in the Q (nil for the first)
	key <Integer> (inherited from LookupKey) the record''s start time in msec
	value <Event> (inherited from Association) the record''s event.'!

Siren.ScheduleRecord class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventListDialog
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.EventListDialog class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MusicMagnitude
	superclass: #{Core.Magnitude}
	indexedType: #none
	private: false
	instanceVariableNames: 'value '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.MusicMagnitude comment:
'Instances of the subclasses of MusicMagnitude are used to represent scalar magnitudes in musical objects. The class MusicMagnitude is a place for the music magnitudes to share their state and basic accessing behavior.

MusicMagnitudes generally implement precise mixed-mode arithmetic and comparison methods using a per-species generality table and auto-coercion within a species.

The"type abstraction" classes (Duration, Pitch, Amplitude, etc.) represent "what"; are never instantiated; their class creation methods answer instances of their species, but they manage coersion within the classes of their species. The "representational abstraction" classes (NominalMagnitude, OrdinalMagnitude, NumericalMagnitude, RatioMagnitude, etc.) represent "how"; they share value model semantics and coercion techniques. Note that the concrete implementation classes therefore answer their species by property model rather than by implementation subclass (i.e., HertzPitch species --> Pitch rather than NumericalMagnitude).

There are many examples in the implementation classes and the Siren  outline.

Instance variables:
	value		<Number or Symbol> the magnitude''s value
'!

Siren.MusicMagnitude class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #NumericalMagnitude
	superclass: #{Siren.MusicMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.NumericalMagnitude comment:
'Instances of the subclasses of NumericalMagnitude are music magnitudes with numerical values.
When the values are floating-point numbers, the range is often 0.0 to 1.0.
Integer ranges such as 0 to 127 can also be used in subclasses.

One may want to add range-checking for these cases.

Note that the class NumericalMagnitude is vacuous at present, and exists solely for representational modeling.'!

Siren.NumericalMagnitude class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MSecondDuration
	superclass: #{Siren.NumericalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.MSecondDuration comment:
'Instances of MSecondDuration are duration times in milli-seconds.
This is the default time representation (most general duration) and is usually used for keys in event lists.'!

Siren.MSecondDuration class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #OrdinalMagnitude
	superclass: #{Siren.MusicMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: 'table '
	classInstanceVariableNames: 'Table '
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.OrdinalMagnitude comment:
'Instances of the OrdinalMagnitude classes are order-only magnitudes.
They use the instance or class instance tables for holding comparative relationships among instances (e.g., mag1 might know that it''s > mag2).
The relation-setting (i.e., order assignment) messages are: ==, >>, <<, =<, and =>.
The query messages are: =?, > < <=, and >=.

Each subclass may decide whether instances or the class will hold the table of relationships.
The decision should be made on the basis of the expected number of magnitude instances and the sparseness of their relationships.

See the subclass'' class examples.

Instance Variable:
	table <Dictionary of OrdinalMagnitude -> Symbol> instance rel. table

Class Instance Variable:
	Table <Dictionary of OrdinalMagnitude -> Symbol> class rel. table of all instances
'!

Siren.OrdinalMagnitude class
	instanceVariableNames: 'Table '!

Smalltalk.Siren defineClass: #SecondDuration
	superclass: #{Siren.MSecondDuration}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.SecondDuration comment:
'Instances of SecondDuration are duration times in floating-point seconds.
This is among the most general duration time representations.'!

Siren.SecondDuration class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #IntervalMagnitude
	superclass: #{Siren.NumericalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: 'range '
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.IntervalMagnitude comment:
'Instances of subclasses of the abstract class IntervalMagnitude are NumericalMagnitudes where a specific interval is defined within which values are possible. Examples are MIDI values in the range of 0 .. 127.'!

Siren.IntervalMagnitude class
	instanceVariableNames: 'range '!

Smalltalk.Siren defineClass: #MusicModel
	superclass: #{Siren.MusicMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: 'generalityTable '
	imports: ''
	category: 'Music-Models-Representation'!

Siren.MusicModel comment:
'MusicModel adds the class instance variable generalityTable that is used for "abstract" music magnitude models such as Pitch and Amplitude.

See the class methods for generality.

Class inst Vars:
	generalityTable <Dictionary of (Class ->Integer)> The species generality table
'!

Siren.MusicModel class
	instanceVariableNames: 'generalityTable '!

Smalltalk.Siren defineClass: #Ergon
	superclass: #{Siren.MusicModel}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Ergon comment:
'Class Ergon is the abstract representational class for the amplitude/loudness/dynamic species.
Instances of its subclass species are used to model loudness values.'!

Siren.Ergon class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Amplitude
	superclass: #{Siren.Ergon}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Amplitude comment:
'Instances of classes whose species is Amplitude are used for the loudness parameters of musical events.
Amplitudes come in several flavors, as in the classes RatioMagnitude, MIDIVelocity and SymbolicLoudness.
The class Amplitude is abstract, its class creation method answers an instance of a concrete Amplitude/Loudness class.

Examples:
	Amplitude value: 0.77		"create a ratio instance - range 0.0 to 1.0 (cmusic)"
	Amplitude value: 77		"create an MIDI instance - range 0 to 127"
	Amplitude value: #mp		"create a symbolic instance - range #ppp to #fff"

See also the class example.'!

Siren.Amplitude class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #ConditionalDuration
	superclass: #{Siren.MusicMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.ConditionalDuration comment:
'Instances of ConditionalDuration are duration times where the value is a block.
The accessing protocol allows them to be spawned as co-processes in schedulers.
The valueAt: and waitUntil: methods allow flexible conditional scheduling and tests have shown that several dozen conditionals can be managed in real-time on modest hardware.

See the class examples.
'!

Siren.ConditionalDuration class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #NominalMagnitude
	superclass: #{Siren.MusicMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: 'NameMap '
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.NominalMagnitude comment:
'Instances of the NominalMagnitude classes are named (symbolic) properties where a symbol <--> value map is well-established for a given range and domain, e.g., 0.0 to 1.0 or 0 to 127.
Examples are pitch (#d4) or dynamic (#mp) names.

Class Instance Variable:
	NameMap	<Dictionary or Array of (Symbol -> Number or Interval)> the class'' look-up table'!

Siren.NominalMagnitude class
	instanceVariableNames: 'NameMap '!

Smalltalk.Siren defineClass: #SymbolicPitch
	superclass: #{Siren.NominalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: 'fracPitch '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.SymbolicPitch comment:
'Instances of SymbolicPitch represent well-tempered note names relative to a4=440Hz.
The range is c0 to g#9 and the values are symbols.
Note the confusion between the sharp sign (always placed after the note name) and Smalltalk''s symbol key #.
One often writes (''c#3'' asSymbol) to be safe.

Instance Variables:
	fracPitch <Float> the remainder for microtonal tunings

The class instance variable NameMap is used for the name <--> MIDI key number mapping array.
'!

Siren.SymbolicPitch class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SymbolicLoudness
	superclass: #{Siren.NominalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.SymbolicLoudness comment:
'Instances of SymbolicLoudness are dynamic names in the range #ppp to #fff.

The class instance variable NameMap is used for the name <--> MIDI velocity range (0 to 127) mapping dictionary.
'!

Siren.SymbolicLoudness class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #HertzPitch
	superclass: #{Siren.NumericalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.HertzPitch comment:
'Instances of HertzPitch are frequency values in Hertz.
This is the most general Pitch representation.
The value is assumed to be a floating-point number.'!

Siren.HertzPitch class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIPitch
	superclass: #{Siren.IntervalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.MIDIPitch comment:
'Instances of MIDIPitch are well-tempered piano key numbers where 60 is middle-c (c3) according to the MIDI standard.

The translation key <--> Hz. is done with the logarithm or 27.5 Hz. (very low A = 440/16).

Note that AdC''s additions make fractional MIDI pitches possible, whereby the first 2 digits to the right of the decimal point signify pitch cents. There are coercion methods such as asFracMIDI for handling microtonal MIDI pitches.
'!

Siren.MIDIPitch class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MeasureDuration
	superclass: #{Siren.NumericalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: 'timeSignature '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.MeasureDuration class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIDevice
	superclass: #{UI.Model}
	indexedType: #none
	private: false
	instanceVariableNames: 'port stream debug '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-MIDI'!

Siren.MIDIDevice comment:
'MIDIDevice is a subclass of Model whose instances are used to model MIDI input/output hardware devices.
The abstract class MIDIDevice implements the generic MIDI note on/off type events.
Subclasses of MIDIDevice exist for specific models of devices and  implement the device-specific (system exclusive) commands.
MIDIDevice are passed Events and channel numbers by their voices and generate commands as ByteArrays that they pass to MIDIPorts.
Several devices may share one port (if there are several MIDI-capable devices on one cable), and one voice may point to several device/channel pairs on one or more devices.

Instance variables:
	port			<MidiPort> the MIDIPort I use

Class variable:
	MStream	<WriteStream> the stream I dump by bytes on if my port is nil (good for debugging new subclasses)

Standard MIDI Commands:
		Note On = 9n kk vv
		Note Off = 8n kk vv
		Key Pressure = An kk vv
		Pitch Wheel = En ll ll
		After Touch Channel Pressure = Dn vv
		Control Change = Bn cc vv
		Program change = Cn pp

Relevant Constants for description of MIDI commands:
		n = 4-bit channel number
		kk = 7-bit key number
		vv = 7-bit key velocity
		ll = 7-bit low-order value
		hh = 7-bit high-order value
		cc = 7-bit control number
		pp = 7-bit program number'!

Siren.MIDIDevice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIFB01
	superclass: #{Siren.MIDIDevice}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-MIDI'!

Siren.MIDIFB01 comment:
'MidiFB01 is a type of MidiDevice for the system exclusive commands of the Yamaha FB-01 synthesiser.
It implements the special commands available here (like note commands with fractional pitch and given duration).

The standard FB01 note command is like:
		2n k f v d1 d2 = cmd:channel, noteNum, frac, vel, durLow, durHigh
'!

Siren.MIDIFB01 class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PField
	superclass: #{Siren.MusicMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: 'name field '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.PField comment:
'Instances of PField magnitudes are used for arbitrary parameters of musical events oriented towards formats with positional parameter declarations, e.g., Music-V-style notelist formats (see uses).

Instance variables:
	field		<Integer> field in the score
	name	<String or Symbol> the parameter''s name (optional)

Examples:
	PField field: 5 value: 0.7 name: #position
	PField field: 9 value: ''legato'' name: #expression
'!

Siren.PField class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #HierarchyLayoutManager
	superclass: #{Siren.LayoutManager}
	indexedType: #none
	private: false
	instanceVariableNames: 'length xStep yStep treeAccessor '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.HierarchyLayoutManager comment:
'Instances of the HierarchyLayoutManager classes are used to generate display lists from tree-like data and tree accessors.

Instance Variables:
	length		<Integer or Point> temp. depth used in layout
	xStep		<Integer> step size for x
	yStep		<Integer> step size for x
	treeAccessor	<TreeAccessor> the TreeAccessor used to access the model
'!

Siren.HierarchyLayoutManager class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #IndentedListLayoutManager
	superclass: #{Siren.HierarchyLayoutManager}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.IndentedListLayoutManager comment:
'Instances of IndentedListLayoutManager are used to generate display lists which mirror the hierarchy of their models (accessed using a TreeAccessor) in the indentation of a list-like outline.'!

Siren.IndentedListLayoutManager class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #TypedOSCMessage
	superclass: #{Siren.OSCMessage}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-OSC'!

Siren.TypedOSCMessage class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIPort
	superclass: #{Core.PositionableStream}
	indexedType: #none
	private: false
	instanceVariableNames: 'defaultInterface driver inputData '
	classInstanceVariableNames: 'useSingleton '
	imports: '
			private MIDICommands.*
			private MIDIioctls.*
			private GeneralMIDIMap.*
			private GeneralMIDIDrums.*
			'
	category: 'MusicIO-MIDI'!

Siren.MIDIPort comment:
'An instance of a subclass of MIDIPort is used for the interface betweeen Siren 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 Siren outline view.

There is typically only one instance of MIDIPort. The messages new, default, and instance all answer the sole instance. 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.

Instance Variables:
	readProcess <Process> The loop process to read input data.
	rData <OrderedCollection of (Integer -> ByteArray)> The available data.
	error <Value> The error value, or nil.
	debug <Booolean> if true, dump messages to transcript rather than to output.

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)
'!

Siren.MIDIPort class
	instanceVariableNames: 'useSingleton '!

Smalltalk.Siren defineClass: #PortMIDIPort
	superclass: #{Siren.MIDIPort}
	indexedType: #none
	private: false
	instanceVariableNames: 'isOn '
	classInstanceVariableNames: ''
	imports: '
			private MIDICommands.*
			private MIDIioctls.*
			private GeneralMIDIMap.*
			private GeneralMIDIDrums.*
			'
	category: 'MusicIO-MIDI'!

Siren.PortMIDIPort class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIDump
	superclass: #{UI.Model}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: '
			private MIDICommands.*
			'
	category: 'MusicIO-MIDI'!

Siren.MIDIDump comment:
'An instance of MIDIDump is used as a dependent of the MIDIPort to demonstrate the use of dependency for MIDI input. See the class example.
'!

Siren.MIDIDump class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Length
	superclass: #{Siren.OrdinalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.Length comment:
'Instances of Length represent subjective length (~ duration * loudness) values.
The instance variable tables are used for the name -> relation symbol map.

See the class examples.'!

Siren.Length class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventList
	superclass: #{Siren.MusicEvent}
	indexedType: #none
	private: false
	instanceVariableNames: 'events index startedAt '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Events'!

Siren.EventList comment:
'Instances of class EventList (subclass of DurationEvent), are used for holding onto multiple timed events. EventLists are events themselves and can have arbitrary properties as well as a collection of (relative start time -> event) associations.

Class EventList implements much collection-style protocol for event processing, as well as having special block application methods. All EventList algorithms are recurrsive in order to handle arbitrarily-deep hierarchical EventLists (possible because EventList is a subclass of DurationEvent). The class also has numerous powerful and flexible instance creation methods.

Instance Variables:
	events <SortedCollection of EventAssociations (Duration => MEvent)> 
				the list''s events, time/event associations sorted by relative start times
	index <Integer> the current position (used in scheduling)

Standard properties are #tempoScale for the tempo scaling factor (used by the CMN editors as the MS/whole note scale), and #code (#duration, #delta, or #startStop) to support different event coding schemes. The class also supports typed links to other event lists, e.g., "eList1 isTonicAnswerOf: eList2" or "eList1 hasPreviousVersion: eList2", which can be very useful properties in editors and browsers.

The global dictionary EventLists holds onto instances that can be referenced with their name symbols; these can have gensym-like names such as #temp.23. Creating a named EventList automatically adds it to the global dictionary. There are tools for browsing and maintaining this dictionary.
'!

Siren.EventList class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventGenerator
	superclass: #{Siren.EventList}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.EventGenerator comment:
'This class is the parent of most of the simple music-structure classes in this category.
An EventGenerator can create an EventList using its given parameters and a function (block) to use to create Event sequences.  See the subclasses for examples.
'!

Siren.EventGenerator class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Ostinato
	superclass: #{Siren.EventGenerator}
	indexedType: #none
	private: false
	instanceVariableNames: 'list playing process '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Ostinato comment:
'Ostinati can repeat their basic eventList without end, if necessary.

Instance variables:
	list <EventList> my riff
	playing <Boolean> am i on?
	process <Process> the process that''s playing me
'!

Siren.Ostinato class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Cloud
	superclass: #{Siren.EventGenerator}
	indexedType: #none
	private: false
	instanceVariableNames: 'density '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Cloud comment:
'Clouds are eventLists that are describe in terms of their contours or selection criteria.
This can be (e.g.,) POD-style specification of ranges for random selection, or selection from a given set of pitches.
See the class examples of my subclasses for description examples.

Instance variables:
	density		<Number> number of notes per second'!

Siren.Cloud class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DynamicCloud
	superclass: #{Siren.Cloud}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.DynamicCloud comment:
'Dynamic clouds are clouds that have starting and ending conditions (ranges or selection sets) and interpolate between them over their duration.'!

Siren.DynamicCloud class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayListController
	superclass: #{UI.ControllerWithMenu}
	indexedType: #none
	private: false
	instanceVariableNames: 'selection selOffset '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayListViews'!

Siren.DisplayListController comment:
'Instances of DisplayListController (or its subclasses) are used by DisplayListViews (or subclass instances) for menu management and selection.

By default, a simple YellowButton menu allows for group/inspect/redraw/zoom.
The RedButton is used for display item selection (by pointing or boxing-in).
LeftShift extends the selection, and dragging a selection moves it.

Instance Variables:
	selection		<DisplayList> the object''s selected items'!

Siren.DisplayListController class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #TimeSequenceController
	superclass: #{Siren.DisplayListController}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.TimeSequenceController class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PitchTimeController
	superclass: #{Siren.TimeSequenceController}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.PitchTimeController class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Function
	superclass: #{Siren.DurationEvent}
	indexedType: #objects
	private: false
	instanceVariableNames: 'data range domain '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Functions'!

Siren.Function comment:
'Instances of of Function and its subclasses represent abstractions of 1- or n-dimensional functions of 1 variable (e.g., time). 
Class Function is concrete and represents functions that are described by a array of data points assumed to lie equally-spaced in the unit interval.
Functions are normally created from an array of values over the unit interval; x varies from 0.0 to 1.0 and y is free over that range.
One can address them within the unit interval with atX: or one can address them with integer indeces up to the data set''s size with atIndex: (can be dangerous).

Examples are a ramp from 0 to 1 such as:
	[(LinearFunction from: #((0 0) (1 2))) at: 0.33]
or a spline that traces a sine-like path centered around 1:
	[(SplineFunction from: #((0 1) (0.33 2) (0.67 0) (1 1))) at: 0.35]

InstanceVariables:
	data <Array of Numbers, OrderedCollection of (Z)Points, or UninterpretedBytes>
				the data values or breakpoints of the function
	domain <Interval> the x-range
	range <Interval> the y-range

'!

Siren.Function class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #LinearFunction
	superclass: #{Siren.Function}
	indexedType: #objects
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Functions'!

Siren.LinearFunction comment:
'Instances of LinearFunction are line-segment functions of one free variable.

Example:
	LinearFunction from: #((0 0) (0.1 1) (0.2 0.6) (0.9 0.4) (1 0))
'!

Siren.LinearFunction class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Spectrum
	superclass: #{Siren.Function}
	indexedType: #objects
	private: false
	instanceVariableNames: 'sound window windowSize stepSize fft magReal '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Functions'!

Siren.Spectrum comment:
'Instances of Spectrum represent 3-D data functions derived from sound analysis.

Instance Variables:
	sound <StoredSound> the receiver''s sound
	window <Symbol>the window type id
	windowSize <Integer> the window size
	stepSize <Integer> the step between windows
	fft <FFT> the receiver''s FFT analyzer
'!

Siren.Spectrum class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Sound
	superclass: #{Siren.Function}
	indexedType: #objects
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: '
			private SoundConstants.*
			'
	category: 'Music-Sound'!

Siren.Sound comment:
'Instances of the subclasses of Sound are used to represent sound objects.
The abstract class Sound is vacuous.
Sounds use their Function and DurationEvent behaviors actively.
'!

Siren.Sound class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #ComponentSound
	superclass: #{Siren.Sound}
	indexedType: #objects
	private: false
	instanceVariableNames: 'start stop sound offset '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Sound'!

Siren.ComponentSound comment:
'A ComponentSound is used as a component (a "splice" element, if you will).
It represents another sound that "composes" the composite.

Instance Variables:
	sound	<StoredSound> the "subject" sound 
	start	<Integer> the starting sample in the composite
	stop		<Integer> the ending sample in the composite
'!

Siren.ComponentSound class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SplineFunction
	superclass: #{Siren.LinearFunction}
	indexedType: #objects
	private: false
	instanceVariableNames: 'linSeg '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Functions'!

Siren.SplineFunction comment:
'Instances of SplineSeg are cubic splines betwen their points.

Instance Variable:
	myForm		<Form> my cached for (used for value look-up)'!

Siren.SplineFunction class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #RatioMagnitude
	superclass: #{Siren.NumericalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: 'relative '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.RatioMagnitude comment:
'Instances of the RatioMagnitude subclasses are fractional values relative to some context-defined reference value--their relative. The relative may be provided, or it may be assumed if uninitialized (e.g., for a default whole note''s duration or default reference pitch).

Instance Variable:
	relative		<MusicMagnitude> the reference value'!

Siren.RatioMagnitude class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #RatioPitch
	superclass: #{Siren.RatioMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.RatioPitch comment:
'Instances of RatioPitch are fractional pitch values normally relative to c=261.623Hz.
They can be transformed into other values (e.g., note names or key numbers), but often at a loss of accuracy if they are not well-tempered.
'!

Siren.RatioPitch class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #RatioDuration
	superclass: #{Siren.RatioMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.RatioDuration comment:
'Instances of RatioDuration are ''beat'' fractions.
They can be expanded into msec. relative to some given event (a whole note), or use the default tempo of 1 sec.'!

Siren.RatioDuration class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #RatioLoudness
	superclass: #{Siren.RatioMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.RatioLoudness comment:
'Instances of RatioLoudness are fractional amplitude values normally relative to the range 0 to 1.
'!

Siren.RatioLoudness class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayListEditor
	superclass: #{UI.Model}
	indexedType: #none
	private: false
	instanceVariableNames: 'selection list '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayListViews'!

Siren.DisplayListEditor comment:
'An instance of DisplayListEditor is used as the intermediary model for viewing lists.
It handles selection and operation on the list.
It adds itself as a dependent of the list and echoes its change messages (i.e., the update: message says self changed).

Instance Variable:
	list			<DisplayList> the ''subject'' list model
	selection		<DisplayItem orDisplayLIst> the current selection or nil
'!

Siren.DisplayListEditor class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventListEditor
	superclass: #{Siren.DisplayListEditor}
	indexedType: #none
	private: false
	instanceVariableNames: 'eventList ostinato defaultEvent '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.EventListEditor comment:
'Instances of EventListEditor are used as the models of EventListViews.
They hold onto EventLists and send messages to them.

Instance variables:
	list 			<EventList> my EventList (subject)
	ostinato 	<Ostinato> my loop process (if i''m looping)
	selection 	<EventList> The "selected" notes
	ampl 		<Number or Amplitude> My default amplitude
'!

Siren.EventListEditor class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SoundCanvas
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'vZoom hZoom soundView scroller componentPart '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Sound'!

Siren.SoundCanvas class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #OSCPort
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'socket address '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-OSC'!

Siren.OSCPort class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SoundEditor
	superclass: #{UI.Model}
	indexedType: #none
	private: false
	instanceVariableNames: 'copyBuffer sound markers selection1 selection2 lastSelection buffer view changed '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Sound'!

Siren.SoundEditor class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #FFTWInterface
	superclass: #{External.ExternalInterface}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: '
			private Siren.FFTWInterfaceDictionary.*
			'
	category: 'MusicIO-External'
	attributes: #(
			#(#includeFiles #('fftw_lite.h'))
			#(#includeDirectories #('DLLCC'))
			#(#libraryFiles #('fftw_lite.dylib' 'libfftw3f.a'))
			#(#libraryDirectories #('DLLCC' '/usr/local/lib'))
			#(#beVirtual false)
			#(#optimizationLevel #full))!

Siren.FFTWInterface class
	instanceVariableNames: ''!

Smalltalk defineClass: #ZPoint
	superclass: #{Core.Point}
	indexedType: #none
	private: false
	instanceVariableNames: 'z '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Graphics-Geometry'!

ZPoint comment:
'ZPoints define a third instance variable (z) in addition to Point''s x and y to hold 3-dimensional point values.'!

Smalltalk.ZPoint class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIVelocity
	superclass: #{Siren.IntervalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.MIDIVelocity comment:
'Instances of MIDIVelocity are key velocities (approximately proportional to loudness) in the range 0 to 127.

The SymbolicLoudness class maps symbolic dynamic names onto this range on an approximately logarithmic scale.'!

Siren.MIDIVelocity class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayRectangle
	superclass: #{Siren.DisplayLine}
	indexedType: #none
	private: false
	instanceVariableNames: 'fill '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayLists'!

Siren.DisplayRectangle comment:
'Instances of DisplayRectangle can be used to display bordered or filled rectangles in display lists. This is a subclass of DisplayLine for reasons of practicality. a "purist" (e.g., David Liebs), would create an intermediate abstract class (e.g., BoundedDisplayItem) for both DisplayLine and DisplayRectangle.

Instanve Variables:
	fill		<Boolean or nil> whether or not to fill the receiver on display (no by default)'!

Siren.DisplayRectangle class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayPolyline
	superclass: #{Siren.DisplayRectangle}
	indexedType: #none
	private: false
	instanceVariableNames: 'vertices '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayLists'!

Siren.DisplayPolyline class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayListSubcanvas
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'displayList componentPart displayListView scroller vZoom hZoom '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayListViews'!

Siren.DisplayListSubcanvas class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SoundPort
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Sound'!

Siren.SoundPort comment:
'SoundPort instances are interfaces to real-time sound I/O streams.

Concrete subclasses add primitive interfaces to special devices such as audio ports or coprocessors.'!

Siren.SoundPort class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayString
	superclass: #{Siren.DisplayItem}
	indexedType: #none
	private: false
	instanceVariableNames: 'string font '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayLists'!

Siren.DisplayString comment:
'Instances of DisplayString are used for visual text items in display lists.

Instance Variables:
	string		<String or Text> the object''s string/text
	font		<ImplementationFont> the object''s display font'!

Siren.DisplayString class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayList
	superclass: #{Graphics.DependentComposite}
	indexedType: #none
	private: false
	instanceVariableNames: 'offset '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayLists'!

Siren.DisplayList comment:
'Instances of DisplayList are used for representing composed structured graphics. They can be nested (see the class examples) and can display themselves and their components.

Instance Variables:
	offset		<Point> the object''s offset distance relative to its container'!

Siren.DisplayList class
	instanceVariableNames: ''!

Smalltalk.Tools defineClass: #ListWorkBook
	superclass: #{Tools.Workbook}
	indexedType: #none
	private: false
	instanceVariableNames: 'textView topicTree pagelist '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Tools-Workspace'!

Tools.ListWorkBook class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIPF70
	superclass: #{Siren.MIDIDevice}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-MIDI'!

Siren.MIDIPF70 comment:
'MidiPF70 is a type of MidiDevice for the system exclusive commands of the Yamaha PF-70 electric piano.
It implements the special commands available here (like note commands with fractional pitch and given duration).

The standard PF70 note command is like:
		2n k f v d1 d2 = cmd:channel, noteNum, frac, vel, durLow, durHigh
'!

Siren.MIDIPF70 class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Swell
	superclass: #{Siren.EventModifier}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventModifiers'!

Siren.Swell class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DBLoudness
	superclass: #{Siren.RatioLoudness}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.DBLoudness comment:
'Instances of DBLoudness represent deciBel loudness values.
'!

Siren.DBLoudness class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventScheduler
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'clients appointments running doWait startTime '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Support'!

Siren.EventScheduler comment:
'An instance of EventScheduler is a simple driver for real-time message-passing among any classes that can return timed event association values.  One uses Schedules by setting up clients which a master scheduler process continuously evaluates for their next timed activations. Clients can return appointments which are sorted into the scheduler''s list of (time -> event) associations that is evaluated continuously when running. Changed messages are sent from the accessor messages to simplify EventScheduler browsers and inspectors.

Instance variables:
	clients 		<Collection> client objects
	appointments <SEventQueue> the schedule
	running 		<Boolean> running schedule?
	doWait		<Boolean> whether or not the scheduler should do real-time 
					scheduling--set to false if there is a lower level of scheduling 
					(e.g., primitive-level) or if output need not be real-time

Class variable:
	Schedule 	<EventScheduler> shared instance accessed by the class message masterSchedule

See the class examples.'!

Siren.EventScheduler class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PortAudioPort
	superclass: #{Siren.SoundPort}
	indexedType: #none
	private: false
	instanceVariableNames: 'rate format outChannels interface isOpen isRunning bufferSize '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Sound'!

Siren.PortAudioPort class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SmartPortAudioPort
	superclass: #{Siren.PortAudioPort}
	indexedType: #none
	private: false
	instanceVariableNames: 'inChannels ioSemaphore inClients outClients ioProcess inBuffer outBuffer '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Sound'!

Siren.SmartPortAudioPort class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PortAudioInterface
	superclass: #{External.ExternalInterface}
	indexedType: #none
	private: false
	instanceVariableNames: 'cbProcess ioSemaphore '
	classInstanceVariableNames: ''
	imports: '
			private Siren.PortAudioInterfaceDictionary.*
			'
	category: 'MusicIO-External'
	attributes: #(
			#(#includeFiles #('portaudio_lite.h'))
			#(#includeDirectories #('DLLCC'))
			#(#libraryFiles #('portaudio_lite.dylib' 'libportaudio.a'))
			#(#libraryDirectories #('DLLCC' '/usr/local/lib'))
			#(#beVirtual false)
			#(#optimizationLevel #full))!

Siren.PortAudioInterface class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PitchTimeLayoutManager
	superclass: #{Siren.TimeSequenceLayoutManager}
	indexedType: #none
	private: false
	instanceVariableNames: 'pitchScale pitchOffset '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.PitchTimeLayoutManager comment:
'Instances of the PitchTimeLayoutManager classes are used to generate display lists with "x = time" and "y = pitch" from event lists.
The default layout is linear pitch-to-y, like piano-roll or Hauer-Steffens notation.

Instance Variables:
	pitchScale		<Integer> the key#/pixel scale
	pitchOffset		<Integer> the bottom of the layout as a key#
'!

Siren.PitchTimeLayoutManager class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PositionTimeLayoutManager
	superclass: #{Siren.PitchTimeLayoutManager}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.PositionTimeLayoutManager comment:
'Instances of PositionTimeLayoutManager are used to generate display lists with "x = time" and "y = position" from event lists.
'!

Siren.PositionTimeLayoutManager class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventListPanel
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'editorView scoreView textView '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.EventListPanel class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIPacket
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'length time flags duration data '
	classInstanceVariableNames: ''
	imports: '
			private MIDICommands.*
			'
	category: 'MusicIO-MIDI'!

Siren.MIDIPacket comment:
'A MIDIPacket represents a simple MIDI datagram with a time-stamp and a data array.

Instance Variables
	data <ByteArray> The MIDI data bytes (may include a MIDI command and running status messages)
	length <Integer> The number of meaningful bytes in the data
	time <Integer> The msec timestamp when the packet was created or received.
	flags <Integer> Any ''flags'' received from the MIDI driver.

Pool dictionaries:
	MIDICommands <Dictionary of (command symbol -> octet)> e.g., (#noteOn -> 16r90)
		(It is initialized by class MIDIPort.)

See the class comment in MIDIPort for details.
'!

Siren.MIDIPacket class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Cluster
	superclass: #{Siren.EventGenerator}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Cluster comment:
'Cluster is an abstraction of a set of simultaneous (or same-pitch) events.
A cluster need only have a set of pitches, or a rhythm.
'!

Siren.Cluster class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Chord
	superclass: #{Siren.Cluster}
	indexedType: #none
	private: false
	instanceVariableNames: 'root inversion type arity '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Chord comment:
'Instances of Chord are eventLists that can be created by giving them a root and inversion. They can return eventLists.

Instance variables:
	root <Pitch> the root of the chord
	inversion <Integer> the inversion level (unused at present)'!

Siren.Chord class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Roll
	superclass: #{Siren.Cluster}
	indexedType: #none
	private: false
	instanceVariableNames: 'number delta noteDuration '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Roll comment:
'Rolls repeat their single events.
It will eventually be possible to apply pitch, amplitude or duration envelopes to them as well.

Instance variables:
	number			<Integer> how many notes to play
	delta			<Number> delta time between events
	noteDuration	<Number> duration of events'!

Siren.Roll class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Trill
	superclass: #{Siren.Roll}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Trill comment:
'A trill is like a roll, except that it can be given an eventList for repetition.'!

Siren.Trill class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Arpeggio
	superclass: #{Siren.Chord}
	indexedType: #none
	private: false
	instanceVariableNames: 'delay '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.Arpeggio comment:
'Arpeggii can be created on Chords or other event lists and can step through their events (assumed to be simultaneous at the start) with the given delay time.

Instance variables:
	delay <Duration or Number> the delay betyween the onsets of my events'!

Siren.Arpeggio class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Sharpness
	superclass: #{Siren.OrdinalMagnitude}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Implementation'!

Siren.Sharpness comment:
'Instances of Sharpness represent subjective sharpness (~ pitch * loudness) values.
The instance variable tables are used for the name -> relation symbol map.

See the class examples.'!

Siren.Sharpness class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #FourierSummation
	superclass: #{Siren.Function}
	indexedType: #objects
	private: false
	instanceVariableNames: 'myForm myArray '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Functions'!

Siren.FourierSummation comment:
'Instances of FourierSummation are functions that interpret their points as 3-element arrays (harmonic, amplitude, phase), and sum sine waves into their data array based on the fourier summation of these components.

Instance variables:
	myForm		<Form> my function plot--default size = 1024@180
	myArray		<Array of Floats> my value array--default length = 1024

Example:
	| fcn |
	fcn := 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)).
	Transcript show: (fcn at: 0.14) printString; cr.
'!

Siren.FourierSummation class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SEventQueue
	superclass: #{Core.SequenceableCollection}
	indexedType: #none
	private: false
	instanceVariableNames: 'first last '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Support'!

Siren.SEventQueue comment:
'An SEventQueue is a doubly-linked list that has methods for inserting elements in time-sorted order.
These are used for real-time schedules because they can have faster search methods than using SortedCollections of OrderedCollections for the EventScheduler.
The current implementation uses a simple linear search. For really big schedules, this could be made faster with a binary search or tree-based schedule.

Instance Variables:
	first <ScheduleRecord> The first event in the Q
	last <ScheduleRecord> The last event in the Q'!

Siren.SEventQueue class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DisplayListView
	superclass: #{UI.AutoScrollingView}
	indexedType: #none
	private: false
	instanceVariableNames: 'displayList pixmap background page zoom grid backgroundColor foregroundColor redrawn cache '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-DisplayListViews'!

Siren.DisplayListView comment:
'DisplayListViews can display and scroll structured graphics display lists generated by various models or layout managers.
Using their page offset, they can scroll over very large display lists.
They can have colored or gridded background pixmaps and can scroll/page on demand; they display their lists on their graphics contexts.

Subclasses generally override the displayOn: or displayOnImage methods, and add initialization or transformation methods.

Instance Variables
	displayList	<DisplayList> the view''s display list
	pixmap			<Pixmap or nil> the view''s cached display pixel map (optional)
	background		<Pixmap or nil> the view''s background form (e.g., gridding)
	zoom			<Point or nil> the display list''s zoom-in factor or nil
	pageOffset		<Point> the offset in "pages" used for very large display lists
	backgroundColor	<ColorValue> graphics background color
	foregroundColor	<ColorValue> graphics display color
	redrawn		<nil or not> set to nil to re-draw cache

See the class examples for numerous ways of using display list views.
The subclasses add knowledge of smart display list generation, background pixmap generation (e.g., gridding), display of item or x/y-range selection, x- or y-scaling or step/grid, property->color mapping, "clef forms" or other special pixmaps, multiple-model viewing, etc.
'!

Siren.DisplayListView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #TimeSequenceView
	superclass: #{Siren.DisplayListView}
	indexedType: #none
	private: false
	instanceVariableNames: 'clefForm xScaleDefault xScale xColor yColor headColor clefColor itemAccessors '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.TimeSequenceView comment:
'Instances of SequenceView (and its subclasses) are time-line oriented DisplayListViews.
Time may run horizontally (default) or vertically, depending on the layout manager.

Instance Variables:
	clefForm	<Form> the view''s clef form or axes (optional), may be staff or scale/grid/ruler
	xScale	<Number> the time-to-x-coordinate scale
	xColor	<ColorValue> the color of the lines for note duration
	yColor	<ColorValue> the color of the lines for note amplitude
	headColor	<ColorValue> the note head color
	clefColor	<ColorValue> the clef color
	itemAccessors	<Symbol or Dictionary of (Voice -> Symbol)> the item accessor(s) to use to generate the display list items for the notes

This implementation is MODE 1.1, STEIM, Amsterdam, May/June 1990; updated at the Lagoon in Palo Alto, July, 1991-May, 1992.

The entirety of this software is Copyright (c) 1990, Stephen Travis Pope, Nomad Object Design/Nomad Computer Music Research. 
All Rights Reserved.'!

Siren.TimeSequenceView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PitchTimeView
	superclass: #{Siren.TimeSequenceView}
	indexedType: #none
	private: false
	instanceVariableNames: 'pitchOffset yScale yScaleDefault '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.PitchTimeView comment:
'Instances of PitchTimeView (and its subclasses) are time-line oriented DisplayListViews which make the assumption that the y-coordinate is related to pitch.
The relationship may be a function (as in piano-roll and Hauer-Steffens notation), or involve a look-up table (as in CMN).

Instance Variables:
	pitchOffset	<Number> the view''s lowest pitch (normally a key number)'!

Siren.PitchTimeView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SoundView
	superclass: #{Siren.DisplayListView}
	indexedType: #none
	private: false
	instanceVariableNames: 'step scale '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Sound'!

Siren.SoundView comment:
'Instances of SoundView are used for displaying samples sound objects in R4.
They can do horizontal scrolling and scaling.

Instance variables:
	displayContents	<GraphicsContext> the cached display
	marker1, 2		<Number> the offset into the sound in samples for 2 markers
	step				<Number> the step size for displaying--show every n''th sample
	scale			<Number> the vertical scale for multiplying the sample''s value
'!

Siren.SoundView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PositionTimeView
	superclass: #{Siren.PitchTimeView}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.PositionTimeView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #HauerSteffensView
	superclass: #{Siren.PitchTimeView}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.HauerSteffensView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #FunctionView
	superclass: #{Siren.DisplayListView}
	indexedType: #none
	private: false
	instanceVariableNames: 'models aScale vRange hRange colors width '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Functions'!

Siren.FunctionView comment:
'Instances of class FunctionView are views on breakpoint or sampled data functions.
They can display up to 4 model functions on a fixed-size non-scrolling field.

Instance variables:
	models		<Array of 1 to 4 Functions> the function array
	aScale		<Number> the y-scale of the view
	vRange		<Number> the vertical range, normally = aScale
	hRange		<Number> the horizontal range
	colors 	<Array of 1 to 4 colorValues> the colors for the 4 functions'!

Siren.FunctionView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #EventAssociation
	superclass: #{Core.Association}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Support'!

Siren.EventAssociation class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #PhraseView
	superclass: #{Siren.TimeSequenceView}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Siren.PhraseView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #OSCBundle
	superclass: #{Siren.AbstractOSCPacket}
	indexedType: #none
	private: false
	instanceVariableNames: 'messages time '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-OSC'!

Siren.OSCBundle class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #IndentedTreeLayoutManager
	superclass: #{Siren.IndentedListLayoutManager}
	indexedType: #none
	private: false
	instanceVariableNames: 'list '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.IndentedTreeLayoutManager comment:
'Instances of IndentedTreeLayoutManager are used to generate display lists which mirror the hierarchy of their models (accessed using a TreeAccessor) in the indentation of a list-like tree.'!

Siren.IndentedTreeLayoutManager class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Chronos
	superclass: #{Siren.MusicModel}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Chronos comment:
'Class Chronos is the abstract representational class for the duration and meter species.
Instances of its subclass species are used to model times, durations and metronomes.'!

Siren.Chronos class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Meter
	superclass: #{Siren.Chronos}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Meter comment:
'Instances of the Meter species model the tempo or metronome used to map durations.
This class can be used as a concrete one (adding a few methods to fill it out), or like the other representational classes (making concrete classes of this species).
In the later case, the value instance variable could hold a number, process or block.'!

Siren.Meter class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Duration
	superclass: #{Siren.Chronos}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Duration comment:
'Instances of classes whose species is Duration are used for the duration parameters of musical events.
Durations come in several flavors, as in the classes RatioDuration, MSecondDuration and ConditionalDuration.
The class Duration is abstract, its class creation method answers an instance of a concrete Duration class.

Examples:
	Duration value: 0.77		"create an instance with seconds as the unit"
	Duration value: 770		"create an instance with milliseconds as the unit"
	Duration value: 1/4		"create an instance with beats as the unit"
	Duration value: [ :x | x > 4]	"create an instance for: ''until x > 4''"

See also the class example.'!

Siren.Duration class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Chroma
	superclass: #{Siren.MusicModel}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Chroma comment:
'Class Chroma is the abstract representational class for the pitch and mode-element species.
Instances of its subclass species are used to model pitches, gamut members, and frequencies.'!

Siren.Chroma class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Pitch
	superclass: #{Siren.Chroma}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Pitch comment:
'Instances of classes whose species is Pitch are used for the pitch or frequency parameters of musical events.
Pitches come in several flavors, as in the classes HertzPitch, RatioPitch, MIDIPitch and SymbolicPitch.
The class Pitch is abstract, its class creation method answers an instance of a concrete Pitch class.

Examples:
	Pitch value: 440.0 	"create an instance with units of Hertz"
	Pitch value: 77		"create an instance with units of MIDI key numbers"
	Pitch value: #e4		"create a symbolic instance"
	Pitch value: ''e4''		"same as using a symbol"
	Pitch value: 4/3		"create a ratio instance"

Note that new pitch representations such as music11-like pch (4.11 = 11th note in oct 4) or oct (4.1100 = oct4 + 1100 cts) notations can be added by overriding the float-to-Hz or float-to-midi conversions.

See also the class example.'!

Siren.Pitch class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #ModeMember
	superclass: #{Siren.Chroma}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.ModeMember comment:
'Instances of the ModeMember species model pitches as elements of a mode (e.g., minor) or gamut (e.g., pentatonic on F).
This class can be used as a concrete one (adding a few methods to fill it out), or like the other representational classes (making concrete classes of this species).
In the later case, the value instance variable could hold a number or pitch, and the mode or gamut could be shared.'!

Siren.ModeMember class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SoundController
	superclass: #{UI.ControllerWithMenu}
	indexedType: #none
	private: false
	instanceVariableNames: 'selection '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Sound'!

Siren.SoundController comment:
'Instances of SoundController are used with SoundViews and perform horizontal scrolling and menu operations'!

Siren.SoundController class
	instanceVariableNames: ''!

Smalltalk defineClass: #ScoreView
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Editors'!

Smalltalk.ScoreView class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #IOVoice
	superclass: #{UI.Model}
	indexedType: #none
	private: false
	instanceVariableNames: 'name instrument stream '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.IOVoice comment:
'Subclasses of Voice implement the mappings between symbolic event parameters (like timbre descriptions and pitch names) and concrete output event data.
They can be used for generating sound compiler notelists or (real-time output or file dumps of) MIDI data.

The instance variables are:
	instrument <Symbol, association or device> my instrument
	name 		<String or Symbol> my handle or name
	stream		<Stream> my (optional) I/O stream

The global object Voices is a dictionary that note events can refer to with integer or symbolic keys.
'!

Siren.IOVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #NotelistVoice
	superclass: #{Siren.IOVoice}
	indexedType: #none
	private: false
	instanceVariableNames: 'parameterMap lastTime header '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.NotelistVoice comment:
'Instances of the subclasses of NotelistVoice can read or write note list file streams in various formats for software sound synthesis packages such as cmusic, csound, or cmix.

Note that we are output-only at present.

Instance Variables:
	parameterMap		<OrderedCollection of Symbols, Blocks, or Associations> 
			the object''s P:=map, used to print out or parse note commands.

'!

Siren.NotelistVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #CmixVoice
	superclass: #{Siren.NotelistVoice}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.CmixVoice comment:
'Missing comment'!

Siren.CmixVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIVoice
	superclass: #{Siren.IOVoice}
	indexedType: #none
	private: false
	instanceVariableNames: 'currentTime '
	classInstanceVariableNames: 'default '
	imports: ''
	category: 'MusicIO-Voices'!

Siren.MIDIVoice comment:
'A MidiVoice is used to play note events onto one or more output devices.
I store a MidiDevice (with a MIDIPort) in my instrument variable and play events onto it when asked to.

Instance variables:
	instrument	<MIDIDevice> my instrument, a MidiDevice
'!

Siren.MIDIVoice class
	instanceVariableNames: 'default '!

Smalltalk.Siren defineClass: #SoundVoice
	superclass: #{Siren.IOVoice}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.SoundVoice comment:
'Missing comment'!

Siren.SoundVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SuperColliderVoice
	superclass: #{Siren.NotelistVoice}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.SuperColliderVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #OSCVoice
	superclass: #{Siren.NotelistVoice}
	indexedType: #objects
	private: false
	instanceVariableNames: 'port '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-OSC'!

Siren.OSCVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #CsoundVoice
	superclass: #{Siren.NotelistVoice}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.CsoundVoice comment:
'Missing comment'!

Siren.CsoundVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #MIDIFileVoice
	superclass: #{Siren.IOVoice}
	indexedType: #none
	private: false
	instanceVariableNames: 'fileType tracks ppq tempo '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.MIDIFileVoice comment:
'Missing comment'!

Siren.MIDIFileVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Rubato
	superclass: #{Siren.EventModifier}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventModifiers'!

Siren.Rubato class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #OSCByteConvertor
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'packetData '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-OSC'!

Siren.OSCByteConvertor class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SelectionCloud
	superclass: #{Siren.Cloud}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.SelectionCloud comment:
'Selection clouds are created with a set of pitch and amplitude values and return eventLists selected from these values.'!

Siren.SelectionCloud class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #DynamicSelectionCloud
	superclass: #{Siren.SelectionCloud}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-EventGenerators'!

Siren.DynamicSelectionCloud comment:
'Dynamic selection clouds are described by their beginning and ending pitch and amplitude sets and interpolate between them.'!

Siren.DynamicSelectionCloud class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #STreeAccessor
	superclass: #{Siren.StructureAccessor}
	indexedType: #none
	private: false
	instanceVariableNames: 'terminalQuery childAccessor '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Layout'!

Siren.STreeAccessor comment:
'Instances of STreeAccessor tree-navigation interfaces to diverse data structures.
The basic display tree walking protocol is: 
	(aSTreeAccessor childrenOf: aModelNode); and
	(aSTreeAccessor isTerminal: aModelNode).

InstanceVariableNames:
	terminalQuery	<Symbol or Block> the isLeaf or hasChildrenNot query
	childAccessor		<Symbol or Block> accessor to get a node''s children
						Model items are passed to these (if they''re Blocks),
						or have to perform: them (if they''re Symbols).

'!

Siren.STreeAccessor class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #ExponentialFunction
	superclass: #{Siren.LinearFunction}
	indexedType: #objects
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Functions'!

Siren.ExponentialFunction comment:
'Instances of ExpSeg are exponential-segment functions of one free variable.
They use ZPoints for x|y|exponent. The exponent determines the speed of the exponential/logarithmic transition between breakpoint values. A value of 0 leads to linear interpolation.

Example:
	ExponentialFunction from: #((0 0 -5) (0.2 1 -3) (0.8 0.5 -2) (1 0))
'!

Siren.ExponentialFunction class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #CmusicVoice
	superclass: #{Siren.NotelistVoice}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicIO-Voices'!

Siren.CmusicVoice comment:
'Missing comment'!

Siren.CmusicVoice class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Positus
	superclass: #{Siren.MusicModel}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Positus comment:
'Class Positus is the abstract representational class for the position, space, and direction species.
Instances of its subclass species are used to model spatial and positional values.'!

Siren.Positus class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Spatialization
	superclass: #{Siren.Positus}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Spatialization comment:
'Instances of the Spatialization species model the characteristics and configuration of room simulations in scores.
This class can be used as a concrete one (adding a few methods to fill it out), or like the other representational classes (making concrete classes of this species).
In the later case, the value instance variable would hold a record with the geometry or the room, the positions of default sources, and the listener''s position and features.'!

Siren.Spatialization class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Directionality
	superclass: #{Siren.Positus}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Directionality comment:
'Instances of the Directionality species model the radiation characteristics of sound sources in room simulations.
This class can be used as a concrete one (adding a few methods to fill it out), or like the other representational classes (making concrete classes of this species).
In the later case, the value instance variable would hold a 1- or 2-dimensional position as a number or point.'!

Siren.Directionality class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #Position
	superclass: #{Siren.Positus}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Models-Representation'!

Siren.Position comment:
'Instances of the Position species model the position of sound sources in room simulations or scores.
This class can be used as a concrete one (adding a few methods to fill it out), or like the other representational classes (making concrete classes of this species).
In the later case, the value instance variable could hold a number or point.'!

Siren.Position class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #SampledSound
	superclass: #{Siren.Sound}
	indexedType: #objects
	private: false
	instanceVariableNames: 'name rate format channels samplesInMemory firstIndex '
	classInstanceVariableNames: ''
	imports: '
			private SoundConstants.*
			'
	category: 'Music-Sound'!

Siren.SampledSound comment:
'Instances of StoredSound represent digitally-sampled sound objects. 
Their "samples" are stored in the "data" instance variable inherited from Function.
They can be read/written to/from files (using SoundFile objects), displayed (in SoundViews) and played (via SoundPorts).
The class StoredSound is concrete and assumes 16-bit linear encoding of samples; there are subclasses for floating-point and 8-bit Mu-law sample types.
There are useful class methods for creating a number of standard sounds such as silence, impulses, swept sine waves, etc.
Note that StoredSound uses the ''range'' instance variable inherited from Function as its (integer) size.
Instance variables:
	samplesInMemory	<Integer> The number of samples held in memory (may be smaller than 
			size for very large sounds)
	firstIndex	<Integer> The sample index of the start of the in-memory samples  (may be non-zero 
			for very large "paged" sounds)
	changed	<Boolean> whether or not the im-memory samples have been changed (and will need 
			to be written out before another page is read in)

Important Properties:
	cueList	<OrderedCollection of (name -> interval)> the names sub-regions in the sound.

Other properties, such as the sound''s (optional) file name, its sample rate, or the number of channels, are stored in the property list dictionary that is inherited by virtue of being an event subclass.

KNOWN BUGS:

Note that not all of this class is finished--there are methods that call non-existent user primitives and have no Smalltalk implementations (like the heavy DSP).

'!

Siren.SampledSound class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #FloatSound
	superclass: #{Siren.SampledSound}
	indexedType: #objects
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Sound'!

Siren.FloatSound comment:
'Instances of FloatSound are used for sounds with 32-bit floating-point numbers as samples.
There are behaviors for mapping into other formats.'!

Siren.FloatSound class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #VirtualSound
	superclass: #{Siren.SampledSound}
	indexedType: #objects
	private: false
	instanceVariableNames: 'source '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Sound'!

Siren.VirtualSound comment:
'An instance of VirtualSound is a "reference" to another sound, typically by a named cue list entry.

Instance Variables:
	source	<StoredSound> the sound to which the VirtualSound points'!

Siren.VirtualSound class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #CompositeSound
	superclass: #{Siren.VirtualSound}
	indexedType: #objects
	private: false
	instanceVariableNames: 'components '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Sound'!

Siren.CompositeSound comment:
'An instance of CompositeSound can be used to represent a sound constructed by "splicing together" sections of other sounds.
It uses its components collection to maintain sounds and sample ranges that have been pasted together.
It responds to sampleAt: and sampleAt:put: just like other sounds, but stores only its components list on files unless explicitly told to store samples.

Instance Variables:
	components	<SortedCollection of ComponentSounds> the "sub-sounds" of the CompositeSound'!

Siren.CompositeSound class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #GapSound
	superclass: #{Siren.VirtualSound}
	indexedType: #objects
	private: false
	instanceVariableNames: 'cutList '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Music-Sound'!

Siren.GapSound comment:
'An instance of GapSound can be used to represent a sound with samples deleted from it (i.e., a gap).
It uses its cut list instance variable to maintain sample ranges that have been deleted from the sound that comprises it (the source).
It responds to sampleAt: and sampleAt:put: just like other sounds, but stores only its cut list on files unless explicitly told to store samples.

Instance Variables:
	source		<StoredSound> the primary source sound
	cutList		<SortedCollection of Integers> the list of deleted sections by sample index i.e., pointers denoting sections that have been deleted. For example, having a cutList of (1000 2000) means that samples (1000, 2000] have been virtually cut.
'!

Siren.GapSound class
	instanceVariableNames: ''!

Smalltalk.Siren defineClass: #FunctionController
	superclass: #{Siren.DisplayListController}
	indexedType: #none
	private: false
	instanceVariableNames: 'modelIndex '
	classInstanceVariableNames: ''
	imports: ''
	category: 'MusicUI-Functions'!

Siren.FunctionController comment:
'Instances of FunctionController are the controllers used within FunctionViews.
The allow click-and-drag breakpoint function editing.

Instance Variables
	modelIndex		<Integer 1-4> the index into the view''s array of functions.'!

Siren.FunctionController class
	instanceVariableNames: ''!

Siren.OSCBundle defineSharedVariable: #MillisecToNTP
	private: false
	constant: false
	category: 'Time constants'
	initializer: '((2 raisedTo: 32) / 1000)'!

Siren.OSCBundle defineSharedVariable: #MillisecScale
	private: false
	constant: false
	category: 'Time constants'
	initializer: '4295000'!

Siren.OSCBundle defineSharedVariable: #SecsInAYear
	private: false
	constant: false
	category: 'Time constants'
	initializer: '(60 * 60 * 24 * 365)'!

Siren.MIDIDevice defineSharedVariable: #MStream
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.IOVoice defineSharedVariable: #DefaultVoiceClass
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.PortAudioInterface defineSharedVariable: #PA_Constants
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.MIDIPort defineSharedVariable: #Instance
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.MIDIPort defineSharedVariable: #UseSingleton
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.MIDIPort defineSharedVariable: #Debug
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.OSCPort defineSharedVariable: #DefaultIP
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.OSCPort defineSharedVariable: #DefaultPort
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SoundPort defineSharedVariable: #DefaultOutChannels
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SoundPort defineSharedVariable: #DefaultSampleRate
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SoundPort defineSharedVariable: #DefaultBlockSize
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SoundPort defineSharedVariable: #DefaultInChannels
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.MIDIVoice defineSharedVariable: #Instance
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.MIDIVoice defineSharedVariable: #UseSingleton
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SampledSound defineSharedVariable: #MaxIntSample
	private: false
	constant: false
	category: 'class constants'
	initializer: nil!

Siren.SampledSound defineSharedVariable: #MinIntSample
	private: false
	constant: false
	category: 'class constants'
	initializer: nil!

Siren.SampledSound defineSharedVariable: #MagicNumbers
	private: false
	constant: false
	category: 'class constants'
	initializer: nil!

Siren.SoundFile defineSharedVariable: #FileFormats
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SoundFile defineSharedVariable: #Interface
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SoundFile defineSharedVariable: #SampleFormats
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.MusicMagnitude defineSharedVariable: #TypeClasses
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.MusicMagnitude defineSharedVariable: #TypeCodes
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.EventScheduler defineSharedVariable: #Schedule
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SirenUtility defineSharedVariable: #Sounds
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SirenUtility defineSharedVariable: #ScoreDir
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SirenUtility defineSharedVariable: #SoundDir
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SirenUtility defineSharedVariable: #FileSep
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SirenUtility defineSharedVariable: #EventLists
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SirenUtility defineSharedVariable: #Voices
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.SirenUtility defineSharedVariable: #DataDir
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.DisplayListView defineSharedVariable: #MusicConstants
	private: false
	constant: false
	category: 'class var accessing'
	initializer: 'IdentityDictionary new'!

Siren.LibSndFileInterface defineSharedVariable: #SF_Constants
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.PortMIDIPort defineSharedVariable: #Streams
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

Siren.PortMIDIPort defineSharedVariable: #Devices
	private: false
	constant: false
	category: 'As yet unclassified'
	initializer: nil!

!Siren.SirenUtility methodsFor: 'actions'!

cleanUp

	MIDIPort cleanUp!

freeLists

	SirenUtility flushTempEventLists!

resetSound
	"Reset the parameters of the sound port"

	SoundPort initialize.
	blockSize value: SoundPort blockSize.
	inChannels value: SoundPort inChannels.
	outChannels value: SoundPort outChannels.
	sampleRate value: SoundPort sampleRate.!

stopMIDI
	"Turn off MIDI"

	MIDIPort default allNotesOff!

stopSound

	"This stub method was generated by UIDefiner"

	SoundPort default stop; close!

testMIDIin

	MIDIPort testInput!

testMIDIout

	| lis sel |
	sel := (Menu labelArray: #('single note' 'scale' 'dense chaos')) startUp.
	sel == 0 ifTrue: [^self].
	sel == 1 ifTrue: [^MIDIPort testANote].
	sel == 2 ifTrue: [^(EventList scaleExampleFrom: 48 to: 72 in: 1500) playOn: MIDIVoice default].
	lis := EventList newAnonymous.
	2 timesRepeat: 
		[lis addAll: ((DynamicCloud  dur: 6  
			pitch: #((50 to: 64) (78 to: 80))
			ampl: #((60 to: 80) (90 to: 110))
			voice: #((1) (1))
			density: 8) eventList) .
		lis addAll: ((DynamicCloud  dur: 6  
			pitch: #((22 to: 48) (38 to: 40))
			ampl: #((80 to: 100) (60 to: 120))
			voice: #((1) (1))
			density: 9) eventList)].
	lis playOn: MIDIVoice default!

testOSCout

	| sel |
	sel := (Menu labelArray: #('CSL FM example' 'MIDI example')) startUp.
	sel == 0 ifTrue: [^self].
	sel == 1 ifTrue: [^OSCVoice fm_example1].
		^OSCVoice midiScaleExample!

testSndFileIn

	"This stub method was generated by UIDefiner"

	^self!

testSoundIn

	"This stub method was generated by UIDefiner"

	^self!

testSoundOut
	"Play a sound"

	PortAudioPort playSweep! !

!Siren.SirenUtility methodsFor: 'aspects'!

blockSize
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^blockSize isNil
		ifTrue:
			[blockSize := nil asValue]
		ifFalse:
			[blockSize]!

defaultVoiceClass
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^defaultVoiceClass isNil
		ifTrue:
			[defaultVoiceClass := nil asValue]
		ifFalse:
			[defaultVoiceClass]!

inChannels
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^inChannels isNil
		ifTrue:
			[inChannels := nil asValue]
		ifFalse:
			[inChannels]!

oscIP
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^oscIP isNil
		ifTrue:
			[oscIP := String new asValue]
		ifFalse:
			[oscIP]!

oscPort
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^oscPort isNil
		ifTrue:
			[oscPort := 0 asValue]
		ifFalse:
			[oscPort]!

outChannels
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^outChannels isNil
		ifTrue:
			[outChannels := nil asValue]
		ifFalse:
			[outChannels]!

sampleRate
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^sampleRate isNil
		ifTrue:
			[sampleRate := nil asValue]
		ifFalse:
			[sampleRate]! !

!Siren.SirenUtility methodsFor: 'initialize release'!

initialize
	"Set up the defaults values."
	"self open"

	super initialize.
	defaultVoiceClass := #MIDIVoice asValue.
	blockSize := SoundPort blockSize asValue.
	inChannels := SoundPort inChannels asValue.
	outChannels := SoundPort outChannels asValue.
	sampleRate := SoundPort sampleRate asValue.
	oscPort := OSCPort defaultPort asValue.
	oscIP := OSCPort defaultIP printString asValue! !

!Siren.SirenUtility methodsFor: 'interface opening'!

postOpenWith: aBuilder 
	"This message is sent by the builder after it has opened a completed 
	window."

	defaultVoiceClass onChangeSend: #chVoiceClass to: self.
	blockSize onChangeSend: #chBlockSize to: self.
	inChannels onChangeSend: #chInChans to: self.
	outChannels onChangeSend: #chOutChans to: self.
	sampleRate onChangeSend: #chSRate to: self.
	oscPort onChangeSend: #chOSCPort to: self.
	oscIP onChangeSend: #chOSCIP to: self.! !

!Siren.SirenUtility methodsFor: 'actions--change'!

chBlockSize
	"Respond to a selection in the block size menu"

	SoundPort blockSize: blockSize value.
"	Transcript show: 'Sound IO block size set to ', SoundPort blockSize printString; cr"!

chInChans
	"Respond to a selection in the input channel menu"

	SoundPort inChannels: inChannels value.
"	Transcript show: 'Sound IO # input channels set to ', SoundPort inChannels printString; cr"!

chOSCIP
	"Respond to a selection in the OSC IP menu"

	| str addr |
	str := oscIP value.
	str first isDigit			"if the IP is an address like '127.0.0.1' "
		ifTrue: [addr := IPSocketAddress stringToBytes: str]
		ifFalse: [addr := IPSocketAddress hostAddressByName: str].
	OSCPort defaultIP: addr.
"	Transcript show: 'OSC host set to ', OSCPort defaultIP printString; cr"!

chOSCPort
	"Respond to a selection in the OSC port menu"

	OSCPort defaultPort: oscPort value.
"	Transcript show: 'OSC input port set to ', OSCPort defaultPort printString; cr"!

chOutChans
	"Respond to a selection in the output channel menu"

	SoundPort outChannels: outChannels value.
"	Transcript show: 'Sound IO # output channels set to ', SoundPort outChannels printString; cr"!

chSRate
	"Respond to a selection in the sampleRate menu"

	SoundPort sampleRate: sampleRate value.
"	Transcript show: 'Sound IO sample rate set to ', SoundPort sampleRate printString; cr"!

chVoiceClass
	"Respond to a selection in the voice class menu"

	IOVoice setDefault: defaultVoiceClass value.
"	Transcript show: 'Default IO voice class set to ', defaultVoiceClass value; cr"! !

!Siren.SirenUtility class methodsFor: 'class initialization'!

initialize
	"Edit these to taste for your installation."
	"SirenUtility initialize"

	FileSep := String with: Filename separator.
"Flush cache dictionaries"
	EventLists := Dictionary new.
	Voices := Dictionary new.
	Sounds := Dictionary new.
"Set search paths"
	DataDir := ''.			"Basic data directory"
	ScoreDir := ''.			"Scores"
	SoundDir := ''.		"Sound files"!

initializeSirenSTP
	"Edit these to taste for your installation."
	"SirenUtility initializeSirenSTP"

	| root |
	SirenUtility initialize.
	root := '/Volumes/Content/'.			"Root of media content"
	ScoreDir := root, 'Scores', FileSep.		"Scores"
	SoundDir := root, 'Sound', FileSep.		"Sound files"
	DataDir := SoundDir.					"Basic data directory"!

initializeVoices
	"SirenUtility initializeVoices"

	Voices := Dictionary new.
! !

!Siren.SirenUtility class methodsFor: 'utilities'!

categoryList

	^#( 'Music-Magnitudes-Representation'
		'Music-Magnitudes-Implementation'
		'Music-Events'
		'Music-Functions'
		'Music-Voices'
		'Music-Sound'
		'Music-EventGenerators'
		'Music-MIDI-Ports'
		'Music-Support'
"		'Music-VirtualReality'"
		'MusicUI-DisplayLists'
		'MusicUI-DisplayListViews'
		'MusicUI-Layout'
		'MusicUI-Editors'
		'MusicUI-Functions'
		'MusicUI-Sound'
		'MusicUI-Mixing'
"		'MusicUI-Networks'"
		'MusicUI-Trees'
		'MusicUI-LinearPrediction'
"		'MusicUI-PhaseVocoder'"
"		'MusicUI-Support'
		'MusicUI-SupportOLD'")!

utilityMessages
	"Select these utilities, inspect..."
	
"	MusicMagnitude MusicConstantDictionary
	(Browser browseAllCallsOn:
		(Smalltalk associationAt: #MusicConstants))
	RemoteSocketServer allInstances

	Object initializeDependentsFields
	DependentsFields inspect
	Undeclared inspect

	SampledSound instanceCount
	Voices
	EventLists
	MIDIVoice allInstances
	MIDIDevice
	SunMIDIPort instanceCount
	MIDIPort instanceCount
	ProPort instanceCount
	SunMIDIPort allNotesOff
	MIDIPort allNotesOff
	ProPort allSoundOff
	SampledSound allSubInstances
	EventListEditor allSubInstances
	Undeclared		DependentsFields
	AbstractEvent allSubInstances size
	DisplayItem allSubInstances
	DisplayListView allSubInstances
	Semaphore allInstances
	Semaphore instanceCount
	Process instanceCount
	Process allInstances

"! !

!Siren.SirenUtility class methodsFor: 'html utilities'!

htmlFooter
	"Answer the file footer for HTML output."

	^'<P>
<ADDRESS>XXX Outline -- stp@create.ucsb.edu -- Generated ',
(Time dateAndTimeNow printString), 
'</ADDRESS>
</BODY>
</HTML>
'!

htmlForString: str
	"Do semi-smart line-wrapping or HTML representation of the given string."

	| cr |
	cr := String with: Character cr.
	^(((((str  copyReplaceAll: ''' ''' with: '  ')
			breakIntoLinesOf: 80)
			copyReplaceAll: '(''' with: '(<I>')
			copyReplaceAll: ''' )' with: '</I> )')
			copyReplaceAll: ' ' with: '&nbsp;')
			copyReplaceAll: cr with: (cr, '<BR>', cr)!

htmlHeader
	"Answer the file header for HTML output."

	^'<HTML>
<HEAD>
<TITLE>XXX</TITLE>
</HEAD>
<BODY><P>
<A NAME="TOP">
<H2>XXX</H2>
<P><HR><P>
<H2>Package Category Outline</H2>
'! !

!Siren.SirenUtility class methodsFor: 'example access'!

formNamed: key
	"Answer the given item from the global music constants."
	"(Siren formNamed: #altoClef) display"

	^(DisplayListView musicConstants at: #forms) at: key!

musicConstants
	"Answer the global music constants (mostly icons)."
	"Siren musicConstants inspect"
	"Browser browseAllCallsOn:
		(Smalltalk associationAt: #MusicConstants)"

	^DisplayListView musicConstants! !

!Siren.SirenUtility class methodsFor: 'class var accessing'!

atVoice: nam put: vox
	"Add to the class's voice dictionary."

	^Voices at: nam put: vox!

binDir
	"Answer the class's executable (binary) storage directory."

	^DataDir!

dataDir
	"Answer the class's default data storage directory."

	^DataDir!

schedule
	"Answer the 'global' scheduler."
	"Siren schedule"

	EventScheduler instance!

scoreDir
	"Answer the class's default score storage directory."

	^ScoreDir!

soundDir
	"Answer the class's default sound storage directory."

	^SoundDir!

sounds
	"Answer the class's sound dictionary."

	^Sounds!

voiceNamed: nam
	"Add to the class's voice dictionary."

	^Voices at: nam ifAbsent: [nil]!

voiceNamed: nam put: vox
	"Add to the class's voice dictionary."

	^Voices at: nam put: vox!

voices
	"Answer the class's voice dictionary."

	^Voices! !

!Siren.SirenUtility class methodsFor: 'event lists'!

eventList: aName put: anEventList
	"Store the give n event list in the shared dictionary; handle hierarchical names"
	"SirenUtility eventList: 'aa/bb/cc' put: nil"

	| path strm dict |
	(aName includes: $/ )
		ifFalse: [(EventLists includesKey: #Temp)
				ifFalse: [EventLists at: #Temp put: Dictionary new].
			^(EventLists at: #Temp) at: aName put: anEventList].
	path := OrderedCollection new.
	strm := ReadStream on: aName.
	[strm atEnd] whileFalse:
		[path add: (strm upTo: $/ ) asSymbol].
	dict := EventLists.
	1 to: path size - 1 do:
		[ :ind | | lev |
		lev := path at: ind.
		(dict includesKey: lev)
			ifFalse: [dict at: lev put: Dictionary new].
		dict := dict at: lev].
	dict at: path last put: anEventList.!

eventListAt: aName
	"Get the event list at the given (possibly hierarchical) name in the shared dictionary"
	"SirenUtility eventList: 'aa/bb/cc' "

	| path strm dict |
	(aName includes: $/ )
		ifFalse: [(EventLists includesKey: #Temp)
				ifFalse: [^nil].
			^(EventLists at: #Temp) at: aName ifAbsent: [nil]].
	path := OrderedCollection new.
	strm := ReadStream on: aName.
	[strm atEnd] whileFalse:
		[path add: (strm upTo: $/ ) asSymbol].
	dict := EventLists.
	1 to: path size - 1 do:
		[ :ind | | lev |
		lev := path at: ind.
		(dict includesKey: lev)
			ifFalse: [^nil].
		dict := dict at: lev].
	^dict at: path!

eventLists
	"Answer the class's event list dictionary."

	^EventLists!

flushAllEventLists
	"Flush the class's event list dictionary."
	"SirenUtility flushAllEventLists"

	EventLists := Dictionary new.
	EventLists at: #Temp put: Dictionary new.!

flushTempEventLists
	"Flush the class's temporary event list dictionary."
	"SirenUtility flushTempEventLists"

	EventLists at: #Temp put: Dictionary new.! !

!Siren.AbstractOSCPacket methodsFor: 'accessing'!

oscBytes

	^oscBytes!

oscSize

	^oscBytes size! !

!Siren.AbstractOSCPacket methodsFor: 'osc'!

toOSCBytes: converter 

	self subclassResponsibility.
	^self! !

!Siren.AbstractOSCPacket methodsFor: 'initialize-release'!

init

	| convertor |
	convertor := OSCByteConvertor new.
	self toOSCBytes: convertor.
	oscBytes := convertor contents.
	^self! !

!Siren.EventListTreeItem methodsFor: 'initialize-release'!

lists: aCollection

	lists := aCollection! !

!Siren.EventListTreeItem methodsFor: 'accessing'!

children
	
	^lists!

hasChildren

	^lists notEmpty! !

!Siren.EventListTreeEditor methodsFor: 'accessing'!

scoreView
	"Answer up the score view"

	^scoreView! !

!Siren.EventListTreeEditor methodsFor: 'aspects'!

listTree
	"Answer up the event list dictionary tree"

	^listTree! !

!Siren.EventListTreeEditor methodsFor: 'initialize/release'!

initialize
	"EventListTreeEditor open"

	| model |
	scoreView := Siren.DisplayListSubcanvas new.
	listTree := SelectionInTree new.
	model := TreeModel on: (EventListTreeItem new lists: SirenUtility eventLists)
					displayRoot: false.
	listTree list: model! !

!Siren.EventListTreeEditor class methodsFor: 'resources'!

treeMenu	
	^Menu labelArray: #(copy cut paste again undo)! !

!Siren.LayoutManager methodsFor: 'accessing'!

displayList
	"Answer the receiver's display list (generated)."

	^self generateDisplayList!

displayListFor: aView
	"Answer the receiver's display list (generated)."

	view := aView.
	^self generateDisplayList!

orientation: aSymbol
	"Set the receiver's orientation."

	orientation := aSymbol!

view: aView
	"Set the receiver's view."

	view := aView! !

!Siren.LayoutManager methodsFor: 'generating'!

addItem: anAss atNode: aPath in: aDisplayList
	"Add the given item to the display list."

	^self subclassResponsibility!

generateDisplayList
	"Generate and answer the receiver's display list."

	| list |
	list := DisplayList new.
	view model items do:
		[ :e |
		 self addItem: e atNode: (OrderedCollection new) in: list].
	^list! !

!Siren.OSCMessage methodsFor: 'osc'!

addressToOSCBytes: convertor 
	"Convert the address to the OSC format"
	address toOSCBytes: convertor.
	^self!

argumentsToOSCBytes: convertor 
	"Add the type tags, then the arguments"

	convertor nextPut: $, asInteger.
	self typesToOSC: convertor.
	arguments do: [:each | each toOSCBytes: convertor].
	^self!

toOSCBytes: converter 
	"Convert myself to OSC format"

	self addressToOSCBytes: converter.
	arguments notEmpty ifTrue: [self argumentsToOSCBytes: converter].
	^self!

typesToOSC: converter

	| index numPad |
	arguments isEmpty ifTrue: [^self].
	arguments do: [:each | each toOSCTypeOn: converter].
	index := 1.
	numPad := 4 - (converter position \\ 4).
	"This isn't very ST -- should be numPad do:[converter nextPut: 0]"
	[index <= numPad]
		whileTrue: 
			[converter nextPut: 0.
			index := index + 1].
	^self! !

!Siren.OSCMessage methodsFor: 'initialize-release'!

initAddress: t1 arguments: t2 

	address := t1.
	arguments := t2.
	self init.
	^self! !

!Siren.OSCMessage methodsFor: 'printing'!

printOn: aStream

	aStream nextPutAll: 'OSC: ', address, '  ', arguments printString.! !

!Siren.OSCMessage class methodsFor: 'instance-creation'!

for: t1 
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self new initAddress: t1 arguments: #()!

for: addr with: args 
	"Answer a TOM with the given address and arguments"

	^self new initAddress: addr arguments: args! !

!Siren.OSCMessage class methodsFor: 'examples'!

example1
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/example1' with: #(100)!

example2
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/example2' with: #('tester' 10.4)!

example3
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/example3' with: #(20 30 'blah')! !

!Siren.DisplayListTracker methodsFor: 'events'!

mouseMovedEvent: aMouseMovedEvent


	controller mouseMovedEvent: aMouseMovedEvent.
	^nil!

redButtonPressedEvent: aMouseButtonEvent 

	aMouseButtonEvent isBlue ifTrue: [self halt].
	controller redButtonPressedAt: (self cursorPointFor: aMouseButtonEvent).
"	self trackSelectionFor: (self cursorPointFor: aMouseButtonEvent).
"	aMouseButtonEvent initiator grabMouseEventFor: self.
	^nil! !

!Siren.DisplayListTracker methodsFor: 'private'!

trackSelectionFor: aPoint 
	| isInside |
	isInside := self viewHasPoint: aPoint.
	(inside == false)
		ifFalse: [isInside
				ifFalse: 
					[controller mouseExitedAt: aPoint.
					inside := false]]
		ifTrue: [isInside
				ifTrue: 
					[controller mouseEnteredAt: aPoint.
					inside := true]].
	controller mouseMovedTo: aPoint! !

!Siren.AbstractEvent methodsFor: 'initialize-release'!

initialize
	"Set up the default state of the receiver--add props. dict."

	properties := IdentityDictionary new.!

release
	"Flush the receiver."

	properties == nil
		ifFalse: [properties associationsDo: 
			[ :a |
			a key release.
			a value release]].
	properties  := nil! !

!Siren.AbstractEvent methodsFor: 'accessing'!

, anArgument
	"Add the argument as a property of the receiver."
	"440 Hz, 250 msec, (#voice -> #flute)"
	"(440 Hz, 250 msec, (#voice -> #flute)), 
		((1/4) => (471 Hz, (1/4) beat, (#voice -> #flute)))"

	anArgument isMusicMagnitude
		ifTrue: [self perform: anArgument species propertyName with: anArgument]
		ifFalse: [(anArgument isMemberOf: Association)
			ifTrue: [self perform: (anArgument key, ':') asSymbol with: anArgument value]
			ifFalse: [(anArgument isMemberOf: EventAssociation)
				ifTrue: [^EventList new add: self; add: anArgument]
				ifFalse: [self error: 'Unknown property type.']]]!

date
	"Answer the receiver's 'date'."

	self propCheck.
	^properties at: #date ifAbsent: [nil]!

date: obj
	"Set the receiver's 'date'."

	self propCheck.
	^properties at: #date put: obj!

inspect
	"Inspect the receiver--Use a special inspector for Event types"
	"EventInspectorView open: (EventInspector inspect: MEvent someInstance)"

"	EventInspectorView open: (EventInspector inspect: self)"
	^super inspect!

name
	"Answer the receiver's 'name'."

	self propCheck.
	^properties at: #name ifAbsent: ['']!

species
	"Answer AEvent--all subclasses look like me"

	^AbstractEvent!

version
	"Answer the receiver's 'version'."

	self propCheck.
	^properties at: #version ifAbsent: [nil]!

version: obj
	"Set the receiver's 'version'."

	self propCheck.
	^properties at: #version put: obj! !

!Siren.AbstractEvent methodsFor: 'comparing'!

= anObject 
	"Answer whether the receiver and the argument represent the same values."

	^anObject isEvent
		ifFalse: [false]
		ifTrue: [self properties = anObject properties]! !

!Siren.AbstractEvent methodsFor: 'testing'!

isEvent
	"Answer true."

	^true!

isSound
	"Answer false."

	^false! !

!Siren.AbstractEvent methodsFor: 'properties'!

at: aProp
	"Answer a value from the property list dictionary (or an instVar)."

	self propCheck.
	^properties at: aProp ifAbsent: [nil]!

at: aProp ifAbsent: otherCase
	"Answer a value from the property list dictionary or the value of the given block."

"	(self respondsTo: aProp)
		ifTrue: [^self perform: aProp].
	^properties == nil
		ifTrue: [otherCase]
		ifFalse: [properties at: aProp ifAbsent: otherCase value]

"
	properties ifNil: [^nil].
	^properties at: aProp ifAbsent: 
		["(self respondsTo: aProp)
			ifTrue: [self perform: aProp]
			ifFalse: ["otherCase value"]"]!

at: aProp put: aVal
	"Set a value in the receiver's property list dictionary (or instVar)."

	self propCheck.
	properties at: aProp put: aVal!

doesNotUnderstand: aMessage 
	"Handle doesNotUnderstand: to try to access the property dictionary.
	If this is unsuccessful, announce that the receiver does not understand the argument."
	"N.B.: One may want to turn this off for debugging."

	| sel |
	sel := aMessage selector.
	(sel includes: $:)									"access property"
		ifFalse: [(properties ~~ nil and: [properties includesKey: sel])
			ifTrue: [^properties at: sel]]
		ifTrue: [((sel occurrencesOf: $:) = 1)		"set property"
			ifTrue: [^self at: (sel copyUpTo: $:) asSymbol
					put: (aMessage arguments at: 1)]].
	^super doesNotUnderstand: aMessage					"otherwise"!

hasProperty: aSymbol
	"Answer whether or not the receiver's property list dictionary includes the symbol as a key."

	properties ifNil: [^nil].
	^properties includesKey: aSymbol!

properties
	"Answer the receiver's property list dictionary."

	^properties!

respondsTo: aSymbol
	"Answer whether the method dictionary of the receiver's class contains 
	aSymbol as a message selector OR if the selector is unary and is a key 
	in the receiver's property dictionary."

	(super respondsTo: aSymbol) ifTrue: [^true].
"	((properties ~~ nil) and: [properties includesKey: aSymbol])
		ifTrue: [^true].
	(aSymbol occurrencesOf: $:) = 1 ifTrue: [^true].
"	^false! !

!Siren.AbstractEvent methodsFor: 'scheduling'!

scheduleOn: aChannel
	"Perform or interpret the receiver on the argument; override in subclasses."

	aChannel schedule: self at: 0! !

!Siren.AbstractEvent methodsFor: 'printing'!

asExplorerString: showHide

	| stream |
	stream := WriteStream on: (String new: 128).
	properties ifNotNil:
		[properties do: 
			[ :pr | self display: showHide field: pr on: stream]].
	^(super asExplorerString: showHide), stream contents!

display: showHide field: filter on: stream

	| obj |
	(showHide includes: filter)
		ifTrue: [obj := self at: filter ifAbsent: [''].
			obj isString ifFalse: [obj := obj printString].
			obj isEmpty
				ifFalse: [stream isEmpty 
						ifFalse: [stream nextPutAll: ': '].
					stream nextPutAll: obj]]!

displayField: filter on: stream

	| obj |
	stream isEmpty ifFalse: [stream nextPutAll: ': '].
	obj := self at: filter ifAbsent: [''].
	obj isString ifFalse: [obj := obj printString].
	stream nextPutAll: obj!

printOn: aStream 
	"Format and print the receiver on the argument."

	aStream nextPut: $(.
	super printOn: aStream.
	aStream space.
	properties == nil
		ifFalse: [properties associationsDo:
				[ :ass |
				aStream nextPutAll: ' (', ass key, ': ', ass value printString, ')']].
	aStream nextPut: $); cr!

storeOn: aStream 
	"Format and store the source the receiver on the argument."

	aStream nextPutAll: (properties == nil
			ifTrue: ['(', self class name, '']
			ifFalse: ['((', self class name, '']).
	properties == nil
		ifFalse: [aStream nextPutAll: ')'.
			properties associationsDo: [ :ass |
					aStream nextPutAll: ' at: '.
					ass key storeOn: aStream.
					aStream nextPutAll: ' put: '.
					ass value storeOn: aStream.
					aStream nextPut: $;; cr.]].
	aStream nextPutAll: ')'!

templateFields
	"Answer the field names for the instances of the receiver class."

	| coll |
	coll := self class templateFields asOrderedCollection.
	coll remove: #properties.
	properties ifNotNil:
		[coll addAll: properties keys].
	^coll! !

!Siren.AbstractEvent methodsFor: 'private'!

propCheck
	"Make sure the receiver has a property list dictionary."

	properties == nil
		ifTrue: [properties := IdentityDictionary new].! !

!Siren.AbstractEvent class methodsFor: 'examples'!

eventInspectExample
	"Demonstrate the creation of an AEvent."
	"AbstractEvent example"

	((AbstractEvent new) color: #green; shape: #round) inspect!

example
	"Demonstrate the creation of an AEvent."
	"AbstractEvent example inspect"

	^(AbstractEvent new) color: #green; shape: #round! !

!Siren.DurationEvent methodsFor: 'accessing'!

dur
	"Answer the receiver's duration."

	^self duration!

dur: newValue
	"Set the receiver's duration."

	^self duration: newValue!

duration
	"Answer the receiver's duration."

	^duration!

duration: newValue
	"Set the receiver's duration."

	newValue species == Duration
		ifTrue: [duration := newValue]
		ifFalse: [duration := Duration value: newValue]!

order
	"Answer the receiver's order."

	^self at: #order ifAbsent: [0].!

order: anOrder
	"Set the receiver's order."

	^self at: #order put: anOrder!

voice
	"Answer the receiver's voice or some reasonable default."

	self propCheck.
	^properties at: #voice ifAbsent: [nil]!

voice: aValue
	"Set the receiver's voice to the argument."

	properties at: #voice put: aValue! !

!Siren.DurationEvent methodsFor: 'comparing'!

= anObject 
	"Answer whether the receiver and the argument represent the same values."

	^anObject isEvent
		ifFalse: [false]
		ifTrue: [((self duration = anObject duration) and:
				[super = anObject])]! !

!Siren.DurationEvent methodsFor: 'scheduling'!

play
	"Play the receiver by scheduling it."

	^self playAt: nil!

playAt: aTime
	"Play the receiver on its voice then."
	
	^self playOn: self voice at: aTime!

playOn: aVoice at: aTime
	"Play the receiver on the voice then."
	
	aVoice == nil
		ifTrue: [^IOVoice default playEvent: self at: aTime].
	aVoice isVoice
		ifTrue: [aVoice playEvent: self at: aTime].
	(SirenUtility voices includesKey: aVoice)
			ifTrue: [^(SirenUtility voices at: aVoice) playEvent: self at: aTime].
	aVoice isInteger
			ifTrue: [IOVoice default playEvent: self at: aTime]!

scheduleAt: aTime
	"Play the receiver on its voice then."
	
	self playAt: aTime.
	^nil! !

!Siren.DurationEvent methodsFor: 'printing'!

printOn: aStream 
	"Format and print the receiver on the argument."

	aStream nextPutAll: '(', self class name.
	duration == nil
		ifFalse: [aStream nextPutAll: ' ', duration printString]. 
	properties == nil
		ifFalse: [properties associationsDo:
				[ :ass |
				aStream nextPutAll: ' (', ass key, ': ', ass value printString, ')']].
	aStream nextPutAll: ')'; cr!

storeOn: aStream 
	"Format and store the source the receiver on the argument."

	aStream nextPutAll: (properties == nil
			ifTrue: ['(', self class name, '']
			ifFalse: ['((', self class name, '']).
	duration == nil
		ifFalse: [aStream nextPutAll: ' dur: ', duration value storeString]. 
	properties == nil
		ifFalse: [aStream nextPutAll: ')'.
			properties associationsDo: [ :ass |
					aStream nextPutAll: ' at: '.
					ass key storeOn: aStream.
					aStream nextPutAll: ' put: '.
					ass value storeOn: aStream.
					aStream nextPut: $;; cr.]].
	aStream nextPutAll: ')'! !

!Siren.DurationEvent class methodsFor: 'instance creation'!

dur: aD voice: aVoice
	"Answer a DurationEvent instance initialized with the arguments."

	^self new duration: aD; voice: aVoice! !

!Siren.DurationEvent class methodsFor: 'examples'!

eventInspectExample
	"Demonstrate the creation of a DEvent."
	"DurationEvent example inspect"

	(DurationEvent dur: 250 msec voice: #flute) inspect!

example
	"Demonstrate the creation of a DEvent."
	"DurationEvent example"

	^DurationEvent dur: 250 msec voice: #flute! !

!Siren.MusicEvent methodsFor: 'accessing'!

ampl
	"Answer the receiver's loudness"

	^loudness!

ampl: aValue
	"Set the receiver's loudness to the argument."

	^self loudness: aValue!

amplitude
	"Answer the receiver's loudness"

	^loudness!

amplitude: aValue
	"Set the receiver's loudness to the argument."

	(aValue species == Amplitude)
		ifFalse: [loudness := Amplitude value: aValue]
		ifTrue: [loudness := aValue]!

loudness
	"Answer the receiver's loudness"

	^loudness!

loudness: aValue
	"Set the receiver's loudness to the argument."

	(aValue species == Amplitude)
		ifFalse: [loudness := Amplitude value: aValue]
		ifTrue: [loudness := aValue]!

pitch
	"Answer the receiver's pitch"

	^pitch!

pitch: aValue
	"Set the receiver's pitch to the argument."

	(aValue species == Pitch)
		ifFalse: [pitch := Pitch value: aValue]
		ifTrue: [pitch := aValue]!

voice
	"Answer the receiver's voice"

	^voice!

voice: aValue
	"Set the receiver's voice to the argument."

	voice := aValue! !

!Siren.MusicEvent methodsFor: 'comparing'!

= anObject 
	"Answer whether the receiver and the argument represent the same values."

	^anObject isEvent
		ifFalse: [false]
		ifTrue: [(self pitch = anObject pitch) and: 
			[(self loudness = anObject loudness) and: 
				[super = anObject]]]! !

!Siren.MusicEvent methodsFor: 'processing'!

transposeBy: aStep
	"Add the given step to the receiver's pitch."

	self pitch: (self pitch + aStep)! !

!Siren.MusicEvent methodsFor: 'printing'!

printOn: aStream 
	"Format and print the receiver on the argument."

"	^self printVerboseOn: aStream
"	^self printTerseOn: aStream!

printTerseOn: aStream 
	"Format and print the receiver on the argument as tersely as possible."

	duration == nil
		ifFalse: [aStream nextPutAll: duration printString]. 
	pitch == nil
		ifFalse: [aStream nextPutAll: ' ', pitch printString].
	loudness == nil
"		ifFalse: [aStream nextPutAll: ' (l: ', loudness value printString,')']."
		ifFalse: [aStream nextPutAll: ' ', loudness printString].
	voice == nil
		ifFalse: [aStream nextPutAll: ' (v: ', voice printString, ')'].
	properties == nil
		ifFalse: [properties associationsDo:
				[ :ass |
				aStream nextPutAll: ' (', ass key, ': ', ass value printString, ')']].
	aStream "nextPutAll: ')';" cr!

printVerboseOn: aStream 
	"Format and print the receiver on the argument."

	aStream nextPutAll: '(MEvent'; crtab.
	duration == nil
		ifFalse: [aStream nextPutAll: ' duration: ', duration printString; crtab]. 
	pitch == nil
		ifFalse: [aStream nextPutAll: ' pitch: ', pitch printString; crtab].
	voice == nil
		ifFalse: [aStream nextPutAll: ' voice: ', voice printString; crtab].
	loudness == nil
		ifFalse: [aStream nextPutAll: ' loudness: ', loudness value printString].
	properties == nil
		ifFalse: [properties associationsDo:
				[ :ass |
				aStream crtab; nextPutAll: ' ', ass key, ': ', ass value printString]].
	aStream nextPutAll: ')'; cr!

readDataFrom: aDataStream size: size
	"Read a new event from the given stream using the compact format."

	| propSize |
	duration := MusicMagnitude readDataFrom: aDataStream.
	pitch := MusicMagnitude readDataFrom: aDataStream.
	loudness := MusicMagnitude readDataFrom: aDataStream.
	voice := aDataStream next.
	propSize := size - self class instSize.
	propSize > 0 ifTrue: [self propCheck].
	propSize timesRepeat:
		[properties at: (aDataStream next) put: (aDataStream next)]!

storeDataOn: aDataStream
	"Store myself on a DataStream. Answer self."
	"Format: 4 normal IVs, properts as key/value (or nil)"

	| cntInstVars cntProps |
	cntInstVars := self class instSize.
	properties isNil
		ifFalse: [cntProps := properties size]
		ifTrue: [cntProps := 0].
	aDataStream
		beginInstance: self class
		size: cntInstVars + cntProps.
	MusicMagnitude store: duration on: aDataStream.
	MusicMagnitude store: pitch on: aDataStream.
	MusicMagnitude store: loudness on: aDataStream.
	aDataStream nextPut: voice.
	properties isNil
		ifFalse: [properties associationsDo: 
				[ :ass |
				aDataStream nextPut: ass key.
				aDataStream nextPut: ass value]]!

storeOn: aStream 
	"Format and store the source the receiver on the argument."

	aStream nextPutAll: (properties == nil
			ifTrue: ['(', self class name, '']
			ifFalse: ['((', self class name, '']).
	duration == nil
		ifFalse: [aStream nextPutAll: ' dur: ', duration value storeString]. 
	pitch == nil
		ifFalse: [aStream nextPutAll: ' pitch: ', pitch value storeString].
	loudness == nil
		ifFalse: [aStream nextPutAll: ' ampl: ', loudness value storeString].
	voice == nil
		ifFalse: [aStream nextPutAll: ' voice: ', voice storeString].
	properties == nil
		ifFalse: [aStream nextPutAll: ')'.
			properties associationsDo: [ :ass |
					aStream nextPutAll: ' at: '.
					ass key storeOn: aStream.
					aStream nextPutAll: ' put: '.
					ass value storeOn: aStream.
					aStream nextPut: $;; cr.]].
	aStream nextPutAll: ')'! !

!Siren.MusicEvent class methodsFor: 'class initialization'!

initialize
	"Initialize the global dictionary of Events (optional)."
	"MEvent initializeEventDictionary"

	Smalltalk at: #Events put: Dictionary new!

initializeEventDictionary
	"Initialize the global dictionary of Events (optional)."
	"MEvent initializeEventDictionary"

	Smalltalk at: #Events put: Dictionary new! !

!Siren.MusicEvent class methodsFor: 'instance creation'!

ampl: anA voice: aVoice
	"Answer a MusicEvent instance initialized with the arguments."

	^self new loudness: anA; voice: aVoice!

dur: aD ampl: anA voice: aVoice
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent dur: 1000 ampl: 127 voice: 2"
	"MusicEvent dur: 1/4 ampl: #mf voice: #flute"

	^self new duration: aD;
			loudness: anA;
			voice: aVoice!

dur: aD pitch: aP
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent dur: 1000 pitch: 30"
	"MusicEvent dur: 1/4 pitch: #c3"

	^self new duration: aD; pitch: aP!

dur: aD pitch: aP ampl: anA
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent dur: 1000 pitch: 30 ampl: 127 "
	"MusicEvent dur: 1/4 pitch: #c3 ampl: #mf "

	^self new duration: aD;
			pitch: aP; 
			loudness: anA!

dur: aD pitch: aP ampl: anA voice: aVoice
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent dur: 1000 pitch: 30 ampl: 127 voice: 2"
	"MusicEvent dur: 1/4 pitch: #c3 ampl: #mf voice: #flute"

	^self new duration: aD;
			pitch: aP; 
			loudness: anA;
			voice: aVoice!

dur: aD pitch: aP voice: aVoice
	"Answer a MusicEvent instance initialized with the arguments."

	^self new duration: aD; pitch: aP; voice: aVoice!

dur: aD voice: aVoice ampl: anA
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent dur: 1000 pitch: 30 ampl: 127 voice: 2"
	"MusicEvent dur: 1/4 pitch: #c3 ampl: #mf voice: #flute"

	^self new duration: aD;
			loudness: anA;
			voice: aVoice!

duration: aD pitch: aP
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent dur: 1000 pitch: 30"
	"MusicEvent dur: 1/4 pitch: #c3"

	^self new duration: aD; pitch: aP!

duration: aD pitch: aP ampl: anA
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent dur: 1000 pitch: 30 ampl: 127 "
	"MusicEvent dur: 1/4 pitch: #c3 ampl: #mf "

	^self new duration: aD;
			pitch: aP; 
			loudness: anA!

pitch: aP
	"Answer a MusicEvent instance initialized with the argument."
	"MusicEvent pitch: 440.0"
	"MusicEvent pitch: #c3"

	^self new pitch: aP!

pitch: aP ampl: anA voice: aVoice
	"Answer a MusicEvent instance initialized with the arguments."
	"MusicEvent pitch: 30 ampl: 127 voice: 2"
	"MusicEvent pitch: #c3 ampl: #mf voice: #flute"

	^self new pitch: aP; 
			loudness: anA;
			voice: aVoice! !

!Siren.MusicEvent class methodsFor: 'examples'!

eventInspectExample
	"Demonstrate the terse format of event description."
	"MusicEvent eventInspectExample"

	(440 Hz, 250 msec, (#voice -> #flute), 0.7071 ampl) inspect
"	((880 Hz, 250 msec, (#voice -> #flute), 0.7071 ampl) accent: #sfz) inspect"!

example
	"Demonstrate the terse format of event description."
	"MusicEvent example inspect"

	^(440 Hz, 250 msec, (#voice -> #flute), 0.7071 ampl)
"	((880 Hz, 250 msec, (#voice -> #flute), 0.7071 ampl) accent: #sfz) inspect"! !

!Siren.ActionEvent methodsFor: 'accessing'!

action
	"Answer the receiver's 'action'."

	^action!

action: anObject
	"Set the receiver's instance variable 'action' to be anObject."

	action := anObject! !

!Siren.ActionEvent methodsFor: 'scheduling'!

playAt: aTime
	"Play the receiver by executing its action block."
	
	^action value! !

!Siren.ActionEvent class methodsFor: 'instance creation'!

dur: aD action: actionBlock
	"Answer an ActionEvent instance initialized with the arguments."

	^self new duration: aD; action: actionBlock!

dur: aD block: actionBlock
	"Answer an ActionEvent instance initialized with the arguments."

	^self new duration: aD; action: actionBlock! !

!Siren.ActionEvent class methodsFor: 'examples'!

eventExample
	"Demonstrate the creation of an ActionEvent."
	"ActionEvent eventExample play"

	^ActionEvent dur: 250 msec action: 
		[ | gc |
		gc := Window currentWindow graphicsContext.
	 	gc paint: ColorValue red.
		gc displayRectangle: ((20@20) extent: (20@20)).
		(Delay forSeconds: 1) wait.
		Window currentWindow refresh]!

listExample
	"Demonstrate the creation of an ActionEvent."
	"ActionEvent listExample"
	"ActionEvent playExample"

	| list gc bounds rand |
	list := EventList newAnonymous.
	rand := Random new.
	gc := Window currentWindow graphicsContext.
	bounds := gc clippingBounds.
	50 timesRepeat:
		[list add: (ActionEvent dur: 80 msec action: 
			[gc paint: ColorValue random.
			gc displayRectangle: (((rand next * bounds width) truncated
							@ (rand next * bounds height) truncated)
				extent: ((rand next * 40) truncated)
							@ (rand next * 40) truncated)])].
	^list!

playExample
	"Demonstrate the performance of a list of ActionEvents."
	"ActionEvent playExample"

	EventScheduler instance addClient: self listExample in: (100 msec).
	EventScheduler instance run.
	(Delay forSeconds: 3) wait.
	Window currentWindow refresh! !

!Siren.SoundFile methodsFor: 'initialize-release'!

close
	"Close the receiver's file."

	index ifNil: [^self].
	Interface lsf_close: index. !

initialize
	"Set up the instance variables of a default sound file."

	self propCheck.
	Interface ifNil: [Interface := LibSndFileInterface new].
	index := nil.
	position := 0.!

open
	"Open the named file and read the header"

	self openForReading.
!

openForReading
	"Open the named file and read the header"

	| compFormat |
	mode := #read.
	index := Interface lsf_open: name with: (LibSndFileInterface const: #LSFM_READ).
	(index < 0) ifTrue: [self error: 'Cannot open soundfile'].
	compFormat := Interface lsf_get_format: index.
	fileFormat := FileFormats at: (compFormat bitAnd: 16rFFF000).
	sampleFormat := SampleFormats at: (compFormat bitAnd: 16r0F).
	rate := Interface lsf_get_rate: index.
	channels := Interface lsf_get_channels: index.
	size := Interface lsf_get_frames: index.! !

!Siren.SoundFile methodsFor: 'accessing'!

channels
	"Answer the instance variable 'channels'."

	^channels!

channels: theChannels
	"Accept the argument, 'theChannels', as the new instance variable 'channels'."

	channels := theChannels.!

fileFormat

	^fileFormat!

fileFormat: aValue

	fileFormat := aValue!

mode: theMode
	"Set the receiver's mode to #read or #write"

	mode := theMode!

name
	"Answer the instance variable 'name'."

	^name!

name: theFileName
	"Accept the argument, 'theFileName', as the new instance variable 'name'."

	name := theFileName.!

rate
	"Return the instance variable 'rate'."

	^ rate!

rate: aRate
	"Set the instance variable 'rate'."

	rate := aRate!

sampleFormat

	^sampleFormat!

sampleFormat: aValue

	sampleFormat := aValue!

samples
	"Return the instance variable 'samples'."

	^sound samples!

size
	"Answer the number of sample frames in the file."

	^size!

size: aNumber
	"Set the number of sample frames in the file."

	^size := aNumber!

sizeInSamples
	"Answer the number of sample frames in the file."

	^size * channels!

sound
	"Answer the instance variable 'sound'."

	^sound!

sound: theSound
	"Accept the argument, 'theSound', as the new instance variable 'sound'."

	sound := theSound! !

!Siren.SoundFile methodsFor: 'printing'!

printOn: aStream 
	"Format and print the receiver on the argument."

	aStream nextPutAll: self class name, 
		' named ', self name printString, 
		' rate: ', self rate printString,
		' channels: ', self channels printString,
		' size: ', size printString; cr.! !

!Siren.SoundFile methodsFor: 'read/write'!

readSamples
	"Read samples from the file into the sample buffer."

	| data |
	index ifNil: [self error: 'Cannot operate on unopened file'].
	(position = 0) ifFalse: 
		[Interface lsf_seek: index with: 0 with: (LibSndFileInterface const: #SEEK_SET)].
	(sampleFormat == #lin16Bit)
		ifFalse: [self error: 'other file formats not imlpemented...'].
	sound isNil 
		ifTrue: [data := (WordArray new: size * channels) copyToHeapAsSigned: #malloc:.
				data beGarbageCollectable]
		ifFalse: [sound samples isPointer
			ifTrue: [data := sound samples]
			ifFalse: [data := sound samples copyToHeapAsSigned: #malloc:.
					data beGarbageCollectable]].
	Interface lsf_read_Isamples: index with: data with: (size * channels).
	sound ifNil:
		[sound := SampledSound named: name size: size rate: rate channels: channels 
						format: sampleFormat data: data].
	sound samplesInMemory: channels * size!

saveSound: snd
	"Write samples from the given sound to the receiver file."

	| type which data howMany |
	(snd format == #lin16Bit)
		ifFalse: [self error: 'other file formats not imlpemented...'].
	('*.aiff' match: name)
		ifTrue: [type := LibSndFileInterface const: #LSF_FORMAT_AIFF].
	('*.wav' match: name)
		ifTrue: [type := LibSndFileInterface const: #LSF_FORMAT_WAV].
	type ifNil: [self error: 'Can''t determine sound file format.'].
	data := snd data copyToHeapAsSigned: #malloc:.
	data beGarbageCollectable.
	which := Interface lsf_create: name 
				with: (LibSndFileInterface const: #LSFM_WRITE) 
				with: (type bitOr: (LibSndFileInterface const: #LSF_FORMAT_PCM_16))
				with: snd rate with: snd channels.
	howMany := Interface lsf_write_Isamples: which with: data with: (snd size * snd channels).
	Transcript show: 'Wrote ', howMany printString, ' samples to sound file ', name; cr.
	Interface lsf_close: which.! !

!Siren.SoundFile class methodsFor: 'instance creation'!

named: nameString
	"Open the given file (EBICSF, SPARC, or NeXT soundfile format)"
	"SoundFile named: '/Users/stp/CSL/Data/moon.snd'"

	| me |
	me := self new initialize.
	me name: nameString.
	^me!

readFileNamed: nameString
	"Open the given file (EBICSF, SPARC, or NeXT soundfile format)"
	"SoundFile named: '/Users/stp/CSL/Data/moon.snd'"

	| me |
	me := self new initialize.
	me name: nameString.
	me openForReading.
	(me sizeInSamples > 1000000) 
		ifTrue: [(Dialog confirm: 'SoundFile has ' , me samples printString, ' samples; Load it anyway?')
			ifFalse: [^me]].
	me readSamples.
	^me! !

!Siren.SoundFile class methodsFor: 'class initialization'!

initialize
	"Set up the file format and sample format dictionaries to interface with libsndfile."
	"SoundFile initialize"

	FileFormats := (((LibSndFileInterface const: #LSF_FORMAT_WAV) -> #riff),
		((LibSndFileInterface const: #LSF_FORMAT_AIFF) -> #aiff),
		((LibSndFileInterface const: #LSF_FORMAT_AU) -> #au),
		((LibSndFileInterface const: #LSF_FORMAT_IRCAM) -> #bicsf),
		((LibSndFileInterface const: #LSF_FORMAT_RAW) -> #raw)).
	SampleFormats := (((LibSndFileInterface const: #LSF_FORMAT_PCM_S8) -> #lin8bit),
		((LibSndFileInterface const: #LSF_FORMAT_PCM_16) -> #lin16Bit),
		((LibSndFileInterface const: #LSF_FORMAT_PCM_24) -> #lin24Bit),
		((LibSndFileInterface const: #LSF_FORMAT_PCM_32) -> #lin32Bit),
		((LibSndFileInterface const: #LSF_FORMAT_FLOAT) -> #float32),
		((LibSndFileInterface const: #LSF_FORMAT_DOUBLE) -> #float64)).
!

initializeSoundFileFlags
	"Set up the file format and sample format dictionaries to interface with libsndfile."
	"SoundFile initializeSoundFileFlags"

	FileFormats := (((LibSndFileInterface const: #LSF_FORMAT_WAV) -> #riff),
		((LibSndFileInterface const: #LSF_FORMAT_AIFF) -> #aiff),
		((LibSndFileInterface const: #LSF_FORMAT_AU) -> #au),
		((LibSndFileInterface const: #LSF_FORMAT_IRCAM) -> #bicsf),
		((LibSndFileInterface const: #LSF_FORMAT_RAW) -> #raw)).
	SampleFormats := (((LibSndFileInterface const: #LSF_FORMAT_PCM_S8) -> #lin8bit),
		((LibSndFileInterface const: #LSF_FORMAT_PCM_16) -> #lin16Bit),
		((LibSndFileInterface const: #LSF_FORMAT_PCM_24) -> #lin24Bit),
		((LibSndFileInterface const: #LSF_FORMAT_PCM_32) -> #lin32Bit),
		((LibSndFileInterface const: #LSF_FORMAT_FLOAT) -> #float32),
		((LibSndFileInterface const: #LSF_FORMAT_DOUBLE) -> #float64)).! !

!Siren.StructureAccessor methodsFor: 'accessing'!

itemGenerator
	"Answer the iG of the receiver."

	^itemGenerator!

itemGenerator: anObject
	"Set the iG of the receiver."

	itemGenerator := anObject!

subject
	"Answer the subject of the receiver."

	^subject!

subject: anObject
	"Set the subject of the receiver."

	subject := anObject! !

!Siren.StructureAccessor methodsFor: 'item accessing'!

itemFor: aModel
	"Answer a display item for the argument"

	(itemGenerator isMemberOf: Symbol)
		ifTrue: [^aModel perform: itemGenerator].
	(itemGenerator isMemberOf: BlockClosure)
		ifTrue: [^itemGenerator value: aModel].
	self error: 'Unknown item generator type.'! !

!Siren.LibSndFileInterface class methodsFor: 'class initialization'!

const: flag
	
	^SF_Constants at: flag!

initialize
	"Set up the class constants dictionary"
	"LibSndFileInterface initialize"
 
	"	/* file formats */
	#define LSF_FORMAT_WAV 0x010000         /* Microsoft WAV format (little endian). */
	#define LSF_FORMAT_AIFF 0x020000        /* Apple/SGI AIFF format (big endian). */
	#define LSF_FORMAT_AU 0x030000          /* Sun/NeXT AU format (big endian). */
	#define LSF_FORMAT_RAW 0x040000         /* RAW PCM data. */
	#define LSF_FORMAT_IRCAM 0x0A0000       /* Berkeley/IRCAM/CARL */

	        /* sample formats */
	#define LSF_FORMAT_PCM_S8 0x0001        /* Signed 8 bit data */
	#define LSF_FORMAT_PCM_16 0x0002        /* Signed 16 bit data */
	#define LSF_FORMAT_PCM_24 0x0003        /* Signed 24 bit data */
	#define LSF_FORMAT_PCM_32 0x0004        /* Signed 32 bit data */
	#define LSF_FORMAT_FLOAT 0x0006         /* 32 bit float data */
	#define LSF_FORMAT_DOUBLE 0x0007        /* 64 bit float data */

	        /* Modes for opening files. */
	#define LSFM_READ 0x10
	#define LSFM_WRITE 0x20
	#define LSFM_RDWR 0x30
	"
	SF_Constants := IdentityDictionary new.
	SF_Constants at: #LSF_FORMAT_WAV put: 16r010000.
	SF_Constants at: #LSF_FORMAT_AIFF put: 16r020000.
	SF_Constants at: #LSF_FORMAT_AU put: 16r030000.
	SF_Constants at: #LSF_FORMAT_RAW put: 16r040000.
	SF_Constants at: #LSF_FORMAT_IRCAM put: 16r0A0000.
	SF_Constants at: #LSF_FORMAT_PCM_S8 put: 16r0001.
	SF_Constants at: #LSF_FORMAT_PCM_16 put: 16r0002.
	SF_Constants at: #LSF_FORMAT_PCM_24 put: 16r0003.
	SF_Constants at: #LSF_FORMAT_PCM_32 put: 16r0004.
	SF_Constants at: #LSF_FORMAT_FLOAT put: 16r0006.
	SF_Constants at: #LSF_FORMAT_DOUBLE put: 16r0007.
	SF_Constants at: #LSFM_READ put: 16r10.
	SF_Constants at: #LSFM_WRITE put: 16r20.
	SF_Constants at: #LSFM_RDWR put: 16r30.
	SF_Constants at: #SEEK_SET put: 0.
	SF_Constants at: #SEEK_CURRENT put: 1.
	SF_Constants at: #SEEK_END put: 2.! !

!Siren.LibSndFileInterface class methodsFor: 'examples'!

example1: filename
	"Demonstrate using the LibSndFileInterface; this will dump some messages to the transcript"
	"LibSndFileInterface example1: 'Data/unbelichtet.aiff'"

	| interface index val data frames channels |
	interface := LibSndFileInterface new.
"Open a sound file"
	index := interface lsf_open: filename with: (self const: #LSFM_READ).
	Transcript cr; show: 'Open returned: ', index printString; cr.
"Read the file format, rate, # of channels, etc."
	val := interface lsf_get_format: index.
	Transcript show: 'getSndFileFormat returned: ', (val printStringRadix: 16); cr.
	val := interface lsf_get_rate: index.
	Transcript show: 'getSndFileRate returned: ', val printString; cr.
	channels := interface lsf_get_channels: index.
	Transcript show: 'getSndFileChannels returned: ', channels printString; cr.
	frames := interface lsf_get_frames: index.
	Transcript show: 'getSndFileFrames returned: ', frames printString; cr.
"Allocate a buffer of shorts"
	data := (WordArray new: frames * channels) copyToHeapAsSigned: #malloc:.
	data beGarbageCollectable.
"Read samples into it"
	val := interface lsf_read_Isamples: index with: data with: frames.
	Transcript show: 'readSndFileISamples returned: ', val printString; cr.
"Dump the first 100 samples"
	0 to: 99 do:
		[ :in | Transcript show: (data at: in) printString; tab].
"Close the file"
	val := interface lsf_close: index.
	Transcript cr; show: 'closeSndFile returned: ', val printString; cr.! !

!Siren.DisplayItem methodsFor: 'initialize-release'!

initialize
	"Initialize the instance variables of the receiver."

	offset := 0@0! !

!Siren.DisplayItem methodsFor: 'accessing'!

color
	"Answer the receiver's display color."

	^color!

color: aVal
	"Set the receiver's display color to the argument."

	color := ((aVal isMemberOf: BlockClosure)
		ifTrue: [aVal value]
		ifFalse: [aVal])!

extent
	"Answer the extent of the receiver (dummy in this class)."

	^1@1!

extent: aPoint
	"Ignored"

!

offset
	"Answer the receiver's offset."

	^offset!

offset: aPoint
	"Set the receiver's offset to the argument."

	offset := aPoint! !

!Siren.DisplayItem methodsFor: 'bounds accessing'!

computePreferredBounds
	"Answer the receiver's bounds--hack."

	^self bounds! !

!Siren.DisplayItem methodsFor: 'copying'!

copy
	"Answer a shallow copy of the receiver."

	^self shallowCopy! !

!Siren.DisplayItem methodsFor: 'testing'!

isDisplayItem
	"Answer whether the receiver is a kind of DisplayItem"

	^true! !

!Siren.DisplayItem methodsFor: 'transforming'!

asVisualComponent
	"Answer an encapsulated version of the receiver which understands
	VisualComponent protocol."

	^TranslatingWrapper on: self!

scaleBy: aPoint
	"Translate the receiver's offset by the argument."

	self extent: self extent * aPoint!

scaledBy: aPoint
	"Translate the receiver's offset by the argument."

	^self copy extent: self extent * aPoint!

translateBy: aPoint
	"Translate the receiver's offset by the argument."

	self offset: self offset + aPoint! !

!Siren.DisplayItem methodsFor: 'displaying'!

displayOn: aGraphicsContext
	"Display the receiver on the argument."

	self subclassResponsibility!

displayPostScriptOn: aPostScriptContext
	"Display the receiver on the argument in PostScript."

	self subclassResponsibility! !

!Siren.DisplayItem methodsFor: 'printing'!

printCompleteOn: aStream depth: depth
	"Print the receiver in the given Stream."

	aStream cr.
	depth timesRepeat: [aStream tab].
	aStream nextPutAll: '(a ', self class name. 
	model == nil ifFalse: [aStream nextPutAll: ' model: '. model printOn: aStream].
	offset == nil ifFalse: [aStream nextPutAll: ' offset: '. offset printOn: aStream].
	aStream nextPut: $).!

printOn: aStream
	"Print the receiver in the given Stream."

	aStream nextPutAll: '(a ', self class name. 
	model == nil ifFalse: [aStream nextPutAll: ' model: '. model printOn: aStream].
	offset == nil ifFalse: [aStream nextPutAll: ' offset: '. offset printOn: aStream].
	aStream nextPut: $); cr.! !

!Siren.DisplayItem class methodsFor: 'instance creation'!

model: aM offset: aPt
	"Answer an instance of DisplayItem with the given instance variables."

	^self new model: aM; offset: aPt!

offset: aPt
	"Answer an instance of DisplayItem with the given instance variables."

	^self new offset: aPt! !

!Siren.DisplayLine methodsFor: 'accessing'!

corner: aPoint
	"Set the corner of the receiver (offset + extent)."

	offset == nil
		ifFalse: [extent := aPoint - offset].
"	end := (offset == nil
			ifTrue: [aPoint]
			ifFalse: [start + aPoint])."
	^self!

extent
	"Answer the extent of the receiver."

	^extent !

extent: aPoint
	"Set the extent of the receiver."

	extent := aPoint
"	end := (offset == nil
			ifTrue: [aPoint]
			ifFalse: [start + aPoint])"!

width: aVal
	width := aVal! !

!Siren.DisplayLine methodsFor: 'bounds accessing'!

bounds
	"Answer the receiver's bounds." 

	^offset extent: ((extent x max: 1) @ (extent y max: 1))! !

!Siren.DisplayLine methodsFor: 'displaying'!

displayOn: aGraphicsContext
	"Display a line between startPoint and endPoint."

"	Transcript show: start printString, '	', end printString; cr."
	color == nil
		ifFalse: [aGraphicsContext paint: color].
	width == nil
		ifFalse: [aGraphicsContext lineWidth: width].
	aGraphicsContext displayLineFrom: offset to: (offset + extent)!

displayPostScriptOn: aPostscriptContext
	"Display the receiver on the argument as a PostScript item."

	aPostscriptContext translate: offset.
	aPostscriptContext rlineto: extent	.
	aPostscriptContext translate: offset negated.! !

!Siren.DisplayLine methodsFor: 'printing'!

printOn: aStream
	"Print the receiver in the given Stream."

	aStream nextPutAll: ' (a ', self class name. 
	model == nil ifFalse: [aStream nextPutAll: ' model: '. model printOn: aStream].
	offset == nil ifFalse: [aStream nextPutAll: ' offset: '. offset printOn: aStream].
	extent == nil ifFalse: [aStream nextPutAll: ' extent: '. extent printOn: aStream].
"	color == nil ifFalse: [aStream nextPutAll: ' color: '. color printOn: aStream]."
	aStream nextPut: $); cr.! !

!Siren.DisplayLine class methodsFor: 'instance creation'!

offset: oPoint extent: ePoint	
	"Answer an initialized instance."

	^((self new) offset: oPoint) extent: ePoint! !

!Siren.DisplayVisual methodsFor: 'accessing'!

bounds
	"Answer the visual's bounds translated by the receiver's translation."

	^offset == nil
		ifTrue: [visual bounds]
		ifFalse: [visual bounds translatedBy: offset]!

visual
	"Answer the receiver's visual."

	^visual!

visual: aVisual
	"Set the receiver's visual."

	visual := aVisual.
"	aVisual palette == nil
		ifTrue: [aVisual palette: MappedPalette colorDefault]
"! !

!Siren.DisplayVisual methodsFor: 'displaying'!

displayOn: aGraphicsContext
	"Display the receiver's visual on the given graphics context."

	color == nil
		ifFalse: [aGraphicsContext paint: color].
	aGraphicsContext translateBy: offset.
	visual displayOn: aGraphicsContext.
	aGraphicsContext translateBy: offset negated.!

displayPostScriptOn: aPostscriptContext
	"Display the receiver on the argument as a PostScript item."

	aPostscriptContext translate: offset.
	aPostscriptContext imageWidth: visual width 
			height: visual height 
			bits: visual bits.
	aPostscriptContext translate: offset negated! !

!Siren.DisplayVisual methodsFor: 'printing'!

printOn: aStream
	"Print the receiver in the given Stream."

	aStream nextPutAll: '(a ', self class name. 
	model == nil ifFalse: [aStream nextPutAll: ' model: '. model printOn: aStream].
	visual == nil ifFalse: [aStream nextPutAll: ' visual: '. visual printOn: aStream].
	offset == nil ifFalse: [aStream nextPutAll: ' offset: '. offset printOn: aStream].
	aStream nextPut: $); cr.!

storeOn: aStream
	"Print the receiver in the given Stream."

	aStream nextPutAll: '('; store: self class. 
	visual == nil ifFalse: [aStream nextPutAll: ' visual: '. visual storeOn: aStream].
	offset == nil ifFalse: [aStream nextPutAll: ' offset: '. offset printOn: aStream].
	aStream nextPut: $)! !

!Siren.DisplayVisual class methodsFor: 'instance creation'!

model: aM visual: aV offset: aPt
	"Answer an instance of DisplayItem with the given instance variables."

	^self new model: aM; visual: aV; offset: aPt!

visual: aV
	"Answer an instance of DisplayItem with the given instance variables."

	^(super new initialize) visual: aV!

visual: aV offset: aPt
	"Answer an instance of DisplayItem with the given instance variables."

	^(super new initialize) visual: aV; offset: aPt!

visual: aV origin: aPt
	"Answer an instance of DisplayItem with the given instance variables."

	^(super new initialize) visual: aV; offset: aPt! !

!Siren.DisplayVisual class methodsFor: 'examples'!

convertMusicConstants
	"Convert all the forms to opaque forms"
	"DisplayVisual convertMusicConstants. DisplayVisual displayMusicConstants"

	| all |
	all := DisplayListView constants.
	all keysAndValuesDo: [ :key :group |
		Transcript show: key printString; cr.
		(group isMemberOf: Dictionary)
			ifTrue: [group keysAndValuesDo:
				[ :str :item | | newI |
				(item isKindOf: DisplayVisual)
					ifTrue: [newI := item.
						item visual: (OpaqueImage figure: item visual 
							shape: (item visual convertToCoverageWithOpaquePixel: 1))]
					ifFalse: [(item isMemberOf: Depth1Image)
							ifTrue: [newI := OpaqueImage figure: item 
												shape: (item convertToCoverageWithOpaquePixel: 1)]].
				group at: str put: newI]].
		((group isMemberOf: Array) or: [(group isMemberOf: OrderedCollection)])
			ifTrue: [group do:
					[ :item |
					(item isMemberOf: OpaqueImage)
						ifFalse: [item visual: (OpaqueImage figure: item visual 
								shape: (item visual convertToCoverageWithOpaquePixel: 1))]]]]!

displayMusicConstants
	"Draw a nice table of the hierarchical image dictionary."
	"DisplayVisual displayMusicConstants"

	| win xstep y i ystep comp |
	win := ScheduledWindow model: nil label: 'MusicConstants' 
			minimumSize: 700@750.
	comp := CompositePart new.
	xstep := 64.
	ystep := 48.
	y := 0.
	DisplayListView constants keysAndValuesDo: [ :title :dict |
		i := 0.
		comp add: (DisplayString string: title) at: (xstep@y).
		y := y + (ystep/2).
		dict keysAndValuesDo:
			[ :str :item |
			comp add: (DisplayString string: (str respondsToArithmetic 
					ifTrue: [str printString] ifFalse: [str "clipTo: 6"]))
			 at: ((i*xstep+8)@y).
			comp add: item at: ((i*xstep+8)@(y+(ystep/2))).
			i := i + 1.
			i = 14 ifTrue: [i := 0. y := y + (ystep * 1.5)]].
		y := y + ystep + 24].
	win component: (BoundedWrapper on: comp).
	win open!

storeMusicConstants
	"Write out all the forms to opaque forms"
	"DisplayVisual storeMusicConstants"

	| fil all |
	fil := 'MusicConstants.st' asFilename writeStream.
	all := DisplayListView constants.
	all keysAndValuesDo: [ :key :group |
		Transcript show: key printString; cr.
		(group isMemberOf: Dictionary)
			ifTrue: [fil cr; nextPutAll: 'Siren.DisplayListView constants at: ', key printString,
						 ' put: IdentityDictionary new.'; cr.
					group keysAndValuesDo:
					[ :str :item | 
					fil cr; nextPutAll: '(Siren.DisplayListView constants at: ', key printString, 
						') at: ''', str, ''' asSymbol put: '.
					item storeOn: fil.
					fil nextPut: $!! ]].
		((group isMemberOf: Array) or: [(group isMemberOf: OrderedCollection)])
			ifTrue: [fil cr; nextPutAll: 'Siren.DisplayListView constants at: ', key printString,
						 ' put: OrderedCollection new.'; cr.
				group do:
					[ :item |
					fil cr; nextPutAll: '(Siren.DisplayListView constants at: ', key printString, ') add: '.
					item storeOn: fil.
					fil nextPut: $!! ]]].
	fil close!

visualExample
	"Open a display list view with randomly-placed random-color visuals over the given extent."
	"DisplayVisual visualExample"

	(DisplayList visualsX: 600 byY: 600) display! !

!Siren.EventModifier methodsFor: 'accessing'!

function
	"Answer the receiver's 'function'."

	^function!

function: anObject
	"Set the receiver's instance variable 'function' to be anObject."

	function := anObject!

index
	"Answer the receiver's 'index'."

	^index!

index: anObject
	"Set the receiver's instance variable 'index' to be anObject."

	index := anObject!

scale
	"Answer the receiver's 'scale'."

	^scale!

scale: anObject
	"Set the receiver's instance variable 'scale' to be anObject."

	scale := anObject!

selector
	"Answer the receiver's 'selector'."

	^selector!

selector: anObject
	"Set the receiver's instance variable 'selector' to be anObject."

	selector := anObject!

start
	"Answer the receiver's 'start'."

	^start!

start: anObject
	"Set the receiver's instance variable 'start' to be anObject."

	start := anObject!

stop
	"Answer the receiver's 'stop'."

	^stop!

stop: anObject
	"Set the receiver's instance variable 'stop' to be anObject."

	stop := anObject! !

!Siren.EventModifier methodsFor: 'application'!

applyTo: evtList

	evtList applyFunction: function to: selector startingAt: 0!

valueIn: evtList at: time

	^function at: (time / evtList dur)! !

!Siren.EventModifier class methodsFor: 'instance creation'!

new
	"Create a new modifier and initialize it"

	^ super new initialize.! !

!Siren.TimeSequenceLayoutManager methodsFor: 'accessing'!

timeOffset: aNumber
	"Set the receiver's time offset."

	timeOffset := aNumber!

timeScale: aNumber
	"Set the receiver's time scale."

	timeScale := aNumber! !

!Siren.TimeSequenceLayoutManager methodsFor: 'generating'!

addItem: anAss atNode: aPath in: aDisplayList 
	"Add the given item to the display list."

	| path2 anE displayPoint item |
	anE := anAss value.
	anE hasItems
		ifTrue: [^anE do:  
			[ :i | 
			path2 := aPath copy.
			path2 addLast: anE.
			self addItem: anAss key + i key => i value
					atNode: path2
					in: aDisplayList]].
	displayPoint := self displayPointFor: anAss.
	item := view itemFor: anAss.
	item offset: displayPoint.
	aDisplayList add: item atPath: aPath!

displayPointFor: anAss 
	"Answer the display point for the argument."

	| dPoint |
	dPoint := (anAss key asMS value - timeOffset / timeScale) asInteger.
	(orientation == nil or: [orientation == #left])
		ifTrue: [dPoint := dPoint @ 0]
		ifFalse: [orientation == #top
			ifTrue: [dPoint := 0 @ dPoint]].
	^dPoint! !

!Siren.Peal methodsFor: 'accessing'!

at: index
	"Answer the element in the base collection currently mapped into the index position."

	^ base at: (self basicAt: index)!

at: index put: object

	^ self error: 'Can''t modify a peal by at:put:'!

currentChange
	"Answer the base as mapped by the current change."

	^(1 to: self size) collect: [ :i  | self at: i ]!

finished
	"Answer is the peal has completed it's last change"

	^ finished!

setBase: baseSet
	"Initialize the base set and everything else."

	base := baseSet.
	directions := Array new: self size withAll: 1.
	position := 1.
	1 to: self size do: [:i | self basicAt: i put: i].
	finished := false.!

size
	"Redone here because SequencableCollection overrides it."

	^ self basicSize! !

!Siren.Peal methodsFor: 'changing'!

change
	"Produce the next change in the peal"

	self changeOrder: 1 bounds: (1 to: self size)! !

!Siren.Peal methodsFor: 'playing'!

eventList

	| list |
	list := EventList new.
	self playOn: list durations: 125 meter: 125 at: 0.
	^list!

play
	self eventList play!

playOn0: out durations: dur meter: mtr at: start
	"Play the receiver"

	| time siz |
	time := start.
	siz := self size.
	[finished] whileFalse:
		[1 to: siz do: 
			[ :i | out play: (self at: i) at: time dur: dur amp: 100. 
				time := time + mtr ].
		self change.
		1 to: siz - 1 do: 
			[ :i | out play: (self at: i) at: time dur: dur amp: 100. 
				time := time + mtr ].
		out play: (self at: siz ) at: time dur: dur + mtr amp: 100.  
				time := time + mtr + mtr.
		self change].
	1 to: siz do: 
			[ :i | out play: (self at: i) at: time dur: dur amp: 100. 
				time := time + mtr ].!

playOn: vox
	self eventList playOn: vox!

playOn: out durations: dur meter: mtr at: start
	"Play the receiver"

	| time siz |
	time := start.
	siz := self size.
	[finished] whileFalse:
		[1 to: siz do: 
			[ :i | out add: time => ((self at: i) note, dur msec, 100 velocity). 
				time := time + mtr].
		self change.
		1 to: siz - 1 do: 
			[ :i | out add: time => ((self at: i) note, dur msec, 100 velocity). 
				time := time + mtr].
		out add: time => ((self at: siz) note, (dur + mtr) msec, 100 velocity).  
				time := time + mtr + mtr.
		self change].
	1 to: siz do: 
			[ :i | out add: time => ((self at: i) note, dur msec, 100 velocity). 
				time := time + mtr].! !

!Siren.Peal methodsFor: 'private'!

changeOrder: n bounds: range
	"Produce the next change in the peal by moving the element n within the range.  If the element wants to move outside the range, alter it's direction and move the next element (recursively call this)."

	| neighbor location |
	location := self indexOfElement: n.
	neighbor := location + (directions at: n).

	(n = self size) ifTrue:
		[(n = 1) ifFalse:
			[self swap: location with: neighbor.
			directions at: n-1 put: (directions at: n-1) negated].
		finished := true.
		^ self].

	(range includes: neighbor) ifTrue:
		[self swap: location with: neighbor]
	ifFalse:
		[directions at: n put: (directions at: n ) negated.
		self changeOrder: n+1 bounds: (range copyWithout: location)]!

indexOfElement: n
	"Answer the index the anElement item  (i.e. the n-th item, not the item = to n) within the receiver."

	1 to: self size do:
		[:i | (self basicAt: i) = n ifTrue: [^ i]].
	^ self error: 'An element does not exist in this peal'!

swap: index1 with: index2
	"We need to redefine this, since we redefined at: & at:put: to map trhrough the base collection"

	| temp |
	temp := self basicAt: index1.
	self basicAt: index1 put: (self basicAt: index2).
	self basicAt: index2 put: temp! !

!Siren.Peal class methodsFor: 'instance creation'!

new: size
	"Create a new peal on the given size with 1..size as the base set."

	| newPeal |
	newPeal := self basicNew: size.
	newPeal setBase: (1 to: size).
	^ newPeal!

upon: baseSet
	"Create a new peal on the base set given"

	| newPeal |
	newPeal := self basicNew: baseSet size.
	newPeal setBase: baseSet.
	^ newPeal! !

!Siren.Peal class methodsFor: 'examples'!

pealExample1
	"Play a simple bell peal"
	"Peal pealExample1"

	(Peal upon: #(60 62 64 65)) eventList edit.! !

!Siren.PortMidiInterface methodsFor: 'types'!

OEoop
	<C: typedef void * OEoop>! !

!Siren.PortMidiInterface class methodsFor: 'examples'!

example1
	"Demonstrate using the PortMidiInterface"
	"PortMidiInterface example1"
	"PortMidiInterface unloadLibraries"

	| pm val index |
	pm := PortMidiInterface new.
"Initialize PM"
	val := pm pm_initialize.
	Transcript cr; show: 'PM_init returned: ', val printString; cr.
"Open PM device 1 for output"
	index := pm pm_open: 1 with: 0.
	Transcript show: 'PM_open returned: ', val printString; cr.

	pm pm_test.
	Transcript show: 'PM_test...'; cr.

	val := pm pm_close: index.
	Transcript show: 'PM_close returned: ', val printString; cr.
	val := pm pm_terminate.
	Transcript show: 'PM_terminate returned: ', val printString; cr.!

testMIDI
	"Demonstrate using the PortMidiInterface to call the test note function in the driver"
	"PortMidiInterface testMIDI"
	"PortMidiInterface unloadLibraries"

	| pm val |
	pm := PortMidiInterface new.
	val := pm pm_initialize.
	Transcript cr; show: 'PM_init returned: ', val printString; cr.
	val := pm pm_open: 1 with: 0.
	Transcript show: 'PM_open returned: ', val printString; cr.
	pm pm_test.			"call the test code in the driver"
	val := pm pm_close: val.
	Transcript show: 'PM_close returned: ', val printString; cr.
	val := pm pm_terminate.
	Transcript show: 'PM_terminate returned: ', val printString; cr.!

testMIDI2
	"Demonstrate using the PortMidiInterface to play a note on/off cmd pair"
	"PortMidiInterface testMIDI2"
	"PortMidiInterface unloadLibraries"

	| pm val which |
	pm := PortMidiInterface new.
"Init"
	val := pm pm_initialize.
	Transcript cr; show: 'PM_init returned: ', val printString; cr.
"Open"
	which := pm pm_open: 1 with: 0.
	Transcript show: 'PM_open returned: ', which printString; cr.
"Send note on"
	pm pm_write_data3: which with: 144 with: 48 with: 120.
	1 wait.
"Send note off"
	pm pm_write_data3: which with: 128 with: 48 with: 120.
	1 wait.
"Close"
	val := pm pm_close: which.
	Transcript show: 'PM_close returned: ', val printString; cr.
"Terminate"
	val := pm pm_terminate.
	Transcript show: 'PM_terminate returned: ', val printString; cr.! !

!Siren.ScheduleRecord methodsFor: 'accessing'!

next
	"Answer the receiver's 'next'."

	^next!

next: anObject
	"Set the receiver's instance variable 'next' to be anObject."

	next := anObject!

previous
	"Answer the receiver's 'previous'."

	^previous!

previous: anObject
	"Set the receiver's instance variable 'previous' to be anObject."

	previous := anObject!

start
	^self key! !

!Siren.MusicMagnitude methodsFor: 'accessing'!

, anArgument
	"Answer en Event with the given magnitude as its first property."
	"440 Hz, 250 msec"

	| event |
	event := MusicEvent new.
	event perform: self species propertyName with: self.
	anArgument isMusicMagnitude ifTrue:
		[event perform: anArgument species propertyName with: anArgument].
	^event!

value
	"Answer the instance variable 'value'."

	^value!

value: theValue
	"Accept the argument, 'theValue', as the new instance variable 'value'."

	value := theValue! !

!Siren.MusicMagnitude methodsFor: 'arithmetic'!

* aValue
	"Answer the product of the receiver and the argument"

	| arr more less |
	^(aValue class == self class)
		ifTrue: [self class new value: (self value * aValue value)]
		ifFalse: [(aValue species == self species)
			ifTrue: [arr := self species generalize: self and: aValue.
				more := arr first.
				less := arr last.
				more class new value: (more value * (less coerceTo: more class) value)]
			ifFalse: [self class new value: (value * aValue)]]!

+ aValue
	"Answer the sum of the receiver and the argument, doing generality-based
	class coercion within a species using auto-generated coercion messages."
	"If the same class, add values directly."
	"If the same species, add values coercing generality."

	| arr more less |
	^(aValue class == self class)
		ifTrue: [self class new value: (self value + aValue value)]
		ifFalse: [(aValue species == self species)
			ifTrue: [arr := self species generalize: self and: aValue.
				more := arr first.
				less := arr last.
				more class new value: (more value + (less coerceTo: more class) value)]
			ifFalse: [self class new value: (value + aValue)]]!

- aValue
	"Answer the difference of the receiver and the argument"

	| arr more less |
	^(aValue class == self class)
		ifTrue: [self class new value: (self value - aValue value)]
		ifFalse: [(aValue species == self species)
			ifTrue: [arr := self species generalize: self and: aValue.
				more := arr first.
				less := (arr last coerceTo: more class) value.
				more == self
					ifTrue: [more class new value: (more value - less)]
					ifFalse: [more class new value: (less - more value)]]
			ifFalse: [self class new value: (value - aValue)]]!

/ aValue
	"Answer the quotient of the receiver and the argument"

	| arr more less |
	^(aValue class == self class)
		ifTrue: [self class new value: (self value / aValue value)]
		ifFalse: [(aValue species == self species)
			ifTrue: [arr := self species generalize: self and: aValue.
				more := arr first.
				less := (arr last coerceTo: more class) value.
				more == self
					ifTrue: [more class new value: (more value / less)]
					ifFalse: [more class new value: (less / more value)]]
			ifFalse: [self class new value: (value / aValue)]]! !

!Siren.MusicMagnitude methodsFor: 'testing'!

isImmediate
	"Answer whether the receiver has an immediate representation."

	^self units ~~ nil!

isMusicMagnitude
	"Answer true for the receiver and all subsclasses."

	^true! !

!Siren.MusicMagnitude methodsFor: 'comparing'!

< aValue
	"Answer whether the receiver is less than the argument"
	"100 msec < 0.2 sec"

	| arr more |
	^(aValue class == self class)
		ifTrue: [self value < aValue value]
		ifFalse: [(aValue species == self species)
			ifTrue: [arr := self species generalize: self and: aValue.
				more := arr first.
				more == self 		"if I'm the more general"
					ifTrue: [aValue value < ((self coerceTo: aValue class) value)]
					ifFalse: [self value < ((aValue coerceTo: self class) value)]]
			ifFalse: [value < aValue]]!

= aValue
	"Answer whether the receiver and the argument are equal."

	| arr more less |
	^(aValue class == self class)
		ifTrue: [self value = aValue value]
		ifFalse: [(aValue species == self species)
			ifTrue: [arr := self species generalize: self and: aValue.
				more := arr first.
				less := arr last.
				(more value = (less coerceTo: more class) value)]
			ifFalse: [value = aValue]]!

hash
	"Answer a SmallInteger unique to the receiver."

	^value hash! !

!Siren.MusicMagnitude methodsFor: 'converting'!

adaptToNumber: rcvr andSend: selector
	"If I am involved in arithmetic with a number, convert me to a number."

	^self class value: (rcvr perform: selector with: self value)!

coerceTo: type
	"Coerce the receiver to the given class and answer a new object."

	^self perform: type ddMsgName!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^self class ddMsgName!

msec
	"Answer a MSecondDuration."

	^self asMsec! !

!Siren.MusicMagnitude methodsFor: 'printing'!

printOn: aStream
	"Print the receiver on the argument as a MM declaration."

	^self printOn: aStream parens: true!

printOn: aStream parens: useParens
	"Print the receiver on the argument as a MM declaration."

	useParens
		ifTrue: [self isImmediate
			ifFalse: [aStream nextPutAll: '(', self species name, ': ', 
				self printValue, ')']
			ifTrue: [aStream nextPutAll: '(', self printValue, self units, ')']]
		ifFalse: [self isImmediate
			ifFalse: [aStream nextPutAll: self species name, ': ', self printValue]
			ifTrue: [aStream nextPutAll: self printValue, self units]]!

printString
	"Answer a String whose characters are a description of the receiver."

	| aStream |
	aStream := WriteStream on: (String new: 16).
	self printOn: aStream  parens: true.
	^aStream contents!

printStringParens: bool
	"Answer a String whose characters are a description of the receiver."

	| aStream |
	aStream := WriteStream on: (String new: 16).
	self printOn: aStream  parens: bool.
	^aStream contents!

printValue

	value isString ifTrue: [^value].
"	value isFloat ifTrue: [^value printPlaces: 4].
"	^value printString!

storeOn: aStream
	"Store the receiver on the argument as a MM declaration."

	aStream nextPutAll: '(', self class name, ' value: ', value printString, ')'!

units
	"Answer the units string of the receiver."

	^nil! !

!Siren.MusicMagnitude methodsFor: 'private'!

species
	"Answer MusicMagnitude."

	^MusicMagnitude! !

!Siren.MusicMagnitude class methodsFor: 'instance creation'!

value: aValue
	"Answer and instance with the argument as its value."

	^self new value: aValue! !

!Siren.MusicMagnitude class methodsFor: 'class constants'!

color
	"Answer the default color to display instances with (hack for making pretty graphs)."

	| spec |
	spec := self new species.
	^(spec == MusicMagnitude)
		ifTrue: [ColorValue gray]
		ifFalse: [spec color]!

propertyName
	"Answer the receiver class's default property name."

	^self subclassResponsibility!

relativeMember
	"Answer the receiver class's species member used as a relative magnitude."

	^self subclassResponsibility! !

!Siren.MusicMagnitude class methodsFor: 'objects from disk'!

initializeTypeCodes
	"Set up the class dictionary of subclass typecodes."
	"MusicMagnitude initializeTypeCodes"
	"TypeCodes"	"TypeClasses" 

	| classes |
	TypeCodes := IdentityDictionary new.		"class --> int"
	TypeClasses := IdentityDictionary new.	"int -> class"
				"Class list hard-coded for portability to new versions"
	classes := #(RatioDuration SymbolicLoudness Sharpness Position ConditionalDuration 
		PField MIDIVelocity SecondDuration Meter NumericalMagnitude Chronos Chroma 
		Spatialization Length RatioPitch Amplitude IntervalMagnitude ModeMember 
		NominalMagnitude DBLoudness HertzPitch RatioMagnitude MusicModel 
		MSecondDuration Ergon Duration Pitch SymbolicPitch RatioLoudness 
		OrdinalMagnitude MIDIPitch Directionality Positus).
	1 to: classes size do:
		[ :ind |
		TypeCodes at: (Smalltalk at: (classes at: ind)) put: ind + 64.
		TypeClasses at: ind + 64 put: (Smalltalk at: (classes at: ind))].!

readDataFrom: refStream
	"Store an instance of some music magnitude subclass on the given reference stream compactly and quickly."

	| key iClass value |
	key := refStream byteStream peek.
							"Special key for nil, skip it"
	key == 1 ifTrue: [refStream byteStream next.
			^nil].
							"Special key for music magnitudes"
"	key == 128	
		ifFalse: [self error: 'Stream does not contain a music magnitude.']."
								"Class is stored as a byte key"
	iClass := TypeClasses at: (refStream byteStream next) 
					ifAbsent: [self error: 'Stream does not contain a music magnitude.'].
	value := refStream next.		"Value stored as an object"
	^iClass value: value!

store: inst on: refStream
	"Store an instance of some music magnitude subclass on the given reference stream compactly and quickly."

	inst ifNil: [^refStream nextPut: inst].
"	refStream byteStream nextPut: 128.	"	"special key for music magnitudes"
											"Store class as a type key"
	refStream byteStream nextPut: (TypeCodes at: inst class
								ifAbsent: [inst class typeCode]).
	refStream nextPut: inst value.			"Store value as an object"! !

!Siren.NumericalMagnitude methodsFor: 'double dispatching'!

adaptInteger: val
	^self class value: val!

adaptToInteger
	^self value! !

!Siren.MSecondDuration methodsFor: 'converting'!

adaptToFloat
	"Answer a float of seconds."

	^self asSeconds value!

asBeat
	"Answer a RatioDuration."

	^RatioDuration new value: ((value / 1000) asFloat)!

asMS
	"Answer a MSecondDuration."

	^self asMsec!

asMsec
	"Answer a MSecondDuration."

	^self!

asRatio
	"Answer a RatioDuration."

	^RatioDuration new value: ((value / 1000) asFloat)!

asSec
	"Answer a float of seconds."

	^self asSeconds!

asSeconds
	"Answer a float of seconds."

	^SecondDuration new value: (value / 1000) asFloat!

mostGeneral
	"Answer the receiver in seconds."

	^self asSec! !

!Siren.MSecondDuration methodsFor: 'printing'!

units
	"Answer the units string of the receiver."

	^' msec'! !

!Siren.MSecondDuration methodsFor: 'private'!

species
	"Answer Duration."

	^Duration! !

!Siren.MSecondDuration class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asMsec! !

!Siren.OrdinalMagnitude methodsFor: 'initialize-release'!

release
	"Release the receiver's table."

	| relatives |
	table == nil
		ifFalse: [relatives := table keys.
			table := nil.
			relatives do: [ :m | m release]]! !

!Siren.OrdinalMagnitude methodsFor: 'accessing'!

hash
	"Answer a SmallInteger unique to the receiver.  Essential.  See  
	Object documentation whatIsAPrimitive."

	<primitive: 75>
	^self!

table
	"Answer the receiver's loop-up table--its or the class'."

	^table == nil
		ifFalse: [table]
		ifTrue: [self class table == nil
			ifFalse: [self class table at: self]
			ifTrue: [table := Dictionary new.
					table]]!

value
	"Signal an error."

	^self error: 'OrdinalMagnitudes don''t have values!!'!

value: theValue
	"Signal an error."

	^super value: theValue
"
	^self error: 'OrdinalMagnitudes don''t have values!!'
"! !

!Siren.OrdinalMagnitude methodsFor: 'arithmetic'!

* aValue
	"Answer that it is an error to attempt arithmetic with ordinal magnitudes."

	^self error: 'Arithmetic is undefined for this receiver.'!

+ aValue
	"Answer that it is an error to attempt arithmetic with ordinal magnitudes."

	^self error: 'Arithmetic is undefined for this receiver.'!

- aValue
	"Answer that it is an error to attempt arithmetic with ordinal magnitudes."

	^self error: 'Arithmetic is undefined for this receiver.'!

/ aValue
	"Answer that it is an error to attempt arithmetic with ordinal magnitudes."

	^self error: 'Arithmetic is undefined for this receiver.'!

= aValue
	"Answer whether the receiver and the argument are equivalent."

	^self == aValue! !

!Siren.OrdinalMagnitude methodsFor: 'converting'!

mostGeneral
	"Answer that it is an error to attempt coercion with ordinal magnitudes."

	^self error: 'Coercion is undefined for this receiver.'! !

!Siren.OrdinalMagnitude methodsFor: 'ordering'!

< anotherOMag
	"Answer whether the receiver is less than the argument."

	^(self table includesKey: anotherOMag)
		ifTrue: [((self table at: anotherOMag) == #<)]
		ifFalse: [((self table associations detect:
					[ :z |
					(z value == #<) and: [z key < anotherOMag]]
			ifNone: [nil]) ~~ nil)]!

<< anotherOMag
	"Specify that the receiver is less than the argument."

	(self table includesKey: anotherOMag)
		ifTrue: [(self table at: anotherOMag) == #<
			ifFalse: [^self error: 'relationship ',
							(self table at: anotherOMag),
							' already specified.']]
		ifFalse: [self table at: anotherOMag put: #<.
				anotherOMag >> self]!

<= anotherOMag
	"Answer whether the receiver is less than or equal to the argument."

	^(self table includesKey: anotherOMag)
		ifTrue: [(((self table at: anotherOMag) == #<)
				or: [(self table at: anotherOMag) == #<=])
				or: [(self table at: anotherOMag) == #=]]
		ifFalse: [(self table associations detect:
			[ :r |
			(((r value == #<) or: [r value == #=])
							or: [r value == #<=])
					and: [r key <= anotherOMag]]
			ifNone: [nil]) ~~ nil]!

=< anotherOMag
	"Specify that the receiver is less than or equal to the argument."

	(self table includesKey: anotherOMag)
		ifTrue: [(self table at: anotherOMag) == #<=
			ifFalse: [^self error: 'relationship ',
							(self table at: anotherOMag),
							' already specified.']]
		ifFalse: [self table at: anotherOMag put: #<=.
				anotherOMag => self]!

== anotherOMag
	"Specify that the receiver is equal to the argument."

	(self table includesKey: anotherOMag)
		ifTrue: [(self table at: anotherOMag) == #=
			ifFalse: [^self error: 'relationship ',
							(self table at: anotherOMag),
							' already specified.']]
		ifFalse: [self table at: anotherOMag put: #=.
				anotherOMag == self]!

=> anotherOMag
	"Specify that the receiver is greater than or equal to the argument."

	(self table includesKey: anotherOMag)
		ifTrue: [(self table at: anotherOMag) == #>=
			ifFalse: [^self error: 'relationship ',
							(self table at: anotherOMag),
							' already specified.']]
		ifFalse: [self table at: anotherOMag put: #>=.
				anotherOMag =< self]!

=? anotherOMag
	"Answer whether the receiver is equal to the argument."

	^self == anotherOMag
		ifTrue: [true]
		ifFalse: [(self table includesKey: anotherOMag)
			ifTrue: [((self table at: anotherOMag) == #=)]
			ifFalse: [((self table associations detect:
					[ :z |
					(z value == #=) and: [z key =? anotherOMag]]
				ifNone: [nil]) ~~ nil)]]!

> anotherOMag
	"Answer whether the receiver is greater than the argument."

	^(self table includesKey: anotherOMag)
		ifTrue: [(self table at: anotherOMag) == #>]
		ifFalse: [(self table associations detect:
					[ :z |
					(z value == #>) and: [z key > anotherOMag]]
			ifNone: [nil]) ~~ nil]!

>= anotherOMag
	"Answer whether the receiver is greater than or equal to the argument."

	^(self table includesKey: anotherOMag)
		ifTrue: [(((self table at: anotherOMag) == #>)
				or: [(self table at: anotherOMag) == #>=])
				or: [(self table at: anotherOMag) == #=]]
		ifFalse: [(self table associations detect:
			[ :r |
			(((r value == #>) or: [r value == #=])
							or: [r value == #>=])
					and: [r key >= anotherOMag]]
			ifNone: [nil]) ~~ nil]!

>> anotherOMag
	"Specify that the receiver is greater than the argument."

	(self table includesKey: anotherOMag)
		ifTrue: [(self table at: anotherOMag) == #>
			ifFalse: [^self error: 'relationship ',
							(self table at: anotherOMag),
							' already specified.']]
		ifFalse: [self table at: anotherOMag put: #>.
				anotherOMag << self]! !

!Siren.OrdinalMagnitude methodsFor: 'printing'!

printOn: aStream
	"Print the receiver as an ordinal magnitude."

	value == nil
		ifTrue: [aStream nextPutAll:
						(self class name "copyFrom: 1 to: 3")]
		ifFalse: [aStream nextPutAll: value printString].
! !

!Siren.OrdinalMagnitude class methodsFor: 'class initialization'!

flush
	"Release the shared class table and all instances."
	"OrdinalMagnitude allSubclassesDo: [ :t | t flush]"

	Table == nil
		ifFalse: [Table keysDo: [ :d | d release].
				Table := nil]
		ifTrue: [self allInstancesDo: [ :d | d release]]!

useTable
	"Set up a shared class table for all instances."

	Table := Dictionary new.! !

!Siren.OrdinalMagnitude class methodsFor: 'instance creation'!

new
	"Answer a new instance and, if it's in use, add it to the table."

	| me |
	me := super new.
	Table == nil ifFalse: [Table at: me put: Dictionary new].
	^me!

value: aValue
	"Answer an instance"

	^self new value: aValue! !

!Siren.OrdinalMagnitude class methodsFor: 'table access'!

table
	"Answer the class' instance look-up table."

	^Table!

values
	"Answer the sorted values."
	
	^Table == nil
		ifTrue: [self allInstances asSortedCollection]
		ifFalse: [Table keys asSortedCollection]! !

!Siren.SecondDuration methodsFor: 'converting'!

asMsec
	"Answer a MSecondDuration."

	^MSecondDuration new value: (value * 1000) truncated!

asRatio
	"Answer a RatioDuration."

	^RatioDuration new value: value!

asSeconds
	"Answer a float of seconds."

	^self! !

!Siren.SecondDuration methodsFor: 'printing'!

printOn: aStream
	"Print the receiver on the argument as a MM declaration."

	| valstr |
	valstr := value printString.
	valstr size > 6
		ifTrue: [valstr := valstr copyFrom: 1 to: 5].
	aStream nextPutAll: '(', valstr, self units, ')'!

units
	"Answer the units string of the receiver."

	^' sec'! !

!Siren.SecondDuration class methodsFor: '-- all --'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asSec! !

!Siren.IntervalMagnitude methodsFor: 'accessing'!

value: theValue
	"Accept theValue, as the new instance variable 'value' -- perform  range-checking"

	| range |
	range := self class range.
	range == nil
		ifFalse: [((theValue < range first) or: [theValue > range last])
			ifTrue: [self error: 'value out of range.']].
	value := theValue! !

!Siren.IntervalMagnitude class methodsFor: 'class inst var access'!

range
	"Answer the class' range."

	^range!

range: anInterval
	"Set the class' range."

	range := anInterval! !

!Siren.MusicModel class methodsFor: 'generality'!

generality
	"Answer the class inst var for the generality table."

	^generalityTable!

generalize: aMag and: otherMag
	"Answer an array of the two arguments with the most general first."

	^(generalityTable at: aMag class) > (generalityTable at: otherMag class)
		ifTrue: [Array with: aMag with: otherMag]
		ifFalse: [Array with: otherMag with: aMag]! !

!Siren.MusicModel class methodsFor: 'examples'!

examples

	"Pitch generalize: (#c pitch) and: (21 key)"
	"Pitch generalize: (#c pitch) and: (220 Hz)"
	"Pitch generalize: (220 Hz) and: (#c pitch)"
	"Amplitude generalize: (21 dB) and: (88 velocity)"
	"Amplitude generalize: (#ff ampl) and: (127 velocity)"
	"Duration generalize: (100 msec) and: ((1/4) beat)"
	"Duration generalize: (100 msec) and: (0.3 sec)"

	 (#ff ampl) - (32 velocity)! !

!Siren.Amplitude class methodsFor: 'instance creation'!

value: aValue
	"Answer a new instance of a member of my species."

	aValue species == Amplitude ifTrue: [^aValue].
	(aValue isMemberOf: SmallInteger)
			ifTrue: [^MIDIVelocity new value: aValue].
	(aValue isKindOf: LimitedPrecisionReal)
			ifTrue: [^RatioLoudness new value: aValue].
	(aValue isMemberOf: Fraction)
			ifTrue: [^RatioLoudness new value: aValue].
	(aValue isKindOf: String)
			ifTrue: [^SymbolicLoudness new value: aValue asSymbol].
	^self new value: aValue! !

!Siren.Amplitude class methodsFor: 'class constants'!

color
	"Answer the default color to display instances with."

	^ColorValue blue!

initialize
	"Set up the class inst var, a generality table."
	"Amplitude initialize"
	"Amplitude generalize: (21 dB) and: (88 velocity)"
	"Amplitude generalize: (#ff ampl) and: (127 velocity)"

	generalityTable :=
		((DBLoudness -> 80),
		(RatioLoudness -> 80),
		(MIDIVelocity -> 40),
		(SymbolicLoudness -> 20))!

initializeGenerality
	"Set up the class inst var, a generality table."
	"Amplitude initialize"
	"Amplitude generalize: (21 dB) and: (88 velocity)"
	"Amplitude generalize: (#ff ampl) and: (127 velocity)"

	generalityTable :=
		((DBLoudness -> 80),
		(RatioLoudness -> 80),
		(MIDIVelocity -> 40),
		(SymbolicLoudness -> 20))!

mostGeneral
	"Answer the most general-purpose duration--relative"

	^#asRatio!

propertyName
	"Answer the receiver class's default property name."

	^#loudness:!

relativeMember
	"Answer the receiver class's species member used as a relative magnitude."

	^RatioLoudness!

species
	"Answer Amplitude."

	^Amplitude! !

!Siren.Amplitude class methodsFor: 'examples'!

example
	"Print a simple message to the transcript demonstrating the various types."
	"SymbolicLoudness nameMap inspect"
	"Amplitude example"

	| me |
	me := Amplitude value: #mf.
	Transcript show: me printString; cr;
		show: me asRatio printString; cr;
		show: me asMIDI printString; cr.
	me inspect! !

!Siren.ConditionalDuration methodsFor: 'accessing'!

valueAt: anArg
	"Answer the result of passing the argument to the receiver's block."

	^value value: anArg!

waitUntil: anArg
	"Cycle the receiver until the argument fulfills the receiver's block."

	[value value: anArg value] whileFalse: [Processor yield]! !

!Siren.ConditionalDuration methodsFor: 'arithmetic'!

* aValue
	"Answer that it is an error to attempt arithmetic with this magnitude."

	^self error: 'Arithmetic is undefined for this receiver.'!

+ aDuration
	"Answer the sum of the receiver and the argument--the composition of two blocks"

	| block |
	^aDuration class == self class 
		ifTrue: [block _ aDuration value.
			self class new value:
					[ :a | (value value: a) and: [block value: a]]]
		ifFalse: [block := aDuration value.
			self class new value:
					[ :a | (value value: a) and: [a > block]]]!

- aValue
	"Answer that it is an error to attempt arithmetic with this magnitude."

	^self error: 'Arithmetic is undefined for this receiver.'!

/ aValue
	"Answer that it is an error to attempt arithmetic with this magnitude."

	^self error: 'Arithmetic is undefined for this receiver.'! !

!Siren.ConditionalDuration methodsFor: 'converting'!

mostGeneral
	"It is an error to try this here--we implement what's ok for CDs"

	^self error: 'Arithmetic is not possible with conditional durations.'! !

!Siren.ConditionalDuration methodsFor: 'private'!

species
	"Answer Duration."

	^Duration! !

!Siren.ConditionalDuration class methodsFor: 'examples'!

example
	"Print a simple message to the transcript demonstrating the various types."
	"ConditionalDuration example"

	| t0 t me |
	t0 := Time millisecondClockValue.		"get the time"
					"make a duration 'until the argument is > 3000'"
	me := ConditionalDuration value: [ :x | x > 3000].
	Transcript cr; show: t0 printString; cr.
					"use it to wait 3000 msec."
	me waitUntil: [Time millisecondClockValue - t0].
	t := Time millisecondClockValue.	
	Transcript show: (t - t0) printString; cr.
					"make a duration 'until 3 seconds from now'"
	me := ConditionalDuration value: [ :x | Time millisecondClockValue > (t + 3000)].
					"wait it out."
	me waitUntil: [].
	t := Time millisecondClockValue.	
	Transcript show: (t - t0) printString; cr.
	me inspect! !

!Siren.NominalMagnitude class methodsFor: 'class instance variables'!

nameMap
	"Answer the class instance variable NameMap."

	^NameMap! !

!Siren.SymbolicPitch methodsFor: 'accessing'!

accidental
	"Answer the receiver's accidental, if any."

	| c |
	(value size > 1)
		ifFalse: [^nil]
		ifTrue: [c := value at: 2].
	^((c == $#) or: [c == $b])
		ifTrue: [c]
		ifFalse: [nil]!

fracPitch
	"return microtonal offset as fractions of a halfstep"

	^fracPitch!

fracPitch: aValue
	"set microtonal offset as fractions of a halfstep"

	fracPitch := aValue.! !

!Siren.SymbolicPitch methodsFor: 'arithmetic'!

+ aValue
	"Answer the sum of the receiver and the argument-handle adding Integers as a special case."

	^(aValue isMemberOf: self class)
		ifTrue: [self class new value: (value + aValue value)]
		ifFalse: [(aValue isMemberOf: SmallInteger)
			ifTrue: [SymbolicPitch fromMIDI: (self asMIDI value + aValue)]
			ifFalse: [super + aValue]]!

- aValue
	"Answer the sum of the receiver and the argument-handle adding Integers as a special case."

	^(aValue isMemberOf: self class)
		ifTrue: [self class new value: (value - aValue value)]
		ifFalse: [(aValue isMemberOf: SmallInteger)
			ifTrue: [SymbolicPitch fromMIDI: (self asMIDI value - aValue)]
			ifFalse: [super - aValue ]]!

transposeBy: aValue
	"transpose a SymbolicPitch by aValue in fractional halfsteps"

	^((self asFracMIDI transposeBy: aValue) asFracSymbol).! !

!Siren.SymbolicPitch methodsFor: 'converting'!

asFracMIDI
	"Assuming value is a symbolic note name, answer a key number."
	"MIDI is 0=c-based, NameMap is 0=a-based"
	"(Pitch value: #c3) asFracMIDI"
	"(Pitch value: 'c#3' asSymbol) asFracMIDI"
	"( 'c#3' pitch fracPitch: -0.4) asFracMIDI"

	(fracPitch isNil)
	ifTrue: [^self asMIDI]
	ifFalse: [^(self asMIDI value + fracPitch) key].!

asHertz
	"assuming value is a symbolic note name, return a frequency "
	"(Pitch value: #a6) asHertz"		"440.0 is Midi key 69"
	"(Pitch value: #c5) asHertz"		"261.0 is Midi key 60 or middle C"

	^(self asFracMIDI) asHertz!

asHz
	"assuming value is a symbolic note name, return a frequency "
	"(Pitch value: #a6) asHertz"		"440.0 is Midi key 69"
	"(Pitch value: #c5) asHertz"		"261.0 is Midi key 60 or middle C"

	^self asHertz!

asMIDI
	"Assuming value is a symbolic note name, answer a key number."
	"MIDI is 0=c-based, NameMap is 0=a-based"
	"(Pitch value: #c3) asMIDI"
	"(Pitch value: 'c#3' asSymbol) asMIDI"

	| noteName octave |
	octave := '3'.	"if no octave is given, assume c3 - b3."
	noteName := self value.
	noteName last isDigit
		ifTrue: [octave := noteName last.
			noteName := noteName copyFrom: 1 to: noteName size - 1].
	(noteName last == $-)	"if octave has minus sign...."
		ifTrue: [octave := octave digitValue negated.
			noteName := noteName copyFrom: 1 to: noteName size - 1].
	^MIDIPitch new value: (self class nameMap detect: [:item | item key == noteName asSymbol]) value - 3 + (octave asString asNumber + 2 * 12)!

asSymbol
	"Answer a SymbolicPitch."

	^self.!

mostGeneral
	"Answer the most numerically meaningful version of the receiver."

	^self asHertz! !

!Siren.SymbolicPitch methodsFor: 'printing'!

printOn0: aStream
	"Print the receiver as a symbolic pitch."

	value printOn: aStream!

units
	"Answer the units string of the receiver."

	^' pitch'! !

!Siren.SymbolicPitch methodsFor: 'private'!

species
	"Answer Pitch."

	^Pitch! !

!Siren.SymbolicPitch class methodsFor: 'class initialization'!

initialize
	"Set up the class name mapping array."
	"SymbolicPitch initialize"

	NameMap := OrderedCollection new: 32.
	NameMap add: ('a' asSymbol -> 0);
			add: ('a#' asSymbol -> 1);
			add: ('a-sharp' asSymbol -> 1);
			add: ('bb' asSymbol -> 1);
			add: ('b-flat' asSymbol -> 1);
			add: ('b' asSymbol -> 2);
			add: ('cb' asSymbol -> 2);
			add: ('c-flat' asSymbol -> 2);
			add: ('c' asSymbol -> 3);
			add: ('c#' asSymbol -> 4);
			add: ('c-sharp' asSymbol -> 4);
			add: ('d-flat' asSymbol -> 4);
			add: ('db' asSymbol -> 4);
			add: ('d' asSymbol -> 5);
			add: ('d#' asSymbol -> 6);
			add: ('d-sharp' asSymbol -> 6);
			add: ('e-flat' asSymbol -> 6);
			add: ('eb' asSymbol -> 6);
			add: ('e' asSymbol -> 7);
			add: ('f-flat' asSymbol -> 7);
			add: ('fb' asSymbol -> 7);
			add: ('f' asSymbol -> 8);
			add: ('f#' asSymbol -> 9);
			add: ('f-sharp' asSymbol -> 9);
			add: ('g-flat' asSymbol -> 9);
			add: ('gb' asSymbol -> 9);
			add: ('g' asSymbol -> 10);
			add: ('g#' asSymbol -> 11);
			add: ('g-sharp' asSymbol -> 11);
			add: ('a-flat' asSymbol -> 11);
			add: ('ab' asSymbol -> 11).! !

!Siren.SymbolicPitch class methodsFor: 'instance creation'!

fromFracMIDI: aValue
	"Assuming value is a key number, answer a symbolic pitch name"
	"SymbolicPitch fromMIDI: (Pitch value: 69)"
	"SymbolicPitch fromMIDI: (Pitch value: 68.9 key)"
	"(fromMIDI throws away microtonal offset, fromFracMIDI keeps it.)"	

	| tempSym tempKey |
	tempKey := aValue value.
	tempSym := self value: ((NameMap detect: 
					[ :ass | 
					ass value = (tempKey rounded + 3 \\ 12)]) key, 
		(aValue value // 12 - 2) printString).
	tempSym fracPitch: (tempKey - tempKey rounded).
	^tempSym.!

fromMIDI: aValue
	"Assuming value is a key number, answer a symbolic pitch name"
	"SymbolicPitch fromMIDI: (Pitch value: 69)"
	"SymbolicPitch fromMIDI: (Pitch value: 68.9 key)"
	"(fromMIDI throws away microtonal offset, fromFracMIDI keeps it.)"	

	^self value: ((NameMap detect: 
					[ :ass | 
					ass value = (aValue value rounded + 3 \\ 12)]) key, 
		(aValue value // 12 - 2) printString).! !

!Siren.SymbolicPitch class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asNote! !

!Siren.SymbolicLoudness methodsFor: 'converting'!

asDB
	"Answer a dB loudness; ratio 1 = 0dB, ratio 0.5 = -6dB, etc."
	"(#ff ampl) asDB"

	^self asRatio asDB!

asMIDI
	"Answer a MIDIVelocity."
	"(#pp ampl) asMIDI"		"pianissimo is MIDI velocity 24"

	^MIDIVelocity new value:
			((self class nameMap keyAtValue: value) last)!

asRatio
	"Answer a RatioLoudness."
	"(#pp ampl) asRatio"		"pianissimo is 0.2"

	^RatioLoudness new value:
			(((self class nameMap keyAtValue: value) last / 120) asFloat)!

asSymbol
	"Answer a SymbolicLoudness."

	^self!

mostGeneral
	"Answer the most numerically meaningful version of the receiver."

	^self asRatio! !

!Siren.SymbolicLoudness methodsFor: 'printing'!

printOn0: aStream
	"Print the receiver as a symbolic amplitude."

	aStream nextPutAll: value!

units
	"Answer the units string of the receiver."

	^' ampl'! !

!Siren.SymbolicLoudness methodsFor: 'private'!

species
	"Answer Amplitude."

	^Amplitude! !

!Siren.SymbolicLoudness class methodsFor: 'class initialization'!

initialize
	"Set up the class dynamic mapping dictionary"
	"SymbolicLoudness initialize"
	
	NameMap := Dictionary new.
	NameMap at: (1 to: 12) put: #ppp.
	NameMap at: (12 to: 24) put: #pp.
	NameMap at: (24 to: 38) put: #p.
	NameMap at: (38 to: 50) put: #mp.
	NameMap at: (50 to: 70) put: #mf.
	NameMap at: (70 to: 85) put: #f.
	NameMap at: (85 to: 100) put: #ff.
	NameMap at: (100 to: 127) put: #fff.! !

!Siren.SymbolicLoudness class methodsFor: 'instance creation'!

fromMIDI: aValue
	"Assuming value is a key velocity, answer a symbolic loudness name"
	"(Amplitude value: 39) asSymbol"

	| int |
	int := NameMap keys detect: [ :range | range includes: aValue]
				ifNone: [^self error: 'invalid Loudness range.'].
	^(self new) value: (NameMap at: int)! !

!Siren.SymbolicLoudness class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asDynamic! !

!Siren.HertzPitch methodsFor: 'converting'!

asFracMIDI
	"Assuming value is a frequency, Answer a fractional key number"
	"(Pitch value: 440.0) asFracMIDI"		"440.0 is MIDI key 69.0"
	"(Pitch value: 445.0) asFracMIDI" 	"445.0 is MIDI key 69.195..."

	"(8.17579891564371 Hz is the frequency of C0.)"

	^MIDIPitch new value: (((value / 8.17579891564371) log: 2) * 12)!

asFracSymbol
	"Assuming value is a frequency, answer a symbolic note name"
	"(Pitch value: 880.0) asSymbol"			"880.0 is a4"
	"(438.0 Hz) asFracSymbol inspect"			"262.0 is a3 - an offset"
	"(asSymbol throws away microtonal offset, asFracSymbol keeps it.)"	

	^SymbolicPitch fromFracMIDI: (self asFracMIDI)!

asHertz
	"Answer a HertzPitch."

	^self!

asHz
	"Answer a HertzPitch."

	^self!

asMIDI
	"Assuming value is a frequency, Answer a key number"
	"(Pitch value: 440.0) asMIDI"		"440.0 is MIDI key 69"
	"(Pitch value: 261.3) asMIDI"		"261.3 is MIDI key 60"
		
	^MIDIPitch new value: (self asFracMIDI value rounded).!

asSymbol
	"Assuming value is a frequency, answer a symbolic note name"
	"(Pitch value: 880.0) asSymbol"			"880.0 is a4"
	"(438.0 Hz) asSymbol"			"438.0 is a3 - an offset."
	"(asSymbol throws away microtonal offset, asFracSymbol keeps it.)"	

	^SymbolicPitch fromMIDI: (self asMIDI)! !

!Siren.HertzPitch methodsFor: 'printing'!

printOn0: aStream
	"Print the receiver as a pitch string in Hertz."

	aStream nextPutAll: value printString, ' Hz.'!

units
	"Answer the units string of the receiver."

	^' Hz'! !

!Siren.HertzPitch methodsFor: 'private'!

species
	"Answer Pitch."

	^Pitch! !

!Siren.HertzPitch class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asHz! !

!Siren.MIDIPitch methodsFor: 'converting'!

asFracMIDI
	"Answer a fractionalMIDIPitch (or integer if value is no fraction)."
	"60 key asFracMIDI "
	"60.4 key asFracMIDI "

	^self!

asFracSymbol
	"Assuming value is a key number, answer a symbolic pitch name"
	"(Pitch value: 37) asSymbol "
	"(Pitch value: 41.6 key) asSymbol "
	"(443 Hz) asFracSymbol inspect"
	"( asSymbol throws away microtonal offset, asFracSymbol keeps it.)"	

	^SymbolicPitch fromFracMIDI: value.!

asHertz
	"Assuming value is a key number, answer a frequency"
	"(Pitch value: 69) asHertz"			"440.0 is Midi key 69"
	"(Pitch value: 60) asHertz"			"261.62... is Midi key 60"
	"(Pitch value: 60.51 key) asHertz"	"269.44... is Midi key 60 + 51 cents."
			"NOTE: the key message coerces a float to become a MIDIPitch;
			by default a float becomes a PitchHertz. 
			(see class Pitch, instance creation)"
	"(8.17579891564371 = MIDInote 0 in Hz.)"

	^HertzPitch new value: (8.17579891564371 * (2.0 raisedTo:
									((value asFloat) / 12.0)))!

asHz
	"Assuming value is a key number, answer a frequency"
	"(Pitch value: 69) asHertz"		"440.0 is Midi key 69"
	"(Pitch value: 60) asHertz"		"261.0 is Midi key 60"
	"(Pitch value: 60.51 key) asHertz"	"269.44... is Midi key 60 + 51 cents."

	^self asHertz!

asMIDI
	"Answer a MIDIPitch."
	"60 key asMIDI "
	"60.6 key asMIDI "

	(value isMemberOf: SmallInteger)
			ifTrue: [^self].
	(value isMemberOf: Float)
			ifTrue: [^self value rounded key].!

asSymbol
	"Assuming value is a key number, answer a symbolic pitch name"
	"(Pitch value: 37) asSymbol"

	^SymbolicPitch fromMIDI: value!

mostGeneral
	"Answer the most numerically meaningful version of the receiver."

	^self asHertz! !

!Siren.MIDIPitch methodsFor: 'printing'!

units
	"Answer the units string of the receiver."

	^' key'! !

!Siren.MIDIPitch methodsFor: 'private'!

species
	"Answer Pitch."

	^Pitch! !

!Siren.MIDIPitch class methodsFor: 'class initialization'!

initialize
	"Initialize the class instance variable."
	"MIDIPitch initialize."

	range := 1 to: 127! !

!Siren.MIDIPitch class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asKey! !

!Siren.MeasureDuration methodsFor: 'printing'!

units
	"Answer the units string of the receiver."

	^' measures'! !

!Siren.MeasureDuration methodsFor: 'private'!

species
	"Answer Duration."

	^Duration! !

!Siren.MIDIDevice methodsFor: 'initialization'!

checkPort
	"Ensure that the receiver's output port is initialized."

	port == nil
		ifTrue: [port := MIDIPort default].!

close
	"Release the receiver's hold on the port."

"	Transcript show: 'MidiDevice reset: ', port printString; cr."
	port close.
	port := nil!

initialize
	"Set the receiver up on the default output port."
 
"	Transcript show: 'MidiDevice init...'; cr."
"	port == nil
		ifTrue: [port := MIDIPort default].
"	debug := nil.		"or true"
	stream := 1.!

initialize: thePort
	"Set the receiver up on the given output port."

"	Transcript show: 'MidiDevice init...'; cr."
	port == nil
		ifTrue: [port := thePort].!

play: anAss on: channelNumber
	"play the given event on my port as a default MIDI noteOn/noteOff pair"

	| s e |
	s := anAss key.
	e := anAss value.
	self checkPort.
	port ifNil:
		[^MIDIPort default play: (self mapPitch: e pitch) 
			at: s value 
			dur: (self mapDuration: e dur) 
			amp: (self mapAmplitude: e ampl) 
			voice: channelNumber].
	port play: (self mapPitch: e pitch) 
			at: s value 
			dur: (self mapDuration: e dur) 
			amp: (self mapAmplitude: e ampl) 
			voice: channelNumber! !

!Siren.MIDIDevice methodsFor: 'accessing'!

port
	"Answer the receiver's I/O port."

	^port!

port: aPort
	"Set the receiver's I/O port."

	port := aPort!

stream
	"Answer the receiver's MIDI stream ID."

	^stream!

stream: aNumber
	"Set the receiver's MIDI stream ID."

	stream := aNumber! !

!Siren.MIDIDevice methodsFor: 'note events'!

play: anEvent at: aTime
	"Play the given event on my port as a default MIDI noteOn/noteOff pair."

	| p d a v now |
	now := Time millisecondClockValue.
	(Delay forMilliseconds: aTime - now) wait.
	p := self mapPitch: anEvent pitch.
	d := self mapDuration: anEvent dur.
	a := self mapAmplitude: anEvent ampl.
	v := self mapVoice: anEvent voice.
	nil == debug
		ifFalse: [MStream nextPutAll: 
					aTime value printString, ' ', p printString, ' ', 
					d printString,' ', a printString,' ', v printString; cr].
	self checkPort.
	port play: stream pitch: p value dur: d amp: a voice: v! !

!Siren.MIDIDevice methodsFor: 'parameter mapping'!

mapAmplitude: aVal
	"Map a numerical or symbolic amplitude to a MIDI-compatible 
	 volume number (key velocity)"


	^(aVal species == Amplitude 
		ifTrue: [aVal asMIDI value]
		ifFalse: [aVal class == SmallInteger 
			ifTrue: [^aVal]		"assume a volume number"
									"assume a dynamic name"
			ifFalse: [(aVal isNil or: [aVal value isNil])
				ifTrue: [64]
				ifFalse: [Amplitude value: aVal asMIDI]]])!

mapDuration: aVal
	"Map a numerical or symbolic duration value to a 
	 MIDI-compatible duration in msec."

	^(aVal species == Duration
		ifTrue: [aVal asMS value]"if duration type, answer msec"
		ifFalse: [aVal class == SmallInteger
			ifTrue: [aVal]		"if Int, assume mec"
									"else assume a symbolic or float dur"
			ifFalse: [Duration value: aVal asMidi]])!

mapPitch: aVal
	"Map a numerical or symbolic pitch to a MIDI-compatible note number."

	^(aVal species == Pitch
		ifTrue: [aVal asMIDI value]
		ifFalse: [aVal class == SmallInteger
			ifTrue: [aVal]			"assume a note number"
										"assume a note name or freq"
			ifFalse: [Pitch value: aVal asMidi	]])!

mapVoice: aVal
	"Map a numerical or symbolic pitch to a MIDI-compatible note number."

	^(aVal isInteger
		ifTrue: [(aVal truncated min: 15) max: 0]
		ifFalse: [aVal species == IOVoice
			ifTrue: [aVal asNumber]
			ifFalse: [1]])! !

!Siren.MIDIDevice class methodsFor: 'class initialization'!

initialize
	"Set up the one class variable"
	"MIDIDevice initialize"

"	MStream := Transcript.
	MStream := WriteStream on: (String new: 4096).
"	MStream := nil! !

!Siren.MIDIDevice class methodsFor: 'instance creation'!

new
	"Open a generic MidiDevice on the default Port"
	"MIDIDevice new"

	| me |
	me := super new initialize.
"	me port: MIDIPort default open."
	^me!

on: port
	"Open a generic MidiDevice on the given Port"

	| me |
	me := super new initialize.
	me port: port.
	^me! !

!Siren.MIDIDevice class methodsFor: 'examples'!

playOnDevice
	"Play a note out MIDI from the device level."
	"self halt. MIDIDevice playOnDevice"
	"MIDIPort allNotesOff"
	"MIDIDevice initialize"
	"MStream inspect"

	| device port |
	port := MIDIPort default.
	device := MIDIDevice on: port.
	device play: (60 key, 500 msec, 96 velocity, (#voice -> 0)) at: 0.
	(Delay forSeconds: 1) wait.
	port allNotesOff.
	device close.
	Transcript show: ' done.'; cr.!

scheduleExample
	"Play MIDI using the event scheduler."
	"MIDIDevice scheduleExample"
	"EventScheduler interrupt; flush"

	 | device port voice scale start |
	port := MIDIPort default. 
	device := MIDIDevice on: port.
	voice := MIDIVoice on: device.
	scale := EventList scaleExampleFrom: 36 to: 60 in: 2000.
	scale scale: #duration by: 2.
	start := Time millisecondClockValue + 500.
	scale events do: [ :ass |
		ass event voice: voice.
		ass key: (ass key value + start) msec.
		EventScheduler instance addClient: ass].
	"self halt."
	EventScheduler instance run! !

!Siren.MIDIFB01 methodsFor: 'note events'!

play: anEvent on: aChannel
	"send FB01-specific code for note with fraction and duration
		2n k f v d1 d2 = cmd:channel, noteNum, frac, vel, durLow, durHigh"

	| bytes realDur |
	bytes := ByteArray new: 6.
	bytes at: 1 put: (16r20 + aChannel).
	bytes at: 2 put: (self mapPitch: anEvent pitch).
	bytes at: 3 put: 0.
	bytes at: 4 put: (self mapAmplitude: anEvent ampl).
	realDur := self mapDuration: anEvent duration.
	bytes at: 5 put: (realDur bitAnd: 127).
	bytes at: 6 put: (realDur bitShift: -7 ).
	port == nil
		ifTrue: [MStream nextPutAll: 'FB01: ', bytes printString; cr]
		ifFalse: [port put: bytes length: 6]! !

!Siren.PField methodsFor: 'accessing'!

field
	"Answer the instance variable 'field'."

	^field!

field: theNumber
	"Accept the argument, 'theNumber', as the new instance nariable 'field'."

	field := theNumber!

name
	"Answer the instance variable 'name'."

	^name!

name: theName
	"Accept the argument, 'theName', as the new instance nariable 'name'."

	name := theName! !

!Siren.PField class methodsFor: 'instance creation'!

field: aNumber value: aValue
	"Answer an initialized instance."

	^(self new) field: aNumber; value: aValue!

field: aNumber value: aValue name: aName
	"Answer an initialized instance."

	^(self new) field: aNumber; value: aValue; name: aName! !

!Siren.HierarchyLayoutManager methodsFor: 'accessing'!

accessor: aTA
	"Set the receiver's tree accessor to the argument."

	treeAccessor := aTA!

xStep: aNumber
	"Set the receiver's x step size to the argument."

	xStep := aNumber!

yStep: aNumber
	"Set the receiver's y step size to the argument."

	yStep := aNumber! !

!Siren.HierarchyLayoutManager methodsFor: 'generating'!

addItem1: aModelNode atNode: aPath in: aDisplayList
	"Add the given item to the display list."

	| wrapper displayPoint item path2 displayList2 |
	displayPoint := self displayPositionFor: aPath.
	length := length + 1.
	item := treeAccessor itemFor: aModelNode.
	wrapper := TranslatingWrapper on: item at: displayPoint.
	(treeAccessor isTerminal: aModelNode)
		ifFalse: [displayList2 := DisplayList new.
			displayList2 model: aModelNode.
			(treeAccessor childrenOf: aModelNode) do: [ :i | 
				path2 := aPath copy.
				path2 addLast: aModelNode.
				self addItem: i
					atNode: path2
					in: displayList2].
			displayList2 add: wrapper.
			aDisplayList add: displayList2 atPath: aPath]
		ifTrue: [aDisplayList add: item atPath: aPath]!

addItem: aModelNode atNode: aPath in: aDisplayList
	"Add the given item to the display list."

	| displayPoint item path2 displayList2 |
	displayPoint := self displayPositionFor: aPath.
	length := length + 1.
	item := treeAccessor itemFor: aModelNode.
	item offset: displayPoint.
	(treeAccessor isTerminal: aModelNode)
		ifFalse: [displayList2 := DisplayList new.
			displayList2 model: aModelNode.
			(treeAccessor childrenOf: aModelNode) do: 
				[ :i | 
				path2 := aPath copy.
				path2 addLast: aModelNode.
				self addItem: i
					atNode: path2
					in: displayList2].
			displayList2 add: item"; offset: displayPoint".
			aDisplayList add: displayList2 atPath: aPath]
		ifTrue: [aDisplayList add: item atPath: aPath]!

displayPositionFor: aPath
	"Answer the display position of the element at the given path."

	^self subclassResponsibility!

generateDisplayList
	"Generate and answer the receiver's display list."

	| list |
	list := DisplayList new.
	self addItem: treeAccessor subject
			atNode: (OrderedCollection new)
			in: list.
	^list! !

!Siren.IndentedListLayoutManager methodsFor: 'generating'!

displayPositionFor1: aPath
	"Answer the display position of the element at the given path."

	| pt |
	length == nil ifTrue: [length := 0].
	pt := (orientation == #left or: [orientation == nil])
		ifFalse: [(length * xStep) @ (aPath size * yStep)]
		ifTrue: [(aPath size * xStep) @ (length * yStep)].
	Transcript show: aPath printString, ' ', pt printString; cr.
	^pt!

displayPositionFor: aPath
	"Answer the display position of the element at the given path."

	length == nil ifTrue: [length := 0].
	^(orientation == #left or: [orientation == nil])
		ifFalse: [(length * xStep) @ (aPath size * yStep)]
		ifTrue: [(aPath size * xStep) @ (length * yStep)]! !

!Siren.IndentedListLayoutManager class methodsFor: 'examples'!

colorListExample
	"IndentedListLayoutManager colorListExample display"

	| acc me |
	acc := STreeAccessor subject: MusicMagnitude
			terminalQuery: [ :c | c subclasses size = 0]
			childAccessor: #sortedSubclasses
			itemGenerator: [ :c | (DisplayString new)
									model: c;
									string: c name;
									color: [c color]].
	me := (self new) accessor: acc; xStep: 16; yStep: 16.
	^me displayList display!

listExample
	"IndentedListLayoutManager listExample display"
	"DisplayListView classListExample"

	| acc me |
	acc := STreeAccessor subject: Magnitude
			terminalQuery: [ :c | c subclasses size = 0]
			childAccessor: #sortedSubclasses
			itemGenerator: [ :c | (DisplayString new)
									model: c;
									string: c name;
									color: ColorValue darkGray].
	me := (self new) accessor: acc; xStep: 16; yStep: 16.
	^me displayList! !

!Siren.TypedOSCMessage methodsFor: 'osc'!

argumentsToOSCBytes: t1 
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	t1 nextPut: $, asInteger.
	self typesToOSC: t1.
	super argumentsToOSCBytes: t1.
	^self!

typesToOSC: t1 
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	| t2 t3 |
	arguments isEmpty ifTrue: [^self].
	arguments do: [:t4 | t4 toOSCTypeOn: t1].
	t2 := 1.
	t3 := 4 - (t1 position \\ 4).
	[t2 <= t3]
		whileTrue: 
			[t1 nextPut: 0.
			t2 := t2 + 1].
	^self! !

!Siren.TypedOSCMessage class methodsFor: 'examples'!

changeFreq
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/freq' with: #(440)!

scDecreaseVolume
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/sc/mixer/volume' with: #(1 0.2)!

scIncreaseVolume
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/sc/mixer/volume' with: #(1 1.0)!

scRun
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/sc/run'!

scStop
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/sc/stop'!

start
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self for: '/start'! !

!Siren.MIDIPort methodsFor: 'initialize release'!

initialize
	"Setup the receiver's instance variables."

	Debug := false.
!

release
	"Release--break and dependencies on the error value."

	super release.
! !

!Siren.MIDIPort methodsFor: 'accessing'!

debug
	"Answer the receiver's debug value (answer false if it's nil)."

	^Debug == true!

debug: aBoolean
	"Set the receiver's debug value"

	Debug := aBoolean!

input
	"Answer the receiver's Q of input data."
	"MIDIPort default input"

	^inputData!

isActive
	"Answer whether the receiver is active."

	self subclassResponsibility!

resetInput
	"Reset the receiver's Q of input data."
	"MIDIPort instance resetInput"

	inputData := OrderedCollection new: 16! !

!Siren.MIDIPort methodsFor: 'open/close'!

close
	"Close MIDI."
	"MIDIPort instance close"

	self subclassResponsibility!

open
	"Open the MIDI driver -- start the lower-level driver up."
	"MIDIPort default"

	self subclassResponsibility!

reset
	"Reset the port."

	self allNotesOff! !

!Siren.MIDIPort methodsFor: 'ioctl'!

eventsAvailable
	"Answer the number of events in the input Q."
	"MIDIPort instance eventsAvailable"

	^inputData size!

hasBuffer
	"Answer whether the MIDI driver has a time-stamped output buffer."

	^false!

hasClock
	"Answer whether the MIDI driver has its own clock."

	^false!

hasControllerCache
	"Answer whether the MIDI driver supports a controller data buffer."

	^false!

hasDurs
	"Answer whether the MIDI driver supports a 1-call note-on/off command."

	^false!

startControllerCaching
	"Start caching MIDI controller in the driver."

	self subclassResponsibility!

startMIDIEcho
	"Start echoing MIDI input from the driver."

	self subclassResponsibility!

startMIDIInput
	"Start signalling the read semaphore on MIDI input."

	self subclassResponsibility!

stopControllerCaching
	"Start caching MIDI controller in the driver."

	self subclassResponsibility!

stopMIDIEcho
	"Stop echoing MIDI input from the driver."

	self subclassResponsibility!

stopMIDIInput
	"Stop signalling the read semaphore on MIDI input."

	self subclassResponsibility! !

!Siren.MIDIPort methodsFor: 'control commands'!

allNotesOff
	"Turn all MIDI notes off using the channel message 123."
	"MIDIPort default allNotesOff"

	| answer |
	0 to: 15 do:
		[ :chan |
		answer := self controlChange: chan controller: 123 to: 0.
		answer = 3
			ifFalse: [self error: 'Error sending note-off.']]!

allNotesOffVerbose
	"Turn all MIDI notes off (the verbose way)."
	"MIDIPort instance allNotesOff"

	| bytes answer |
	bytes := ByteArray new: 3.
	bytes at: 3 put: 0.
	0 to: 15 do:
		[ :chan |
		0 to: 127 do:
			[ :key |
			bytes at: 1 put: (16r80 bitOr: chan).
			bytes at: 2 put: key.
			answer := self put: bytes length: 3.
			answer = 3
				ifFalse: [self error: 'Error sending note-off.']]]!

controlChange: chan controller: controller to: value
	"Send out a control-change command now."
	"No range-checking on arguments."

	| data |
	data := ByteArray new: 3.
	data at: 1 put: ((MIDICommands at: #ctrlChange) bitOr: chan);
		at: 2 put: controller;
		at: 3 put: value.
	^self put: data length: 3!

pitchBend: chan to: value
	"Send out a pitch-bend command at the given time."

	| data pitchBytes |
	data := ByteArray new: 3.
"Get the two-byte 14-bit value for the argument."
	pitchBytes := MIDIPacket bytesFor14BitValue: value.
	data at: 1 put: ((MIDICommands at: #pitchWheel) bitOr: chan);
		at: 2 put: pitchBytes first;
		at: 3 put: (pitchBytes at: 2).
	self put: data length: 3!

programChange: chan to: value
	"Send out a program-change command at the given time."
	"No range-checking on arguments."

	| data prog answer |
	data := ByteArray new: 2.
	value isInteger
		ifTrue: [prog := value]
		ifFalse: [prog := GeneralMIDIMap at: value asSymbol
						ifAbsent: [self error: 'Instrument not found']].
	data at: 1 put: ((MIDICommands at: #progChange) bitOr: chan);
		at: 2 put: prog - 1.
	answer := self put: data length: 2.
	(Delay forMilliseconds: 50) wait.		"Give the synth some time..."
	^answer!

sysex: command
	"Send out a MIDI system exclusive data packet at the given time."

	| data size |
	size := command size + 2.
	data := ByteArray new: size.
	data at: 1 put: (MIDICommands at: #sysEx).
	1 to: command size do:
		[ :i | data at: (i + 1) put: (command at: i)].
	data at: size put: 16rF7.
	self put: data length: size! !

!Siren.MIDIPort methodsFor: 'read/write'!

get: packet
	"Read the data from the receiver into the argument (a MIDIPacket). Answer the number of data bytes read."

	| len data |
	data := ByteArray new: packet data size + 4.
	len := self primReadPacket: packet size: data size data: data.
	len = 0
		ifTrue: [^0].
	packet data
		replaceFrom: 1
		to: packet data size
		with: data
		startingAt: 5.
	packet time: (data longAt: 1 bigEndian: false).
	^len-4!

nextEventInto: anEventAssociation
	"Record via the receiver into the argument."
	"Answer a MusicEvent; if there's no note-off yet, put nil in the duration."

	| pa1 |
	pa1 := inputData first.
	pa1 length = 3
		ifFalse: [self error: ('Unknown MIDI packet (len = ', pa1 length printString, ')')].
	pa1 command = 16r90		"if note-on command..."
		ifTrue: [inputData do:	"look for note off."
			[ :pa2 | 
			pa2 pitch = pa1 pitch
				ifTrue: [(pa2 command = 16r80 or: [pa2 ampl = 0])
					ifTrue: [inputData removeFirst.
						inputData remove: pa2.
						^MusicEvent dur: (pa2 time - pa1 time) 
							pitch: pa1 pitch ampl: pa1 ampl voice: pa1 voice]]]]
		ifFalse: [self error: ('Unknown MIDI packet (cmd = ', 
						pa1 command printString, ')')].
	^MusicEvent dur: nil pitch: pa1 pitch ampl: pa1 ampl voice: pa1 voice!

nextMessage
	"Answer the first (length -> bytes) association from the input data collection."
	"MIDIPort instance nextMessage"

	^inputData isEmpty
		ifTrue: [nil]
		ifFalse: [inputData removeFirst]!

play: aPitch dur: aDur amp: anAmp voice: aVoice
	"Play a note (on/off message pair) with the given parameters on the receiver."
	"This is the default method for drivers with no 1-call noteOn/Off command."
	"Dur is assumed to be in msec."

	self playOn: aPitch amp: anAmp voice: aVoice.
"Schedule note-off in a forked wait block."
	aDur isNil
		ifFalse: [[(Delay forMilliseconds: aDur) wait.
				self playOff: aPitch amp: anAmp voice: aVoice] fork]!

play: streamID pitch: aPitch dur: aDur amp: anAmp voice: aVoice
	"Play a note (on/off message pair) with the given parameters on the receiver."
	"This is the default method for drivers with no 1-call noteOn/Off command."

	 | s v |
	v := (aVoice - 1) max: 0.		"zero-based stream and voice index"
	s := (streamID - 1) max: 0.
	self playOn: s pitch: aPitch amp: anAmp voice: v.
"Schedule note-off in a forked wait block."
	aDur isNil
		ifFalse: [[(Delay forMilliseconds: aDur) wait.
				self playOff: s pitch: aPitch amp: anAmp voice: v] fork]!

playOff: streamID pitch: aPitch amp: anAmp voice: aVoice
	"Play a note-on command with the given parameters on the receiver."

	self subclassResponsibility!

playOn: streamID pitch: aPitch amp: anAmp voice: aVoice
	"Play a note-on command with the given parameters on the receiver."

	self subclassResponsibility!

put: data length: size
	"Send the argument data to the receiver now."

	self subclassResponsibility!

readAll
	"Read all pending input."
	"MIDIPort instance readAll"
	"MIDIPort instance input"

	| evts packet len |
	evts := self eventsAvailable.
	evts timesRepeat:
		[packet := MIDIPacket new.
		len := self get: packet.
		len > 3
			ifTrue: [packet := MIDIPacket new: len.
				len := self get: packet].
		inputData addLast: packet.
		Debug ifTrue: [Transcript show: packet printString; cr;
			tab; show: self eventsAvailable printString, ' events left in Q'; cr]].
	self changed: #read with: evts!

readController: index
	"Read the given controller value."

	| array |
	array := ByteArray new: 1.
	self readControllersFrom: index to: index into: array.
	^array first!

readControllersFrom: lo to: hi into: array
	"Read a range of controllers"

	self subclassResponsibility! !

!Siren.MIDIPort class methodsFor: 'instance creation'!

concreteClass
	"Answer the class to use for MIDI"

	^PortMIDIPort!

default
	"Answer the unique instance (create it lazily, if necessary)."
	"MIDIPort default"
	"MIDIPort instanceCount"

	UseSingleton
		ifTrue: [Instance ifNil: [Instance := self concreteClass basicNew initialize].
			Instance isActive ifFalse: [Instance open].
			^Instance].
	^self concreteClass basicNew initialize!

instance
	"Answer the unique instance (create it lazily, if necessary)."

	^self default!

new
	"Make sure there's only ever one of me."
	"MIDIPort new"
	"MIDIPort instanceCount"

	^UseSingleton
		ifTrue:  [self default]
		ifFalse: [self concreteClass basicNew initialize]!

release
	"Release the unique instance"
	"MIDIPort release"

	Instance := nil! !

!Siren.MIDIPort class methodsFor: 'class initialization'!

debug
	"Answer the class' debugging flag."

	^Debug!

debug: aBool
	"Answer the class' debugging flag."

	Debug := aBool!

initialize
	"Set up the dictionaries of commands, ioctl primitive selectors and arguments, and general MIDI maps."
	"MIDIPort initialize"
	
	Debug := false.
	UseSingleton := true.
	Instance := nil.
	MIDIPort initializeMIDITables!

initializeMIDITables
	"MIDIPort initializeMIDITables"

	Siren at: #MIDICommands put: Dictionary new.
	(Siren at: #MIDICommands) at: #noteOn put: 16r90.
	(Siren at: #MIDICommands) at: #noteOff put: 16r80.
	(Siren at: #MIDICommands) at: #polyTouch put: 16rA0.
	(Siren at: #MIDICommands) at: #chanTouch put: 16rD0.
	(Siren at: #MIDICommands) at: #ctrlChange put: 16rB0.
	(Siren at: #MIDICommands) at: #progChange put: 16rC0.
	(Siren at: #MIDICommands) at: #pitchWheel put: 16rE0.
	(Siren at: #MIDICommands) at: #sysEx put: 16rF0.

	Siren at: #GeneralMIDIMap put: Dictionary new.
	(Siren at: #GeneralMIDIMap) add: ('Acoustic Grand Piano' asSymbol -> 1).
	(Siren at: #GeneralMIDIMap) add: ('Bright Acoustic Piano' asSymbol -> 2).
	(Siren at: #GeneralMIDIMap) add: ('Electric Grand Piano' asSymbol -> 3).
	(Siren at: #GeneralMIDIMap) add: ('Honky-tonk Piano' asSymbol -> 4).
	(Siren at: #GeneralMIDIMap) add: ('Electric Piano 1' asSymbol -> 5).
	(Siren at: #GeneralMIDIMap) add: ('Electric Piano 2' asSymbol -> 6).
	(Siren at: #GeneralMIDIMap) add: ('Harpsichord' asSymbol -> 7).
	(Siren at: #GeneralMIDIMap) add: ('Clavinet' asSymbol -> 8).
	(Siren at: #GeneralMIDIMap) add: ('Celesta' asSymbol -> 9).
	(Siren at: #GeneralMIDIMap) add: ('Glockenspiel' asSymbol -> 10).
	(Siren at: #GeneralMIDIMap) add: ('Music Box' asSymbol -> 11).
	(Siren at: #GeneralMIDIMap) add: ('Vibraphone' asSymbol -> 12).
	(Siren at: #GeneralMIDIMap) add: ('Marimba' asSymbol -> 13).
	(Siren at: #GeneralMIDIMap) add: ('Xylophone' asSymbol -> 14).
	(Siren at: #GeneralMIDIMap) add: ('Tubular Bells' asSymbol -> 15).
	(Siren at: #GeneralMIDIMap) add: ('Dulcimer' asSymbol -> 16).
	(Siren at: #GeneralMIDIMap) add: ('Drawbar Organ' asSymbol -> 17).
	(Siren at: #GeneralMIDIMap) add: ('Percussive Organ' asSymbol -> 18).
	(Siren at: #GeneralMIDIMap) add: ('Rock Organ' asSymbol -> 19).
	(Siren at: #GeneralMIDIMap) add: ('Church Organ' asSymbol -> 20).
	(Siren at: #GeneralMIDIMap) add: ('Reed Organ' asSymbol -> 21).
	(Siren at: #GeneralMIDIMap) add: ('Accordion' asSymbol -> 22).
	(Siren at: #GeneralMIDIMap) add: ('Harmonica' asSymbol -> 23).
	(Siren at: #GeneralMIDIMap) add: ('Tango Accordion' asSymbol -> 24).
	(Siren at: #GeneralMIDIMap) add: ('Acoustic Guitar (nylon)' asSymbol -> 25).
	(Siren at: #GeneralMIDIMap) add: ('Acoustic Guitar (steel)' asSymbol -> 26).
	(Siren at: #GeneralMIDIMap) add: ('Electric Guitar (jazz)' asSymbol -> 27).
	(Siren at: #GeneralMIDIMap) add: ('Electric Guitar (clean)' asSymbol -> 28).
	(Siren at: #GeneralMIDIMap) add: ('Electric Guitar (muted)' asSymbol -> 29).
	(Siren at: #GeneralMIDIMap) add: ('Overdriven Guitar' asSymbol -> 30).
	(Siren at: #GeneralMIDIMap) add: ('Distortion Guitar' asSymbol -> 31).
	(Siren at: #GeneralMIDIMap) add: ('Guitar harmonics' asSymbol -> 32).
	(Siren at: #GeneralMIDIMap) add: ('Acoustic Bass' asSymbol -> 33).
	(Siren at: #GeneralMIDIMap) add: ('Electric Bass (finger)' asSymbol -> 34).
	(Siren at: #GeneralMIDIMap) add: ('Electric Bass (pick)' asSymbol -> 35).
	(Siren at: #GeneralMIDIMap) add: ('Fretless Bass' asSymbol -> 36).
	(Siren at: #GeneralMIDIMap) add: ('Slap Bass 1' asSymbol -> 37).
	(Siren at: #GeneralMIDIMap) add: ('Slap Bass 2' asSymbol -> 38).
	(Siren at: #GeneralMIDIMap) add: ('Synth Bass 1' asSymbol -> 39).
	(Siren at: #GeneralMIDIMap) add: ('Synth Bass 2' asSymbol -> 40).
	(Siren at: #GeneralMIDIMap) add: ('Violin' asSymbol -> 41).
	(Siren at: #GeneralMIDIMap) add: ('Viola' asSymbol -> 42).
	(Siren at: #GeneralMIDIMap) add: ('Cello' asSymbol -> 43).
	(Siren at: #GeneralMIDIMap) add: ('Contrabass' asSymbol -> 44).
	(Siren at: #GeneralMIDIMap) add: ('Tremolo Strings' asSymbol -> 45).
	(Siren at: #GeneralMIDIMap) add: ('Pizzicato Strings' asSymbol -> 46).
	(Siren at: #GeneralMIDIMap) add: ('Orchestral Harp' asSymbol -> 47).
	(Siren at: #GeneralMIDIMap) add: ('Timpani' asSymbol -> 48).
	(Siren at: #GeneralMIDIMap) add: ('String Ensemble 1' asSymbol -> 49).
	(Siren at: #GeneralMIDIMap) add: ('String Ensemble 2' asSymbol -> 50).
	(Siren at: #GeneralMIDIMap) add: ('SynthStrings 1' asSymbol -> 51).
	(Siren at: #GeneralMIDIMap) add: ('SynthStrings 2' asSymbol -> 52).
	(Siren at: #GeneralMIDIMap) add: ('Choir Aahs' asSymbol -> 53).
	(Siren at: #GeneralMIDIMap) add: ('Voice Oohs' asSymbol -> 54).
	(Siren at: #GeneralMIDIMap) add: ('Synth Voice' asSymbol -> 55).
	(Siren at: #GeneralMIDIMap) add: ('Orchestra Hit' asSymbol -> 56).
	(Siren at: #GeneralMIDIMap) add: ('Trumpet' asSymbol -> 57).
	(Siren at: #GeneralMIDIMap) add: ('Trombone' asSymbol -> 58).
	(Siren at: #GeneralMIDIMap) add: ('Tuba' asSymbol -> 59).
	(Siren at: #GeneralMIDIMap) add: ('Muted Trumpet' asSymbol -> 60).
	(Siren at: #GeneralMIDIMap) add: ('French Horn' asSymbol -> 61).
	(Siren at: #GeneralMIDIMap) add: ('Brass Section' asSymbol -> 62).
	(Siren at: #GeneralMIDIMap) add: ('SynthBrass 1' asSymbol -> 63).
	(Siren at: #GeneralMIDIMap) add: ('SynthBrass 2' asSymbol -> 64).
	(Siren at: #GeneralMIDIMap) add: ('Soprano Sax' asSymbol -> 65).
	(Siren at: #GeneralMIDIMap) add: ('Alto Sax' asSymbol -> 66).
	(Siren at: #GeneralMIDIMap) add: ('Tenor Sax' asSymbol -> 67).
	(Siren at: #GeneralMIDIMap) add: ('Baritone Sax' asSymbol -> 68).
	(Siren at: #GeneralMIDIMap) add: ('Oboe' asSymbol -> 69).
	(Siren at: #GeneralMIDIMap) add: ('English Horn' asSymbol -> 70).
	(Siren at: #GeneralMIDIMap) add: ('Bassoon' asSymbol -> 71).
	(Siren at: #GeneralMIDIMap) add: ('Clarinet' asSymbol -> 72).
	(Siren at: #GeneralMIDIMap) add: ('Piccolo' asSymbol -> 73).
	(Siren at: #GeneralMIDIMap) add: ('Flute' asSymbol -> 74).
	(Siren at: #GeneralMIDIMap) add: ('Recorder' asSymbol -> 75).
	(Siren at: #GeneralMIDIMap) add: ('Pan Flute' asSymbol -> 76).
	(Siren at: #GeneralMIDIMap) add: ('Blown Bottle' asSymbol -> 77).
	(Siren at: #GeneralMIDIMap) add: ('Shakuhachi' asSymbol -> 78).
	(Siren at: #GeneralMIDIMap) add: ('Whistle' asSymbol -> 79).
	(Siren at: #GeneralMIDIMap) add: ('Ocarina' asSymbol -> 80).
	(Siren at: #GeneralMIDIMap) add: ('Lead 1 (square)' asSymbol -> 81).
	(Siren at: #GeneralMIDIMap) add: ('Lead 2 (sawtooth)' asSymbol -> 82).
	(Siren at: #GeneralMIDIMap) add: ('Lead 3 (calliope)' asSymbol -> 83).
	(Siren at: #GeneralMIDIMap) add: ('Lead 4 (chiff)' asSymbol -> 84).
	(Siren at: #GeneralMIDIMap) add: ('Lead 5 (charang)' asSymbol -> 85).
	(Siren at: #GeneralMIDIMap) add: ('Lead 6 (voice)' asSymbol -> 86).
	(Siren at: #GeneralMIDIMap) add: ('Lead 7 (fifths)' asSymbol -> 87).
	(Siren at: #GeneralMIDIMap) add: ('Lead 8 (bass + lead)' asSymbol -> 88).
	(Siren at: #GeneralMIDIMap) add: ('Pad 1 (new age)' asSymbol -> 89).
	(Siren at: #GeneralMIDIMap) add: ('Pad 2 (warm)' asSymbol -> 90).
	(Siren at: #GeneralMIDIMap) add: ('Pad 3 (polysynth)' asSymbol -> 91).
	(Siren at: #GeneralMIDIMap) add: ('Pad 4 (choir)' asSymbol -> 92).
	(Siren at: #GeneralMIDIMap) add: ('Pad 5 (bowed)' asSymbol -> 93).
	(Siren at: #GeneralMIDIMap) add: ('Pad 6 (metallic)' asSymbol -> 94).
	(Siren at: #GeneralMIDIMap) add: ('Pad 7 (halo)' asSymbol -> 95).
	(Siren at: #GeneralMIDIMap) add: ('Pad 8 (sweep)' asSymbol -> 96).
	(Siren at: #GeneralMIDIMap) add: ('FX 1 (rain)' asSymbol -> 97).
	(Siren at: #GeneralMIDIMap) add: ('FX 2 (soundtrack)' asSymbol -> 98).
	(Siren at: #GeneralMIDIMap) add: ('FX 3 (crystal)' asSymbol -> 99).
	(Siren at: #GeneralMIDIMap) add: ('FX 4 (atmosphere)' asSymbol -> 100).
	(Siren at: #GeneralMIDIMap) add: ('FX 5 (brightness)' asSymbol -> 101).
	(Siren at: #GeneralMIDIMap) add: ('FX 6 (goblins)' asSymbol -> 102).
	(Siren at: #GeneralMIDIMap) add: ('FX 7 (echoes)' asSymbol -> 103).
	(Siren at: #GeneralMIDIMap) add: ('FX 8 (sci-fi)' asSymbol -> 104).
	(Siren at: #GeneralMIDIMap) add: ('Sitar' asSymbol -> 105).
	(Siren at: #GeneralMIDIMap) add: ('Banjo' asSymbol -> 106).
	(Siren at: #GeneralMIDIMap) add: ('Shamisen' asSymbol -> 107).
	(Siren at: #GeneralMIDIMap) add: ('Koto' asSymbol -> 108).
	(Siren at: #GeneralMIDIMap) add: ('Kalimba' asSymbol -> 109).
	(Siren at: #GeneralMIDIMap) add: ('Bag pipe' asSymbol -> 110).
	(Siren at: #GeneralMIDIMap) add: ('Fiddle' asSymbol -> 111).
	(Siren at: #GeneralMIDIMap) add: ('Shanai' asSymbol -> 112).
	(Siren at: #GeneralMIDIMap) add: ('Tinkle Bell' asSymbol -> 113).
	(Siren at: #GeneralMIDIMap) add: ('Agogo' asSymbol -> 114).
	(Siren at: #GeneralMIDIMap) add: ('Steel Drums' asSymbol -> 115).
	(Siren at: #GeneralMIDIMap) add: ('Woodblock' asSymbol -> 116).
	(Siren at: #GeneralMIDIMap) add: ('Taiko Drum' asSymbol -> 117).
	(Siren at: #GeneralMIDIMap) add: ('Melodic Tom' asSymbol -> 118).
	(Siren at: #GeneralMIDIMap) add: ('Synth Drum' asSymbol -> 119).
	(Siren at: #GeneralMIDIMap) add: ('Reverse Cymbal' asSymbol -> 120).
	(Siren at: #GeneralMIDIMap) add: ('Guitar Fret Noise' asSymbol -> 121).
	(Siren at: #GeneralMIDIMap) add: ('Breath Noise' asSymbol -> 122).
	(Siren at: #GeneralMIDIMap) add: ('Seashore' asSymbol -> 123).
	(Siren at: #GeneralMIDIMap) add: ('Bird Tweet' asSymbol -> 124).
	(Siren at: #GeneralMIDIMap) add: ('Telephone Ring' asSymbol -> 125).
	(Siren at: #GeneralMIDIMap) add: ('Helicopter' asSymbol -> 126).
	(Siren at: #GeneralMIDIMap) add: ('Applause' asSymbol -> 127).
	(Siren at: #GeneralMIDIMap) add: ('Gunshot' asSymbol -> 128).

	Siren at: #GeneralMIDIDrums put: Dictionary new.
	(Siren at: #GeneralMIDIDrums) add: ('Open Cuica' asSymbol -> 79).
	(Siren at: #GeneralMIDIDrums) add: ('Mute Triangle' asSymbol -> 80).
	(Siren at: #GeneralMIDIDrums) add: ('Open Triangle' asSymbol -> 81).
	(Siren at: #GeneralMIDIDrums) add: ('Acoustic Bass Drum' asSymbol -> 35).
	(Siren at: #GeneralMIDIDrums) add: ('Bass Drum 1' asSymbol -> 36).
	(Siren at: #GeneralMIDIDrums) add: ('Side Stick' asSymbol -> 37).
	(Siren at: #GeneralMIDIDrums) add: ('Acoustic Snare' asSymbol -> 38).
	(Siren at: #GeneralMIDIDrums) add: ('Hand Clap' asSymbol -> 39).
	(Siren at: #GeneralMIDIDrums) add: ('Electric Snare' asSymbol -> 40).
	(Siren at: #GeneralMIDIDrums) add: ('Low Floor Tom' asSymbol -> 41).
	(Siren at: #GeneralMIDIDrums) add: ('Closed Hi Hat' asSymbol -> 42).
	(Siren at: #GeneralMIDIDrums) add: ('High Floor Tom' asSymbol -> 43).
	(Siren at: #GeneralMIDIDrums) add: ('Pedal Hi-Hat' asSymbol -> 44).
	(Siren at: #GeneralMIDIDrums) add: ('Low Tom' asSymbol -> 45).
	(Siren at: #GeneralMIDIDrums) add: ('Open Hi-Hat' asSymbol -> 46).
	(Siren at: #GeneralMIDIDrums) add: ('Low-Mid Tom' asSymbol -> 47).
	(Siren at: #GeneralMIDIDrums) add: ('Hi Mid Tom' asSymbol -> 48).
	(Siren at: #GeneralMIDIDrums) add: ('Crash Cymbal 1' asSymbol -> 49).
	(Siren at: #GeneralMIDIDrums) add: ('High Tom' asSymbol -> 50).
	(Siren at: #GeneralMIDIDrums) add: ('Ride Cymbal 1' asSymbol -> 51).
	(Siren at: #GeneralMIDIDrums) add: ('Chinese Cymbal' asSymbol -> 52).
	(Siren at: #GeneralMIDIDrums) add: ('Ride Bell' asSymbol -> 53).
	(Siren at: #GeneralMIDIDrums) add: ('Tambourine' asSymbol -> 54).
	(Siren at: #GeneralMIDIDrums) add: ('Splash Cymbal' asSymbol -> 55).
	(Siren at: #GeneralMIDIDrums) add: ('Cowbell' asSymbol -> 56).
	(Siren at: #GeneralMIDIDrums) add: ('Crash Cymbal 2' asSymbol -> 57).
	(Siren at: #GeneralMIDIDrums) add: ('Vibraslap' asSymbol -> 58).
	(Siren at: #GeneralMIDIDrums) add: ('Ride Cymbal 2' asSymbol -> 59).
	(Siren at: #GeneralMIDIDrums) add: ('Hi Bongo' asSymbol -> 60).
	(Siren at: #GeneralMIDIDrums) add: ('Low Bongo' asSymbol -> 61).
	(Siren at: #GeneralMIDIDrums) add: ('Mute Hi Conga' asSymbol -> 62).
	(Siren at: #GeneralMIDIDrums) add: ('Open Hi Conga' asSymbol -> 63).
	(Siren at: #GeneralMIDIDrums) add: ('Low Conga' asSymbol -> 64).
	(Siren at: #GeneralMIDIDrums) add: ('High Timbale' asSymbol -> 65).
	(Siren at: #GeneralMIDIDrums) add: ('Low Timbale' asSymbol -> 66).
	(Siren at: #GeneralMIDIDrums) add: ('High Agogo' asSymbol -> 67).
	(Siren at: #GeneralMIDIDrums) add: ('Low Agogo' asSymbol -> 68).
	(Siren at: #GeneralMIDIDrums) add: ('Cabasa' asSymbol -> 69).
	(Siren at: #GeneralMIDIDrums) add: ('Maracas' asSymbol -> 70).
	(Siren at: #GeneralMIDIDrums) add: ('Short Whistle' asSymbol -> 71).
	(Siren at: #GeneralMIDIDrums) add: ('Long Whistle' asSymbol -> 72).
	(Siren at: #GeneralMIDIDrums) add: ('Short Guiro' asSymbol -> 73).
	(Siren at: #GeneralMIDIDrums) add: ('Long Guiro' asSymbol -> 74).
	(Siren at: #GeneralMIDIDrums) add: ('Claves' asSymbol -> 75).
	(Siren at: #GeneralMIDIDrums) add: ('Hi Wood Block' asSymbol -> 76).
	(Siren at: #GeneralMIDIDrums) add: ('Low Wood Block' asSymbol -> 77).
	(Siren at: #GeneralMIDIDrums) add: ('Mute Cuica' asSymbol -> 78).

	Siren at: #GeneralMIDIFamilies put: Dictionary new.
	(Siren at: #GeneralMIDIFamilies) add: ('Pianos' asSymbol -> (1 to: 8)).
	(Siren at: #GeneralMIDIFamilies) add: ('Tuned Pecussion' asSymbol -> (9 to: 16)).
	(Siren at: #GeneralMIDIFamilies) add: ('Organs' asSymbol -> (17 to: 24)).
	(Siren at: #GeneralMIDIFamilies) add: ('Guitars' asSymbol -> (25 to: 32)).
	(Siren at: #GeneralMIDIFamilies) add: ('Bass' asSymbol -> (33 to: 40)).
	(Siren at: #GeneralMIDIFamilies) add: ('Solo Strings' asSymbol -> (41 to: 48)).
	(Siren at: #GeneralMIDIFamilies) add: ('Ensembles' asSymbol -> (49 to: 56)).
	(Siren at: #GeneralMIDIFamilies) add: ('Brass' asSymbol -> (57 to: 64)).
	(Siren at: #GeneralMIDIFamilies) add: ('Reeds' asSymbol -> (65 to: 72)).
	(Siren at: #GeneralMIDIFamilies) add: ('Pipes' asSymbol -> (73 to: 80)).
	(Siren at: #GeneralMIDIFamilies) add: ('Synth Lead' asSymbol -> (81 to: 88)).
	(Siren at: #GeneralMIDIFamilies) add: ('Synth Pad' asSymbol -> (89 to: 96)).
	(Siren at: #GeneralMIDIFamilies) add: ('Synth Effects' asSymbol -> (97 to: 104)).
	(Siren at: #GeneralMIDIFamilies) add: ('Ethnic' asSymbol -> (105 to: 112)).
	(Siren at: #GeneralMIDIFamilies) add: ('Percussion' asSymbol -> (113 to: 120)).
	(Siren at: #GeneralMIDIFamilies) add: ('Sound Effects' asSymbol -> (121 to: 128)).! !

!Siren.MIDIPort class methodsFor: 'utilities'!

allNotesOff
	"MIDIPort allNotesOff"

	Instance ifNotNil:
		[Instance allNotesOff.
		Instance flush]!

cleanUp
	"Close down and clean up all MIDI, sound IO, event lists, etc.; then garbage collect."
	"MIDIPort cleanUp"

	Instance := nil.
	PortMIDIPort instanceCount = 0
		ifFalse: [PortMIDIPort allInstances do: 
			[ :p | 
			p isActive ifTrue: [p close]. 
			p release]]. 
	EventScheduler instance interrupt; flush.
	MIDIVoice flushDefault.
	SirenUtility flushTempEventLists.
	ExternalInterface allSubclassesDo: 
		[ :each | each  unloadLibraries].
	ObjectMemory globalGarbageCollect!

showInput
	"Open MIDI, wait to read something, then dump it to the transcript."
	"This toggles on and off if called repeatedly."
	"MIDIPort showInput"
	"Instance input"

	self default open.
	Instance debug
		ifTrue: [Transcript cr; show: 'Stopping MIDI Dump'; cr; cr.
			Instance debug: false.
			Instance stopMIDIEcho.
			Instance stopMIDIInput]
		ifFalse: [Transcript cr; cr; show: 'Starting MIDI Dump'; cr.
			Instance debug: true.
			Instance startMIDIEcho.
			Instance stopControllerCaching.
			Instance startMIDIInput]! !

!Siren.MIDIPort class methodsFor: 'input tests'!

dumpExample
	"Set up a MIDI dump object as a dependent of the input port. Dump for 10 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"

	| inst port |
"Get a MIDIDump and the input port."
	inst := MIDIDump new.
	port := MIDIPort default.
"Add the dump as a dependent of the port."
	port addDependent: inst.
	port debug: false.
"Turn on the port for input."
	port startMIDIEcho.
	port startMIDIInput.
"Wait"
	Cursor wait showWhile: [(Delay forSeconds: 10) wait].
"Turn the port off"
	port stopMIDIInput.
"Don't forget to remove the dependent."
	port removeDependent: inst.!

testInput
	"Open MIDI, try to read something--dump it to the transcript."
	"MIDIPort testInput"
	"MIDIPort testInputStop"
	"Instance input"
	"Instance resetInput"
	"MIDIPort instance eventsAvailable"

	self new.
	Instance debug: true.
	Instance startMIDIEcho.
	Instance stopControllerCaching.
	Instance startMIDIInput.

"Execute this to end the test"
"	Instance debug: false.
	Instance stopMIDIEcho.
	Instance stopMIDIInput"

"	Instance eventsAvailable"!

testInput0
	"Open MIDI, try to read something."
	"MIDIPort testInput"

	| inst data |
	inst := MIDIPort default.
	[inst input isEmpty]
		whileTrue: [(Delay forSeconds: 1) wait].
	data := inst input first.
	Transcript show: data printString.
	self halt.!

testInput1
	"Open MIDI, try to read something."
	"MIDIPort testInput1"
	"Instance input"

	| port |
	port := MIDIPort default.
	port startMIDIEcho.
	port startMIDIInput.
		[port input isEmpty] whileTrue: 
			[(Delay forSeconds: 1) wait].
	Transcript show: 'Events: ', port eventsAvailable printString; cr.
	port input inspect!

testInputGet
	"Get the default's pending input."
	"MIDIPort testInputGet"

	MIDIPort default readAll!

testInputStop
	"Execute this to end the input test"
	"MIDIPort testInputStop"

	self new.
	Instance debug: false.
	Instance stopMIDIEcho.
	Instance stopMIDIInput! !

!Siren.MIDIPort class methodsFor: 'basic output tests'!

testAllNotesOff
	"Try to open and close the MIDI port."
	"MIDIPort testAllNotesOff"

	| inst err data |
	inst := MIDIPort new.
	err := inst open.
	Transcript cr; show: 'open --> ', err printString; cr.
	data := ByteArray new: 3.
	data at: 1 put: 16r90.		"note-on"
	data at: 2 put: 60.			"pitch"
	data at: 3 put: 127.		"velocity"
	err := inst put: data length: 3.
	err := inst allNotesOff.
	Transcript show: 'ANO --> ', err printString; cr.
	err := inst close.
	Transcript show: 'close --> ', err printString; cr.!

testANote
	"Open MIDI, play a note."
	"MIDIPort release"
	"MIDIPort testANote"

	| inst |
	inst := MIDIPort new.
	inst open.
	inst play: 48 dur: 500 amp: 120 voice: 0.
	1 wait.
	inst close.!

testARandomNote
	"Open MIDI, play a note."
	"MIDIPort testARandomNote"

	| inst err data rand |
	rand := Random new.
	inst := MIDIPort new.
	err := inst open.
	Transcript cr; show: 'open --> ', err printString; cr.
	data := ByteArray new: 3.
	data at: 1 put: 16r90.			"note-on"
	data at: 2 put: (rand next * 36 + 24) truncated.		"pitch"
	data at: 3 put: 96.		"velocity"
	err := inst put: data length: 3.
	Transcript show: 'note-on --> ', err printString; cr.
	(Delay forMilliseconds: 1000) wait.
	data at: 1 put: 16r80.		"silence = note-off"
	err := inst put: data length: 3.
	Transcript show: 'note-off --> ', err printString; cr.!

testAScale
	"Open MIDI, play a fast scale."
	"Play all the notes now; expect the instance to do the delays."
	"Scale example answers (start dur pitch amp)."
	"MIDIPort testAScale"
	"MIDIPort cleanUp"

	| inst |
	inst := MIDIPort default.
	inst open.
	(MIDIPort scaleExampleFrom: 60 to: 84 in: 2000) do:
		[ :not |
		inst play: (not at: 3) dur: (not at: 2) amp: (not at: 4) voice: 0.
		(Delay forMilliseconds: (not at: 2)) wait].!

testInspect
	"Inspect a MIDI port."
	"MIDIPort testInspect"

	| inst err |
	inst := MIDIPort new.
	inst inspect.
"	err := inst open.
	Transcript cr; show: 'open --> ', err printString; cr.
	err := inst test.
	Transcript show: 'test --> ', err printString; cr.
	err := inst close.
	Transcript show: 'close --> ', err printString; cr.
"!

testMouseMIDI
	"Open MIDI, play notes based on the mouse position."
	"x --> voice; y --> pitch."
	"MIDIPort testMouseMIDI"

	| inst moPt ht |
	inst := MIDIPort default.
	inst open.
	MIDIPort setupTunedPercussion.
	(Delay forMilliseconds: 200) wait.
	ht := Screen default bounds height.
	[InputState default mouseButtons = 0] whileTrue:
		[moPt := InputSensor cursorPoint.
		inst play: (ht - moPt y // 20 + 24) dur: 250 amp: 96 
				voice: (moPt x // 50).
		(Delay forMilliseconds: 100) wait].!

testNoteOnOff
	"Open MIDI, play a note, and close it."
	"MIDIPort testNoteOnOff"

	| inst err data |
	inst := MIDIPort new.
	data := ByteArray new: 3.
	data at: 1 put: 16r90.
	data at: 2 put: 64.
	data at: 3 put: 96.
	err := inst open.
	Transcript cr; show: 'open --> ', err printString; cr.
	err := inst put: data length: 3.
	Transcript show: 'play --> ', err printString; cr.
	data at: 3 put: 0.
	err := inst put: data length: 3.
	Transcript show: 'play --> ', err printString; cr.
	err := inst close.
	Transcript show: 'close --> ', err printString; cr.!

testOpenClose
	"Try to open and close the MIDI port."
	"MIDIPort testOpenClose"
	"MIDIPort default close"
	"MIDIPort cleanUp"

	| inst err |
	inst := MIDIPort new.
	err := inst open.
	Transcript cr; show: 'open --> ', err printString; cr.
	err := inst close.
	Instance := nil.
	Transcript show: 'close --> ', err printString; cr.!

testOutput
	"Open MIDI, play some random notes, and close it."
	"MIDIPort testOutput"

	| inst err |
	inst := MIDIPort new.
	err := inst open.
	Transcript cr; show: 'open --> ', err printString; cr.
	err := inst test.
	Transcript show: 'test --> ', err printString; cr.
	err := inst close.
	Transcript show: 'close --> ', err printString; cr.! !

!Siren.MIDIPort class methodsFor: 'controller tests'!

testControllerCaching
	"Set up uncached controller reading--make a loop that reads and prints controller 48 
	twice a second (until you press a mouse button)."
	"MIDIPort testControllerCaching"
	"MIDIPort testInputStop"

	self new.
	Instance debug: false.
	Instance startMIDIEcho.
	Instance startControllerCaching.
	Instance startMIDIInput.
	[InputSensor default anyButtonPressed]
		whileFalse:
		[Transcript show: 'Ctrl 48: ', (Instance readController: 48) printString; cr.
		(Delay forMilliseconds: 500) wait].
	MIDIPort testInputStop!

testControllerCaching2
	"Set up uncached controller reading--read controllers 48-52 as an array and print it; stop on mouse press."
	"MIDIPort testControllerCaching2"
	"MIDIPort testInputStop"

	| array |
	self new.
	Instance debug: false.
	Instance startMIDIEcho.
	Instance startControllerCaching.
	Instance startMIDIInput.
	[InputSensor default anyButtonPressed]
		whileFalse:
		[array := Instance readControllersFrom: 48 to: 52.
		Transcript tab; show: array printString; cr.
		(Delay forMilliseconds: 500) wait].
	MIDIPort testInputStop!

testControllerCachingFrom: lo to: hi
	"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"
	"MIDIPort testInputStop"

	| array |
	self new.
	Instance debug: false.
	Instance startMIDIEcho.
	Instance startControllerCaching.
	Instance startMIDIInput.
	[InputSensor default anyButtonPressed]
		whileFalse:
		[array := Instance readControllersFrom: lo to: hi.
		Transcript tab; show: array printString; cr.
		(Delay forMilliseconds: 500) wait].
	MIDIPort testInputStop!

testControllerInput
	"Set up uncached controller reading and dump input to the transcript."
	"MIDIPort testControllerInput"
	"MIDIPort testInputStop"

	self new.
	Instance debug: true.
	Instance startMIDIEcho.
	Instance stopControllerCaching.
	Instance startMIDIInput.! !

!Siren.MIDIPort class methodsFor: 'driver performance tests'!

testRandomPlayHighLevel: num dur: dur
	"Play 'num' random pitches (molto legato) spaced 'dur' msec apart."
	"This version uses the 1-call note command and driver-side scheduler."
	"MIDIPort testRandomPlayHighLevel: 64 dur: 80"

	| inst rand time |
	rand := Random new.
	inst := MIDIPort default.
	time := Time millisecondClockValue + 100.
	num timesRepeat:
		[inst play: (rand next * 36 + 24) truncated "random pitches"
				at: time 
				dur: (dur * 4) truncated 		"molto legato"
				amp: 96 voice: 0.
		time := time + dur].
	(Delay forMilliseconds: ((num * dur) + 1000)) wait!

testRandomPlayLowLevel: num dur: dur
	"Play 'num' random pitches spaced 'dur' msec apart."
	"This test creates the messages and does the scheduling right here."
	"MIDIPort testRandomPlayLowLevel: 64 dur: 80"

	| inst data rand |
	rand := Random new.
	inst := MIDIPort new.
	inst open.
	data := ByteArray new: 3.
	data at: 3 put: 96.				"velocity"
"	data at: 2 put: 60."
	num timesRepeat:
		[data at: 1 put: 16r90.		"note-on"
		data at: 2 put: (rand next * 36 + 24) truncated.	"pitch"
		inst put: data length: 3.
		(Delay forMilliseconds: dur) wait.
		Processor yield.
		data at: 1 put: 16r80.			"note-off"
		inst put: data length: 3]!

testRollLowLevel: num dur: dur
	"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: 80"

	| inst data |
	inst := MIDIPort new.
	inst open.
	data := ByteArray new: 3.
	data at: 3 put: 96.				"velocity"
	data at: 2 put: 60.				"pitch"
	num timesRepeat:
		[data at: 1 put: 16r90.		"note-on"
		inst put: data length: 3.
		(Delay forMilliseconds: dur) wait.
		data at: 1 put: 16r80.			"note-off"
		inst put: data length: 3]! !

!Siren.MIDIPort class methodsFor: 'message tests'!

testBend
	"Demonstrate pitch-bend by playing two notes and bending them."
	"MIDIPort testBend"

	| port |
	port := self default.
	port open.
"Set the recorder instrument."
	port programChange: 0 to: 'Recorder'.
	port programChange: 1 to: 'Recorder'.
"Play two notes."
	port play: 76 dur: 5000 amp: 60 voice: 0.
	port play: 80 dur: 5000 amp: 60 voice: 1.
"Bend them--one up, one down."
	0 to: 500 do:
		[ :i |
		port pitchBend: 0 to: 4096 + (i * 8).
		port pitchBend: 1 to: 4096 - (i * 8).
		(Delay forMilliseconds: 10) wait]!

testControlContinuous
	"Demonstrate control commands by playing a note and making a crescendo with the volume pedal."
	"MIDIPort testControlContinuous"

	| port |
	port := self default.
	port open.
"Set the sound to organ."
	port programChange: 0 to: 'Church Organ'.
"Play a note."
	port play: 60 dur: 5000 amp: 70 voice: 0.
"Fade it."
	0 to: 120 do:
		[ :i |
		port controlChange: 0 controller: 4 to: i.
		(Delay forMilliseconds: 50) wait]!

testProgramChange
	"Demonstrate program change by setting up an organ instrument to play on."
	"MIDIPort testProgramChange"

	| port |
	port := self default.
	port open.
"Set the flute instrument."
	port programChange: 0 to: 'Church Organ'.
"Play a note."
	port play: 72 dur: 1000 amp: 70 voice: 0.
	port play: 76 dur: 900 amp: 70 voice: 0.!

testSysex
	"Demonstrate system exclusive commands by loading the Santur scale
	and playing a scale."
	"Note that this is a Roland GS sysEx, but it should work on QuickTime Musical Instruments."
	"MIDIPort testSysex"

	| port command |
	port := self default.
	port open.
"Set the flute instrument."
	port programChange: 0 to: 'Sitar'.
"Down-load an alternate tuning scale."
	command := #(16r41 16r10 16r42 16r12 16r40 16r11 "address"
			16r40 16r40 16r72 16r40 16r40 16r0E 16r40 16r40 16r40 16r0E 16r40 16r40 "scale"
			16r0E 16r53 "checksum").
	port sysex: command.
"Play a scale."
	MIDIPort testAScale! !

!Siren.MIDIPort class methodsFor: 'general MIDI patches'!

resetEnsemble
	"Reset the GM map"
	"MIDIPort resetEnsemble"

	| port |
	port := self default.
	port open.
	GeneralMIDIMap do:
		[ :item |
		port programChange: item to: item + 1]!

setAllInstrumentsTo: iname
	"Set instruments 0-15 to the General MIDI name iname"
	"MIDIPort setAllInstrumentsTo: 'Tenor Sax'. MIDIPort default test"
	"MIDIPort setAllInstrumentsTo: 'Vibraphone'. MIDIPort default test"

	| port |
	port := self default.
	0 to: 15 do:
		[ :ind |
		port programChange: ind to: (GeneralMIDIMap at: iname)]!

setEnsemble: orch
	"Down-load a general MIDI patch for the given ensemble (a collection of 
	[chan -> name] associations)."
	"MIDIPort setEnsemble: 
			(Array with: (0 -> 'Reed Organ') with: (1 -> 'Rock Organ')
					with: (2 -> 'Church Organ') with: (3 -> 'Drawbar Organ')
					with: (4 -> 'Percussive Organ') with: (5 -> 'Accordion'))"

	| port |
	port := self default.
	port open.
	orch do:
		[ :item |
		port programChange: item key to: (GeneralMIDIMap at: item value asSymbol)]!

setEnsembleInOrder: orch
	"Down-load a general MIDI patch for the given ensemble (a collection of symbolic keys 
	into the General MIDI voice map) mapping the first element to MIDI channel 1, etc."

	| port |
	port := self default.
	port open.
	1 to: orch size do:
		[ :ind |
		port programChange: (ind - 1) to: (GeneralMIDIMap at: (orch at: ind) asSymbol)]!

setupOrgan
	"Down-load a general MIDI patch for a 4-voice organ."
	"MIDIPort setupOrgan. MIDIPort instance test"

	MIDIPort setEnsemble: 
			(OrderedCollection with: (0 -> 'Reed Organ') with: (1 -> 'Rock Organ')
					with: (2 -> 'Church Organ') with: (3 -> 'Drawbar Organ')
					with: (4 -> 'Percussive Organ') with: (5 -> 'Accordion'))!

setupTunedPercussion
	"Down-load a general MIDI patch for a 16-voice percussion ensemble."
	"MIDIPort setupTunedPercussion. MIDIPort default test"

	MIDIPort setEnsembleInOrder: 
			#(Agogo 'Tinkle Bell' Timpani Xylophone 
				Applause 'Taiko Drum' Glockenspiel 'Synth Drum'
				Gunshot 'Steel Drums' Helicopter Vibraphone
				Woodblock 'Telephone Ring' Kalimba 'Blown Bottle')!

setupWindOrchestra
	"Down-load a general MIDI patch for a 16-voice wind ensemble."
	"This could be done more tersely with the setEnsembleInOrder: method."
	"MIDIPort setupWindOrchestra. MIDIPort default test"
	"GeneralMIDIMap keys asSortedCollection"

	| port orch |
	port := self default.
	port open.
	orch := #(Flute 'English Horn' Bassoon Clarinet Piccolo 'Pan Flute' Recorder 'Tenor Sax'
			  Flute 'English Horn' Bassoon Clarinet Piccolo 'Pan Flute' Recorder 'Tenor Sax').
	1 to: orch size do:
		[ :ind |
		port programChange: (ind - 1) to: (GeneralMIDIMap at: (orch at: ind) asString)]! !

!Siren.MIDIPort class methodsFor: 'examples'!

examples
	"Select and execute the following for usage examples."

"Test for the existence of the MIDI driver and, if present, report its version and properties to the transcript."
	"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"
"Open MIDI, play notes based on the mouse position (x --> voice; y --> pitch) until mouse down."
	"MIDIPort testMouseMIDI"

"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"

"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"

"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"

"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"

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

scaleExampleFrom: lo to: hi in: dur
	"Answer array of (start dur pitch amp)"
	"MIDIPort scaleExampleFrom: 48 to: 72 in: 2000"

	| arr size ndur |
	size := hi - lo + 1.
	ndur := (dur / size) truncated.
	arr := Array new: size.
	1 to: size do:
		[ :ind |
		arr at: ind put: (Array with: (ind - 1 * ndur) with: ndur with: (ind + lo - 1) with: 96)].
	^arr! !

!Siren.PortMIDIPort methodsFor: 'initialize release'!

initialize
	"Setup the receiver's instance variables."
	"MIDIPort testANote"

	| return numDevices str | 
	super initialize.
	driver := PortMidiInterface new.
	return := driver pm_initialize.
	(return = 0)
		ifFalse: [self error: 'MIDI initialize failed'].
	isOn := false.
	Streams := Array new: 8 withAll: nil.
	numDevices := driver pm_count_devices.
	Devices := Array new: numDevices.
"	str := (String new: 128) gcCopyToHeap.
	Transcript cr; show: 'Open MIDI driver; devices:'; cr.
	1 to: numDevices do:
		[ :ind | | len |
		len := driver pm_get_name: ind with: str.
		Devices at: ind put: str copyCStringFromHeap.
		Transcript tab; show: (Devices at: ind); cr]
"!

release
	"Release--break and dependencies on the error value."

	super release.
	driver pm_terminate.! !

!Siren.PortMIDIPort methodsFor: 'open/close'!

close
	"Close MIDI."
	"MIDIPort instance close"

	self close: 0!

close: stream
	"Close MIDI."
	"MIDIPort instance close"

	driver pm_close: stream.
	isOn := false.
	^#OK!

open
	"Open the MIDI driver -- start the lower-level driver up."
	"MIDIPort default"

	self isActive
		ifTrue: [^self].
	^self openOutput: 1!

openInput: device
	"Open the MIDI driver -- start the lower-level driver up."
	"MIDIPort default"

	| which |
	which := driver pm_open: device with: 1.
	(which < 0)
		ifTrue: [self error: 'MIDI open failed'].
	Streams at: which put: #in.
	isOn := true.
	^which!

openOutput: device
	"Open the MIDI driver -- start the lower-level driver up."
	"MIDIPort default"

	| which |
	which := driver pm_open: device with: 0.
	(which < 0)
		ifTrue: [self error: 'MIDI open failed'].
	Streams at: (which + 1) put: #out.
	isOn := true.
	^which!

terminate
	"Close MIDI."
	"MIDIPort instance close"

	driver pm_terminate.
	isOn := false! !

!Siren.PortMIDIPort methodsFor: 'accessing'!

isActive
	"Answer whether the receiver is active."

	^(isOn == true)! !

!Siren.PortMIDIPort methodsFor: 'ioctl'!

startControllerCaching
	"Start caching MIDI controller in the driver."

	self halt!

startMIDIInput
	"Start signalling the read semaphore on MIDI input."

	self halt!

stopControllerCaching
	"Start caching MIDI controller in the driver."

	self halt!

stopMIDIInput
	"Stop signalling the read semaphore on MIDI input."

	self halt! !

!Siren.PortMIDIPort methodsFor: 'read/write'!

playOff: aPitch amp: anAmp voice: aVoice
	"Play the argument on the receiver--no duration-->no note-off."

	driver pm_write_data3: 0 with: ((MIDICommands at: #noteOff) bitOr: aVoice) with: aPitch with: anAmp!

playOff: streamID pitch: pitch amp: amp voice: voice
	"Play the argument on the receiver."

	driver pm_write_data3: streamID with: ((MIDICommands at: #noteOn) bitOr: voice) with: pitch with: 0!

playOn: aPitch amp: anAmp voice: aVoice
	"Play the argument on the receiver--no duration-->no note-off."

	driver pm_write_data3: 0 with: ((MIDICommands at: #noteOn) bitOr: aVoice) with: aPitch with: anAmp!

playOn: streamID pitch: pitch amp: amp voice: voice
	"Play the argument on the receiver--no duration-->no note-off."

	driver pm_write_data3: streamID with: ((MIDICommands at: #noteOn) bitOr: voice) with: pitch with: amp!

put: streamID data: data length: size
	"Send the argument (a ByteArray for historical reasons) to the receiver now."

	(size < 2)
		ifTrue: [self error: 'Unknown MIDI command format -- illegal length'].
	(size = 2)
		ifTrue: [driver pm_write_data2: streamID with: (data at: 1) with: (data at: 2)].
	(size = 3)
		ifTrue: [driver pm_write_data3: streamID with: (data at: 1) with: (data at: 2) with: (data at: 3)].
	(size > 3)
		ifTrue: [self halt]. 		"not supported yet"
	^#OK!

put: data length: size
	"Send the argument (a ByteArray for historical reasons) to the receiver now."

	^self put: 0 data: data length: size! !

!Siren.PortMIDIPort class methodsFor: 'examples'!

testANote
	"Open MIDI, play a note."
	"PortMIDIPort testANote"

	| inst |
	inst := MIDIPort new.		"this answers an instance of me."
	inst open.
	inst playOn: 36 amp: 100 voice: 0.
	1 wait.
	inst playOff: 36 amp: 100 voice: 0.
	inst close.
	inst terminate.
	MIDIPort cleanUp! !

!Siren.MIDIDump methodsFor: 'updating'!

update: aspect
	"The model (port) changed; echo MIDI input to the Transcript."
	"MIDIDump example"

	| port packet stream |
	port := MIDIPort default.
	packet := port input removeFirst.
	stream := WriteStream on: (String new: 32).
	stream nextPutAll: (MIDICommands keyAtValue: packet command), ' [ '.
	1 to: packet length do:
		[ :i | packet print: (packet data at: i) on: stream. stream space].
	stream nextPut: $].
	Transcript show: stream contents; cr! !

!Siren.MIDIDump class methodsFor: 'examples'!

example
	"Set up a MIDI dump object as a dependent of the input port. Dump for 10 seconds,
	then turn off. The default update: method just dumps the MIDI packet into the transcript; 
	customize this by writing your own update: method."
	"MIDIDump example"

	| inst port |
"Get a MIDIDump and the input port."
	inst := MIDIDump new.
	port := MIDIPort default.
"Add the dump as a dependent of the port."
	port addDependent: inst.
	port debug: false.
"Turn on the port for input."
	port startMIDIEcho.
	port startMIDIInput.
"Wait"
	Cursor wait showWhile: [(Delay forSeconds: 10) wait].
"Turn the port off"
	port stopMIDIInput.
"Don't forget to remove the dependent."
	port removeDependent: inst.! !

!Siren.Length methodsFor: 'private'!

species
	"Answer Duration."

	^Duration! !

!Siren.Length class methodsFor: 'examples'!

example
	"Demonstrate the use of an OrdinalMagnitude with a scale of length."
	"Length example"

	| a b c d e f |		"define six values with names."
	Length useTable.
	a := Length value: #sip.
	b := Length value: #gasp.
	c := Length value: #focus.
	d := Length value: #inhale.
	e := Length value: #gesture.
	f := Length value: #breath.
					"put them in some order."
	a << b.	b << c.	c << d.	d << e.	e << f.
					"now ask some queries and let them extrapolate."
	Transcript show: '(f > e) ', (f > e) printString; cr.
	Transcript show: '(f > b) ', (f > b) printString; cr.
	Transcript show: '(f =? f) ', (f =? f) printString; cr.
	Transcript show: '(f =? a) ', (f =? a) printString; cr.
	Transcript show: '(f >= a) ', (f >= a) printString; cr.
	Transcript show: '(d >= b) ', (d >= b) printString; cr.
	Transcript show: '(b >= d) ', (b >= d) printString; cr.
	Transcript show: '(f < a) ', (f < a) printString; cr.
	Transcript show: '(a < f) ', (a < f) printString; cr.
					"inspect them if you hold down <LEFT-SHIFT>"
	Length allInstances asSortedCollection inspect! !

!Siren.EventList methodsFor: 'initialize-release'!

empty
	"Throw away the receiver's events."

	self duration: 0.
	self index: 1.
	self events: SortedCollection new.!

initializeAnonymous
	"Initialize an un-named instance with default state."

	self initialize.
	self propCheck.
	duration := Duration value: 0.
	self name: #anonymous.
	self events: SortedCollection new.!

initializeNamed: aName
	"Initialize a named instance with default state."

	self initializeAnonymous.
	self name: aName asSymbol.
	SirenUtility eventList: aName asSymbol put: self!

release
	"Flush the receiver."

	super release.
	events == nil
		ifFalse: [events do: [ :e | e release]].
	events := nil.
	index := nil!

reset
	"Reset the receiver's index."

	index := 1! !

!Siren.EventList methodsFor: 'accessing'!

addModifier: aModifier
	"Add the given event modifier to the receiver's special modifier collection"

	properties == nil
		ifTrue: [properties := Dictionary new].
	(properties includesKey: #modifiers)
		ifFalse: [properties at: #modifiers put: OrderedCollection new].
	(properties at: #modifiers) add: aModifier!

code
	"Answer a symbol about the receiver's events' coding, usually #durations or #noteOnOff"

	^properties at: #code ifAbsent: [nil]!

code: aSymbol
	"Set the code about the receiver's events, usually #durations or #noteOnOff"

	self propCheck.
	properties at: #code put: aSymbol!

deepCopy
	"Answer a careful deep copy."

	| newEL |
	newEL := EventList newAnonymous.
	self name == nil
		ifFalse: [newEL name: self name deepCopy asSymbol].	
	properties == nil ifFalse:
		[properties associationsDo: [ :anAssociation |
			newEL at: anAssociation key copy put: anAssociation value copy]].
	self events do: [ :anAssociation |
		newEL add: ((anAssociation key copy) => (anAssociation value copy)) ].
	^newEL!

defaultEventClass
	"Answer the default note event class."

	^MusicEvent!

edit
	"Open your favorite graphical editor on the receiver (left-shift-sensitive)"

	HauerSteffensView openOnEventList: self

"	InputState default  shiftDown
		ifTrue: [CMNView openOn: ((EventListEditor new) list: self)]
		ifFalse: [InputState default  ctrlDown
			ifTrue: [SequenceView openOn: 
						((EventListEditor new) list: self)]
			ifFalse: [HauerSteffensView openOn: 
					((EventListEditor new) list: self)]]"!

index: aNumber
	"Set the receiver's event index."

	index := aNumber!

name
	"Answer the receiver's name."

	self propCheck.
	^properties at: #name ifAbsent: ['']!

name: aName
	"Set the receiver's name."

	properties at: #name put: aName asSymbol!

next
	"Answer the next event in the list."

	index == nil
		ifTrue: [index := 1].
	^self events at: (index := index + 1)!

removeModifier: aModifier
	"Remove the given event modifier from the receiver's collection."

	^(properties == nil or: [(properties includesKey: #modifiers) not])
		ifTrue: [nil]
		ifFalse: [(properties at: #modifiers) remove: aModifier ifAbsent: [nil]]!

shallowCopy
	"Answer a shallow copy of the receiver's events."

	| newEL |
	newEL := super shallowCopy.
	newEL events: SortedCollection new.
	self events do: 
		[ :anAssociation |
		newEL add: ((anAssociation key copy) => (anAssociation value copy))].
	^newEL!

size
	"Answer the number of events in the receiver."

	events ifNil: [^0].
	^events size!

tempo: someValue
	"Set the tempo to scale the events by."

	self propCheck.
	properties at: #tempoScale put: someValue!

voice
	"Answer the receiver's voice."

	^voice!

voice: aValue
	"Set the receiver's voice to the argument."

	voice := aValue!

voices
	"Answer the collection of voices used by thereceiver  event list."
	"EventList randomExample voices"

	| coll ev |
	coll := OrderedCollection new.
	events do:
		[ :ass |
		ev := ass event.
		ev voice ifNotNil:
			[(coll includes: ev voice)
				ifFalse: [coll add: ev voice]]].
	^coll! !

!Siren.EventList methodsFor: 'event accessing'!

, anEorA
	"Add a new event or association to the receiver."

	^self add: anEorA!

add: anEorA
	"Add a new event or association to the receiver."

	| newAss val |
	anEorA isAssociation
		ifTrue: [newAss := anEorA key copy => anEorA value.
			val := anEorA value.
			newAss key species == Duration
				ifFalse: [newAss key: (Duration value: newAss key)].
			val isEvent
				ifFalse: [val isMusicMagnitude
					ifTrue: [val := (self defaultEventClass new)
							perform: val species propertyName with: val]
					ifFalse: [self error: 'Unknown EventAssociation value']].
			self events add: newAss.
			val duration == nil
				ifTrue: [self duration: (duration max: newAss key)]
				ifFalse: [self duration: (duration max: (newAss key + val duration))]]
		ifFalse: [self events add: (duration => anEorA).
			anEorA duration ifNotNil: [self duration: duration + anEorA duration]].
	self changed: #added!

add: anEvent at: aTime
	"Add a new event to the receiver at the given relative start time."

	| t |
	t := Duration value: aTime.
	self events add: (t => anEvent).
	anEvent duration == nil
		ifFalse: [duration := duration max: (t + anEvent duration)].
	self changed: #added!

addAll: anEventList
	"Add the given event list's events to the receiver."

	anEventList events do:
		[ :assoc |
			self events add: (assoc key copy => assoc value copy).
			assoc value duration == nil 
				ifTrue: [duration := duration max: assoc key]
				ifFalse: [duration := duration 
							max: (assoc key + assoc value duration)]].
	self changed: #added!

eventKeys
	"Answer the keys (durations) of the receiver's eventDictionary."

	^events keys!

eventNear: aPoint
	"Answer an event within a reasonable distance (100 msec.) of the given point's x."

	| margin oldAss |
	(events includesKey: aPoint x)
		ifTrue: [^events at: aPoint x].
	margin := 100. "ms"
	oldAss := nil.
	events do:
		[ :anAss |
		anAss key > aPoint x
			ifTrue: [oldAss == nil
				ifTrue: [(anAss key - aPoint x) abs < margin
					ifTrue: [^anAss]
					ifFalse: [^nil]]
				ifFalse: [(anAss key - aPoint x) abs < margin
						ifTrue: [^anAss].
					(oldAss key - aPoint x) abs < margin
						ifTrue: [^oldAss].
					^nil]].
		oldAss := anAss].
	^nil!

eventNearestTime: aTime
	"Answer an event within a reasonable distance (32 msec.) of the given point."

	| margin ass d |
	ass := events first.
	margin := ass key value - aTime abs.
	events do:
		[ :anAss | 
		d := (anAss key value - aTime) abs.
		d < margin ifTrue: 
			[margin := d.
			ass := anAss]].
	^ass!

eventNearTime: aTime
	"Answer an event within a reasonable distance (32 msec.) of the given point."

	| margin oldAss t |
	margin := 32. "ms"
	oldAss := events first.
	events do:
		[ :anAss | 
		t := anAss key value.
		t > aTime
			ifTrue: [oldAss == nil
				ifTrue: [(t - aTime) abs < margin
					ifTrue: [^anAss]
					ifFalse: [^nil]]
				ifFalse: [(t - aTime) abs < margin
						ifTrue: [^anAss].
					(oldAss key - aTime) abs < margin
						ifTrue: [^oldAss].
					^nil]].
		oldAss := anAss].
	^nil!

events
	"Answer the receiver's eventDictionary."

	^events!

events: anEL
	"Set the receiver's eventDictionary."

	events := anEL.
	self changed: #added!

eventValues
	"Answer the values (events) of the receiver's eventDictionary."

	^events values!

recomputeDuration
	"Recompute the total duration of the receiver."

	| tDur t d |
	tDur := 0 msec.
	events do:
		[ :ass |
		t := ass key.
		d := ass event duration.
		tDur := tDur max: (t +d)].
	duration := tDur.
	self changed: #duration! !

!Siren.EventList methodsFor: 'comparing'!

= anObject 
	"Answer whether the receiver and the argument represent the same events."

	^anObject isEventList
		ifFalse: [false]
		ifTrue: [((self duration = anObject duration) and:
				[self events = anObject events])]! !

!Siren.EventList methodsFor: 'testing'!

hasItems
	"Answer whether or not the receiver has items or components (true)."

	^true!

isEmpty
	"Answer whether the receiver is an event list (true)."

	^events isEmpty!

isEventList
	"Answer whether the receiver is an event list (true)."

	^true!

species
	"Answer EventList."

	^EventList! !

!Siren.EventList methodsFor: 'collecting'!

collect: aBlock
	"Iterate over the receiver's events with the given block"

	| newList |
	newList := EventList new.
	self events do: 
		[ :anE |
		newList add: (aBlock value: anE)].
	^newList!

collectAll: aBlock
	"Iterate over the receiver's events with the given block"

	| newList |
	newList := EventList new.
	self events do: 
		[ :anE |
		anE isEventList				"hierarchical tree descent"
			ifTrue: [(anE collectAll: aBlock) do:
				[ :evt | newList add: (evt key + anE key) => evt value]]
			ifFalse: [newList add: (aBlock value: anE)]].
	^newList!

do: aBlock
	"Iterate over the receiver's event associations with the given block"

	self events do: aBlock!

eventsDo: aBlock
	"Iterate over the receiver's events with the given block"

	^self events do: [:assoc | aBlock value: (assoc event) ]!

excerptFrom: start to: end
	"Answer a hierarchical list delineated by the given times."

	| newList newEvent |
	newList := EventList newAnonymous.
	events do: 
		[ :anAssoc |				"iterate over all my events"
		(anAssoc key value between: start and: end) 
			ifTrue: [newEvent := anAssoc value.
				(newEvent isEventList)	"map and add normal events"
					ifFalse: [newList add: anAssoc]
									"unfold sub-event lists recursively"
					ifTrue: [newList add:
							(newEvent excerptFrom: 0 
									to: (end - anAssoc key))]]].
	^newList!

expanded
	"Expand all sub-event lists and answer one large flat list."

	| newList newEvent |
	newList := EventList newAnonymous.
	events do: [ :anEvent |				"iterate over all my events"
		newEvent := anEvent value "copy".	"copy them"
		(newEvent species == EventList) 
			ifTrue:						"unfold sub-lists recursively"
				[newEvent expanded do: [ :aSubEvent |
					newList add: (newEvent map: 
						((anEvent key + aSubEvent key) 
								-> aSubEvent value))]]
			ifFalse: 						"map and add normal events"
				[newList add: ( anEvent key -> newEvent)]].
	^newList!

expandedFrom: start to: end
	"Expand all sub-event lists and answer one flat list with events between the given times."

	| newList newEvent |
	((start value = 0) and: [end > duration])
		ifTrue: [^self expanded].
	newList := EventList newAnonymous.
	events do: 
		[ :anEvent |				"iterate over all my events"
		newEvent := anEvent value.
		(anEvent key between: start and: end) 
			ifTrue: [newEvent isEventList
							 "map and add normal events"
				ifFalse: [newList add: 
							(anEvent key => newEvent copy)]
								"unfold sub-event lists recursively"
				ifTrue: [(newEvent
							expandedFrom: (MSecondDuration value: 0)
							to: (end - anEvent key)) 
						do: [ :aSubEvent |
							newList add: (newEvent map: 
									((anEvent key + aSubEvent key) 
										=> aSubEvent value))]]]].
	^newList!

group: selection
	"Group the argument's events as a sub-event list in the receiver's list."

	| temp startTime |
	selection isEventList
		ifFalse: [self remove: selection.
			self add: ((EventList newAnonymous) add: (selection key: 0))
						at: selection key]
		ifTrue: [startTime := selection events first key.
			selection do:
				[ :anAss |
				temp := self remove: anAss.
				temp == nil
					ifTrue: [selection remove: anAss].
				startTime = 0
					ifFalse: [anAss key: (anAss key - startTime)]].
			selection duration: (selection duration value - startTime).
			self add: selection at: startTime]!

includes: anAss
	"Answer whether the given association is in the receiver's event list."

	^self events includes: anAss!

keysAndValuesDo: aBlock
	^self events do:
		[ :assoc |
		aBlock value: assoc key value: assoc value].!

remove: anA
	"Remove the given event from the receiver's  collection"

	| victims |
	victims := self events removeAllSuchThat:
		[ :item |
		(item key = anA key) and: [item value = anA value]].
	victims isEmpty
		ifTrue: [^self error: 'no such event in list...'].
	self changed: #deleted.
	^victims size = 1
		ifTrue: [victims first]
		ifFalse: [victims]!

select: aBlock
	"Iterate over the receiver's events with the given block"

	| newList |
	newList := EventList new.
	self events do: 
		[ :anE |
		(aBlock value: anE)
			ifTrue: [newList add: anE]].
	^newList!

selectAll: aBlock
	"Iterate over the receiver's events with the given block"

	| newList |
	newList := EventList new.
	self events do: 
		[ :anE |
			anE isEventList				"hierarchical tree descent"
			ifTrue: [(anE selectAll: aBlock) do:
				[ :evt | newList add: (evt key + anE key) => evt value]]
			ifFalse: [(aBlock value: anE)
				ifTrue: [newList add: anE]]].
	^newList!

selectKey: aBlock
	"Iterate over the receiver's events with the given block"

	| newList |
	newList := EventList new.
	self events do: 
		[ :anE |
		anE isEventList				"hierarchical tree descent"
			ifTrue: [(anE selectKey: aBlock) do:
				[ :evt | newList add: (evt key + anE key) => evt value]]
			ifFalse: [(aBlock value: anE key)
				ifTrue: [newList add: anE]]].
	^newList!

selectValue: aBlock
	"Iterate over the receiver's events with the given block"

	| newList |
	newList := EventList new.
	self events do: 
		[ :anE |
		anE isEventList				"hierarchical tree descent"
			ifTrue: [(anE selectValue: aBlock) do:
				[ :evt | newList add: (evt key + anE key) => evt value]]
			ifFalse: [(aBlock value: anE value)
				ifTrue: [newList add: anE]]].
	^newList!

timesDo: aBlock
	"Iterate over the receiver's events' relative start times with the given block"

	^self events do: [ :assoc | aBlock value: (assoc key) ]! !

!Siren.EventList methodsFor: 'processing'!

addValue: theValue toProp: theSelector
	"Add theValue to the aspect refered to by theSelector of all events."
	"someEventList addValue: (MSecondDuration value: 10000) toProp: #duration"
	"someEventList addValue: (MIDIPitch value: 100) toProp: #pitch"

	self applyBlock: [ :v | (v class new value: (v value + theValue))] 
			toProp: theSelector!

applyBlock: theBlock toProp: theSelector
	"Apply the given block to the aspect refered to by theSelector of all events."
	"someEList applyBlock: [ :d | (MSecondDuration value: (d asMS value * 10))] 
			toProp: #duration"

	| anEvent val |
	self events do: 
		[ :anAssociation |
		anEvent := anAssociation value.
		anEvent isEventList
			ifTrue: [anEvent applyBlock: theBlock toProp: theSelector]
			ifFalse: [val := anEvent perform: theSelector.
				anEvent perform: ((theSelector, ':') asSymbol)
						with: (theBlock value: val)]]!

applyFunction: aFunction to: aSelector
	"Apply the given function to the range of the given selector"

	^self applyFunction: aFunction to: aSelector startingAt: 0!

applyFunction: aFunction to: aSelector startingAt: sTime
	"Apply the given function to the range of the given selector starting at sTime."

	| aTime anEvent tDur timeC |
	tDur := self duration value.
	self events do:
		[ :anAssoc |
		aTime := anAssoc key value.
		timeC := anAssoc key class.
		anEvent := anAssoc value.
		aSelector == #tempo
			ifTrue: [anAssoc key: (timeC value: (aTime * (aFunction at: (aTime / tDur))))]
			ifFalse: [anEvent isEventList 
				ifTrue: [anEvent apply: aFunction to: aSelector startingAt: aTime]
				ifFalse: [ | oldVal |
					oldVal := anEvent perform: aSelector.
					anEvent perform: ((aSelector, ':') asSymbol) 
							with: (oldVal class value: (oldVal value * (aFunction at: (aTime / tDur))))]]]!

chooseRange: theRange toProp: theSelector
	"Select a value from the given range for the aspect refered to by theSelector of all events."
	"someEventList chooseRange: (500 to: 700) toProp: #duration"
	"someEventList chooseRange: #(36 38 40 41 43 45) toProp: #pitch"
	
	| size |
	size := theRange size.
	self applyBlock: [ :v | (v class new value: 
					(theRange at: ((Random new next * size) truncated + 1)))] 
			toProp: theSelector!

scale: theSelector by: theValue
	"Multiply the aspect refered to by theSelector of all events by theValue."
	"someEventList scale: #duration by: 2"

	self applyBlock: [ :v | (v class new value: (v value * theValue))] 
			toProp: theSelector!

scaleStartTimesBy: theValue
	"Multiply the start times all events by theValue."

	self events do: [ :assoc | assoc key: (assoc key * theValue) ].
	self recomputeDuration!

scaleValue: theValue toProp: theSelector
	"Multiply the aspect refered to by theSelector of all events by theValue."
	"someEventList scaleValue: 3 toProp: #duration"

	self applyBlock: [ :v | (v class new value: (v value * theValue))] 
			toProp: theSelector!

setValue: theValue toProp: theSelector
	"Set the aspect refered to by theSelector of all events to theValue."
	"someEventList scaleValue: (MSecondDuration value: 10) toProp: #duration"

	self applyBlock: [ :v | theValue] 
			toProp: theSelector!

spreadValue: theRandomPercent toProp: theSelector
	"Spread the given property of all events by the given random range (%+-)."

	self applyBlock: [ :v | (v class new value: (v value 
				* (1 + ((Random new next - 0.5) 
					* theRandomPercent / 50) rounded)))] 
			toProp: theSelector! !

!Siren.EventList methodsFor: 'scheduling'!

nextTime: ass
	"Answer the time delay between the given event association and the one that follows it."

	| ind next |
	ind := events indexOf: ass ifAbsent: [0].
	ind = 0 ifTrue: [^0 sec].
	ind = events size ifTrue: [^0 sec].
	next := events at: ind + 1.
	^(next start - ass start)!

play
	"Play the event list by passing it off to the event scheduler."
 
	EventScheduler instance addClient: self in: 0 msec.
	EventScheduler instance run!

play0
	"Play the event list on the voice by expanding its events"

	self voice == nil
		ifTrue: [self playOn: IOVoice default at: 0]
		ifFalse: [self playOn: self voice at: 0]!

play1
	"Play the event list on the voice by expanding its events"

	self events do:
		[ :ass |
		ass event playAt: ass start]!

playOn: aVoice
	"Play the event list on the voice by expanding its events"

	^self playOn: aVoice at: Time millisecondClockValue!

playOn: aVoice at: startTime
	"Play the event list on the voice by expanding its events"

	aVoice play: self at: startTime.!

scheduleAt: aTime 
	"Expand an eventList for the appointment scheduler."

	| newEvent |
	index == nil
		ifTrue: [index := 1.
			startedAt := Time millisecondClockValue].
	self events isEmpty ifTrue: [^nil].
	newEvent := self map: (self events at: index) at: aTime.
	newEvent voice ifNil:
		[newEvent voice: voice].
	newEvent play.			"play the next event"
	index := index + 1.
	index > self events size
		ifTrue: [index := nil. ^nil].
	^(self events at: index) key asMsec + startedAt
			"answer the time of the next appointment"! !

!Siren.EventList methodsFor: 'code conversion'!

continueProperties
	"Fill in missing properties with values that are continued from previous ones."
	"((440 Hz, (1/4 beat), (#voice -> #flute)), (1/4 => 460 Hz), (1/2 => 500 Hz), (3/4 => 320 Hz), (4/4 => 440 Hz)) continueProperties"

	| opitch oduration oloudness ovoice ev |
	ev := events first value.
	ev pitch == nil ifFalse: [opitch := ev pitch].
	ev duration == nil ifFalse: [oduration := ev duration].
	ev loudness == nil ifFalse: [oloudness := ev loudness].
	ev voice == nil ifFalse: [ovoice := ev voice].
	events do: [ :evt |
		evt pitch == nil ifTrue: [evt pitch: opitch]
					ifFalse: [opitch := evt pitch].
		evt duration == nil ifTrue: [evt duration: oduration]
					ifFalse: [oduration := evt duration].
		evt loudness == nil ifTrue: [evt loudness: oloudness]
					ifFalse: [oloudness := evt loudness].
		evt voice == nil ifTrue: [evt voice: ovoice]
					ifFalse: [ovoice := evt voice]]!

findNoteOff: anAssociation
	"Locate the noteOff event that corresponds to anAssociation; answer the sum of the delta times between (the duration)"

	| instrument time assoc1 assoc2 event2 event1 |
	instrument := anAssociation value voice.
	time := 0.
	event1 := anAssociation value.
	assoc1 := anAssociation.
	[true] whileTrue:
		[assoc2 := events after: assoc1.  		"check next"
		assoc2 isNil ifTrue: [^time].			"if missing."
		time := time + (assoc2 key value).			"sum delta times"
		event2 := assoc2 value.
		event2 voice = instrument						"if same instrument"
			ifTrue: [event2 ampl asMIDI = 0				"if ampl = 0"
				ifTrue: [event2 pitch = event1 pitch		"if same note"
					ifTrue: [^assoc2 key - anAssociation key]]]. "^time"
		event1 := event2.
		assoc1 := assoc2].						"next event"!

setDurations
	"Take a delta-time encoded event list and determine the durations."
	"(SunMIDIFileVoice on: ('mode/data/fuga1.midi' asFilename readStream binary)) 
		eventList setDurations inspect"

	| time sTime event newEventList anAss ind |
	(self code == #noteOnOff)				"if i'm delta not coded"
		ifFalse: [^nil ].
	newEventList := SortedCollection new.
	ind := 1.
	[ind <= events size] whileTrue:			"for all note on events"
		[anAss := events at: ind.
		sTime := anAss key.
		event := anAss value copy.
		(event ampl value) > 0 			"until note off found"
			ifTrue: [((event voice) bitAnd: 16rF0) = 16r90
				ifTrue: [time := self findNoteOff: anAss.
					time isNil
						ifTrue: [self error: 'no note off found...' ].
					event duration: (Duration value: time).
									"strip the Midi command"
					event voice: (event voice bitAnd: 16r0F).
					newEventList add: (sTime => event).
					Transcript show: ind printString, ' ']
								"meta or control event"
				ifFalse: ["Transcript show: 'meta event'; cr."
					newEventList add: (sTime => event)]].
		ind := ind + 1].
	self code: #durations.
						"now remove noteOffs"
	self events: newEventList.
	self start: newEventList first key.
	self at: #events put: (events size).
	self changed!

setDurations0
	"Take a delta-time encoded event list and determine the durations."
	"SunMIDIFileVoice midiFileExample"

	| time sTime event newEventList anAss ind |
	(self code == #noteOnOff)				"if i'm delta not coded"
		ifFalse: [^nil ].
	newEventList := SortedCollection new.
	ind := 1.
	[ind <= events size] whileTrue:			"for all note on events"
		[anAss := events at: ind.
		sTime := anAss key.
		event := anAss value copy.
		(event ampl value) > 0 			"until note off found"
			ifTrue: [((event voice) bitAnd: 16rF0) = 16r90
				ifTrue: [time := self findNoteOff: anAss.
					time isNil
						ifTrue: [self error: 'no note off found...' ].
					event duration: (Duration value: time).
									"strip the Midi command"
					event voice: (event voice bitAnd: 16r0F).
					newEventList add: (sTime => event).
					Transcript show: ind printString, ' ']
								"meta or control event"
				ifFalse: ["Transcript show: 'meta event'; cr."
					newEventList add: (sTime => event)]].
		ind := ind + 1].
	self code: #durations.
						"now remove noteOffs"
	self events: newEventList.
	self start: newEventList first key.
	self at: #events put: (events size).
	self changed!

tracks
	"Used on MIDIScores."

	^self event! !

!Siren.EventList methodsFor: 'printing'!

asExplorerString

	^self name!

asExplorerString: ignored

	^self name!

printOn: aStream
	"Print out the receiver's events on the argument."

	| anEvent count |
	aStream nextPutAll: '(EList ', (self name printString), ' (d: ', duration printString.
	events == nil
		ifTrue: [aStream nextPutAll: ', ', self size printString, ' events'].
	aStream nextPut: $).
"	self pitch == nil
		ifFalse: [aStream nextPutAll: ' pitch: ', self pitch printString].
"	(self at: #tempo) == nil
		ifFalse: [aStream nextPutAll: ' tempo: ', (self at: #tempo) printString].
	aStream cr.
	InputState default shiftDown
			ifFalse: [count := self size min: 20]
			ifTrue: [count := self size].
	1 to: count do: [ :ind |
		anEvent := events at: ind.
		aStream tab; nextPutAll: anEvent key printString, ' => '.
		anEvent value printOn: aStream].
	count = self size 
			ifTrue: [aStream nextPut: $); cr]
			ifFalse: [aStream nextPutAll: ' ... )'; cr]!

readDataFrom: aDataStream size: size
	"Read a new event list from the given stream using the compact format."

	| cntEvents time evt |
	super readDataFrom: aDataStream size: size.	"MusicEvent's compact format"
	cntEvents := (aDataStream byteStream nextNumber: 4) - EventList instSize.
	self events: (SortedCollection new: cntEvents).
	cntEvents timesRepeat:
		["aDataStream byteStream next == 192
			ifFalse: [self error: 'Missing event key in data stream']."
				"check key for events"
		time := MusicMagnitude readDataFrom: aDataStream.
		evt := aDataStream next.
		self add: evt at: time]!

storeDataOn: aDataStream
	"Store myself on a DataStream. Answer self."
	"Format is: inst header, 4 'normal' inst vars, properties as key/val (or nil)
	# of events, events as key/val."
	"Skip index and startedAt inst vars."

	| cntEvents ass |
	super storeDataOn: aDataStream.		"MusicEvent's compact format"
	cntEvents := events size.
	aDataStream byteStream nextNumber: 4 
			put: cntEvents + EventList instSize.
	1 to: cntEvents do:
		[ :i | 
		ass := events at: i.
		"aDataStream byteStream nextPut: 192."		"key for events"
		MusicMagnitude store: ass key on: aDataStream.
		aDataStream byteStream nextPut: 9.
		ass value storeDataOn: aDataStream]!

storeOn: aStream
	"Store source code for the receiver's events on the argument."
	"(((EventList named: #Bach) expandedFrom: 0 to: 12000) 
				name: #Bach3) storeOnFile: 'Bach3.el'"

	| count title1 title2 anAssoc tempo |
	tempo := self at: #tempo.
	(self pitch == nil and: [tempo == nil])
		ifFalse: [aStream nextPutAll: '((', self class name, ' named: ',
							self name storeString, ')'.
			self pitch == nil
					ifFalse: [aStream nextPutAll: '	pitch: ',
								self pitch printString].
			tempo == nil
					ifFalse: [self pitch == nil
							ifFalse: [aStream nextPut: $;].
				aStream nextPutAll: '	tempo: ', tempo printString].
			aStream nextPut: $); cr].
	properties == nil
		ifFalse: [properties associationsDo:
			[ :ass |
			ass key = #name
				ifFalse: [aStream nextPutAll: '(', self class name, ' named: ',
							self name storeString, ')'; tab.
						aStream nextPutAll: ass key, ': ',
							ass value storeString, '.'; cr]]].
	title1 := '(', self class name, ' named: ', self name storeString, ')
add: ('.
	title2 := '); 
add: ('.
	count := events size.
	aStream nextPutAll: title1.
	1 to: count do: [ :ind |
		(ind \\ 10) = 0
			ifTrue: [aStream nextPutAll: ')!!'; cr; nextPutAll: title1].
		anAssoc := events at: ind.
		aStream nextPutAll: anAssoc key asMS value printString, ' => '.
		anAssoc value storeOn: aStream.
		((ind \\ 10) = 9 or: [ind = count])
			ifFalse: [aStream nextPutAll: title2]].
	aStream nextPutAll: ')!!'; cr!

storeOnFile: aName
	"Store out the receiver's events on the file named by the argument."

	| fs |
	fs := (Filename named: aName) writeStream.
	self storeOn: fs.
	fs close.!

templateFields
	"Answer the field names for the instances of the receiver class."

	| coll |
	coll := super templateFields.
	coll remove: #index.
	coll remove: #startedAt.
	^coll! !

!Siren.EventList methodsFor: 'private'!

map: anAssociation
	"Map the receiver's special properties and/or eventModifiers onto the given event."

	| aTime anE |
	aTime := anAssociation key.
	anE := anAssociation value.
	self pitch == nil 			"if i'm transposed"
		ifFalse: [anE pitch: (Pitch value: (anE pitch + self pitch))].
	self loudness == nil 		"or scaled"
		ifFalse: [anE loudness: (anE loudness + self loudness)].
	(properties == nil) 		"or have props"
		ifFalse: [properties associationsDo: [ :ass |
			ass key == #tempo
				ifTrue: [aTime := aTime * ass value.
						anE duration: anE duration * ass value]
				ifFalse: [anE at: ass key put: ass value]]].
	^anAssociation!

map: anAssociation at: startTime
	"Map the receiver's special properties and/or eventModifiers onto the given event."

	| aTime anE |
	aTime := anAssociation key.
	anE := anAssociation value copy.
	(self at: #pitch) == nil 			"if i'm transposed"
		ifFalse: [anE pitch: (anE pitch + (self pitch))].
	(self at: #loudness) == nil 			"or scaled"
		ifFalse: [anE loudness: (anE loudness + (self at: #loudness))].
	(self at: #tempo) == nil 			"or have a tempo map"
		ifFalse: [aTime := aTime * ((self at: #tempo) at: aTime).
			anE duration: anE duration * ((self at: #tempo) at: anE duration)].
	^anE! !

!Siren.EventList class methodsFor: 'class initialization'!

flush
	"Erase the shared EventList dictionary and try to clean up all events."
	"EventList flush."

	SirenUtility flushAllEventLists.
	EventList allInstancesDo: [ :el | el release].
	ObjectMemory garbageCollect.
	Transcript cr; show: EventList instanceCount printString, ' event lists  	',
			MusicEvent instanceCount printString, ' events  	',
			Duration instanceCount printString, ' durations'; cr.!

initialize
	"Initialize the shared EventList dictionary."
	"EventList initialize."
	"EventList someInstance"

	SirenUtility classPool at: #EventLists put: Dictionary new.
!

loadDirectory: pattern
	"Load all event lists (.ev, .midi, and .gio files), from the given directory."
	"EventList loadDirectory: MusicMagnitude modeRootName, 'scores/'"

	| size |
	size := pattern size-1.
	(FileDirectory default filesMatching: pattern, '*.ev') do:
		[ :fName |
		Transcript show: (fName copyFrom: size to: fName size); cr.
		(FileStream fileNamed: fName) fileIn].
	(FileDirectory default filesMatching: pattern, '*.gio') do:
		[ :fName |
		Transcript show: (fName copyFrom: size to: fName size); cr.
		"EventLists at: (fName copyFrom: size to: fName size - 5) asSymbol
				put: (AdagioVoice parse: 
						(Filename named: fName) readStream) asEventList"].
	(FileDirectory default filesMatching: pattern, '*.midi') do:
		[ :fName |
		Transcript show: (fName copyFrom: size to: fName size); cr.
		"EventLists at: (fName copyFrom: size to: fName size - 5) asSymbol
				put: (MIDIFileVoice parse: 
						(Filename named: fName) readStream) asEventList"].!

storeAll
	"Write out all event lists."
	"EventList storeAll"

	| fname fstream |
	fname := Dialog request: 'File name for storing event lists'
			initialAnswer: 'events.st'.
	fname isEmpty ifTrue: [^self].
	fstream := (Filename named: fname) writeStream.
	fstream ifNil: [^self].
	Cursor write showWhile:
		[SirenUtility eventLists do:
			[ :list |
			list storeOn: fstream.
			fstream cr; cr]].
	fstream close.! !

!Siren.EventList class methodsFor: 'instance creation'!

basicNew: ignored
	"Create and answer a new default-named instance of me"

	^self basicNew!

named: myName fromPitches: pitchCollection
	"Answer an event list made with the given collection of pitches."
	"EventList named: (EventList nextName) fromPitches: #(36 38 40 43 45)"

	| me |
	me := self newNamed: (myName asSymbol).
	pitchCollection do:
		[ :item |
		me add: (me defaultEventClass pitch: item)
			at: 0].
	^me!

named: myName fromSelectors: selArray values: valueArrayArray
	"Answer an event list made with the given collection of values applied to the given array of selectors."
	"EventList named: 'phrase1'  
		fromSelectors: #(duration: loudness: phoneme:) 
		values: (Array with: #(595 545 545 540 570 800 540) 
				with: #(0.8 0.6 0.4 0.3 0.4 0.5 0.2) 
				with: #(dun kel kam mer ge sprae che))"

	| me size evt start selector data|
	me := self newNamed: (myName asSymbol).
	size := (valueArrayArray detect: [ :d | d hasItems]) size.
	1 to: size do:
		[ :evtIndex |
		evt := me defaultEventClass new.
		start := nil.
		1 to: (selArray size) do:
			[ :selIndex |
			selector := selArray at: selIndex.
			data := valueArrayArray at: selIndex.
			selector == #start: 
				ifTrue: [start := (Duration value: (data at: evtIndex))]
				ifFalse: [data hasItems
					ifTrue: [evt perform: selector with: (data at: evtIndex)]
					ifFalse: [evt perform: selector with: data]]].
		start == nil
			ifTrue: [me add: evt]
			ifFalse: [me add: evt at: start]].
	^me!

named: myName fromStartTimes: timeCollection
	"Answer an event list with the given collection of relative start times."
	"EventList named: (EventList nextName) fromStartTimes: #(0 500 1000 1500 2000 2500 3000)"

	| me |
	me := self newNamed: (myName asSymbol).
	timeCollection do:
		[ :item |
		me add: (me defaultEventClass new)
			at: item].
	^me!

new
	"Create and answer a new default-named instance of me"

	^self newAnonymous!

new: ignored
	"Create and answer a new default-named instance of me"

	^self newAnonymous!

newAnonymous
	"Create and answer a new default un-named (and therefore un-persistent) instance of me"

	^super new initializeAnonymous! !

!Siren.EventList class methodsFor: 'named constants'!

named: aName
	"Answer the named instance from the dictionary or a new EL."
	"EventList named: #random1"

	| el |
	el := SirenUtility eventListAt: aName asSymbol.
	^el isNil
		ifTrue: [self newNamed: aName]
		ifFalse: [el]!

named: aName ifAbsent: theBlock
	"Answer the named instance from the dictionary or the result of the block."

	| el |
	el := SirenUtility eventListAt: aName asSymbol.
	^el isNil
		ifTrue: [theBlock value]
		ifFalse: [el]!

namedLists
	"Answer the dictionary of named event lists."
	"EventList namedLists"

	^SirenUtility eventLists!

newNamed: aName
	"Create and answer a new named (and stored) instance."

	^super new initializeNamed: aName!

nextName
	"Answer a gensym-type of event list name for which no EL exists in the dictionary."

	| temp |
	temp := 1.
	[(SirenUtility eventLists at: #Temp) includesKey: (('te', temp printString) asSymbol)]
			whileTrue: [temp := temp + 1].
	^'te', temp printString asSymbol! !

!Siren.EventList class methodsFor: 'examples'!

creationExamples
	"Select the expressions below one-at-a-time and inspect the results."

"	EventList named: (EventList nextName) 
		fromPitches: #(36 38 40 43 45).
"
"	(EventList named: (EventList nextName) 
		fromStartTimes: #(0 500 1000 1500 2000 2500 3000)) inspect.
"
	((440 Hz, 250 msec, (#voice -> #flute)), 
		((1/4) => (471 Hz, (1/4) beat, (#voice -> #flute)))) inspect!

randomExample
	"Create an event list with random data."
	"EventList randomExample"
	"EventList randomExample edit"
	"PitchTimeView randomExample"
	"(EventList newNamed: #pRand)
			addAll: (EventList randomExample);
			addAll: (EventList randomExample)"

	| rand size arr1 arr2 arr3 arr4 |
	rand := Random new.
	size := 64.
	arr1 := Array new: size.
	arr2 := Array new: size.
	arr3 := Array new: size.
	arr4 := Array new: size.
	1 to: size do: [ :i | arr1 at: i put: (rand nextIntFrom: 150 to: 300)].		"dur"
	1 to: size do: [ :i | arr2 at: i put: (rand nextIntFrom: 40 to: 100)].		"amp"
	1 to: size do: [ :i | arr3 at: i put: (rand nextIntFrom: 1 to: 1)].			"voice"
	1 to: size do: [ :i | arr4 at: i put: (rand nextIntFrom: 36 to: 60)].		"pitch"
	^EventList named: 'random1'  
		fromSelectors: #(duration: loudness: voice: pitch:) 
		values: (Array with: arr1 with: arr2 with: arr3 with: arr4)!

randomExample: length
	"Create an event list with random data."
	"EventList randomExample: 200"
	"PitchTimeView randomExample"
	"(EventList randomExample: 40) playOn: MIDIVoice default"

	| rand arr1 arr2 arr3 arr4 |
	rand := Random new.
	arr1 := Array new: length.
	arr2 := Array new: length.
	arr3 := Array new: length.
	arr4 := Array new: length.
	1 to: length do: [ :i | arr1 at: i put: (rand nextIntFrom: 200 to: 300)].
	1 to: length do: [ :i | arr2 at: i put: (rand nextIntFrom: 32 to: 80)].
	1 to: length do: [ :i | arr3 at: i put: (rand nextIntFrom: 1 to: 1)].
	1 to: length do: [ :i | arr4 at: i put: (rand nextIntFrom: 60 to: 72)].
	^EventList named: 'random1'  
		fromSelectors: #(duration: loudness: voice: pitch:) 
		values: (Array with: arr1 with: arr2 with: arr3 with: arr4)!

randomExample: length from: data
	"Create an event list with random data."
	"(EventList randomExample: 64 
		from: ((#duration: -> (100 to: 200)), (#pitch: -> (36 to: 60)),
				(#ampl: -> (48 to: 64)), (#voice: -> (1 to: 4)))) edit"

	| rand arr sels i a |
	rand := Random new.
	arr := Array new: data size.
	sels := Array new: data size.
	i := 1.
	data associationsDo: [ :item | 
		a := Array new: length.
		(item value isMemberOf: Interval)
			ifTrue: [item value first isInteger
				ifTrue: [1 to: length do: [ :c | 
					a at: c put: (rand nextIntFrom: item value first to: item value last)]]
				ifFalse: [1 to: length do: [ :c | 
					a at: c put: (rand nextFrom: item value first to: item value last)]]]
			ifFalse: [1 to: length do: [ :c | 
				a at: c put: (item value atRandom: rand)]].
		arr at: i put: a.
		sels at: i put: item key.
		i := i + 1].
	^EventList named: EventList nextName
		fromSelectors: sels 
		values: arr!

randomSWSSExample
	"Create an event list with random data appropriate for software sound synthesis."
	"EventList randomSWSSExample edit"
	"(EventList newNamed: #sRand)
			addAll: (EventList randomSWSSExample);
			addAll: (EventList randomSWSSExample)"

	| rand size params data |
	rand := Random new.
	size := 128.
	params := 9.
	data := Array new: params.
	1 to: params do:
		[ :i | data at: i put: (Array new: size)].
	1 to: size do: 
		[ :i | (data at: 1) at: i put: (rand nextIntFrom: 200 to: 450)].	"dur"
	1 to: size do: 
		[ :i | (data at: 2) at: i put: (rand nextFrom: 0.3 to: 0.7)].		"ampl"
	1 to: size do: 
		[ :i | (data at: 3) at: i put: (rand nextFrom: 60 to: 500)].		"pitch"
	1 to: size do: 
		[ :i | (data at: 4) at: i put: (rand nextIntFrom: 1.0 to: 5.0)].	"ratio"
	1 to: size do: 
		[ :i | (data at: 5) at: i put: (rand nextFrom: 1.0 to: 8.0)].		"index"
	1 to: size do: 
		[ :i | (data at: 6) at: i put: (rand nextFrom: 0.05 to: 0.1)].	"attack"
	1 to: size do: 
		[ :i | (data at: 7) at: i put: (rand nextFrom: 0.05 to: 0.1)].	"decay"
	1 to: size do: 
		[ :i | (data at: 8) at: i put: (rand nextFrom: 0.01 to: 0.1)].	"i:=attack"
	1 to: size do: 
		[ :i | (data at: 9) at: i put: (rand nextFrom: 0.05 to: 0.1)].	"i:=decay"
	^EventList named: #SWSSExample
		fromSelectors: #(duration: loudness: pitch: cmratio: index: 
					attack: decay: iAttack: iDecay:) 
		values: data!

randomSWSSExample2
	"Create an event list with random data appropriate for software sound synthesis."
	"EventList randomSWSSExample2 edit"
	"((EventList newNamed: #sRand)
			addAll: (EventList randomSWSSExample2);
			addAll: (EventList randomSWSSExample2)) edit"

	| rand size params data |
	rand := Random new.
	size := 64.
	params := 6.
	data := Array new: params.
	1 to: params do:
		[ :i | data at: i put: (Array new: size)].
	1 to: size do: 
		[ :i | (data at: 1) at: i put: (rand nextIntFrom: 300 to: 750)].	"dur"
	1 to: size do: 
		[ :i | (data at: 2) at: i put: (rand nextFrom: 0.35 to: 1.0)].		"ampl"
	1 to: size do: 
		[ :i | (data at: 3) at: i 
					put: ((rand nextIntFrom: 36 to: 80) truncateTo: 2)].	"pitch"
	1 to: size do: 
		[ :i | (data at: 4) at: i put: (LinearFunction exampleEnvelope)].	"envelope"
	1 to: size do: 
		[ :i | (data at: 5) at: i put: (rand nextFrom: 0.0 to: 1.0)].		"position"
	1 to: size do: 
		[ :i | (data at: 6) at: i put: (rand nextFrom: 0.3 to: 1.0)].		"duty"
	^EventList named: #SWSSExample
		fromSelectors: #(duration: loudness: pitch: envelope: position: duty:) 
		values: data!

scaleExample2
	"Answer a scale where the event property types are mixed."
	"EventList scaleExample2"

	| el start stop size arr4 dur ndur |
	start := 48.
	stop := 60.
	size := stop - start.
	dur := 2000.	"msec"
	ndur := (dur / size) truncated.
	arr4 := Array new: size + 1.
	0 to: size do: [ :i | arr4 at: i+1 put: (start + i)].
	el := EventList named: EventList nextName
		fromSelectors: #(duration: loudness: pitch:) 
		values: (Array with: ndur with: 72 with: arr4).
"Scramble the property types"
	el eventsDo:
		[ :ev |
		ev dur: (ev dur perform: (#(asMsec asBeat asSeconds) atRandom)).
		ev pitch: (ev pitch perform: (#(asHz asMIDI asSymbol) atRandom)).
		ev ampl: (ev ampl perform: (#(asDB asMIDI asSymbol asRatio) atRandom))].
	^el!

scaleExampleFrom: start to: stop in: dur
	"Answer an event list with a scale."
	"EventList scaleExampleFrom: 48 to: 60 in: 1500"
	"(EventList scaleExampleFrom: 48 to: 84 in: 1500) play"
	"MIDIDevice MIDIPlayExample2"

	| size arr4 ndur |
	size := (stop - start) abs.
	ndur := (dur / size) truncated "+ 1".
	arr4 := Array new: size + 1.
	start > stop
		ifTrue: [0 to: size do: [ :i | arr4 at: i+1 put: (start - i)]]
		ifFalse: [0 to: size do: [ :i | arr4 at: i+1 put: (start + i)]].
	^EventList named: EventList nextName  
		fromSelectors: #(duration: loudness: voice: pitch:) 
		values: (Array with: ndur with: 72 with: 1 with: arr4)!

sentenceExample
	"Create an event list for a beautiful sentence."
	"EventList sentenceExample"
	EventList named: 'phrase2'  
		fromSelectors: #(duration: loudness: phoneme:) 
		values: (Array with: #(420 420 720 354    408 420 615 570 
					369 312 369    690 462 477 660 ) 
				with: #(0.4 0.3 0.9 0.6    0.42 0.32 0.8 0.5 
					0.44 0.52 0.2   0.94 0.62 0.46 0.34 ) 
				with: #(#und #die #fra #gen    #sind #die #sae #tze 
					#die #ich #nicht    #aus #spre #chen #kann)).
	^EventList named: 'phrase2'!

wordExample
	"Create an event list for a long word."
	"EventList wordExample"

	EventList named: 'phrase1'  
		fromSelectors: #(duration: loudness: phoneme:) 
		values: (Array with: #(595 545 545 540 570 800 540) 
				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! !

!Siren.EventGenerator methodsFor: 'playing'!

edit
	"edit me"

	self eventList edit!

eventList
	"Answer an eventList for the receiver."

	self subclassResponsibility!

play
	"Play the receiver's event list."

	self eventList play! !

!Siren.EventGenerator class methodsFor: 'accessing instances'!

all
	"Find the and return instances of me or any subclasses of me."

	| coll |
	coll := Set new.
	self allSubInstancesDo: [ :anI | coll add: anI ].
	^coll!

allPlaying
	"Find all playing instances of me or my subclasses."

	| coll |
	coll := Set new.
	self allSubInstancesDo: [ :anI | anI playing ifTrue: [coll add: anI]].
	^coll!

named: aName 
	"Find the named instance of me or a subclass of me."

	^SirenUtility eventLists at: aName
		ifAbsent: [SirenUtility events at: aName ifAbsent: [nil]]!

stopAll
	"Find all playing instances of me or my subclasses and terminate them."

	self allSubInstancesDo: [ :anI | anI playing ifTrue: [anI stop]].! !

!Siren.EventGenerator class methodsFor: 'examples'!

randomExample
	"Play random examples from the set of examples in my subclasses"
	"[Sensor anyButtonPressed] whileFalse: [EventGenerator randomExample]"

	((Smalltalk at: 
			(#(Cloud DynamicCloud SelectionCloud DynamicSelectionCloud "Peal") 
					atRandom)) 
		randomExample) play! !

!Siren.Ostinato methodsFor: 'initializing'!

initializeAnonymous
	"set up a new Ostinato."

	super initializeAnonymous.
	playing := false.!

initializeNamed: aName
	"Set up a new Ostinato."

	super initializeNamed: aName.
	playing := false.! !

!Siren.Ostinato methodsFor: 'accessing'!

events
	^list events!

list
	^list!

list: anEL
	list := anEL!

process: anBlockOrProc
	process := anBlockOrProc! !

!Siren.Ostinato methodsFor: 'playing'!

play: numberOfTimes
	"return an event list from the given number of times playing"

	| time pattern |
	time := 0 msec.
	pattern := EventList newAnonymous.
	numberOfTimes timesRepeat:
		[pattern add: list at: time.
		time := list duration + time].
	pattern play!

playAt: firstTime
	"Play an ostinato until turned off."

	| startTime realTime | 
	startTime := firstTime.
	realTime := Time millisecondClockValue.
	playing := true.
	voice := list voice == nil 
		ifFalse: [list voice] 
		ifTrue: [IOVoice default].
	process := [[playing] whileTrue: 
			[voice play: list at: startTime.
			startTime := startTime + list duration.
			(Delay untilMilliseconds: realTime + startTime - 500) wait.
		Transcript show: 'x '. ]] fork"At: 5"!

playOn: aVoice at: firstTime
	"play an ostinato until turned off."

	| startTime | 
	startTime := firstTime.
	playing := true.
	process := [[playing] whileTrue: 
			[list playOn: aVoice at: startTime.
			startTime := startTime + list duration.
			(Delay untilMilliseconds: startTime - 500) wait]] forkAt: 5!

stop
	"stop a playing Ostinato."
		
	playing := false.
"	self list stop"! !

!Siren.Ostinato methodsFor: 'printing'!

printOn: aStream

	self name isNil ifFalse: [
		aStream nextPutAll: 'Ostinato named: ', self name asString, ' on EventList: (']
	ifTrue: [
		aStream nextPutAll: 'Ostinato on EventList: ('].
	aStream cr.
	list printOn: aStream.
	aStream nextPutAll: ') playing= ', playing printString; cr! !

!Siren.Ostinato class methodsFor: 'instance creation'!

named: aSymbol onList: anEL 
	"create an Ostinato on the given EventList."

	| me |
	me := self new initializeNamed: aSymbol.
	me list: anEL.
	^me!

named: aSymbol onList: anEL dur: theDur
	"create an Ostinato on the given EventList."

	| me |
	me := self new initializeNamed: aSymbol.
	me list: anEL.
	me dur: theDur.
	^me!

newNamed: aSymbol onList: anEL 
	"create an Ostinato on the given EventList."

	| me |
	me := self new initializeNamed: aSymbol.
	me list: anEL.
	^me!

onList: anEL 
	"create an Ostinato on the given EventList."

	| me |
	me := self new initializeAnonymous.
	me list: anEL.
	me events: anEL events.
	^me!

onList: anEL dur: theDur
	"create an Ostinato on the given EventList."

	| me |
	me := self new initializeAnonymous.
	me list: anEL.
	me dur: theDur.
	^me! !

!Siren.Ostinato class methodsFor: 'examples'!

ostinatoExamples
	"Create and edit some ostinati."
	"NOT WORKING"

	"Ostinato onList: (EventList named: #scale1)"
	"Ostinato onList: (EventList named: #scale1) edit"
	"Ostinato newNamed: #scaleO onList: (EventList named: #scale1)"
	"(Ostinato named: #scaleO) play"
	"(Ostinato named: #scaleO) playOn: 1 at: 0"
	"(Ostinato named: #scaleO) stop"! !

!Siren.Cloud methodsFor: 'accessing'!

density: theDensity
	"set the density instance variable"

	density := theDensity! !

!Siren.Cloud methodsFor: 'playing'!

eventList
	"Make the cloud's events; assume that the receiver's pitch, loudness and 
	durations are actually intervals"

	| pattern number length start |
	pattern := EventList new.
	number := duration asSeconds value asFloat * density.
	length := (1000 / density) rounded.
	start := 0.
	number truncated timesRepeat:
		[pattern add: (MusicEvent 
				dur: (length * 2) 	"legato"
				pitch: self pitch value atRandom
				ampl: self loudness value atRandom
				voice: ((self voice == nil 
						ifTrue: [nil] 
						ifFalse: [self voice atRandom])))
			at: start.
		start := start + length].
	^pattern!

eventList0
	"Make the cloud's events; assume that the receiver's pitch, loudness and 
	durations are actually intervals"

	| pattern rand number length start |
	pattern := EventList new.
	rand := Random new.
	number := duration asSeconds value asFloat * density.
	length := (1000 / density) rounded.
	start := 0.
	number truncated timesRepeat:
		[pattern add: (MusicEvent 
			dur: (length * 2 "+ (2000 * rand next) rounded")
			pitch: ((self pitch value first) + 
					((rand next * (self pitch value last - self pitch value first)) rounded))
			ampl: ((self loudness value first) + 
					((rand next * (self loudness value last - self loudness value first)) rounded))
			voice: ((self voice == nil 
						ifTrue: [nil] 
						ifFalse: [(self voice first) + 
					((rand next * (self voice last - self voice first)) rounded)])))
			at: start.
		start := start + length].
	^pattern! !

!Siren.Cloud class methodsFor: 'instance creation'!

dur: aD pitch: aP ampl: anA
	"return an initialized Cloud"

	^self new duration: aD; 
		pitch: aP; 
		loudness: anA!

dur: aD pitch: aP ampl: anA voice: aVoice
	"return an initialized Cloud"

	^self new duration: aD; 
		pitch: aP; 
		loudness: anA; 
		voice: aVoice!

dur: aD pitch: aP ampl: anA voice: aVoice density: aDe
	"return an initialized Cloud"

	^self new duration: aD; 
		pitch: aP; 
		loudness: anA; 
		voice: aVoice; 
		density: aDe!

dur: aD voice: aVoice density: aDe
	"return an initialized Cloud"

	^self new duration: aD; 
		voice: aVoice; 
		density: aDe!

fromDictionary: aDictionary
	"Answer an initialized Cloud generated from the given property map dictionary."

	"(Cloud fromDictionary: ((#duration -> 5000), 
			(#pitch -> (48 to: 74)),
			(#loudness -> (50 to: 100)), 
			(#voice -> (1 to: 4)), 
			(#density -> 25))) eventList edit"


	| me |
	me := self new.
	aDictionary associationsDo: 
		[ :ass |
		me perform: ((ass key, ':') asSymbol) with: ass value].
	^me! !

!Siren.Cloud class methodsFor: 'examples'!

dictionaryExample
	"Answer an initialized Cloud generated from the given property map dictionary."
	"Cloud dictionaryExample"

	(Cloud fromDictionary: ((#duration -> 5000), 
			(#pitch -> (48 to: 70)),
			(#loudness -> (50 to: 100)), 
			(#voice -> (1 to: 4)), 
			(#density -> 15))) eventList edit!

example1
	"Create and edit a low 6 second stochastic cloud with 5 events per second"
	"Cloud example1"

	| c |
	c := (Cloud  dur: 6
		pitch: (48 to: 60)  
		ampl: (80 to: 120) 
		voice: (1 to: 4)
		density: 5) eventList.
"	c edit "
	c play
"	c inspect"!

randomExample
	"Play a stochastic cloud with random properties."
	"Cloud randomExample"

	| rand pBase score |
	rand := Random new.
	pBase := 20 + (rand next * 60) rounded.
	score := (Cloud  dur: (2 + (rand next * 6) asInteger)
		pitch: (pBase to: (pBase + (rand next * 40)) rounded)  
		ampl: (80 to: 120) 
		voice: (1 to: 8)
		density: (rand next * 20)) eventList.
	score play.
	(Delay forSeconds: 5) wait.
	MIDIPort allNotesOff
"	score inspect
	score edit
"!

randomExample2
	"Play a stochastic cloud with random properties."
	"Cloud randomExample2"

	| rand pBase score |
	rand := Random new.
	pBase := 20 + (rand next * 60) rounded.
	score := (Cloud  dur: (3 + (rand next * 6))
		pitch: (pBase to: (pBase + (rand next * 40)) rounded)  
		ampl: (80 to: 120) 
		voice: (1 to: 8)
		density: (rand next * 20)) eventList.
	score play.
	MIDIPort allNotesOff
"	score inspect
	score edit
"! !

!Siren.DynamicCloud methodsFor: 'playing'!

eventList
	"make the cloud's events--assume that the receiver's pitch, loudness and durations are actually ARRAYS of two intervals (for the start and end ranges)"

	| pattern rand number length start percent pRange p1 p2 p3 p4 
		amplRange a1 a2 a3 a4 v1 v2 v3 v4 vRange |
	pattern := EventList new.
	rand := Random new.
	number := duration asSeconds value asFloat * density.
	length := (1000 / density) rounded.
	start := 0.
	(self pitch value isMemberOf: Array)
		ifTrue: [pRange := self pitch value first.
			p1 := self pitch value first first.
			p2 := self pitch value first last.
			p3 := self pitch value last first - p1.
			p4 := self pitch value last last - p2]
		ifFalse: [pRange := self pitch value].
	(self loudness value isMemberOf: Array)
		ifTrue: [amplRange := self loudness value first.
			a1 := self loudness value first first.
			a2 := self loudness value first last.
			a3 := self loudness value last first - a1.
			a4 := self loudness value last last - a2]
		ifFalse: [amplRange := self loudness value].
	(self voice isMemberOf: Array)
		ifTrue: [vRange := self voice first.
			v1 := self voice first first.
			v2 := self voice first last.
			v3 := self voice last first - v1.
			v4 := self voice last last - v2]
		ifFalse: [vRange := self voice].
	1 to: number truncated do:
		[ :count |
		pattern add: (MusicEvent 
			dur: (length * 2 + (length * rand next) rounded)
			pitch: ((pRange first rounded) + 
					((rand next * (pRange last - pRange first)) rounded))
			ampl: ((amplRange first rounded) + 
					((rand next * (amplRange last - amplRange first)) rounded))
			voice: (self voice == nil 
						ifTrue: [nil] 
						ifFalse: [(vRange first rounded) + 
					((rand next * (vRange last - vRange first)) rounded)]))
			at: start.
		start := start + length.
	percent := count / number.
	(self pitch value isMemberOf: Array)
		ifTrue: [pRange := p1 + (p3 * percent) to: p2 + (p4 * percent)].
	(self loudness value isMemberOf: Array)
		ifTrue: [amplRange := a1 + (a3 * percent) to: a2 + (a4 * percent)].
	(self voice isMemberOf: Array)
		ifTrue: [vRange := v1 + (v3 * percent) to: v2 + (v4 * percent)]].
	^pattern! !

!Siren.DynamicCloud class methodsFor: 'examples'!

crescendoExample
	"Create and play an 8-second cloud that goes from soft to loud on the same pitch."
	"DynamicCloud crescendoExample"

	(DynamicCloud  dur: 6  
		pitch: (40 to: 40)
		ampl: #((20 to: 30) (80 to: 100))
		voice: (1 to: 1)
		density: 20) eventList 
	"edit" 
	play.  MIDIPort allNotesOffIn: 8
	"inspect"!

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

	(DynamicCloud  dur: 6  
		pitch: #((30 to: 44) (50 to: 50))
		ampl: #((20 to: 40) (90 to: 120))
		voice: (1 to: 4)
		density: 20) eventList "edit" play "inspect"!

example4
	"Edit a 6 second cloud that focuses on its center"
	"DynamicCloud example4"

	(DynamicCloud  dur: 6  
		pitch: #((30 to: 64) (40 to: 40))
		ampl: #((20 to: 40) (90 to: 120))
		voice: (1 to: 8)
		density: 10) eventList edit!

focusExample
	"Create a 6-second cloud that focuses on its center."
	"DynamicCloud focusExample"

	(DynamicCloud  dur: 6  
		pitch: #((60 to: 84) (68 to: 68))
		ampl: #((20 to: 20) (90 to: 100))
		voice: (1 to: 8)
		density: 10) eventList edit "play" "inspect"!

randomExample
	"Create a dynamic second cloud with random properties."
	"DynamicCloud randomExample edit"

	| rand |
	rand := Random new.
	^DynamicCloud  dur: (rand nextIntFrom: 3 to: 8)  
		pitch: (Array with: ((rand nextIntFrom: 24 to: 48) 
					to: (rand nextIntFrom: 24 to: 54))
				with: ((rand nextIntFrom: 48 to: 60)
					to: (rand nextIntFrom: 60 to: 72)))
		ampl: (Array with: ((rand nextIntFrom: 24 to: 36)
					to: (rand nextIntFrom: 36 to: 48))
				with: ((rand nextIntFrom: 72 to: 88)
					to: (rand nextIntFrom: 80 to: 112)))
		voice: (1 to: 4)
		density: (rand nextIntFrom: 4 to: 16)! !

!Siren.DisplayListController methodsFor: 'initialize-release'!

initializeMenu
	"Set up the receiver's YellowButtonMenu"

"	| menu |
	menu := Menu new.
	menu addItem:  ((MenuItem labeled: 'group') value: #group).
	menu addItem:  ((MenuItem labeled: 'inspect') value: #inspectModel).
	menu addItem:  ((MenuItem labeled: 'redraw') value: #redraw).
	menu addItem:  ((MenuItem labeled: 'zoom') value: #zoom).
	menuHolder := menu asValue
"
	self menuHolder: self class defaultEditMenu asValue! !

!Siren.DisplayListController methodsFor: 'accessing'!

selection
	"Answer the receiver's 'selection'."
self halt.
	^selection!

selection: anObject
	"Set the receiver's instance variable 'selection' to be anObject."
self halt.
	selection := anObject!

selectionTracker
	^DisplayListTracker on: self! !

!Siren.DisplayListController methodsFor: 'menu messages'!

again
	"Repeat the last operation"!

copyItem
	"Copy the selection."

	!

cutItem
	"Cut the selection."!

doInspect
	"Inspect the view (if leftShiftDown) or the model."

	sensor shiftDown
		ifTrue: [self inspect]
		ifFalse: [model inspect]!

flattenItems
	"Flatten the hierarchy of the selection."!

groupItems
	"Group the selection."

	self halt!

inspectDisplayList
	"Inspect the display list."

	view displayList inspect!

inspectEditor
	"Inspect the display list editor."

	model inspect!

inspectModel
	"Inspect the model."

	model list inspect!

optionsDialog
	"Run the options dialog box."!

pasteFromBuffer
	"Paste the selection."!

pasteItem
	"Paste the selection."!

redraw
	"Redraw the view and clear the selection."

	view invalidate.
	model == nil
		ifFalse: [model select: nil]!

redrawView
	"redraw the display list."

	view invalidate!

undo
	"Un-do the last operation"!

ungroupItems
	"Un-group the selection."!

zoom
	"Prompt the user for a scale point, zoom the view's scale, and redraw."

self halt.
"	| pt prompt |
	prompt := (view xScale == nil)
		ifTrue: ['1@1']
		ifFalse: [(view xScale @ view yScale) printString].
	pt := DialogView request: 'zoom to scale:'
			initialAnswer: prompt.
	pt = '' ifFalse: [pt := Compiler evaluate: pt.
			pt class == Point
				ifTrue: [view xScale: pt x; yScale: pt y; redraw]]
"!

zoomInPoint
	"Zoom the view's scale, and redraw."

	view xScale: 1.0; yScale: 1.0; redraw!

zoomOutPoint
	"Zoom the view's scale, and redraw."

	view xScale: 1.0; yScale: 1.0; redraw!

zoomTo1
	"Zoom the view's scale, and redraw."

	view xScale: 1.0; yScale: 1.0; redraw! !

!Siren.DisplayListController methodsFor: 'selecting'!

selectAtPoint: aPoint
	"Hit-detect the model for the given pt."

	| item transPt |
	transPt := view transformPoint: aPoint.
	item := view list itemWithin: 8 of: transPt.
"	Transcript show: aPoint printString, '(', transPt rounded printString, ') -- sel: ', item printString; cr.
"	model select: item.
	item == nil
		ifTrue: [selOffset := nil]
		ifFalse: [selOffset := aPoint - item bounds origin].
	^item!

selectFromPoint: aPoint toPoint: anotherPoint
	"Hit-detect the model for the given range."

	| item |
	(aPoint dist: anotherPoint) < 8
		ifTrue: [item := view list itemWithin: 24 of: aPoint.
			Transcript show: 'Select ', item printString; cr.
			item == nil
				ifTrue: [model select: item]
				ifFalse: [model select: item]]
		ifFalse: [model select:
				(view list itemsWithin: 
						(aPoint corner: anotherPoint))]!

selectRangeWhile: durationBlock
	"Hit-detect items from the display list or drag a selection as long as the block is true."

	| pt1 pt2 gc color1 color2 |
	gc := view graphicsContext.
	color1 := LookPreferences defaultForWindows backgroundColor.
	color2 := LookPreferences defaultForWindows foregroundColor.
	pt1 := sensor cursorPoint.
	pt2 := pt1 + (4@4).
	sensor cursorPoint: pt2.
	durationBlock whileTrue:
		[gc paint: color1.
		gc displayRectangularBorder: (pt1 corner: pt2).
		pt2 := sensor cursorPoint.
		gc paint: color2.
		gc displayRectangularBorder: (pt1 corner: pt2)]! !

!Siren.DisplayListController methodsFor: 'control defaults'!

blueButtonActivity
	"Drag scroll amplified by 2@2 with the blue button."

	| pt1 pt2 |
	pt1 := sensor cursorPoint.
	[sensor anyButtonPressed] whileTrue:
		[pt2 := sensor cursorPoint.
		(pt2 = pt1) 
			ifFalse: [view scroll: (pt2 - pt1) * 2.
"				Transcript show: (pt2 - pt1) printString; cr."
				pt1 := pt2]]!

redButtonActivity
	"Hit-detect items from the display list or drag a selection."

	| pt1 pt2 selRect | self halt.
	model == nil ifTrue: [^nil].
	((model selection == nil) or: [sensor shiftDown])
		ifTrue: [pt1 := sensor cursorPoint.
			self selectRangeWhile: [sensor anyButtonPressed].
			pt2 := sensor cursorPoint.
"			Transcript show: 'Select: ', pt1 printString, ' to ', pt2 printString; cr."
			self selectFromPoint: pt1 - view pageOffset - view class inset
					toPoint: pt2 - view pageOffset - view class inset]
		ifFalse: [selRect := model selection bounds.
			pt1 := sensor cursorPoint - view pageOffset.
			(selRect containsPoint: pt1 r)	"if you click off the selection, clear it"
				ifFalse: [model select: nil.
					Transcript show: 'clear'; cr.
					sensor waitNoButton]
											"if you click near the selection, drag it"
				ifTrue: [self dragSelectionWhile: [sensor anyButtonPressed].
					model moveSelectionTo: sensor cursorPoint]]!

redButtonPressedAt: pt
	"Handle the red mouse click"

	| item |
self halt.
	item := self selectAtPoint: pt.!

yellowButtonActivity0
	"Drag scroll amplified by 2@2 with the blue button."

	| pt1 pt2 |
	pt1 := self sensor cursorPoint.
	[self sensor anyButtonPressed] whileTrue:
		[pt2 := sensor cursorPoint.
		(pt2 = pt1) 
			ifFalse: [view scrollBy: (pt2 - pt1) * 2.
"				Transcript show: (pt2 - pt1) printString; cr."
				pt1 := self sensor cursorPoint]]! !

!Siren.DisplayListController methodsFor: 'events'!

dragSelection: anEvent
	"Dragging is event oriented."

	model moveSelectionTo: (self sensor mousePointForEvent: anEvent) - selOffset!

mouseMovedEvent: anEvent
	"Mouse dragging"

	anEvent redButtonPressed ifFalse: [^self].
	model selection == nil
		ifTrue: [self selectEvent: anEvent]
		ifFalse: [self dragSelection: anEvent]!

redButtonPressedEvent: event 

	self selectEvent: event.!

redButtonReleasedEvent: event 

	selOffset := nil!

selectEvent: anEvent
	| point |
	point := self sensor mousePointForEvent: anEvent.
	self selectAtPoint: point! !

!Siren.TimeSequenceController methodsFor: 'initialize-release'!

initializeMenu
	"Set up the receiver's YellowButtonMenu"

	super initializeMenu
"	self menuHolder: (ValueHolder with: (PopUpMenu
			labelList: #(('group' 'inspect') ('play' 'pan') ('redraw') ('store' 'read'))
			values: #(group doInspect play pan redraw storeList readList)))"! !

!Siren.TimeSequenceController methodsFor: 'menu messages'!

doInspect
	"Inspect the editor (if leftShiftDown) or the list."

	sensor shiftDown
		ifTrue: [model inspect]
		ifFalse: [model eventList inspect]!

group
	"Group the current selection as a separate sub-event list in the model."

	model selection == nil
		ifTrue: [view flash.
			^self].
	model group: model selection.
	model select: nil!

pan
	"Pan the view in x highlighting the time value until mouse press."

	| time0 startTime now |
	sensor waitNoButton.
	time0 := Time millisecondClockValue.
	startTime := view offsetX * view xScale.
	now := startTime.
	[sensor anyButtonPressed or: [now > model list duration value]]
		whileFalse: [now := startTime + Time millisecondClockValue - time0.
			view highlightTime: now.
"			Transcript show: now printString; cr."
"			(Delay forMilliseconds: 100) wait."
			view highlightTime: now].
	sensor waitNoButton!

play
	"Play the list."

	model eventList play.
	MIDIPort allNotesOffIn: (model eventList duration asSec value + 3)!

readList
	"Copy the model list from the global dictionary."

	| name |
	name := Dialog request: 'EventList name for storing model:'
			initialAnswer: model list name.
	name = '' ifTrue: [^self].
	(SirenUtility eventLists includesKey: name)
		ifTrue: [model eventList: (SirenUtility eventLists at: name) copy]
		ifFalse: [Transcript cr; show: 'No such event list.'; cr.]!

storeList
	"Copy the model list into the global dictionary."

	| name count |
	name := Dialog request: 'EventList name for storing model:'
			initialAnswer: model list name.
	name = '' ifTrue: [^self].
	(SirenUtility eventLists includesKey: name)
		ifTrue: [(Dialog confirm: 'Store over existing event list?')
			ifTrue: [SirenUtility eventLists at: name put: model list copy]
			ifFalse: [count := 2.
				[SirenUtility eventLists includesKey: (name, '.', count printString)]
					whileTrue: [count := count + 1].
				SirenUtility eventLists at: (name, '.', count printString) asSymbol
						put: (SirenUtility eventLists at: name).
				SirenUtility eventLists at: name put: model list copy]]
		ifFalse: [SirenUtility eventLists at: name put: model list copy]! !

!Siren.TimeSequenceController methodsFor: 'selecting'!

selectFromPoint: aPoint toPoint: anotherPoint
	"Hit-detect the model for the given range."

	| collection eList |
	collection := view list itemsFromX: aPoint x toX: anotherPoint x.
	eList := EventList newAnonymous.
	collection do:
		[ :i | 
		i model == nil
			ifFalse: [eList add: i model]].
	model select: eList.
	selection := collection.
	view invalidate! !

!Siren.PitchTimeController methodsFor: 'selecting'!

selectFromPoint: aPoint toPoint: anotherPoint
	"Hit-detect the model for the given range."

	| collection eList |
	collection := view list itemsWithin: (aPoint corner: anotherPoint).
	collection == nil ifTrue: [^self].
	eList := EventList newAnonymous.
	collection do:
		[ :i | 
		i model == nil
			ifFalse: [eList add: i model]].
	model select: eList.
	selection := collection.
	view invalidate! !

!Siren.Function methodsFor: 'initialize-release'!

initialize: size
	"Initialize the receiver for the given size."

	data := OrderedCollection new: size.
"	data := Array new: size withAll: 0."
	range := 0.0 to: 0.0.
	domain := 0.0 to: 0.0! !

!Siren.Function methodsFor: 'accessing'!

add: aValue
	"Add the argument to the receiver's point collection."

	data add: aValue.
	aValue < range first
		ifTrue: [self range: (aValue to: range last)].
	aValue > range stop
		ifTrue: [self range: (range first to: aValue)].!

at: anIndex 
	"Answer the value at the given index (between 0 and 1 -or- 1 and data size)."

	anIndex isSymbol
		ifTrue: [^super at: anIndex].
	anIndex isInteger
		ifTrue: [^data at: ((anIndex max: 1) min: data size)].
	^ data at: ((self size * anIndex max: 1) min: data size) truncated!

at: anIndex put: aValue 
	"Answer the value at the given index (between 0 and 1)."

	anIndex isSymbol
		ifTrue: [^ super at: anIndex put: aValue].
	^ data at: ((self size * anIndex max: 1)
			min: data size) truncated put: aValue!

atX: anIndex
	"Answer the value at the given index (between 0 and 1)."

	^self at: anIndex!

data
	"Answer the receiver's 'data'."

	^data!

data: anObject
	"Set the receiver's instance variable 'data' to be anObject."

	data := anObject!

dataClass
	"Answer the class of the elements in the receiver's data collection."

	data ifNil: [^nil].
	data ifEmpty: [^nil].
	^data first class!

domain
	"Answer the receiver's 'domain'."

	^domain!

domain: anObject
	"Set the receiver's instance variable 'domain' to be anObject."

	domain := anObject!

pointAt: index
	"Answer the given value in the receiver's breakpoint collection."

	^data at: index!

pointAt: index put: value
	"Assign the given values in the receiver's breakpoint collection."

	data at: index put: value.
	value < range first
		ifTrue: [self range: (value to: range last)].
	value > range last
		ifTrue: [self range: (range first to: value)].!

points
	"Answer the receiver's 'data'."

	| i |
	data ifNil: [^nil].
	data first isPoint ifTrue: [^data].
	i := 0.
	^data collect: [ :d | i := i + 1. (i@d) ].!

range
	"Answer the receiver's 'range'."

	^range!

range: anObject
	"Set the receiver's instance variable 'range' to be anObject."

	range := anObject!

realPointAt: index
	"Answer the given value in the receiver's breakpoint collection (this is not overridden in ExpSeg)."

	^data at: index!

sampleAt: anIndex
	"Answer the value at the given index (between 0 and 1)."

	^self at: anIndex!

scale
	"Answer the receiver's 'scale'."

	^range extent!

selection
	^nil!

size
	"Answer the size of the receiver's collection of breakpoints."

	| si |
	si := domain last - domain first.
	^(si = 0)
		ifTrue: [data size]
		ifFalse: [si]! !

!Siren.Function methodsFor: 'geometry'!

hasPointNear: anXValue
	"Answer whether or not the receiver has a function breakpoint near the given x value."

	^(self detect: [ :pt | (pt x - anXValue) abs < 0.01] ifNone: [nil]) ~= nil!

indexOfPointNearestX: anXValue
	"Answer the receiver`s point nearest the given x value."
	"(LinSeg from: #((0@0) (0.3@1) (0.6@0.6) (1@0))) indexOfPointNearestX: 0.7"

	| ind |
	ind := 1.
	2 to: data size do:
		[ :i |
		((data at: ind) x - anXValue) abs < 
			((data at: i) x - anXValue) abs
		ifFalse: [ind := i]
		ifTrue: [^ind]].
	^data size! !

!Siren.Function methodsFor: 'enumerating'!

detect: aBlock ifNone: exceptionBlock 
	"Evaluate aBlock with each of the receiver's elements as the argument.
	Answer the first element for which aBlock evaluates to true."

	self do: [:each | (aBlock value: each) ifTrue: [^each]].
	^exceptionBlock value!

do: aBlock 
	"Evaluate aBlock with each of the receiver's elements as the argument."

	data do: aBlock!

isEmpty
	^data isEmpty! !

!Siren.Function methodsFor: 'processing'!

averagedTo: size
	"Answer an averaged version of the receiver of the given size."
	"(Function readFloatsFrom: (MusicMagnitude modeRootName, 'data/y2.2.env')) averagedTo: 32"
 
	| ilen factor fcn sum index |
	ilen := self size.
	fcn := Function ofSize: size.
	size > ilen
		ifTrue: [^self interpolatedTo: size].
	factor := (size / ilen) asFloat.
	1 to: size do:
			[ :i |
			sum := 0.
			0 to: size do:
				[ :j |
				index :=  (i + j * factor) truncated max: 1.
				sum := sum + (data at: index)].
			fcn add: (sum/size)].
	^fcn!

freeData
	"Release the receiver's 'data'."

	data := nil!

maxTo: size win: wsize
	"Answer a version of the receiver of the given size taking the maximum value of each window."
	"(Function readFloatsFrom: 'snd/yy/ec/syl/y2.env') maxTo: 32 win: 8"
 
	| ilen factor fcn step maxv |
	ilen := self size.
	fcn := Function ofSize: size.
	size > ilen
		ifTrue: [^self interpolatedTo: size].
	factor := (1/size) asFloat.
	step := (factor / wsize) asFloat.
	0 to: size -1 do:
			[ :i |
			maxv := 0.
			0 to: wsize -1 do:
				[ :j |
				maxv := maxv max: (self at: (i*factor)+(j+step)) abs].
			fcn add: maxv].
	^fcn!

sampledTo: size
	"Answer a down-sampled version of the receiver of the given size."
	"((Function readFloatsFrom: (MusicMagnitude modeRootName, 'data/y2.2.env')) sampledTo: 64) edit"
 
	| ilen factor fcn |
	ilen := self size.
	fcn := Function ofSize: size.
	size > ilen
		ifTrue: [^self interpolatedTo: size].
	factor := (size / ilen) asFloat.
	1 to: size do:
			[ :i |
			fcn add: (self at: i*factor)].
	^fcn!

smoothed
	"Answer a version of the receiver smoothed to about 32 points."
	"| arr snd fcn|
	arr := Array new: 512.
	snd := 'Nomad:Squeak:Siren:sounds:kombination.1.aiff' asSoundFile.
	snd rmsFrom: 1 to: snd size into: arr.
	fcn := (Function from: arr).
	fcn smoothed edit"

	| nu avgs sc bre prev epsilon |
	nu := 64.
	avgs := Array new: nu.
	sc := (data size / nu) truncated.
"get the running averages"
	1 to: nu do:
		[ :in | | sum |
		1 to: sc do:
			[ :sa | | val |
			sum := 0.0.
			val := data at: (in - 1 * sc + sa).
			sum := sum + val].
		avgs at: in put: (sum / sc)].
"try to find constant regions and jumps"
	bre := OrderedCollection new: nu / 2.
	prev := avgs first.
	bre add: 0 @ prev.		"first breakpoint"
	epsilon := 0.33.
	2 to: nu - 1 do:
		[ :in | | this diff dif2 next |
		this := avgs at: in.
		next := avgs at: in + 1.
		diff := this - prev.
		this / (diff abs) > epsilon
			ifTrue: [dif2 := next - this.
				diff sign = dif2 sign ifFalse: [bre add: ((in * sc) @ this)]].
		prev := this].
	bre add: (data size @ avgs last).
	^LinearFunction from: bre!

updateRange

	| min max |
	data first isPoint
		ifTrue: [domain := data first x to: data last x]
		ifFalse: [domain := 1 to: data size].
	min := 1.0e30.  max := -1.0e30.
	data do: [ :d |
		d < min ifTrue: [min := d].
		d > max ifTrue: [max := d]].
	self range: (min to: max)! !

!Siren.Function methodsFor: 'ui'!

edit
	"Open a function view on the receiver."
	"(LinearFunction default scaleBy: 256@1) edit"

	FunctionView onFunction: self!

updateSelector
	^#ratio! !

!Siren.Function methodsFor: 'printing'!

printOn: aStream
	"Format and print the receiver on the argument."

	| pt |
	data size > 16
		ifTrue: [^super printOn: aStream].
	aStream nextPutAll: self class name, ' from: '.
	aStream nextPut: $#; nextPut: $(.
	1 to: data size do: 
			[ :i |
			pt := data at: i.
			aStream nextPut: $(.
			pt x printOn: aStream.
			aStream space.
			pt y printOn: aStream.
			aStream nextPut: $); space].
			aStream nextPut: $)! !

!Siren.Function class methodsFor: 'instance creation'!

default
	"Answer a default instance of the receiver class."

	^Function fileExample!

from: anArray
	"Answer a function with the given array of collections, points, or data values."
	"(Function from: #( 0 1 0 1 0 1 0 1 0 1 0)) at: 0.4"
	"(LinSeg from: #((0 0) (0.5 1) (1 0))) at: 0.2"

	| me |
	me := self ofSize: anArray size.
	(anArray first isKindOf: Collection)
		ifTrue: [anArray do: 
			[ :anCollection | 
				me add: (anCollection first @ anCollection last)].
			^me].
	(anArray first respondsTo: #x)
		ifTrue: [anArray do: 
				[ :aPoint | 
				me add: (aPoint x @ aPoint y)].
			^me].
	anArray do: 
		[ :aValue | 
			me add: aValue].
	me updateRange.
	^me!

new
	"Answer an instance of the receiver class."

	^(super new) initialize!

ofSize: size
	"Answer an instance of the receiver class of the requested size."
	"Function ofSize: 8"

	^(super new) initialize: size!

randomOfSize: size from: low to: high
	"Answer a function with the given number of data points in the given range."
	"FunctionView onFunction: (Function randomOfSize: 64 from: 0.2 to: 0.9)"

	| rand me |
	me := self ofSize: size.
	rand := Random new.
	1 to: size do: [ :i | me add: (rand nextFrom: low to: high)].
	me domain: (1 to: size).
	^me!

randomWalkSize: size from: low to: high
	"Answer a function with the given number of data points in the given range."
	"FunctionView onFunction: (Function randomWalkSize: 128 from: 0.3 to: 0.7)"

	| rand me val step v t |
	me := self ofSize: size.
	rand := Random new.
	val :=rand nextFrom: low to: high.
	step := high - low / 4.
	1 to: size do: [ :i | 
		v := rand nextFrom: step negated to: step.
		t := (val + v).
		((t  > high or: [t < low])) ifTrue: [v := v negated].
		val := val + v.
		me add: val].
	^me!

readFloatsFrom: filename
	"Answer a function with the given points."
	"Function readFloatsFrom: 'snd/yy/ec/syl/y2.env'"

	| file stream size me |
	file := filename asFilename.
	file exists ifFalse: [^self error: 'non-extant file ', filename].
	size := file fileSize / 4.		"assume floating-point values"
	me := self ofSize: size.
	stream := file readStream binary.
	1 to: size do: [ :i | me add: stream nextFloat].
	^me! !

!Siren.Function class methodsFor: 'class constants'!

defaultSize
	"Answer the default size for the instances' storage array."

	^1024! !

!Siren.Function class methodsFor: 'examples'!

averagedFunctionFileExample
	"Function usage example; read a function from a binary file and view it."
	"Function averagedFunctionFileExample"

	FunctionView onFunction:
			 ((Function readFloatsFrom: 
				SirenUtility dataDir, 'y2.2.env') 
		averagedTo: 32)!

fileExample
	"Function usage example; read a function from a binary file and view it."
	"Function fileExample inspect"

	^Function readFloatsFrom: SirenUtility dataDir, 'y2.2.env'!

functionFileExample
	"Function usage example; read a function from a binary file and view it."
	"Function functionFileExample"

	FunctionView onFunction: Function default!

functionPlayExample
	"Function usage example; make a roll-type eventList and apply a crescendo/decrescendo to it"
	"Function functionPlayExample"

	| temp fcn |
	temp := EventList newNamed: #test3.
	(0 to: 4000 by: 50) do: 	"4 seconds, 20 notes per second"
			[ :index |			"add the same note"
			temp add: (MusicEvent dur: 100 pitch: 36 ampl: 100) 
					at: index].
	fcn := LinearFunction from: #((0 @ 0) (0.5 @ 1) (1 @ 0)).
	temp applyFunction: fcn to: #loudness.
	temp play!

functionViewExample
	"Function usage example; make a z-z function and view it."
	"Function functionViewExample"

	FunctionView onFunction: (Function from: #( 0 1 0 0.5 1.0 0.5 0 1 0 0.3 0.6 0.9 1 0 1 0))!

maxedFunctionFileExample
	"Function usage example; read a function from a binary file and view it."
	"Function maxedFunctionFileExample"

	FunctionView onFunction: ((Function readFloatsFrom: SirenUtility dataDir, 'y2.2.env') maxTo: 128 win: 32)!

randomViewExample
	"Function usage example; make a random walk fcn and view it."
	"Function randomViewExample"

	FunctionView onFunction: (Function randomOfSize: 64 from: 0.2 to: 0.9)!

randomViewExample2
	"Function usage example; make a random walk fcn and view it."
	"Function randomViewExample2"

	FunctionView onFunction: (Function randomWalkSize: 128 from: 0.3 to: 0.7)! !

!Siren.Function class methodsFor: 'standard functions'!

exponentialADSR1
	"Answer a exponential attack/decay/sustain/release envelope."
	"Function exponentialADSR1"
	"FunctionView openOn: Function exponentialADSR1"

	^ExponentialFunction from: #((0 0 5) (0.2 1 -3) (0.8 0.5 -2) (1 0))!

exponentialADSR2
	"Answer a exponential attack/decay/sustain/release envelope."
	"Function exponentialADSR2"
	"FunctionView openOn: Function exponentialADSR2"

	^ExponentialFunction from: #((0 0 5) (0.05 1 -3) (0.8 0.5 -2) (1 0))!

linearADSR1
	"Answer a linear attact/decat/sustain/release envelope."
	"Function linearADSR1"
	"FunctionView openOn: Function linearADSR1"

	^LinearFunction from: #((0 0) (0.1 1) (0.2 0.5) (0.9 0.5) (1 0))!

linearADSR2
	"Answer a linear attact/decat/sustain/release envelope."
	"Function linearADSR2"
	"FunctionView openOn: Function linearADSR2"

	^LinearFunction from: #((0 0) (0.05 1) (0.1 0.5) (0.9 0.4) (1 0))!

spline
	"Answer a generic spline curve."
	"Function spline"
	"FunctionView openOn: Function spline"

	^SplineFunction from: #((0 0.5) (0.35 0.6) (0.65 0.2) (1 0.6))!

sumOfSines
	"Answer a simple Fourier summation."
	"Function sumOfSines"
	"FunctionView openOn: Function sumOfSines"

	^FourierSummation from: #((1 0.5 0) (3 0.3 0) (5 0.2 0) (7 0.17 0) (9 0.15 0) (11 0.1 0))! !

!Siren.LinearFunction methodsFor: 'accessing'!

add: anItem
	"Add the argument to the receiver's point collection."

	data add: anItem.
	anItem x < domain first
		ifTrue: [self domain: (anItem x to: domain last)].
	anItem x > domain last
		ifTrue: [self domain: (domain first to: anItem x)].
	anItem y < range first
		ifTrue: [self range: (anItem y to: range last)].
	anItem y > range last
		ifTrue: [self range: (range first to: anItem y)]!

at: anIndex
	"Answer the value at the given index in my range--do linear interpolation."

	| xVal count x1 x2 y1 y2|
	xVal := data first x.
	count := 1.
	[xVal < anIndex] whileTrue:
		[count := count + 1.
		count > data size
			ifTrue: [^data last x].
		xVal := (data at: count) x].
	xVal = anIndex 
		ifTrue: [^(data at: count) y]
		ifFalse: [x1 := (data at: (count - 1)) x.
			x2 := (data at: count) x.
			y1 := (data at: (count - 1)) y.
			y2 := (data at: count) y.
			^y1 + ((anIndex - x1) / (x2 - x1) * (y2 - y1))]! !

!Siren.LinearFunction methodsFor: 'processing'!

normalize
	"Normalize the receiver to the range and domain of 0-1, inclusive."

	| xs ys |
	xs := domain stop - domain first.
	ys := range stop - range first.
	((xs abs > 1) or: [ys abs > 1]) ifTrue: 
		[data := data collect: [ :pt |
			pt setX: (pt x / xs) setY: (pt y / ys)].
		range := 0 to: 1.
		domain := 0 to: 1]!

scaleBy: scalePt
	"Scale the receiver's points by the given scale."
	"LinearFunction default scaleBy: 5@1"

	| newD |
	newD := Array new: data size.
	1 to: data size do:
		[ :i | newD at: i put: (data at: i) * scalePt].
	data := newD.
	self updateRange!

updateRange

	| minx maxx miny maxy |
	domain := 1 to: data size.
	minx := 1.0e30.  maxx := -1.0e30.
	miny := 1.0e30.  maxy := -1.0e30.
	data do: [ :d |
		d x < minx ifTrue: [minx := d x].
		d x > maxx ifTrue: [maxx := d x].
		d y < miny ifTrue: [miny := d y].
		d y > maxy ifTrue: [maxy := d y]].
	self range: (miny to: maxy).
	self domain: (minx to: maxx)! !

!Siren.LinearFunction class methodsFor: 'instance creation'!

a: att d: dec s: sus r: rel
	"Answer an ADSR envelope."
	"FunctionView onFunction: (LinearFunction a: 0.06 d: 0.2 s: 0.4 r: 0.3)"

"	| susT |
	susT := 1.0 - (att + dec).
	^LinearFunction from: 
		{ { 0. 0 }. { att. 1 }. { att+dec. sus }. { susT. sus }. { 1. 0 } }"!

default
	"Answer a default instance of the receiver class."

	^LinearFunction from: #((0 0) (0.15 1.0) (0.25 0.75) (0.75 0.5) (1 0))! !

!Siren.LinearFunction class methodsFor: 'examples'!

exampleEnvelope
	"Answer a LinSeg of a typical envelope function."
	"LinearFunction exampleEnvelope"
	"FunctionView onFunction: (LinearFunction exampleEnvelope)"

	| rand pts size x y |
	rand := Random new.
	size := rand nextIntFrom: 5 to: 15.
	pts := Array new: size.
	pts at: 1 put: 0@0.
	pts at: size put: 1@0.
	x := 0.
	y := 0.8.
	2 to: size - 1 do:
		[ :i |
		x := x + (rand nextFrom: 0.05 to: 0.15).
		y := ((y + (rand nextFrom: -0.3 to: 0.3)) min: 1.0) max: 0.0.
		pts at: i put: (x@y)].
	^LinearFunction from: pts!

linearExample
	"Make a LinSeg and answer a value."
	"LinearFunction linearExample"

	| fcn |
	fcn := LinearFunction ofSize: 2.
	fcn add: (0@0.5); add: (1@1).
	Transcript show: (fcn at: 0.14) printString; cr.
	fcn inspect!

linearViewExample
	"Make a LinSeg and open a view on its form."
	"LinearFunction linearViewExample"

	FunctionView onFunction: 
		(LinearFunction from: #((0 0) (0.1 1) (0.2 0.6) (0.9 0.4) (1 0)))! !

!Siren.Spectrum methodsFor: 'initialize-release'!

initialize
	"Set up the receiver."

	super initialize.
	window := " #rectangular " #triangular.
	windowSize := 256.
	stepSize := 1024.
"	fft := FFT new nu: (windowSize log: 2) truncated.
"	magReal := #real.			"mag, real, polar, or complex"! !

!Siren.Spectrum methodsFor: 'accessing'!

D
	"Answer the receiver's decimation factor."

	^windowSize / stepSize!

D: factor
	"Set the receiver's decimation factor."

	^stepSize := windowSize / factor!

fft
	"Answer the receiver's 'fft'."

	^fft!

fft: anObject
	"Set the receiver's instance variable 'fft' to be anObject."

	fft := anObject!

N
	"Answer the receiver's 'windowSize'."

	^windowSize!

N: aNum
	"Set the receiver's instance variable 'windowSize' to be aNum."

	windowSize := aNum.
	fft := FFT new nu: (windowSize log: 2) truncated!

sound
	"Answer the receiver's 'sound'."

	^sound!

sound: anObject
	"Set the receiver's instance variable 'sound' to be anObject."

	sound := anObject!

stepSize
	"Answer the receiver's 'stepSize'."

	^stepSize!

stepSize: anObject
	"Set the receiver's instance variable 'stepSize' to be anObject."

	stepSize := anObject!

windowSize
	"Answer the receiver's 'windowSize'."

	^windowSize!

windowSize: aNum
	"Set the receiver's instance variable 'windowSize' to be aNum."

	windowSize := aNum.
	fft := FFT new nu: (windowSize log: 2) truncated! !

!Siren.Spectrum methodsFor: 'frames'!

at: anIndex
	"Answer the value at the given index (between 1 and self size)."

	anIndex isSymbol
		ifTrue: [^ super at: anIndex].
	^data at: anIndex!

at: anIndex put: aValue
	"Answer the value at the given index (between 0 and 1)."

	anIndex isSymbol ifTrue: [^super at: anIndex put: aValue].
	data at: anIndex put: aValue!

frame: anIndex
	"Answer the frame at the given index (between 1 and self size)."

	^data at: anIndex!

frame: anIndex do: aBlock
	"Iterate over a single frame with the given block."

	magReal == #real
		ifTrue: [(data at: anIndex) do: aBlock]

		ifFalse: [ | base |
			base := anIndex * windowSize.
			base to: base + windowSize do: aBlock]!

frame: frm imagAt: frq
	"Answer the given imag value in the given frame."

	magReal == #complex
		ifTrue: [^(data at: frm) at: frq + windowSize]!

frame: frm imagAt: frq put: val
	"Set the given imag value in the given frame."

	magReal == #complex
		ifTrue: [(data at: frm) at: frq + windowSize put: val]!

frame: anIndex put: aValue
	"Set the value at the given index (between 0 and 1)."

	anIndex isSymbol ifTrue: [^super at: anIndex put: aValue].
	data at: anIndex put: aValue!

frame: frm realAt: frq
	"Answer the requested real value from the given frame."

	^(data at: frm) at: frq!

frame: frm realAt: frq put: val
	"Set the given real value in the given frame."

	magReal == #complex
		ifTrue: [(data at: frm) at: frq put: val]! !

!Siren.Spectrum methodsFor: 'modes'!

complex
	"Set the receiver's instance variable 'magReal' to be #complex."

	magReal := #complex!

real
	"Set the receiver's instance variable 'magReal' to be #real."

	magReal := #real!

setWindow: aSymbol
	"Set the receiver's 'window'."
	"#rectangular, #triangular"

	window := aSymbol!

window
	"Answer the receiver's 'window'."

	^window! !

!Siren.Spectrum methodsFor: 'time warp'!

interpolateFrame: ind
	"Answer a new frame interpolated for the given (floating-point) index."

	| bas fra pre nex int |
	bas := ind truncated max: 1.
	fra := ind fractionPart.
	pre := data at: bas.
	fra < 0.05 ifTrue: [^pre].
	bas = data size ifTrue: [^pre].
	nex := data at: bas + 1.
	int := pre class new: pre size.
	1 to: pre size do:
		[ :x | | v0 v1 |
		v0 := pre at: x.
		v1 := nex at: x.
		int at: x put: (v0 + ((v1 - v0) * fra))].
	^int!

timewarpBy: fact
	"Interpolate/decimate the receiver by the given (float or function) time factor."

	| siz newD |
	fact isNumber ifTrue:
		[siz := (data size * fact) truncated.
		newD := Array new: siz.
		1 to: siz do:
			[ :in |
			newD at: in put: (self interpolateFrame: (in asFloat / fact))].
		data := newD.
		^self]
				"else fact is a function"! !

!Siren.Spectrum methodsFor: 'computing'!

compute
	"Do the FFT and store the results into the receiver's data."
	"Display restoreAfter: [Spectrum sweepExample]"
	"Display restoreAfter: [Spectrum fileExample]"

	| dSize min max |
	dSize := (sound size / stepSize) truncated.
	data := Array new: dSize.		"array of arrays"
	min := 1.0e30.  max := -1.0e30.
	Cursor execute showWhile:
		[1 to: dSize do:
			[ :index |
			fft realData: (self window: index).
			fft transformForward: true.		"do fft"
			magReal == #real				"pack data--real or complex"
				ifTrue: [data at: index put: fft realData]
				ifFalse: [data at: index put: 				"store complex spectrum as mag"
					((1 to: windowSize) collect:
						[ :i | ((fft realData at: i) squared 
								+ (fft imagData at: i) squared) sqrt])].
			(data at: index) do:			"calc min/max"
				[ :d | min := d min: min.
					max := d max: max]]].
	self range: (min to: max)!

setData
	"Create the receiver's data array for the right number of frames."

	data := Array new: (sound size / stepSize) truncated.
	domain := 0 to: data size!

updateRange
	"Iterate over the receier's data getting the RMS data range"

	| min max off |
	min := 1.0e30.  max := -1.0e30.
	off := windowSize / 2.
	magReal == #real ifTrue:
		[data do: 
			[ :fr | fr do: 
				[ :d | min := d min: min.
					max := d max: max]]].

	magReal == #complex ifTrue:
		[data do: 
			[ :fr | 1 to: off do: 
				[ :ind | | val |
				val :=  ((fr at: ind) squared + (fr at: ind + off) squared) sqrt.
					min := val min: min.
					max := val max: max]]].

	self range: (min to: max)!

window: index
	"Answer the real data array for the given window of the receiver's sound."

	| dArray base sHalf  ind|
	dArray := Array new: windowSize.
	base := index - 1 * stepSize.		"index is 1-based"
	window == #rectangular
		ifTrue: [1 to: dArray size do:
				[ :i | dArray at: i put: (sound intSampleAt: (base + i))]].
	window == #triangular
		ifTrue: [sHalf := dArray size / 2.
			1 to: sHalf do:
				[ :i | dArray at: i put: ((sound intSampleAt: (base + i)) * (i / sHalf)) truncated].
			1 to: sHalf do:
				[ :i | 
				ind := i + sHalf.
				dArray at: ind put: ((sound intSampleAt: (base + ind)) * (1 - (i / sHalf )) truncated)]].
	^dArray! !

!Siren.Spectrum methodsFor: 'printing'!

printOn: aStream
	"Format and print the receiver on the argument."

	aStream nextPutAll: self class name, ' on: '.
	sound printOn: aStream.
	aStream nextPutAll: (' window: ', windowSize printString, ' step: ', stepSize printString)! !

!Siren.Spectrum class methodsFor: 'examples'!

fileExample
	"Read a sound from disk and take its fft."
	"Spectrum fileExample"
	"Display restoreAfter: [Spectrum fileExample]"

	| me |
	me := self new initialize.
	me sound: (SampledSound fromFile: (SirenUtility soundDir, 'kombination.snd')).
	me windowSize: 512.
	me stepSize: 512.
	me real.
	me compute.
	me display.
	^me!

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

	| me |
	me := self new initialize.
	me sound: (SampledSound sweepDur: 1.0 rate: 16000 from: 20 to: 2000 chans: 1).
	me windowSize: 256.
	me stepSize: 512.
	me complex.
	me compute.
	me display.
	^me! !

!Siren.Sound methodsFor: 'initialize-release'!

initialize
	"Set up a default sound."

	self propCheck.
	range := -1.0 to: 1.0! !

!Siren.Sound methodsFor: 'cue accessing'!

addCueNamed: cName from: start to: stop
	"Add the given named cue region to the receiver's list."

	| list key |
	self cueList == nil
		ifTrue: [self cueList: OrderedCollection new].
	list := self cueList.
	key := cName asSymbol.
	1 to: list size do:
		[ :ind |
		((list at: ind) key == key)
			ifTrue: [^list at: ind put: (key -> (start to: stop))]].
	list add: (cName asSymbol -> (start to: stop)).!

cueList
	"Answer the receiver's 'cueList'."

	properties ifNil: [^nil].
	^properties at: #cueList ifAbsent: [nil]!

cueList: CollectionOfCues
	"Set the receiver's 'cueList' to be the given OrderedCollection of (#name -> (start to: stop)) cues."

	^properties at: #cueList put: CollectionOfCues!

cueNamed: cName
	"Answer a sound derived from the receiver using the samples between the named cue points."

	^self subclassResponsibility!

cueRegionNamed: cName
	"Answer a indices for the samples between the named cue points."

	| q |
	self cueList == nil
		ifTrue: [self error: 'There is no cue by the name ', 
				cName printString, ' in this sound.'].
	q := self cueList detect: [ :c | c key == cName] ifNone: [nil].
	q == nil
		ifTrue: [self error: 'There is no cue by the name ', 
				cName printString, ' in this sound.'].
	^q value! !

!Siren.Sound methodsFor: 'testing'!

isSound
	"Answer true."

	^true! !

!Siren.Sound class methodsFor: 'instance accessing'!

named: aName
	"Answer the sound by the given name, or nil."

	^SirenUtility sounds at: aName asSymbol ifAbsent: [nil]!

named: aName put: aSound
	"Put the given sound in the shared dictionary under the given name."

	SirenUtility sounds ifNil: [^nil].
	^SirenUtility sounds at: aName asSymbol put: aSound! !

!Siren.ComponentSound methodsFor: 'accessing'!

offset
	"Answer the receiver's 'offset'."

	^offset!

offset: anObject
	"Set the receiver's 'offset' to be anObject."

	offset := anObject!

sound
	"Answer the receiver's 'sound'."

	^sound!

sound: anObject
	"Set the receiver's 'sound' to be anObject."

	sound := anObject!

start
	"Answer the receiver's 'start'."

	^start!

start: anObject
	"Set the receiver's 'start' to be anObject."

	start := anObject!

stop
	"Answer the receiver's 'stop'."

	^stop!

stop: anObject
	"Set the receiver's 'stop' to be anObject."

	stop := anObject.
	((start ~~ nil) & (sound ~~ nil))
		ifTrue: [self duration: (stop - start / sound rate) asFloat]! !

!Siren.ComponentSound methodsFor: 'testing'!

includes: anIndex
	"Answer whether the argument is within the range of the receiver."

	^(anIndex >= start) & (anIndex <= stop)! !

!Siren.ComponentSound methodsFor: 'printing'!

printOn: aStream
	"Pretty-print the receiver on the argument."

	| ra |
	sound ifNil: [^super printOn: aStream].
	ra := sound rate asFloat.
	aStream nextPutAll: '(ComponentSound on ', sound name, 
			' from ', ((start asFloat / ra) printWidth: 6), 
			' to ', ((stop asFloat / ra) printWidth: 6).
	offset ifNotNil: 
		[aStream nextPutAll: ' starting at ', offset printString].
	aStream nextPutAll: ')'; cr.! !

!Siren.ComponentSound class methodsFor: 'instance creation'!

on: sound from: start to: stop
	"Answer a new instance initialized with the arguments."

	^(self new) sound: sound; start: start; stop: stop!

on: sound start: start stop: stop
	"Answer a new instance initialized with the arguments."

	^(self new) sound: sound; start: start; stop: stop!

on: sound start: start stop: stop offset: offset
	"Answer a new instance initialized with the arguments."

	^(self new) sound: sound; start: start; stop: stop; offset: offset! !

!Siren.SplineFunction methodsFor: 'accessing'!

at: anIndex
	"Answer the value at the given index in my range--take it out of my computed form."

	linSeg == nil
		ifTrue: [self computeCurve].
	^linSeg at: anIndex! !

!Siren.SplineFunction methodsFor: 'computing'!

computeCurve
	"Compute the receiver by cubic interpolation; use the Geometric spline."

	| points spline |
	points := OrderedCollection new: data size.
	data do: [ :pt |
			points add: (pt * 100)].
	spline := Spline controlPoints: points.
	points := spline computeCurve; computePoints.
	1 to: points size do: [ :i |
			points at: i put: ((points at: i) / 100)].
	linSeg := LinearFunction from: points! !

!Siren.SplineFunction class methodsFor: 'instance creation'!

default
	"Answer a default instance of the receiver class."

	^SplineFunction from: #((0 0.5) (0.35 0.7) (0.65 0.35) (1 0.5))! !

!Siren.SplineFunction class methodsFor: 'examples'!

splineExample
	"Make a SplineSeg."
	"SplineFunction splineExample"

	| fcn |
	fcn := SplineFunction from: #((0 1) (0.35 1.3) (0.65 0.7) (1 1)).
	fcn inspect.
"	^fcn at: 0.14 
"!

splineViewExample
	"Make a SplineSeg and open a view on its form."
	"SplineFunction splineSegViewExample"

	FunctionView onFunction: 
		(SplineFunction from: #((0 0.5) (0.35 0.7) (0.65 0.2) (1 0.5)))! !

!Siren.RatioMagnitude methodsFor: 'accessing'!

realValue
	"Answer the receiver's value mapped to the receiver."

	^value!

value
	"Answer the receiver's value mapped to the receiver."

	^relative == nil
		ifTrue: [value]
		ifFalse: [self class new value: (relative value * value)]! !

!Siren.RatioMagnitude methodsFor: 'converting'!

asRatio
	"Answer self."

	^self!

map
	"Apply the receivers reference."

	relative == nil
		ifFalse: [value := value * relative value.
				relative := nil]!

relativeTo: aRelative
	"Set the receivers reference."

	relative := aRelative! !

!Siren.RatioMagnitude class methodsFor: 'instance creation'!

value: aValue relative: aMMagnitude
	"Answer and instance with the argument as its value."

	^(self new value: aValue) relativeTo: aMMagnitude! !

!Siren.RatioPitch methodsFor: 'converting'!

asFracMIDI
	"return a RatioPitch as a fractional MIDINote."

	^self asHertz asFracMIDI.!

asFracSymbol
	"Assuming value is a ratio, answer a symbolic note name"

	^SymbolicPitch fromFracMIDI: (self asFracMIDI)!

asHertz
	"Answer a HertzPitch."

	^relative == nil
		ifTrue: [HertzPitch new value: (value * 261.623)]
		ifFalse: [HertzPitch new value:
					(value * (relative asHertz value))]!

asMIDI
	"return a RatioPitch as a MIDINote (rounded)."

	^self asHertz asMIDI.!

asSymbol
	"Assuming value is a ratio, answer a symbolic note name"

	^SymbolicPitch fromMIDI: (self asMIDI)!

mostGeneral
	"Answer the most numerically meaningful version of the receiver."

	^self asHertz! !

!Siren.RatioPitch methodsFor: 'printing'!

printOn0: aStream
	"Print the receiver as a ratio."

	aStream nextPutAll: "'p:'," value printString! !

!Siren.RatioPitch methodsFor: 'private'!

species
	"Answer Pitch."

	^Pitch! !

!Siren.RatioPitch class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asStep! !

!Siren.RatioDuration methodsFor: 'converting'!

asMS
	"Answer a MSecondDuration."

	^relative == nil
		ifTrue: [MSecondDuration new value: ((value * 1000) truncated)]
		ifFalse: [MSecondDuration new value:
					((value * 1000 * (relative asRatio value)) truncated)]!

asMsec
	"Answer a MSecondDuration."

	^relative == nil
		ifTrue: [MSecondDuration new value: ((value * 1000) truncated)]
		ifFalse: [MSecondDuration new value:
					((value * 1000 * (relative asRatio value)) truncated)]!

asSec
	"Answer a SecondDuration."

	^relative == nil
		ifTrue: [SecondDuration new value: value asFloat]
		ifFalse: [SecondDuration new value:
					(value asFloat * (relative asRatio value))]!

mostGeneral
	"Answer the most numerically meaningful version of the receiver."

	^self asSec! !

!Siren.RatioDuration methodsFor: 'printing'!

printOn0: aStream
	"Print the receiver as a fractional duration."

	aStream nextPutAll: "'d:'," value printString!

units
	"Answer the units string of the receiver."

	^' beat'! !

!Siren.RatioDuration methodsFor: 'private'!

species
	"Answer Duration."

	^Duration! !

!Siren.RatioDuration methodsFor: 'double dispatching'!

quotientFromInteger: numerator
	"Answer a MM whose value is the argument over the receiver's value."
	"1/4 beat"

	^self species relativeMember value: (numerator / value)! !

!Siren.RatioDuration class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asBeat! !

!Siren.RatioLoudness methodsFor: 'converting'!

asDB
	"Answer a dB loudness; ratio 1 = 0dB, ratio 0.5 = -6dB, etc."
	"(RatioLoudness value: 0.25) asDB"

	^DBLoudness new value: (20 * (value log))!

asMIDI
	"Answer a MIDI key velocity (0 to 127)"
	
	^MIDIVelocity new value: ((value * 127.0) asInteger)!

asSymbol
	"Answer a symbolic dynamic."
	
	^(self asMIDI) asSymbol! !

!Siren.RatioLoudness methodsFor: 'printing'!

printOn0: aStream
	"Print the receiver as a ratio."

	aStream nextPutAll: "'a:'," value printString! !

!Siren.RatioLoudness methodsFor: 'private'!

species
	"Answer Amplitude."

	^Amplitude! !

!Siren.RatioLoudness class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asAmpl! !

!Siren.DisplayListEditor methodsFor: 'initialize-release'!

release
	"Remove the dependency to the list."

	list removeDependent: self.
	list release! !

!Siren.DisplayListEditor methodsFor: 'accessing'!

displayList
	"Answer the receiver's list."

	^list!

list
	"Answer the receiver's list."

	^list!

list: aList
	"Set the receiver's list, removing the dependency to the former list if necessary."

	list == nil
		ifFalse: [list removeDependent: self].
	list := aList.
	list addDependent: self!

moveSelectionTo: newPoint
	"Move the editor's selection to the new point in the list."

	| oldOff |
	selection hasItems
		ifFalse: [oldOff := selection offset.
			selection offset: newPoint.
			self changed": #position with: (oldOff corner: newPoint)"]
		ifTrue: [selection components do: [ : i | list remove: i].
			selection offset: newPoint.
			list add: selection.
			self changed]!

select: sel
	"Set the receiver's selection."

"	sel == nil ifTrue: [Transcript show: 'clear'; cr]."
	selection := sel.
	self changed: #selection with: sel!

selection
	"Answer the receiver's selection."

	^selection! !

!Siren.DisplayListEditor methodsFor: 'updating'!

update: anAspect with: anArg from: aModel
	"Echo the model's changes, assuming a view is a dependent of the receiver."

	self changed: anAspect with: anArg! !

!Siren.DisplayListEditor methodsFor: 'editing'!

doDisplay
	"inspect button"

	self changed!

doEdit
	"edit button"

	self doInspect!

doFile
	"file i/o button"

	Transcript show: 'No File.'; cr.!

doInspect
	"Inspect the editor (if shiftDown) or the model."

	InputState default shiftDown
		ifTrue: [self inspect]
		ifFalse: [list inspect]!

doZoom
	"zoom button"! !

!Siren.DisplayListEditor class methodsFor: 'instance creation'!

on: model
	^self new list: model! !

!Siren.EventListEditor methodsFor: 'initialize-release'!

release
	"Release the receiver's list."

	list removeDependent: self.
	list release! !

!Siren.EventListEditor methodsFor: 'accessing'!

ampl
	"Answer the amplitude of the default event."

	^defaultEvent == nil
		ifTrue: [0]
		ifFalse: [defaultEvent  loudness value]!

ampl: newValue
	"Set the amplitude of the default event."

	defaultEvent == nil
		ifTrue: [^self].
	defaultEvent loudness: newValue asInteger.
	self changed: #ampl!

defaultEvent
	"Answer the receiver's default event."

	^defaultEvent == nil
		ifTrue: [list defaultEventClass new]
		ifFalse: [defaultEvent]!

defaultEvent: anEvent
	"Set the receiver's default event."

	defaultEvent := anEvent!

displayList
	"Answer nil."

	^nil!

eventList
	"Answer the receiver's subject eventList."

	^eventList!

eventList: anEL
	"Set the given EventList as the receiver's model."

	eventList := anEL.
	defaultEvent := eventList defaultEventClass new.
	self changed: #list!

items
	"Answer the subject eventList's events."

	^eventList events!

selection
	"Answer the collection of selected events."

	^selection! !

!Siren.EventListEditor methodsFor: 'list access'!

add: anAss
	"Add the given EventAssociation to the model list."

	list add: anAss.
	self changed: #added.!

doInspect
	"Inspect the receiver's list."

	InputState default shiftDown
		ifTrue: [self inspect]
		ifFalse: [list inspect]!

eraseList
	"Flush the receiver's list."

	list empty.
	self changed: #deleted.!

play
	"Play the receiver's eventList."

"	Sensor leftShiftDown 
		ifTrue: [self startStop]
		ifFalse: [list play]
"
	eventList play!

remove: anEvent
	"Remove the given event from the receiver's list"

"	Transcript show: 'remove: ', anEvent printString; cr."
	list remove: anEvent.
	self changed: #deleted.!

startStop
	"If not playing, create ostinato and start; if playing, stop and release."

	ostinato == nil
		ifTrue: [ostinato := Ostinato onList: list.
				[ostinato play]]
		ifFalse: [ostinato stop.
				ostinato := nil]! !

!Siren.EventListEditor methodsFor: 'links and groups'!

group: events
	"Group the event list argument within the receiver's list."

	list group: events.
	self changed: #events!

link: anEvent with: anOtherEvent
	"Set up a link between the two events or lists."

	^self!

select: anAorEL
	"Add the given association to the list of selected items."

	| mo |
	anAorEL == nil
		ifTrue: [^selection := nil].
	mo := anAorEL isEvent
		ifTrue: [anAorEL]
		ifFalse: [anAorEL model].
	mo hasItems
		ifTrue: [selection := mo]
		ifFalse: [selection == nil
				ifTrue: [selection := EventList newNamed: EventList nextName ].
			selection add: mo]!

selectItem: anAorEL
	"Set the given association as the receiver's selection."

	selection := anAorEL! !

!Siren.SoundCanvas methodsFor: 'actions'!

flushCoordinateCaches

	"ignored"!

zoomToZero

	"This stub method was generated by UIDefiner"

	^self! !

!Siren.SoundCanvas methodsFor: 'aspects'!

container: ignored
!

hZoom
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^hZoom isNil
		ifTrue:
			[hZoom := 1 asValue]
		ifFalse:
			[hZoom]!

setComponent: aComponent
	"Set the receiver's component part"

	componentPart := aComponent!

vZoom
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^vZoom isNil
		ifTrue:
			[vZoom := 0.5 asValue]
		ifFalse:
			[vZoom]! !

!Siren.SoundCanvas methodsFor: 'initialize'!

createSoundView
	"Create and answer the receiver's sound view"
	"SoundCanvas open"

	soundView := SoundView soundViewOn: SampledSound sawtooth.
"	componentPart == nil
		ifTrue: [componentPart := DisplayListView componentOnList: displayList].

"! !

!Siren.OSCPort methodsFor: 'actions'!

send: aMessage 
	"^self"

	socket sendTo: address buffer: aMessage oscBytes! !

!Siren.OSCPort methodsFor: 'initialize-release'!

init: t1 
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	socket := SocketAccessor newUDP.
	address := t1.
	^self! !

!Siren.OSCPort methodsFor: 'accessing'!

portNumber: newPortNum 
	"Reset the receiver's UDP socket port number"

	address port: newPortNum! !

!Siren.OSCPort class methodsFor: 'defaults'!

defaultCSLOSCPort
	"The default for CSL"

	^54321!

defaultIP
	"Answer the class var default"

	^DefaultIP!

defaultIP: value
	"Answer the class var default"

	DefaultIP := value!

defaultPort
	"Answer the class var default"

	^DefaultPort!

defaultPort: value
	"Set the class var default"

	DefaultPort := value!

defaultSCOSCPort
	"Answer the default port for OSC"

	^57123! !

!Siren.OSCPort class methodsFor: 'instance-creation'!

cslHost
	"Answer the default port for CSL"

	^self to: (IPSocketAddress hostName: 'localhost' port: 54321)!

default
	"Answer the default instance"

	^self to: (IPSocketAddress hostAddress: DefaultIP port: DefaultPort)!

localhost
	"Answer an OSC port on the local host"

	^self to: (IPSocketAddress hostAddress: (ByteArray
				with: 127
				with: 0
				with: 0
				with: 1)
			port: DefaultPort)!

scHost
	"Answer the default OSC server reference"

	^self to: (IPSocketAddress hostAddress: (ByteArray
				with: 192
				with: 168
				with: 61
				with: 1)
			port: self defaultSCOSCPort)!

to: t1 
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^self new init: t1!

to: ip port: port
	"^an OSCPort with the given attributes"
	"OSCPort to: #[127 0 0 1] port: 54321"

	^self to: (IPSocketAddress hostAddress: ip port: port)!

toHostName: t1 
	"Answer an instance on the given host"

	^self to: (IPSocketAddress hostName: t1 port: DefaultPort)!

toHostName: aHostName portNumber: aPort 
	"^an OSCPort 
	Convenience method"

	^self to: (IPSocketAddress hostName: aHostName port: aPort)!

toLocalhostPortNumber: aPort 
	"^an OSCPort 
	Convenience method"

	^self toHostName: 'localhost' port: aPort! !

!Siren.OSCPort class methodsFor: 'examples'!

demo
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	self sendStart.
	self sendFreq.
	self sendStop.
	^self!

demo2
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	self scHost send: TypedOSCMessage start.
	self scHost send: TypedOSCMessage changeFreq.
	self scHost send: TypedOSCMessage scStop.
	^self!

sendFreq
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	self localhost send: TypedOSCMessage changeFreq.
	^self!

sendStart
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	self localhost send: TypedOSCMessage start.
	^self!

sendStop
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	self localhost send: TypedOSCMessage scStop.
	^self! !

!Siren.OSCPort class methodsFor: 'class initialization'!

initialize
	"Setup the defaults"
	"OSCPort initialize"

	OSCPort defaultIP: #[127 0 0 1].
	OSCPort defaultPort: 57123! !

!Siren.SoundEditor methodsFor: 'initialize release'!

initialize
	"Set up the proper default values for the receiver."

	markers := OrderedCollection new.
	changed := false!

reinitialize
	"Set up the proper default values for the receiver."

	markers := OrderedCollection new.
	sound := selection1 := selection2 := lastSelection := copyBuffer := nil.
	changed := false! !

!Siren.SoundEditor methodsFor: 'accessing'!

mark: aSampleNumber
	"Set the receiver's model"

	markers == nil ifTrue: [markers := Dictionary new].
	markers at: aSampleNumber printString asSymbol put: aSampleNumber!

markChanged: aBoolean
	"Set the flag to tell the receiver that the model's been changed."

	changed := aBoolean!

markers
	"Answer the receiver's markers"

	^markers!

markers: aCollection
	"Set the receiver's marker collection."

	markers := aCollection!

selection1: anInteger
	"Set the receiver's primary selection."

	selection1 := anInteger!

selection2: anInteger
	"Set the receiver's secondary selection."

	selection2 := anInteger!

setModel: aModel
	"Set the receiver's model."

	self reinitialize.
	super setModel: aModel.
	self changed: #text!

size
	^sound size!

sound
	"Answer the receiver's model"

	^sound!

sound: aSound
	"Set the receiver's model to be the given sound"

	sound == nil ifFalse: [sound release].
	self reinitialize.
	sound := aSound!

view: aView
	"Set the receiver's view to be the given view"

	view := aView! !

!Siren.SoundEditor methodsFor: 'selection'!

select0: cursorX
	"take a mouse point and place a marker on the sound sample there"

	|selection |
	selection := (view sampleForX: cursorX).
	view showMarkerAt: cursorX.
	markers add: selection.
	selection1 == nil
		ifTrue: [selection1 := selection]
		ifFalse: [selection2 == nil
			ifTrue: [selection2 := selection]].
"	self changed: #markers with: cursorX"!

select: cursorX
	"Take a mouse point and place a marker on the sound sample there."

	|selection |
	selection := (view sampleForX: cursorX).
	view showMarkerAt: cursorX.
"	Transcript show: cursorX printString, ' --> ', selection printString; cr."
"	markers add: selection."
	selection1 == nil
		ifTrue: [selection1 := selection]
		ifFalse: [selection2 := selection]!

selectedSound
	"Answer a sound from the receiver view's selection."

	| s |
	((selection1 == nil) or: [selection2 == nil])
		ifTrue: [^nil].
	Cursor wait showWhile:
		[s := sound fromSample: selection1 toSample: selection2].
	^s!

setSelection: aMarker
	"Set the primary selection to be the given marker."

	lastSelection = selection2
		ifFalse: [selection2 := aMarker.
				lastSelection := aMarker]
		ifTrue: [selection1 := aMarker.
				lastSelection := aMarker].
	((selection1 == nil) or: [selection2 == nil])
		ifTrue: [^self].
	selection1 > selection2
		ifTrue: [ |temp |
			temp := selection1.
			selection1 := selection2.
			selection2 := temp].
	view displaySelection: selection1 and: selection2! !

!Siren.SoundEditor methodsFor: 'interaction'!

copy
	"Copy the current selection into the temporary copy buffer."

	| selSnd |
	selSnd := self selectedSound.
	selSnd == nil
		ifFalse: [copyBuffer := selSnd]!

cut
	"Cut out the current selection."

	sound cutFrom: (selection1 min: selection2) to: (selection1 max: selection2).
	self changed.!

file
	"Save the receiver's sound to a file."

	| name |
	name := Dialog
		request: 'Save copy buffer to:' 
		initialAnswer: sound name.
	name = '' ifTrue: [^nil].
	copyBuffer == nil
		ifTrue: [(SoundFile named: name) saveSound: self selectedSound]
		ifFalse: [(SoundFile named: name) saveSound: copyBuffer].!

newCopy
	"Make a fresh copy of the receiver's model's sound."

	self setModel: sound copy!

play
	"Play the receiver's sound"

	((selection1 ~~ nil) and: [selection2 ~~ nil])
		ifTrue: [SoundPort default playSamples: sound samples
				rate: sound rate chans: sound channels 
				from: (sound mapSampleIndex: selection1) * sound sampleSize - 1
				to: (sound mapSampleIndex: selection2) * sound sampleSize - 1]
		ifFalse: [changed 
			ifTrue: [SoundPort default playSamples: sound samples
					rate: sound rate chans: sound channels 
					from: 1
					to: sound samples sizeInBytes]
			ifFalse: [sound play]]!

spawn
	"Open a soundfile editor."

	SoundView openOn: sound! !

!Siren.SoundEditor class methodsFor: 'instance creation'!

new
	"Answer an initialized instance."

	^super new initialize! !

!ZPoint methodsFor: 'accessing'!

z
	^z!

z: aNumber
	z := aNumber! !

!ZPoint methodsFor: 'arithmetic'!

* aZPointOrNumber 
	"only works if the argument is a ZPoint or Number"

	| scalePoint |
	scalePoint := (aZPointOrNumber isMemberOf: ZPoint)
				ifTrue: [aZPointOrNumber]
				ifFalse: [ZPoint
						x: aZPointOrNumber
						y: aZPointOrNumber
						z: aZPointOrNumber].
	^ZPoint
		x: x * scalePoint x
		y: y * scalePoint y
		z: z * scalePoint z!

+ aZPointOrNumber 
	"Answer a new ZPoint that is the sum of the receiver and delta 
	(which is a ZPoint  
	or Number)."

	| deltaPoint |
	deltaPoint := (aZPointOrNumber isMemberOf: ZPoint)
				ifTrue: [aZPointOrNumber]
				ifFalse: [ZPoint
						x: aZPointOrNumber
						y: aZPointOrNumber
						z: aZPointOrNumber].
	^ZPoint
		x: x + deltaPoint x
		y: y + deltaPoint y
		z: z + deltaPoint z!

- aZPointOrNumber 
	"Answer a new ZPoint that is the sum of the receiver and delta 
	(which is a ZPoint  
	or Number)."

	| deltaPoint |
	deltaPoint := (aZPointOrNumber isMemberOf: ZPoint)
				ifTrue: [aZPointOrNumber]
				ifFalse: [ZPoint
						x: aZPointOrNumber
						y: aZPointOrNumber
						z: aZPointOrNumber].
	^ZPoint
		x: x - deltaPoint x
		y: y - deltaPoint y
		z: z - deltaPoint z!

/ aZPointOrNumber 
	"only works if the argument is a ZPoint or Number"

	| scalePoint |
	scalePoint := (aZPointOrNumber isMemberOf: ZPoint)
				ifTrue: [aZPointOrNumber]
				ifFalse: [ZPoint
						x: aZPointOrNumber
						y: aZPointOrNumber
						z: aZPointOrNumber].
	^ZPoint
		x: x / scalePoint x
		y: y / scalePoint y
		z: z / scalePoint z!

rounded
	^self class basicNew
		setX: x rounded
		setY: y rounded
		setZ: z rounded! !

!ZPoint methodsFor: 'converting'!

asPoint
	^x@y! !

!ZPoint methodsFor: 'point functions'!

dotProduct: aZPoint
	| temp |
	temp := self * aZPoint.
	^temp x abs + temp y abs + temp z abs! !

!ZPoint methodsFor: 'printing'!

printOn: aStream
	super printOn: aStream.
	aStream nextPutAll: '@'.
	z printOn: aStream!

storeOn: aStream
	"copied & modified from class Point"

	aStream nextPut: $(;
	nextPutAll: self species name;
	nextPutAll: ' x: ';
	store: x;
	nextPutAll: ' y: ';
	store: y;
	nextPutAll: ' z: ';
	store: z;
	nextPut: $).! !

!ZPoint methodsFor: 'private'!

convertToFloats
	x := x asFloat.
	y := y asFloat.
	z := z asFloat!

setX: xValue setY: yValue setZ: zValue
	x := xValue.
	y := yValue.
	z := zValue! !

!ZPoint methodsFor: 'testing'!

= aPoint
	^super = aPoint and: [z = aPoint z]!

hash
	^super hash bitXor: z hash!

isZero
	^super isZero and: [z isZero]! !

!ZPoint class methodsFor: 'instance creation'!

x: xValue y: yValue z: zValue
	^self basicNew setX: xValue setY: yValue setZ: zValue! !

!Siren.MIDIVelocity methodsFor: 'converting'!

asDB
	"Answer a dB loudness; ratio 1 = 0dB, ratio 0.5 = -6dB, etc."
	"(MIDIVelocity value: 64) asDB"

	^DBLoudness new value: (20 * ((value / 127) asFloat log))!

asMIDI
	"Answer a MIDIVelocity."

	^self!

asRatio
	"Answer a RatioLoudness."

	^RatioLoudness new value: ((value / 127.0) asFloat)!

asSymbol
	"Answer a symbolic loudness."
	"(Amplitude value: 107) asSymbol"	"107 is fff"

	^SymbolicLoudness fromMIDI: value!

mostGeneral
	"Answer the most numerically meaningful version of the receiver."

	^self asRatio! !

!Siren.MIDIVelocity methodsFor: 'printing'!

printOn0: aStream
	"Print the receiver as a MIDI velocity."

	aStream nextPutAll: "'v:'," value printString!

units
	"Answer the units string of the receiver."

	^' vel'! !

!Siren.MIDIVelocity methodsFor: 'private'!

species
	"Answer Amplitude."

	^Amplitude! !

!Siren.MIDIVelocity methodsFor: 'accessing'!

value: theValue
	"Truncate"

	super value: theValue.
	value := value truncated.! !

!Siren.MIDIVelocity class methodsFor: 'class initialization'!

initialize
	"Initialize the class instance variable."
	"MIDIVelocity initialize."

	range := 0 to: 127! !

!Siren.MIDIVelocity class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asVelocity! !

!Siren.DisplayRectangle methodsFor: 'accessing'!

fill: aBoolean
	"Set the receiver's filling Boolean."

	fill := aBoolean! !

!Siren.DisplayRectangle methodsFor: 'displaying'!

displayOn: aGraphicsContext
	"Display a line between startPoint and endPoint."

"	Transcript show: start printString, '	', end printString; cr."
	color == nil
		ifFalse: [aGraphicsContext paint: color].
	(fill == nil or: [fill not])
		ifFalse: [aGraphicsContext displayRectangle: (offset extent: extent)]
		ifTrue: [aGraphicsContext displayRectangularBorder: (offset extent: extent)]!

displayPostScriptOn: aPostscriptContext
	"Display the receiver on the argument as a PostScript item."

	aPostscriptContext translate: offset.
	aPostscriptContext box: extent y width: extent x.
	aPostscriptContext translate: offset negated.! !

!Siren.DisplayRectangle class methodsFor: 'examples'!

rectangleExample
	"Open a display list view with randomly-placed random-color rectangles over the given extent."
	"DisplayRectangle rectangleExample"

	(DisplayList rectanglesX: 600 byY: 600) display! !

!Siren.DisplayPolyline methodsFor: 'accessing'!

bounds
	"Answer the receiver's bounds." 

	| ext |
	ext := 0@0.
	vertices do: [ :pt | ext := ext max: pt].
	^offset extent: ext!

extent
	"Answer the receiver's extent." 

	^self bounds extent!

vertices: pointArray
	"Set the receiver's vertices."

	vertices := pointArray asArray! !

!Siren.DisplayPolyline methodsFor: 'displaying'!

displayOn: aGraphicsContext
	"Stroke the receiver's edges on the supplied GraphicsContext."

	color == nil
		ifFalse: [aGraphicsContext paint: color].
	offset == nil
		ifFalse: [aGraphicsContext translateBy: offset].
	(fill == nil or: [fill not])
		ifTrue: [aGraphicsContext displayPolyline: vertices at: Point zero]
		ifFalse: [aGraphicsContext displayPolygon: vertices at: Point zero].
	offset == nil
		ifFalse: [aGraphicsContext translateBy: offset negated].! !

!Siren.DisplayPolyline methodsFor: 'transforming'!

scaledBy: aPoint
	"Scale all the receiver's points by the argument; answer a copy."

	|  newPts |
	newPts := vertices collect: [ :pt | (pt * aPoint) truncated].
	^self class offset: offset vertices: newPts! !

!Siren.DisplayPolyline class methodsFor: 'instance creation'!

offset: offPt vertices: arrayOfPoints
	"Answer a new DisplayPolyline with the arguments as its vertices."

	^self basicNew offset: offPt; vertices: arrayOfPoints!

vertices: arrayOfPoints
	"Answer a new DisplayPolyline with the arguments as its vertices."

	^self basicNew vertices: arrayOfPoints! !

!Siren.DisplayPolyline class methodsFor: 'examples'!

polylineExample
	"Open a display list view with randomly-placed random-color polylines over the given extent."
	"DisplayPolyline polylineExample"

	(DisplayList polylinesX: 600 byY: 600) display! !

!Siren.DisplayListSubcanvas methodsFor: 'actions'!

nextPage

	"This stub method was generated by UIDefiner"

	^self!

prevPage

	"This stub method was generated by UIDefiner"

	^self!

zoomChanged
	"Sent when the users moves the zoom sliders."

	displayListView zoom: (hZoom value @ vZoom value).
	scroller changed!

zoomTo1
	"Sent when the users presses the zoom-to-1 button."

	hZoom value: 1.
	vZoom value: 1.
	self zoomChanged!

zoomToLast

	"This stub method was generated by UIDefiner"

	^self! !

!Siren.DisplayListSubcanvas methodsFor: 'aspects'!

createDisplayListView
	"Create and answer the receiver's display list view"
	"DisplayListSubcanvas open"

	displayList == nil
		ifTrue: [displayList := DisplayList stringsX: 2000 byY: 2000].
	componentPart == nil
		ifTrue: [componentPart := DisplayListView componentOnList: displayList].
	displayListView := componentPart components first component component.
	scroller := displayListView container.
	^componentPart!

hZoom
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^hZoom isNil
		ifTrue:
			[hZoom := 1 asValue]
		ifFalse:
			[hZoom]!

setComponent: aComponent
	"Set the receiver's component part"

	componentPart := aComponent!

setDisplayList: aDisplayList
	"Set the receiver's display list"

	displayList := aDisplayList
!

vZoom
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^vZoom isNil
		ifTrue:
			[vZoom := 1 asValue]
		ifFalse:
			[vZoom]! !

!Siren.DisplayListSubcanvas class methodsFor: 'instance creation'!

onList: aDisplayList

	| me |
	me := self new.
	me setDisplayList: aDisplayList.
	me open!

openOnList: aDisplayList
	"DisplayListSubcanvas openOnList: (DisplayList rectanglesX: 6000 byY: 6000)"
	"DisplayListSubcanvas openOnList: (DisplayList polylinesX: 2000 byY: 2000)"

	| me |
	me := self new.
	me setDisplayList: aDisplayList.
	self openOn: me! !

!Siren.DisplayListSubcanvas class methodsFor: 'interface specs'!

windowSpec
	"Answer the user's favorite window layout"

	^self windowSpecLeftBottom! !

!Siren.SoundPort methodsFor: 'initialize/release'!

initialize
	"Answer an initialized version of the receiver."

	^self!

release
	"Terminate and release the receiver."

	^self! !

!Siren.SoundPort methodsFor: 'play/record'!

play: aSound
	"Play the argument on the receiver over the DACs."

	self subclassResponsibility!

record: aSound
	"Record into the argument via the receiver."

	self subclassResponsibility! !

!Siren.SoundPort methodsFor: 'open/close'!

close
	"Close the receiver sound port"

	^self!

open
	"Open the receiver sound port"

	^self!

start
	"Start the receiver sound port"

	^self!

stop
	"Stop the receiver sound port"

	^self! !

!Siren.SoundPort class methodsFor: 'instance creation'!

default
	"Answer a default instance of the appropriate subclass."
	"SoundPort defaultClass instanceCount"

	| count |
	count := self defaultClass instanceCount.
	(count > 0)
		ifTrue: [^self defaultClass someInstance].
	^self defaultClass new initialize!

defaultClass
	"Answer the appropriate subclass."

	^PortAudioPort! !

!Siren.SoundPort class methodsFor: 'class var accessing'!

blockSize
	^DefaultBlockSize
!

blockSize: value
	DefaultBlockSize := value!

inChannels
	^DefaultInChannels!

inChannels: value
	DefaultInChannels := value!

outChannels
	^DefaultOutChannels!

outChannels: value
	DefaultOutChannels := value!

sampleRate
	^DefaultSampleRate!

sampleRate: value
	DefaultSampleRate := value! !

!Siren.SoundPort class methodsFor: 'class initialization'!

initialize
	"Set up the defaults for the class constants."
	"SoundPort initialize"

	SoundPort outChannels: 2.
	SoundPort inChannels: 0.
	SoundPort sampleRate: 44100.
	SoundPort blockSize: 1024.
! !

!Siren.DisplayString methodsFor: 'accessing'!

bounds
	"Answer a rectangle that circumscribes the receiver."

	^offset extent: self extent!

extent
	"Answer a rectangle that circumscribes the receiver."

" clean but broken version...
	^font == nil
		ifFalse: [((TextMeasurer forFont: font)
				measureSring: string
				fromCharacterIndex: 1
				to: string size) @ font height]
		ifTrue: [((self class stringMeasurer)
				measureSring: string
				fromCharacterIndex: 1
				to: string size) @ 12]
"
	^(string size * 12) @ 12!

font
	"Answer the receiver's font."

	^font!

font: aFont
	"Set the receiver's font."

	font := aFont!

string
	"Answer the receiver's string."

	^string!

string: aString
	"Set the receiver's string."

	string := aString! !

!Siren.DisplayString methodsFor: 'displaying'!

displayOn: aGraphicsContext
	"display the receiver on the argument."

"	Transcript show: (aGraphicsContext translation  + offset) printString; space."
	font == nil
		ifFalse: [aGraphicsContext font: font].
	color == nil
		ifFalse: [aGraphicsContext paint: color].
	aGraphicsContext displayString: string at: (offset + (0@14))!

displayPostScriptOn: aPostscriptContext
	"Display the receiver on the argument as a PostScript item."

	aPostscriptContext translate: offset.
	aPostscriptContext show: string.
	aPostscriptContext translate: offset negated.! !

!Siren.DisplayString methodsFor: 'printing'!

printOn: aStream
	"comment stating purpose of message"

	aStream nextPutAll: ' (a ', self class name, ' offset: '.
	offset printOn: aStream.
	aStream nextPutAll: ' string: '.
	string printOn: aStream.
"	color == nil
		ifFalse: [aStream nextPutAll: ' color: '. color printOn: aStream].
"	aStream nextPut: $); cr.! !

!Siren.DisplayString class methodsFor: 'instance creation'!

new
	"Answer a new initialized instance."

	^super new initialize!

string: aString
	"Answer a new instance with the argument as its string."

	^self new string: aString!

string: aString offset: oPoint
	"Answer a new instance with the arguments as its string and offset point."

	^self new string: aString; offset: oPoint! !

!Siren.DisplayString class methodsFor: 'examples'!

stringExample
	"Open a display list view with randomly-placed random-color strings over the given extent."
	"DisplayString stringExample"

	(DisplayList stringsX: 600 byY: 600) display! !

!Siren.DisplayList methodsFor: 'accessing'!

color
	^ColorValue black!

flatten
	"Answer a copy of the receiver with its hierarchy flattened."

	| new |
	new := DisplayList new.
	components do:
		[ :item |
		item hasItems
			ifTrue: [ | subnew |
				subnew := item flatten components.
				subnew do: [ :subitem |
					new add: (subitem translateBy: item offset)]]
			ifFalse: [new add: item]].
	^new!

itemsFromX: x1 toX: x2
	"Answer the list of items whose offsets are within the given X range."

	| dList |
	dList := DisplayList new.
	components do: 
		[ :i |
		i hasItems
			ifTrue:
				[ | tList |
				tList := i itemsFromX: x1 + i offset x 
							toX: x2 + i offset x.
				tList isEmpty ifFalse: [dList add: tList at: i offset]]
			ifFalse:
				[(i offset x between: x1 and: x2) ifTrue: [dList add: i]]].
	^dList!

itemsIntersecting: rect
	"Answer the list of items whose boundingBoxes intersect the given rectangle. 
	Clip them to the box's border if necessary."

	| dList dList2 |
	dList := DisplayList new.
	components do: 
		[ :i |
		i hasItems
			ifTrue: [dList2 := i itemsIntersecting: (rect translateBy: self offset).
				dList2 == nil
					ifFalse: [dList add: (dList2 offset: i offset)]]
			ifFalse: [(((i boundingBox) translateBy: self offset) 
								intersects: rect)
					ifTrue: [dList add: (i clipTo: (rect translateBy: ((0@0) - self offset)))]]].
	^(dList components isEmpty)
		ifTrue: [nil]
		ifFalse: [(dList components size = 1)
			ifTrue: [dList components first]
			ifFalse: [dList]]!

itemsWithin: rect
	"Answer the list of items whose boundingBoxes are entirely within the given rectangle."

	| dList movedRect |
	movedRect := rect moveBy: self offset negated.
	dList := DisplayList new.
	components do: 
		[ :comp |
		comp hasItems
			ifTrue: [ | items |
				items := comp itemsWithin: movedRect.
				items == nil ifFalse: [dList add: items]]
			ifFalse: [(movedRect contains: comp bounds) 
					ifTrue: [dList add: comp]]].
	(dList components isEmpty)
		ifTrue: [^nil]
		ifFalse: [(dList components size = 1)
			ifTrue: [ | first |
				first := dList components first.
				first hasItems
					ifTrue: [first container: nil].
				^first copy translateBy: self offset]
			ifFalse: [^dList translateBy: self offset]]!

itemsWithin: dist of: point
	"Answer the list of items whose boundingBoxes are entirely within the given rectangle."

	| dList |
	dList := DisplayList new.
	components do: 
		[ :comp |
		comp hasItems
			ifTrue: [dList add: 
					(comp itemsWithin: dist of: (point - self offset))]
			ifFalse: [((comp bounds expandBy: dist) containsPoint: point)
					ifTrue: [dList add: comp]]].
	^(dList components isEmpty)
		ifTrue: [nil]
		ifFalse: [(dList components size = 1)
			ifTrue: [dList components first]
			ifFalse: [dList]]!

itemWithin: dist of: point
	"Answer the list of items whose boundingBoxes are entirely within the given rectangle."

	components do: 
		[ :comp |
		comp hasItems
			ifTrue: [ | item |
				(item := comp itemWithin: dist of: (point - self offset)) == nil
					ifFalse: [^item]]
			ifFalse: [((comp bounds expandedBy: dist) containsPoint: point)
					ifTrue: [^comp]]].
	^nil!

modelsFromX: x1 toX: x2
	"Answer the list of model-space-items whose offsets are within the given X range."

	| dList |
	dList := DisplayList new.
	components do: 
		[ :i |
		((i offset x >= x1) and: [i offset x <= x2])
				ifTrue: [dList add: i]].
	^dList collect: [ :i | i model]!

nodeAt: aPath
	"Answer the element described by the collection of items in the argument."

	| next path2 |
	components isEmpty ifTrue: [^nil].
	next := components
			detect: [ :i | 
					i "component" model == aPath first]
			ifNone: [nil].
	next == nil
		ifFalse: [aPath size = 1
			ifTrue: [^next]
			ifFalse: [path2 := aPath copy.
				path2 removeFirst.
				^next nodeAt: path2]]
		ifTrue: [^nil]!

nodeFor: aModel
	"Answer the element whose model is the argument."

	components isEmpty ifTrue: [^nil].
	^components
			detect: [ :i | 
					i model == aModel]
			ifNone: [nil].!

offset
	"Answer the receiver's offset."

	^offset == nil
		ifTrue: [0@0]
		ifFalse: [offset]!

offset: aPoint
	"Set the receiver's offset."

	offset := aPoint!

wrapperClass
	"Raise an error."

	self error: 'I don''t use Wrappers!!'! !

!Siren.DisplayList methodsFor: 'bounds accessing'!

computePreferredBounds
	"Compute the receiver's preferredBounds"

	components isEmpty
		ifTrue: [preferredBounds := (0@0) extent: (0@0)]
		ifFalse: [preferredBounds := components first bounds].
	1 to: components size do:
		[:i |
		preferredBounds := preferredBounds merge: (components at: i) bounds].
	preferredBounds origin < (0@0)
		ifTrue: [preferredBounds := preferredBounds 
				translatedBy: preferredBounds origin negated].
	offset == nil
		ifTrue: [preferredBounds := preferredBounds expandedBy: (64@64)]
		ifFalse: [preferredBounds := (preferredBounds translatedBy: offset) expandedBy: (64@64)].
	^preferredBounds!

extent
	"Answer the extent of the receiver."

	^self preferredBounds extent! !

!Siren.DisplayList methodsFor: 'adding-removing'!

add: aVisualComponent
	"Add the argument to the receiver."

	^self addComponent: aVisualComponent!

add: aVisualComponent at: aPoint
	"Add aVisualComponent to the receiver's components with its offset set to aPoint."

	aVisualComponent offset: aPoint.
	self add: aVisualComponent!

add: anItem atPath: aPath
	"Add the first argument at the node described by the second."

	| node |
	(aPath == nil or: [aPath isEmpty])
		ifTrue: [^self add: anItem].
	node := self nodeAt: aPath.
	node == nil
		ifTrue: [self addComponent: anItem]
		ifFalse: [node addComponent: anItem]!

addComponent: aVisualComponent
	"Add the argument to the receiver."

	components addLast: aVisualComponent.
	aVisualComponent container: self!

addWrapper: aVisualWrapper
	"Raise an error."

	self error: 'I don''t use Wrappers!!'! !

!Siren.DisplayList methodsFor: 'transforming'!

scaledBy: aPoint
	"Scale the receiver's offset by the argument."

	| newMe |
	newMe := self class new.
	self do:
		[ :item |
		newMe add: (item scaledBy: aPoint) at: item offset * aPoint].
	^newMe!

translateBy: aPoint
	"Translate the receiver's offset by the argument."

	self offset: self offset + aPoint! !

!Siren.DisplayList methodsFor: 'enumerating'!

do: aBlock
	"Iterate the argument block over the receiver's components."

	components do: aBlock! !

!Siren.DisplayList methodsFor: 'testing'!

hasItems
	"Answer whether or not the receiver has items or components."

	^true!

isEmpty
	"Amswer whether the receiver has any items."

	^components isEmpty! !

!Siren.DisplayList methodsFor: 'displaying'!

display
	"Open a DisplayListView on the receiver."

	^DisplayListSubcanvas openOnList:  self!

displayNonCached
	"Open a DisplayListView on the receiver."

	^DisplayListView openNonCached: self!

displayOn: aGraphicsContext
	"Display each of the receiver's components."

	| clipBox |
	clipBox := aGraphicsContext clippingBounds.
	offset == nil ifFalse: [aGraphicsContext translateBy: offset].
	components do:
		[ :c |
		(c intersects: clipBox)
			ifTrue: [c displayOn: aGraphicsContext]].
	offset == nil ifFalse: [aGraphicsContext translateBy: offset negated].!

displayPostScriptOn: aPostscriptContext
	"Display each of the receiver's components."

	offset == nil
		ifFalse: [aPostscriptContext translate: offset].
	1 to: components size do:
		[ :i | (components at: i) displayPostScriptOn: aPostscriptContext].
	offset == nil
		ifFalse: [aPostscriptContext translateBy: offset negated].! !

!Siren.DisplayList methodsFor: 'printing'!

printCompleteOn: aStream depth: depth
	"Descend the hierarchy printing on the stream."

	aStream cr.
	depth timesRepeat: [aStream tab].
	aStream nextPutAll: '(a DisplayList '.
	offset == nil
		ifFalse: [aStream nextPutAll: ' offset: ', offset printString].
"	model == nil
		ifFalse: [aStream nextPutAll: ' model: ', model printString]."
	components == nil
		ifFalse: [components do: 
			[ :e | 
			e printCompleteOn: aStream depth: depth + 1]].
	aStream nextPut: $)!

printCompletePostScriptOn: aStream depth: depth
	"Descend the hierarchy printing on the stream."

	aStream cr.
	depth timesRepeat: [aStream tab].
	aStream nextPutAll: '(a DisplayList '.
	offset == nil
		ifFalse: [aStream nextPutAll: ' offset: ', offset printString].
"	model == nil
		ifFalse: [aStream nextPutAll: ' model: ', model printString]."
	components == nil
		ifFalse: [components do: 
			[ :e | 
			e printCompleteOn: aStream depth: depth + 1]].
	aStream nextPut: $)!

printOn: aStream
	"Print the receiver on the argument using the recursive method."

	^InputState default shiftDown 
		ifTrue: [super printOn: aStream]
		ifFalse: [self printCompleteOn: aStream depth: 0]!

printPostScriptOn: aStream
	"Print the receiver on the argument using the recursive method."

	^self printCompletePostScriptOn: aStream depth: 0! !

!Siren.DisplayList class methodsFor: 'examples'!

exampleHierarchical
	"Create and answer a large display list with a lines, strings, and visuals."
	"(DisplayList exampleHierarchical) display"

	| dl1 dl2 dl3 dl4 dl5 dl6 rand size |
	size := 100.
	dl1 := DisplayList new.
	dl2 := DisplayList new.
	dl3 := DisplayList new.
	dl4 := DisplayList new.
	dl5 := DisplayList new.
	dl6 := DisplayList new.
	rand := Random new.
	10 timesRepeat:
		[ | pt |
		pt := (rand next * size) rounded @ ((rand next * size) rounded).
		dl1 add: ((DisplayString string: (pt printString) 
				offset: pt) color: (ColorValue random: rand))].
	10 timesRepeat:
		[ | x y w h |
		x := (rand next * size) rounded.		y := (rand next * size) rounded.
		w := (rand next * size) rounded.		h :=  (rand next * size) rounded.
		dl2 add: ((DisplayRectangle offset: (x@y) extent: (w@h))
				color: (ColorValue random: rand); fill: true)].
	10 timesRepeat:
		[ | x y w h |
		x := (rand next * size) rounded.		y := (rand next * size) rounded.
		w := (rand next * size) rounded.		h :=  (rand next * size) rounded.
		dl3 add: ((DisplayRectangle offset: (x@y) extent: (w@h))
				color: (ColorValue random: rand); fill: true)].
	10 timesRepeat:
		[ | pt |
		pt := (rand next * size) rounded @ ((rand next * size) rounded).
		dl4 add: ((DisplayString string: (pt printString) 
				offset: pt) color: (ColorValue random: rand))].
	dl5 add: dl1; 
		add: dl2 at: (250@0); 
		add: dl3 at: (0@250); 
		add: dl4 at: (250@250).
	dl6 add: dl5 at: (50@50).
	^dl6!

gridExample
	"Create and answer a large display list with lines and strings."
	"(DisplayList gridExample) display"
	"DisplayListView example"

	| dList rand ext step |
	ext := 1000.
	step := 50.
	dList := DisplayList new.
	0 to: (ext/step) do:
		[ :i |
		dList add: (DisplayLine offset: (i*step)@0 extent: 0@ext).
		dList add: (DisplayLine offset: 0@(i*step) extent: ext@0)].
	rand := Random new.
	1 to: 50 do:
		[ :i | | pt |
		pt := (rand next * ext) rounded @ ((rand next * ext) rounded).
		dList add: (DisplayString string: (pt printString) offset: pt)].
	^dList!

gridExampleX: x byY: y
	"Create and answer a large display list with lines and strings."
	"(DisplayList gridExampleX: 3000 byY: 500) display"
	"DisplayListView example"

	| dList rand step |
	step := 50.
	dList := DisplayList new.
	0 to: (x/step) do:
		[ :i |
		dList add: (DisplayLine offset: (i*step)@0 extent: 0@y)].
	0 to: (y/step) do:
		[ :i |
		dList add: (DisplayLine offset: 0@(i*step) extent: x@0)].
	rand := Random new.
	1 to: (x * y / 20000) do:
		[ :i | | pt |
		pt := (rand next * x) rounded @ ((rand next * y) rounded).
		dList add: (DisplayString string: (pt printString) offset: pt)].
	^dList!

polylineExample
	"Answer a display list with randomly-placed random-color polylines over the given extent."
	"DisplayList polylineExample"

	(DisplayList polylinesX: 600 byY: 600 items: 100) display!

polylineExampleHuge
	"Answer a display list with randomly-placed random-color polylines over the given (very large) extent."
	"DisplayList polylineExampleHuge"

	(DisplayList polylinesX: 6000 byY: 6000 items: 10000) display!

polylinesX: x byY: y
	"Answer a display list with randomly-placed random-color polylines over the given extent."
	"(DisplayList polylinesX: 600 byY: 600) display"

	| dList rand inset scale x0 y0 size pointArray |
	inset := 16.
	scale := 128.
	dList := DisplayList new.
	rand := Random new.
	x0 := rand nextIntFrom: inset to: x-inset.
	y0 := rand nextIntFrom: inset to: y-inset.
	((x/20) + (y/20)) timesRepeat:
		[size := rand nextIntFrom: 3 to: 7.
		pointArray := Array new: size.
		pointArray at: 1 put: (0@0).
		pointArray at: size put: (0@0).
		x0 := y0 := 0.
		2 to: size-1 do: [ :i |
			x0 := x0 + (rand nextIntFrom: scale negated to: scale).
			y0 := y0 + (rand nextIntFrom: scale negated to: scale).
			pointArray at: i put: (x0 @ y0)].
		dList add: ((DisplayPolyline
				offset: ((rand nextIntFrom: inset to: x-inset)
						@(rand nextIntFrom: inset to: y-inset)))
				color: ColorValue random;
				vertices: pointArray)].
	^dList!

polylinesX: x byY: y items: num
	"Answer a display list with randomly-placed random-color polylines over the given extent."
	"(DisplayList polylinesX: 600 byY: 600 items: 1000) display"

	| dList rand inset scale x0 y0 size pointArray |
	inset := 16.
	scale := 128.
	dList := DisplayList new.
	rand := Random new.
	x0 := rand nextIntFrom: inset to: x-inset.
	y0 := rand nextIntFrom: inset to: y-inset.
	num timesRepeat:
		[size := rand nextIntFrom: 3 to: 7.
		pointArray := Array new: size.
		pointArray at: 1 put: (0@0).
		pointArray at: size put: (0@0).
		x0 := y0 := 0.
		2 to: size-1 do: [ :i |
			x0 := x0 + (rand nextIntFrom: scale negated to: scale).
			y0 := y0 + (rand nextIntFrom: scale negated to: scale).
			pointArray at: i put: (x0 @ y0)].
		dList add: ((DisplayPolyline
				offset: ((rand nextIntFrom: inset to: x-inset)
						@(rand nextIntFrom: inset to: y-inset)))
				color: ColorValue random;
				vertices: pointArray)].
	^dList!

postScriptExample
	"Create and answer a large display list with a lines, strings, and visuals."
	"DisplayList postScriptExample"
	"NOT WORKING"

	| dList psRenderer file |
	dList := (DisplayList stringsX: 600 byY: 900) "DisplayList example2".
	file := 'temp.ps' asFilename writeStream.
	psRenderer := PostScriptPrinter new.
	psRenderer file: file; start: nil.
"	psRenderer scale: 10@10."
	dList displayPostScriptOn: psRenderer.
	psRenderer endNoPrint: nil. 		"this prints it."
"	'temp.ps' asFilename edit."!

randomExample
	"Create and answer a large display list with a lines, strings, and visuals."
	"(DisplayList randomExample) display"

	| dList rand ext size |
	ext := 1500.
	size := ext / 2.
	dList := DisplayList new.
	rand := Random new.
	20 timesRepeat:
		[ | x y w h |					"add rectangles"
		x := (rand next * size) rounded.
		y := (rand next * size) rounded.
		w := (rand next * size/10) rounded.
		h :=  (rand next * size/10) rounded.
		dList add: ((DisplayRectangle
				offset: (x@y)
				extent: (w@h))
				color: ColorValue random; fill: true)].
	20 timesRepeat:
		[ | pt1 pt2 |					"add lines"
		pt1 := (rand next * size) rounded @ ((rand next * size) rounded).
		pt2 := (rand next * size) rounded @ ((rand next * size) rounded).
		dList add: ((DisplayLine offset: pt2 extent: (pt1 - pt2))
				color: ColorValue random)].
	20 timesRepeat:
		[ | pt |						"add strings"
		pt := (rand next * size) rounded @ ((rand next * size) rounded).
		dList add: ((DisplayString string: (pt printString) 
				offset: pt) color: ColorValue random)].
	20 timesRepeat:
		[ | pt coll |					"add visuals"
		coll := Cursor allInstances.
		pt := (rand next * ext) rounded @ ((rand next * ext) rounded).
		dList add: ((DisplayVisual visual: (coll atRandom image) 
				offset: pt) color: ColorValue random)].
	^dList!

rectangleExample
	"Answer a display list with randomly-placed random-color rectangles over the given extent."
	"DisplayList rectangleExample"

	(DisplayList rectanglesX: 2000 byY: 2000) display!

rectanglesX: x byY: y
	"Answer a display list with randomly-placed random-color rectangles over the given extent."
	"(DisplayList rectanglesX: 600 byY: 600) display"

	| dList rand x0 y0 x1 y1 scale w h |
	dList := DisplayList new.
	rand := Random new.
	scale := 0.
	x0 := x - (scale * 2).
	y0 := y - (scale * 2).
	((x/10) + (y/10)) timesRepeat:
		[x1 := scale + (rand next * x0) rounded.
		y1 := scale + (rand next * y0) rounded.
		w := ((rand next * 0.5 * (x / 20) abs) rounded).
		h :=  ((rand next * 0.5 * (y / 20) abs) rounded).
		dList add: ((DisplayRectangle
				offset: (x1@y1)
				extent: (w@h))
				color: ColorValue random; fill: true)].
	^dList!

stringExample
	"Open a display list view with randomly-placed random-color strings over the given extent."
	"DisplayList stringExample"

	(DisplayList stringsX: 4000 byY: 4000) display!

stringsX: x byY: y
	"Answer a display list with randomly-placed random-color strings over the given extent."
	"(DisplayList stringsX: 600 byY: 600) display"

	| dList rand inset |
	inset := 16.
	dList := DisplayList new.
	rand := Random new.
	((x/5) + (y/5)) timesRepeat:
		[ | pos |
		pos := (rand nextIntFrom: inset to: x - inset) @ (rand nextIntFrom: inset to: y - inset).
		dList add: ((DisplayString
				string: pos printString
				offset: pos)
				color: ColorValue random)].
	^dList!

visualsX: x byY: y
	"Answer a display list with randomly-placed random-color visuals over the given extent."
	"(DisplayList visualsX: 600 byY: 600) display"

	| dList rand inset |
	inset := 16.
	dList := DisplayList new.
	rand := Random new.
	((x/10) + (y/10)) timesRepeat:
		[dList add: ((DisplayVisual
				visual: (rand nextIntFrom: 10 to: 100000) printString asParagraph
				offset: ((rand nextIntFrom: inset to: x-inset)
						@(rand nextIntFrom: inset to: y-inset)))
				color: ColorValue random)].
	^dList! !

!Tools.ListWorkBook methodsFor: 'initialize-release'!

initialize
	"ListWorkBook open"

	super initialize!

postBuildWith: aBuilder
	self pageHolder selectionIndexHolder
		onChangeSend: #pageChanged to: self! !

!Tools.ListWorkBook methodsFor: 'aspects'!

pageList
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^self pageHolder!

tabControl

	^(builder componentAt: #textView) widget!

textView
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^textView isNil
		ifTrue:
			[textView := String new asValue]
		ifFalse:
			[textView]! !

!Tools.ListWorkBook class methodsFor: 'utilities'!

catAllToWebPage
	"Take the directory of single web pages produced by the HTML output and concatenate them all onto 1 large web page (for printing)."
	"ListWorkBook catAllToWebPage"

	| dir in out toc hr string stream |
	dir := 'Workbook/'.
	toc := (dir, 'contents.txt') asFilename readStream.
	out := (dir, 'Siren_Workbook.html') asFilename writeStream.
	in := (dir, 'index.html') asFilename.
	hr := '
<P><HR><P>
'.
	string := in contentsOfEntireFile.
	string := string copyReplaceAll: '"./' with: '"#'.
	string := string copyReplaceAll: '.html"'with: '"'.
	out nextPutAll: string.
	[toc atEnd] whileFalse:
		[ | fil |
		fil := toc upTo: Character cr.
		stream := (dir, fil, '.html') asFilename readStream.
		stream upToAll: '<!!-- START -->'.
		out nextPutAll: hr.
		out nextPutAll: '<A NAME="', fil, '">'; cr.
		out nextPutAll: (stream upToAll: '<!!-- END -->').
		stream close.
		].
	out nextPutAll: hr.
	out nextPutAll: '</BODY></HTML>
'.
	toc close.
	out close.! !

!Siren.Swell methodsFor: 'initialize-release'!

initialize
	selector := #loudness! !

!Siren.Swell class methodsFor: 'as yet unclassified'!

example
	"Swell example"

	| roll decresc |
	roll := ((Roll length: 2000 rhythm: 100 note: 60) ampl: 80) eventList.	"100 msec = 10/sec"
	decresc := Swell new function: (LinearFunction from: #((0 1) (1 0.5))); scale: 60.
	decresc applyTo: roll.
	roll play! !

!Siren.DBLoudness methodsFor: 'converting'!

asMIDI
	"Answer a MIDI key velocity (0 to 127)"
	
	^self asRatio asMIDI!

asRatio
	"Answer the receiver as a ratio between 0 and 1."
	"-12 dB asRatio"

	^RatioLoudness new value: (10 raisedTo: (value / 20))!

positiveDB
	"Answer the zero-based positive dB value of the receiver."
	"90 dB = 32000"
	"0 = 90		-6 = 80		-12 = 70"

	^90 + (value / 0.6)! !

!Siren.DBLoudness methodsFor: 'printing'!

units
	"Answer the units string of the receiver."

	^' dB'! !

!Siren.DBLoudness methodsFor: 'double dispatch'!

differenceFromDynamic: aDyn
	"Subtract the argument from the receiver after turning both into db."

	^DBLoudness value: (self value - aDyn asDB value)!

differenceFromVelocity: aDyn
	"Subtract the argument from the receiver after turning both into db."

	^DBLoudness value: (self value - aDyn asDB value)!

productFromDynamic: aDyn
	"Multiply the argument by the receiver after turning both into db."

	^DBLoudness value: (self value * aDyn asDB value)!

productFromVelocity: aDyn
	"Multiply the argument by the receiver after turning both into db."

	^DBLoudness value: (self value * aDyn asDB value)!

quotientFromDynamic: aDyn
	"Divide the argument by the receiver after turning both into db."

	^DBLoudness value: (self value / aDyn asDB value)!

quotientFromVelocity: aDyn
	"Divide the argument by the receiver after turning both into db."

	^DBLoudness value: (self value / aDyn asDB value)!

sumFromDynamic: aDyn
	"Add the argument to the receiver after turning both into db."

	^DBLoudness value: (self value + aDyn asDB value)! !

!Siren.DBLoudness methodsFor: 'private'!

species
	"Answer Amplitude."

	^Amplitude! !

!Siren.DBLoudness class methodsFor: 'coercion'!

ddMsgName
	"Answer the selector to be used in coercing double-dispatching messages."

	^#asDB! !

!Siren.EventScheduler methodsFor: 'initializing'!

initialize
	"Set up the default Schedule"
	"Schedule initialize"

	self clients: nil.
	self running: false.
	doWait := true! !

!Siren.EventScheduler methodsFor: 'accessing'!

addClient: theApp
	"Add the argument, 'theApp', to the instance variable 'appointments'."

	appointments == nil
		ifTrue: [self setUpFirstAppointments].
	appointments add: theApp.
	self changed: #appointments!

addClient: theApp at: start
	"Add the argument, 'theApp', to the instance variable 'appointments'."

	appointments == nil
		ifTrue: [self setUpFirstAppointments].
	appointments add: (start -> theApp).
	self changed: #appointments!

addClient: theApp in: start
	"Add the argument, 'theApp', to the instance variable 'appointments'."

	appointments == nil
		ifTrue: [self setUpFirstAppointments].
	appointments add: ((Time millisecondClockValue + start asMsec value 
							+ 100 "fudge") msec
					 -> theApp).
	self changed: #appointments!

appointments: theAppointments
	"Accept the argument, 'theAppointments', as the new instance variable 'appointments'."

	appointments := theAppointments.
	self changed: appointments!

clients: theClients
	"Accept the argument, 'theClients', as the new instance variable 'clients'."

	clients := theClients.
	self changed: #clients!

dontWait
	"Specify that the scheduler should NOT do the waiting--i.e., there is a lower level of scheduling going on"
	"Schedule dontWait"

	doWait := false!

isRunning
	"Answer the instance variable 'running'."

	^running!

running: theBool
	"Accept the argument, 'theBool', as the instance variable 'running'."

	running := theBool.
"	Transcript show: 'Schedule: ', (theBool ifTrue: ['on'] ifFalse: ['off']); cr.
"	self changed: #running!

wait
	"Specify that the scheduler should do the waiting--i.e., Smalltalk-level real-time."
	"Schedule wait"

	doWait := true! !

!Siren.EventScheduler methodsFor: 'running'!

callNextAppointment
	"Calls upon the next appointment to be made and then reschedules the next one"

	| entry event thisTime nextTime time now |
	entry := appointments removeFirst.		"this is an association"
	time := entry key.						"get the delta time"
	event := entry value.					"get the event or list"
	(doWait == true)							"if running in real-time, wait"
		ifTrue: [thisTime := time msec value.
			now := Time millisecondClockValue.
			thisTime > now
				ifTrue: [(Delay forMilliseconds: (thisTime - now)) wait].
			nextTime := event scheduleAt: thisTime]		"schedule the event"
		ifFalse: [nextTime := event scheduleAt: time].	
	InputState default shiftDown
		ifTrue: [Transcript show: nextTime printString; cr].
	nextTime == nil ifFalse:					"re-add it if it returned anything"
			[appointments add: (nextTime => event)]!

flush
	"Reset the appointment list."
	"EventScheduler flush"

	appointments := SortedCollection new.
	running := false!

run
	"Set up the first meetings and then run them all till no one wants a meeting anymore."

	running ifTrue: [^nil].
	self setUpFirstAppointments.		"set start time"
	self running: true.				"set flag"
	startTime := Time millisecondClockValue.
									"as long as on"
	[(running == false) or: [appointments isEmpty]]
									"try next appointment"
		whileFalse: [self callNextAppointment].
									"cancel flag"
	self running: false!

setUpFirstAppointments
	"Create the appointment list and initialize it with the initial appointments."

	| firstTime |
	appointments == nil
		ifTrue: [appointments := SEventQueue new].
	clients == nil
		ifFalse: [clients do:
			[ :user |
			firstTime := user firstSchedule.
			appointments add: (firstTime => user)]]! !

!Siren.EventScheduler methodsFor: 'updating'!

release
	"Clean up the schedule."

	appointments := nil.
	clients := nil!

update: anAspect
	"Check if clients are waiting"

	appointments == nil
		ifFalse: [self running: false.
			appointments := nil]! !

!Siren.EventScheduler methodsFor: 'interrupting'!

interrupt
	"Stop all running schedules by brute force."

	self running: false.
	^self! !

!Siren.EventScheduler class methodsFor: 'class initialization'!

release
	"Clear away the global EventScheduler."
	"EventScheduler release"

	EventScheduler instance release.
	Schedule := nil! !

!Siren.EventScheduler class methodsFor: 'instance creation'!

clients: clientList
	"Add a client list to the newly-created Scheduler and answer it."

	^self instance clients: clientList!

initialize
	"Set up the global Scheduler."
	"EventScheduler initialize "

	Schedule == nil
		ifFalse: [self release].
	Schedule := EventScheduler new initialize.!

instance
	"EventScheduler instance"

	Schedule ifNil: [self initialize].
	^Schedule! !

!Siren.EventScheduler class methodsFor: 'interrupting'!

flush
	"Reset all running schedules by brute force."

	EventScheduler allInstances do:
		[ :thisOne |
		thisOne flush]!

interrupt
	"Stop all running schedules by brute force."

	EventScheduler allInstances do:
		[ :thisOne |
		thisOne interrupt]! !

!Siren.EventScheduler class methodsFor: 'examples'!

scheduleExample
	"Play MIDI using the event scheduler."
	"EventScheduler scheduleExample"
	"Schedule interrupt; flush"

	 | scale start |
	scale := EventList scaleExampleFrom: 36 to: 60 in: 2000.
	scale scale: #duration by: 2.
	start := Time millisecondClockValue + 100.
	scale events do: [ :ass |
		ass event voice: MIDIVoice default.
		ass key: (ass key value + start) msec.
		EventScheduler instance addClient: ass].
	"self halt."
	EventScheduler instance run!

scheduleExample2
	"Play MIDI using the event scheduler."
	"EventScheduler scheduleExample2"
	"EventScheduler interrupt; flush"

	| scale |
	scale := EventList scaleExampleFrom: 36 to: 60 in: 2000.
	scale scale: #duration by: 2.
	scale voice: MIDIVoice default.
	EventScheduler instance addClient: (Time millisecondClockValue + 500) msec -> scale.
	"self halt."
	EventScheduler instance run! !

!Siren.PortAudioPort methodsFor: 'initialize/release'!

initialize
	"Answer an initialized version of the receiver."

	| val |
	isOpen := false.
	isRunning := false.
"Set the default settings"
	rate := SoundPort sampleRate.
	format := #lin16Bit.
	outChannels := SoundPort outChannels.
	bufferSize := SoundPort blockSize.
"Create the external interface"
	interface := PortAudioInterface new.
"Initialize PA"
	val := interface pa_initialize.
	(val = 0) ifFalse: [self error: 'PortAudio init returned: ', val printString; cr].! !

!Siren.PortAudioPort methodsFor: 'accessing'!

format

	^format!

format: aValue

	format := aValue!

outChannels

	^outChannels!

outChannels: aValue

	outChannels := aValue!

rate

	^rate!

rate: aValue

	rate := aValue! !

!Siren.PortAudioPort methodsFor: 'open/close'!

close
	"Close the receiver sound port"

	| val |
	isOpen ifFalse: [^self].
	val := interface pa_close.
	interface pa_terminate.
	Transcript show: 'PortAudio close returned: ', val printString; cr.
	isOpen := false.
!

open
	"Open the receiver sound port"

	| val |
	isOpen ifTrue: [^self].
"Open the PortAudio interface."
	val := interface pa_open_p: 1 with: outChannels 
				with: (PortAudioInterface const: format) 
				with: rate with: bufferSize.
"	Transcript show: 'PortAudio open returned: ', val printString; cr."
	isOpen := true!

start
	"Start the receiver sound port"

	Transcript cr; show: 'PortAudio running...'; cr.
	interface pa_start.
	isRunning := true.!

stop
	"Stop the receiver sound port"

	isRunning ifFalse: [^self].
	interface pa_stop.
	Transcript show: 'PortAudio stopped...'; cr.
	isRunning := false! !

!Siren.PortAudioPort methodsFor: 'play/record'!

play: aSound
	"Play the argument on the receiver over the DACs."

	| samps |
	self open.
	isRunning
		ifFalse: [self start].
	samps := aSound data gcCopyToHeap.
	interface pa_play: samps with: aSound size! !

!Siren.PortAudioPort class methodsFor: 'initialize/release'!

stop
	"Stop and close the running instance"
	"PortAudioPort stop"
	"PortAudioPort instanceCount"
	"PortAudioPort someInstance"

	| inst |
	(self instanceCount > 0)
		ifTrue: [inst := self someInstance]
		ifFalse: [^self].
	inst stop.
	inst close.
	inst terminate.
	inst release.
	ObjectMemory garbageCollect! !

!Siren.PortAudioPort class methodsFor: 'examples'!

playSweep
	"Play a swept sine wave using the simple output-only port audio port."
	"PortAudioPort playSweep"

	| port snd |
	SoundPort outChannels: 2.
	SoundPort inChannels: 0.
	SoundPort sampleRate: 44100.
	SoundPort blockSize: 1024.
	port := self new initialize.
	port open.
	snd := SampledSound sweepDur: 3.0 rate: 44100 from: 40 to: 400 chans: 2.
	port play: snd.
	(snd duration value + 1) wait.
	port close.! !

!Siren.SmartPortAudioPort methodsFor: 'play/record'!

nextInBuffer
	"Get the next input buffer for the clients from the interface."!

nextOutBuffer
	"Get the next output buffer from the clients and sum it into my buffer for playing."

"Loop through the output clients asking them to contribute to the buffer."
	1 to: outClients size do:
		[ :count | | ass ind |
		ass := outClients at: count.
		ind := ass key nextBufferInto: outBuffer 
					frames: bufferSize channels: outChannels 
					format: format startingAt: ass value.
"They return their frame index or nil when done."
		ind isNil
			ifTrue: [self halt. outClients removeAtIndex: count]
			ifFalse: [ass value: ind]]!

play: aSound
	"Play the argument on the receiver over the DACs."

	| wasRunning |
	self open.
	isRunning
		ifTrue: [wasRunning := true]
		ifFalse: [wasRunning := false.
			self start].
	outClients add: (aSound -> 0).
	wasRunning ifFalse: 
		[[(aSound duration value "+ 1") wait.
		self stop] fork].! !

!Siren.SmartPortAudioPort methodsFor: 'accessing'!

inChannels

	^inChannels!

inChannels: aValue

	inChannels := aValue!

outClients

	^outClients! !

!Siren.SmartPortAudioPort methodsFor: 'initialize/release'!

initialize
	"Answer an initialized version of the receiver."

	super initialize.
	inClients := OrderedCollection new.
	outClients := OrderedCollection new.
"Set the default settings"
	inChannels := SoundPort inChannels.
"Set up semaphores and read/write loops"
	ioSemaphore := Semaphore new.
	ioProcess := [
		[ioSemaphore wait. 
		inChannels > 0 ifTrue: [self nextInBuffer]. 
		outChannels > 0 ifTrue: [self nextOutBuffer]. 
		true] whileTrue] fork.! !

!Siren.SmartPortAudioPort methodsFor: 'open/close'!

close
	"Close the receiver sound port"

	super close.
	ioProcess terminate!

open
	"Open the receiver sound port"

	| val |
	isOpen ifTrue: [^self].
"Allocate I/O buffers and put them in the heap."
	((inChannels ~= 0) and: [inBuffer isNil])
		ifTrue: [inBuffer := (WordArray new: bufferSize * inChannels withAll: 32767) 
						gcCopyToHeap].
	((outChannels ~= 0) and: [outBuffer isNil])
		ifTrue: [outBuffer := (WordArray new: bufferSize * outChannels withAll: 32767) 
						gcCopyToHeap].
"Open the PortAudio interface."
	val := interface pa_open_s: 1 with: inChannels with: outChannels 
				with: (PortAudioInterface const: format) 
				with: rate with: bufferSize 
				with: ioSemaphore with: inBuffer with: outBuffer.
"	Transcript show: 'PortAudio open returned: ', val printString; cr."
	isOpen := true! !

!Siren.SmartPortAudioPort class methodsFor: 'examples'!

test1
	"PortAudioPort test1"

	| port snd |
	port := self new initialize.
	port open.
	snd := SampledSound sweepDur: 2.0 rate: 44100 from: 10 to: 100 chans: 2.
	port outClients add: (snd -> 0).
	port nextOutBuffer.
	port inspect!

test2
	"PortAudioPort test2"

	| port snd |
	SoundPort outChannels: 1.
	SoundPort inChannels: 0.
	SoundPort sampleRate: 44100.
	SoundPort blockSize: 1024.
	port := self new initialize.
	port open.
	snd := SampledSound sweepDur: 3.0 rate: 44100 from: 40 to: 400 chans: 1.
	port play: snd! !

!Siren.PortAudioInterface methodsFor: 'types'!

OEoop
	<C: typedef void * OEoop>! !

!Siren.PortAudioInterface methodsFor: 'accessing'!

cbProcess

	^cbProcess!

cbProcess: aValue

	cbProcess := aValue!

ioSemaphore

	^ioSemaphore!

ioSemaphore: aValue

	ioSemaphore := aValue! !

!Siren.PortAudioInterface class methodsFor: 'examples'!

example1
	"Demonstrate using the PortAudioInterface with the semaphore-signalling interface"
	"PortAudioInterface example1"

	| pa val io_sem bufFrames oChans sampBuf |
	bufFrames := 1024.
	oChans := 2.
	pa := PortAudioInterface new.
"Initialize PA"
	val := pa pa_initialize.
	Transcript cr; show: 'PA_init returned: ', val printString; cr.
"Set up semaphores and read/write loops"
	io_sem := Semaphore new.
	pa ioSemaphore: io_sem.
	pa cbProcess: [[io_sem wait. Transcript show: 'PA callback'; cr. true] whileTrue] fork.
"Allocate I/O buffers."
	sampBuf := (WordArray new: bufFrames * oChans) gcCopyToHeap.
"Open PA"
	val := pa pa_open_s: 1 with: 0 with: oChans 	"device, I/O channels"
				with: (self const: #paInt16) 	"sample format"
				with: 44100 					"sample rate"
				with: bufFrames 				"frames per buffer"
				with: io_sem 					"I/O semaphore"
				with: sampBuf with: sampBuf .	"I/O buffers"
	Transcript show: 'PA_open returned: ', val printString; cr.
"START"
	1 wait.
	val := pa pa_start.
	Transcript cr; show: 'PA_start returned: ', val printString; cr.
"wait"
	1 wait.
"STOP, close, ..."
	val := pa pa_stop.
	Transcript show: 'PA_stop returned: ', val printString; cr.
	pa cbProcess terminate.
	val := pa pa_close.
	Transcript show: 'PA_close returned: ', val printString; cr.!

example2
	"Demonstrate using the PortAudioInterface with the non-semaphore-signalling interface"
	"PortAudioInterface example2"

	| pa val bufFrames oChans |
	bufFrames := 1024.
	oChans := 2.
	pa := PortAudioInterface new.
"Initialize PA"
	val := pa pa_initialize.
	Transcript cr; show: 'PA_init returned: ', val printString; cr.
"Open PA"
	val := pa pa_open_p: 1 with: 0 with: oChans 	"device, I/O channels"
				with: (self const: #paInt16) 	"sample format"
				with: 44100 					"sample rate"
				with: bufFrames. 				"frames per buffer"
	Transcript show: 'PA_open returned: ', val printString; cr.
"START"
	1 wait.
	val := pa pa_start.
	Transcript cr; show: 'PA_start returned: ', val printString; cr.
"wait"
	1 wait.
"STOP, close, ..."
	val := pa pa_stop.
	Transcript show: 'PA_stop returned: ', val printString; cr.
	val := pa pa_close.
	Transcript show: 'PA_close returned: ', val printString; cr.! !

!Siren.PortAudioInterface class methodsFor: 'class initialization'!

const: flag
	
	^PA_Constants at: flag!

initialize
	"Set up the class constants dictionary"
	"PortAudioInterface initialize"
 
	"	#define paFloat32      ((PaSampleFormat) (1<<0)) /*always available*/
		#define paInt16        ((PaSampleFormat) (1<<1)) /*always available*/
		#define paInt32        ((PaSampleFormat) (1<<2)) /*always available*/
		#define paInt24        ((PaSampleFormat) (1<<3))
	"
	PA_Constants := IdentityDictionary new.
	PA_Constants at: #paFloat32 put: 1.
	PA_Constants at: #float32 put: 1.
	PA_Constants at: #paInt16 put: 2.
	PA_Constants at: #lin16Bit put: 2.
	PA_Constants at: #paInt32 put: 4.
	PA_Constants at: #paInt24 put: 8.! !

!Siren.PitchTimeLayoutManager methodsFor: 'accessing'!

pitchOffset: aNumber
	"Set the receiver's pitch offset."

	pitchOffset := aNumber!

pitchScale: aNumber
	"Set the receiver's pitch scale."

	pitchScale := aNumber! !

!Siren.PitchTimeLayoutManager methodsFor: 'generating'!

displayPointFor: anAss
	"Answer the display point for the argument."

	^((anAss key asMS value - timeOffset / timeScale) asInteger)
		@ ((60*pitchScale) 
			- ((anAss event pitch asMIDI value - pitchOffset) * pitchScale))! !

!Siren.PositionTimeLayoutManager methodsFor: 'generating'!

displayPointFor: anAss
	"Answer the display point for the argument."

	^((anAss key asMS value - timeOffset / timeScale) asInteger)
		@ ((anAss event position - pitchOffset) * pitchScale)! !

!Siren.EventListPanel methodsFor: 'accessing'!

editorView
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^editorView isNil
		ifTrue:
			[editorView := Siren.EventListDialog new]
		ifFalse:
			[editorView]!

scoreView
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^scoreView isNil
		ifTrue:
			[scoreView := ScoreView new]
		ifFalse:
			[scoreView]!

textView
	"This method was generated by UIDefiner.  Any edits made here
	may be lost whenever methods are automatically defined.  The
	initialization provided below may have been preempted by an
	initialize method."

	^textView isNil
		ifTrue:
			[textView := Siren.EventListText new]
		ifFalse:
			[textView]! !

!Siren.MIDIPacket methodsFor: 'accessing'!

ampl
	"Answer the receiver's MIDI velocity."

	^data at: 3!

ampl: a
	"Set the receiver's MIDI velocity."

	data at: 3 put: a!

channel
	"Answer the receiver's MIDI channel."

	^(data at: 1) bitAnd: 16r0F!

command
	"Answer the receiver's MIDI command nibble."

	^(data at: 1) bitAnd: 16rF0!

data
	"Answer the receiver's 'data' byte array."

	^data!

data: anObject
	"Set the receiver's instance variable 'data' to be anObject."

	data := anObject!

duration
	"Answer the receiver's duration."

	^duration!

duration: d
	"Set the receiver's duration."

	duration := d!

key
	"Answer the receiver's MIDI key number."

	^data at: 2!

key: k
	"Set the receiver's MIDI key number."

	^data at: 2 put: k!

length
	"Answer the receiver's 'length'."

	^length!

length: anObject
	"Set the receiver's instance variable 'length' to be anObject."

	length := anObject!

pitch
	"Answer the receiver's MIDI key number."

	^data at: 2!

pitch: aValue
	"Set the receiver's MIDI key number."

	data at: 2 put: aValue!

second: aValue
	"Set the receiver's MIDI key number."

	data at: 2 put: aValue!

setPitch: p dur: d loudness: vol
	"Answer an instance of the class with the given parameters."

	self pitch: p; velocity: vol; duration: d!

status: s
	"Set the receiver's MIDI command."

	data at: 1 put: s!

third: aValue
	"Set the receiver's MIDI key velocity."

	data at: 3 put: aValue!

time
	"Answer the receiver's 'time'."

	^time!

time: anObject
	"Set the receiver's instance variable 'time' to be anObject."

	time := anObject!

vel
	"Answer the receiver's MIDI velocity."

	^data at: 3!

vel: v
	"Set the receiver's MIDI velocity."

	^data at: 3 put: v!

voice
	"Answer the receiver's MIDI channel."

	^(data at: 1) bitAnd: 16r0F!

voice: v
	"Set the receiver's MIDI channel."

	(data at: 1) put: (((data at: 1) bitAnd: 16rF0) bitOr: v)! !

!Siren.MIDIPacket methodsFor: 'printing'!

print: x on: aStream
	"Store a two-digit hexadecimal version of the 8-bit argument x on the stream."

	| digs |
	digs := '01234567890ABCDEF'.
	aStream nextPut: (digs at: ((x bitAnd: 16r00F0) bitShift: -4) + 1).
	aStream nextPut: (digs at: (x bitAnd: 16r000F) + 1)!

printOn: aStream
	"Store a readable version of the receiver on the argument."

	length ifNil: [length := data size].
	aStream nextPutAll: (MIDICommands keyAtValue: data first ifAbsent: ['unknown status']).
	aStream nextPutAll: ((time isNil
			ifFalse: [' t: ', time printString]
			ifTrue: ['']), 
		((flags isNil and: [flags ~= 0])
			ifFalse: [' f: ', flags printString]
			ifTrue: ['']), 
		' d: [').
	1 to: length do:
		[ :ind | 
		self print: (data at: ind) on: aStream.
		aStream space].
	aStream skip: -1; nextPut: $]! !

!Siren.MIDIPacket class methodsFor: 'instance creation'!

bytesFor14BitValue: val
	"Answer a two-element array with the 14-bit values that correspond to the given value."
	"val should be +- 8192"

	| int |
	val abs > 8191
		ifTrue: [self error: '14-bit value out of range.'].
	int := val + 8192.
	^Array with: (int bitAnd: 16r7F) with: ((int bitShift: -7) bitAnd: 16r7F)!

fromBytes: aByteArray
	"Answer a new instance of a MIDI packet instantiated from the given byte array."

	^super new data: aByteArray!

new
	"Answer a new instance with a 3-byte data array."

	^self new: 3!

new: size
	"Answer a new instance with a data array of the given size."

	^self fromBytes: (ByteArray new: size)!

setPitch: p dur: d loudness: vol
	"Answer an instance of the class with the given parameters."

	^self new pitch: p; velocity: vol; duration: d! !

!Siren.Cluster methodsFor: 'playing'!

eventList
	"make the cluster's events"

	| pattern |
	pattern := EventList newAnonymous.
	self events do:
		[ :anEvent |
		pattern add: (MusicEvent 
			dur: self duration
			pitch: ((anEvent isAssociation) 
					ifTrue: [anEvent value pitch]
					ifFalse: [anEvent])
			ampl: self loudness
			voice: self voice)
			at: 0 ].
	^pattern! !

!Siren.Cluster class methodsFor: 'instance creation'!

dur: aD list: anEL ampl: anA
	"return an initialized Cluster with the given list as pitches"

	^self new duration: aD; 
		events: anEL; 
		loudness: anA!

dur: aD list: anEL ampl: anA voice: aV
	"return an initialized Cluster with the given list as pitches"

	^self new duration: aD; 
		events: anEL; 
		loudness: anA; 
		voice: aV!

dur: aD pitchSet: aColl ampl: anA voice: aV
	"return an initialized Cluster with the given list as pitches"

	^self new duration: aD; 
		events: aColl; 
		loudness: anA; 
		voice: aV! !

!Siren.Cluster class methodsFor: 'examples'!

example1
	"Cluster example1"
	"play a 2 second c-major chord"

	(Cluster  dur: 2.0
		pitchSet: (#(0 4 7  12 16 19  24 28 31  36 40 43  48) collect: [ :val | val + 24])
		ampl: 100
		voice: 1) play!

example2
	"Cluster example2"
	"play a low 2 second cluster"

	(Cluster  dur: 2.0
		pitchSet: #(48  50  52  54 56)
		ampl: 100
		voice: 6) play! !

!Siren.Chord methodsFor: 'accessing'!

arity: aNumber
	"set the number of notes of the receiver chord"

	arity := aNumber!

duration: aDuration
	"set the durations of my notes"
	
	| realDuration |
	aDuration class == Duration 
		ifTrue: [ realDuration := aDuration ]
		ifFalse: [ realDuration := Duration value: aDuration ].
	events do: [ :anAssociation |
		anAssociation value duration: realDuration ].
	duration := realDuration asMS!

eventList
	"Answer the receiver's events"

	type == #major
		ifTrue: [arity = 3
			ifTrue: [^self majorTriad]
			ifFalse: [^self majorTetrad]]
		ifFalse: [arity = 3
			ifTrue: [^self minorTriad]
			ifFalse: [^self minorTetrad]].
	self error: 'Unknown chord type/arity'!

inversion: number
	"set the inversion of the receiver chord"

	inversion := number!

root: tonic
	"set the root of the receiver chord"

	root := tonic!

type: aSymbol
	"set the type symbol of the receiver chord"

	type := aSymbol! !

!Siren.Chord methodsFor: 'generating events'!

majorTetrad
	"return a three-note major chord on the given tonic in the given inversion"
	"Chord majorTetradOn: 'c3' inversion: 0"

	| me rootNote |
	me := EventList newAnonymous.
	rootNote := me defaultEventClass dur: duration pitch: root ampl: 100.
	inversion = 0 ifTrue:
		[ me add: rootNote at: 0;
			add: (rootNote copy transposeBy: 4) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0 ].
	inversion = 1 ifTrue:
		[ me add: (rootNote copy transposeBy: 4) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0;
			add: (rootNote copy transposeBy: 16) at: 0 ].
	inversion = 2 ifTrue:
		[ me add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0;
			add: (rootNote copy transposeBy: 16) at: 0;
			add: (rootNote copy transposeBy: 19) at: 0 ].
	^me!

majorTriad
	"return a three-note major chord on the given tonic in the given inversion"
	"Chord majorTriadOn: 'c3' inversion: 0"

	| me rootNote |
	me := EventList newAnonymous.
	rootNote := me defaultEventClass dur: duration pitch: root ampl: 100.
	inversion = 0 ifTrue:
		[ me add: rootNote at: 0;
			add: (rootNote copy transposeBy: 4) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0 ].
	inversion = 1 ifTrue:
		[ me add: (rootNote copy transposeBy: 4) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0 ].
	inversion = 2 ifTrue:
		[ me add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0;
			add: (rootNote copy transposeBy: 16) at: 0 ].
	^me!

minorTetrad
	"return a three-note minor chord on the given tonic in the given inversion"
	"Chord minorTetradOn: 'c3' inversion: 0"

	| me rootNote |
	me := EventList newAnonymous.
	rootNote := me defaultEventClass dur: duration pitch: root ampl: 100.
	inversion = 0 ifTrue:
		[ me add: rootNote at: 0;
			add: (rootNote copy transposeBy: 3) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0 ].
	inversion = 1 ifTrue:
		[ me add: (rootNote copy transposeBy: 3) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0;
			add: (rootNote copy transposeBy: 15) at: 0 ].
	inversion = 2 ifTrue:
		[ me add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0;
			add: (rootNote copy transposeBy: 15) at: 0;
			add: (rootNote copy transposeBy: 19) at: 0 ].
	^me!

minorTriad
	"return a three-note minor chord on the given tonic in the given inversion"
	"Chord minorTriadOn: 'c3' inversion: 0"

	| me rootNote |
	me := EventList newAnonymous.
	rootNote := me defaultEventClass dur: duration pitch: root ampl: 100.
	inversion = 0 ifTrue:
		[ me add: rootNote at: 0;
			add: (rootNote copy transposeBy: 3) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0 ].
	inversion = 1 ifTrue:
		[ me add: (rootNote copy transposeBy: 3) at: 0;
			add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0 ].
	inversion = 2 ifTrue:
		[ me add: (rootNote copy transposeBy: 7) at: 0;
			add: (rootNote copy transposeBy: 12) at: 0;
			add: (rootNote copy transposeBy: 15) at: 0 ].
	^me! !

!Siren.Chord class methodsFor: 'instance creation'!

majorTetradOn: tonic inversion: inversion
	"return a three-note major chord on the given tonic in the given inversion"
	"Chord majorTetradOn: 'c3' inversion: 0"

	| me |
	me := self newAnonymous.
	me root: tonic;
		inversion: inversion;
		type: #major;
		arity: 4.
	^me!

majorTriadOn: tonic inversion: inversion
	"return a three-note major chord on the given tonic in the given inversion"
	"Chord majorTriadOn: 'c3' inversion: 0"

	| me |
	me := self newAnonymous.
	me root: tonic;
		inversion: inversion;
		type: #major;
		arity: 3.
	^me!

minorTetradOn: tonic inversion: inversion
	"return a three-note minor chord on the given tonic in the given inversion"
	"Chord minorTetradOn: 'c3' inversion: 0"

	| me |
	me := self newAnonymous.
	me root: tonic;
		inversion: inversion;
		type: #minor;
		arity: 4.
	^me!

minorTriadOn: tonic inversion: inversion
	"return a three-note minor chord on the given tonic in the given inversion"
	"Chord minorTriadOn: 'c3' inversion: 0"

	| me |
	me := self newAnonymous.
	me root: tonic;
		inversion: inversion;
		type: #minor;
		arity: 3.
	^me! !

!Siren.Chord class methodsFor: 'examples'!

example
	"Create and edit some chords."

	"(Chord majorTriadOn: 'c3' inversion: 0) inspect"

	"((Chord majorTetradOn: 'f2' inversion: 2) duration: 1.0) inspect"

	((Chord majorTetradOn: 'g5' inversion: 1) duration: 1.0) edit! !

!Siren.Roll methodsFor: 'initialize'!

length: aLength rhythm: aDuration note: aNote
	"make a new roll of the specified length..."

	delta := aDuration.
	noteDuration := aDuration.
	aLength class == Float "it's seconds"
		ifTrue: [number := aLength * 1000 / aDuration]
		ifFalse: [number := aLength / aDuration].
	self pitch: (Pitch value: aNote).
	^self!

number: aNumber rhythm: aDuration note: aNote
	"make a new roll with the specified number of notes..."

	delta := aDuration.
	number := aNumber.
	noteDuration := aDuration.
	self pitch: (Pitch value: aNote).
	^self! !

!Siren.Roll methodsFor: 'accessing'!

duration
	"computer the duration"

	^duration == nil 
		ifFalse: [ duration ]
		ifTrue: [ number * noteDuration ]! !

!Siren.Roll methodsFor: 'playing'!

eventList
	"return an eventList for me"

	| newList |
	newList := EventList newAnonymous.
	0 to: number do:
		[ :count |
			newList add: (self defaultEventClass 
					dur: noteDuration pitch: self pitch ampl: self loudness) 
				at: (delta * count) ].
	^newList! !

!Siren.Roll class methodsFor: 'instance creation'!

length: aNumber rhythm: aDuration note: aNote
	"return a new roll of the specified length..."

	^self new length: aNumber rhythm: aDuration note: aNote!

number: aNumber rhythm: aDuration note: aNote
	"return a new roll with the specified number of events..."

	^self new number: aNumber rhythm: aDuration note: aNote! !

!Siren.Roll class methodsFor: 'examples'!

rollExample
	"Create and edit/play a few rolls."

	"((Roll length: 2000 rhythm: 100 note: 48) ampl: 100) eventList"
	"((Roll length: 2000 rhythm: 100 note: 48) ampl: 100) eventList edit"
	((Roll length: 8000 rhythm: 50 note: 60) ampl: 80) eventList play.
"	MIDIPort allNotesOff."! !

!Siren.Trill methodsFor: 'playing'!

eventList
	"return an eventList for me"

	| newList |
	newList := EventList newAnonymous.
	0 to: number do:
		[ :count |
			newList add: (self defaultEventClass 
					dur: noteDuration 
					pitch: (self pitch value at: ((count \\ self pitch value size) + 1)) 
					ampl: self loudness) 
				at: (delta * count) ].
	^newList! !

!Siren.Trill class methodsFor: 'instance creation'!

length: aNumber rhythm: aDuration note: aNote
	"return a new roll of the specified length..."

	^self new length: aNumber rhythm: aDuration note: aNote!

length: aNumber rhythm: aDuration notes: aNote
	"return a new trill of the specified length..."

	^self new length: aNumber rhythm: aDuration note: aNote! !

!Siren.Arpeggio methodsFor: 'accessing'!

delay: aValue
	"set the delay between the onset of my notes"

	delay := aValue.
	self setDelays.! !

!Siren.Arpeggio methodsFor: 'playing'!

edit
	"edit me"

"	HauerSteffensView openWith8ButtonsAndHAndVScrollingOn: self"!

eventList
	"return my event list"

	^events!

play
	"don't expand me"

	^self playAt: 0! !

!Siren.Arpeggio methodsFor: 'private'!

setDelays
	"set the start times of my notes"

	| counter |
	counter := 0.
	events do:
		[ :anAss |
		anAss key: (MSecondDuration value: (anAss key value + (counter * delay))).
		counter := counter + 1]! !

!Siren.Arpeggio class methodsFor: 'instance creation'!

on: aChordOrList delay: aDelay
	"create an Arpeggio on the given chord or event list"

	| me |
	me := self newAnonymous.
	me events: aChordOrList events copy.
	me delay: aDelay.
	^me! !

!Siren.Sharpness methodsFor: 'private'!

species
	"Answer Pitch."

	^Pitch! !

!Siren.Sharpness class methodsFor: 'examples'!

example
	"Demonstrate the use of an OrdinalMagnitude with a scale of sharpness."
	"Sharpness example"

	| a b c d e f |		"define six values with names."
	a := Sharpness value: #soft.
	b := Sharpness value: #nasal.
	c := Sharpness value: #round.
	d := Sharpness value: #high.
	e := Sharpness value: #loud.
	f := Sharpness value: #shrill.
					"put them in some order."
	a << b.	b << c.	c << d.	d << e.	e << f.
					"now ask some queries and let them extrapolate."
	Transcript show: '(f > e) ', (f > e) printString; cr.
	Transcript show: '(f > b) ', (f > b) printString; cr.
	Transcript show: '(f =? f) ', (f =? f) printString; cr.
	Transcript show: '(f =? a) ', (f =? a) printString; cr.
	Transcript show: '(f >= a) ', (f >= a) printString; cr.
	Transcript show: '(d >= b) ', (d >= b) printString; cr.
	Transcript show: '(b >= d) ', (b >= d) printString; cr.
	Transcript show: '(f < a) ', (f < a) printString; cr.
	Transcript show: '(a < f) ', (a < f) printString; cr.
	Sharpness values inspect! !

!Siren.FourierSummation methodsFor: 'initialize-release'!

initialize: size
	"Initialize the receiver for the given size."

	super initialize: size.
	domain := -1.0 to: 1.0! !

!Siren.FourierSummation methodsFor: 'accessing'!

add: a3DPoint
	"Add the argument to the receiver's point collection."

	| y |
	data add: a3DPoint.
	y := a3DPoint y.
	y < domain first
		ifTrue: [self domain: (y to: domain last)].
	y > domain last
		ifTrue: [self domain: (domain first to: y)].!

at: theIndex
	"Answer the value from my array--assume an index in the range 0 to 1"

	myArray == nil
		ifTrue: [self computeCurve "^self computeValueAt: theIndex"].
	^myArray at: ((theIndex * myArray size) truncated max: 1)! !

!Siren.FourierSummation methodsFor: 'computing'!

computeCurve
	"Compute the block by sine summation--set the values in the cached array."

	| width twoPi scale |
	width := self class defaultSize.
	myArray := Array new: width.
	twoPi := 2.0 * Float pi.
	scale := twoPi / width.
	1 to: width do:
		[ :x |
		myArray at: x put: (self computeValueAt: (x * scale))].!

computeValueAt: anIndex
	"Compute the answer by sine summation."
	"(Fourier from: #((1 1 0))) computeValueAt: (Float pi / 4)"

	| value twoPi |
	twoPi := 2.0 * Float pi.
	value := 0.
	data do:
		[ :aZPoint |		"each point is (number @ amplitude @ phase)"
		value := value + (((anIndex * aZPoint x) asFloat
						+ (aZPoint z asFloat / 360.0 * twoPi)) sin
					* aZPoint y asFloat)].
	^value! !

!Siren.FourierSummation class methodsFor: 'instance creation'!

default
	"Answer a default instance of the receiver class."

	^FourierSummation from: #((1 0.5 0) (3 0.3 0) (5 0.2 0) 
							(7 0.17 0) (9 0.15 0) (11 0.1 0))!

from: anArrayOfZPoints
	"Answer a sum-of-sines function with the given points."

	| me |
	me := self ofSize: anArrayOfZPoints size.
	anArrayOfZPoints do: 
			[ :anArray | 
			me add: (anArray first @ (anArray at: 2) @ anArray last)].
	^me! !

!Siren.FourierSummation class methodsFor: 'examples'!

fourierExample
	"Make a Sine summation that approaches a square wave"
	"FourierSummation fourierExample"

	| fcn |
	fcn := 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)).
	Transcript show: (fcn at: 0.14) printString; cr.
	fcn inspect!

fourierViewExample
	"Make a Sine summation that approaches a square wave and open a view on it."
	"FourierSummation fourierViewExample"

	FunctionView onFunction: (FourierSummation from: #((1 0.5 0) (3 0.3 0) (5 0.2 0) 
							(7 0.17 0) (9 0.15 0) (11 0.1 0)))! !

!Siren.SEventQueue methodsFor: 'accessing'!

add: eventAss
	"Add the given eventAssociation to the receiver in the right place."

	| key which |
	key := eventAss key asMsec value.
	which := self locateSlotFor: key.
	which key: key.
	which value: eventAss value.!

do: aBlock
	"Refer to the comment in Collection|do:."

	| item |
	self isEmpty ifTrue: [^nil].
	item := first.
		[aBlock value: item. 
		item := item next.
		item notNil] whileTrue.!

isEmpty

	^first isNil!

removeFirst
	"Remove and answer the first item from the list."

	| item |
	first ifNil: [^nil].
	item := first.
	first := first next.
	^item!

size
	"Answer how many elements the receiver contains."

	| item tally |
	self isEmpty
		ifTrue: [^0].
	tally := 0.
	item := first.
	[item := item next notNil] whileTrue: [tally := tally + 1].
	^ tally! !

!Siren.SEventQueue methodsFor: 'private'!

locateSlotFor: start
	"Find the proper slot for inserting a new event with the given start time."

	| new item |
	new := ScheduleRecord new.
	first ifNil:
		[first := last := new.
		^new].
"If it's before the first scheduled event, prepend."
	first start >= start
		ifTrue: [first previous: new.
			new next: first.
			first := new.
			^new].
"If it's after the last scheduled event, append."
	last start <= start
		ifTrue: [last next: new.
			new previous: last.
			last := new.
			^new].
"Else do linear search and then insert."
	item := first.
	[item key < start]
		whileTrue: [item := item next].
	item previous next: new.
	new previous: item previous.
	item previous: new.
	new next: item.
	^new! !

!Siren.DisplayListView methodsFor: 'initialize-release'!

component
	"Answer a DLView for use as a component."
	"IndentedListLayoutManager listExample display"

	| part wrapper |
	part := CompositePart new.
	wrapper := LookPreferences edgeDecorator on: self.
	wrapper useHorizontalScrollBar; noMenuBar.
	part add: wrapper in: (0@0 extent: 1.0@1.0).
	^part!

initialize
	"Initialize the receiver's instance variables--the default is to cache."

	super initialize.
"	page := 0@0. -- unused"
	zoom := nil.
	backgroundColor := self class backgroundColor.
	redrawn := false.
"	InputState default shiftDown
		ifFalse: [cache := true]
		ifTrue: [cache := false]
"	cache := false!

initializeCache
	"Initialize the receiver's instance variables for cacheing the pixmap during display."

	| ext |
	cache := true.
	ext := displayList extent truncated.
	ext x: (ext x min: 2048).
	ext y: (ext y min: 2048).
	pixmap := Pixmap extent: ext	 "self class defaultCacheExtent".
	pixmap background: self backgroundColor.
	page := 0@0!

initializeWithGrid: gPoint color: gColor
	"Initialize the receiver's instance variables--the default is to cache."

	| cacheE bGC |
	self initialize.
	self initializeCache.
	cacheE := self class defaultCacheExtent.
	background := Pixmap extent: cacheE.
	bGC := background graphicsContext.
	bGC paint: DisplayListView backgroundColor.
	bGC displayRectangle: background bounds.
	bGC paint: gColor.
	0 to: cacheE x by: gPoint x do:
		[ :x |
		bGC displayLineFrom: (x@0) to: (x @ cacheE y)].
	0 to: cacheE y by: gPoint y do:
		[ :y |
		bGC displayLineFrom: (0@y) to: (cacheE x @ y)].!

release
	"Remove the receiver as a dependent of its model."

	model release.
	model removeDependent: self.
	model := nil.
	super release! !

!Siren.DisplayListView methodsFor: 'accessing'!

background: anObject
	"Set the receiver's 'background' to be anObject."

	background := anObject!

backgroundColor
	"Answer the receiver's backgroundColor or the default."

	^backgroundColor == nil
		ifTrue: [self class backgroundColor]
		ifFalse: [backgroundColor]!

bounds: aRectangle
	"Set the receiver's bounds (and page offset)."

	super bounds: aRectangle.
	page := 0@0.!

cache: aBoolean
	"Set the receiver's 'cache' to be aBoolean."

	cache := aBoolean!

displayList
	"Answer the receiver's display list."

	^displayList!

displayList: anObject
	"Set the receiver's 'displayList' to be anObject."

	displayList := anObject.
	cache ifTrue: [self initializeCache] ifFalse: [pixmap := nil]!

foregroundColor
	"Answer the receiver's foregroundColor or the default."

	^foregroundColor == nil
		ifTrue: [self class foregroundColor]
		ifFalse: [foregroundColor]!

list
	"Answer the receiver's display list."

	^displayList!

pageOffset
	"Answer the receiver's 'pageOffset'."

	^page!

pageOffset: anObject
	"Set the receiver's 'pageOffset' to be anObject."

	page := anObject!

pixmap: aPMorNil
	"Set (or destroy) the receiver's cached pixmap."

	pixmap := aPMorNil!

preferredBounds
	"Answer the displayList's bounds."

	| rect |
	displayList == nil
		ifTrue: [^super preferredBounds].
	rect := displayList preferredBounds.
	^zoom == nil
		ifTrue: [(rect origin) extent: (rect extent + (self class inset * 2))]
		ifFalse: [((rect origin) extent: (rect extent + (self class inset * 2))) scaledBy: zoom]!

zoom: aPoint
	"Set the receiver's 'zoom' to be aPoint."

	zoom := aPoint.
	(zoom == (1@1))
		ifTrue: [zoom := nil].
	self invalidate! !

!Siren.DisplayListView methodsFor: 'controller access'!

defaultControllerClass

	^DisplayListController!

transformPoint: aPoint
	"zoom, scroll, and page the given point, used for hit-detection."

	^zoom == nil
		ifTrue: [aPoint "- scrollOffset value - (16@16)"]
		ifFalse: [aPoint "- scrollOffset value" / zoom]! !

!Siren.DisplayListView methodsFor: 'displaying'!

displayOn: aGraphicsContext

	cache ifFalse: [aGraphicsContext paint: backgroundColor.
				aGraphicsContext displayRectangle: aGraphicsContext clippingBounds].
	self class inset == nil
		ifFalse: [aGraphicsContext translateBy: self class inset].
	pixmap == nil
		ifTrue: [zoom == nil
			ifTrue: [displayList displayOn: aGraphicsContext]
			ifFalse: [(displayList scaledBy: zoom) displayOn: aGraphicsContext]]
		ifFalse: [redrawn == nil ifTrue: [self displayOnImage].
				pixmap displayOn: aGraphicsContext].
	((model notNil) and: [model selection notNil])
		ifTrue: [self displaySelectionOn: aGraphicsContext].
	self class inset == nil
		ifFalse: [aGraphicsContext translateBy: self class inset negated].!

displaySelectionOn: aGC
	"Display the model's selection using some form of highlighting."

	| sel bound bRect wRect |
	sel := model selection.
	sel == nil ifTrue: [^self].
	bRect := (-3@-3) extent: (6@6).
	wRect := (-2@-2) extent: (4@4).
	bound := sel bounds.
	#(topLeft topRight bottomRight bottomLeft) do:
		[ :cor | | pt |
		pt := bound perform: cor.
		aGC paint: ColorValue black.
		aGC displayRectangle: bRect at: pt.
		aGC paint: ColorValue white.
		aGC displayRectangle: wRect at: pt]!

invalidate

	redrawn := nil.
	super invalidate! !

!Siren.DisplayListView methodsFor: 'private'!

scrollableExtent
	"Answer the extent of the receiver's display object bounding box."

	^zoom == nil
		ifTrue: [super scrollableExtent]
		ifFalse: [super scrollableExtent * zoom]!

visibleExtent
	"Answer the extent of the receiver's clipping box."

	^zoom == nil
		ifTrue: [super visibleExtent]
		ifFalse: [super visibleExtent * zoom]! !

!Siren.DisplayListView class methodsFor: 'instance creation'!

componentOnList: dlist
	"Answer a DLView for use as a component."
	"IndentedListLayoutManager listExample display"

	| container me wrapper dled |
	container := CompositePart new.
	dled := DisplayListEditor on: dlist.
	me := self on: dled.
	wrapper := LookPreferences edgeDecorator on: me.
	wrapper useHorizontalScrollBar; noMenuBar.
	container add: wrapper in: (0@0 extent: 1.0@1.0).
	^container!

componentOnList: dlist withGrid: gPoint inColor: gColor
	"Answer a DLView for use as a component."

	| container me wrapper dled |
	container := CompositePart new.
	dled := DisplayListEditor on: dlist.
	gPoint == nil
		ifFalse: [me := self new initialize: dlist withGrid: gPoint color: gColor]
		ifTrue: [me := self new initialize].
	me displayList: dled list.
	me model: dled.
	wrapper := LookPreferences edgeDecorator on: me.
	wrapper useHorizontalScrollBar; noMenuBar.
	container add: wrapper
			borderedIn: (0@0.1 extent: 1.0@0.9).
	^container!

on: anEditor

	| me |
	me := self new initialize.
	me displayList: anEditor list.
	me model: anEditor.
	^me!

open4SquareOn: aDisplayList
	"Open the receiver on the given display list."
	"DisplayListView open4SquareOn: (DisplayList rectanglesX: 2000 byY: 2000)"

	| dls |
	dls := DisplayListSubcanvas new.
	dls setDisplayList: aDisplayList.
	dls setComponent: (self componentOnList: aDisplayList).
	DisplayListSubcanvas openOn: dls withSpec: #windowSpec4Square!

openOn: aDisplayList
	"Open the receiver on the given display list."
	"DisplayListView openOn: (DisplayList rectanglesX: 2000 byY: 2000)"

	| dls |
	dls := DisplayListSubcanvas new.
	dls setDisplayList: aDisplayList.
	dls setComponent: (self componentOnList: aDisplayList).
self halt.
	DisplayListSubcanvas openOn: dls! !

!Siren.DisplayListView class methodsFor: 'constant access'!

backgroundColor
	"Answer the default background color for display list views."
	"ColorValue constantNames #(#black #blue #brown #chartreuse #cyan #darkCyan #darkGray #darkGreen #darkMagenta #darkRed #olive #gray #green #lightYellow #lightGray #magenta #navy #orange #orchid #paleGreen #pink #purple #red #royalBlue #salmon #lightCyan #springGreen #veryDarkGray #veryLightGray #white #yellow )"

	^"LookPreferences defaultForWindows backgroundColor" 
	ColorValue lightYellow!

constants

	^MusicConstants!

constants: aDictionary

	MusicConstants := aDictionary!

defaultCacheExtent
	"Answer the default extent of the receiver's instances' cache."

	^600@600!

foregroundColor
	"Answer the default foreground color for display list views."

	^LookPreferences defaultForWindows foregroundColor!

inset
	"Answer the default display inset."

	^8@8! !

!Siren.DisplayListView class methodsFor: 'examples'!

classListExample
	"DisplayListView classListExample"

	IndentedListLayoutManager listExample display!

classTreeExample
	"DisplayListView classTreeExample"

	(IndentedTreeLayoutManager listExample) display!

colorClassListExample
	"DisplayListView colorClassListExample"

	IndentedListLayoutManager colorListExample!

exampleHierarchical
	"DisplayListView exampleHierarchical"

	DisplayList exampleHierarchical display! !

!Siren.DisplayListView class methodsFor: 'class initialization'!

initialize
	"Init the class"
	"DisplayListView initialize"

	MusicConstants := IdentityDictionary new! !

!Siren.TimeSequenceView methodsFor: 'initialize-release'!

initialize
	"Initialize the instance variables of the receiver."

	super initialize.
	xScaleDefault := 15.
	xScale := 15.
	page := 0@0! !

!Siren.TimeSequenceView methodsFor: 'accessing'!

cachedPixmap
	"Answer the receiver's cached pixmap."

	^pixmap!

clefColor
	"Answer the receiver's 'clefColor'."

	^clefColor!

clefColor: anObject
	"Set the receiver's 'clefColor' to be anObject."

	clefColor := anObject!

clefForm
	"Answer the receiver's clef form."

	^clefForm!

clefWidth
	"Answer the receiver's clef form width."

	^clefForm width!

headColor
	"Answer the receiver's 'headColor'."

	^headColor!

headColor: anObject
	"Set the receiver's 'headColor' to be anObject."

	headColor := anObject!

xColor
	"Answer the receiver's 'xColor'."

	^xColor!

xColor: anObject
	"Set the receiver's 'xColor' to be anObject."

	xColor := anObject!

xScale
	"Answer the receiver's 'xScale'."

	^xScale!

xScale: anObject
	"Set the receiver's 'xScale' to be anObject."

	xScale := anObject!

yColor
	"Answer the receiver's 'yColor'."

	^yColor!

yColor: anObject
	"Set the receiver's 'yColor' to be anObject."

	yColor := anObject! !

!Siren.TimeSequenceView methodsFor: 'layout manager access'!

generateItemFor: anAss
	"Answer the correct visual item for the given assoc. (sent from layoutManagers)."

	| dItem vox arr |
	arr := MusicConstants at: #NoteIcons.
	vox := anAss value voice.
	vox == nil 
		ifTrue: [vox := 1] 
		ifFalse: [vox isInteger
			ifTrue: [vox := (vox max: 1) min: arr size]
			ifFalse: [vox := vox first asInteger \\ arr size]].	"hack -- take the first letter as the key for note heads"
	dItem := (arr at: (vox )) copy.
	dItem color: (headColor == nil
				ifTrue: [ColorValue white]
				ifFalse: [headColor]).
	dItem model: anAss.
	^dItem!

itemFor: anAss
	"Answer the correct visual item for the given assoc. (sent from layoutManagers)."

	itemAccessors == nil
		ifTrue: [^self generateItemFor: anAss].
	itemAccessors isSymbol
		ifTrue: [^self perform: itemAccessors with: anAss].
	(itemAccessors isMemberOf: BlockClosure)
		ifTrue: [^itemAccessors value: anAss].
	(itemAccessors isMemberOf: Dictionary)
		ifTrue: [^self perform: (itemAccessors at: anAss value voice ifAbsent: [0]) 
						with: anAss].!

layoutManager
	"Answer a default layout manager for the receiver."

	^(TimeSequenceLayoutManager new)
			timeScale: xScale;
			timeOffset: 0! !

!Siren.TimeSequenceView methodsFor: 'controller access'!

defaultControllerClass
	"Answer the class of the default controller for the receiver."

	^TimeSequenceController! !

!Siren.TimeSequenceView methodsFor: 'transforming'!

formXFromTime: aTime
	"take a note's time and return a display point x coordinate relative to the form."

	^((aTime - (page x) / xScale) asInteger)!

timeFromX: aNumber
	"Answer an event list time from the argument."

	^(aNumber x - self class inset x - self bounds width * xScale + page x) max: 0! !

!Siren.TimeSequenceView methodsFor: 'displaying'!

displayOn: aGC
	"Display the receiver on the argument."

	aGC paint: self backgroundColor.
	aGC displayRectangle: aGC clippingBounds.
	pixmap == nil
		ifFalse: [(redrawn == true)
			ifFalse: [ | pGC |
				redrawn := true.
				pGC := pixmap graphicsContext.
				self reallyDisplayOn: pGC].
			pixmap displayOn: aGC]
		ifTrue: [self reallyDisplayOn: aGC]!

displayOnImage
	"Display the receiver on its cached form."

	| pGC |
	redrawn := true.
	pGC := pixmap graphicsContext.
	pGC paint: self backgroundColor.
	pGC displayRectangle: pixmap bounds.
	pGC paint: self foregroundColor.
	pGC clippingRectangle: pixmap bounds.
	self class inset == nil
		ifFalse: [pGC translateBy: self class inset].
"	clefForm == nil 
		ifFalse: [clefForm displayOn: pGC.
				pGC translateBy: clefForm width@0].
"	background == nil ifFalse: [background displayOn: pGC].
	displayList displayOn: pGC.
	model selection == nil
		ifFalse: 
			[controller sensor shiftDown ifTrue: [self halt].
			pGC displayRectangularBorder: controller selection bounds].!

highlightFrom: pt1 to: pt2
	"Show selection of the range pt1--pt2."

	| gc |
	gc := self graphicsContext.
	gc paint: DisplayList foregroundColor.
	gc displayRectangularBorder: (pt1 corner: pt2)!

highlightTime: value
	"Show selection of the time value value."

"	| x |
	x := (value / xScale - page x - cachedForm offset x 
				+ insetDisplayBox left + self class inset x) min: (insetDisplayBox right).
	(x > (insetDisplayBox right - 64))
		ifTrue: [self scroll: ((insetDisplayBox width - 128) negated@0).
			x := x - (insetDisplayBox width - 128)].
	Display fill: ((x @ insetDisplayBox top)
			extent: (2 @insetDisplayBox height))
		rule: Form reverse
		mask: Form black."!

reallyDisplayOn: gc
	"Display the receiver on its cached form."

	gc paint: self backgroundColor.
	gc displayRectangle: gc clippingBounds.
	gc paint: self foregroundColor.
	self class inset == nil
		ifFalse: [gc translateBy: self class inset].
	clefForm == nil 
		ifFalse: [clefForm displayOn: gc.
				gc translateBy: clefForm width@0].
	background == nil ifFalse: [background displayOn: gc].
	displayList displayOn: gc.
	model selection == nil
		ifFalse: 
			[controller sensor shiftDown ifTrue: [self halt].
			gc displayRectangularBorder: controller selection bounds].!

zoom: aPoint
	"Set the receiver's 'zoom' to be aPoint."

	xScale := xScaleDefault * aPoint x.
	Cursor wait showWhile:
		[self displayList: (self layoutManager displayListFor: self).
		super zoom: aPoint]! !

!Siren.TimeSequenceView class methodsFor: 'constant access'!

defaultCacheExtent
	"Answer the default extent of the receiver's instances' cache."

	^600@200!

inset
	"Answer the default display inset."

	^16@16! !

!Siren.TimeSequenceView class methodsFor: 'examples'!

randomExample
	"Open a sequence view on a random event list."
	"TimeSequenceView randomExample"

	TimeSequenceView openOnEventList: EventList randomExample! !

!Siren.TimeSequenceView class methodsFor: 'instance creation'!

openOnEventList: anEventList
	"Open the receiver on the given event list."
	"TimeSequenceView openOnEventList: EventList randomExample"

	| me dls |
	me := self new initialize model: (EventListEditor new eventList: anEventList).
	me displayList: (me layoutManager displayListFor: me).
	me initializeCache.
	dls := DisplayListSubcanvas new.
	dls setDisplayList: me displayList.
	dls setComponent: me component.
	DisplayListSubcanvas openOn: dls! !

!Siren.PitchTimeView methodsFor: 'initialize-release'!

initialize
	"Initialize the instance variables of the receiver."

	super initialize.
	xScale := 12.
	xScaleDefault := xScale.
	yScale := 6.
	yScaleDefault := yScale.
	pitchOffset := 24! !

!Siren.PitchTimeView methodsFor: 'layout manager access'!

generateItemFor: anAss
	"Answer the correct visual item for the given assoc. (sent from layoutManagers)."
	"ColorValue constantNames"
	"EventList randomSWSSExample2 edit"

	^super generateItemFor: anAss.
"
	| yHeight x y list event |
	event := anAss value.
	yHeight := 24.
	event duration == nil
			ifTrue: [x := 250 / xScale]
			ifFalse: [x := event duration asMS value / xScale].
	((event ampl == nil) or: [event ampl value == nil])
		ifFalse: [y := (event ampl asRatio value * yHeight) truncated]
		ifTrue: [y := 0].
	list := (DisplayList new) model: anAss.
	list add: (super generateItemFor: anAss).
	list add: ((DisplayLine new)
				offset: 0@0;
				extent: x@0;
				color: (xColor == nil ifTrue: [ColorValue cyan] ifFalse: [xColor])).
	list add: ((DisplayLine new)
				offset: 0@0;
				extent: 0@y negated;
				color: (yColor == nil ifTrue: [ColorValue red] ifFalse: [yColor])).
	(event at: #envelope) == nil
		ifFalse: [ | fcn points | fcn := event at: #envelope.
				points := fcn data collect: [ :pt | (pt x * x) @ (pt y * y negated)].
				list add: (DisplayPolyline offset: (1@0) vertices: points)].
	(event at: #position) == nil
		ifFalse: [list add: ((DisplayLine new)
				offset: 2@0;
				extent: 0@((event at: #position) - 0.5 * yHeight);
				color: (yColor == nil ifTrue: [ColorValue green] ifFalse: [yColor]))].
	(event at: #duty) == nil
		ifFalse: [list add: ((DisplayLine new)
				offset: 0@2;
				extent: ((event at: #duty) * x)@0;
				color: (yColor == nil ifTrue: [ColorValue yellow] ifFalse: [yColor]))].
	^list
"!

itemFor: anAss
	"Answer the correct visual item for the given assoc. (sent from layoutManagers)."
	"ColorValue constantNames "
	"PitchTimeView openOnEventList: (EventList scaleExampleFrom: 48 to: 84 in: 10000)"

	| yHeight x y list event it |
	event := anAss value.
	yHeight := 24.
	event duration == nil
			ifTrue: [x := 250 / xScale]
			ifFalse: [x := event duration asMS value / xScale].
	((event ampl == nil) or: [event ampl value == nil])
		ifFalse: [y := (event ampl asRatio value * yHeight) truncated]
		ifTrue: [y := 0].
	list := (DisplayList new) model: anAss.
	it := super itemFor: anAss.
	it == nil ifFalse: [list add: it].
	list add: ((DisplayLine new)
				offset: 0@-2;
				extent: x@0;
				color: (xColor == nil ifTrue: [ColorValue blue] ifFalse: [xColor])).
	list add: ((DisplayLine new)
				offset: 0@0;
				extent: 0@y negated;
				color: (yColor == nil ifTrue: [ColorValue red] ifFalse: [yColor])).
	(event at: #envelope) == nil
		ifFalse: [ | fcn points | fcn := event at: #envelope.
				points := fcn data collect: [ :pt | (pt x * x) @ (pt y * y negated)].
				list add: (DisplayPolyline offset: (1@0) vertices: points)].
	(event at: #position) == nil
		ifFalse: [list add: ((DisplayLine new)
				offset: 2@0;
				extent: 0@((event at: #position) - 0.5 * yHeight);
				color: (yColor == nil ifTrue: [ColorValue green] ifFalse: [yColor]))].
	^list!

layoutManager
	"Answer a default layout manager for the receiver."

	^(PitchTimeLayoutManager new)
			timeScale: xScale;
			timeOffset: page x;
			pitchScale: yScale;
			pitchOffset: pitchOffset! !

!Siren.PitchTimeView methodsFor: 'controller access'!

defaultControllerClass
	"Answer the class of the default controller for the receiver."

	^PitchTimeController! !

!Siren.PitchTimeView methodsFor: 'transforming'!

pitchFromY: aPoint
	"Answer the transformed pitch represented by the y-coordinate of the argument."

	^(aPoint y - self class inset y / yScale + pitchOffset) max: 0! !

!Siren.PitchTimeView methodsFor: 'displaying'!

highlightFrom: pt1 to: pt2
	"Show selection of the range pt1--pt2"

"	pt1 x = pt2 x
		ifTrue: [^self].
	Display border: (pt1 corner: pt2)
		width: 2
		mask: Form gray."!

zoom: aPoint
	"Set the receiver's 'zoom' to be aPoint."

	xScale := xScaleDefault * aPoint x.
	yScale := yScaleDefault * aPoint y.
	Cursor wait showWhile:
		[self displayList: (self layoutManager displayListFor: self).
		super zoom: aPoint]! !

!Siren.PitchTimeView class methodsFor: 'instance creation'!

label
	"Answer the view label for the receiver's instances."

	^'Pitch-Time Editor'! !

!Siren.PitchTimeView class methodsFor: 'subview creation'!

addButtonsOn: me for: dled to: container in: rect
	"Add the default button panel to the instance in the given rectangle."

	| x0 y0 x1 y1 |
	x0 := rect left.	x1 := rect right.
	y0 := rect top.	y1 := rect bottom.
	self addButtonOn: me labeled: 'Play'
		doing: [:model :value | dled play] 
		in: (LayoutFrame new leftFraction: 0; topFraction: y0; 
							rightFraction: 0.12;	bottomFraction: y1) 
		to: container.
	self addButtonOn: me labeled: 'File'
		doing: [:model :value | dled doFile] 
		in: (LayoutFrame new leftFraction: 0.12; topFraction: y0; 
							rightFraction: 0.25;	bottomFraction: y1) 
		to: container.
	self addButtonOn: me labeled: 'Edit'
		doing: [:model :value | dled doEdit] 
		in: (LayoutFrame new leftFraction: 0.25;	topFraction: y0; 
							rightFraction: 0.37;	bottomFraction: y1) 
		to: container.
	self addButtonOn: me labeled: 'Inspect'
		doing: [:model :value | dled doInspect] 
		in: (LayoutFrame new leftFraction: 0.37;	topFraction: 0.0; 
							rightFraction: 0.5;	bottomFraction: y1) 
		to: container.
	self addButtonOn: me labeled: 'Display'
		doing: [:model :value | dled doDisplay] 
		in: (LayoutFrame new leftFraction: 0.5;	topFraction: y0; 
							rightFraction: 0.62;	bottomFraction: y1) 
		to: container.
	self addButtonOn: me labeled: 'Zoom'
		doing: [:model :value | dled doZoom] 
		in: (LayoutFrame new leftFraction: 0.62;	topFraction: y0; 
							rightFraction: 0.75;	bottomFraction: y1) 
		to: container.
	self addButtonOn: me labeled: 'View'
		doing: [:model :value | dled doViewEdit] 
		in: (LayoutFrame new leftFraction: 0.75;	topFraction: y0; 
							rightFraction: 0.87;	bottomFraction: y1) 
		to: container.
	self addButtonOn: me labeled: 'Properties'
		doing: [:model :value | dled doPropEdit] 
		in: (LayoutFrame new leftFraction: 0.87;	topFraction: y0; 
							rightFraction: 1.0;	bottomFraction: y1) 
		to: container.! !

!Siren.PitchTimeView class methodsFor: 'examples'!

randomExample
	"Open a p-t view on a random event list."
	"PitchTimeView randomExample"

	PitchTimeView openOnEventList: EventList randomExample!

randomExampleLong
	"Open a p-t view on a random event list."
	"PitchTimeView randomExampleLong"

	| list |
	list := EventList randomExample: 1024.
	list addAll: (EventList randomExample: 1024).
	PitchTimeView openOnEventList: list! !

!Siren.SoundView methodsFor: 'initialize-release'!

initialize
	"Set up the proper default values for the receiver."

	super initialize.
	scale := 100.
	step := 100.
	pixmap := Pixmap extent: (3000@220)! !

!Siren.SoundView methodsFor: 'accessing'!

dataExtent
	"Answer the extent of the receiver's display object bounding box."

Transcript show: (self model size / step) printString; cr.
	^(self model size / step) @ 0!

preferredBounds
	"Answer the extent of the receiver's display object bounding box."

"	Transcript show: (self model size / step) printString; cr."
	^0@0 extent: ((self model size / step) @ 180)!

scale
	"Return the instance variable 'scale'."

	^ scale!

scale: theScale
	"Accept the argument, 'theScale', as the new instance variable 'scale'."

	scale := theScale.!

sound
	"Answer the model's sound."

	^model sound!

sound: aSound
	"Set the model's sound."

	| width |
	model sound: aSound.
	width := (((aSound size / step) truncated) min: 16384) max: 512.
"	Transcript show: 'width: ', width printString; cr."
	pixmap := Pixmap extent: (width @ 180).
	self redraw.
	self changed: #text.
	self changedPreferredBounds: nil.
	self changed!

step
	"Return the instance variable 'step'."

	^ step!

step: theStep
	"Accept the argument, 'theStep', as the new instance variable 'step'."

	step := theStep.!

text
	"Answer the default display text for the receiver's sound."

	| stream snd |
	stream := WriteStream on: (String new: 64).
	snd := self sound.
	stream nextPutAll: 'rate: ', snd rate printString; cr.
	stream nextPutAll: 'fmt: ', snd format; cr.
	snd duration == nil
		ifFalse: [stream nextPutAll: 'dur: ', 
						((self sound duration asSec value printString) clipTo: 4); cr].
	stream nextPutAll: 'samps: ', snd size rounded printString; cr.
	((snd at: #comment) == nil)
		ifFalse: [stream nextPutAll: 'cmnt: ', (snd at: #comment); cr].
	^stream contents!

text0
	| stream |
	stream := WriteStream on: (String new: 64).
	self sound properties == nil
		ifFalse: [self sound properties associationsDo:
			[ :ass |
			ass key = #name
				ifFalse: [stream nextPutAll: ass key, ': ', 
						ass value printString; cr]]].
	self sound duration == nil
		ifFalse: [stream nextPutAll: 'duration: ', 
						((self sound duration asSec value printString) clipTo: 4); cr].
	stream nextPutAll: 'samples: ', self sound size rounded printString; cr.
	^stream contents! !

!Siren.SoundView methodsFor: 'controller access'!

defaultControllerClass
	"Answer the default controller class for use with the receiver."

	^SoundController! !

!Siren.SoundView methodsFor: 'displaying'!

displayOn: aGraphicsContext
	"Display the receiver's sound on the argument"

	^self displayOnGC: aGraphicsContext
"
	pixmap == nil
		ifFalse: [redrawn == nil
				ifTrue: [self displayOnGC: pixmap graphicsContext].
			pixmap displayOn: aGraphicsContext]
		ifTrue: [self displayOnGC: aGraphicsContext]
"!

displayOnGC: aGC
	"Show the receiver's sound on the GC."

	| sound box centerY scaledScale setPoint value index oldPt ssize offsetIndex |
	redrawn := true.
	pixmap == nil
		ifTrue: [aGC clear]
		ifFalse: [aGC paint: DisplayListView backgroundColor.
				aGC displayRectangle: pixmap bounds].
	box := aGC clippingBounds.
	aGC paint: DisplayListView foregroundColor.
	centerY := box center y.
	model == nil ifTrue: [^self].
	sound := self sound.
	ssize := sound size.
	scaledScale := (sound class maxSample / scale) asInteger.
"Calculate the left-most sample and position"
	setPoint := box left.
	offsetIndex := setPoint * step.
	value := (sound sampleAt: (offsetIndex max: 1)) // scaledScale.
	value := ((centerY - value) ) "min: box height - 1) max: 0".
	oldPt := setPoint @ value.
"Loop for the samples in the to-be-displayed region"
	1 to: box width do:
		[ :counter | | pt |
		index := (counter * step) + offsetIndex.
		index > ssize ifTrue: [^self]. 
		value := (sound sampleAt: index) // scaledScale.
		value := ((centerY - value) ) "min: box height - 1) max: 0".
		pt := (setPoint + counter) @ (value asInteger).
		aGC displayLineFrom: oldPt to: pt.
		oldPt := pt]!

displaySelection: s1 and: s2
	"Show the selections (up to 2 markers) in the current sound"

	| box pGC |
	model == nil ifTrue: [^self].
	pGC := pixmap graphicsContext.
	box := pGC clippingBounds.
	pGC paint: ColorValue white.
	pGC displayRectangle: ((0@0) extent: (box width @ 16)).
	pGC paint: self foregroundColor.
	s1 == nil
		ifFalse: [Cursor caret displayOn: pGC at: (((self xForSample: s1)- 8)@8)].
	s2 == nil
		ifFalse: [Cursor caret displayOn: pGC at: (((self xForSample: s2) - 8)@8)].
	super invalidate.!

redraw
	"Redisplay the receiver's sound"

	redrawn := nil.
	self displayOn: self graphicsContext.
	self invalidate! !

!Siren.SoundView methodsFor: 'selection'!

eraseMarkerAt: xValue
	"take a mouse point and place a marker on the sound sample there"

"	| pGC p1 p2 box x |
	x := xValue.
	pGC := pixmap graphicsContext.
	box := pGC clippingBounds.
	p1 := x @ box top.
	p2 := x @ box bottom.
	pGC displayLineFrom: p1 to: p2.
	pixmap displayOn: self graphicsContext."!

showMarkerAt: xValue
	"take a mouse point and place a marker on the sound sample there"

	| pGC p1 p2 box x |
	x := xValue.
	pGC := pixmap graphicsContext.
	pGC paint: ColorValue white.
	box := pGC clippingBounds.
	p1 := x @ box top.
	p2 := x @ box bottom.
	pGC displayLineFrom: p1 to: p2.
	pixmap displayOn: self graphicsContext! !

!Siren.SoundView methodsFor: 'converting indeces'!

sampleForX: xValue
	"Answer the sample under the point with x = xValue."

	^(xValue "- scrollOffset value x" * step)!

xForSample: sample
	"Answer the xValue of the sample with the given index."

	^(sample / step + scrollOffset value x)! !

!Siren.SoundView class methodsFor: 'instance creation'!

openOn2: aSound
	"Take the sound as model and open a SoundView"
	"SoundView openOn: (SampledSound fromFile: '/s1/k11/ir/kombination.snd')"
	"SoundView openOn2: (SampledSound sweepExample)"

	| topView topWindow |
	topView := (self soundViewOn: aSound) first.
	topWindow := (ScheduledWindow model: aSound
			label: ('Sound View on: ', aSound name)
			minimumSize: 500@180)
			maximumSize: 1000@200.
	topWindow component: topView.
	topWindow icon: (Icon constantNamed: #systemBrowser).
	topWindow openWithExtent: topWindow minimumSize!

openOn: aSound
	"Take the sound as model and open a SoundView"
	"SoundView openOn: (SampledSound fromFile: '/s1/k11/ir/kombination.snd')"
	"SoundView openOn: (SoundManager soundNamed: #ramp)"

	| topView topWindow |
	topView := (self soundViewOn: aSound) components first.
	topWindow := (ScheduledWindow model: aSound
			label: ('Sound View on: ', aSound name)
			minimumSize: 500@220)
			maximumSize: 1000@220.
	topWindow component: topView.
	topWindow icon: (Icon constantNamed: #systemBrowser).
	topWindow openWithExtent: topWindow minimumSize!

soundViewOn: aSound
	"Take the sound as model and open a SoundView"
	"SoundView openOn: (SampledSound fromFile: '/s1/k11/ir/kombination.snd')"
	"SoundView openOn: (SampledSound sweepExample)"

	| topView me editor edgeDecorator |
	topView := CompositePart new.
	me := self new initialize.
	editor := SoundEditor new sound: aSound.
	me model: editor.
	editor view: me.
"SoundView"
	edgeDecorator := LookPreferences edgeDecorator on: me.
	edgeDecorator noVerticalScrollBar; useHorizontalScrollBar; noMenuBar.
	topView add: edgeDecorator
			in: (LayoutFrame new leftFraction: 0; topFraction: 0; rightFraction: 1; bottomFraction: 1).
	^topView! !

!Siren.SoundView class methodsFor: 'examples'!

example1
	"Open a sound view on the example sound file."
	"SoundView example1"

	SoundView openOn: (SampledSound sweepExample)!

example2
	"Open a sound view on the an empty sound."
	"SoundView example2"

	SoundView openOn: (SampledSound new initialize)! !

!Siren.PositionTimeView methodsFor: 'layout manager access'!

layoutManager
	"Answer a default layout manager for the receiver."

	^(PositionTimeLayoutManager new)
			timeScale: xScale;
			timeOffset: page x;
			pitchScale: yScale;
			pitchOffset: pitchOffset! !

!Siren.PositionTimeView methodsFor: 'initialize release'!

initialize
	"Initialize the instance variables of the receiver."

	super initialize.
	xScale := 15.
	yScale := 40.
	pitchOffset := 40! !

!Siren.HauerSteffensView methodsFor: 'initialize-release'!

initialize
	"Initialize the instance variables of the receiver."

	super initialize.
	self initForm.
	pitchOffset := 30! !

!Siren.HauerSteffensView methodsFor: 'private'!

clef: anInteger at: aPoint bottom: aBottom form: cForm
	"Display a C clef for the given octave at the given point."

	(aPoint y + 20) < aBottom ifTrue: 
		[(Text string: ('c', anInteger printString) emphasis: 2) 
				displayOn: cForm at: aPoint + (2 @ -3)]!

drawStaff
	"Display a Hauer-Stephens-type staff onto the given pixmap."

	| box top1 bottom1 left1 right1  left2 right2 aGC cGC aNum count octave |
	box := background bounds.
"	Transcript show: box printString; cr."
	aGC := background graphicsContext.
	aGC paint: self backgroundColor.
	aGC displayRectangle: background bounds.
	aGC paint: (clefColor == nil
				ifTrue: [ColorValue darkGray]
				ifFalse: [clefColor]).
	cGC := clefForm graphicsContext.
	cGC paint: self backgroundColor.
	cGC displayRectangle: clefForm bounds.
	cGC paint: (headColor == nil
				ifTrue: [ColorValue darkGray]
				ifFalse: [headColor]).
	top1 := box top.
	bottom1 := box bottom.
	left1 := box left.
	right1 := box right.
	left2 := clefForm bounds left.
	right2 := clefForm bounds right.
							"count number of octaves"
	octave := (bottom1 - top1 / (0 + (yScale * 8) + yScale)) asInteger.
	aNum := top1.
	count := 3.
	[aNum < bottom1] whileTrue: 	"loop from top1 to bottom1 drawing lines"
		[aGC displayLineFrom: left1@aNum to: right1@aNum.
		cGC displayLineFrom: left2@aNum to: right2@aNum.
		count := count + 1.
		count \\ 5 = 1			"skip @ c"
			ifTrue: [(aNum + 20) < bottom1
					ifTrue: [('c', octave printString) displayOn: cGC at: (left1@aNum) + (2 @ 10)].
				octave := octave - 1.
				aNum := aNum + yScale].			
		count \\ 5 = 4			"skip @ e"
			ifTrue: [aNum := aNum + yScale].
		aNum := aNum +  (yScale * 2)]!

initForm
	"Copy the default backgroundForm and draw a H-S staff on it."

	background := Pixmap extent: 1200@400.
	background background: self backgroundColor.
	clefForm := Pixmap extent: 32@400.
	clefForm background: self backgroundColor.
	yScale == nil
		ifFalse: [self drawStaff]! !

!Siren.HauerSteffensView class methodsFor: 'instance creation'!

label
	"Answer the view label for the receiver's instances."

	^'Hauer-Steffens Event List View'! !

!Siren.HauerSteffensView class methodsFor: 'constant access'!

defaultCacheExtent
	"Answer the default extent of the receiver's instances' cache."

	^1000@600!

inset
	"Answer the default display inset."

	^16@16! !

!Siren.HauerSteffensView class methodsFor: 'examples'!

randomExample
	"Open a HSView on two merged random event lists."
	"HauerSteffensView randomExample"

	HauerSteffensView openOnEventList:
		((EventList newNamed: #pRand)
				addAll: (EventList randomExample);
				addAll: (EventList randomExample))!

randomSWSSExample
	"Open a HSView on two merged random event lists."
	"HauerSteffensView randomSWSSExample"

	HauerSteffensView openOn:
			((EventListEditor new) eventList: ((EventList newNamed: #sRand)
			addAll: (EventList randomSWSSExample2);
			addAll: (EventList randomSWSSExample2);
			addAll: (EventList randomSWSSExample2))) size: 400@220! !

!Siren.FunctionView methodsFor: 'initialize-release'!

initialize
	"Initialize the receiver's instance variables for cacheing the pixmap during display."

	super initialize.
	vRange := 256.
	hRange := 512.
	aScale := 256.
	models := Array new:4.
	colors := Array with: (DisplayListView foregroundColor)
				with: ColorValue red
				with: ColorValue cyan
				with: ColorValue green.
	width := 2.!

initializeCacheing
	"Initialize the receiver's instance variables for cacheing the pixmap during display."

	vRange := 256.
	hRange := 512.
	aScale := 256.
	models := Array new:4.
	colors := Array with: (DisplayListView foregroundColor)
				with: ColorValue red
				with: ColorValue cyan
				with: ColorValue green.
	width := 2.!

initializeCacheingSmall
	"Initialize the receiver's instance variables for cacheing the pixmap during display."

	vRange := 192.
	hRange := 384.
	aScale := 192.
	models := Array new:4.
	colors := Array with: (DisplayListView foregroundColor)
				with: ColorValue red
				with: ColorValue cyan
				with: ColorValue green.
	width := 2.!

initializeCacheSized: x by: y
	"Initialize the receiver's instance variables for cacheing the pixmap during display."

	vRange := y.
	hRange := x.
	aScale := y.
	models := Array new:4.
	colors := Array with: (DisplayListView foregroundColor)
				with: ColorValue red
				with: ColorValue cyan
				with: ColorValue green.
	width := 2.!

setPixmap
	"Reset the receiver's pixmap display cache after changing its size."

	pixmap := Pixmap extent: (hRange+1)@(vRange+1).
	pixmap background: DisplayListView backgroundColor! !

!Siren.FunctionView methodsFor: 'accessing'!

aScale
	"Answer the receiver's 'aScale'."

	^aScale!

aScale: anObject
	"Set the receiver's instance variable 'aScale' to be anObject."

	aScale := anObject!

colors
	"Answer the receiver's color array."

	^colors!

colors: anArrayOfSize4
	"Set the receiver's color array to the argument."

	colors := anArrayOfSize4!

displayExtent
	"Answer the receiver's visual extent."

	^hRange@vRange!

hRange
	"Answer the receiver's 'hRange'."

	^hRange!

hRange: aNumber
	"Set the receiver's horizontal extent to aNumber."

	hRange := aNumber!

model
	"Answer the fisrt element from receiver's array of models."

	^models at: 1!

modelAt: anIndex
	"Answer the receiver's model at the given index."

	^models at: anIndex!

modelAt: anIndex put: aFunction
	"Set the receiver's model at the given index to the given function."

	models at: anIndex put: aFunction.
	controller model: aFunction.
	anIndex == 1
		ifTrue: [self model: aFunction]!

models
	"Answer the receiver's first model."

	^models!

models: anArray
	"Set the receiver's array of models to the argument."

	models := anArray.
	self model: (anArray at: 1)!

vRange
	"Answer the receiver's 'vRange'."

	^vRange!

vRange: aNumber
	"Set the receiver's vertical extent to aNumber."

	vRange := aNumber! !

!Siren.FunctionView methodsFor: 'controller access'!

defaultControllerClass

	^FunctionController! !

!Siren.FunctionView methodsFor: 'displaying'!

displayGridsOn: gc
	"Display the gridding for the receiver on its cached form."

	gc paint: DisplayListView backgroundColor.
	gc displayRectangle: pixmap bounds.
	gc paint: DisplayListView foregroundColor.
	gc paint: ColorValue gray.
	0 to: hRange by: 32 do:
		[ :xVal |
		gc displayLineFrom: (xVal @ 0) to: (xVal @ vRange)].
	0 to: vRange by: 32 do:
		[ :yVal |
		gc displayLineFrom: (0 @ yVal) to: (hRange @ yVal)]!

displayOn: aGraphicsContext

	cache ifFalse: [aGraphicsContext paint: backgroundColor.
				aGraphicsContext displayRectangle: aGraphicsContext clippingBounds].
	self class inset == nil
		ifFalse: [aGraphicsContext translateBy: self class inset].
	self displayOnImage.
	pixmap displayOn: aGraphicsContext.
	self class inset == nil
		ifFalse: [aGraphicsContext translateBy: self class inset negated].!

displayOnImage
	"Display the receiver on its cached form."
	"FunctionView example"

	| gc oldPt mod |
	gc := pixmap graphicsContext.
	self displayGridsOn: gc.
	gc lineWidth: width.
	1 to: models size do:
		[ :k |
		(mod := models at: k) == nil
			ifFalse: [ | min domain int |
				gc paint: (colors at: k).
				oldPt := 0@vRange - ((mod at: (1.0/hRange) asFloat) * aScale).
				int := mod domain.
				min := int first.
				domain := mod size.
				1 to: hRange do:
					[ :i | | val |
					val := vRange - ((mod at: (i asFloat/hRange) asFloat) * aScale).
					val := (val + min / domain) truncated.
					gc displayLineFrom: oldPt to: i@val.
					oldPt := i@val]]]!

updateBetween: p1 and: p2 with: color on: gc
	"Display the receiver on its cached form."
	"FunctionView example"

	| x1 x2 y1 y2 ins |
	ins := self class inset x.
	x1 := (p1 x + ins) truncated. y1 := (vRange - p1 y + ins) truncated.
	x2 := (p2 x + ins) truncated. y2 := (vRange - p2 y + ins) truncated.
	gc paint: color.
	gc displayLineFrom: (x1@y1) to: (x2@y2).! !

!Siren.FunctionView methodsFor: 'private'!

setModel: aModel
	"Set the receiver's model to be aModel"

	super setModel: aModel.
	models at: 1 put: aModel! !

!Siren.FunctionView class methodsFor: 'constant access'!

defaultColors
	"Answer the default color array for the 4 functions."

	^Array with: ColorValue blue with: ColorValue red
				with: ColorValue cyan with: ColorValue green!

inset
	"Answer the default display inset."

	^6@6! !

!Siren.FunctionView class methodsFor: 'instance creation'!

onFunction: function
	"FunctionView example"

	^self onFunctions: (Array with: function with: nil with: nil with: nil)
			x: 512 y: 256!

onFunction: function and: function2 and: function3 and: function4 x: x y: y
	"FunctionView 
		onFunction: (LinSeg from: #((0@0) (0.15@1.0) (0.25@0.75) (0.75@0.5) (1@0))) 
		and: (LinSeg from: #((0@0.1) (0.3@0.7) (0.7@0.45) (1@0))) 
		and: (LinSeg from: #((0@0) (0.05@1) (0.2@0.35) (1@0))) 
		and: (LinSeg from: #((0@0.2) (0.6@0.3) (1@0.3))) 
		x: 512 y: 256"

	^self onFunctions: (Array with: function with: function2
				with: function3 with: function4)
		withColors: self defaultColors
		x: x y: y!

onFunction: function x: x y: y
	"FunctionView onFunction: (LinSeg from: #((0@0) (0.15@1.0) (0.25@0.75) (0.75@0.5) (1@0))) x: 256 y: 128"

	^self onFunctions: (Array with: function with: nil with: nil with: nil)
			x: x y: y!

onFunctions0: functionArray withColors: colorArray x: x y: y
	"FunctionView onFunctions: (Array 
			with: (LinSeg from: #((0@0) (0.15@1.0) (0.25@0.75) (0.75@0.5) (1@0))) 
			with: (LinSeg from: #((0@0.1) (0.3@0.7) (0.7@0.45) (1@0))) 
			with: (LinSeg from: #((0@0) (0.05@1) (0.2@0.35) (1@0))) 
			with: (LinSeg from: #((0@0.2) (0.6@0.3) (1@0.3))))
		withColors: (Array with: ColorValue yellow with: ColorValue red
				with: ColorValue cyan with: ColorValue green)
		x: 512 y: 256"

	| window container me wrapper |
	window := ScheduledWindow new.
	window label: 'Function Editor'.
	window minimumSize: (x*1.05+22) truncated @(y+20).
	window maximumSize: (x*1.05+22) truncated @(y+20).
	container := CompositePart new.
	me := self new initialize.
	me initializeCacheSized: x by: y.
	me hRange: x; vRange: y; aScale: y; setPixmap.
	me models: functionArray.
	me colors: (Array with: ColorValue yellow with: ColorValue red
			with: ColorValue cyan with: ColorValue green).
"	self addColorBarsTo: container in: ((0@0) extent: (0.05@1))
			colors: me colors controller: me controller.
"	wrapper := LookPreferences edgeDecorator on: me.
	wrapper noVerticalScrollBar.
	container add: wrapper
			borderedIn: (0.05@0 extent: 0.95@1.0).
	window component: container.	
	window open!

onFunctions: functionArray withColors: colorArray x: x y: y
	"FunctionView onFunctions: (Array 
			with: (LinearFunction from: #((0@0) (0.15@1.0) (0.25@0.75) (0.75@0.5) (1@0))) 
			with: (LinearFunction from: #((0@0.1) (0.3@0.7) (0.7@0.45) (1@0))) 
			with: (LinearFunction from: #((0@0) (0.05@1) (0.2@0.35) (1@0))) 
			with: (LinearFunction from: #((0@0.2) (0.6@0.3) (1@0.3))))
		withColors: (Array with: ColorValue yellow with: ColorValue red
				with: ColorValue cyan with: ColorValue green)
		x: 512 y: 256"

	| window container me wrapper |
	window := ScheduledWindow new.
	window label: 'Function Editor'.
	window minimumSize: (x*1.05+22) truncated @(y+20).
	window maximumSize: (x*1.05+22) truncated @(y+20).
	container := CompositePart new.
	me := self new initialize.
	me initializeCacheSized: x by: y.
	me hRange: x; vRange: y; aScale: y; setPixmap.
	me models: functionArray.
	me colors: colorArray.
	wrapper := LookPreferences edgeDecorator on: me.
	wrapper noVerticalScrollBar.
	container add: wrapper borderedIn: (0@0 extent: 1@1).
	window component: container.	
	window open!

onFunctions: functionArray x: x y: y
	"FunctionView onFunctions: (Array 
			with: (LinSeg from: #((0@0) (0.15@1.0) (0.25@0.75) (0.75@0.5) (1@0))) 
			with: (LinSeg from: #((0@0.1) (0.3@0.7) (0.7@0.45) (1@0))) 
			with: (LinSeg from: #((0@0) (0.05@1) (0.2@0.35) (1@0))) 
			with: (LinSeg from: #((0@0.2) (0.6@0.3) (1@0.3))))
		x: 512 y: 256"

	^self onFunctions: functionArray
		withColors: self defaultColors
		x: x y: y!

smallOnFunction: function
	"FunctionView example"

	^self onFunctions: (Array with: function with: nil with: nil with: nil)
			x: 384 y: 192! !

!Siren.FunctionView class methodsFor: 'subview creation'!

addColorBarsTo: topView in: rect colors: colorArray controller: controller
	"Add color bars for selecting among the receiver's function plots to the top view in the given rectangle."

	| y1 y2 |
	y1 := rect height / 4.
	self addColorButtonTo: topView
			in: (rect origin extent: (rect width @ y1)) 
			withColor: (colorArray at: 1) sending: 1 to: controller.
	self addColorButtonTo: topView
			in: (rect origin + (0@y1) extent: (rect width @ y1))
			withColor: (colorArray at: 2) sending: 2 to: controller.
	y2 := rect height / 4 * 2.
	self addColorButtonTo: topView
			in: (rect origin + (0@y2) extent: (rect width @ y1))
			withColor: (colorArray at: 3) sending: 3 to: controller.
	y2 := rect height / 4 * 3.
	self addColorButtonTo: topView
			in: ((rect origin + (0@y2)) extent: (rect width @ y1)) 
			withColor: (colorArray at: 4) sending: 4 to: controller.!

addColorButtonTo: topView in: rect withColor: color sending: arg to: controller
	"Add a colored push-button to the given topView sending the block to the controller."

	| button view wrapper |
	button := Button trigger
			model: ((PluggableAdaptor on: controller) getBlock: [:m | true]
					putBlock: [:m :v | m perform: #selectModel: with: arg]
					updateBlock: [:m :a :p | false]);
			label: 'X'.
	view := ColorActionButton model: button model.
	view controller beTriggerOnUp.
	view offColor: color; onColor: color.
	wrapper := BorderedWrapper on: view.
	wrapper borderWidth: 1; inset: 1.
	button component: wrapper.
	topView add: button in: rect! !

!Siren.FunctionView class methodsFor: 'examples'!

example
	"FunctionView example"

	FunctionView multiFunctionExample!

expSegExample
	"Demonstrate an ExpSeg function with different weights."
	"FunctionView expSegExample"

	FunctionView onFunction: (ExponentialFunction from: #((0 0 3) (0.3 1 5) (0.6 0.3 0) (1 0)))!

fileExample
	"Read an extracted envelope as floating-point data from a file."
	"FunctionView fileExample"

	FunctionView onFunction: (Function readFloatsFrom: MusicMagnitude modeRootName, 'data/y2.2.env')!

linSegExample
	"FunctionView linSegExample"

	FunctionView onFunction: (LinearFunction from: #((0 0) (0.15 1.0) (0.25 0.75) (0.75 0.5) (1 0)))!

multiFunctionExample
	"Open a function editor on 4 different functions."
	"FunctionView multiFunctionExample"

	FunctionView 
		onFunction: (LinearFunction from: #((0 0) (0.12 1) (0.17 0.74) 
				(0.35 0.5) (0.9 0.4) (1 0) )) 
		and: (FourierSummation from: #((1 0.5 0)  (3 0.15 0)  (5 0.1 0)  (7 0.075 0)  (9 0.055 0)  (11 0.05 0))) 
		and: (ExponentialFunction from: #((0 0 5) (0.05 1 -5) (0.2 0.25 -1) (1 0))) 
		and: (SplineFunction from: #((0 0.8) (0.3 0.9) (0.7 0.75) (0.85 0.9) (1 0.8))) 
		x: 512 y: 256!

smallFileExample
	"Read an extracted envelope as floating-point data from a file."
	"FunctionView smallFileExample"

	FunctionView smallOnFunction: (Function readFloatsFrom: 'snd/yy/ec/syl/y2.env')
"	FunctionView smallOnFunction: (Function readFloatsFrom: 'snd/yy/ec/syl/y2.2.env')"!

smallMultiFunctionExample
	"Open a function editor on 4 different functions."
	"FunctionView smallMultiFunctionExample"

	 
	FunctionView onFunctions: (Array 
			with: (LinearFunction from: #((0 0) (0.12 1) (0.17 0.74) 
					(0.35 0.5) (0.9 0.4) (1 0)))
			with: (LinearFunction from: #((0 0.1) (0.3 0.5) (0.7 0.3) (1 0)))
			with: (ExponentialFunction from: #((0 0 5) (0.05 1 -5) (0.2 0.25 -1) (1 0)))
			with: (SplineFunction default))
		withColors: self defaultColors
		x: 384 y: 192! !

!Siren.EventAssociation methodsFor: 'accessing'!

, anArgument
	"Add the argument as a property of the receiver."
	"((0 beat) => (1/16 beat,  'c3' pitch)),
	  ((1/16 beat) => (1/16 beat, 'b2'  pitch))"

	^EventList new add: self; add: anArgument!

event
	"Answer the receiver's event (value)."

	^self value!

start
	"Answer the receiver's start (key)."

	^self key!

start: aTime
	"Set the receiver's start (key)."

	self key: (Duration value: aTime)!

stop
	"Answer the stop time of the event association."

	^self start + self event duration!

time
	"Answer the receiver's start (key)."

	^self key! !

!Siren.EventAssociation methodsFor: 'comparing'!

< anAssociation 
	"Handle events, associations, and time/order sorting."

	anAssociation isAssociation
		ifFalse: [^value < anAssociation].
	key ifNil:
		[anAssociation key ifNil:
			[^((key order) < (anAssociation order))]].
	^key < anAssociation key!

= anAssociation
	"If the argument's not an association, compare it to the receiver's value, otherwise answer whether the receiver's key and value are both equal to the argument's."

	(anAssociation isAssociation)
		ifFalse: [^value = anAssociation].
	key = anAssociation key
		ifTrue: [^value = anAssociation value]
		ifFalse: [^false]! !

!Siren.EventAssociation methodsFor: 'printing'!

printOn: aStream 
	"Append to the argument, aStream, the two elements of the 
	EventAssociation separated by a double-right arrow (=>)."

	key printOn: aStream.
	aStream nextPutAll: ' => '.
	value printOn: aStream! !

!Siren.PhraseView methodsFor: 'layout manager access'!

generateItemFor: anAss
	"Answer the correct visual item for the given assoc. (sent from layoutManagers)."

	| ampV |
	ampV := anAss value loudness value * 32.
	^(DisplayList new
		model: anAss;
		add: ((DisplayString new)
			offset: 2@12;
			model: anAss);
		add: ((DisplayLine new)
			offset: (0@(26 - ampV));
			extent: (0@ampV));
		add: ((DisplayLine new)
			offset: 0@26;
			extent: (anAss value duration value / xScale - 4)@0))!

itemFor0: anAss
	"Answer the correct visual item for the given assoc. (sent from layoutManagers)."

	^(DisplayList new
		model: anAss;
		add: ((DisplayItem new)
			offset: 1@0;
			model: anAss;
			visual: (anAss value phoneme asDisplayText form));
		add: ((DisplayLine new)
"			offset: 0@12;
			extent: 0@(anAss value loudness value * 24 negated);
"			offset: 0@18;
			extent: (anAss value loudness value * 32)@0);
		add: ((DisplayLine new)
			offset: 0@14;
			extent: (anAss value duration value / xScale - 4)@0))!

itemFor: anAss
	"Answer the correct visual item for the given assoc. (sent from layoutManagers)."

	| ampV |
	ampV := anAss value loudness value * 32.
	^(DisplayList new
		model: anAss;
		add: ((DisplayString new)
			offset: 2@12;
			model: anAss);
		add: ((DisplayLine new)
			offset: (0@(26 - ampV));
			extent: (0@ampV));
		add: ((DisplayLine new)
			offset: 0@26;
			extent: (anAss value duration value / xScale - 4)@0))! !

!Siren.PhraseView class methodsFor: 'examples'!

example
	"PhraseView example"
	"NOT WORKING"

	PhraseView openOn:
		((EventListEditor new) eventList: (EventList named: #phrase1) copy)! !

!Siren.OSCBundle methodsFor: 'initialize-release'!

init: aCollection 
	self init: aCollection time: Timestamp now!

init: aCollection time: aTime 
	"Initialize the receiver."

	messages := aCollection.
	time := aTime.
	self init! !

!Siren.OSCBundle methodsFor: 'osc'!

timeTagToOSCBytes: aConvertor 
	"Store a time thingy into the given convertor"

	time isNil 
		ifTrue: 
			[8 timesRepeat: [aConvertor nextPut: 0].
			^self].
	(self class asSecondsSince1900: time) toOSCBytes: aConvertor.
	(self class asNTPMilliseconds: time) toOSCBytes: aConvertor!

toOSCBytes: aConvertor 
	"^self"

	'#bundle' toOSCBytes: aConvertor.
	self timeTagToOSCBytes: aConvertor.
	messages
		do: 
			[:aMessage | 
			aMessage oscSize toOSCBytes: aConvertor.
			aConvertor nextPutAll: aMessage oscBytes]! !

!Siren.OSCBundle class methodsFor: 'instance creation'!

with: aCollection
	^self with: aCollection at: Timestamp now!

with: aCollection at: aTimestamp
	^self new init: aCollection time: aTimestamp! !

!Siren.OSCBundle class methodsFor: 'examples'!

example1
	"Simple example"

	^self 
		with: (Array with: OSCMessage scRun with: OSCMessage scIncreaseVolume)!

example2

	^self with: (Array with: self example1 with: OSCMessage scDecreaseVolume)! !

!Siren.OSCBundle class methodsFor: 'support'!

asNTPMilliseconds: aTimestamp 
	"Convert aTimestamp to the OSC representation of the same"

	| msec |
	msec := aTimestamp millisecond * MillisecToNTP. "((2 raisedTo: 32) / 1000)"
	^msec asInteger!

asSecondsSince1900: aTimestamp 
	"Convert aTimestamp to seconds since 1900"

	| sec |
	"Gets us the number of seconds since Jan 1. 1901"
	sec := aTimestamp asSeconds.
	"sec := sec + SecsInAYear."
	"Convert that to be GMT-based"
	sec := TimeZone default convertLocalSecondsToGMT: sec.
	"Add one year worth of seconds"
	^sec + SecsInAYear. "(60 * 60 * 24 * 365)"!

timestampToOSC: aTimestamp 
	"Convert aTimestamp to the OSC representation of the same"

	| sec msec |
	sec := self asSecondsSince1900: aTimestamp.
	msec := self asNTPMilliseconds: aTimestamp.
	^(sec bitShift: 32) bitOr: msec! !

!Siren.OSCBundle class methodsFor: 'class initialization'!

initialize
	"Initialize the values of the shared variables."
	"OSCBundle initialize"

	MillisecScale = 4295000.
	MillisecToNTP:= ((2 raisedTo: 32) / 1000).
	SecsInAYear:= 60 * 60 * 24 * 365 ! !

!Siren.IndentedTreeLayoutManager methodsFor: 'generating'!

addItem: aModelNode atNode: aPath under: parentPoint in: aDisplayList
	"Add the given item to the display list."

	|  displayPoint item path2 displayList2 |
	displayPoint := (self displayPositionFor: aPath)
						+ (parentPoint x@0).
	length := length + 1.
	item := treeAccessor itemFor: aModelNode.
	item offset: displayPoint.
	(treeAccessor isTerminal: aModelNode)
		ifFalse: [displayList2 := DisplayList new.
			displayList2 model: aModelNode.
			(treeAccessor childrenOf: aModelNode) do: [ :i | 
				path2 := aPath copy.
				path2 addLast: aModelNode.
				self addItem: i
					atNode: path2
					under: item bounds rightCenter
					in: displayList2].
			displayList2 add: item.
			aDisplayList add: displayList2 atPath: aPath.
			parentPoint = (0@0)
				ifFalse: [aDisplayList add:
						((DisplayLine new)
							offset: item bounds leftCenter + (-2@4);
							corner: parentPoint + (2@4); color: ColorValue white)
					atPath: aPath]]
				ifTrue: [aDisplayList add: item atPath: aPath.
					aDisplayList add:
							((DisplayLine new)
								offset: item bounds leftCenter + (-2@4);
								corner: parentPoint + (2@4); color: ColorValue white)
						atPath: aPath]!

generateDisplayList
	"Generate and answer the receiver's display list."

	list := DisplayList new.
	self addItem: treeAccessor subject
			atNode: (OrderedCollection new)
			under: 0@0
			in: list.
	^list! !

!Siren.IndentedTreeLayoutManager class methodsFor: 'examples'!

listExample
	"IndentedTreeLayoutManager listExample display"
	"DisplayListView classTreeExample"

	| acc me |
	acc := STreeAccessor subject: "View" Magnitude
			terminalQuery: [ :c | c subclasses size = 0]
			childAccessor: #sortedSubclasses
			itemGenerator: [ :c | (DisplayString new)
							model: c; 
							string: c name; 
							color: ColorValue black].
	me := (self new) accessor: acc; xStep: 4; yStep: 12.
	^me displayList! !

!Siren.Duration class methodsFor: 'instance creation'!

value: aValue
	"Answer a new instance of a member of my species."

	| number |
	aValue species == Duration ifTrue: [^aValue].
	(aValue isMemberOf: SmallInteger)
		ifTrue: [aValue < 10
			ifTrue: [^SecondDuration new value: aValue]
			ifFalse: [^MSecondDuration new value: aValue]].
	(aValue isKindOf: LimitedPrecisionReal)
		ifTrue: [^SecondDuration new value: aValue].
	(aValue isMemberOf: Fraction)
		ifTrue: [^RatioDuration new value: aValue].
	(aValue isKindOf: String)
		ifTrue: [number := Number readFrom: (ReadStream on: aValue).
			number = 0 ifTrue: [^self error: 'Unknown duration value.'].
			^Duration value: number].
	(aValue isMemberOf: BlockContext)
		ifTrue: [^ConditionalDuration new value: aValue].
"	^self error: 'Unknown duration type.'"
	^aValue! !

!Siren.Duration class methodsFor: 'class constants'!

color
	"Answer the default color to display instances with."

	^ColorValue red!

initialize
	"Set up the class inst var, a generality table."
	"Duration initialize"
	"Duration generalize: (100 msec) and: ((1/4) beat)"
	"Duration generalize: (100 msec) and: (0.3 sec)"

	generalityTable :=
		((SecondDuration -> 80),
		(RatioDuration -> 60),
		(MSecondDuration -> 40),
		(ConditionalDuration -> 0))!

initializeGeneralities
	"Set up the class inst var, a generality table."
	"Duration initializeGeneralities"
	"Duration generalize: (100 msec) and: ((1/4) beat)"
	"Duration generalize: (100 msec) and: (0.3 sec)"

	generalityTable :=
		((SecondDuration -> 80),
		(RatioDuration -> 60),
		(MSecondDuration -> 40),
		(ConditionalDuration -> 0))!

mostGeneral
	"Answer the most general-purpose duration--seconds"

	^#asSec!

propertyName
	"Answer the receiver class's default property name."

	^#duration:!

relativeMember
	"Answer the receiver class's species member used as a relative magnitude."

	^RatioDuration!

species
	"Answer Duration."

	^Duration! !

!Siren.Duration class methodsFor: 'examples'!

example
	"Print a simple message to the transcript demonstrating the various types."
	"Duration example"

	| me |
	me := Duration value: 1/4.
	Transcript show: me printString; cr;
		show: me asMS printString; cr.
	me inspect! !

!Siren.Pitch class methodsFor: 'instance creation'!

value: aValue
	"Answer a new instance of a member of my species."

	aValue species == Pitch ifTrue: [^aValue].
	(aValue isMemberOf: SmallInteger)
			ifTrue: [^MIDIPitch new value: aValue].
	(aValue isKindOf: LimitedPrecisionReal)
			ifTrue: [^HertzPitch new value: aValue].
	(aValue isMemberOf: Fraction)
			ifTrue: [^RatioPitch new value: aValue].
	(aValue isKindOf: String)
			ifTrue: [^SymbolicPitch new value: aValue asSymbol].
	^self new value: aValue! !

!Siren.Pitch class methodsFor: 'class constants'!

color
	"Answer the default color to display instances with."

	^ColorValue green!

initialize
	"Set up the class inst var, a generality table."
	"Pitch initialize"
	"Pitch generalize: (#c pitch) and: (21 key)"
	"Pitch generalize: (#c pitch) and: (220 Hz)"
	"Pitch generalize: (220 Hz) and: (#c pitch)"

	generalityTable :=
		((HertzPitch -> 80),
		(RatioPitch -> 60),
		(MIDIPitch -> 40),
		(SymbolicPitch -> 20))!

initializeGenerality
	"Set up the class inst var, a generality table."
	"Pitch initializeGenerality"
	"Pitch generalize: (#c pitch) and: (21 key)"
	"Pitch generalize: (#c pitch) and: (220 Hz)"
	"Pitch generalize: (220 Hz) and: (#c pitch)"

	generalityTable :=
		((HertzPitch -> 80),
		(RatioPitch -> 60),
		(MIDIPitch -> 40),
		(SymbolicPitch -> 20))!

mostGeneral
	"Answer the most general-purpose duration--Hertz"

	^#asHz!

propertyName
	"Answer the receiver class's default property name."

	^#pitch:!

relativeMember
	"Answer the receiver class's species member used as a relative magnitude."

	^RatioPitch!

species
	"Answer Pitch."

	^Pitch! !

!Siren.Pitch class methodsFor: 'examples'!

example
	"Print a simple message to the transcript demonstrating the various types."
	"SymbolicPitch nameMap inspect"
	"Pitch example"

	| me |
	me := Pitch value: 'c3'.
	Transcript show: me printString; cr;
		show: me asHertz printString; cr;
		show: me asMIDI printString; cr.
	me inspect!

exampleAdC
"Pitch exampleAdC"
	
	"Some examples of where current Pitch implementation is problematic:"

	"440.0 Hz asSymbol"	"shows up as a5, not the standard MIDIname a3."
	"445 Hz asMIDI"		"is quantized down OK, but should be a3/note 69 also."	
	"439.9 Hz asSymbol"			"gets quantized down to g#!!"
	"60 key asHertz asMIDI"		"does not survive conversion as MIDInote 60."

	"My suggestions:"
	"438 Hz asSymbol"  	"rounds to nearest chromatic note, a3."
	"443.5 Hz asMIDI"	"detto."
	"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 inspect" 	"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 asFracMIDI and asFracSymbol keep microtonal 	precision."! !

!Siren.SoundController methodsFor: 'control defaults'!

redButtonActivity
	"set a marker with the red button"

	| pt |
	pt := self sensor cursorPoint.
	self sensor waitNoButton.
"	pt y < 16
		ifTrue: [^self selectMarkerAt: pt x]."
	model select: pt x!

selectMarkerAt: x
	"Select the marker at the given x value."

	| markers sel |
	markers := model markers.
	sel := markers
		detect: [ :m | ((view xForSample: m) - x) abs <= 4]
		ifNone: [self halt. ^nil].
	model setSelection: sel! !

!Siren.SoundController methodsFor: 'menu messages'!

clearMarkers: ignored
	"comment stating purpose of message"

	model markers: OrderedCollection new.
	model selection1: nil.
	model selection2: nil.
	view redraw!

copy
	"copy the selected sound"

	model copy!

cut
	"cut the selected segment out of the receiver's model sound"

	model cut!

doInspect
	"Inspect the view."

	model inspect!

fadeIn
	"Apply a ramp fade-in envelope to the receiver's sound."

	| time |
	time := Number readFrom: 
		(ReadStream on: 
			(Dialog request: 'Fade-in time' 
					initialAnswer: '0.1')).
	(time between: 0.001 and: 2.0) ifFalse: [^nil].
	Cursor execute showWhile:
		[model sound fadeInOver: time.
		view redraw.
		model markChanged: true]!

fadeOut
	"Apply a ramp fade-out envelope to the receiver's sound."

	| time |
	time := Number readFrom: 
		(ReadStream on: 
			(Dialog request: 'Fade-out time' 
					initialAnswer: '0.1')).
	(time between: 0.001 and: 2.0) ifFalse: [^nil].
	Cursor execute showWhile:
		[model sound fadeOutOver: time.
		view redraw.
		model markChanged: true]!

file: ignored
	"Open the file dialog"

	model file!

inspect: ignored
	"Inspect the view."

	model inspect!

menu

	^Menu
		labelList: #((copy cut paste) (accept cancel) (spawn inspect) ('vertical scale' 'horizontal scale') ('fade in' 'fade out'))
		values: #(copy cut paste 
				accept cancel spawn doInspect
				setVscale setHscale 
				fadeIn fadeOut)!

newCopy: ignored
	"Make a fresh copy of the receiver's model's sound."

	model newCopy!

paste
	"paste the selected sound into the model at the current selected marker"

	model paste!

play: ignored
	"play the model"

	model play!

save
	"Save a copy of the model to the disk."

	model save!

setHscale
	"set the horizontal scale of the view"

	| newStep |
	newStep := Number readFrom: 
		(ReadStream on: 
			(Dialog request: 'New horizontal scale?' 
					initialAnswer: view step printString)).
	(newStep between: 1 and: 10000) ifFalse: [^nil].
	view step: newStep.
	view redraw!

setVscale
	"set the vertical scale of the view"

	| newScale |
	newScale := Number readFrom: 
		(ReadStream on: 
			(Dialog request: 'New vertical scale?'
					initialAnswer: view scale printString)).
	(newScale between: 1 and: 1000) ifFalse: [^nil].
	view scale: newScale.
	view redraw!

spawn
	"Edit a copy of the model."

	model spawn! !

!Siren.IOVoice methodsFor: 'initialize-release'!

close
	"Close the stream, devise, or whatever."

	self subclassResponsibility!

initialize
	"Set up a Voice."

	^self!

initializeNamed: aName 
	"Set up a named Voice."

	self initialize.
	(aName isKindOf: Number)
		ifTrue: [name := aName printString]
		ifFalse: [name := aName].
	SirenUtility voices ~~ nil
		ifTrue: [SirenUtility voices at: name asSymbol put: self]! !

!Siren.IOVoice methodsFor: 'accessing'!

instrument
	"return my instrument."

	^instrument!

instrument: newValue
	"set my instrument."

	instrument := newValue!

stream
	"answer the stream of the receiver voice"

	^stream!

stream: someStream
	"plug in a stream to the receiver voice"

	stream := someStream! !

!Siren.IOVoice methodsFor: 'events'!

eventList
	"Answer an event list read from the receiver."

	| e |
	e := EventList newAnonymous.
	self readOnto: e.
	^e!

play: anEventList
	"Expand the list and play it now."

	^self play: anEventList at: Time millisecondClockValue!

play: anEventOrList at: start
	"Expand the list and play it at the given time."

	| t e |
	anEventOrList hasItems
		ifFalse: [^self playEvent: anEventOrList at: start].
	anEventOrList events do:
		[ :ass |
		t := ass key asMsec.
		e := ass value.
		e hasItems 
			ifTrue: [self play: e at: t + start]
			ifFalse: [self playEvent: e at: t + start]]!

playEvent: anEvent
	"Play the argument now."

	self playEvent: anEvent at: 0!

playEvent: anEvent at: aTime
	"make sure my subclasses implement this"

	self subclassResponsibility!

readOnto: eventList
	"Read events from the receiver into the argument."

	self subclassResponsibility! !

!Siren.IOVoice methodsFor: 'testing'!

isVoice
	"Answer true"

	^true! !

!Siren.IOVoice methodsFor: 'printing'!

printOn: aStream
	"Print the receivcer on the argument."

	aStream nextPutAll: 'a ', self class name! !

!Siren.IOVoice class methodsFor: 'class initialization'!

initialize
	"set up the shared dictionary for caching voices"
	"Voice initialize."

	SirenUtility initializeVoices.
	DefaultVoiceClass := #MIDIVoice!

reset
	"Reset all sub-instances of Voice."
	"Voice reset"

	self allSubInstancesDo: [ :aV | aV reset ]!

setDefault: className
	"Set the default class to use for new voices"
	"IOVoice initialize."

	DefaultVoiceClass := className! !

!Siren.IOVoice class methodsFor: 'instance creation'!

default
	"Answer a voice."

	DefaultVoiceClass == #MIDIVoice ifTrue: [^MIDIVoice default].
	DefaultVoiceClass == #OSCVoice ifTrue: [^OSCVoice default].
	DefaultVoiceClass == #SuperColliderVoice ifTrue: [^SuperColliderVoice default].
	(SirenUtility voices == nil or: [SirenUtility voices isEmpty])
		ifFalse: [^SirenUtility voices associations asOrderedCollection first value]
		ifTrue: [self error: 'Unknown default voice class'].!

named: aName
	"Answer the shared dictionary's voice item at the given name (or the name itself if it's a voice)"

	(aName isKindOf: IOVoice)
		ifTrue: [^aName].
	(aName class == SmallInteger)
		ifTrue: [^MIDIVoice default].
	(SirenUtility voices ~~ nil and: [SirenUtility voices includesKey: aName printString asSymbol])
		ifTrue: [^SirenUtility voices at: aName printString asSymbol]
			ifFalse: [IOVoice default].!

on: someStream
	"Answer a new voice instance on the given stream."

	| me |
	me := self new initialize.
	me stream: someStream.
	^me! !

!Siren.NotelistVoice methodsFor: 'initialize-release'!

close
	"Close the receiver's output stream (if it's a file)."

	stream flush; close!

initialize
	"Initialize the receiver."

	self parameterMap: self class defaultParameterMap!

initializeNamed: aName onStream: theStream
	"Answer a new named NotelistVoice on the given stream."

	self initializeNamed: aName.
	stream := theStream!

initializeOnStream: theStream
	"Answer a new named NotelistVoice on the given stream."

	self initialize.
	stream := theStream! !

!Siren.NotelistVoice methodsFor: 'accessing'!

addMap: anItem
	"Add the argument (a symbol or block), to the parameterMap of the receiver"

	parameterMap addLast: anItem!

header: aString
	"Set the file header string of the receiver."

	header := aString!

parameterMap
	"Answer the parameterMap of the receiver."

	^parameterMap!

parameterMap: anOrderedCollection
	"Set the parameterMap of the receiver."

	parameterMap := anOrderedCollection! !

!Siren.NotelistVoice methodsFor: 'events'!

dump: aStringOrValue 
	"The case I handle is strings. Subclasses may override."

	stream nextPutAll: aStringOrValue!

endLine
	"Put the proper command terminator on the receiver's stream."

	stream cr!

mapProperty: item of: ass within: list 
	"Write the given event association to the receiver's stream in the appropriate format."

	| event time |
	event := ass value.
	time := ass key asSec value.
	item isSymbol
		ifTrue: [item == #start
				ifTrue: [self dump: time printString]
				ifFalse: [item == #delay
						ifTrue: [self dump: (list nextTime: ass) asSec value printString]
						ifFalse: [self dump: ((event perform: item) asFloat printString: 4)]].
			^self].
	item isString
		ifTrue: [self dump: item.
			^self].
	(item isMemberOf: BlockClosure)
		ifTrue: [item numArgs = 1
				ifTrue: ["1-arg blocks = send event"
					self dump: (item value: event)
					"2-arg blocks = send ass and list"]
				ifFalse: [self dump: (item value: ass value: list)]]
		ifFalse: [self error: 'unknown voice map item type.']!

play: anEL
	"Perform the argument."

	self writeHeader.
	self play: anEL at: 0.
	self writeFooter!

play: anEL at: start
	"Perform the argument."

	| time event |
	anEL do: 
		[ :ass |
		time := ass key asSec value.
		event := ass value.
		event isEventList
			ifTrue: [self play: event at: time + start]
			ifFalse: [self playAssociation: ass in: anEL]].!

playAssociation: ass in: list
	"Write the given event association to the receiver's stream in the appropriate format."

	stream == nil ifTrue: [^self].
	parameterMap do:			"print out the fields in my p-map"
		[ :item |
		self mapProperty: item of: ass within: list ]
		separatedBy: [self space].
	self endLine!

space
	"Put the proper field separator on the receiver's stream."

	stream space; tab!

writeFooter
	"Write a notelist file footer on the receiver's stream."

	^self!

writeHeader
	"Write a notelist file header on the receiver's stream."

	^self! !

!Siren.NotelistVoice class methodsFor: 'instance creation'!

defaultParameterMap
	"Answer the default parameterMap of the receiver class's instances."

	self subclassResponsibility!

newNamed: aName onFile: fName
	"Set up a NotelistVoice on the given output file"
	"CmusicVoice newNamed: 'violin' onFile: 't.out.cmusic'"

	^self new initializeNamed: aName onStream: (FileStream named: fName)!

newNamed: aName onStream: aStream
	"Set up a NotelistVoice on the given output stream"
	"CmusicVoice newNamed: 'violin' onStream: Transcript"

	^self new initializeNamed: aName onStream: aStream!

onFileNamed: fName
	"Set up a NotelistVoice on the given output file"
	"CmusicVoice onFile: 't.out.cmusic'"

	^self new initializeOnStream: fName asFilename writeStream! !

!Siren.CmixVoice methodsFor: 'events'!

endLine

	stream nextPutAll: ');'; cr.
	^self!

playEvent: evt at: start 
	"Play the given event"

	| param newEv siz |
	stream == nil ifTrue: [^self].
	stream nextPutAll: parameterMap first.
	newEv := 2.
	siz := parameterMap size.
	[newEv <= siz]
		whileTrue: 
			[(param := parameterMap at: newEv) isSymbol
				ifTrue: [param == #start
						ifTrue: [stream nextPutAll: start printString]
						ifFalse: [stream nextPutAll: (evt perform: param) printString]]
				ifFalse: [param isString
						ifTrue: [stream nextPutAll: param]
						ifFalse: [(param isMemberOf: BlockContext)
								ifTrue: [stream nextPutAll: (param value: evt)]
								ifFalse: [self error: 'unknown voice map item type.']]].
			self space.
			newEv := newEv + 1].
	self endLine.
	^self!

space

	stream nextPut: $,; space; tab.
	^self!

writeHeader
	"Write out a CMix score file header."

	stream nextPutAll: '/* cmix MINC data file created ' , Date today printString , ' */'; cr.
	stream cr; nextPutAll: 'system("sfcreate -r 44100 -c 1 -i out.snd");'; cr.
	stream nextPutAll: 'output("out.snd");			/* output sound file */'; cr.
	stream nextPutAll: 'makegen(1, 10, 1024, 1)		/* f1 = sine wave */'; cr; cr.
	^self! !

!Siren.CmixVoice class methodsFor: 'instance creation'!

default

	CmixVoice newNamed: 'fm' onStream: 'tmp.m' asFilename writeStream.
	^self!

defaultParameterMap
	"CmixVoice randomExampleToFileAndEdit"

	| map |
	map := OrderedCollection new: 8.
	map add: [ :ass :list | 'ins(', (ass key asSec value asFloat printString: 4)].
	map add: [ :t2 | t2 duration asSec value printString: 4 ].
	map add: [ :t3 | t3 loudness asDB positiveDB truncated printString].
	^map! !

!Siren.CmixVoice class methodsFor: 'examples'!

randomExampleToFileAndEdit
	"CmixVoice randomExampleToFileAndEdit"

	self randomExampleToFileNamed: 'rand.m'.
	^self!

randomExampleToFileNamed: fnam 
	"Create a random event list and store it on a file."

	| evt strm list rand vox |
	strm := (Filename named: fnam) writeStream.
	rand := Random new.
"Create the event list and add a few additional properties"
	(list := EventList randomExample: 64)
		do: 
			[ :ass | 
			(evt := ass event) at: #pos put: rand next.
			evt at: #prop1 put: (rand nextFrom: 320 to: 480).
			evt at: #prop2 put: (rand nextIntFrom: 12000 to: 23000)].
"Create a cmix voice and customize its parameter map."
	(vox := CmixVoice newNamed: 'file:=two' onStream: strm) addMap: #pos.
	vox addMap: [ :item | ((item at: #prop1) / 1000) printString].
	vox addMap: #prop2.
"Play the list on the voice."
	vox play: list.
	strm close.
	(Filename named: fnam) edit.
	^self! !

!Siren.MIDIVoice methodsFor: 'initialize-release'!

close
	"Close the stream, device, or whatever."

	instrument close!

initialize
	"Set up a MIDIVoice's time counter."

	super initialize.
	currentTime := nil!

reset
	"Reset my time."
	
	currentTime := nil! !

!Siren.MIDIVoice methodsFor: 'accessing'!

number
	"Answer my channel or icon number"

	| n |
	n := Number readFrom: (ReadStream on: (name)).
	n = 0 ifTrue: [^self channel].
	^n! !

!Siren.MIDIVoice methodsFor: 'events'!

playEvent: anEvent at: aTime
	"Send the given Event to the instrument."

	^aTime isNil
		ifTrue: [instrument play: anEvent at: Time millisecondClockValue]
		ifFalse: [instrument play: anEvent at: aTime asMsec value]! !

!Siren.MIDIVoice class methodsFor: 'class initialization'!

flushDefault
	"Reset the class's default instance."
	"MIDIVoice flushDefault"

	Instance := nil!

initialize
	"Reset the class's default instance."
	"MIDIVoice initialize"

	Instance := nil.
	UseSingleton := true.! !

!Siren.MIDIVoice class methodsFor: 'instance creation'!

default
	"Answer the default MIDIVoice."
	"MIDIVoice default"

	UseSingleton
		ifTrue: [Instance ifNil: [Instance := MIDIVoice on: (MIDIDevice on: MIDIPort default)].
			^Instance]
		ifFalse: [^MIDIVoice on: (MIDIDevice on: MIDIPort default)]!

named: aName onDevice: aMidiDevice channel: aChannel
	"set up a MidiVoice on the given device and channel"
	"MidiVoice newNamed: 'xxx' onDevice: (MidiFB01 newOn: 1) channel: 1"

	| me |
	me := self new initializeNamed: aName.
	me instrument add: (aChannel -> aMidiDevice).
	^me!

new
	"Cache the latest instance."

	^self default!

on: aMidiDevice
	"Answer up a MidiVoice on the given device."
	"MIDIVoice on: (MIDIDevice on: (MIDIPort default openOutput))"

	| me |
	me := self basicNew initialize.
	me instrument: aMidiDevice.
	^me!

on: aMidiDevice channel: aChannel
	"set up a MidiVoice on the given device and channel"
	"MidiVoice onDevice: (MidiFB01 newOn: 1) channel: 1"

	| me |
	me := self new initializeNamed: aChannel.
	me instrument add: (aChannel -> aMidiDevice).
	^me! !

!Siren.MIDIVoice class methodsFor: 'examples'!

randomExample
	"Play some random notes on a voice on a device on a port."
	"MIDIVoice randomExample"

	| events voice |
	events := (EventList randomExample: 80 
		from: ((#duration: -> (20 to: 80)), (#pitch: -> (48 to: 80)),
				(#ampl: -> (84 to: 124)), (#voice: -> (0 to: 0)))).
	voice := MIDIVoice default.
	voice play: events.
!

scaleExample
	"Play a scale on a voice on a device on a port to the Apple MIDI Mgr."
	"MIDIVoice voiceExample"
	"MIDIPort allNotesOff"

	| voice device port scale |
	port := MIDIPort default.
	device := MIDIDevice on: port.
	voice := MIDIVoice on: device.
	scale := EventList scaleExampleFrom: 24 to: 100 in: 4000.
"	scale eventsDo: [ :e | e duration: (e duration * 3)]."
	voice play: scale!

voiceInspect
	"MIDIVoice voiceInspect"

	(MIDIVoice on: (MIDIDevice on: (MIDIPort default writeStream))) inspect! !

!Siren.SoundVoice methodsFor: 'events'!

playEvent: t1 at: t2 

	stream play: t1.
	^self! !

!Siren.SoundVoice class methodsFor: 'constant access'!

default

	^self on: SoundPort default! !

!Siren.SuperColliderVoice methodsFor: 'events'!

endLine
	"Put the proper command terminator on the receiver's stream."

	stream nextPutAll: ' ],'; cr!

space
	"Put the proper field separator on the receiver's stream."

	stream space; nextPut: $,; space; tab!

writeFooter
	"Write a notelist file footer on the receiver's stream."

"	stream skip: -2.
"	stream nextPutAll: '];'; cr!

writeHeader
	"Write a SuperCollider notelist file header on the receiver's stream."

	header ifNotNil: 
		[^stream nextPutAll: header].

	stream nextPutAll: 
'// SC2 Score for rotating bell clusters; generated on ',
		Date today printString, ' at ',  Time now printString, '

// Instrument format [ delT, \panner, \fname, dur, att, dec, ampl, angVel ]

score = #[
'! !

!Siren.SuperColliderVoice class methodsFor: 'instance creation'!

defaultParameterMap
	"Answer up the parameter map for the default instance."
	"Instrument format
		[ delT, 'fm', fname, dur, att, dec, ampl, index ] "

	| map |
	map := OrderedCollection new: 8.
	map add: [ :ass :li | '	[ ', ((li nextTime: ass) asSec value asFloat printString: 6)].
	map add: '\fm'.
	map add: [ :e | e duration asSec value printString: 4].
	map add: [ :e | e attack value printString: 4].
	map add: [ :e | e decay value printString: 4].
	map add: [ :e | e ampl value asFloat printString: 4].
	map add: [ :e | e index printString: 4].
	^map!

defaultParameterMapForPanner
	"Answer up the parameter map for the default instance."
	"Instrument format
		[ delT, 'panner', fname, dur, att, dec, ampl, angVel ] "

	| map |
	map := OrderedCollection new: 8.
	map add: [ :ass :li | '	[ ', (li nextTime: ass) asSec value 
										printString: 5].
	map add: '\panner'.
	map add: [ :e | '\', e partial].
	map add: [ :e | e duration asSec value printString].
	map add: [ :e | e attack value printString].
	map add: [ :e | e decay value printString].
	map add: [ :e | e ampl value printString truncateTo: 6].
	map add: [ :e | e angularVelocity printString truncateTo: 6].
	^map! !

!Siren.SuperColliderVoice class methodsFor: 'examples'!

randomExampleToFileAndEdit
	"SuperColliderVoice randomExampleToFileAndEdit"

	self randomExampleToFileNamed: 'rand.sc'.
	^self!

randomExampleToFileNamed: fnam 
	"Create a random event list and store it on a file."
	"SuperColliderVoice randomExampleToFileAndEdit"

	| evt strm list rand vox |
	strm := (Filename named: fnam) writeStream.
	rand := Random new.
"Create the event list and add a few additional properties"
	(list := EventList randomExample: 64)
		do: [ :ass | 
			evt := ass event.
			evt at: #attack put: (rand nextFrom: 0.001 to: 0.03).
			evt at: #decay put: (rand nextFrom: 0.2 to: 0.3).
			evt at: #index put: (rand nextFrom: 0.2 to: 2.3).
			evt at: #decay put: (rand nextFrom: 0.2 to: 0.3)].
"Create a SuperCollider voice and customize its parameter map."
	vox := SuperColliderVoice newNamed: '1' onStream: strm.
"Play the list on the voice."
	vox play: list.
	strm close.
	(Filename named: fnam) edit.
	^self! !

!Siren.OSCVoice methodsFor: 'accessing'!

port
	"Return the OSCPort"

	^port!

port: aPort 
	"Set the OSCPort"

	port := aPort.
	^self! !

!Siren.OSCVoice methodsFor: 'events'!

oscMessageFrom: event 
	"Answer a message for the given event by passing it to my parameter-mapper block"

	^parameterMap value: event!

play: anEL 
	"Expand the list and play it now."

	self play: anEL at: Time millisecondClockValue!

play: anEvent at: aTime 
	"Play the given event on my port"

	anEvent hasItems ifFalse: [^self playEvent: anEvent at: aTime].
	anEvent events do: 
			[:ass | | dur event |
			dur := ass key asMsec value.
			(event := ass value) hasItems 
				ifTrue: [self play: event at: dur + aTime]
				ifFalse: [self playEvent: event at: dur + aTime]].
	^self!

playAssociation: ass in: list 
	"Play the association..."

	| realTime |
	realTime := ass key asMsec value.
	self waitTill: realTime.
	stream := OrderedCollection new.
	parameterMap do: [ :item | self
			mapProperty: item
			of: ass
			within: list].
	stream size = 0 ifTrue: [^self].
	stream size = 1 ifTrue: [^port send: (stream at: 1)].
	port send: (OSCBundle with: stream).
	^self!

playEvent: event at: start 
	"Expand the list and play it at the given time."

	start ifNotNil:
		[self waitTill: start].
	event hasItems ifTrue: [^self play: event at: start].
	port send: (self oscMessageFrom: event)!

waitTill: time

	| now |
	time ifNotNil: 
			[now := Time millisecondClockValue.
			time > now
				ifTrue: [(Delay forMilliseconds: time - now) wait]
				ifFalse: [nil]].
	^self! !

!Siren.OSCVoice methodsFor: 'utilities'!

send: command args: args
	"Send an OSC command with the given arguments"

	port send: (OSCMessage for: command with: args)! !

!Siren.OSCVoice class methodsFor: 'instance creation'!

default
	"Answer an OSC voice for use with OSC-to-MIDI by default."

	^self onPort: OSCPort default!

defaultParameterMap
	"Answer the default parameterMap for use with OSC-to-MIDI. 
	In this class, we create and return a OSCMessage with data taken from the given event."

	^[ :e | 
	| arr timestamp |
	arr := Array
			with: (e voice isInteger ifTrue: [e voice] ifFalse: [1])
			with: e pitch asMIDI value
			with: e ampl asMIDI value.
	timestamp := Timestamp now.
	OSCBundle with: (Array 
			with: (TypedOSCMessage for: '/osc/midi/out/noteOn' with: arr)
			with: (OSCBundle 
				with: (Array with: (TypedOSCMessage for: '/osc/midi/out/noteOff' with: arr))
				at: (timestamp addMilliseconds: e duration asMsec value)))
		at: timestamp]!

defaultParameterMapForCSLFM
	"Answer the default parameterMap for use with the CSL FM example instrument."

	^[ :e | | arr |
	arr := Array new: 5.				"set up the parameter map"
	arr at: 1 put: e duration asSec value.
	arr at: 2 put: e ampl value.
	arr at: 3 put: e pitch asHz value.
	arr at: 4 put: e pitch asHz value * e ratio.
	arr at: 5 put: e index.
	OSCMessage for: '/i1/pn' with: arr]!

defaultParameterMapWithBundle
	"Answer the default parameterMap for use with OSC-to-MIDI. In this class, we create and return a TypedOSCMessage with data taken from the given event. This allows us to have other versions that create OSC bundles."

	^[ :e | | arr |
	arr := Array with: 1
			with: e pitch asMIDI value
			with: e ampl asMIDI value.
	OSCBundle 
		with: (Array with: (TypedOSCMessage for: '/osc/midi/out/noteOn' with: arr)
				with: (OSCBundle 
						with: (TypedOSCMessage for: '/osc/midi/out/noteOff' with: arr)
						at: (Timestamp now addMilliseconds: e duration asMsec value)))]!

localhost
	"Answer an instance on the local host"

	^self onPort: OSCPort localhost!

onPort: pt
	"Answer an instance on the given port"

	| me |
	(me := self new initialize) port: pt.
	^me!

scHost
	"Answer the default voice for SC over OSC"

	^self onPort: OSCPort scHost! !

!Siren.OSCVoice class methodsFor: 'examples'!

fm_example1
	"Play a few random notes on the CSL FM instrument"
	"OSCVoice fm_example1"
	"Startup up the CSL OSC demo and run these"
	"CSL FMInstrument has args: dur, ampl, c_fr, m_fr, ind"

	| events voice  |
	events := (EventList randomExample: 15 					"create the event list"
		from: ((#duration: -> (0.2 to: 0.5)), (#pitch: -> (110.1 to: 220.1)), (#ratio: -> (0.8 to: 1.2)),
					(#ampl: -> (0.2 to: 1.0)), (#index: -> (1.0  to: 6.0)))).

	voice := self onPort: (OSCPort to: #[127 0 0 1] port: 54321).	"edit this to taste."
	voice  instrument: '/i1/pn'.									"default instrument"
	voice parameterMap: self defaultParameterMapForCSLFM.	"plug in the parameter map"

	events playOn: voice!

fm_example2
	"Play a few random notes on several of the CSL FM instruments"
		"OSCVoice fm_example2"
	"Startup up the CSL OSC demo and run these"
	"CSL FMInstrument has args: dur, ampl, c_fr, m_fr, ind"

	| events voice map rand |						"create the event list"
	events := (EventList randomExample: 25 
		from: ((#duration: -> (0.15 to: 0.5)), (#pitch: -> (110.1 to: 220.1)), (#ratio: -> (0.8 to: 1.2)),
					(#ampl: -> (0.2 to: 1.0)), (#index: -> (1.0  to: 6.0)))).
	rand := Random new.
	events eventsDo: [ :ev | | inst |					"plug in voice properties"
		inst := rand nextIntFrom: 1 to: 4.
		ev voice: ('/i', inst printString, '/pn')].
	map := OrderedCollection new: 5.				"set up the parameter map"
	map add: [ :e | e duration asSec value].
	map add: [ :e | e ampl value].
	map add: [ :e | e pitch asHz value].
	map add: [ :e | e pitch asHz value * e ratio].
	map add: [ :e | e index].
	voice := self onPort: (OSCPort to: #[127 0 0 1] port: 54321).	"edit this to taste."
	voice  instrument: nil.							"no default instrument, use the events voices"
	voice parameterMap: map.					"plug in the parameter map"
	events playOn: voice!

midiScaleExample
	"OSCVoice midiScaleExample"

	 | list voice |
	list := EventList scaleExampleFrom: 48 to: 60 in: 1500.
	voice := self default.
	voice play: list.!

snd_example1
	"Play a few random notes on several of the CSL sound file instruments"
		"OSCVoice snd_example1"
	"Startup up the CSL OSC demo and run these"
	"CSL SndFileInstrument has args: ampl, pos, start, stop"

	| events voice map rand dir |
						"create the event list"
	rand := Random new.
	dir := '/Users/stp/CSL/Data/'.
	events := EventList named: EventList nextName.
	25 timesRepeat:
		[ | in |
		in := rand nextIntFrom: 5 to: 8.
		events add: (MusicEvent new duration: (rand nextFrom: 0.1 to: 0.5); 
				voice: ('/i', in printString, '/pn');
				ampl: (rand nextFrom: 0.2 to: 1.0); 
				pos: (rand nextFrom: -1.0 to: 1.0) "; 
				start: (rand nextFrom: 0.2 to: 0.5); 
				stop: (rand nextFrom: 0.2 to: 0.5)")].
	map := OrderedCollection new: 2.				"set up the parameter map"
	map add: [ :e | e ampl value].
	map add: [ :e | e pos].
"	map add: [ :e | e start].
	map add: [ :e | e stop].
"	voice := self onPort: (OSCPort to: #[127 0 0 1] port: 54321).	"edit this to taste."
	voice  instrument: nil.							"no default instrument, use the events voices"
	voice send: '/i5/fi' args: (Array with: (dir, 'moon.snd')).
	voice send: '/i6/fi' args: (Array with: (dir, 'wet.snd')).
	voice send: '/i7/fi' args: (Array with: (dir, 'round.snd')).
	voice send: '/i8/fi' args: (Array with: (dir, 'shine.snd')).
	voice parameterMap: map.					"plug in the parameter map"

	events playOn: voice! !

!Siren.CsoundVoice methodsFor: 'events'!

writeFooter

	stream cr; nextPutAll: 'end'; cr; cr.
	^self!

writeHeader

	stream nextPutAll: ';; csound score file created ', Date today printString; cr.
	stream cr; nextPutAll: 'f1 0 1024 10 1'; cr.
	stream cr.
	^self! !

!Siren.CsoundVoice class methodsFor: 'instance creation'!

defaultParameterMap

	| map |
	(map := OrderedCollection new: 8) add: [:t2 | 'i' , (t2 voice == nil
				ifTrue: [1]
				ifFalse: [t2 voice]) printString].
	map add: #start.
	map add: [ :t3 | t3 duration asSec value asFloat printString: 4].
	map add: [ :t4 | t4 loudness asDB positiveDB printString: 4].
	^map! !

!Siren.CsoundVoice class methodsFor: 'examples'!

randomExampleToFileAndEdit
	"CsoundVoice randomExampleToFileAndEdit"

	self randomExampleToFileNamed: 'rand.sco'.
	^self!

randomExampleToFileNamed: fnam 
	"Create a random event list and store it on a file."
	"CsoundVoice randomExampleToFileAndEdit"

	| evt strm list rand vox |
	strm := (Filename named: fnam) writeStream.
	rand := Random new.
"Create the event list and add a few additional properties"
	(list := EventList randomExample: 64)
		do: [ :ass | 
			(evt := ass event) at: #pos put: rand next.
			evt at: #prop1 put: (rand nextFrom: 320 to: 480).
			evt at: #prop2 put: (rand nextIntFrom: 12000 to: 23000)].
"Create a csounc voice and customize its parameter map."
	(vox := CsoundVoice newNamed: '1' onStream: strm) addMap: #pos.
	vox addMap: [ :item | ((item at: #prop1) / 1000) printString: 4].
	vox addMap: #prop2.
"Play the list on the voice."
	vox play: list.
	strm close.
	(Filename named: fnam) edit.
	^self! !

!Siren.MIDIFileVoice methodsFor: 'accessing'!

defaultEventClass
	"	***This is decompiled code.*** 
	No source was available"

	^MusicEvent!

tempo
	"	***This is decompiled code.*** 
	No source was available"

	^tempo!

tempo: t1 
	"	***This is decompiled code.*** 
	No source was available"

	tempo := t1.
	^self! !

!Siren.MIDIFileVoice methodsFor: 'read/write'!

eventList
	"	***This is decompiled code.*** 
	No source was available"

	| t1 t2 |
	t1 := EventList newAnonymous.
	self readMThd.
	[stream atEnd]
		whileFalse: 
			[t2 := EventList newAnonymous.
			self readTrackOnto: t2.
			t1 add: t2 at: 0].
	Transcript show: t1 events size printString , ' track EventLists'; cr.
	^t1!

readMThd
	"	***This is decompiled code.*** 
	No source was available"

	| t1 t2 |
	t1 := stream size.
	Transcript show: 'size: ' , t1 printString; cr.
	(t2 := stream nextWord) == 19796 ifFalse: [Transcript show: 'wierd value in MIDI header' , t2 printString; cr].
	t2 := stream nextWord.
	t2 := stream nextWord.
	t2 := stream nextWord.
	fileType := stream nextWord.
	Transcript show: 'MIDI file format is ' , fileType printString; cr.
	tracks := stream nextWord.
	Transcript show: 'number of tracks = ' , tracks printString; cr.
	ppq := stream nextWord.
	Transcript show: 'ticks per quarter beat = ' , ppq printString; cr.
	^self!

readMTrk
	"	***This is decompiled code.*** 
	No source was available"

	| t1 t2 |
	(t2 := stream nextWord) == 19796 ifFalse: [self error: 'MT expected in track chunk'].
	t2 := stream nextWord.
	t2 := stream nextWord.
	^t1 := stream nextWord!

readOnto: t1 
	"	***This is decompiled code.*** 
	No source was available"

	| t2 |
	self readMThd.
	[stream atEnd]
		whileFalse: 
			[t2 := EventList newAnonymous.
			self readTrackOnto: t2.
			t1 add: t2 at: 0].
	Transcript show: t1 events size printString , ' track.'; cr.
	^self!

readTrackOnto: t1 
	"	***This is decompiled code.*** 
	No source was available"

	| t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 |
	t8 := self readMTrk.
	t9 := stream position.
	t2 := 0.
	t3 := 0.
	[stream position < (t9 + t8)]
		whileTrue: 
			[t4 := ((t2 := t2 + self getVarLen) / ppq * 600000 / 1000 asFloat - t3) asInteger.
			t3 := t3 + t4.
			((t5 := stream next) bitAnd: 128)
				= 0 ifFalse: [t5 == 255
					ifTrue: [t6 := stream next]
					ifFalse: [(t6 := t5) == 240 ifFalse: [t5 := stream next]]].
			(t6 < 128 or: [t6 == 240])
				ifTrue: [t6 == 81
						ifTrue: 
							[tempo := 0.
							t10 := 1.
							t11 := self getVarLen.
							[t10 <= t11]
								whileTrue: 
									[tempo := tempo * 256 + stream next.
									t10 := t10 + 1]]
						ifFalse: 
							[Transcript show: 'meta or sysex, ' , (t6 printStringRadix: 16) , ' -- '.
							t12 := 1.
							t13 := self getVarLen.
							[t12 <= t13]
								whileTrue: 
									[Transcript show: (stream next printStringRadix: 16)
											, ' '.
									t12 := t12 + 1].
							Transcript cr]]
				ifFalse: 
					[(t6 bitAnd: 224)
						= 192
						ifTrue: [t7 := nil]
						ifFalse: [t7 := stream next].
					t1 add: (self defaultEventClass
							pitch: t5
							ampl: t7
							voice: (t6 bitAnd: 15))
						at: t3]].
	t1 code: #noteOnOff.
	t1 setDurations.
	Transcript show: t1 events size printString , ' events'; cr.
	^self! !

!Siren.MIDIFileVoice methodsFor: 'private'!

getVarLen
	"	***This is decompiled code.*** 
	No source was available"

	| t1 t2 |
	t1 := 0.
	t2 := 128.
	[(t2 bitAnd: 128)
		== 0]
		whileFalse: 
			[t2 := stream next.
			t1 := (t1 bitShift: 7)
						+ (t2 bitAnd: 127)].
	^t1! !

!Siren.MIDIFileVoice class methodsFor: 'instance creation'!

newOn: fnam 

	| inst |
	(inst := self new) stream: (Filename named: fnam) readStream binary.
	^inst! !

!Siren.MIDIFileVoice class methodsFor: 'examples'!

midiFileExample
	"MIDIFileVoice midiFileExample"

	(MIDIFileVoice newOn: './Data/barok.midi')
		readOnto: (EventList newNamed: #K008).
	(EventList named: #K008) edit.
	^self! !

!Siren.Rubato methodsFor: 'initialize-release'!

initialize
	selector := #tempo! !

!Siren.Rubato class methodsFor: 'examples'!

example
	"Rubato example"

	| roll rub |
	roll := ((Roll length: 2000 rhythm: 100 note: 60) ampl: 80) eventList.	"100 msec = 10/sec"
"	rub := Rubato new function: (LinearFunction from: #((0 1) (0.5 0.8) (1 1.2))); scale: 10. "
	rub := Rubato new function: (LinearFunction from: #((0 1) (1 1.5))); scale: 10.
	rub applyTo: roll.
	roll play! !

!Siren.OSCByteConvertor methodsFor: 'initialize-release'!

init
	packetData := ByteArray new writeStream.
	^self! !

!Siren.OSCByteConvertor methodsFor: 'stream'!

contents
	^packetData contents!

next: t1 put: t2 
	^packetData next: t1 put: t2!

nextPut: t1 
	^packetData nextPut: t1!

nextPutAll: t1 

	^packetData nextPutAll: t1!

nextPutType: char

	^self nextPut: char asInteger!

position
	^packetData position! !

!Siren.OSCByteConvertor class methodsFor: 'instance creation'!

new
	"	***This is decompiled code.*** 
	This may reflect a problem with the configuration of your image and its 
	sources and changes files. 
	Please refer to the documentation and the settings tool for help in setting up 
	the proper source code files."

	^super new init! !

!Siren.SelectionCloud methodsFor: 'playing'!

eventList
	"Make the cloud's events"
	"assume that the receiver's pitch, loudness and durations are actually intervals"

	| pattern rand number length start |
	pattern := EventList new.
	rand := Random new.
	density isEvent
		ifTrue: [^self eventListWithDensityFunction].
	number := duration asSeconds value asFloat * density.
	length := (1000 / density) rounded.
	start := 0.
	number truncated timesRepeat:
		[ pattern add: (MusicEvent 
			dur: (length * 2 "+ (2000 * rand next) rounded")
			pitch: (self pitch value atRandom: rand)
			ampl: (self loudness value atRandom: rand)
			voice: (self voice atRandom: rand))
			at: start.
		start := start + length].
	^pattern!

eventListWithDensityFunction
	"make the cloud's events"
	"assume that the receiver's density is a function and that the voices are a selection set."

	| dur pattern rand dens time playing ends snd |
	pattern := EventList new.
	rand := Random new.
	time := 0.0. 		"msec"
	playing := 0.
	dens := density at: 0.
	ends := SortedCollection new: (density range extent + 1) truncated.
"prime the generators"
	playing := 1.
	ends add: 0.0.							"time at first note end"
	time := 0.0.
	dur := duration asSec value.
"Step through the end times."
	[time < dur] whileTrue:
		[time := ends removeFirst.			"step to next end time"
		playing := playing - 1.
		dens := density at: (time / dur).
		[dens > playing] whileTrue: 
			[snd := self voice atRandom: rand.
			pattern add: (MusicEvent dur: snd duration 
								voice: snd 
								ampl: (1.0 / dens)) at: time.
			ends add: (time + snd duration asSec value).
			playing := playing + 1]].
	^pattern! !

!Siren.SelectionCloud class methodsFor: 'examples'!

example1
	"Create a low 4 second cloud selecting pitch, amp and voice from value arrays."
	"SelectionCloud example1"

	(SelectionCloud  dur: 4  
		pitch: #(32 40 48 50 52 55 57 )
		ampl: #(80 80 120)
		voice: #(1 2 3 4)
		density: 8) eventList "edit" play "inspect"!

exampleRand
	"Edit a selection cloud with random properties."
	"SelectionCloud exampleRand"

	| rand pBase pArray aArray score |
	rand := Random new.
	pBase := 30 + (rand next * 40) rounded.
	pArray := Array new: 2 + ((rand next * 4) rounded).
	1 to: pArray size do: [:ind | pArray at: ind put: (pBase+(rand  next * 12) rounded)].
	pBase := 20 + (rand next * 60) rounded.
	aArray := Array new: 2 + ((rand next * 6) rounded).
	1 to: aArray size do: [:ind | aArray at: ind put: (pBase+(rand  next * (120 - pBase)) rounded)].
	score := (SelectionCloud  dur: (3 + (rand next * 4) rounded)
		pitch: pArray
		ampl: aArray
		voice: (1 to: 8)
		density: (3 + (rand next * 15) rounded)) eventList.
	score edit.
"	score play.
	(Delay forMilliseconds: (score duration + 1000)) wait
"!

randomExample
	"Create a selection cloud with random properties."
	"SelectionCloud randomExample"

	| rand pBase pArray aArray score |
	rand := Random new.
	pBase := 20 + (rand next * 40) rounded.
	pArray := Array new: 2 + ((rand next * 4) rounded).
	1 to: pArray size do: [:ind | pArray at: ind put: (pBase+(rand  next * 12) rounded)].
	pBase := 20 + (rand next * 60) rounded.
	aArray := Array new: 2 + ((rand next * 6) rounded).
	1 to: aArray size do: [:ind | aArray at: ind put: (pBase+(rand  next * (120 - pBase)) rounded)].
	score := (SelectionCloud  dur: (3 + (rand next * 4) rounded)
		pitch: pArray
		ampl: aArray
		voice: (1 to: 8)
		density: (3 + (rand next * 15) rounded)) eventList.
	score inspect.
"	score play.
	(Delay forMilliseconds: (score duration + 1000)) wait
"! !

!Siren.DynamicSelectionCloud methodsFor: 'playing'!

eventList
	"make the dynamic selection cloud's events"
	"assume that the receiver's pitch, loudness and durations are actually 2-D arrays"

	| pattern rand number length start |
	pattern := EventList new.
	rand := Random new.
	number := duration asSeconds value asFloat * density.
	length := (1000 / density) rounded.
	start := 0.
	1 to: number do:
		[ :counter |
		pattern add: (MusicEvent 
			dur: (length * 2 "+ (1000 * rand next) rounded")
			pitch: ((rand next > (counter / number)) 
					ifTrue: [ (self pitch value at: 1) atRandom: rand  ]
					ifFalse: [ (self pitch value at: 2) atRandom: rand ])
			ampl: (self loudness value atRandom: rand)
			voice: (self voice atRandom: rand))
			at: start.
		start := start + length].
	^pattern! !

!Siren.DynamicSelectionCloud class methodsFor: 'examples'!

example1
	"Create a selection cloud that focuses onto a trill."
	"DynamicSelectionCloud example1"

	(DynamicSelectionCloud  dur: 4
		pitch: #( #(36 38 40 43 45) #(53 54) )
		ampl: #(80 80 120)
		voice: #(1 3 5 7)
		density: 16) eventList "edit" play "inspect"!

example2
	"Create a selection cloud that makes a transition from one triad to another"
	"DynamicSelectionCloud example2"

	(DynamicSelectionCloud dur: 4
		pitch: #( #(60 62 64) #(72 74 76) )
		ampl: #(80 80 120)
		voice: #(1 3 5 7)
		density: 12) eventList edit "inspect" "play"!

randomExample
	"Answer a dynamic selection cloud with random properties."
	"DynamicSelectionCloud randomExample"

	| rand pBase pArray1 pArray2 aArray1 score |
	rand := Random new.
	pBase := rand nextIntFrom: 42 to: 54.
	pArray1 := Array new: (rand nextIntFrom: 2 to: 4).
	1 to: pArray1 size do: 
			[:ind | pArray1 at: ind put: (rand nextIntFrom: pBase to: pBase + 12)].
	pBase := rand nextIntFrom: 60 to: 72.
	pArray2 := Array new: (rand nextIntFrom: 4 to: 8).
	1 to: pArray2 size do: 
			[:ind | pArray2 at: ind put: (rand nextIntFrom: pBase to: pBase + 12)].
	pBase := rand nextIntFrom: 40 to: 60.
	aArray1 := Array new: (rand nextIntFrom: 2 to: 6).
	1 to: aArray1 size do: 
			[:ind | aArray1 at: ind put: (rand nextIntFrom: pBase to: pBase + 50)].
	Transcript cr; show: 'DynSelCloud: ', pArray1 printString; cr; 
			show: pArray2 printString; cr; cr.
	score := (DynamicSelectionCloud  dur: 8
		pitch: (Array with: pArray1 with: pArray2)
		ampl: aArray1
		voice: (1 to: 8)
		density: (rand nextIntFrom: 5 to: 15)) eventList.
"	score inspect.
"
"	score play.
	(Delay forMilliseconds: (score duration + 1000)) wait
"
	score edit! !

!Siren.STreeAccessor methodsFor: 'accessing'!

childAccessor: anObject
	"Set the cA of the receiver."

	childAccessor := anObject!

terminalQuery: anObject
	"Set the tQ of the receiver."

	terminalQuery := anObject!

treeAccessor
	"Answer the receiver."

	^self! !

!Siren.STreeAccessor methodsFor: 'tree accessing'!

childrenOf: aNode
	"Answer the children of the argument by using the child accessor."

	(childAccessor isSymbol)
		ifTrue: [^aNode perform: childAccessor].
	(childAccessor isMemberOf: BlockClosure)
		ifTrue: [^childAccessor value: aNode].
	self error: 'Unknown child accessor type.'!

isTerminal: aNode
	"Answer whether the argument is terminal by using the terminal query."

	(terminalQuery isSymbol)
		ifTrue: [^aNode perform: terminalQuery].
	(terminalQuery isMemberOf: BlockClosure)
		ifTrue: [^terminalQuery value: aNode].
	self error: 'Unknown terminal query type.'!

leavesOf: aRoot
	"Answer the leaves of the argument by using the child accessor."

	| kids leaves |
	leaves := OrderedCollection new.
	(childAccessor isSymbol)
		ifTrue: [kids := aRoot perform: childAccessor]
		ifFalse: [(childAccessor isMemberOf: BlockClosure)
			ifTrue: [kids := childAccessor value: aRoot]
			ifFalse: [self error: 'Unknown child accessor type.']].
	kids isEmpty
		ifFalse: [kids do:
			[ :aChild |
			(self isTerminal: aChild)
				ifTrue: [leaves add: aChild]
				ifFalse: [leaves addAll: (self leavesOf: aChild)]]].
	^leaves! !

!Siren.STreeAccessor class methodsFor: 'instance creation'!

subject: aS terminalQuery: aTQ childAccessor: aCA itemGenerator: anIG
	"Answer an initialized instance."

	^(self new) subject: aS;
			terminalQuery: aTQ;
			childAccessor: aCA;
			itemGenerator: anIG! !

!Siren.ExponentialFunction methodsFor: 'accessing'!

at: anIndex
	"Answer the value at the given index in my range--do exponential interpolation such
 	that if f(x, i) is the i-th function value in the transition from breakpoint v[J] to 
	v[J+1], then:
		f(x) = v[J] + (v[J+1] - v[J])*(1 - exp(i*x[J]/(N-1)))/(1 - exp(x[J]))
	for 0 <= i < N, where N is the number of function points between t[J] and the next 
	horizontal value, and x is the exponential weight whereby x = 0 will yield a 
	straight line, x < 0 will yield an exponential transition, and x > 0 will yield a 
	logarithmic transition."

	| weight interp xVal count x1 x2 y1 y2|
	xVal := data first x.
	count := 1.
	[xVal < anIndex] whileTrue:
		[count := count + 1.
		count > data size
			ifTrue: [^data last x].
		xVal := (data at: count) x].
	xVal = anIndex 
		ifTrue: [^(data at: count) y]
		ifFalse: [x1 := (data at: (count - 1)) x.
			x2 := (data at: count) x.
			y1 := (data at: (count - 1)) y.
			y2 := (data at: count) y.
			weight := (data at: count - 1) z.
			interp := (anIndex - x1) / (x2 - x1).
			weight == 0		"special case for linear interpolation"
				ifTrue: [^y1 + (interp * (y2 - y1))].
			^y1 + ((y2 - y1) * ((1 - (interp * weight) exp) / (1 - weight exp)))]!

pointAt: index
	"Answer the given value in the receiver's breakpoint collection as a 2-D point."

	| pt |
	pt := data at: index.
	^pt x @ pt y! !

!Siren.ExponentialFunction methodsFor: 'printing'!

printOn: aStream
	"Format and print the receiver on the argument."

	| pt |
	data size > 16
		ifTrue: [^super printOn: aStream].
	aStream nextPutAll: self class name, ' from: '.
	aStream nextPut: $#; nextPut: $(.
	1 to: data size do: 
			[ :i |
			pt := data at: i.
			aStream nextPut: $(.
			pt x printOn: aStream.
			aStream space.
			pt y printOn: aStream.
			aStream space.
			(pt respondsTo: #z)
				ifTrue: [pt z printOn: aStream].
			aStream nextPut: $); space].
			aStream nextPut: $)! !

!Siren.ExponentialFunction class methodsFor: 'instance creation'!

a: att d: dec s: sus r: rel
	"Answer an ADSR envelope."
	"FunctionView onFunction: (ExponentialFunction a: 0.06 d: 0.2 s: 0.4 r: 0.3)"

"	| susT |
	susT := 1.0 - (att + dec).
	^ExponentialFunction from: 
		{ { 0. 0. 3 }. { att. 1. -3 }. { att+dec. sus. -2 }. { susT. sus. -2 }. { 1. 0 } }"!

default
	"Answer a default instance of the receiver class."

	^ExponentialFunction from: #((0 0 3 ) (0.08 1.0 -3) (0.2 0.5 -1) (1 0))!

from: anArrayOfPoints
	"Answer a function with the given points."
	"ExponentialFunction from: #((0 0 5) (0.5 1 5) (1 0))"

	| me |
	me := self ofSize: anArrayOfPoints size.
	anArrayOfPoints do: 
			[ :anArray | 
			me add: (anArray first @ (anArray at: 2) @ anArray last)].
	^me! !

!Siren.ExponentialFunction class methodsFor: 'examples'!

expADSRViewExample
	"Make an exp seg and open a view on its form."
	"ExponentialFunction expsegViewExample"

	FunctionView onFunction: 
		(ExponentialFunction from: #((0 0 -5) (0.2 1 -3) (0.8 0.5 -2) (1 0)))!

expASRViewExample
	"Make an exp seg and open a view on its form."
	"ExponentialFunction expASRViewExample"

	FunctionView onFunction: 
		(ExponentialFunction from: #((0 0 -3) (0.3 0.9 0) (0.7 0.9 -3) (1 0)))!

expsegViewExample
	"Make an exp seg and open a view on its form."
	"ExponentialFunction expsegViewExample"

	FunctionView onFunction: 
		(ExponentialFunction from: #((0 0 -3) (0.2 1 -5) (0.6 0.5 -1) (1 0)))! !

!Siren.CmusicVoice methodsFor: 'events'!

endLine

	stream nextPut: $;; cr.
	^self!

writeFooter

	stream cr; nextPutAll: 'ter;'; cr; cr.
	^self!

writeHeader
	"Write the cmusic score file header."

	stream nextPutAll: '/* cmusic score file created ', Date today printString , ' */'; cr.
	stream cr; nextPutAll: '#include <carl/cmusic.h>'; cr.
	stream nextPutAll: '#include "instruments.sc"'; cr.
	stream cr; nextPutAll: 'SINE(f1);'; cr; cr.
	^self! !

!Siren.CmusicVoice class methodsFor: 'instance creation'!

defaultParameterMap
	"Answer the default cmusic p-map"

	| map |
	map := OrderedCollection new: 8.
	map add: 'note '.
	map add: #start.
	map add: 'ins'.
	map add: [ :t2 | t2 duration asSec value printString ].
	map add: [ :t3 | (t3 loudness asDB value printString ), 'dB'].
	^map! !

!Siren.CmusicVoice class methodsFor: 'examples'!

randomExampleToFileAndEdit
	"CmusicVoice randomExampleToFileAndEdit"

	self randomExampleToFileNamed: 'rand.sc'.
	^self!

randomExampleToFileNamed: fnam 
	"Store the given score to a file"

	| ev strm list rand vox |
	strm := (Filename named: fnam) writeStream.
	rand := Random new.
"Create the event list and add a few additional properties"
	(list := EventList randomExample: 64)
		do: 
			[ :ass | 
			(ev := ass event) at: #pos put: rand next.
			ev at: #prop1 put: (rand nextFrom: 320 to: 480).
			ev at: #prop2 put: (rand nextIntFrom: 12000 to: 23000)].
"Create a cmusic voice and customize its parameter map."
	(vox := CmusicVoice newNamed: 'violin' onStream: strm) parameterMap at: 3 
			put: [ :t8 | #(#in1 #in2 #in3 #in4) at: t8 voice \\ 4 + 1].
	vox addMap: [ :t9 | (t9 pitch asHz value printString: 5), 'Hz'].
	vox addMap: #pos.
	vox addMap: [ :it | ((it at: #prop1) / 10) truncated printString].
	vox addMap: #prop2.
"Play the list on the voice."
	vox play: list.
	strm close.
	(Filename named: fnam) edit.
	^self!

randomExampleToTranscript
	"CmusicVoice randomExampleToTranscript"

	(CmusicVoice newNamed: 'violin' onStream: Transcript)
		play: (EventList randomExample: 8).
	^self! !

!Siren.SampledSound methodsFor: 'initialize-release'!

initialize
	"Set up a default sound."

	super initialize.
	firstIndex := 0.
	folder := ''.
	name := ''.
	rate := 44100.
	channels := 1.
	format := #lin16Bit.! !

!Siren.SampledSound methodsFor: 'sample accessing'!

at: anIndex 
	"Answer the value at the given index (between 0 and 1)."

	anIndex isSymbol
		ifTrue: [^ super at: anIndex].
	^ self sampleAt: anIndex!

cutFrom: startSample to: stopSample
	"Cut the designated section from the receiver; answer a new sound (!!)."

	^GapSound on: self cutFrom: startSample to: stopSample!

floatSampleAt: index
	"Answer the sample at the given index as a normalized floating-point number."

	^(self intSampleAt: index) asFloat / self class maxSample!

floatSampleAt: index put: aValue
	"Stuff the given floating-point sample at the given index after scaling."

	self intSampleAt: index put: (aValue * self class maxSample) truncated!

intSampleAt: index
	"Answer the sample at the given index as an integer."

	^self sampleAt: index!

intSampleAt: index put: aValue
	"Stuff the given integer sample at the given index."

	self sampleAt: index put: aValue!

paste: aSound from: startSample to: stopSample at: index
	"Paste the given sound into the receiver; answer a new sound (!!)."

	^CompositeSound on: self paste: aSound from: startSample to: stopSample at: index!

sampleAt: index
	"Answer the sample at the given index, mapping and paging as necessary."

	| newIndex |
	self samples == nil
			ifTrue: [^self error: 'There are no samples for this sound.'].
	newIndex := self mapSampleIndex: index.
	newIndex > domain last		"answer 0 after end of samples."
		ifTrue: [^0].
	newIndex <= samplesInMemory
		ifTrue: [^self privateSampleAt: newIndex]
		ifFalse: ["Transcript show: 'Reading next page of sample file.'; cr."
				self realSound readSamplesFrom: newIndex + firstIndex.
				^self sampleAt: newIndex].
	self error: 'Indexing off the end of a sound.'!

sampleAt: index put: aValue
	"Stuff the given sample at the given index (after lots of checking and testing)."

	| newIndex |
	newIndex := self mapSampleIndex: index.
	newIndex <= data size
		ifFalse: [self error: 'Attempt to store past the end of a sound.'].
	(newIndex <= samplesInMemory)
		ifFalse: ["Transcript show: 'Reading next page of sample file.'; cr."
				self realSound readSamplesFrom: newIndex + firstIndex.
				^self sampleAt: newIndex put: aValue].
	^self privateSampleAt: index put: aValue! !

!Siren.SampledSound methodsFor: 'testing'!

hasGaps
	"Answer whether the receiver has any gaps."

	^false!

isComposite
	"Answer whether the receiver is a composite sound."

	^false!

isEmpty
	"Answer whether the receiver is empty."

	^data
		ifNil: [true]
		ifNotNil: [data isEmpty]!

isVirtual
	"Answer whether the receiver is a virtual sound."

	^false! !

!Siren.SampledSound methodsFor: 'merging'!

extractChannel: cNumber into: aSound
	"Extract the given channel from the receiver into the argument sound."

	<primitive: 17021>!

mergeChannel: cNumber from: aSound
	"Merge the argument sound into the given channel of the receiver."

	<primitive: 17022>! !

!Siren.SampledSound methodsFor: 'enumerating'!

do: aBlock 
	"Evaluate aBlock with each of the receiver's elements as the argument."

	self samples do: aBlock! !

!Siren.SampledSound methodsFor: 'interpolating'!

autoCorrelationBetween: index1 and: index2 length: length
	"Answer the cummulative error between the portions of my waveform starting at the given two indices and extending for the given length. The larger this error, the greater the difference between the two waveforms."

	| error i1 e |
	error := 0.
	i1 := index1.
	index2 to: (index2 + length - 1) do: [:i2 |
		e := (data at: i1) - (data at: i2).
		e < 0 ifTrue: [e := 0 - e].
		error := error + e.
		i1 := i1 + 1].
	^ error!

errorBetween: sampleArray1 and: sampleArray2
	"Answer the cummulative error between the two sample arrays, which are assumed to be the same size."

	| error e |
	error := 0.
	1 to: sampleArray1 size do: [:i |
		e := (sampleArray1 at: i) - (sampleArray2 at: i).
		e < 0 ifTrue: [e := 0 - e].
		error := error + e].
	^ error!

fadeInOver: fTime
	"Apply a fade-in ramp to the receiver with the given time."

	| fDur |
	fTime > self duration asSec value
		ifTrue: [Transcript show: 'Illegal fade-in time'; cr. ^nil].
	fDur := fTime * self rate * self channels.
	1 to: fDur do:
		[ :index |
		self sampleAt: index 
			put: (self sampleAt: index) * (index / fDur)]!

fadeOutOver: fTime
	"Apply a fade-in ramp to the receiver with the given time."

	| fDur s0 |
	fTime > self duration asSec value
		ifTrue: [Transcript show: 'Illegal fade-in time'; cr. ^nil].
	fDur := fTime * self rate * self channels.
	s0 := domain - fDur.
	s0 to: domain do:
		[ :index |
		self sampleAt: index 
			put: (self sampleAt: index) * (1.0 - (index - s0 / fDur))]!

interpolatedWindowAt: index width: nSamples
	"Return an array of N samples starting at the given index in my data."

	| scale baseIndex scaledFrac scaledOneMinusFrac prevSample nextSample v |
	scale := 10000.
	index isInteger
		ifTrue: [^ (index to: index + nSamples - 1) collect: [:i | data at: i]].
	baseIndex := index truncated.
	scaledFrac := ((index asFloat - baseIndex) * scale) truncated.
	scaledOneMinusFrac := scale - scaledFrac.
	prevSample := data at: baseIndex.
	^ (baseIndex + 1 to: baseIndex + nSamples) collect: [:i |
		nextSample := data at: i.
		v := ((nextSample * scaledFrac) + (prevSample * scaledOneMinusFrac)) // scale.
		prevSample := nextSample.
		v].!

sampledFrom: aStart to: aStop into: anArray
	"Place a down-sampled version of the receiver in the argument array."

	<primitive: 17081> "ca n'existe pas"!

sampledTo: anArray
	"Place a down-sampled version of the receiver in the argument array."

	^self sampledFrom: 1 to: self size into: anArray!

sampleMaxFrom: aStart to: aStop into: anArray
	"Place a version of the root-mean-square energy of the receiver in the argument array."
	
"	| arr snd |
	arr := Array new: 64 withAll: 0.0.
	snd := StoredSound fileExample.
	snd sampleMaxFrom: 1 to: snd size into: arr.
	(Function from: arr) edit."

"	<primitive: 17082>
"
	| factor max |
	anArray size > self size
		ifTrue: [^self error: 'Cannot take and rms into a larger array...'].
	factor := ((aStop - aStart) / (anArray size)) asFloat.
	0 to: anArray size - 1 do:
		[ :i | 
		max := 0.
		(i * factor) asInteger to: (i + 1 * factor) asInteger do:
			[ :j |
			max := max max: (self sampleAt: (aStart + j))].
		anArray at: i + 1 put: (max asFloat / 32767.0) asFloat].! !

!Siren.SampledSound methodsFor: 'envelopes'!

average
	"Answer the average sample value of the receiver (normally the DC offset)."

	| value min max samp |
	min := max := self sampleAt: 1.
	value := 0.
	1 to: self size do:
		[ :i |
		samp := self sampleAt: i.
		value := value + samp.
		(samp > max) ifTrue: [max := samp].
		(samp < min) ifTrue: [min := samp]].
	self domain: (min to: max).
	value := (value / self size) asFloat.
	self at: #average put: value.
	^value!

computeEnvelope: type
	"Compute a sample envelope for the receiver with 128 points per second."

	| eSize env |
	self size = 0 ifTrue: [^self].
	Cursor execute showWhile:
		[eSize := (self duration value truncated max: 1) * 128.
"		Transcript show: 'Creating envelope of size: ', eSize printString; cr."
		env := (Array new: eSize).
		type == #peak
			ifTrue: [self peaksFrom: 1 to: self size into: env]
			ifFalse: [self rmsFrom: 1 to: self size into: env]].
	self envelope: env.
	self hasFile
		ifTrue: [Cursor write showWhile: [self writeEnvelope: type]].!

computePeakEnvelope
	"Compute a windowed peak sample envelope for the receiver with 1024 points per 4 seconds."

	self computeEnvelope: #peak!

computeRMSEnvelope
	"Compute a windowed root-mean-square sample envelope for the receiver."

	self computeEnvelope: #RMS!

edit
	"Edit the receiver."

	SoundView openOn: self!

max
	"Answer the maximum value of the samples."

	| max t |
	max := 0.
	1 to: self size do:
		[ :i | 
		t := self sampleAt: i.
		(t abs > max) ifTrue: [max := t abs]].
	^max!

offsetBy: aFactor
	"Answer a copy of the receiver offset by the given factor."

	^self offsetFrom: 1 to: self size by: aFactor!

offsetFrom: aStart to: aStop by: anOffset
	"Answer a copy of the receiver offset by the given factor."

	| newSound |
	newSound := self copyAllButSamples.
	1 to: self size do:
		[ :i |
		newSound sampleAt: i put: ((self sampleAt: i) + anOffset)].
	^newSound!

peakEnvelope
	"Answer the receiver's peak-detected envelope, or compute a new one."

	| env |
	env := self envelope.
	(env ~~ nil and: [env size > 0])
		ifTrue: [^env].
	(self readEnvelope: #peak)
		ifFalse: [self computePeakEnvelope].
	^self envelope!

peaksFrom: aStart to: aStop into: anArray
	"Place a version of the root-mean-square energy of the receiver in the argument array."
	
"	| arr snd |
	arr := Array new: 1024.
	snd := 'Nomad:Squeak:Siren:sounds:kombination.1.aiff' asSoundFile.
	TimeProfileBrowser onBlock: [10 timesRepeat: 
		[snd rmsFrom: 1 to: snd size into: arr]].
	(Function from: arr) edit."

"	<primitive: 17082>
"
	| aSize factor max maxPos peak sample |
	aSize := anArray size.
	aSize > self size
		ifTrue: [^self error: 'Cannot take and rms into a larger array...'].
	factor := ((aStop - aStart) asFloat / (aSize)).
	max := 0.
	aStart = 0
		ifTrue: [0 to: aSize - 1 do:
				[ :i | 
				peak := 0.
				(i * factor) truncated to: (i + 1 * factor) truncated do:
					[ :j |
					sample := (self sampleAt: j) abs.
					sample > max
						ifTrue: [max := sample.
							maxPos := j].
					sample > peak
						ifTrue: [peak := sample]].
				anArray at: i + 1 put: peak truncated]]
		ifFalse: [0 to: aSize - 1 do:
				[ :i |
				peak := 0.0.
				(i * factor) truncated to: (i + 1 * factor) truncated do:
					[ :j |
					sample := (self sampleAt: (aStart + j)) abs.
					sample > max
						ifTrue: [max := sample.
							maxPos := aStart + j].
					sample > peak
						ifTrue: [peak := sample]].
				anArray at: i + 1 put: peak truncated]].
	self at: #maxSample put: max.
	self at: #maxSamplePos put: maxPos!

readEnvelope: type
	"Try to read the sample envelope for the receiver from a file named XXX.env."

	| sfName ext index eName eStream eSize env |
	sfName := self name.
	sfName isEmpty ifTrue: [^false].
	ext := (type == #peak ifTrue: ['pk'] ifFalse: ['env']).
	^self hasFile
		ifTrue: [index := sfName indexOfSubCollection: '.snd' startingAt: 1.
			eName := (sfName copyFrom: 1 to: index), ext.
			eName asFilename exists
				ifTrue: [eStream := eName asFilename readStream binary.
					eSize := eName asFilename fileSize / 2.
"					Transcript show: 'Reading env ', eName, 
							' size ', eSize printString; cr."
					env := Array new: eSize.
					1 to: eSize do:
						[ :i | env at: i put: eStream nextWord].
					eStream close.
					self envelope: env.
					true]
			ifFalse: ["Transcript show: 'No envelope file found ', 
						(sfName copyFrom: 1 to: index), ext; cr. "
					false]]
		ifFalse: [self envelope ~~ nil]!

rmsEnvelope
	"Answer the receiver's envelope, or compute a new one."

	| env |
	env := properties at: #envelope ifAbsent: [nil].
	(env ~~ nil and: [env size > 0])
		ifTrue: [^env].
	(self readEnvelope: #RMS)
		ifFalse: [self computeRMSEnvelope].
	^self envelope!

rmsFrom: aStart to: aStop into: anArray
	"Place a version of the root-mean-square energy of the receiver in the argument array."
	
"	| arr snd |
	arr := Array new: 512.
	snd := StoredSound fileExample.
	snd rmsFrom: 1 to: snd size into: arr.
	(Function from: arr) edit."

	| aSize factor max maxPos sum sample |
	aSize := anArray size.
	aSize > self size
		ifTrue: [^self error: 'Cannot take and rms into a larger array...'].
	factor := ((aStop - aStart) asFloat / (aSize)).
	max := -1.0e30.
	0 to: aSize - 1 do:
		[ :i | 
		sum := 0.0.
		(i * factor) truncated to: (i + 1 * factor) truncated do:
			[ :j |
			sample := self sampleAt: (aStart + j) abs.
			sample > max
				ifTrue: [max := sample.
						maxPos := aStart + j].
			sum := sum + (sample * sample)].
		anArray at: i + 1 put: (sum sqrt / factor) truncated].
	self at: #maxSample put: max.
	self at: #maxSamplePos put: maxPos!

rmsTo: anArray
	"Place a version of the root-mean-square energy of the receiver in the argument array."

	^self rmsFrom: 1 to: self size into: anArray!

scaledBy: aScaleFactor
	"Answer a copy of the receiver scaled by the given factor."

	^self scaledFrom: 1 to: self size by: aScaleFactor!

scaledByEnvelopeArray: anArray
	"Answer a copy of the receiver scaled by the given envelope array."

	^self scaledFrom: 1 to: self size byEnvelopeArray: anArray!

scaledByFunction: aFunction
	"Answer a copy of the receiver scaled by the given breakpoint envelope expressed as points."

	^self scaledFrom: 1 to: self size byFunction: aFunction!

scaledFrom: aStart to: aStop by: aScaleFactor
	"Answer a copy of the receiver scaled by the given factor."

"	<primitive: 17084>"
	| newSound |
	newSound := self copyAllButSamples.
	1 to: self size do:
		[ :i |
		newSound sampleAt: i put: ((self sampleAt: i) * aScaleFactor)].
	^newSound!

scaledFrom: aStart to: aStop byEnvelopeArray: anArray
	"Answer a copy of the receiver scaled by the given envelope array."

	<primitive: 17086>!

scaledFrom: aStart to: aStop byFunction: aFunction
	"Answer a copy of the receiver scaled by the given breakpoint envelope expressed as points."
	"SoundView openOn: (StoredSound sawtooth scaledByFunction: (LinearFunction from: #((0 0) (1 1))))"

"	<primitive: 17085>"
	| newSound ssize |
	newSound := self copyAllButSamples.
	ssize := 1.0 / self size.
	1 to: self size do:
		[ :i |
		newSound sampleAt: i put: ((self sampleAt: i) * (aFunction at: (i * ssize)))].
	^newSound!

writeEnvelope: type
	"Write the sample envelope for the receiver to a file named XXX.env or XXX.pk."

	| sfName index eName env eStream eSize |
	sfName := self name.
	index := sfName indexOfSubCollection: '.snd' startingAt: 1.
	type == #peak		"or #RMS"
		ifTrue: [eName := (sfName copyFrom: 1 to: index), 'pk']
		ifFalse: [eName := (sfName copyFrom: 1 to: index), 'env'].
	eName asFilename exists
		ifTrue: [(Dialog confirm: 
					'Do you really want to overwrite the envelope file ', eName)
			ifFalse: [^self]].
	eStream := eName asFilename writeStream binary.
	env := self envelope.
	eSize := env size.
"	Transcript show: 'Writing envelope file ', eName, 
						' size ', eSize printString; cr."
	1 to: eSize do:
		[ :i | 
		 eStream nextWordPut: (env at: i)].
	eStream commit; close! !

!Siren.SampledSound methodsFor: 'printing'!

printOn: aStream
	"pretty-print the receiver."

	aStream nextPutAll: '(', self class name, ' '; cr.
	name == nil
		ifFalse: [aStream tab; nextPutAll: 'named: ', name printString; cr].
	duration == nil
		ifFalse: [aStream tab; nextPutAll: 'duration: ', duration printString; cr].
	rate == nil
		ifFalse: [aStream tab; nextPutAll: 'rate: ', rate printString; cr].
	channels == nil
		ifFalse: [aStream tab; nextPutAll: 'channels: ', channels printString; cr].
	format == nil
		ifFalse: [aStream tab; nextPutAll: 'format: ', format printString; cr].
	range == nil
		ifFalse: [aStream tab; nextPutAll: 'range: ', range printString; cr].
	properties == nil
		ifFalse: [properties associationsDo:
			[ :ass |
			ass key == #envelope
				ifFalse: [aStream tab; nextPutAll: ass key, ': ', ass value printString; cr]]].
	self envelope == nil
		ifFalse: [aStream tab; nextPutAll: 'envelope: ', self envelope size printString, ' values'; cr].
	self cueList == nil
		ifFalse: [aStream tab; nextPutAll: 'cue list: ', self cueList size printString, ' regions'; cr].
	self printSamplesOn: aStream.
	aStream nextPutAll: ')'; cr.!

printSamples
	"pretty-print a few samples."

	| stream |
	stream := WriteStream on: (String new: 4096).
	1 to: 512 do:
		[ :i | stream nextPutAll: ((self sampleAt: i) printString); cr].
	^stream contents!

printSamplesOn: aStream
	"Pretty-print a few samples--32 by default, 1024 if shift-down, *all* of ctrl and shift down."

	| number |
	data ifNil: [^aStream nextPutAll: '(no samples)'].
	number := 128 min: self size.
	aStream tab; nextPutAll: 'samples: #('.
	1 to: number do:
		[ :i | aStream nextPutAll: ((self sampleAt: i) printString), ' '].
	aStream nextPutAll: '...'.
	aStream nextPut: $);  cr.!

storeOnFileNamed: aName
	"Save the receiver on the sound file named by the argument."

	(SoundFile named: aName) saveSound: self! !

!Siren.SampledSound methodsFor: 'private'!

getData
	"Read samples in from the file is none in memory."

	| realme |
	samplesInMemory > 0
		ifTrue: [data := nil].
	realme := SampledSound fromFile: self fullName.
	data :=  realme data.
	samplesInMemory := realme samplesInMemory.
	domain := realme domain.!

mapSampleIndex: index
	"Map the given sample index according to the 'page' (firstIndex) of the receiver."

	^index - firstIndex!

privateSampleAt: index
	"Answer the sample at the given index as an integer."

	^data isCPointer
		ifTrue: [data at: index - 1]
		ifFalse: [(data at: index) - 32767]!

privateSampleAt: index put: aValue
	"Stuff the given sample at the given index (safely)."

	data at: index put: (((aValue truncated + 32767) min: 65536) max: 0)!

readSamplesFrom: index
	"Read in samples from disk starting a bit before the given index."

	| file fSize offset |
	file := self file.
	file == nil
		ifTrue: [self error: 'There is no sound file associated with this sound.'].
	fSize := file size.
	offset := index - 128.
	(offset + samplesInMemory) > fSize
		ifTrue: [offset := fSize - samplesInMemory].
	Cursor read showWhile: [file readSamplesStartingAt: offset].
	firstIndex := offset!

realSound
	"Answer the real sound for the receiver (overridden in virtual subclasses)."

	^self!

scaleSampleIndex: index
	"Scale the given sample index according to the sample size of the receiver."

	^index * self sampleSize - 1.! !

!Siren.SampledSound methodsFor: 'accessing'!

allocateData

	| sound samps |
	samps := self size " * channels". 
	(((#lin16Bit -> [sound := SampledSound new initialize.
			sound samples: (WordArray new: samps withAll: 0)])),
	(#float32Bit -> [sound := FloatSound new initialize.
			sound samples: (ByteArray new: samps * 4).
			1 to: samps do: [ :i | sound samples floatAt: i put: 0.0]]))
		case: format
		otherwise: [self error: 'Unknown sound format.'].
	samplesInMemory := samps!

channels
	"Answer the receiver's 'channels'."

	^channels!

channels: anObject
	"Set the receiver's instance variable 'channels' to be anObject."

	channels := anObject!

copy
	"Answer a 'clean' copy of the receiver."

	| newMe |
	newMe := SampledSound named: self name
			size: self size format: self format.
	self properties associationsDo:
		[ :ass |
		newMe at: (ass key) put: (ass value copy)].
	1 to: self size do:
		[ :index |
		newMe sampleAt: index put: (self sampleAt: index)].
	^newMe!

copyAllButSamples
	"Answer a 'clean' copy of the receiver, but don't write in the samples yet."

	| newMe |
	newMe := SampledSound named: self name
			size: self size 
			rate: self rate
			channels: self channels 
			format: self format.
	self properties associationsDo:
		[ :ass |
		newMe at: (ass key) put: (ass value copy)].
	^newMe!

copyFrom: start to: stop
	"Answer a copy of the receiver for the given sample range."
	"((StoredSound sweepDur: 1.0 rate: 44100 from: 50 to: 5000 chans: 1)
				copyFrom: 10000 to: 10512) edit"

	| newMe |
	newMe := self copyAllButSamples.
	newMe size: stop - start + 1.
	newMe allocateData.
	start to: stop - 1 do:
		[ :index |
		newMe sampleAt: index - start + 1 put: (self sampleAt: index)].
	^newMe!

cueNamed: cName
	"Answer a sound derived from the receiver using the samples between the named cue points."

	| snd start stop region |
	region := self cueRegionNamed: cName.
	start := region first.
	stop := region last.
	snd := self copyAllButSamples.
	snd size: (stop - start).
	snd samples: (ByteArray new: (stop - start * self sampleSize)).
	start to: stop do:
		[ :ind |
		snd sampleAt: (ind - start + 1) put: (self sampleAt: ind)].
	^snd.!

envelope
	"Answer the receiver's envelope."

	properties ifNil: [^nil].
	^properties at: #envelope ifAbsent: [nil]!

envelope: anEnv
	"Set the receiver's envelope property."

	properties at: #envelope put: anEnv!

file
	"Answer the sound file for the receiver (if present)."

	| file |
	self hasFile
		ifTrue: [file := SoundFile named: self name.
			file sound: self.
			^file]
		ifFalse: [^nil]!

firstIndex
	"Answer the receiver's 'firstIndex'."

	^firstIndex!

firstIndex: anObject
	"Set the receiver's instance variable 'firstIndex' to be anObject."

	firstIndex := anObject!

floatsFrom: aStart to: aStop
	"Answer a copy of the receiver's data within the given sample range."

	| samps |
	samps := ByteArray new: (aStop - aStart) * 4.
	aStart to: aStop - 1 do:
		[ :i | samps floatAt: (i - aStart + 1) put: (self floatSampleAt: i)].
	^samps!

format
	"Answer the receiver's 'format'."

	^format!

format: fSymbol
	"Set the receiver's instance variable 'format' to be fSymbol."

	format := fSymbol.
	range := ((#lin16Bit -> [-32768 to: 32767]), (#float32Bit -> [-1.0 to: 1.0])) 
		case: fSymbol otherwise: [-1.0 to: 1.0]!

from: aStart to: aStop
	"Answer a copy of the receiver within the given time range."

	| sound |
	sound := SampledSound named: self name
			size: ((aStart - aStop) asSec * self rate) asInteger
			format: self format.
	((aStart * 2 - 1) max: 1) to: (aStop * 2) by: 2 do:
		[ :i | sound sampleAt: i put: (self sampleAt: i)].
	^sound!

fromSample: start toSample: finish
	"Answer a copy of the receiver within the given sample range."

	| ssize sdata |
	((start == nil) or: [finish == nil])
		ifTrue: [^SampledSound new].
	ssize := finish - start.
	sdata := Array new: ssize.
	1 to: ssize do:
		[ :index |
		sdata at: index put: (self sampleAt: index + start)].
	^SampledSound fromData: sdata
		rate: self rate
		channels: self channels
		format: self format!

hasFile
	"Answer whether the receiver is stored on a file of the same name."

	^self name asFilename exists!

longFormat
	"Answer the receiver's format as a descriptive string."

	^(	(#lin16Bit -> ['16-bit linear ints']),
		(#float32Bit -> ['32-bit linear floats']),
		(#Mu8Bit -> ['8-bit Mu-law']))
	case: (properties at: #format ifAbsent: [nil])
	otherwise: ['**unknown**']!

name
	"Answer the receiver's 'name'."

	^name!

name: anObject
	"Set the receiver's instance variable 'name' to be anObject."

	name := anObject!

rate
	"Answer the receiver's 'rate'."

	^rate!

rate: anObject
	"Set the receiver's instance variable 'rate' to be anObject."

	rate := anObject!

samples
	"Answer the instance variable 'data'."

	data ifNil: [self getData].
	^data!

samples: theArray
	"Accept the argument, 'theArray', (a Byte, Word, or Float array, or an UninterpretedBytes)
	as the new instance variable 'samples'."

	data := theArray.
	samplesInMemory := theArray size "/ self sampleSize" .
	self size: data size.!

samplesInMemory
	"Answer the receiver's 'samplesInMemory'."

	^samplesInMemory!

samplesInMemory: anObject
	"Set the receiver's instance variable 'samplesInMemory' to be anObject."

	samplesInMemory := anObject!

sampleSize
	"Answer the size in bytes of the receiver's samples (2)."

	^2!

size
	"Return the receiver's size in samples."

	domain ifNil: [^0].
	domain isInteger ifTrue: [^domain].
	^domain size!

size: aNumber
	"Set the receiver's 'size' in samples."

	domain := 1 to: aNumber.
	self duration: (Duration value: (aNumber asFloat / self rate)).
	data ifNil: [data := WordArray new: (aNumber * self channels)]!

sizeInBytes
	"Answer the size in bytes of the receiver."

	^self size * self sampleSize! !

!Siren.SampledSound methodsFor: 'play/record'!

nextBufferInto: outBuffer frames: bufferSize channels: outChannels format: sformat startingAt: startFrame
	"Copy data from the receiver into the given output buffer; answer the current sample index."

	| frames numToDo |
"	Transcript show: startFrame printString; cr."
	frames := self size.
	startFrame >= frames ifTrue: [^nil].
	sformat == format ifFalse: [self error: 'Output format not supported.'].
	numToDo := (startFrame + bufferSize) min: frames.
"If the sound and the output buffer are the same # of channels -- copy samples"
	outChannels = channels
		ifTrue: [1 to: numToDo do:
			[ :ind |
			0 to: channels - 1 do:
				[ :cha | | pos current samp |
				pos := ind + cha.
				current := outBuffer at: pos - 1.
				samp := self sampleAt: (startFrame + pos) "+ 32768".
				outBuffer at: (pos - 1) put: current + samp]].
			numToDo = frames ifTrue: [^nil].
			^startFrame + bufferSize].
"else copy mono sound to multi-channel output"
	channels = 1
		ifTrue: [1 to: (bufferSize min: frames) do:
			[ :ind | | samp newSamp |
			newSamp := (data at: (startFrame + ind)) + 32767.
			0 to outChannels - 1 do:
				[ :cha |
				samp := outBuffer at: (ind + cha).
				outBuffer at: (ind + cha) put: samp + newSamp]].
		(startFrame + bufferSize) >= frames
			ifTrue: [^nil].
		^startFrame + bufferSize].
	self error: 'unhandled sound output channel mismatch.'!

play
	"Play the receiver out over the default sound port."
	"(SampledSound sweepDur: 2 rate: 44100 from: 20 to: 1000 chans: 1) play"

	SoundPort default play: self! !

!Siren.SampledSound class methodsFor: 'instance creation'!

default
	"Answer the default empty sound."
	"StoredSound default"

	^self duration: 0.5 rate: 44100 channels: 1!

duration: aDur named: nameString rate: aRate channels: aChannels format: aFormat
	"Answer a new 16-bit linear sound with the given properties."

	^self named: nameString size: (aDur * aRate) truncated rate: aRate 
				channels: aChannels format: aFormat!

duration: aDur rate: aRate channels: aChannels
	"Answer a new sound with the given properties."

	^self named: nil size: (aDur * aRate) truncated rate: aRate 
				channels: aChannels format: self formatSymbol!

duration: aDur rate: aRate channels: aChannels format: aFormat
	"Answer a new sound with the given properties."

	^self named: nil size: (aDur * aRate) truncated rate: aRate 
				channels: aChannels format: aFormat!

fromData: anArray named: nameString rate: aRate channels: aChannels format: aFormat
	"Answer a new sound from the given data."
	"Example: 
		| t |
		 t := Array new: 65536. 
		 0 to: 255 do: 
			[ :i | 1 to: 256 do:
				[ :j | t at: ((i * 256) + j) put: (j / 256 * 32000 - 16000) asInteger]].
		^StoredSound fromData: t named: 'ramp' 
				rate: 16000 channels: 1 format: #lin16Bit"

	| sound |
	sound := self named: nameString size: (anArray size / aChannels) truncated
				rate: aRate channels: aChannels format: aFormat.
	1 to: anArray size do:
		[ :i | sound sampleAt: i  put: (anArray at: i)].
	^sound!

fromData: anArray rate: aRate channels: aChannels format: aFormat
	"Answer a new 16-bit linear sound from the given data."
	"StoredSound immediateInspect"

	^self fromData: anArray named: nil rate: aRate channels: aChannels format: aFormat!

fromFile: nameString
	"Open the given file (AIFF, IRCAM, NeXT, or SPARC soundfile format)"
	"SampledSound fromFile: 'Data/kombination.snd'"
	"SampledSound openOn: (StoredSound fromFile: 'snd/new/orchestra.f.snd')"

	^(SoundFile readFileNamed: nameString) sound.!

headerFromFile: nameString
	"Open the given file (EBICSF, NeXT, or SPARC soundfile format)"
	"StoredSound headerFromFile: 'snd/new/orchestra.snd'"

	"FileDirectory on: (FileDirectory dirPathFor: nameString) entries"

	| file ver sound size |
	file := SoundFile open: nameString.
	file == nil ifTrue: [^nil].
	ver := nameString betweenPeriodsIfEmpty: '1.0.0'.
	size := file size.
	sound := self named: nameString size: size rate: file rate 
				channels: file channels format: file format.
	sound duration: (Duration value: (size / sound channels / sound rate) asFloat).
	ver isEmpty
		ifTrue: [sound version: '1a00']
		ifFalse: [sound version: ver].
	file properties == nil
		ifFalse: [file properties associationsDo: 
			[ :ass | sound at: ass key put: ass value]].
	file close.
	^sound!

named: aName size: aSize format: formatSymbol
	"Answer a new Sound with the given name and storage size."

	| sound |
	sound := self size: aSize format: formatSymbol channels: 1.
	sound name: aName.
	^sound!

named: nameString size: size rate: rate channels: chans format: formatSymbol
	"Answer a new Sound with the given storage size (in sample frames), etc."

	| sound  nam |
	(((#lin16Bit -> [sound := SampledSound new initialize])),
	(#float32Bit -> [sound := FloatSound new initialize]))
		case: formatSymbol
		otherwise: [self error: 'Unknown sound format.'].
	sound channels: chans.
	sound rate: rate.
	sound format: formatSymbol.
	sound size: size.
	sound allocateData.
	nameString == nil
		ifFalse: [nam :=  nameString asFilename tail.
			sound name: nam.
			sound folder: nameString asFilename head.
			self named: nam put: sound].
	^sound!

named: nameString size: size rate: rate channels: chans format: formatSymbol data: data
	"Answer a new Sound with the given storage size (in sample frames), etc."

	| sound nam |
	((#lin16Bit -> [sound := SampledSound new initialize.
			sound samples: data]),
	(#float32Bit -> [sound := FloatSound new initialize.
			sound samples: data]))
		case: formatSymbol
		otherwise: [self error: 'Unknown sound format.'].
	sound channels: chans.
	sound rate: rate.
	sound format: formatSymbol.
	sound size: size.
	nameString == nil
		ifFalse: [nam :=  nameString asFilename tail.
			sound name: nam.
			sound folder: nameString asFilename head.
			self named: nam put: sound].
	^sound!

size: size format: formatSymbol channels: chans
	"Answer a new Sound with the given storage size, etc."

	^self named: nil size: size rate: (SoundPort default class defaultRate) 
				channels: chans format: formatSymbol!

size: size rate: aRate channels: chans
	"Answer a new Sound with the given storage size, etc."

	^self named: nil size: size rate: aRate channels: chans format: self formatSymbol!

size: size rate: rate format: formatSymbol channels: chans
	"Answer a new Sound with the given storage size, etc."

	^self named: nil size: size rate: rate channels: chans format: formatSymbol! !

!Siren.SampledSound class methodsFor: 'standard wave forms'!

constantOfDur: dur value: value rate: rate chans: chans
	"Answer a sound with a constant value."
	"SoundView openOn: (StoredSound constantOfDur: 1 value: 10000 rate: 16000 chans: 1)"

	| snd |
	snd := self duration: dur rate: rate channels: chans 
						format: self formatSymbol.
	1 to: (dur * rate * chans) truncated do: 
		[ :i | snd sampleAt: i put: value].
	^snd!

expSweepDur: dur rate: rate from: start to: stop chans: chans
	"Answer a SampledSound with a swept sine wave."
	"SoundView openOn: (SampledSound expSweepDur: 1.0 rate: 44100 from: 20 to: 2000 chans: 1)"
	"SoundView openOn: (SampledSound expSweepDur: 1.0 
			rate: 44100 from: 20 to: 2000 chans: 1)"

	| scale1 snd incr |
	snd := self duration: dur rate: rate channels: chans format: self formatSymbol.
	scale1 := start asFloat / rate * Float pi * 2.
	incr := (stop / start) ln / (rate * dur) + 1.0.
	chans = 1
		ifTrue: [1 to: (dur * rate) truncated do: 
			[ :i | snd intSampleAt: i put: ((i - 1 * scale1) sin * 32767) truncated.
				scale1 := scale1 * incr]]
		ifFalse: [(1 to: ((dur * rate) truncated - 1) by: 2) do: 
			[ :i | | val |
				val := ((i - 1 * scale1) sin * 32767) truncated.
				snd intSampleAt: i put: val.
				snd intSampleAt: ( i + 1) put: val.
				scale1 := scale1 * incr]].
	^snd!

fileFromUser
	"Post a dialog for parameters and read a sound from disk."
	"SampledSound fileFromUser"

	| fname |
	fname := Dialog selectFile.
	fname == nil
		ifTrue: [^nil].
	^self fromFile: fname!

impulseOfDur: dur width: width rate: rate chans: chans
	"Create a sound with an impulse of the given characteristics."
	"SoundView openOn: (StoredSound impulseOfDur: 1 width: 0.001 rate: 16000 chans: 1)"
	"(SoundFile named: 'impulse.snd') saveSound: 
			(StoredSound impulseOfDur: 1 width: 0.001 
			rate: 16000)"

	| snd pulseLen nsamps samps |
	nsamps := (rate * dur * chans) truncated.
	samps := Array new: nsamps.
	pulseLen := (width * rate * chans) truncated.
	1 to: pulseLen do: 
		[ :i | samps at: i put: 32766].
	pulseLen  to: nsamps do: 
		[ :i | samps at: i put: 0].
	snd := SampledSound fromData: samps 
			rate: rate channels: chans format: self formatSymbol.
	^snd!

linearSweepDur: dur rate: rate from: start to: stop chans: chans
	"Answer a StoredSound with a swept sine wave."
	"SoundView openOn: (StoredSound linearSweepDur: 1.0 rate: 8000 from: 20 to: 2000 chans: 1)"

	| scale snd incr |
	snd := self duration: dur rate: rate channels: chans format: self formatSymbol.
	scale := start asFloat / rate * Float pi * 2.
	incr := ((stop asFloat / rate * Float pi * 2) - scale) / (rate * dur).
	chans = 1
		ifTrue: [1 to: (dur * rate) truncated do: 
			[ :i | snd intSampleAt: i put: ((i - 1 * scale) sin * 32767) truncated.
				scale := scale + incr]]
		ifFalse: [(1 to: (dur * rate) truncated by: 2) do: 
			[ :i | snd intSampleAt: i put: ((i - 1 * scale) sin * 32767) truncated.
				snd intSampleAt: ( i + 1) put: ((i - 1 * scale) sin * 32767) truncated.
				scale := scale + incr]].
	^snd!

pulseTrainDur: dur rate: rate freq: freq width: width chans: chans
	"Answer a StoredSound with a pulse train."
	"SoundView openOn: (StoredSound pulseTrainDur: 1.0 rate: 22050 freq: 10 
				width: 0.001 chans: 1 zero: 0)"
	"(StoredSound pulseTrainDur: 5.0 rate: 16000 freq: 250 width: 0.001 chans: 1) storeOnFileNamed: 'snd/new/square2.snd'"

	^self pulseTrainDur: dur rate: rate freq: freq width: width chans: chans zero: 0!

pulseTrainDur: dur rate: rate freq: freq width: width chans: chans zero: zero
	"Answer a StoredSound with a pulse train."
	"SoundView openOn: (StoredSound pulseTrainDur: 1.0 rate: 22050 freq: 10 
				width: 0.001 chans: 1 zero: 0)"
	"(StoredSound pulseTrainDur: 5.0 rate: 16000 freq: 250 width: 0.001 chans: 1) storeOnFileNamed: 'snd/new/square2.snd'"

	| waveLen snd pSamps |
	snd := self duration: dur rate: rate channels: chans 
					format: self formatSymbol.
	waveLen := (rate / freq) truncated.
	pSamps := (rate * width) truncated.
	chans = 1
		ifTrue: [0 to: (dur * freq - 1) truncated do: 
			[ :i |1 to: pSamps do:
				[ :j |snd intSampleAt: ((i * waveLen + j) min: pSamps) put: 32767].
			pSamps + 1 to: waveLen do:
				[ :j |snd intSampleAt: (i * waveLen + j) put: zero]]]
		ifFalse: [(0 to: (dur * freq - 1) truncated by: 2) do: 
			[ :i |1 to: (pSamps * 2) do:
				[ :j |snd intSampleAt: (i * waveLen + j) put: 32767].
			(pSamps * 2 + 1) to: (waveLen * 2) do:
				[ :j |snd intSampleAt: (i * waveLen + j) put: zero]]].
	^snd!

rampDur: dur rate: rate chans: chans
	"Answer a StoredSound with a single ramp of samples."
	"SoundView openOn: (StoredSound rampDur: 2.0 rate: 16000 chans: 1)"

	| incr snd |
	snd := self duration: dur rate: rate channels: chans format: self formatSymbol.
	incr :=  65536.0 / (rate * dur) asFloat.
	snd size: (dur * rate * chans) truncated.
	chans = 1
		ifTrue: [1 to: snd size do: 
			[ :i | snd intSampleAt: i put: (-32768.0 + (i - 1 * incr)) truncated]]
	ifFalse: [(1 to: snd size by: 2) do: 
			[ :i | snd intSampleAt: i put: (-32768.0 + (i - 1 * incr)) truncated.
				snd intSampleAt: (i + 1) put: (-32768.0 + (i - 1 * incr)) truncated]].
	^snd!

sawtooth
	"Answer a StoredSound with raw sawtooth samples."
	"SampledSound sawtooth"
	"SoundView openOn: SampledSound sawtooth"

	^self sawtoothDur: 3.0 rate: 44100 freq: 5 chans: 1!

sawtoothDur: dur rate: rate freq: freq chans: chans
	"Answer a StoredSound with raw sawtooth samples."
	"StoredSound sawtooth"
	"SoundView openOn: (SampledSound sawtoothDur: 1.0 rate: 22050 freq: 100 chans: 1)"

	| waveLen incr snd index |
	snd := self duration: dur rate: rate channels: chans format: self formatSymbol.
	waveLen := (rate / freq / 2) asFloat.
	incr :=  65536.0 / waveLen.
	chans = 1
		ifTrue: [1 to: (dur * rate) do: 
			[ :i | 
			index := (i \\ waveLen) asFloat.
			snd intSampleAt: i put: (-32768.0 + (index - 1.0 * incr)) truncated]]
		ifFalse: [(1 to: (dur * rate) truncated by: 2) do: 
			[ :i |
			index := (i \\ waveLen) asFloat.
			snd intSampleAt: i put: (-32768.0 + (index - 1.0 * incr)) truncated.
			snd intSampleAt: (i + 1) put: (-32768.0 + (index - 1.0 * incr)) truncated]].
	^snd!

sineDur: dur rate: rate freq: freq chans: chans
	"Answer a StoredSound with a pulse train."
	"SoundView openOn: (StoredSound sineDur: 0.5 rate: 22050 freq: 200 chans: 1)"
	"(StoredSound sineDur: 5 rate: 16000 freq: 800 chans: 1) storeOnFileNamed: 'snd/new/sine.snd'"

	| scale snd |
	snd := self duration: dur rate: rate channels: chans format: self formatSymbol.
	scale := freq / rate * Float pi * 2.
	chans = 1
		ifTrue: [1 to: (dur * rate) truncated do: 
			[ :i | snd intSampleAt: i put: ((i - 1 * scale) sin * 32767) truncated]]
		ifFalse: [(1 to: (dur * rate) truncated by: 2) do: 
			[ :i | snd intSampleAt: i put: ((i - 1 * scale) sin * 32767) truncated.
				snd intSampleAt: ( i + 1) put: ((i - 1 * scale) sin * 32767) truncated]].
	^snd!

squareDur: dur rate: rate freq: freq chans: chans
	"Answer a StoredSound with a square wave."
	"SoundView openOn: (StoredSound squareDur: 1.0 rate: 1000 freq: 20 chans: 1)"

	^self pulseTrainDur: dur rate: rate freq: freq width: (0.5 / freq) chans: chans zero: -32767!

sweepDur: dur rate: rate from: start to: stop chans: chans
	"Answer a StoredSound with a swept sine wave."
	"SoundView openOn: (SampledSound sweepDur: 0.5 rate: 8000 from: 20 to: 500 chans: 1)"
	"SoundView openOn: (FloatSound sweepDur: 0.5 rate: 8000 from: 20 to: 500 chans: 1)"
	"(SampledSound sweepDur: 2.0 rate: 44100 from: 50 to: 500 chans: 1) 
				storeOnFileNamed: 'snd/new/sweep.snd'"

	^self expSweepDur: dur rate: rate from: start to: stop chans: chans! !

!Siren.SampledSound class methodsFor: 'class constants'!

formatSymbol
	"Answer the symbolic code used for the format of the receiver's instances."

	^#lin16Bit!

maxSample
	"Answer the maximum value of the receiver class."

	^32767!

minSample
	"Answer the minimum value of the receiver class."

	^-32768! !

!Siren.SampledSound class methodsFor: 'examples'!

fileExample
	"Answer a typical sound read in from a file."
	"SampledSound fileExample inspect"

	| nam |
	nam := FileSelectionDialog allowDirectoryResult: false allowFileResult: true.
	name isEmpty ifTrue: [^self].
	^SampledSound fromFile: nam!

immediateInspect
	"Answer a sampled sound from immediate data; inspect it."
	"StoredSound immediateInspect"

	| t |
	 t := Array new: 64000. 
	 0 to: 255 do: 
		[ :i | 1 to: 250 do:
			[ :j | t at: ((i * 250) + j) 
			put: (j / 250 * 32000 - 16000) asInteger]].
	(SampledSound fromData: t named: 'ramp' 
			rate: 32000 channels: 1 format: #lin16Bit) inspect!

rmsViewExample
	"Read a sampled sound from the file, take the rms into 64 values, and edit that."
	"SampledSound rmsViewExample"

	| arr snd |
	arr := Array new: 128 withAll: 0.0.
	snd := SampledSound fileExample.
	snd sampleMaxFrom: 1 to: snd size into: arr.
	(Function from: arr) edit.!

sweepExample
	"Open a sound view on a swept sine wave."
	"SampledSound sweepExample"

	^self sweepDur: 2.0 rate: 44100 from: 20 to: 100 chans: 1!

sweepView
	"Open a sound view on a swept sine wave."
	"SampledSound sweepView"

	(SampledSound sweepDur: 2.0 rate: 44100 from: 20 to: 100 chans: 1) edit! !

!Siren.FloatSound methodsFor: 'accessing'!

format
	"Return the receiver's format--a symbol constant."

	^#float32Bit!

sampleSize
	"Answer the size in bytes of the receiver's samples (4)."

	^4! !

!Siren.FloatSound methodsFor: 'sample accessing'!

intSampleAt: index
	"Answer the sample at the given index as a scaled 16-bit integer."

	^((self sampleAt: index) * 32767.0) rounded!

intSampleAt: index put: aValue
	"Put the given integer sample at the given index after scaling."

	self sampleAt: index put: (aValue asFloat / 32767.0)! !

!Siren.FloatSound methodsFor: 'private'!

privateSampleAt: index
	"Answer the sample at the given index."

	^data floatAt: (index -1 * 4 + 1)!

privateSampleAt: index put: aValue
	"Stuff the given sample into the data array at the given index (no checking)."

	data floatAt: (index -1 * 4 + 1) put: aValue asFloat! !

!Siren.FloatSound class methodsFor: 'class constants'!

formatSymbol
	"Answer the symbolic code used for the format of the receiver's instances."

	^#float32Bit!

maxSample
	"Answer the maximum value of the receiver class."

	^1.0!

minSample
	"Answer the minimum value of the receiver class."

	^-1.0! !

!Siren.FloatSound class methodsFor: 'examples'!

fileExample
	"Answer a typical float sound read in from a file."
	"SoundView openOn: FloatSound fileExample"

	^FloatSound fromFile: 'snd/new/orchestra.f.snd'! !

!Siren.VirtualSound methodsFor: 'accessing'!

cue: cueName
	"Set the cue name of the receiver"

	properties at: #cue put: cueName.
	domain := source cueRegionNamed: cueName.
	duration := SecondDuration value: (self size / self rate) asFloat.
	self name: self name, '.', cueName!

source
	"Answer the receiver's source sound."

	^source!

source: aSound
	"Set the receiver's source sound."

	source := aSound.
	self rate: source rate.
	self channels: source channels.
	self name: source name.!

start: start
	"Set the argument as the starting sample of the receiver."

	properties at: #start put: start.
	(properties includesKey: #stop)
		ifTrue: [self size: (properties at: #stop) - start].!

stop: stop
	"Set the argument as the ending sample of the receiver."

	properties at: #stop put: stop.
	(properties includesKey: #start)
		ifTrue: [self size: stop - (properties at: #start)].! !

!Siren.VirtualSound methodsFor: 'sample accessing'!

sampleAt: index put: aValue
	"Stuff the given sample at the given index **after transforming into a 'real' sound**."

	self getReal.
	^super sampleAt: index put: aValue! !

!Siren.VirtualSound methodsFor: 'testing'!

isVirtual
	"Answer whether the receiver is a virtual sound."

	^true! !

!Siren.VirtualSound methodsFor: 'private'!

getReal
	"Answer a 'real sound' based on the receiver."

	| newSound |
	newSound := self copyAllButSamples.
	1 to: self size do:
		[ :ind |
		newSound sampleAt: ind put: (self sampleAt: ind)].
	self become: newSound!

mapSampleIndex: index
	"Map the given sample index according to the 'page' (firstIndex) of the source and the receiver's relative offset."

	^(source mapSampleIndex: index) + firstIndex!

realSound
	"Answer the real sound for the receiver."

	^source! !

!Siren.VirtualSound class methodsFor: 'instance creation'!

from: source cue: cName
	"Answer a VirtualSound derived from the argument and named cue region."
	"VirtualSound exampleView"

	| me |
	me := self new initialize.
	me source: source.
	me cue: cName.
	^me! !

!Siren.VirtualSound class methodsFor: 'examples'!

exampleView
	"Open a sound view on a ramp with a chunk cut out of it."
	"VirtualSound exampleView"

	SoundView openOn: (VirtualSound from: 
			(SampledSound fromFile: 'snd/AllGatesAreOpen/nml1.8e.snd') 
				cue: #stream3)! !

!Siren.CompositeSound methodsFor: 'accessing'!

on: aSound
	"Set the receiver's source sound."

	components := SortedCollection sortBlock: [:x :y | x start <= y start].
	components add: (ComponentSound on: aSound start: 1 stop: aSound size).
	self rate: aSound rate.
	self channels: aSound channels.
	self size: aSound size.
	properties := aSound properties copy!

paste: sound from: start to: stop at: index
	"Paste the designated section into the receiver."

	| newItem item number extent oldStop |
	extent := stop - start.
	newItem := ComponentSound on: sound 
				start: index stop: stop - start + index offset: start.
	index >= self size						"if pasting onto the end of the sound"
		ifTrue: [components add: newItem.
				^self].
	number := 1.							" else locate where to put it"
	[(number > components size) | ((components at: number) includes: index)] 
		whileFalse: [number := number + 1].
	item := components at: number.
	index = item stop						"if we're pasting at the end of another splice"
		ifTrue: [self rippleUpFrom: (number + 1) adding: extent.
			components add: newItem.
			^self].
	index = item start						"if we're pasting at the start of another splice"
		ifTrue: [self rippleUpFrom: number adding: extent.
			components add: newItem.
			^self].
											"else we have to break up an existing component"
	self rippleUpFrom: (number + 1) adding: extent.
	oldStop := item stop.
	item stop: index - item start.			"edit the old entry in place"
	components add: newItem.			"insert the new item"
								"add a copy of the old item with new interval and offset"
	components add: (ComponentSound on: item sound 
			start: (index + extent + 1) stop: (oldStop + extent) 
			offset: (item offset + (index - item start) + extent)).
	^self! !

!Siren.CompositeSound methodsFor: 'sample accessing'!

sampleAt: index
	"Answer the sample at the given index, mapping and paging as necessary."

	| q |
	1 to: components size do:
		[ :ind | 
		q := components at: ind.
		(q includes: index)
			ifTrue: [^q sound sampleAt: (index - q start + q offset)]].
	self error: 'No sample by that name, Sir.'! !

!Siren.CompositeSound methodsFor: 'testing'!

isComposite
	"Answer whether the receiver is a composite sound."

	^true! !

!Siren.CompositeSound methodsFor: 'private'!

rippleUpFrom: start adding: insert
	"Shift all the cues above the given one up by the given insert count."

	| item |
	start to: components size do:
		[ :ind | 
		item := (components at: ind).
		item start: (item start + insert).
		item stop: (item stop + insert)].! !

!Siren.CompositeSound class methodsFor: 'instance creation'!

on: source paste: newSound from: start to: stop at: index
	"Answer a CompositeSound derived from the argument pasting in the given sound at the given index."
	"CompositeSound exampleView"

	| me |
	me := self new initialize.
	me on: source.
	me paste: newSound from: start to: stop at: index.
	^me! !

!Siren.CompositeSound class methodsFor: 'examples'!

exampleView
	"Open a sound view on a ramp with a chunk of a sine pasted into it."
	"CompositeSound exampleView"

	| ramp sine |
	ramp := SampledSound rampDur: 1.0 rate: 8000 chans: 1.
	sine := SampledSound sineDur: 0.5 rate: 8000 freq: 400 chans: 1.
	SoundView openOn: (ramp paste: sine from: 200 to: 400 at: 1000)! !

!Siren.GapSound methodsFor: 'accessing'!

cutFrom: startSample to: stopSample
	"Cut the designated section from the receiver."

	| realStart realStop |
	realStart := self mapSampleIndex: startSample.
	realStop := self mapSampleIndex: stopSample.
	cutList == nil
		ifTrue: [cutList := SortedCollection new].
	cutList add: realStart.
	cutList add: realStop.
	self size: (self size - (stopSample - startSample)).
	^self! !

!Siren.GapSound methodsFor: 'testing'!

hasGaps
	"Answer whether the receiver has any gaps."

	^true! !

!Siren.GapSound methodsFor: 'private'!

mapSampleIndex: index
	"Map the given sample index according to the cut list (i.e., deleted sections) of the receiver."

	| mappedIndex |
	((cutList == nil) or: [cutList size < 2])
		ifTrue: [^index - firstIndex].
	mappedIndex := index.
	mappedIndex < cutList first
		ifTrue: [^mappedIndex].
	1 to: cutList size by: 2 do:
		[ :cutIndex |
		mappedIndex := mappedIndex - 
				((cutList at: cutIndex) - (cutList at: cutIndex + 1)).
		(((cutIndex + 1) = cutList size) or: 
				[mappedIndex < (cutList at: cutIndex + 2)])
			ifTrue: [^mappedIndex]].
		^mappedIndex - firstIndex.! !

!Siren.GapSound class methodsFor: 'instance creation'!

on: source cutFrom: start to: stop
	"Answer a CompositeSound derived from the argument missing the given range."
	"GapSound exampleView"

	| me |
	me := self new initialize.
	me source: source.
	me cutFrom: start to: stop.
	^me! !

!Siren.GapSound class methodsFor: 'examples'!

exampleView
	"Open a sound view on a ramp with a chunk cut out of it."
	"GapSound exampleView"

	SoundView openOn: ((SampledSound rampDur: 1.0 rate: 16000 chans: 1) 
			cutFrom: 1000 to: 10000)! !

!Siren.FunctionController methodsFor: 'initialize-release'!

initialize

	super initialize.
	modelIndex := 1.!

initializeMenu
	"Set up the receiver's YellowButtonMenu"

"	self menuHolder: (ValueHolder with: (PopUpMenu
				labelList: #(('inspect') ('redraw' 'set scale') ('add' 'clear' 'enter'))
				values: #(doInspect redraw setAScale addFunction clearFunction enterFunction)))"! !

!Siren.FunctionController methodsFor: 'accessing'!

model
	"Answer the view's model at the receiver's model index."

	^view modelAt: modelIndex! !

!Siren.FunctionController methodsFor: 'control defaults'!

redButtonActivity
	"Hit-detect items from the display list or drag a selection."

	| cPoint value |
	self model == nil ifTrue: [^nil].
	cPoint := sensor cursorPoint - view class inset max: (0@0).
	value := self model at: (cPoint x / view hRange) asFloat.
							"If you're clicking near the function"
	((value * (view vRange)) - (view vRange - cPoint y)) abs < 16
		ifTrue: [(self model hasPointNear: (cPoint x / view hRange))
			ifTrue: [self dragPoint: cPoint]
			ifFalse: [self addPoint: cPoint]]!

selectAtPoint: aPoint
	"Hit-detect the model for the given pt."

	| value transPt |
	transPt := view transformPoint: aPoint.
	value := self model at: (transPt x / view hRange) asFloat.
	((value * (view vRange)) - (view vRange - transPt y)) abs < 16
		ifTrue: [(self model hasPointNear: (transPt x / view hRange))
			ifTrue: [self dragPoint: transPt]
			ifFalse: [self addPoint: transPt]]! !

!Siren.FunctionController methodsFor: 'menu messages'!

addFunction
	"Add a new function to the view's model array."

	| class new nameArray |
	(class := (Menu labels: 'LinSeg\ExpSeg\SplineSeg' withCRs) startUp) = 0
			ifTrue: [^self].
	nameArray := Array with: LinearFunction with: ExponentialFunction with: SplineFunction.
	new := (nameArray at: class) perform: #default.
	view models at: modelIndex put: new.
	view invalidate!

clearFunction
	"Clear the current function to the view's model array."

	view modelAt: modelIndex put: nil.
	view invalidate!

doInspect
	"Inspect the receiver's array of models"

	sensor shiftDown
		ifFalse: [self model inspect]
		ifTrue: [self inspect]!

enterFunction
	"Add a new function to the view's model array after typing in its breakpoints."

	| class new nameArray wgc done block points |
	(class := (Menu labels: 'LinearFunction\ExpSeg\SplineSeg' withCRs) startUp) = 0
			ifTrue: [^self].
	nameArray := Array with: LinearFunction with: ExponentialFunction with: SplineFunction.
	Cursor crossHair showWhile:
		[wgc := view graphicsContext.
		done := false.
		block := [| p |
				sensor waitButton.
				p := sensor mousePoint.
				done := sensor redButtonPressed not.
				sensor waitNoButton.
				ScheduledControllers checkForEvents.
				p].
		wgc flush.
		points := Array new writeStream.
		[done] whileFalse:
			[points nextPut: block value]].
	points := points contents collect:
		[ :pt |
		((pt x / view hRange)@ (view vRange - pt x / view vRange)) float].
	points first x: 0.
	points last x: 1.
	new := (nameArray at: class) from: points.
	view models at: modelIndex put: new.
	view invalidate!

redraw
	"Redraw the view and clear the selection."

	view invalidate!

setAScale
	"comment stating purpose of message"

	|num |
	num := Number readFromString: (Dialog request: 'set scale:'
			initialAnswer: view aScale printString).
	num = 0 ifTrue: [^self].
	view aScale: num.
	view invalidate! !

!Siren.FunctionController methodsFor: 'private'!

addPoint: cPoint
	"Add a new point to the model near the current point."

	| x0 p1 p2 p3 index newFcn mod newPoint scalePoint |
	mod := self model.
	x0 := cPoint x / view hRange.
	index := mod indexOfPointNearestX: x0.
	(mod pointAt: index) x > x0
		ifTrue: [index := index - 1].
	p1 := ((mod pointAt: index) * view displayExtent) truncated.
	p2 := cPoint.
	p3 := ((mod pointAt: (index + 1)) * view displayExtent) truncated.
	p2 := self rubberBandLinesBetween: p1 and: p2 and: p3.
	newFcn := mod class ofSize: mod size + 1.
	1 to: index do:
		[ :i |
		newFcn add: (mod realPointAt: i)].
	newPoint := (mod realPointAt: index) copy.
	scalePoint := (p2 / view displayExtent) float.
	newPoint x: scalePoint x; y: scalePoint y.	"this is for handling ExpSegs"
	newFcn add: newPoint.
	index + 1 to: mod size do:
		[ :i |
		newFcn add: (mod realPointAt: i)].
	view modelAt: modelIndex put: newFcn.
	view invalidate!

dragPoint: cPoint
	"Drag the current point."

	|  p1 p2 p3 index mod oldPt |
	mod := self model.
	index := mod indexOfPointNearestX: (cPoint x / view hRange).
	index = 1
		ifFalse: [p1 := ((mod pointAt: (index - 1)) * view displayExtent) truncated].
	p2 := ((mod pointAt: index) * view displayExtent) truncated.
	index = mod size
		ifFalse: [p3 := ((mod pointAt: (index + 1)) * view displayExtent) truncated].
	p2 := self rubberBandLinesBetween: p1 and: p2 and: p3.
	oldPt := (mod realPointAt: index) copy.
	oldPt x: (p2 x / view hRange) asFloat; 
		y: ((1 - ((view vRange - p2 y) / view vRange) min: 1) max: 0) asFloat.
	mod pointAt: index put: oldPt.
	view invalidate!

rubberBandLinesBetween: p1 and: p2 and: p3
	"Drag two lines between the given end- and middle-points; 
	answer the final mid-point"

	| nPoint gc back fore |
	gc := view graphicsContext.
	back := DisplayListView backgroundColor.
	fore := view colors at: modelIndex.
	p1 == nil				"if dragging the first point"
		ifTrue: [nPoint := p2.
			[sensor anyButtonPressed]
				whileTrue:
					[view updateBetween: nPoint and: p3 with: back on: gc.
					nPoint := sensor cursorPoint - view class inset.
					nPoint y: view vRange - nPoint y.
					nPoint x: (nPoint x min: p3 x - 1).
					view updateBetween: nPoint and: p3 with: fore on: gc].
			^0 @ nPoint y].
	p3 == nil		 		"if dragging the last point"
		ifTrue: [nPoint := p2.
			[sensor anyButtonPressed]
				whileTrue:
					[view updateBetween: p1 and: nPoint with: back on: gc.
					nPoint := sensor cursorPoint - view class inset.
					nPoint y: view vRange - nPoint y.
					nPoint x: (nPoint x max: p1 x + 1).
					view updateBetween: p1 and: nPoint with: fore on: gc].
			^view hRange @ nPoint y].
							"else--dragging internal point"
	nPoint := p2.
	[sensor anyButtonPressed]
		whileTrue: [view updateBetween: p1 and: nPoint with: back on: gc.
			view updateBetween: nPoint and: p3 with: back on: gc.
			nPoint := sensor cursorPoint - view class inset.
			nPoint y: view vRange - nPoint y.
			nPoint x: ((nPoint x min: p3 x - 1) max: p1 x + 1).
			view updateBetween: p1 and: nPoint with: fore on: gc.
			view updateBetween: nPoint and: p3 with: fore on: gc].
		^nPoint!

selectModel: index
	"Set the model index of the receiver to the argument."

	modelIndex := index! !

!Siren.FunctionController methodsFor: 'events'!

mouseMovedEvent: anEvent
	"Mouse dragging"

	anEvent redButtonPressed ifFalse: [^self].
	model selection == nil
		ifTrue: [self selectEvent: anEvent]
		ifFalse: [self dragSelection: anEvent]! !

!Core.Collection class methodsFor: 'instance creation'!

with: firstObject with: secondObject with: thirdObject with: fourthObject  with: fifth with: sixth with: seventh
	"Answer a new instance of a Collection containing the	
	four arguments as the elements."

	| newCollection |
	newCollection := self new.
	newCollection add: firstObject.
	newCollection add: secondObject.
	newCollection add: thirdObject.
	newCollection add: fourthObject.
	newCollection add: fifth.
	newCollection add: sixth.
	newCollection add: seventh.
	^newCollection! !

!Core.Object methodsFor: 'converting'!

note
	"Answer a MIDI pitch whose value is the receiver."

	^Siren.MIDIPitch value: self! !

!Core.Random methodsFor: 'accessing'!

nextIntFrom: low to: high
	"Answer the next random number as a float in the given range."

	| value |
	value := self next.
	^(value * (high + 1 - low) + low) truncated! !

!Graphics.ColorValue class methodsFor: 'instance creation'!

random
	"Answer a ColorValue with a random RGB intensity (between 0 and 1)."

	| rand |
	rand := Random new.
	^self red: rand next green: rand next blue: rand next! !

!Core.Collection methodsFor: 'testing'!

hasItems
	"Answer whether or not the receiver has items or components (true)."

	^true! !

!Tools.VisualLauncher methodsFor: 'actions'!

openSirenUtility

	Siren.SirenUtility open! !

!Tools.Workbook methodsFor: 'actions'!

htmlOutAll
	"Create a directory and save all the receiver's pages there as HTML files."

	| nam folder toc list |
	nam := Dialog 
		requestNewFileName: 'Save the workbook to directory' 
		default: 'Workbook'.
	nam isEmpty ifTrue: [^self].
	folder := Filename named: nam.
	folder exists
		ifTrue: [folder isDirectory
			ifFalse: [Dialog warn: 'File exists and is not a directory.'.
				^self]]
		ifFalse: [folder makeDirectory].
	toc := (folder asString, Filename separator asString, 'index.html') asFilename writeStream.
	list := (folder asString, Filename separator asString, 'contents.txt') asFilename writeStream.
	self writeHTMLHeaderTo: toc.
	1 to: self pages size - 1 do:
		[ :index | | pa ti fn text |
		pa := self pages at: index.
"		Transcript show: 'Save: ', pa label; cr."
		ti := pa label copyReplaceAll: ' ' with: '_'.
		fn := folder asString, Filename separator asString, ti, '.html'.
		text := pa getText.
		Smalltalk.Heeg.HtmlWriter write: text into: fn.
		toc nextPutAll: ('<LI><A HREF="./', ti, '.html">', pa label, '</a>'); cr.
		list nextPutAll: ti; cr].
	self writeHTMLTrailerTo: toc.
	toc close.
	list close! !

!Core.Object methodsFor: 'converting'!

pitch
	"Answer a pitch whose value is the receiver."

	^Siren.Pitch value: self! !

!Core.Object methodsFor: 'associating'!

=> anObject
	"Answer an EventAssociation between the receiver and the argument."
	"1.234 => (DurationEvent dur: 250 msec voice: #flute)"

	^Siren.EventAssociation new key: self value: anObject! !

!Core.Object methodsFor: 'user interface'!

asExplorerString: showHide
	"Ignore the showHide dictionary by default."

	^self asString! !

!Core.Character methodsFor: 'converting'!

asString
	"Answer the receiver converted into a string."

	^String with: self! !

!Tools.WorkspacePage methodsFor: 'private-file access'!

writeText: aText

	| stream |
	stream := self filenameWithEncoding writeStream.
	[stream nextPutAll: aText] ensure: [stream close].
	self rememberFileTimestamp.
	textModelOverride := aText! !

!Core.Object methodsFor: 'testing'!

isEventList
	"Coerces EventLists to true and everything else to false.  EventList
	overrides with ^true"

	^false! !

!Core.Object methodsFor: 'converting'!

velocity
	"Answer a MIDI loudness whose value is the receiver."

	^Siren.MIDIVelocity value: self! !

!Core.SmallInteger methodsFor: 'printing'!

toOSCTypeOn: aConvertor 
	aConvertor nextPutType: $i! !

!UI.MotifWidgetPolicy class methodsFor: 'private-initializing'!

initializeDefaultGenericColors
	"
		WidgetPolicy initialize.
		WidgetPolicy subclasses do: [ :class | class initialize].
		Screen default flushPaintPreferencesCache.
		ScheduledControllers restore
	"

	| background |

	"color"
	self defaultColorWidgetColors
		matchAt: SymbolicPaint foreground put: ColorValue black;
		matchAt: SymbolicPaint background put: (background := 
					(ColorValue lightYellow blendWith: ColorValue veryLightGray));
		matchAt: SymbolicPaint foreground put: (self makeForegroundColorFor: background);
		matchAt: SymbolicPaint selectionForeground put: ColorValue white;
		matchAt: SymbolicPaint selectionBackground put: (ColorValue blue blendWith: ColorValue veryLightGray);
		matchAt: SymbolicPaint inactiveBackground put: background;
		matchAt: SymbolicPaint inactiveForeground put: ColorValue gray;
		matchAt: SymbolicPaint inactiveSelectionForeground put: ColorValue gray;
		matchAt: SymbolicPaint border put: ColorValue black;
		matchAt: SymbolicPaint hilite put: (self makeHiliteColorFor: (ColorValue brightness: 4/5));
		matchAt: SymbolicPaint shadow put: (self makeShadowColorFor: (ColorValue brightness: 4/5));
		matchAt: SymbolicPaint separator put: ColorValue black;
		matchAt: SymbolicPaint traversalHilite put: ColorValue yellow.

	"gray-scale"
	self defaultGrayWidgetColors
		matchAt: SymbolicPaint border put: ColorValue black;
		matchAt: SymbolicPaint background put: (background := ColorValue brightness: 4/5);
		matchAt: SymbolicPaint foreground put: (self makeForegroundColorFor: background);
		matchAt: SymbolicPaint selectionForeground put: ColorValue white;
		matchAt: SymbolicPaint selectionBackground put: ColorValue black;
		matchAt: SymbolicPaint inactiveBackground put: background;
		matchAt: SymbolicPaint inactiveForeground put: (ColorValue brightness: 15/32);
		matchAt: SymbolicPaint inactiveSelectionForeground put: (ColorValue brightness: 15/32);
		matchAt: SymbolicPaint hilite put: (self makeHiliteColorFor: background);
		matchAt: SymbolicPaint shadow put: (self makeShadowColorFor: background);
		matchAt: SymbolicPaint separator put: ColorValue black;
		matchAt: SymbolicPaint traversalHilite put: ColorValue gray.

	"monochrome"
	self defaultMonochromeWidgetColors
		matchAt: SymbolicPaint border put: ColorValue black;
		matchAt: SymbolicPaint background put: (background := ColorValue white);
		matchAt: SymbolicPaint foreground put: (self makeForegroundColorFor: background);
		matchAt: SymbolicPaint selectionForeground put: ColorValue white;
		matchAt: SymbolicPaint selectionBackground put: ColorValue black;
		matchAt: SymbolicPaint inactiveBackground put: background;
		matchAt: SymbolicPaint inactiveForeground put: ColorValue gray;
		matchAt: SymbolicPaint inactiveSelectionForeground put: ColorValue gray;
		matchAt: SymbolicPaint hilite put: (self makeHiliteColorFor: background);
		matchAt: SymbolicPaint shadow put: (self makeShadowColorFor: background);
		matchAt: SymbolicPaint separator put: ColorValue black;
		matchAt: SymbolicPaint traversalHilite put: ColorValue gray! !

!External.CPointer methodsFor: 'testing'!

isCPointer
	"Coerces CPointers to true and everything else to false."

	^true! !

!Core.Object methodsFor: 'user interface'!

asExplorerString

	^self asString! !

!Core.LimitedPrecisionReal methodsFor: 'converting'!

toOSCBytes: aConvertor 
	"There could be some endian issues in this, but I it works on my Mac"

	| flt |
	flt := self asFloat.
	aConvertor nextPut: (flt basicAt: 1).
	aConvertor nextPut: (flt basicAt: 2).
	aConvertor nextPut: (flt basicAt: 3).
	aConvertor nextPut: (flt basicAt: 4)! !

!Core.Point methodsFor: 'testing'!

isPoint
	"Coerces Points to true and everything else to false. Point overrides with ^true"

	^true! !

!Heeg.HtmlWriter methodsFor: 'writing'!

writeToFile
	"HtmlWriter write: ComposedTextEditor initialHelpText into: 'htmltest.html'"

	index := 0.
	wasSansSerif := false.
	self writeHeader.
	[1 to: text runs values size do: [:i | self writeRunNo: i].
	file nextPutAll: '<!!-- END -->', CrString, '</BODY>' , CrString , '</HTML>' , CrString]
		valueNowOrOnUnwindDo: [file close]! !

!Tools.Workbook methodsFor: 'private'!

writeHTMLHeaderTo: strm

	strm nextPutAll: '<HTML>
<HEAD>
<TITLE>The Siren Workbook</TITLE>
</HEAD>
<BODY BGCOLOR="#ffffcc">
<H2>The Siren Workbook</H2>
<H3>Outline</H3>
<UL>
'! !

!Core.Dictionary methodsFor: 'enumerating'!

case: aKey otherwise: otherwiseBlock
	"Execute one of the values of the receiver or the otherwise block."
	"((#a -> [Transcript cr; show: 'a'; cr]),
 		(#b -> [Transcript cr; show: 'b'; cr]),
 		(#c -> [Transcript cr; show: 'c'; cr]))
	case: #b otherwise: [Transcript cr; show: 'other'; cr]"

	^(self at: aKey ifAbsent: otherwiseBlock) value! !

!Core.Collection class methodsFor: 'instance creation'!

with: firstObject with: secondObject with: thirdObject with: fourthObject  with: fifth with: sixth 
	"Answer a new instance of a Collection containing the	
	four arguments as the elements."

	| newCollection |
	newCollection := self new.
	newCollection add: firstObject.
	newCollection add: secondObject.
	newCollection add: thirdObject.
	newCollection add: fourthObject.
	newCollection add: fifth.
	newCollection add: sixth.
	^newCollection! !

!Tools.Workbook methodsFor: 'actions'!

pageRename

	| newLabel current |
	self canRenamePage
		ifFalse: [self error: (#errCannotRenameThisPage << #dialogs >> 'cannot rename this page')].
	current := self currentPage.
	newLabel := Dialog 
		request: (#RenameThisPageAsColon << #dialogs >> 'Rename this page as:') 
		initialAnswer: current label.
	newLabel isEmpty ifTrue: [^self].
	current label: newLabel.
	self pages changed: #at: with: self pageHolder selectionIndex! !

!Heeg.HtmlWriter methodsFor: 'writing'!

writeRunNo: runIndex 
	| v sansSerif |
	v := text runs values at: runIndex.
	sansSerif := (v == #serif or: [(v isKindOf: Array)
					and: [v includes: #serif]]) not.
"	sansSerif & wasSansSerif not ifTrue: [file nextPutAll: '<FONT SIZE=+1>'].
"	v isNil
		ifTrue: [self writeCharacters: (text runs runs at: runIndex)]
		ifFalse: 
			[self writeEmphasis: v.
			self writeCharacters: (text runs runs at: runIndex).
			self emphasisPostprocessing: v].
	"Now we look ahead to the next run, to see if serif is encountered!!"
	v := text runs values size < (runIndex + 1)
				ifTrue: [ #serif]
				ifFalse: [text runs values at: runIndex + 1].
	(v == #serif or: [(v isKindOf: Array)
			and: [v includes: #serif]])
		ifTrue: [file nextPutAll: '</FONT>'; nextPut: Character cr].
	wasSansSerif := sansSerif! !

!Core.False methodsFor: 'printing'!

toOSCTypeOn: aConvertor
	aConvertor nextPutType: $F! !

!Core.SequenceableCollection methodsFor: 'printing'!

toOSCTypeOn: aConvertor 
	aConvertor nextPut: $[.
	self do: [:each | each toOSCTypeOn: aConvertor].
	aConvertor nextPut: $]! !

!Core.String methodsFor: 'printing'!

toOSCTypeOn: aConvertor 
	aConvertor nextPutType: $s! !

!Core.Collection methodsFor: 'processing'!

scrambled
	"Answer a collection of the elements of the receiver in random order."

	^self collect: [ :item | 0 - item]! !

!Core.LimitedPrecisionReal methodsFor: 'printing'!

printString: digits
	"Answer a String whose characters are a description of the receiver."

	| aStream |
	aStream := WriteStream on: (String new: 16).
	self printOn: aStream digits: digits.
	^aStream contents! !

!Core.Object methodsFor: 'testing'!

isAssociation
	"Return true if the receiver is an association.
	Note: Do not override in any class except association."

	^false!

isMusicMagnitude
	"Answer false for anything but a real MusicMagnitude."

	^false! !

!Core.Integer methodsFor: 'converting'!

key
	"Answer a MIDI pitch whose value is the receiver."

	^Siren.MIDIPitch value: self! !

!Core.Collection class methodsFor: 'instance creation'!

with: firstObject with: secondObject with: thirdObject with: fourthObject  with: fifth
	"Answer a new instance of a Collection containing the	
	four arguments as the elements."

	| newCollection |
	newCollection := self new.
	newCollection add: firstObject.
	newCollection add: secondObject.
	newCollection add: thirdObject.
	newCollection add: fourthObject.
	newCollection add: fifth.
	^newCollection! !

!Tools.VisualLauncher methodsFor: 'actions'!

openWorkbook

	ListWorkBook open! !

!Heeg.HtmlWriter methodsFor: 'writing'!

writeEmphasis: emphasisCollectionOrSymbol 
	| e emps |
	emps := emphasisCollectionOrSymbol copy.
	(emps isMemberOf: Array)
		ifFalse: 
			[e := HtmlWriter emphasisTable at: emps ifAbsent: [^nil].
			file nextPutAll: e]
		ifTrue: 
			[emps := emps asOrderedCollection.
			((emps includes: #large)
				and: [emps includes: #serif])
				ifTrue: 
					[emps remove: #serif; remove: #large.
					file nextPutAll: '<FONT SIZE=+2>'].
			emps
				do: 
					[:ane | 
					e := HtmlWriter emphasisTable at: ane ifAbsent: [nil].
					e notNil ifTrue: [file nextPutAll: e]]]! !

!Core.Association methodsFor: 'copying'!

, anAssociation

 	"Answer a Dictionary with of the receiver and the argument as its elements."
	"(#a -> 1), (#b -> 2)"
	| dict |	dict := Dictionary new.
	dict add: self.
	dict add: anAssociation.	^dict! !

!OS.ExternalStream methodsFor: 'nonhomogeneous accessing'!

next80BitFloat
	"Read a 80-bit extended float quantity from the input stream."

	| exp exponent sign mantissa1 mantissa2 |
	exp := self next * 256 + self next.
	exponent := (exp bitAnd: 16r7FFF) - 16383.
	sign := (exp bitAnd: 16r8000) == 0 ifTrue: [1.0] ifFalse: [-1.0].
	mantissa1 := self next * 256 + self next * 256 + self next.
	mantissa2 := self next.
	self skip: 4.
	^(mantissa1 == 0 and: [mantissa2 == 0])
		ifTrue: [0]
		ifFalse: [((mantissa2 >= 16r80
			ifTrue: [mantissa1 asFloat + 0.5]
			ifFalse: [mantissa1 asFloat]) timesTwoPower: exponent - 23) * sign]! !

!Core.SequenceableCollection methodsFor: 'accessing'!

atRandom
	"Answer a random element taken from the receiver."
	"10 timesRepeat: [Transcript show: (String with: ('abcdefghijk' atRandom)); cr] "

	| rand |
	rand := Random new.
	rand next.
	rand next.
	^self at: (rand nextIntFrom: 1 to: self size)! !

!Core.Object methodsFor: 'converting'!

Hz
	"Answer a HertzPitch whose value is the receiver."

	^Siren.HertzPitch value: self! !

!Core.Object methodsFor: 'testing'!

isVoice
	"Answer false"

	^false! !

!Tools.AbstractWorkspace methodsFor: 'actions'!

toggleToolbar

	| menuItem |
	menuItem := self menuBar atNameKey: #toolbar.
	menuItem ifNil: [^self].
	self builder window toolbarVisible: menuItem isOn not.
	menuItem indication: menuItem isOn not! !

!Core.Collection methodsFor: 'processing'!

negated
	"Answer a collection of the negations of the receiver."

	^self collect: [ :item | 0 - item]! !

!Tools.Workbook methodsFor: 'private'!

writeHTMLTrailerTo: strm

	strm nextPutAll: '</UL>
<P>
<A HREF="http://create.ucsb.edu/Siren">Go to the Siren Home Page</A>
<P>
</BODY>
</HTML>
'! !

!Core.Object methodsFor: 'converting'!

ampl
	"Answer a loudness whose value is the receiver."

	^Siren.Amplitude value: self! !

!Core.Behavior methodsFor: 'accessing class hierarchy'!

sortedSubclasses
	"Answer the receiver's subclasses in a sorted collection by name with abstract classes first."

	 | coll coll2 coll3 allClasses |
	subclasses == nil
		ifTrue: [^Set new].
	coll := subclasses collect: [ :e | e name ].
	coll := coll asSortedCollection.
	allClasses := OrderedCollection new: 1024.
	SystemUtils allClassesDo: [ :cls | allClasses add: cls].
	coll := coll collect: [ :e | allClasses detect: [ :cls | cls name == e]].
	coll2 := coll select: [ :e | e subclasses isEmpty].
	coll3 := coll select: [ :e | e subclasses isEmpty not].
	coll2 isEmpty ifTrue: [^coll3].
	coll3 isEmpty ifTrue: [^coll2].
	coll := OrderedCollection withAll: coll2.
	coll addAll: coll3.
	^coll! !

!Tools.Workbook methodsFor: 'actions'!

bossOutAll
	"Boss out the page list"

	| nameString boss stream coll |
	nameString := Dialog 
		requestFileNameWithMessage: (#BOSSOutToC << #dialogs >> 'BOSS Out To:') 
		default: 'Workbook.bos'
		version: #new
		ifFail: ['']
		for: nil.
	nameString isEmpty ifTrue: [^self].
	stream := nameString asFilename writeStream.
	coll := self pages collection.
	coll := coll copyFrom: 1 to: coll size - 1.
	[boss := #{BinaryObjectStorage} value onNew: stream.
		boss nextPut: coll] 
			ensure: [stream close]! !

!Core.Number methodsFor: 'converting'!

wait
	"Delay for the receiver in seconds"

	(Delay forSeconds: self) wait.! !

!Core.Object methodsFor: 'testing'!

isDisplayItem
	"Answer whether the receiver is a kind of DisplayItem"

	^false! !

!Core.Object methodsFor: 'converting'!

dur
	"Answer a duration whose value is the receiver."

	^Siren.Duration value: self! !

!Core.List methodsFor: 'accessing'!

collection
	"Answer the receiver's base collection."

	^collection copyFrom: 1 to: limit! !

!Core.Point methodsFor: 'truncation and round off'!

float
	"Answer a new Point that is the receiver's x and y as floating-point numbers."

	^x asFloat @ y asFloat! !

!Core.Object methodsFor: 'converting'!

dB
	"Answer a deciBel loudness whose value is the receiver."

	^Siren.DBLoudness value: self! !

!Core.Dictionary methodsFor: 'enumerating'!

case: aKey
	"Execute one of the values of the receiver or the otherwise block."
	"((#a -> [Transcript cr; show: 'a'; cr]),
 		(#b -> [Transcript cr; show: 'b'; cr]),
 		(#c -> [Transcript cr; show: 'c'; cr]))	case: #b"

	^self case: aKey otherwise: []! !

!Core.Object methodsFor: 'converting'!

loudness
	"Answer a loudness whose value is the receiver."

	^Siren.Amplitude value: self! !

!Tools.WorkspacePage class methodsFor: 'instance creation'!

labeled: aString with: aText
	"Create a new page unassociated with any file, with aString
	as the label and aText as the initial contents."

	^self new 
		label: aString;
		contents: aText;
		yourself! !

!Core.Interval methodsFor: 'accessing'!

stop
	"Answer the last element of the arithmetic progression."

	^stop! !

!Core.Object methodsFor: 'testing'!

isEvent
	"Coerces Events to true and everything else to false.  Event
	overrides with ^true"

	^false! !

!Core.SequenceableCollection methodsFor: 'processing'!

scrambled
	"Answer a collection of the elements of the receiver in random order."
	" 'abcdefg' scrambled "


	| rand old new |
	new := self species new: self size.
	old := OrderedCollection new: self size.
	1 to: self size do: [ :in | old add: in].
	rand := Random new.
	1 to: self size do: 
		[ :index | | pos |
		pos :=old atRandom: rand.
		old remove: pos.
		new at: index put: (self at: pos)].
	^new! !

!Core.LimitedPrecisionReal methodsFor: 'converting'!

toOSCTypeOn: aConvertor 
	aConvertor nextPutType: $f! !

!Core.Random methodsFor: 'accessing'!

nextFrom: low to: high
	"Answer the next random number as a float in the given range."

	| value |
	value := self next.
	^(value * (high - low) + low)! !

!Core.Collection class methodsFor: 'instance creation'!

with: firstObject with: secondObject with: thirdObject with: fourthObject  with: fifth with: sixth with: seventh with: eighth
	"Answer a new instance of a Collection containing the	
	four arguments as the elements."

	| newCollection |
	newCollection := self new.
	newCollection add: firstObject.
	newCollection add: secondObject.
	newCollection add: thirdObject.
	newCollection add: fourthObject.
	newCollection add: fifth.
	newCollection add: sixth.
	newCollection add: seventh.
	newCollection add: eighth.
	^newCollection! !

!Heeg.HtmlWriter class methodsFor: 'class accessing'!

headerSecond

	^'</TITLE>' , CrString ,
	'</HEAD>' , CrString , 
	'<BODY BGCOLOR="#ffffcc">' , CrString,
	'<!!-- START -->', CrString! !

!Tools.WorkspacePage methodsFor: 'private-file access'!

representBinaryOn: binWriter
	"Represent a workspace by its name and text. "

	| tex |
	tex := (builder componentAt: #text) widget displayContents text.
	tex isEmpty 
		ifTrue: [tex := (builder componentAt: #text) widget getContents].
	tex isEmpty ifTrue: [self halt].
	^MessageSend
			receiver: Array
			selector: #with:with:
			arguments: (Array with: label with: tex)! !

!Core.UndefinedObject methodsForUndefined: 'printing'!

toOSCTypeOn: aConvertor 
	aConvertor nextPutType: $N! !

!Core.SequenceableCollection methodsFor: 'accessing'!

atRandom: rand
	"Answer a random element taken from the receiver."
	"| r | r := Random new.
	10 timesRepeat: [Transcript show: (String with: ('abcdefghijk' atRandom: r)); cr] "

	^self at: (rand nextIntFrom: 1 to: self size)! !

!Core.Dictionary methodsFor: 'copying'!

, anAssociation 	"Add the argument to the receiver."
	"(#a -> 1), (#b -> 2), (#c -> 3)"

	self add: anAssociation.	^self! !

!Core.Object methodsFor: 'testing'!

isPoint
	"Coerces Points to true and everything else to false. Point overrides with ^true"

	^false! !

!Tools.Workbook methodsFor: 'actions'!

saveAll
	"Create a directory and save all the receiver's pages there."

	| sel nam folder toc |
	sel := (Menu labelArray: #('save as BOSS file' 'save as HTML pages' 'save as text files')) startUp.
	(sel = 0) ifTrue: [^self].
	(sel = 1) ifTrue: [^self bossOutAll].
	(sel = 2) ifTrue: [^self htmlOutAll].
	nam := Dialog 
		requestNewFileName: 'Save the workbook to directory' 
		default: 'Workbook'.
	nam isEmpty ifTrue: [^self].
	folder := Filename named: nam.
	folder exists
		ifTrue: [folder isDirectory
			ifFalse: [Dialog warn: 'File exists and is not a directory.'.
				^self]]
		ifFalse: [folder makeDirectory].
	toc := (folder asString, Filename separator asString, 'TableOfContents.txt') 
			asFilename writeStream.
	self pages do:
		[ :pa | | fn |
"		Transcript show: 'Save: ', pa label; cr."
		(pa respondsTo: #setFilename:)
			ifTrue: [ fn := pa label copyReplaceAll: ' ' with: '_'.
				pa setFilename: (folder asString, Filename separator asString, 
							fn, '.ws') asFilename.
			self selectPage: pa.
			pa acceptText.
			toc nextPutAll: (fn, '.ws'); cr]].
	toc close.! !

!Core.Collection methodsFor: 'processing'!

reciprocal
	"Answer a collection of the reciprocals of the receiver."

	^self collect: [ :item | 1 / item]! !

!Core.Object methodsFor: 'converting'!

measures
	"Answer a duration whose value is the receiver."

	^Siren.MeasureDuration value: self! !

!UI.BorderDecorationPolicy methodsFor: 'initialize-release'!

initialize

	inset := 1.
	scrollbarThickness := 8.
	scrollButtonThickness := 8.
	markerThickness := 3.
	minMarkerExtent := 1.
	menuBarHeight := 8.
	verticalScrollBarOnRight := false.
	border := LookPreferences defaultBorder! !

!Core.Object methodsFor: 'converting'!

msec
	"Answer a duration whose value is the receiver." 

	^Siren.MSecondDuration value: self! !

!Core.Object methodsFor: 'testing'!

isTreeNode
	"Answer whether the receiver is a tree node."

	^false! !

!Core.WordArray methodsFor: 'external copying'!

copyToHeapAsSigned: mallocSelector
	"Copy the receiver to the external heap.  The argument is a method selector that accepts one argument and determines how to allocate data for the receiver -- it is typically one of #malloc: or #malloc16:.  The selector's argument is the number of objects of the receiver's baseCType to allocate.  Answer a pointer to the data.  If the allocation fails a primitive failed signal is raised."

	| aPointer size |
	size := self basicSize.
	aPointer := CIntegerType signedShort pointerType referentType perform: mallocSelector with: size.
	aPointer
		copyAt: 0
		from: self
		size: size
		startingAt: 1.
	^aPointer! !

!Core.Object methodsFor: 'testing'!

isCPointer
	"Coerces CPointers to true and everything else to false."

	^false! !

!Core.Object methodsFor: 'converting'!

duration
	"Answer a duration whose value is the receiver."

	^Siren.Duration value: self! !

!Core.UndefinedObject methodsForUndefined: 'printing'!

toOSCBytes: aConvertor
	^self! !

!UI.ScrollValueHolder methodsFor: 'accessing'!

value: aPoint

	| old |
	old := offset.
	offset := aPoint.
	self changed: #value with: old! !

!Core.Point methodsFor: 'coercing'!

@ aZValue
	"Answer a ZPoint with the receiver and the argument as coordinates."

	^ZPoint x: x y: y z: aZValue! !

!Tools.Workbook methodsFor: 'actions'!

loadAll
	"Open a directory and read a set of pages from."

	| nam fil toc |
	nam := Dialog 
		requestFileName: 'Load a workbook from a file or directory' 
		default: 'Workbook.bos'.
	nam isEmpty ifTrue: [^self].
	fil := Filename named: nam.
	fil exists
		ifFalse: [Dialog warn: 'File/fil does not exist.'. ^self].
"if it's a file -- try BOSS"
	fil isDirectory
		ifFalse: [^self loadFromBoss: fil].
"Otherwise, assume it's a workbok folder"
	toc := (fil asString, Filename separator asString, 'TableOfContents.txt') asFilename readStream.
	[toc atEnd] whileFalse:
		[ | lin fnam pg |
		lin := toc upTo: Character cr.
		('*.ws' match: lin)
			ifTrue: [fnam := lin copyFrom: 1 to: lin size - 3]
			ifFalse: [fnam := lin].
		fnam := fnam copyReplaceAll: '_' with: ' '.
		pg := WorkspacePage labeled:  fnam.
		self addAndSelectPage: pg.
		pg contents: (fil asString, Filename separator asString, lin) asFilename contentsOfEntireFile].
	toc close.! !

!Tools.WorkspacePage methodsFor: 'private-file access'!

getText
	"Answer the receiver page's text"

	| tex |
	tex := (builder componentAt: #text) widget displayContents text.
	tex isEmpty 
		ifTrue: [tex := (builder componentAt: #text) widget getContents].
	tex isEmpty ifTrue: [self halt].
	^tex! !

!Core.Object methodsFor: 'converting'!

sec
	"Answer a duration whose value is the receiver."

	^Siren.SecondDuration value: self!

mostGeneral
	^self! !

!Tools.Workbook methodsFor: 'accessing'!

selectPage: pa

	self pageHolder selection: pa! !

!Core.False methodsFor: 'printing'!

printOSCTypeOn: aStream
	aStream nextPut: $F! !

!Core.Association methodsFor: 'testing'!

isAssociation
	"Return true if the receiver is an association."

	^true! !

!Core.Object methodsFor: 'converting'!

beat
	"Answer a ratio duration whose value is the receiver."

	^Siren.RatioDuration value: self! !

!Tools.Workbook methodsFor: 'actions'!

loadFromBoss: nameString
	"Load the outline from a BOSS file"

	| boss stream list vars |
	stream := nameString asFilename readStream.
	list := self pageHolder list.
	list removeFirst.			"remove the default page 1"
	vars := list removeFirst.	"hold onto the variables page"
	[ | arr |
		boss := #{BinaryObjectStorage} value onOldNoScan: stream.
		arr := boss next.
		arr do: [ :item | | pg |
		pg := WorkspacePage labeled:  item first.
		self addAndSelectPage: pg.
		pg contents: item last]]
			ensure: [stream close].
	list addLast: vars! !

!Core.String methodsFor: 'printing'!

toOSCBytes: aConvertor 
	self isEmpty ifTrue: [^self].
	aConvertor nextPutAll: self asByteArray.
	"Align to 4 byte boundry"
	4 - (self size \\ 4) timesRepeat: [aConvertor nextPut: 0]! !

!Tools.Workbook methodsFor: 'accessing'!

addPage: aWorkspacePage
	"The Variables page always remains the last, so any new page
	always ends up being one before the last."

	| otherPage |
	otherPage := self currentPage.
	aWorkspacePage workspace: self.
	otherPage isNil
		ifTrue: [self pages addLast: aWorkspacePage]
		ifFalse: [self pages add: aWorkspacePage after: otherPage]! !

!Tools.AbstractWorkspace methodsFor: 'accessing'!

doItEnvironment
	"Every time the environment is requested, re-update imports 
	if 'all' are imported because namespaces might have been added to the
	system in the meantime."

	importAll ifTrue: [locals updateImports].
	^locals! !

!Core.ClassDescription methodsFor: 'fileIn/Out'!

methodsFor: aString stamp: ignored

	"Answer a ClassCategoryReader for accessing the messages in the method
	dictionary category, aString, of the receiver."

	^ClassCategoryReader class: self category: aString asSymbol
	"False methodsFor: 'logical operations' inspect"! !

!Core.Symbol methodsFor: 'printing'!

toOSCTypeOn: aConvertor 
	aConvertor nextPutType: $s! !

!Heeg.HtmlWriter class methodsFor: 'class accessing'!

headerFirst

	^'<HTML>' , CrString,
	'<HEAD>' , CrString,
	'   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">' , CrString,
	'   <TITLE>'! !

!Core.Object methodsFor: 'testing'!

hasItems
	"Answer whether or not the receiver has items or components."

	^false! !

!Core.Integer methodsFor: 'printing'!

toOSCBytes: aConvertor 
	"pad beginning with 0's"

	aConvertor next: 4 - self digitLength put: 0.
	self digitLength
		to: 1
		by: -1
		do: [:i | aConvertor nextPut: (self digitAt: i)]! !

!Graphics.ColorValue class methodsFor: 'instance creation'!

random: rand
	"Answer a ColorValue with a random RGB intensity (between 0 and 1)."

	^self red: rand next green: rand next blue: rand next! !

!Tools.Workbook methodsFor: 'actions'!

fileNew

	self addAndSelectPage: (WorkspacePage labeled: self pickUniquePageLabel)! !

!Tools.WorkspacePage methodsFor: 'accessing'!

setFilename: aFilename

	filename := aFilename.
	self rememberFileTimestamp! !

!Core.SequenceableCollection methodsFor: 'printing'!

toOSCBytes: aConvertor 
	self do: [:each | each toOSCBytes: aConvertor]! !

!Core.True methodsFor: 'printing'!

toOSCTypeOn: aConvertor 
	aConvertor nextPutType: $T! !

!Tools.AbstractWorkspace methodsFor: 'private'!

buildMenuBar

	| menu |
	menu := self class menuBar.
"	(menu atNameKey: #toggleStatusBar) indicationSelector: #statusBarVisible.
	(menu atNameKey: #editEncoding) submenu: [self buildEncodingSubmenu].
"	^menu! !

!Core.Boolean methodsFor: 'printing'!

toOSCBytes: aConvertor
	^self! !

!Siren.SirenUtility class methodsFor: 'interface specs'!

windowSpec
	"Tools.UIPainter new openOnClass: self andSelector: #windowSpec"

	<resource: #canvas>
	^#(#{UI.FullSpec} 
		#window: 
		#(#{UI.WindowSpec} 
			#properties: #(#{UI.PropertyListDictionary} #positionAutoSave true #sizeType #specifiedSize #openType #advanced #positionType #lastSavedPosition ) 
			#label: 'Siren Configuration Options' 
			#min: #(#{Core.Point} 572 200 ) 
			#max: #(#{Core.Point} 572 200 ) 
			#bounds: #(#{Graphics.Rectangle} 532 74 1104 254 ) ) 
		#component: 
		#(#{UI.SpecCollection} 
			#collection: #(
				#(#{UI.MenuButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 120 32 216 60 ) 
					#name: #voiceMenu 
					#model: #defaultVoiceClass 
					#menu: #voiceMenu ) 
				#(#{UI.LabelSpec} 
					#layout: #(#{Core.Point} 32 36 ) 
					#name: #Label1 
					#label: 'Default voice' ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 424 100 544 124 ) 
					#name: #ActionButton5 
					#model: #cleanUp 
					#label: 'Clean up' 
					#style: #pixelLarge 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 424 136 544 160 ) 
					#name: #ActionButton6 
					#model: #freeLists 
					#label: 'Free event lists' 
					#style: #pixelLarge 
					#defaultable: true ) 
				#(#{UI.CompositeSpecCollection} 
					#collection: #(
						#(#{UI.InputFieldSpec} 
							#layout: #(#{Graphics.Rectangle} 76 0 176 22 ) 
							#name: #oscIPField 
							#model: #oscIP 
							#type: #string ) 
						#(#{UI.InputFieldSpec} 
							#layout: #(#{Graphics.Rectangle} 76 28 176 50 ) 
							#name: #oscPortField 
							#model: #oscPort 
							#type: #number 
							#formatString: '0' ) 
						#(#{UI.LabelSpec} 
							#layout: #(#{Core.Point} 15 28 ) 
							#name: #Label7 
							#label: 'OSC Port' ) 
						#(#{UI.LabelSpec} 
							#layout: #(#{Core.Point} 0 0 ) 
							#name: #Label6 
							#label: 'OSC host IP' ) ) 
					#compositeSpec: 
					#(#{UI.CompositeSpec} 
						#layout: #(#{Graphics.Rectangle} 388 32 564 82 ) 
						#name: #Composite3 ) ) 
				#(#{UI.LabelSpec} 
					#layout: #(#{Core.Point} 40 4 ) 
					#name: #Label8 
					#label: 'Score IO and Voices' 
					#style: #large ) 
				#(#{UI.LabelSpec} 
					#layout: #(#{Core.Point} 416 4 ) 
					#name: #Label9 
					#label: 'OpenSoundControl' 
					#style: #large ) 
				#(#{UI.LabelSpec} 
					#layout: #(#{Core.Point} 280 4 ) 
					#name: #Label10 
					#label: 'Sound IO' 
					#style: #large ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 120 92 216 116 ) 
					#name: #ActionButton2 
					#model: #testSoundIn 
					#label: 'Test sound in' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 120 120 216 144 ) 
					#name: #ActionButton7 
					#model: #testSndFileIn 
					#label: 'Test sound file' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 16 92 112 116 ) 
					#name: #ActionButton1 
					#model: #testMIDIin 
					#label: 'Test MIDI in' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 16 64 112 88 ) 
					#name: #testMIDIout 
					#model: #testMIDIout 
					#label: 'Test MIDI out' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 16 120 112 144 ) 
					#name: #testOSC 
					#model: #testOSCout 
					#label: 'Test OSC out' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 120 64 216 88 ) 
					#name: #testSoundOut 
					#model: #testSoundOut 
					#label: 'Test sound out' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 16 148 112 172 ) 
					#name: #ActionButton3 
					#model: #stopMIDI 
					#label: 'Stop MIDI' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 120 148 216 172 ) 
					#name: #ActionButton8 
					#model: #stopSound 
					#label: 'Stop sound' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 260 148 380 172 ) 
					#name: #ActionButton9 
					#model: #resetSound 
					#label: 'Reset Sound Port' 
					#defaultable: true ) 
				#(#{UI.CompositeSpecCollection} 
					#collection: #(
						#(#{UI.MenuButtonSpec} 
							#layout: #(#{Graphics.Rectangle} 80 84 148 112 ) 
							#name: #MenuButton4 
							#model: #blockSize 
							#menu: #blocksizeMenu ) 
						#(#{UI.MenuButtonSpec} 
							#layout: #(#{Graphics.Rectangle} 80 56 148 84 ) 
							#name: #MenuButton2 
							#model: #outChannels 
							#menu: #chMenu ) 
						#(#{UI.MenuButtonSpec} 
							#layout: #(#{Graphics.Rectangle} 80 28 148 56 ) 
							#name: #MenuButton3 
							#model: #inChannels 
							#menu: #chMenu ) 
						#(#{UI.MenuButtonSpec} 
							#layout: #(#{Graphics.Rectangle} 80 0 148 28 ) 
							#name: #MenuButton1 
							#model: #sampleRate 
							#menu: #rateMenu ) 
						#(#{UI.LabelSpec} 
							#layout: #(#{Core.Point} 6 4 ) 
							#name: #Label2 
							#label: 'Sample rate' ) 
						#(#{UI.LabelSpec} 
							#layout: #(#{Core.Point} 10 32 ) 
							#name: #Label4 
							#label: 'In channels' ) 
						#(#{UI.LabelSpec} 
							#layout: #(#{Core.Point} 0 60 ) 
							#name: #Label3 
							#label: 'Out channels' ) 
						#(#{UI.LabelSpec} 
							#layout: #(#{Core.Point} 18 88 ) 
							#name: #Label5 
							#label: 'BlockSize' ) ) 
					#compositeSpec: 
					#(#{UI.CompositeSpec} 
						#layout: #(#{Graphics.Rectangle} 232 32 380 144 ) 
						#name: #Composite1 ) ) ) ) )! !

!Siren.SirenUtility class methodsFor: 'resources'!

blocksizeMenu
	"Tools.MenuEditor new openOnClass: self andSelector: #blocksizeMenu"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: '64' 
				#value: 64 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '128' 
				#value: 128 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '256' 
				#value: 256 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '512' 
				#value: 512 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '1024' 
				#value: 1024 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '2048' 
				#value: 2048 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '4096' 
				#value: 4096 ) ) #(7 ) nil ) decodeAsLiteralArray!

chMenu
	"Tools.MenuEditor new openOnClass: self andSelector: #chMenu"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: '0' 
				#value: 0 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '1' 
				#value: 1 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '2' 
				#value: 2 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '4' 
				#value: 4 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '5.1' 
				#value: 6 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '8' 
				#value: 8 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '10.2' 
				#value: 12 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '16' 
				#value: 16 ) ) #(7 ) nil ) decodeAsLiteralArray!

rateMenu
	"Tools.MenuEditor new openOnClass: self andSelector: #rateMenu"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: '96k' 
				#value: 96000 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '88k' 
				#value: 88020 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '48k' 
				#value: 48000 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '44k' 
				#value: 44100 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '32k' 
				#value: 32000 ) 
			#(#{UI.MenuItem} 
				#rawLabel: '22k' 
				#value: 22050 ) ) #(6 ) nil ) decodeAsLiteralArray!

voiceMenu
	"Tools.MenuEditor new openOnClass: self andSelector: #voiceMenu"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: 'MIDI' 
				#value: #MIDIVoice ) 
			#(#{UI.MenuItem} 
				#rawLabel: 'OSC' 
				#value: #OSCVoice ) 
			#(#{UI.MenuItem} 
				#rawLabel: 'NoteList' 
				#value: #SuperColliderVoice ) 
			#(#{UI.MenuItem} 
				#rawLabel: 'None' 
				#value: '' ) ) #(4 ) nil ) decodeAsLiteralArray! !

!Siren.EventListTreeEditor class methodsFor: 'interface specs'!

windowSpec
	"Tools.UIPainter new openOnClass: self andSelector: #windowSpec"

	<resource: #canvas>
	^#(#{UI.FullSpec} 
		#window: 
		#(#{UI.WindowSpec} 
			#label: 'Siren EventList Tree' 
			#bounds: #(#{Graphics.Rectangle} 528 71 1119 589 ) ) 
		#component: 
		#(#{UI.SpecCollection} 
			#collection: #(
				#(#{UI.TreeViewSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 0 0 -2 0.5 0 1 ) 
					#name: #treeView 
					#model: #listTree 
					#menu: #treeMenu 
					#useModifierKeys: true 
					#selectionType: #highlight ) 
				#(#{UI.ResizingSplitterSpec} 
					#layout: #(#{Graphics.LayoutFrame} -2 0.5 0 0 2 0.5 0 1 ) 
					#name: #ResizingSplitter1 
					#horizontal: false 
					#minAboveSize: 30 
					#minBelowSize: 30 
					#aboveWidgets: 'treeView' 
					#belowWidgets: 'subcanvas' ) 
				#(#{UI.SubCanvasSpec} 
					#layout: #(#{Graphics.LayoutFrame} 2 0.5 0 0 0 1 0 1 ) 
					#name: #subcanvas 
					#majorKey: #{Siren.DisplayListSubcanvas} 
					#minorKey: #windowSpec 
					#clientKey: #scoreView ) ) ) )! !

!Siren.DisplayListController class methodsFor: 'resources'!

defaultEditMenu
	"MenuEditor new openOnClass: self andSelector: #defaultEditMenu"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: 'group' 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 'group' 
							#value: #groupItems 
							#shortcutKeyCharacter: $g 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'ungroup' 
							#value: #ungroupItems 
							#shortcutKeyCharacter: $u 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'flatten' 
							#value: #flattenItems ) ) #(2 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: 'edit' 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 'copy' 
							#value: #copyItem 
							#shortcutKeyCharacter: $c 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'cut' 
							#value: #cutItem 
							#shortcutKeyCharacter: $x 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'paste' 
							#value: #pasteItem 
							#shortcutKeyCharacter: $v 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'paste from buffer...' 
							#value: #pasteFromBuffer 
							#shortcutKeyCharacter: $V 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'again' 
							#value: #again 
							#shortcutKeyCharacter: $a 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'undo' 
							#value: #undo 
							#shortcutKeyCharacter: $z 
							#shortcutModifiers: 2 ) ) #(4 2 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: 'inspect' 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 'model' 
							#value: #inspectModel 
							#shortcutKeyCharacter: $i 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'editor' 
							#value: #inspectEditor 
							#shortcutKeyCharacter: $e 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'display' 
							#value: #inspectDisplayList ) ) #(3 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: 'display' 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 'redraw' 
							#value: #redrawView ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'zoom to 1:1' 
							#value: #zoomTo1 
							#shortcutKeyCharacter: $1 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'zoom in to point' 
							#value: #zoomInPoint ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'zoom out from point' 
							#value: #zoomOutPoint ) ) #(4 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: 'options...' 
				#value: #optionsDialog ) ) #(5 ) nil ) decodeAsLiteralArray! !

!Siren.SoundCanvas class methodsFor: 'interface specs'!

windowSpec
	"Tools.UIPainter new openOnClass: self andSelector: #windowSpec"

	<resource: #canvas>
	^#(#{UI.FullSpec} 
		#window: 
		#(#{UI.WindowSpec} 
			#label: 'SoundView' 
			#bounds: #(#{Graphics.Rectangle} 548 59 1107 313 ) ) 
		#component: 
		#(#{UI.SpecCollection} 
			#collection: #(
				#(#{UI.ArbitraryComponentSpec} 
					#layout: #(#{Graphics.LayoutFrame} 16 0 0 0 0 1 -16 1 ) 
					#name: #soundView 
					#flags: 0 
					#component: #createSoundView ) 
				#(#{UI.SliderSpec} 
					#layout: #(#{Graphics.LayoutFrame} 16 0 -16 1 0 1 0 1 ) 
					#name: #hZoom 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 5734 5734 5734 ) ) 
					#model: #hZoom 
					#orientation: #horizontal 
					#start: 1 
					#stop: 100 
					#step: 20 ) 
				#(#{UI.SliderSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 0 0 16 0 -16 1 ) 
					#name: #vZoom 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 5734 5734 5734 ) ) 
					#model: #vZoom 
					#orientation: #vertical 
					#start: 0.5 
					#stop: 5.0 
					#step: 1 ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 -16 1 16 0 0 1 ) 
					#name: #ActionButton1 
					#colors: 
					#(#{UI.LookPreferences} 
						#setForegroundColor: #(#{Graphics.ColorValue} #black ) 
						#setBackgroundColor: #(#{Graphics.ColorValue} 8191 3072 3072 ) ) 
					#model: #zoomToZero 
					#label: 'z' 
					#defaultable: true ) ) ) )! !

!Siren.DisplayListSubcanvas class methodsFor: 'interface specs'!

windowSpec4Square
	"UIPainter new openOnClass: self andSelector: #windowSpec4Square"

	<resource: #canvas>
	^#(#{UI.FullSpec} 
		#window: 
		#(#{UI.WindowSpec} 
			#label: 'Display List View' 
			#bounds: #(#{Graphics.Rectangle} 640 236 1031 679 ) ) 
		#component: 
		#(#{UI.SpecCollection} 
			#collection: #(
				#(#{UI.ArbitraryComponentSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 16 0 -16 1 0 1 ) 
					#name: #displayListView 
					#flags: 0 
					#component: #createDisplayListView ) 
				#(#{UI.SliderSpec} 
					#layout: #(#{Graphics.LayoutFrame} 16 0 0 0 -16 1 16 0 ) 
					#name: #hZoom 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 3026 3026 6051 ) ) 
					#model: #hZoom 
					#callbacksSpec: 
					#(#{UI.UIEventCallbackSubSpec} 
						#valueChangeSelector: #zoomChanged ) 
					#orientation: #horizontal 
					#start: 0.25 
					#stop: 4 ) 
				#(#{UI.SliderSpec} 
					#layout: #(#{Graphics.LayoutFrame} -16 1 16 0 0 1 -16 1 ) 
					#name: #vZoom 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 3026 3026 6051 ) ) 
					#model: #vZoom 
					#callbacksSpec: 
					#(#{UI.UIEventCallbackSubSpec} 
						#valueChangeSelector: #zoomChanged ) 
					#orientation: #vertical 
					#start: 0.25 
					#stop: 4 ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 0 0 16 16 ) 
					#name: #zoomTo1 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 8117 8117 5073 ) ) 
					#model: #zoomTo1 
					#tabable: false 
					#label: 'z' 
					#style: #pixelSmall ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 -16 1 16 0 0 1 ) 
					#name: #prevPage 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 4248 4248 6789 ) ) 
					#model: #prevPage 
					#label: '-' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.LayoutFrame} -16 1 -16 1 0 1 0 1 ) 
					#name: #nextPage 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 6789 2557 2557 ) ) 
					#model: #nextPage 
					#label: ' ' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.LayoutFrame} -16 1 0 0 0 1 16 0 ) 
					#name: #zoomToLast 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 8191 5631 3072 ) ) 
					#model: #zoomToLast 
					#label: '0' 
					#defaultable: true ) ) ) )!

windowSpecLeftBottom
	"Tools.UIPainter new openOnClass: self andSelector: #windowSpecLeftBottom"

	<resource: #canvas>
	^#(#{UI.FullSpec} 
		#window: 
		#(#{UI.WindowSpec} 
			#label: 'Display List View' 
			#bounds: #(#{Graphics.Rectangle} 640 85 1031 528 ) ) 
		#component: 
		#(#{UI.SpecCollection} 
			#collection: #(
				#(#{UI.ArbitraryComponentSpec} 
					#layout: #(#{Graphics.LayoutFrame} 16 0 0 0 0 1 -16 1 ) 
					#name: #displayListView 
					#flags: 0 
					#component: #createDisplayListView ) 
				#(#{UI.SliderSpec} 
					#layout: #(#{Graphics.LayoutFrame} 16 0 -16 1 -16 1 0 1 ) 
					#name: #hZoom 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 5734 5734 5734 ) ) 
					#model: #hZoom 
					#callbacksSpec: 
					#(#{UI.UIEventCallbackSubSpec} 
						#valueChangeSelector: #zoomChanged ) 
					#orientation: #horizontal 
					#start: 0.25 
					#stop: 4 ) 
				#(#{UI.SliderSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 16 0 16 0 -16 1 ) 
					#name: #vZoom 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 5734 5734 5734 ) ) 
					#model: #vZoom 
					#callbacksSpec: 
					#(#{UI.UIEventCallbackSubSpec} 
						#valueChangeSelector: #zoomChanged ) 
					#orientation: #vertical 
					#start: 0.25 
					#stop: 4 ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.Rectangle} 0 0 16 16 ) 
					#name: #zoomTo1 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 8117 8117 5073 ) ) 
					#model: #zoomTo1 
					#tabable: false 
					#label: 'z' 
					#style: #pixelSmall ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 -16 1 16 0 0 1 ) 
					#name: #prevPage 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 4248 4248 6789 ) ) 
					#model: #prevPage 
					#label: '-' 
					#defaultable: true ) 
				#(#{UI.ActionButtonSpec} 
					#layout: #(#{Graphics.LayoutFrame} -16 1 -16 1 0 1 0 1 ) 
					#name: #nextPage 
					#colors: 
					#(#{UI.LookPreferences} 
						#setBackgroundColor: #(#{Graphics.ColorValue} 6789 2557 2557 ) ) 
					#model: #nextPage 
					#label: ' ' 
					#defaultable: true ) ) ) )! !

!Tools.ListWorkBook class methodsFor: 'interface specs'!

listMenu
	"MenuEditor new openOnClass: self andSelector: #listMenu"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: '&Page' 
				#nameKey: #fileMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&New' 
							#nameKey: #fileNew 
							#value: #fileNew 
							#shortcutKeyCharacter: $N 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileNew ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Open...' 
							#nameKey: #fileOpen 
							#value: #fileOpen 
							#shortcutKeyCharacter: $O 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileOpen ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Save' 
							#nameKey: #fileSave 
							#value: #fileSave 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $S 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileSave ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save &As...' 
							#nameKey: #fileSaveAs 
							#value: #fileSaveAs 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Load All...' 
							#nameKey: #loadAll 
							#value: #loadAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save All...' 
							#nameKey: #saveAll 
							#value: #saveAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Rename...' 
							#nameKey: #pageRename 
							#value: #pageRename 
							#enablementSelector: #canRenamePage ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Tear Off' 
							#nameKey: #pageTearOff 
							#value: #pageTearOff 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Re&move' 
							#nameKey: #pageClose 
							#value: #pageClose 
							#enablementSelector: #canClosePage ) ) #(1 5 3 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Edit' 
				#nameKey: #editMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Undo' 
							#nameKey: #editUndo 
							#value: #undo 
							#enablementSelector: #canUndo 
							#shortcutKeyCharacter: $Z 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Cu&t' 
							#nameKey: #editCut 
							#value: #cut 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $X 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #cut ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Copy' 
							#nameKey: #editCopy 
							#value: #copySelection 
							#shortcutKeyCharacter: $C 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #copy ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Paste' 
							#nameKey: #editPaste 
							#value: #paste 
							#shortcutKeyCharacter: $V 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #paste ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Select &All' 
							#nameKey: #editSelectAll 
							#value: #selectAll 
							#shortcutKeyCharacter: $A 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Encoding' 
							#nameKey: #editEncoding ) ) #(1 3 1 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Smalltalk' 
				#nameKey: #smalltalkSubmenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Do it' 
							#value: #smalltalkDoIt 
							#shortcutKeyCharacter: $D 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #doIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Print it' 
							#value: #smalltalkPrintIt 
							#shortcutKeyCharacter: $P 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #printIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Inspect it' 
							#value: #smalltalkInspectIt 
							#shortcutKeyCharacter: $I 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #inspectIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'De&bug it' 
							#value: #smalltalkDebugIt 
							#shortcutKeyCharacter: $B 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #debugIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&File it In' 
							#value: #smalltalkFileItIn ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Namespaces...' 
							#value: #editImports 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #namespaces ) ) ) #(4 1 1 ) nil ) ) ) #(3 ) nil ) decodeAsLiteralArray!

menuBar
	"MenuEditor new openOnClass: self andSelector: #menuBar"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: '&Page' 
				#nameKey: #fileMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&New' 
							#nameKey: #fileNew 
							#value: #fileNew 
							#shortcutKeyCharacter: $N 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileNew ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Open...' 
							#nameKey: #fileOpen 
							#value: #fileOpen 
							#shortcutKeyCharacter: $O 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileOpen ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Save' 
							#nameKey: #fileSave 
							#value: #fileSave 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $S 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileSave ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save &As...' 
							#nameKey: #fileSaveAs 
							#value: #fileSaveAs 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Load All...' 
							#nameKey: #loadAll 
							#value: #loadAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save All...' 
							#nameKey: #saveAll 
							#value: #saveAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Rename...' 
							#nameKey: #pageRename 
							#value: #pageRename 
							#enablementSelector: #canRenamePage ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Tear Off' 
							#nameKey: #pageTearOff 
							#value: #pageTearOff 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Re&move' 
							#nameKey: #pageClose 
							#value: #pageClose 
							#enablementSelector: #canClosePage ) ) #(1 5 3 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Edit' 
				#nameKey: #editMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Undo' 
							#nameKey: #editUndo 
							#value: #undo 
							#enablementSelector: #canUndo 
							#shortcutKeyCharacter: $Z 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Cu&t' 
							#nameKey: #editCut 
							#value: #cut 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $X 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #cut ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Copy' 
							#nameKey: #editCopy 
							#value: #copySelection 
							#shortcutKeyCharacter: $C 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #copy ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Paste' 
							#nameKey: #editPaste 
							#value: #paste 
							#shortcutKeyCharacter: $V 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #paste ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Select &All' 
							#nameKey: #editSelectAll 
							#value: #selectAll 
							#shortcutKeyCharacter: $A 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Encoding' 
							#nameKey: #editEncoding ) ) #(1 3 1 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Smalltalk' 
				#nameKey: #smalltalkSubmenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Do it' 
							#value: #smalltalkDoIt 
							#shortcutKeyCharacter: $D 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #doIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Print it' 
							#value: #smalltalkPrintIt 
							#shortcutKeyCharacter: $P 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #printIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Inspect it' 
							#value: #smalltalkInspectIt 
							#shortcutKeyCharacter: $I 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #inspectIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'De&bug it' 
							#value: #smalltalkDebugIt 
							#shortcutKeyCharacter: $B 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #debugIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&File it In' 
							#value: #smalltalkFileItIn ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Namespaces...' 
							#value: #editImports 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #namespaces ) ) ) #(4 1 1 ) nil ) ) ) #(3 ) nil ) decodeAsLiteralArray!

topicMenu
	"MenuEditor new openOnClass: self andSelector: #menuBar"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: '&Page' 
				#nameKey: #fileMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&New' 
							#nameKey: #fileNew 
							#value: #fileNew 
							#shortcutKeyCharacter: $N 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileNew ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Open...' 
							#nameKey: #fileOpen 
							#value: #fileOpen 
							#shortcutKeyCharacter: $O 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileOpen ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Save' 
							#nameKey: #fileSave 
							#value: #fileSave 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $S 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileSave ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save &As...' 
							#nameKey: #fileSaveAs 
							#value: #fileSaveAs 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Load All...' 
							#nameKey: #loadAll 
							#value: #loadAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save All...' 
							#nameKey: #saveAll 
							#value: #saveAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Rename...' 
							#nameKey: #pageRename 
							#value: #pageRename 
							#enablementSelector: #canRenamePage ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Tear Off' 
							#nameKey: #pageTearOff 
							#value: #pageTearOff 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Re&move' 
							#nameKey: #pageClose 
							#value: #pageClose 
							#enablementSelector: #canClosePage ) ) #(1 5 3 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Edit' 
				#nameKey: #editMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Undo' 
							#nameKey: #editUndo 
							#value: #undo 
							#enablementSelector: #canUndo 
							#shortcutKeyCharacter: $Z 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Cu&t' 
							#nameKey: #editCut 
							#value: #cut 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $X 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #cut ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Copy' 
							#nameKey: #editCopy 
							#value: #copySelection 
							#shortcutKeyCharacter: $C 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #copy ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Paste' 
							#nameKey: #editPaste 
							#value: #paste 
							#shortcutKeyCharacter: $V 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #paste ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Select &All' 
							#nameKey: #editSelectAll 
							#value: #selectAll 
							#shortcutKeyCharacter: $A 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Encoding' 
							#nameKey: #editEncoding ) ) #(1 3 1 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Smalltalk' 
				#nameKey: #smalltalkSubmenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Do it' 
							#value: #smalltalkDoIt 
							#shortcutKeyCharacter: $D 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #doIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Print it' 
							#value: #smalltalkPrintIt 
							#shortcutKeyCharacter: $P 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #printIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Inspect it' 
							#value: #smalltalkInspectIt 
							#shortcutKeyCharacter: $I 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #inspectIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'De&bug it' 
							#value: #smalltalkDebugIt 
							#shortcutKeyCharacter: $B 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #debugIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&File it In' 
							#value: #smalltalkFileItIn ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Namespaces...' 
							#value: #editImports 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #namespaces ) ) ) #(4 1 1 ) nil ) ) ) #(3 ) nil ) decodeAsLiteralArray!

windowSpec
	"UIPainter new openOnClass: self andSelector: #windowSpec"

	<resource: #canvas>
	^#(#{UI.FullSpec} 
		#window: 
		#(#{UI.WindowSpec} 
			#label: 'Workspace Outline' 
			#min: #(#{Core.Point} 128 156 ) 
			#max: #(#{Core.Point} 1600 1600 ) 
			#bounds: #(#{Graphics.Rectangle} 636 281 1099 790 ) ) 
		#component: 
		#(#{UI.SpecCollection} 
			#collection: #(
				#(#{UI.SequenceViewSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 0 0 0 1 -4 0.333333 ) 
					#name: #pageList 
					#model: #pageList 
					#menu: #listMenu 
					#useModifierKeys: true 
					#selectionType: #highlight ) 
				#(#{UI.SubCanvasSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 0 0.333333 0 1 0 1 ) 
					#name: #textView 
					#flags: 0 
					#clientKey: #textView ) 
				#(#{UI.ResizingSplitterSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 -4 0.333333 0 1 0 0.333333 ) 
					#name: #ResizingSplitter1 
					#flags: 8 
					#horizontal: true 
					#minAboveSize: 30 
					#minBelowSize: 30 
					#aboveWidgets: 'pageList' 
					#belowWidgets: 'textView' ) ) ) )! !

!Siren.EventListPanel class methodsFor: 'interface specs'!

windowSpec
	"Tools.UIPainter new openOnClass: self andSelector: #windowSpec"

	<resource: #canvas>
	^#(#{UI.FullSpec} 
		#window: 
		#(#{UI.WindowSpec} 
			#label: 
			#(#{Kernel.UserMessage} 
				#key: #UnlabeledCanvas 
				#defaultString: 'Event List Panel' 
				#catalogID: #labels ) 
			#bounds: #(#{Graphics.Rectangle} 645 261 1119 889 ) ) 
		#component: 
		#(#{UI.SpecCollection} 
			#collection: #(
				#(#{UI.SubCanvasSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 0 0 0 1 0 0.416666 ) 
					#name: #Dialog 
					#majorKey: #{Siren.EventListDialog} 
					#minorKey: #dialogCanvas 
					#clientKey: #editorView ) 
				#(#{UI.SubCanvasSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 0 0.416666 0 1 0 0.666666 ) 
					#name: #Text 
					#majorKey: #{Siren.EventListText} 
					#minorKey: #textCanvas1 
					#clientKey: #textView ) 
				#(#{UI.SubCanvasSpec} 
					#layout: #(#{Graphics.LayoutFrame} 0 0 0 0.666666 0 1 0 1 ) 
					#name: #Score 
					#majorKey: #{ScoreView} 
					#minorKey: #scoreCanvas1 
					#clientKey: #scoreView ) ) ) )! !

!Tools.ToolbarIconLibrary class methodsFor: 'resources'!

siren20x20
	"Tools.UIMaskEditor new openOnClass: self andSelector: #siren20x20"

	<resource: #image>
	^CachedImage on: (Image extent: 20@20 depth: 3 bitsPerPixel: 4 palette: (Graphics.MappedPalette withColors: ((Core.Array new: 7) at: 1 put: Graphics.ColorValue black; at: 2 put: Graphics.ColorValue white; at: 3 put: (Graphics.ColorValue scaledRed: 4111 scaledGreen: 4111 scaledBlue: 4111); at: 4 put: Graphics.ColorValue cyan; at: 5 put: Graphics.ColorValue yellow; at: 6 put: Graphics.ColorValue red; at: 7 put: Graphics.ColorValue blue; yourself)) usingBits: (ByteArray fromPackedString: 'DQDQDQDQDQDQDP@@DQDQDQDQDQDQDP@@DQL3DQDQDPDQDP@@DQLCDQDQD@DQDP@@DQLCDQDQAPE!!DP@@DQLCDQDPUPDVDP@@DQLCD@@EUPXQXP@@DQLCDDAUUPE!!XP@@DQLCDDAUUPE!!XP@@DQLCDDAUUPE!!XP@@D3LCDDAUUPE!!XP@@L0@CDDAUUPE!!XP@@L@@CDDAUUPE!!XP@@L@@CD@@EUPXQXP@@D0@3HQDPUPDVDP@@D3L1HQDQAPE!!DP@@DQDQD"DQD@DQDP@@DQDQDQHQDPDQDP@@DQDQDQHQDQDQDP@@DQDQH"DQDQDQDP@@'))! !

!Tools.VisualLauncher class methodsFor: 'resources'!

menuBar
	"Tools.MenuEditor new openOnClass: self andSelector: #menuBar"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: 
				#(#{Kernel.UserMessage} 
					#key: #File 
					#defaultString: '&File' 
					#catalogID: #menus ) 
				#nameKey: #file 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #FileBrowserLabel 
								#defaultString: '&File Browser' 
								#catalogID: #menus ) 
							#value: #openFileBrowser 
							#shortcutKeyCharacter: #F2 
							#shortcutModifiers: 0 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileBrowser ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #OpenFileItemLabel 
								#defaultString: '&Open File...' 
								#catalogID: #menus ) 
							#nameKey: #fileOpen 
							#value: #fileOpen ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #saveImage 
								#defaultString: '&Save Image' 
								#catalogID: #menus ) 
							#nameKey: #imageSave 
							#value: #imageSave 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileSave ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #saveImageAs 
								#defaultString: 'Save Image &As...' 
								#catalogID: #menus ) 
							#nameKey: #imageSaveAs 
							#value: #imageSaveAs ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #permSaveImageAs 
								#defaultString: '&Perm Save Image As...' 
								#catalogID: #menus ) 
							#nameKey: #filePermSaveAs 
							#value: #filePermSaveAs ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #permUndoAs 
								#defaultString: 'Perm &Undo As...' 
								#catalogID: #menus ) 
							#nameKey: #filePermUndoAs 
							#value: #filePermUndoAs ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Set VisualWorks Home...' 
							#value: #setVisualWorksHome ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #exitVW 
								#defaultString: 'E&xit VisualWorks...' 
								#catalogID: #menus ) 
							#nameKey: #visualWorksExit 
							#value: #visualWorksExit ) ) #(2 4 1 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&System' 
				#nameKey: #system 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Parcel Manager' 
							#value: #openParcelManager 
							#shortcutKeyCharacter: #F3 
							#shortcutModifiers: 0 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #cardfile ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #settings 
								#defaultString: '&Settings' 
								#catalogID: #menus ) 
							#nameKey: #visualWorksSettings 
							#value: #visualWorksSettings 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #properties ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Changes' 
							#nameKey: #changes 
							#submenu: #(#{UI.Menu} #(
									#(#{UI.MenuItem} 
										#rawLabel: 
										#(#{Kernel.UserMessage} 
											#key: #openChangeSets 
											#defaultString: 'Change &Sets' 
											#catalogID: #menus ) 
										#value: #changesOpenChangeSets 
										#shortcutKeyCharacter: #F4 
										#shortcutModifiers: 0 ) 
									#(#{UI.MenuItem} 
										#rawLabel: 
										#(#{Kernel.UserMessage} 
											#key: #condenseChanges 
											#defaultString: '&Condense Changes' 
											#catalogID: #menus ) 
										#value: #changesFileCondense ) ) #(2 ) nil ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #collectGarbage 
								#defaultString: 'Collect &Garbage' 
								#catalogID: #menus ) 
							#nameKey: #collectGarbage 
							#value: #collectGarbage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #collectAllGarbage 
								#defaultString: 'Collect &All Garbage' 
								#catalogID: #menus ) 
							#nameKey: #collectAllGarbage 
							#value: #collectAllGarbage ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Clear Transcript' 
							#nameKey: #clearTranscript 
							#value: #clearTranscript 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #delete ) ) ) #(2 1 2 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: 
				#(#{Kernel.UserMessage} 
					#key: #_Browse 
					#defaultString: '&Browse' 
					#catalogID: #menus ) 
				#nameKey: #browse 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #system 
								#defaultString: 'S&ystem' 
								#catalogID: #menus ) 
							#nameKey: #browseSystem 
							#value: #browseSystem 
							#shortcutKeyCharacter: #F5 
							#shortcutModifiers: 0 
							#labelImage: #(#{UI.ResourceRetriever} nil #systemIcon ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #classNamed 
								#defaultString: 'Class &Named...' 
								#catalogID: #menus ) 
							#nameKey: #browseClassNamed 
							#value: #browseClassNamed 
							#shortcutKeyCharacter: $F 
							#shortcutModifiers: 3 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #findClass ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #sendersOfSelector 
								#defaultString: '&Senders of Selector...' 
								#catalogID: #menus ) 
							#nameKey: #browseSendersOf 
							#value: #browseSendersOf 
							#shortcutKeyCharacter: #F6 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #selectorReferences ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #implementorsOfSelector 
								#defaultString: 'I&mplementors of Selector...' 
								#catalogID: #menus ) 
							#nameKey: #browseImplementorsOf 
							#value: #browseImplementorsOf 
							#shortcutKeyCharacter: #F7 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #selectorImplementors ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #referencesToVariable 
								#defaultString: 'References to &Variable...' 
								#catalogID: #menus ) 
							#nameKey: #browseGlobal 
							#value: #browseGlobal 
							#shortcutKeyCharacter: #F5 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #_Inspect 
								#defaultString: '&Inspect' 
								#catalogID: #menus ) 
							#nameKey: #inspect 
							#submenu: #(#{UI.Menu} #(
									#(#{UI.MenuItem} 
										#rawLabel: '&Variable...' 
										#value: #browseInspect 
										#shortcutKeyCharacter: #F5 
										#shortcutModifiers: 8 ) 
									#(#{UI.MenuItem} 
										#rawLabel: 'Processor' 
										#value: #inspectProcessor ) 
									#(#{UI.MenuItem} 
										#rawLabel: 'ScheduledControllers' 
										#value: #inspectScheduledControllers ) ) #(1 2 ) nil ) ) ) #(1 4 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: 
				#(#{Kernel.UserMessage} 
					#key: #toolsMenu 
					#defaultString: '&Tools' 
					#catalogID: #menus ) 
				#nameKey: #tools 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #_Workspace 
								#defaultString: '&Workspace' 
								#catalogID: #menus ) 
							#nameKey: #toolsNewWorkspace 
							#value: #toolsNewWorkspace 
							#shortcutKeyCharacter: #F6 
							#shortcutModifiers: 0 
							#labelImage: #(#{UI.ResourceRetriever} nil #workspaceIcon ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #openChangeList 
								#defaultString: '&Change List' 
								#catalogID: #menus ) 
							#value: #changesOpenChangeList 
							#shortcutKeyCharacter: #F7 
							#shortcutModifiers: 0 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #advanced 
								#defaultString: '&Advanced' 
								#catalogID: #menus ) 
							#nameKey: #advanced ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #dllAndCConnect 
								#defaultString: '&DLL and C Connect' 
								#catalogID: #menus ) 
							#nameKey: #dllcc 
							#value: #openExternalFinder 
							#labelImage: #(#{UI.ResourceRetriever} nil #extFinderIcon ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #database 
								#defaultString: '&Database' 
								#catalogID: #menus ) 
							#nameKey: #database 
							#submenu: #(#{UI.Menu} #(
									#(#{UI.MenuItem} 
										#rawLabel: 
										#(#{Kernel.UserMessage} 
											#key: #adHocSQL 
											#defaultString: 'Ad Hoc &SQL' 
											#catalogID: #menus ) 
										#nameKey: #adHoc 
										#value: #openAdHocQuery ) 
									#(#{UI.MenuItem} 
										#rawLabel: 
										#(#{Kernel.UserMessage} 
											#key: #dataModelr 
											#defaultString: 'Data &Modeler' 
											#catalogID: #menus ) 
										#nameKey: #dataModeler 
										#value: #openDataModelBrowser 
										#labelImage: #(#{UI.ResourceRetriever} nil #dbToolIcon ) ) 
									#(#{UI.MenuItem} 
										#rawLabel: 
										#(#{Kernel.UserMessage} 
											#key: #canvasComposer 
											#defaultString: 'Canvas &Composer' 
											#catalogID: #menus ) 
										#nameKey: #canvasComposer 
										#value: #openCanvasComposer ) 
									#(#{UI.MenuItem} 
										#rawLabel: 
										#(#{Kernel.UserMessage} 
											#key: #newDataForm 
											#defaultString: 'New Data &Form...' 
											#catalogID: #menus ) 
										#nameKey: #dataForm 
										#value: #newDataForm ) 
									#(#{UI.MenuItem} 
										#rawLabel: 
										#(#{Kernel.UserMessage} 
											#key: #newDatabseApplication 
											#defaultString: 'New Database &Application...' 
											#catalogID: #menus ) 
										#nameKey: #dataBaseAp 
										#value: #newDataMain ) ) #(1 4 ) nil ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Workbook' 
							#value: #openWorkbook ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Siren Utility' 
							#value: #openSirenUtility ) ) #(2 2 1 2 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: 
				#(#{Kernel.UserMessage} 
					#key: #windowMenu 
					#defaultString: '&Window' 
					#catalogID: #menus ) 
				#nameKey: #window ) 
			#(#{UI.MenuItem} 
				#rawLabel: 
				#(#{Kernel.UserMessage} 
					#key: #Help 
					#defaultString: '&Help' 
					#catalogID: #menus ) 
				#nameKey: #help 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: 
							#(#{Kernel.UserMessage} 
								#key: #aboutVWNC 
								#defaultString: 'About &VisualWorks NonCommercial...' 
								#catalogID: #menus ) 
							#value: #helpAbout ) ) #(1 ) nil ) ) ) #(6 ) nil ) decodeAsLiteralArray! !

!Tools.Workbook class methodsFor: 'resources'!

menuBar
	"MenuEditor new openOnClass: self andSelector: #menuBar"

	<resource: #menu>
	^#(#{UI.Menu} #(
			#(#{UI.MenuItem} 
				#rawLabel: '&Page' 
				#nameKey: #fileMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&New' 
							#nameKey: #fileNew 
							#value: #fileNew 
							#shortcutKeyCharacter: $N 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileNew ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Open...' 
							#nameKey: #fileOpen 
							#value: #fileOpen 
							#shortcutKeyCharacter: $O 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileOpen ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Save' 
							#nameKey: #fileSave 
							#value: #fileSave 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $S 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #fileSave ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save &As...' 
							#nameKey: #fileSaveAs 
							#value: #fileSaveAs 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Load All...' 
							#nameKey: #loadAll 
							#value: #loadAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Save All...' 
							#nameKey: #saveAll 
							#value: #saveAll ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Rename...' 
							#nameKey: #pageRename 
							#value: #pageRename 
							#enablementSelector: #canRenamePage ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Tear Off' 
							#nameKey: #pageTearOff 
							#value: #pageTearOff 
							#enablementSelector: #isAtTextPage ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Re&move' 
							#nameKey: #pageClose 
							#value: #pageClose 
							#enablementSelector: #canClosePage ) ) #(1 5 3 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Edit' 
				#nameKey: #editMenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Undo' 
							#nameKey: #editUndo 
							#value: #undo 
							#enablementSelector: #canUndo 
							#shortcutKeyCharacter: $Z 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Cu&t' 
							#nameKey: #editCut 
							#value: #cut 
							#enablementSelector: #isAtTextPage 
							#shortcutKeyCharacter: $X 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #cut ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Copy' 
							#nameKey: #editCopy 
							#value: #copySelection 
							#shortcutKeyCharacter: $C 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #copy ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Paste' 
							#nameKey: #editPaste 
							#value: #paste 
							#shortcutKeyCharacter: $V 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #paste ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'Select &All' 
							#nameKey: #editSelectAll 
							#value: #selectAll 
							#shortcutKeyCharacter: $A 
							#shortcutModifiers: 2 ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Encoding' 
							#nameKey: #editEncoding ) ) #(1 3 1 1 ) nil ) ) 
			#(#{UI.MenuItem} 
				#rawLabel: '&Smalltalk' 
				#nameKey: #smalltalkSubmenu 
				#submenu: #(#{UI.Menu} #(
						#(#{UI.MenuItem} 
							#rawLabel: '&Do it' 
							#value: #smalltalkDoIt 
							#shortcutKeyCharacter: $D 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #doIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Print it' 
							#value: #smalltalkPrintIt 
							#shortcutKeyCharacter: $P 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #printIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Inspect it' 
							#value: #smalltalkInspectIt 
							#shortcutKeyCharacter: $I 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #inspectIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: 'De&bug it' 
							#value: #smalltalkDebugIt 
							#shortcutKeyCharacter: $B 
							#shortcutModifiers: 2 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #debugIt ) ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&File it In' 
							#value: #smalltalkFileItIn ) 
						#(#{UI.MenuItem} 
							#rawLabel: '&Namespaces...' 
							#value: #editImports 
							#labelImage: #(#{UI.ResourceRetriever} #{ToolbarIconLibrary} #namespaces ) ) ) #(4 1 1 ) nil ) ) ) #(3 ) nil ) decodeAsLiteralArray! !

!Siren.LibSndFileInterface methodsFor: 'procedures'!

lsf_close: which
	<C: int lsf_close(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

lsf_create: name with: mode with: format with: rate with: channels
	<C: int lsf_create(char * name, unsigned int mode, unsigned int format, unsigned int rate, unsigned int channels)>
	^self externalAccessFailedWith: _errorCode!

lsf_get_channels: which
	<C: int lsf_get_channels(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

lsf_get_format: which
	<C: int lsf_get_format(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

lsf_get_frames: which
	<C: int lsf_get_frames(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

lsf_get_rate: which
	<C: int lsf_get_rate(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

lsf_open: name with: mode
	<C: int lsf_open(char * name, unsigned int mode)>
	^self externalAccessFailedWith: _errorCode!

lsf_read_Fsamples: which with: where with: count
	<C: int lsf_read_Fsamples(unsigned int which, float * where, unsigned int count)>
	^self externalAccessFailedWith: _errorCode!

lsf_read_Isamples: which with: where with: count
	<C: int lsf_read_Isamples(unsigned int which, short * where, unsigned int count)>
	^self externalAccessFailedWith: _errorCode!

lsf_seek: which with: pos with: key
	<C: int lsf_seek(unsigned int which, unsigned int pos, unsigned int key)>
	^self externalAccessFailedWith: _errorCode!

lsf_write_Fsamples: which with: where with: count
	<C: int lsf_write_Fsamples(unsigned int which, float * where, unsigned int count)>
	^self externalAccessFailedWith: _errorCode!

lsf_write_Isamples: which with: where with: count
	<C: int lsf_write_Isamples(unsigned int which, short * where, unsigned int count)>
	^self externalAccessFailedWith: _errorCode! !

!Siren.PortMidiInterface methodsFor: 'procedures'!

pm_close: which
	<C: int pm_close(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

pm_count_devices
	<C: int pm_count_devices(void)>
	^self externalAccessFailedWith: _errorCode!

pm_default_input_device
	<C: int pm_default_input_device(void)>
	^self externalAccessFailedWith: _errorCode!

pm_default_output_device
	<C: int pm_default_output_device(void)>
	^self externalAccessFailedWith: _errorCode!

pm_get: which
	<C: long pm_get(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

pm_get_name: which with: resultString
	<C: int pm_get_name(unsigned int which, OEoop resultString)>
	^self externalAccessFailedWith: _errorCode!

pm_has_error: which
	<C: int pm_has_error(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

pm_initialize
	<C: int pm_initialize(void)>
	^self externalAccessFailedWith: _errorCode!

pm_open: device with: direction
	<C: int pm_open(int device, unsigned int direction)>
	^self externalAccessFailedWith: _errorCode!

pm_poll: which
	<C: int pm_poll(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

pm_read: which
	<C: long pm_read(unsigned int which)>
	^self externalAccessFailedWith: _errorCode!

pm_read_controllers: which with: fromController with: toController with: data
	<C: int pm_read_controllers(unsigned int which, unsigned int fromController, unsigned int toController, short * data)>
	^self externalAccessFailedWith: _errorCode!

pm_start_controller_cacheing
	<C: int pm_start_controller_cacheing(void)>
	^self externalAccessFailedWith: _errorCode!

pm_stop_controller_cacheing
	<C: int pm_stop_controller_cacheing(void)>
	^self externalAccessFailedWith: _errorCode!

pm_terminate
	<C: int pm_terminate(void)>
	^self externalAccessFailedWith: _errorCode!

pm_test
	<C: void pm_test(void)>
	^self externalAccessFailedWith: _errorCode!

pm_write_data2: which with: d1 with: d2
	<C: int pm_write_data2(unsigned int which, unsigned char d1, unsigned char d2)>
	^self externalAccessFailedWith: _errorCode!

pm_write_data3: which with: d1 with: d2 with: d3
	<C: int pm_write_data3(unsigned int which, unsigned char d1, unsigned char d2, unsigned char d3)>
	^self externalAccessFailedWith: _errorCode!

pm_write_long: which with: msg with: length
	<C: int pm_write_long(unsigned int which, long * msg, unsigned int length)>
	^self externalAccessFailedWith: _errorCode!

pm_write_short: which with: msg
	<C: int pm_write_short(unsigned int which, long msg)>
	^self externalAccessFailedWith: _errorCode! !

!Siren.FFTWInterface methodsFor: 'procedures'!

fftw_float_to_int: data
	<C: void fftw_float_to_int(int * data)>
	^self externalAccessFailedWith: _errorCode!

fftw_float_to_short: data
	<C: void fftw_float_to_short(short * data)>
	^self externalAccessFailedWith: _errorCode!

fftw_forward_transform
	<C: void fftw_forward_transform(void)>
	^self externalAccessFailedWith: _errorCode!

fftw_initialize: size with: samples with: spectrum
	<C: int fftw_initialize(unsigned int size, float * samples, float * spectrum)>
	^self externalAccessFailedWith: _errorCode!

fftw_int_to_float: data
	<C: void fftw_int_to_float(int * data)>
	^self externalAccessFailedWith: _errorCode!

fftw_mag_spectrum: data
	<C: void fftw_mag_spectrum(float * data)>
	^self externalAccessFailedWith: _errorCode!

fftw_reverse_transform
	<C: void fftw_reverse_transform(void)>
	^self externalAccessFailedWith: _errorCode!

fftw_short_to_float: data
	<C: void fftw_short_to_float(short * data)>
	^self externalAccessFailedWith: _errorCode! !

!Siren.PortAudioInterface methodsFor: 'procedures'!

pa_close
	<C: int pa_close(void)>
	^self externalAccessFailedWith: _errorCode!

pa_initialize
	<C: int pa_initialize(void)>
	^self externalAccessFailedWith: _errorCode!

pa_open_p: device with: out_ch with: format with: rate with: blockSize
	<C: int pa_open_p(int device, unsigned int out_ch, unsigned long format, unsigned int rate, unsigned int blockSize)>
	^self externalAccessFailedWith: _errorCode!

pa_open_s: device with: in_ch with: out_ch with: format with: rate with: blockSize with: io_sem with: in_buffer with: out_buffer
	<C: int pa_open_s(int device, unsigned int in_ch, unsigned int out_ch, unsigned long format, unsigned int rate, unsigned int blockSize, _oop io_sem, unsigned short * in_buffer, unsigned short * out_buffer)>
	^self externalAccessFailedWith: _errorCode!

pa_play: out_buffer with: numFrames
	<C: int pa_play(unsigned short * out_buffer, unsigned int numFrames)>
	^self externalAccessFailedWith: _errorCode!

pa_start
	<C: int pa_start(void)>
	^self externalAccessFailedWith: _errorCode!

pa_stop
	<C: int pa_stop(void)>
	^self externalAccessFailedWith: _errorCode!

pa_terminate
	<C: int pa_terminate(void)>
	^self externalAccessFailedWith: _errorCode! !

#{Siren.SirenUtility} initialize!

#{Siren.MusicEvent} initialize!

#{Siren.SoundFile} initialize!

#{Siren.LibSndFileInterface} initialize!

#{Siren.Amplitude} initialize!

#{Siren.SymbolicPitch} initialize!

#{Siren.SymbolicLoudness} initialize!

#{Siren.MIDIPitch} initialize!

#{Siren.MIDIDevice} initialize!

#{Siren.MIDIPort} initialize!

#{Siren.EventList} initialize!

#{Siren.OSCPort} initialize!

#{Siren.MIDIVelocity} initialize!

#{Siren.SoundPort} initialize!

#{Siren.EventScheduler} initialize!

#{Siren.PortAudioInterface} initialize!

#{Siren.DisplayListView} initialize!

#{Siren.OSCBundle} initialize!

#{Siren.Duration} initialize!

#{Siren.Pitch} initialize!

#{Siren.IOVoice} initialize!

#{Siren.MIDIVoice} initialize!

"Imported Classes:"!

self error: 'Attempting to file-in parcel imports.  Choose terminate or close'!

Smalltalk.Core defineClass: #Object
	superclass: nil
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Objects'!

Smalltalk.External defineClass: #ExternalInterface
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: 'includeFiles includeDirectories libraryFiles libraryDirectories virtual optimizationLevel '
	imports: '
			private Kernel.OpcodePool.*
			'
	category: 'External-Interface'!

Smalltalk.Core defineClass: #Magnitude
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Magnitude-General'!

Smalltalk.Core defineClass: #Number
	superclass: #{Core.ArithmeticValue}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Magnitude-Numbers'!

Smalltalk.Graphics defineClass: #DependentComposite
	superclass: #{Graphics.CompositePart}
	indexedType: #none
	private: false
	instanceVariableNames: 'model '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Graphics-Visual Objects'!

Smalltalk.Core defineClass: #Association
	superclass: #{Core.LookupKey}
	indexedType: #none
	private: false
	instanceVariableNames: 'value '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Support'!

Smalltalk.Core defineClass: #UndefinedObject
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Objects'!

Smalltalk.Core defineClass: #Random
	superclass: #{Core.Stream}
	indexedType: #none
	private: false
	instanceVariableNames: 'seed '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Magnitude-Numbers'!

Smalltalk.Core defineClass: #Integer
	superclass: #{Core.Number}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Magnitude-Numbers'!

Smalltalk.Core defineClass: #Behavior
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'superclass methodDict format subclasses '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Classes'!

Smalltalk.Core defineClass: #Boolean
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Objects'!

UI.MotifWidgetPolicy class
	instanceVariableNames: ''!

Smalltalk.Core defineClass: #Collection
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Abstract'!

Core.Collection class
	instanceVariableNames: ''!

Smalltalk.Graphics defineClass: #DependentPart
	superclass: #{Graphics.VisualPart}
	indexedType: #none
	private: false
	instanceVariableNames: 'model '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Graphics-Visual Objects'!

Smalltalk.Core defineClass: #SequenceableCollection
	superclass: #{Core.Collection}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Abstract'!

Smalltalk.Core defineClass: #WordArray
	superclass: #{Core.IntegerArray}
	indexedType: #bytes
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Arrayed'!

Smalltalk.Core defineClass: #List
	superclass: #{Core.ArrayedCollection}
	indexedType: #none
	private: false
	instanceVariableNames: 'dependents collection limit collectionSize '
	classInstanceVariableNames: ''
	imports: ''
	category: 'UIBasics-Collections'!

Smalltalk.UI defineClass: #ControllerWithMenu
	superclass: #{UI.Controller}
	indexedType: #none
	private: false
	instanceVariableNames: 'menuHolder performer '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Interface-Framework'!

Graphics.ColorValue class
	instanceVariableNames: ''!

Smalltalk.UI defineClass: #AutoScrollingView
	superclass: #{UI.View}
	indexedType: #none
	private: false
	instanceVariableNames: 'scrollOffset '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Interface-Support'!

Smalltalk.UI defineClass: #BorderDecorationPolicy
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'inset scrollbarThickness scrollButtonThickness markerThickness minMarkerExtent menuBarHeight verticalScrollBarOnRight border '
	classInstanceVariableNames: ''
	imports: ''
	category: 'UIBuilder-Support'!

Smalltalk.Core defineClass: #ClassDescription
	superclass: #{Core.Behavior}
	indexedType: #none
	private: false
	instanceVariableNames: 'instanceVariables organization '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Classes'!

Smalltalk.Core defineClass: #LimitedPrecisionReal
	superclass: #{Core.Number}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Magnitude-Numbers'!

Smalltalk.Core defineClass: #True
	superclass: #{Core.Boolean}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Objects'!

Smalltalk.Core defineClass: #String
	superclass: #{Core.CharacterArray}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Text'!

Smalltalk.Core defineClass: #PositionableStream
	superclass: #{Core.PeekableStream}
	indexedType: #none
	private: false
	instanceVariableNames: 'collection position readLimit writeLimit policy '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Streams'!

Smalltalk.OS defineClass: #ExternalStream
	superclass: #{Core.PositionableStream}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'OS-Streaming'!

Smalltalk.Heeg defineClass: #HtmlWriter
	superclass: #{Heeg.RtfWriter}
	indexedType: #none
	private: false
	instanceVariableNames: 'wasSansSerif '
	classInstanceVariableNames: ''
	imports: ''
	category: 'GH-Utilities'!

Heeg.HtmlWriter class
	instanceVariableNames: ''!

Smalltalk.Core defineClass: #Interval
	superclass: #{Core.SequenceableCollection}
	indexedType: #none
	private: false
	instanceVariableNames: 'start stop step '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Sequenceable'!

Smalltalk.Core defineClass: #Dictionary
	superclass: #{Core.Set}
	indexedType: #objects
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Unordered'!

Smalltalk.Core defineClass: #Character
	superclass: #{Core.Magnitude}
	indexedType: #immediate
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Magnitude-General'!

Smalltalk.UI defineClass: #Model
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'dependents '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Objects'!

Smalltalk.UI defineClass: #ScrollValueHolder
	superclass: #{UI.Model}
	indexedType: #none
	private: false
	instanceVariableNames: 'offset grid extraSpace permitDisplayOutput '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Interface-Support'!

Smalltalk.UI defineClass: #ApplicationModel
	superclass: #{UI.Model}
	indexedType: #none
	private: false
	instanceVariableNames: 'builder uiSession eventHandlers '
	classInstanceVariableNames: 'savedWindowInformation '
	imports: ''
	category: 'UIBuilder-Framework'!

Smalltalk.Tools defineClass: #WorkspacePage
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'workspace label verboseLabel filename fileTimestamp textModel unacceptedText savedSelection savedFileTimestamp textModelOverride '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Tools-Workspace'!

Tools.WorkspacePage class
	instanceVariableNames: ''!

Smalltalk.Tools defineClass: #AbstractWorkspace
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'locals importAll specificImports importSummaryHolder pageExplanationHolder statusBarVisible encodingChannel menuBarHolder '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Tools-Workspace'!

Smalltalk.Tools defineClass: #VisualLauncher
	superclass: #{UI.ApplicationModel}
	indexedType: #none
	private: false
	instanceVariableNames: 'menuBar toolBar textCollector toolDock '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Tools-Misc'!

Tools.VisualLauncher class
	instanceVariableNames: ''!

Smalltalk.Tools defineClass: #Workbook
	superclass: #{Tools.AbstractWorkspace}
	indexedType: #none
	private: false
	instanceVariableNames: 'pageHolder lastInstalledPage '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Tools-Workspace'!

Tools.Workbook class
	instanceVariableNames: ''!

Smalltalk.External defineClass: #CPointer
	superclass: #{External.CDatum}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'External-Data'!

Smalltalk.Core defineClass: #Symbol
	superclass: #{Core.String}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Collections-Text'!

Smalltalk.UI defineClass: #SelectionTracker
	superclass: #{Core.Object}
	indexedType: #none
	private: false
	instanceVariableNames: 'controller '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Interface-Events-Trackers'!

Smalltalk.Core defineClass: #Point
	superclass: #{Core.ArithmeticValue}
	indexedType: #none
	private: false
	instanceVariableNames: 'x y '
	classInstanceVariableNames: ''
	imports: ''
	category: 'Graphics-Geometry'!

Tools.ToolbarIconLibrary class
	instanceVariableNames: ''!

Smalltalk.Core defineClass: #SmallInteger
	superclass: #{Core.Integer}
	indexedType: #immediate
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: 'minVal maxVal maxBits maxBytes '
	imports: ''
	category: 'Magnitude-Numbers'!

Smalltalk.Core defineClass: #False
	superclass: #{Core.Boolean}
	indexedType: #none
	private: false
	instanceVariableNames: ''
	classInstanceVariableNames: ''
	imports: ''
	category: 'Kernel-Objects'!
