'From OLPC2.0 of ''24 October 2006'' [latest update: #1229] on 10 March 2007 at 10:44:08 pm'! "Change Set: Morphic-OggCodec Date: 10 March 2007 Author: Takashi Yamamiya SoundRecorder and etoys (.pr file) support for Ogg vorbis / speex codec module. "! !RecordingControlsMorph methodsFor: 'button commands' stamp: 'tak 3/10/2007 18:16'! changeCodec: aClass name: aString | button newLabel | (aClass notNil and: [aClass isAvailable]) ifTrue: [recorder codec: aClass new. newLabel := aString] ifFalse: [newLabel := 'None']. self submorphs do: [:raw | raw submorphs do: [:each | ((each isKindOf: SimpleButtonMorph) and: [each actionSelector = #chooseCodec]) ifTrue: [button := each]]]. button labelString: newLabel! ! !RecordingControlsMorph methodsFor: 'button commands' stamp: 'tak 3/10/2007 18:11'! chooseCodec | menu | menu := MenuMorph new defaultTarget: self. OggDriver isAvailable ifTrue: [menu add: 'Speex' target: self selector: #changeCodec:name: argumentList: {OggSpeexCodec. 'Speex'}. menu add: 'Vorbis' target: self selector: #changeCodec:name: argumentList: {OggVorbisCodec. 'Vorbis'}]. menu add: 'GSM' target: self selector: #changeCodec:name: argumentList: {GSMCodec. 'GSM'}. menu add: 'None' target: self selector: #changeCodec:name: argumentList: {nil. 'None'}. menu popUpInWorld! ! !RecordingControlsMorph methodsFor: 'button commands' stamp: 'tak 3/6/2007 11:48'! makeTile "Make a tile representing my sound. Get a sound-name from the user by which the sound is to be known." | newStyleTile sndName tile | recorder verifyExistenceOfRecordedSound ifFalse: [^ self]. recorder pause. newStyleTile _ true. newStyleTile ifTrue: [sndName _ FillInTheBlank request: 'Please name your new sound' translated initialAnswer: 'sound' translated. sndName isEmpty ifTrue: [^ self]. sndName _ SampledSound unusedSoundNameLike: sndName. recorder codecSignature ifNil: [SampledSound addLibrarySoundNamed: sndName samples: recorder condensedSamples samplingRate: recorder samplingRate] ifNotNil: [SampledSound addLibrarySoundNamed: sndName bytes: recorder condensedChannels codecSignature: recorder codecSignature]. tile _ SoundTile new literal: sndName] ifFalse: [tile _ InterimSoundMorph new sound: (SampledSound samples: recorder condensedSamples samplingRate: recorder samplingRate)]. tile bounds: tile fullBounds. tile openInHand! ! !RecordingControlsMorph methodsFor: 'initialization' stamp: 'tak 3/6/2007 15:03'! addButtonRows | r fullWidth | r _ AlignmentMorph newRow vResizing: #shrinkWrap. r addMorphBack: (self buttonName: 'Morph' translated action: #makeSoundMorph). r addMorphBack: (Morph new extent: 4@1; color: Color transparent). r addMorphBack: (self buttonName: 'Tile' translated action: #makeTile). r addMorphBack: (Morph new extent: 4@1; color: Color transparent). r addMorphBack: (self buttonName: 'Trim' translated action: #trim). r addMorphBack: (Morph new extent: 4@1; color: Color transparent). r addMorphBack: (self buttonName: 'Show' translated action: #show). self addMorphBack: r. r layoutChanged. fullWidth := r fullBounds width. r _ AlignmentMorph newRow vResizing: #shrinkWrap. r addMorphBack: (self buttonName: 'Record' translated action: #record). r addMorphBack: (Morph new extent: 4@1; color: Color transparent). r addMorphBack: (self buttonName: 'Stop' translated action: #stop). r addMorphBack: (Morph new extent: 4@1; color: Color transparent). r addMorphBack: (self buttonName: 'Play' translated action: #playback). r addMorphBack: (Morph new extent: 4@1; color: Color transparent). r addMorphBack: (self buttonName: 'Codec' translated action: #chooseCodec). r addMorphBack: self makeStatusLight. self addMorphBack: r. self changeCodec: OggSpeexCodec name: 'Speex'. r layoutChanged. fullWidth := fullWidth max: r fullBounds width. ^ fullWidth@(r fullBounds height). ! ! !RecordingControlsMorph methodsFor: 'initialization' stamp: 'yo 1/4/2007 10:21'! initialize | r full | super initialize. self hResizing: #shrinkWrap; vResizing: #shrinkWrap. borderWidth _ 2. self listDirection: #topToBottom. recorder _ SoundRecorder new. full := self addButtonRows. self addRecordLevelSliderIn: full. r _ AlignmentMorph newRow vResizing: #shrinkWrap. r addMorphBack: (self makeRecordMeterIn: full). self addMorphBack: r. self extent: 10@10. "make minimum size" ! ! !SampledSound methodsFor: 'accessing' stamp: 'tak 12/20/2006 17:22'! compressWith: codecClass | codec result | codec := codecClass new. result := codec compressSound: self. codec release. ^ result! ! !SampledSound methodsFor: 'accessing' stamp: 'tak 12/20/2006 17:21'! compressWith: codecClass atRate: aSamplingRate | codec result | codec := codecClass new. result := codec compressSound: self atRate: aSamplingRate. codec release. ^ result! ! !SampledSound class methodsFor: 'sound library' stamp: 'tak 3/6/2007 10:57'! addLibrarySoundNamed: aString bytes: aByteArray codecSignature: signature SoundLibrary at: aString put: (Array with: aByteArray with: signature). ! ! !SampledSound class methodsFor: 'sound library' stamp: 'tak 3/6/2007 11:54'! soundNamed: aString ifAbsent: aBlock "Answer the sound of the given name, or if there is no sound of that name, answer the result of evaluating aBlock" "(SampledSound soundNamed: 'shutterClick') play" | entry samples compressedSound | entry _ SoundLibrary at: aString ifAbsent: [^ aBlock value]. entry ifNil: [^ aBlock value]. entry second isString ifTrue: [compressedSound := Compiler evaluate: entry second. compressedSound source: entry first. ^ compressedSound asSound] ifFalse: [samples _ entry at: 1. samples class isBytes ifTrue: [samples _ self convert8bitSignedTo16Bit: samples]. ^ self samples: samples samplingRate: (entry at: 2)] ! ! !SoundRecorder methodsFor: 'accessing' stamp: 'tak 3/6/2007 11:14'! codecSignature codec ifNil: [^ nil]. ^ recordedSound sounds first codecSignature! ! !SoundRecorder methodsFor: 'recording controls' stamp: 'tak 3/7/2007 20:52'! startRecording "Turn of the sound input driver and start the recording process. Initially, recording is paused." | semaIndex | recordLevel ifNil: [recordLevel _ 0.5]. "lazy initialization" Preferences canRecordWhilePlaying ifFalse: [SoundPlayer shutDown]. recordProcess ifNotNil: [self stopRecording]. codec ifNotNil: [codec reset]. 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. RecorderActive _ true. samplingRate _ self primGetActualRecordingSampleRate. self primSetRecordLevel: (1000.0 * recordLevel) asInteger. recordProcess _ [self recordLoop] newProcess. recordProcess priority: Processor userInterruptPriority. recordProcess resume. ! ! !SoundRecorder methodsFor: 'recording controls' stamp: 'tak 3/5/2007 11:49'! stopRecording "Stop the recording process and turn of the sound input driver." recordProcess ifNotNil: [recordProcess terminate]. recordProcess _ nil. self primStopRecording. RecorderActive _ false. Smalltalk unregisterExternalObject: bufferAvailableSema. ((currentBuffer ~~ nil) and: [nextIndex > 1]) ifTrue: [self emitPartialBuffer]. codec ifNotNil: [codec reset]. self initializeRecordingState. ! ! !SoundRecorder methodsFor: 'results' stamp: 'tak 3/6/2007 11:47'! condensedChannels | writer | writer := ByteArray new writeStream. recordedSound sounds do: [:sound | sound channels do: [:channel | writer nextPutAll: channel]]. ^ writer contents! !