'From Squeak3.0 of 4 February 2001 [latest update: #3545] on 19 February 2001 at 2:27:40 pm'!

!DurationEvent methodsFor: 'scheduling' stamp: 'stp 2/19/2001 13:40'!
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].
	(Siren voices includesKey: aVoice)
			ifTrue: [^(Siren voices at: aVoice) playEvent: self at: aTime].
	aVoice isInteger
			ifTrue: [^MIDIVoice default playEvent: self at: aTime].
	InputSensor default shiftPressed
		ifTrue: [self error: 'Unknown voice.'].
	Transcript show: 'Cannot play event, unknown voice: ', aVoice printString; cr! !


!DynamicCloud methodsFor: 'playing' stamp: 'stp 2/19/2001 14:14'!
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.
"	pattern _ self."
	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.
			vRange isSymbol
				ifTrue: [vRange := nil]
				ifFalse: [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 == nil
							ifTrue: [self voice atRandom]
							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) and: [vRange ~~ nil])
		ifTrue: [vRange _ v1 + (v3 * percent) to: v2 + (v4 * percent)]].
	^pattern!
]style[(9 2 151 137 18 1972)f1bcblue;,f1,f1cred;,f1,f1cred;,f1! !


!MIDIDevice methodsFor: 'initialization' stamp: 'stp 2/19/2001 13:31'!
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! !


!MIDIDevice class methodsFor: 'instance creation' stamp: 'stp 2/19/2001 13:30'!
new
	"Open a generic MidiDevice on the default Port"
	"MIDIDevice new"

	| me |
	me := super new initialize.
"	me port: MIDIPort default open."
	^me! !


!MIDIPatchEditor methodsFor: 'GM name selector' stamp: 'stp 2/19/2001 13:58'!
openNamePopUp
	"Open a view with 128 buttons for the GM instrument names."
	"See the setInstr: method for the behavior on mouse-down."
	"MIDIPatchEditor new openNamePopUp"

	World ifNotNil: [self error: 'Cannot open MVC tools in Morphic projects.'].
	self createNamePopUp.
	instrView controller open"NoTerminate"! !


!MIDIPort class methodsFor: 'instance creation' stamp: 'stp 2/19/2001 13:33'!
concreteClass
	"Answer the class to use for MIDI"

"If you always want to use the built-in MIDI prims, use the first selection"
	^MIDIPort

"Else, use the fancy primitives -- note the kludjy way of guessing the platform..."
"	FileDirectory pathNameDelimiter = $\ ifTrue:[^Win32MIDIPort].
	FileDirectory pathNameDelimiter = $: ifTrue:[^PluginMIDIPort].
	FileDirectory pathNameDelimiter = $/ ifTrue:[self error: 'MIDI is not supported on UNIX yet.'].
	^MIDIPort
"! !


!Siren class methodsFor: 'class initialization' stamp: 'stp 2/19/2001 13:54'!
initializeSiren
	"Edit these to taste for your installation."
	"Siren initializeSiren"
	"SRoot" "Print-it to see the current setting"

	SRoot := SSrcDir.								"Root of media content; edit to taste"
	SDataDir := SRoot, 'Sounds', FileSep.			"Basic data directory"
	SScoreDir := SRoot, 'Scores', FileSep.			"Scores"
	SSoundDir := SRoot, 'Sounds', FileSep.			"Sound files"
"or try
	SSoundDir := 'CD-ROM:Packages:Multimedia:Siren:Sounds:'. "
"	(Smalltalk includesKey: #MinneStoreDB)
		ifTrue: [SDatabase := (Smalltalk at: #MinneStoreDB) openOn: SDatabaseDir]"

"Set up the default named voices"
	(FMSound class organization listAtCategoryNamed: #instruments) 
		do: [ :instr | 
			Siren voiceNamed: instr 
				put: (SynthVoice new instrument: (FMSound perform: instr))]! !

!Siren class methodsFor: 'class initialization' stamp: 'stp 2/19/2001 13:56'!
startUp
	"Make a splash screen on start-up"
	"Smalltalk addToStartUpList: Siren"
	"Siren startUp"

	| center ext pos text | 
	FileSep := FileDirectory pathNameDelimiter asString.
	SLogo ifNotNil:
		[ | blu |
		"Display fillColor: (Color r: 0.2 g: 0.2 b: 0.5)."
		blu := Form extent: Display extent depth: 8.
		blu fillColor: (Color r: 0.2 g: 0.2 b: 0.5).
		Display zoomInTo: blu at: 0@0.
		center := Display center.
		ext := SLogo extent.
		(Delay forMilliseconds: 500) wait.
		pos := (center - (ext / 2)) truncated.
		SLogo displayOn: Display at: pos rule: Form paint.
		text := 'Siren (c) 1984-2001 by Stephen T. Pope (stp@create.ucsb.edu)' asText "allBold".
		text addAttribute: (TextFontChange font3) from: 1 to: 5.
		text addAttribute: (TextEmphasis bold) from: 1 to: 5.
		text addAttribute: (TextFontChange font2) from: 6 to: text size.
		(Delay forMilliseconds: 500) wait.
		(DisplayText text: text) displayOn: Display at: pos + (8@ (ext y - 24)) rule: Form paint.
		(Delay forSeconds: 1) wait].
	World
		ifNil: [ScheduledControllers restore]
		ifNotNil: [World restoreDisplay]! !

!Siren class methodsFor: 'class var accessing' stamp: 'stp 2/19/2001 14:17'!
schedule
	"Answer the 'global' scheduler."
	"Siren schedule"

	^EventScheduler instance! !

!Siren class methodsFor: 'user interface' stamp: 'stp 2/19/2001 13:59'!
sirenMenu
	"Run the menu of Siren test/set-up methods"

	| menuItems choice |
	menuItems := OrderedCollection new.
	menuItems add: 'test MIDI' -> [MIDIPort testANote].
	menuItems add: 'close MIDI' -> [MIDIPort cleanUp].
	menuItems add: 'show MIDI' -> [MIDIPort showInput].
	menuItems add: 'play a sound' -> [FMSound majorChord play].
	menuItems add: 'pick a voice' -> [MIDIPatchEditor new openNamePopUp].
	menuItems add: 'edit MIDI patches' -> [MIDIPatchEditor new openNamePopUp].
	menuItems add: 'browse Paleo object sets' -> [SMSBrowser openOn: (YYYJD DB)].
	menuItems add: 'explore Siren object sets' -> [Siren objectSets explore].
	menuItems add: 'explore event lists' -> [Siren eventLists explore].
	menuItems add: 'open Siren Outline' -> [OutlineBrowser openOn: 
						Siren outline label: 'Siren Outline' ].
	choice := (PopUpMenu labelArray: (menuItems collect: [ :each | each key])
					lines: #(3 4 6 9)) startUp.
	choice = 0 ifTrue: [^nil].
	^((menuItems at: choice) value) value! !

