The Inbox: Sound-tpr.45.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: Sound-tpr.45.mcz

commits-2
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!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Sound-tpr.45.mcz

timrowledge

> 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