-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathsuy-sound-related-modifications.cs
476 lines (375 loc) · 14.4 KB
/
suy-sound-related-modifications.cs
1
'From MIT Squeak 0.9.4 (June 1, 2003) [No updates present.] on 19 May 2017 at 12:10:35 pm'!Object subclass: #SoundPlayer instanceVariableNames: '' classVariableNames: 'ActiveSounds Buffer BufferIndex BufferMSecs NeedRelease PlayerProcess PlayerSemaphore ReadyForBuffer ReleaseProcess ReverbState SamplingRate SoundJustStarted SoundSupported Stereo UseReadySemaphore UseReverb ' poolDictionaries: '' category: 'Sound-Synthesis'!!DialogBoxMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 7/29/2014 11:39'!message: aMessage details: aDetailsString font: aStrikeFont "Set and position my message/question text." | lines m | self useMorphMessage ifFalse: [^self iPhoneMessage: aMessage localized, '-', aDetailsString localized]. messageLineMorphs ifNotNil: [ messageLineMorphs submorphsDo: [:lineM | lineM delete]]. messageLineMorphs _ AlignmentMorph newColumn color: Color transparent; hResizing: #shrinkWrap; vResizing: #shrinkWrap; centering: #center. lines _ aMessage lines. 1 to: lines size do: [:n | m _ StringMorph contents: (lines at: n) font: aStrikeFont. messageLineMorphs addMorphBack: m]. lines _ aDetailsString lines. 1 to: lines size do: [:n | m _ StringMorph contents: (lines at: n) font: aStrikeFont. messageLineMorphs addMorphBack: m]. mainColumn addMorph: messageLineMorphs. self changed.! !!ReporterBlockMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 6/12/2014 01:04'!toggleWatcher "Toggle between hiding and showing a watcher for this block." | frame w palette | (frame := self ownerThatIsA: ScratchFrameMorph) ifNil: [^ self]. (w := frame watcherForBlock: self) ifNil: [ | newWatcher | newWatcher := self createWatcher. frame showWatcher: newWatcher. frame watcherWasAdded: newWatcher reporter: self] ifNotNil: [ w delete. frame watcherWasDeleted: w]. palette := self ownerThatIsA: ScratchBlockPaletteMorph. palette ifNotNil: [palette updateWatcherButtonsForFrame: frame].! !!ScratchFrameMorph methodsFor: '*ScratchOnIPad-accessing' stamp: 'mu 6/6/2014 17:36'!recorderRelatedWatchers | selectors | selectors := #(#soundLevel #isLoud). ^ self scratchWatchers select: [:each | | sele | sele := each valueOfProperty: #selector ifAbsent: []. selectors includes: sele]! !!ScratchFrameMorph methodsFor: '*ScratchOnIPad-callback' stamp: 'mu 6/6/2014 21:31'!watcherWasAdded: aWatcherMorph reporter: aReporterMorph IPhoneScratchProxy isActive ifFalse: [^self]. aWatcherMorph setProperty: #selector toValue: aReporterMorph selector.! !!ScratchFrameMorph methodsFor: '*ScratchOnIPad-callback' stamp: 'mu 6/12/2014 01:03'!watcherWasDeleted: aWatcherMorph IPhoneScratchProxy isActive ifFalse: [^self]. self recorderRelatedWatchers size = 0 ifTrue: [ ScriptableScratchMorph getSoundRecorder stopRecording ]! !!ScratchNotePlayer methodsFor: 'initialization' stamp: 'mu 3/27/2017 13:25'!initialize midiPort _ nil. channel _ 1. volume _ 100. midiKey _ 60.! !!ScratchNotePlayer methodsFor: 'accessing' stamp: 'mu 3/19/2017 16:57'!instrument: aNumber "Set the MIDI instrument number between 1 and 128. Instrument number 1 is piano. The instrument numbers are defined by the General MIDI specification; you can find the list of instruments on the web." | instr | instr _ aNumber rounded within: 1 and: 128. SoiMIDISynth programChange: instr - 1 channel: channel - 1! !!ScratchNotePlayer methodsFor: 'playing' stamp: 'mu 3/27/2017 13:48'!drumOff self drumOff: midiKey! !!ScratchNotePlayer methodsFor: 'playing' stamp: 'mu 3/27/2017 14:55'!drumOff: aNumber "Turn of the currently sounding drum, if any." aNumber ifNotNil: [ SoiMIDISynth noteOff: aNumber velocity: 127 channel: 9. ]. snd ifNotNil: [ snd stopGracefully. snd _ nil].! !!ScratchNotePlayer methodsFor: 'playing' stamp: 'mu 3/19/2017 15:15'!drumOn: aNumber "Play the given drum number at the current volume level. The drum number is defined by the General MIDI spec, which you can find on the web. The absolute value of the rounded drum number is used." | velocity | midiKey _ aNumber rounded abs within: 0 and: 127. velocity _ (1.27 * volume) rounded within: 0 and: 127. SoiMIDISynth noteOn: midiKey velocity: velocity channel: 9! !!ScratchNotePlayer methodsFor: 'playing' stamp: 'mu 3/27/2017 13:37'!noteOff self noteOff: midiKey! !!ScratchNotePlayer methodsFor: 'playing' stamp: 'mu 3/27/2017 14:07'!noteOff: aNumber "Turn of the currently sounding note, if any." aNumber ifNotNil: [ SoiMIDISynth noteOff: aNumber velocity: 127 channel: channel - 1. ]. snd ifNotNil: [ snd stopGracefully. snd _ nil].! !!ScratchNotePlayer methodsFor: 'playing' stamp: 'mu 3/19/2017 15:06'!noteOn: aNumber | velocity | "Play the given note at the current volume level. The note number gives the piano key number where 60 is middle-C and there are 12 keys per octave. The absolute value of the note number is used." midiKey _ aNumber rounded abs within: 0 and: 127. velocity _ (1.27 * volume) rounded within: 0 and: 127. SoiMIDISynth noteOn: midiKey velocity: velocity channel: (channel - 1) ! !!ScratchNotePlayer methodsFor: 'printing' stamp: 'mu 3/27/2017 14:50'!printOn: aStream aStream nextPutAll: self class name. aStream space; nextPutAll: 'channel: ', channel asString. aStream space; nextPutAll: 'note: ', midiKey asString! !!ScratchNoteSelector methodsFor: 'initialization' stamp: 'mu 3/27/2017 10:16'!initialize super initialize. selectedVal _ nil. "if nothing is ever selected, then don't return anything" notePlayer _ nil. self buildKeyboard: 2 baseOctave: 4 keyWidth: 19. self setUpNoteDisplay.! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-defaults' stamp: 'mu 6/12/2014 21:53'!maxRecodingSeconds ^SoiSettings default maxRecodingSeconds! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-private' stamp: 'mu 6/12/2014 00:53'!isRecording ^currentMode == #record! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-private' stamp: 'mu 6/12/2014 02:04'!stopOtherRecordingIfRunning | frame | frame := client ownerThatIsA: ScratchFrameMorph. frame ifNil: [^self]. frame recorderRelatedWatchers do: [:each | each delete. frame watcherWasDeleted: each. ].! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 5/29/2014 01:17'!cancelled "Cancel button was pressed." recorder stopIfPlaying. recorder isActive ifTrue: [ recorder pause. recorder clearRecordedSound. ]. super cancelled.! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 5/28/2014 21:13'!initialize super initialize. self makeModeButtonsPane. self makeRecordMeter. self makeRecordTimer. recorder _ ScriptableScratchMorph getSoundRecorder. recorder recordLevel: 0.5. savedMeterLevels _ OrderedCollection new. curStep _ 1. duration _ 0. self setMode: #stop. self setMode: #play isDisabled: true. "fixes layout" mainColumn centering: #topLeft. self extent: 380@145.! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 5/28/2014 23:10'!play recorder playback. curStep _ 1. self setMode: #play; setMode: #record isDisabled: true. self changed.! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 6/13/2014 00:21'!record self isRecording ifTrue: [^self]. recorder isActive ifTrue: [ (DialogBoxMorph ask: 'To record, you need to stop sound level sensing. Stop now?') ifTrue: [self stopOtherRecordingIfRunning] ifFalse: [^self cancelled] ]. recorder clearRecordedSound. [recorder resumeRecording] ifError: [:err :rcvr | err nslog: '**record**'. ^self cancelled]. savedMeterLevels _ OrderedCollection new. duration _ 0. self setMode: #record; setMode: #play isDisabled: false. self changed ! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 7/2/2014 21:28'!updateTime | dur | dur := duration rounded. timerMorph contents: (self convertToMmss: dur). (currentMode == #record and: [dur > self maxRecodingSeconds]) ifTrue: [ self stop. DialogBoxMorph inform: 'Exceeded recording time limit'. ]! !!ScratchSoundRecorderDialogMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 5/29/2014 01:16'!yes | samples samplingRate snd | recorder stopIfPlaying. self stop. samples _ recorder condensedSamples. samplingRate _ recorder samplingRate. samplingRate = 44100 ifTrue: [ samples _ samples downSampledLowPassFiltering: false. samplingRate _ 22050]. snd _ SampledSound samples: samples samplingRate: samplingRate. samples size nslog: 'yes->samples'. (client notNil and: [samples size > 0]) ifTrue: [ client saveSound: snd name: 'recording' localized,'1']. recorder clearRecordedSound. super yes.! !!ScriptableScratchMorph methodsFor: 'sound ops' stamp: 'mu 3/27/2017 13:48'!drum: midiKey duration: beats elapsed: elapsedMSecs from: aNotePlayer | stage player | aNotePlayer ifNil: [ "first call, start playing the drum" (stage _ self ownerThatIsA: ScratchStageMorph) ifNil: [^ ScratchNotePlayer new]. (player _ stage notePlayerFor: self) ifNil: [^ ScratchNotePlayer new]. ^ player copy drumOn: midiKey]. elapsedMSecs >= ((60000 * beats) // self tempo) ifTrue: [aNotePlayer drumOff: midiKey].! !!ScriptableScratchMorph methodsFor: 'sound ops' stamp: 'mu 3/27/2017 13:36'!noteOn: midiKey duration: beats elapsed: elapsedMSecs from: aNotePlayer | stage player | aNotePlayer ifNil: [ "first call, start playing the note" (stage _ self ownerThatIsA: ScratchStageMorph) ifNil: [^ ScratchNotePlayer new]. (player _ stage notePlayerFor: self) ifNil: [^ ScratchNotePlayer new]. ^ player copy noteOn: midiKey]. elapsedMSecs >= ((60000 * beats) // self tempo) ifTrue: [aNotePlayer noteOff: midiKey].! !!ScriptableScratchMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 6/12/2014 23:35'!isLoud ^ [self class soundRecorder meterLevel > 30] ifError: [false]! !!ScriptableScratchMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 6/12/2014 23:34'!soundLevel ^ [self class soundRecorder meterLevel] ifError: [0]! !!ScratchStageMorph methodsFor: '*ScratchOnIPad-override' stamp: 'mu 5/13/2017 16:13'!midiAllNotesOff | channels | "If the MIDI port is open, send an 'all notes off' command on every channel." "midiPort ifNil: [^ self]. midiPort ensureOpenIfFail: [self closeMIDI]." channels := Set with: 10. channels addAll: (notePlayerDict values collect: [:pl | pl channel]). channels do: [:cha | "player noteOff." SoiMIDISynth allSoundOff: cha. ]. channels nslog: '###midiAllNotesOff###'. ! !!ScriptableScratchMorph class methodsFor: '*ScratchOnIPad-accessing' stamp: 'mu 5/28/2014 21:12'!getSoundRecorder Recorder ifNil: [ Recorder _ SoundRecorder new. ]. ^ Recorder! !!SoiSettings methodsFor: 'settings access' stamp: 'mu 6/12/2014 21:52'!maxRecodingSeconds ^(self settingsDict at: 'MaxRecodingSeconds' ifAbsentPut: [60]) asInteger! !!SoundPlayer class methodsFor: '*ScracthOnIPad-override' stamp: 'mu 5/28/2014 01:30'!shutDown "Stop player process, for example before snapshotting." self stopReleaseProcess. self stopPlayerProcess. ReverbState _ nil.! !!SoundPlayer class methodsFor: '*ScracthOnIPad-override' stamp: 'mu 5/28/2014 01:29'!startUp "Start up the player process." SoundPlayer initialize. SoundPlayer startPlayerProcessBufferSize: (BufferMSecs * SamplingRate) // 1000 rate: SamplingRate stereo: Stereo. self startReleaseProcess! !!SoundPlayer class methodsFor: '*ScracthOnIPad-override' stamp: 'mu 5/29/2014 16:05'!stopPlayingAll "Stop playing all sounds." PlayerSemaphore critical: [ ActiveSounds _ ActiveSounds species new]. self forceRelease! !!SoundPlayer class methodsFor: '*ScratchOnIPad' stamp: 'mu 6/11/2014 16:10'!basicRelease self stopPlayerProcess.! !!SoundPlayer class methodsFor: '*ScratchOnIPad' stamp: 'mu 5/29/2014 01:28'!forceRelease NeedRelease := true. "self basicRelease"! !!SoundPlayer class methodsFor: '*ScratchOnIPad' stamp: 'mu 5/29/2014 01:26'!releaseIfNeeded {ActiveSounds size. NeedRelease} nslog: 'ActiveSounds size, NeedRelease'. ActiveSounds size = 0 ifTrue: [NeedRelease ifTrue: [self basicRelease. NeedRelease _ false]] ifFalse: [NeedRelease _ true]. (Delay forSeconds: 1) wait! !!SoundPlayer class methodsFor: '*ScratchOnIPad' stamp: 'mu 5/29/2014 01:26'!startReleaseProcess "SoundPlayer startReleaseProcess" "IPhoneScratchProxy isActive ifFalse: [^self]." ReleaseProcess == nil ifFalse: [self stopReleaseProcess]. NeedRelease == nil ifTrue: [NeedRelease := true]. ReleaseProcess := [ [PlayerProcess notNil] whileTrue: [ self releaseIfNeeded. ] ] newProcess. ReleaseProcess priority: Processor userBackgroundPriority. ReleaseProcess resume! !!SoundPlayer class methodsFor: '*ScratchOnIPad' stamp: 'mu 5/28/2014 01:13'!stopReleaseProcess ReleaseProcess == nil ifFalse: [ReleaseProcess terminate]. ReleaseProcess := nil.! !!SoundRecorder methodsFor: '*ScratchOnIPad-actions' stamp: 'mu 5/29/2014 01:31'!stopIfPlaying soundPlaying ifNotNil: [ soundPlaying nslog: '###playback sounds force release'. SoundPlayer forceRelease. ]! !!SoundRecorder methodsFor: '*ScratchOnIPad-override' stamp: 'mu 6/12/2014 23:57'!startRecording "Turn of the sound input driver and start the recording process. Initially, recording is paused." | semaIndex actualRate | recordLevel ifNil: [recordLevel _ 0.5]. "lazy initialization" CanRecordWhilePlaying ifFalse: [SoundPlayer shutDown]. recordProcess ifNotNil: [self stopRecording]. paused _ true. meteringBuffer _ SoundBuffer newMonoSampleCount: 1024. meterLevel _ 0. self allocateBuffer. bufferAvailableSema _ Semaphore new. semaIndex _ Smalltalk registerExternalObject: bufferAvailableSema. self primStartRecordingDesiredSampleRate: samplingRate asInteger stereo: stereo semaIndex: semaIndex. (actualRate := self primGetActualRecordingSampleRate) = 0 ifFalse: [ samplingRate := actualRate. ]. self primSetRecordLevel: (1000.0 * recordLevel) asInteger. recordProcess _ [self recordLoop] newProcess. recordProcess priority: Processor userInterruptPriority. recordProcess resume.! !!SoundRecorder methodsFor: '*ScratchOnIPad-override' stamp: 'mu 6/6/2014 22:13'!stopRecording "Stop the recording process and turn of the sound input driver." recordProcess ifNotNil: [recordProcess terminate]. recordProcess _ nil. self primStopRecording. Smalltalk unregisterExternalObject: bufferAvailableSema. ((currentBuffer ~~ nil) and: [nextIndex > 1]) ifTrue: [self emitPartialBuffer]. self initializeRecordingState.! !