tim Rowledge uploaded a new version of Sound to project The Inbox:
http://source.squeak.org/inbox/Sound-tpr.45.mcz ==================== Summary ==================== Name: Sound-tpr.45 Author: tpr Time: 22 November 2015, 3:08:45.33 pm UUID: 47fd8f98-1a2b-4724-95cc-6f1b34b9ff19 Ancestors: Sound-tpr.44 Attempting to start the SoundRecorder on hardwarewith no sound input (ie a RaspberryPi) fails and there is no attempt to handle it. Tacky. Remove the prim failure from primStartRecording... and raise a Warning instead from startRecording. The main recorder process is perfectly happy to run and do nothing. In the Scratchg code I can catch the exception and inform the users rather more politely than by opening a debugger. =============== Diff against Sound-topa.43 =============== Item was added: + ----- Method: Envelope>>computeSustainValueAtMSecs: (in category 'applying') ----- + computeSustainValueAtMSecs: mSecs + "Return the value of this envelope at the given number of milliseconds from its onset. Return zero for times outside the time range of this envelope." + "Note: Unlike the private method incrementalComputeValueAtMSecs:, this method does is not increment. Thus it is slower, but it doesn't depend on being called sequentially at fixed time intervals. + Note: this is the same as computeValueAtMSecs: apart from removing the first section that requires loopEndMSecs t obe nil; this appears to cause a problem when a sound in playing and is stopped whilst the #computeSlopeAtMSecs: method is run inside the SoundPlayer loop" + + | t i | + mSecs < 0 ifTrue: [^ 0.0]. + + " prevent this bit running so that we don't have to fudge loopEndMSecs to nil in #sustainEnd: + ((loopEndMSecs ~~ nil) and: [mSecs >= loopEndMSecs]) ifTrue: [ ""decay phase"" + t := (points at: loopEndIndex) x + (mSecs - loopEndMSecs). + i := self indexOfPointAfterMSecs: t startingAt: loopEndIndex. + i == nil ifTrue: [^ 0.0]. ""past end"" + ^ (self interpolate: t between: (points at: i - 1) and: (points at: i)) * decayScale]. + " + mSecs < loopStartMSecs ifTrue: [ "attack phase" + i := self indexOfPointAfterMSecs: mSecs startingAt: 1. + i = 1 ifTrue: [^ (points at: 1) y * scale]. + ^ self interpolate: mSecs between: (points at: i - 1) and: (points at: i)]. + + "sustain phase" + loopMSecs = 0 ifTrue: [^ (points at: loopEndIndex) y * scale]. "looping on a single point" + t := loopStartMSecs + ((mSecs - loopStartMSecs) \\ loopMSecs). + i := self indexOfPointAfterMSecs: t startingAt: loopStartIndex. + + ^ self interpolate: t between: (points at: i - 1) and: (points at: i) + ! Item was changed: ----- Method: Envelope>>sustainEnd: (in category 'applying') ----- sustainEnd: mSecs "Set the ending time of the sustain phase of this envelope; the decay phase will start this point. Typically derived from a note's duration." "Details: to avoid a sharp transient, the decay phase is scaled so that the beginning of the decay matches the envelope's instantaneous value when the decay phase starts." | vIfSustaining firstVOfDecay | + loopEndMSecs := mSecs. "no longer set to nil in order to pretend to be sustaining" - loopEndMSecs := nil. "pretend to be sustaining" decayScale := 1.0. nextRecomputeTime := 0. + vIfSustaining := self computeSustainValueAtMSecs: mSecs. "get value at end of sustain phase" + "loopEndMSecs := mSecs. not required any more" - vIfSustaining := self computeValueAtMSecs: mSecs. "get value at end of sustain phase" - loopEndMSecs := mSecs. firstVOfDecay := (points at: loopEndIndex) y * scale. firstVOfDecay = 0.0 ifTrue: [decayScale := 1.0] ifFalse: [decayScale := vIfSustaining / firstVOfDecay]. ! Item was changed: ----- Method: SimpleMIDIPort>>midiCmd:channel:byte: (in category 'output') ----- midiCmd: cmd channel: channel byte: dataByte "Immediately output the given MIDI command with the given channel and argument byte to this MIDI port. Assume that the port is open." accessSema critical: [ + self primMIDIWriteNoErrorPort: portNumber - self primMIDIWritePort: portNumber from: (ByteArray with: (cmd bitOr: channel) with: dataByte) at: 0]. ! Item was changed: ----- Method: SimpleMIDIPort>>midiCmd:channel:byte:byte: (in category 'output') ----- midiCmd: cmd channel: channel byte: dataByte1 byte: dataByte2 "Immediately output the given MIDI command with the given channel and argument bytes to this MIDI port. Assume that the port is open." accessSema critical: [ + self primMIDIWriteNoErrorPort: portNumber - self primMIDIWritePort: portNumber from: (ByteArray with: (cmd bitOr: channel) with: dataByte1 with: dataByte2) at: 0]. ! Item was changed: ----- Method: SoundRecorder>>primStartRecordingDesiredSampleRate:stereo:semaIndex: (in category 'primitives') ----- primStartRecordingDesiredSampleRate: samplesPerSec stereo: stereoFlag semaIndex: anInteger + "Start sound recording with the given stereo setting. Use a sampling rate as close to the desired rate as the underlying platform will support. If the given semaphore index is > 0, it is taken to be the index of a Semaphore in the external objects array to be signalled every time a recording buffer is filled. + We do *not* raise a primitiveFailed error here since this prim is called insdied a critical blcok and that often makes things painful. The only really likely case where this prim fails is a linux machine with no sound input hardware (a Raspberry Pi for example). See the startRecording method for how the failure is handled" - "Start sound recording with the given stereo setting. Use a sampling rate as close to the desired rate as the underlying platform will support. If the given semaphore index is > 0, it is taken to be the index of a Semaphore in the external objects array to be signalled every time a recording buffer is filled." <primitive: 'primitiveSoundStartRecording' module: 'SoundPlugin'> + "self primitiveFailed" - self primitiveFailed ! Item was changed: ----- Method: SoundRecorder>>startRecording (in category 'recording controls') ----- startRecording + "Turn on the sound input driver and start the recording process. Initially, recording is paused. + If the primStartRecordingDesiredSampleRate:... fails it almost certainly means we have no usable + sound input device. Rather than having the prim raise a failure error we let it quietly do nothing + (since I hate trying to debug errors inside a critical block) and check the actual sampling rate later. + If the sampling rate is 0 we know the startup failed and raise an application level Signal to let any + user code know about the problem. + You might think we should also use the stopRecording message to close things down cleanly but + that simply results in astorm of attempts to start recording so it is simpler to let it be deluded. An + attempts to start recording will repeat the test and thereby handle any plug-in hardware etc." - "Turn of the sound input driver and start the recording process. Initially, recording is paused." 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. Smalltalk newExternalSemaphoreDo: [ :semaphore :index | bufferAvailableSema := semaphore. self primStartRecordingDesiredSampleRate: samplingRate asInteger stereo: stereo semaIndex: index ]. RecorderActive := true. samplingRate := self primGetActualRecordingSampleRate. + samplingRate = 0 ifTrue: [ Warning signal: 'SoundRecorder: unable to connect to sound input device']. self primSetRecordLevel: (1000.0 * recordLevel) asInteger. recordProcess := [self recordLoop] newProcess. recordProcess priority: Processor userInterruptPriority. recordProcess resume! |
> On 22-11-2015, at 11:08 PM, [hidden email] wrote: > > tim Rowledge uploaded a new version of Sound to project The Inbox: > http://source.squeak.org/inbox/Sound-tpr.45.mcz > > ==================== Summary ==================== > > Name: Sound-tpr.45 > Author: tpr > Time: 22 November 2015, 3:08:45.33 pm > UUID: 47fd8f98-1a2b-4724-95cc-6f1b34b9ff19 > Ancestors: Sound-tpr.44 > > Attempting to start the SoundRecorder on hardwarewith no sound input (ie a RaspberryPi) fails and there is no attempt to handle it. Tacky. > > Remove the prim failure from primStartRecording... and raise a Warning instead from startRecording. The main recorder process is perfectly happy to run and do nothing. In the Scratchg code I can catch the exception and inform the users rather more politely than by opening a debugger. A couple of points about this a) dammit, the save dialogue DID NOT SHOW computeSustainValueAtMSecs, sustainEnd, midiCmd:channel:byte: , midiCmd:channel:byte:byte: as being changed. They should not have been included. b) This is in the inbox because someone may have a better idea than using a generic Warning. I don’t mind as long as I know so I can do the right thing in the Scratch code. c) I can’t test the results of this change on Windows, nor on a linux box that *does* have sound input hardware to connect to - nor a Mac that doesn’t. d) assuming it satisfies everyone we would want to patch the generic RecordingControlsMorph in a similar manner to the Scratch recorder morph. tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Oxymorons: New classic |
Free forum by Nabble | Edit this page |