<div dir="ltr"><div>Cool. I've seen people asking about higher bit depth in Sound plugin. Maybe that will be easier to add now ?</div><div><br></div><div>Best,</div><div>Karl</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, Aug 1, 2020 at 4:45 AM <<a href="mailto:commits@source.squeak.org">commits@source.squeak.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> <br>
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:<br>
<a href="http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2785.mcz" rel="noreferrer" target="_blank">http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2785.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: VMMaker.oscog-eem.2785<br>
Author: eem<br>
Time: 31 July 2020, 7:45:15.985473 pm<br>
UUID: 9fa77ef1-7255-47f3-9dd1-c500f9529cbb<br>
Ancestors: VMMaker.oscog-eem.2784<br>
<br>
Plugins: Clean up the SoundPlugin, eloiminating almost all cCode:'s, making it potentially simulateable once the internal API is implemented.  Use the methodRetur...: API to simplify a number of primitives.  Change primitiveSoundEnableAEC to take either 0, 1 or a boolean.<br>
<br>
Slang: eliminate the arguments to addressOf:put: blocks via nodeIsDeadCode:withParent:<br>
<br>
Simulation: implement unsigned coercion in cCoerce:to: to support this form in primitiveSoundEnableAEC<br>
        (interpreterProxy isIntegerObject: (arg := interpreterProxy stackValue: 0))<br>
                ifTrue:<br>
                        [arg := interpreterProxy integerValueOf: arg.<br>
                         (interpreterProxy cCoerce: arg to: #unsigned) > 1 ifTrue:<br>
                                [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
                         trueOrFalse := arg = 1]<br>
                ifFalse:<br>
                        [(interpreterProxy isBooleanObject: arg) ifFalse:<br>
                                [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
                        trueOrFalse := interpreterProxy booleanValueOf: arg].<br>
<br>
=============== Diff against VMMaker.oscog-eem.2784 ===============<br>
<br>
Item was changed:<br>
  ----- Method: CCodeGenerator>>nodeIsDeadCode:withParent: (in category 'utilities') -----<br>
  nodeIsDeadCode: aNode withParent: parentNode<br>
        "Answer if aNode would not be generated due to dead code elimination."<br>
+       (aNode isLiteralBlock and: [parentNode isSend and: [parentNode selector == #addressOf:put:]]) ifTrue:<br>
+               [^true].<br>
        ^(self nilOrBooleanConditionFor: parentNode)<br>
                ifNil: [false]<br>
                ifNotNil:<br>
                        [:cond| | filter |<br>
                        filter := parentNode selector caseOf:<br>
                                                        {   "First element is accessor for filtered (eliminated) node if expression is true.<br>
                                                                Second element is accessor for filtered (eliminated) node if expression is false."<br>
                                                                [#ifFalse:]                             -> [#(first nil)].<br>
                                                                [#ifFalse:ifTrue:]              -> [#(first last)].<br>
                                                                [#ifTrue:]                              -> [#(nil first)].<br>
                                                                [#ifTrue:ifFalse:]                      -> [#(last first)].<br>
                                                                [#and:]                                 -> [#(nil first)].<br>
                                                                [#or:]                                  -> [#(last nil)].<br>
                                                                [#cppIf:ifTrue:]                        -> [#(nil #second)].<br>
                                                                [#cppIf:ifTrue:ifFalse:]        -> [#(third #second)] }.<br>
                        (cond ifTrue: [filter first] ifFalse: [filter last])<br>
                                ifNil: [false]<br>
                                ifNotNil: [:accessor| aNode == (parentNode args perform: accessor)]]!<br>
<br>
Item was changed:<br>
  ----- Method: Integer>>coerceTo:sim: (in category '*VMMaker-interpreter simulator') -----<br>
  coerceTo: cTypeString sim: interpreter<br>
<br>
        | unitSize |<br>
+       cTypeString last == $* ifTrue:  "C pointer"<br>
-       cTypeString last = $* ifTrue:  "C pointer"<br>
                [unitSize := cTypeString caseOf: {<br>
                [#'char *'] -> [1].<br>
                [#'short *'] -> [2].<br>
                [#'int *'] -> [4].<br>
                [#'long long *'] -> [8].<br>
                [#'float *'] -> [^CFloatArray basicNew interpreter: interpreter address: self unitSize: 4; yourself].<br>
                [#'double *'] -> [^CFloatArray basicNew interpreter: interpreter address: self unitSize: 8; yourself].<br>
                [#'unsigned *'] -> [4].<br>
                [#'unsigned int *'] -> [4].<br>
                [#'unsigned char *'] -> [1].<br>
                [#'signed char *'] -> [1].<br>
                [#'unsigned short *'] -> [2].<br>
                [#'unsigned long long *'] -> [8].<br>
                [#'oop *'] -> [interpreter objectMemory bytesPerOop].<br>
                }<br>
                otherwise: [interpreter objectMemory wordSize].<br>
                ^CArray basicNew<br>
                        interpreter: interpreter address: self unitSize: unitSize;<br>
                        yourself].<br>
+       cTypeString first == $u ifTrue:<br>
+               [unitSize := cTypeString caseOf: {<br>
+               [#usqInt] -> [interpreter objectMemory wordSize].<br>
+               [#usqLong] -> [8].<br>
+               [#unsigned] -> [4].<br>
+               [#'unsigned int'] -> [4].<br>
+               [#'unsigned char'] -> [1].<br>
+               [#'unsigned long'] -> [6].<br>
+               [#'unsigned short'] -> [2].<br>
+               [#'unsigned long long'] -> [8].<br>
+               }<br>
+               otherwise: [self error: 'unknown unsigned type name'].<br>
+               ^self bitAnd: 1 << (8 * unitSize) - 1].<br>
+       ^self  "C number (int, char, etc)"!<br>
-       ^self  "C number (int, char, float, etc)"!<br>
<br>
Item was added:<br>
+ ----- Method: InterpreterProxy>>cCoerce:to: (in category 'simulation only') -----<br>
+ cCoerce: value to: cTypeString<br>
+       "Type coercion. For translation a cast will be emitted. When running in Smalltalk<br>
+         answer a suitable wrapper for correct indexing."<br>
+       ^value<br>
+               ifNil: [value]<br>
+               ifNotNil: [value coerceTo: cTypeString sim: self]!<br>
<br>
Item was changed:<br>
  ----- Method: RePlugin>>rcvrMatchSpacePtr (in category 'rcvr linkage') -----<br>
  rcvrMatchSpacePtr<br>
<br>
        <inline: true><br>
        <returnTypeC: 'int *'><br>
        ^self<br>
                cCoerce: (interpreterProxy fetchArray: 7 ofObject: rcvr)<br>
+               to: #'int *'!<br>
-               to: 'int *'.!<br>
<br>
Item was changed:<br>
  ----- Method: RePlugin>>rcvrPatternStrPtr (in category 'rcvr linkage') -----<br>
  rcvrPatternStrPtr<br>
<br>
        <inline: true><br>
        <returnTypeC: 'char *'><br>
        ^self <br>
                cCoerce: (interpreterProxy fetchArray: 0 ofObject: rcvr) <br>
+               to: #'char *'.!<br>
-               to: 'char *'.!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveGetDefaultSoundPlayer (in category 'primitives') -----<br>
  primitiveGetDefaultSoundPlayer<br>
        "Answer a String with the operating system name of the default output device, or nil"<br>
        "no arguments"<br>
-       | cDeviceName sz newString newStringPtr |<br>
        <export: true><br>
+       | cDeviceName |<br>
+       <var: #cDeviceName type: #'char*'><br>
-       <var: #cDeviceName type: 'char*'><br>
-       <var: #newStringPtr type: 'char*'><br>
<br>
-       "Parse arguments"<br>
-       interpreterProxy methodArgumentCount = 0 <br>
-               ifFalse:[^interpreterProxy primitiveFail].<br>
-       <br>
        "Get the answer."<br>
+       cDeviceName := self getDefaultSoundPlayer.<br>
+       cDeviceName = 0 ifTrue:<br>
+               [^interpreterProxy methodReturnValue: interpreterProxy nilObject].<br>
-       cDeviceName := self cCode: 'getDefaultSoundPlayer()'.<br>
-       cDeviceName == 0 ifTrue: [<br>
-               ^interpreterProxy pop: 1 thenPush: interpreterProxy nilObject<br>
-               ].<br>
<br>
+       ^interpreterProxy methodReturnString: cDeviceName!<br>
-       "Copy the answer to a Squeak String."<br>
-       sz :=  self cCode: 'strlen(cDeviceName)'.<br>
-       newString := interpreterProxy <br>
-                                                               instantiateClass: interpreterProxy classString<br>
-                                                               indexableSize: sz.<br>
-       newStringPtr := interpreterProxy firstIndexableField: newString.<br>
-       self cCode: 'strncpy(newStringPtr, cDeviceName, sz)'.<br>
- <br>
-       self touch: newStringPtr.<br>
-       self touch: cDeviceName.<br>
-       "Pop the receiver, and answer the new string."<br>
-       ^interpreterProxy pop: 1 thenPush: newString!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveGetDefaultSoundRecorder (in category 'primitives') -----<br>
  primitiveGetDefaultSoundRecorder<br>
        "Answer a String with the operating system name of the default input device, or nil"<br>
        "no arguments"<br>
-       | cDeviceName sz newString newStringPtr |<br>
        <export: true><br>
+       | cDeviceName |<br>
+       <var: #cDeviceName type: #'char*'><br>
-       <var: #cDeviceName type: 'char*'><br>
-       <var: #newStringPtr type: 'char*'><br>
<br>
-       "Parse arguments"<br>
-       interpreterProxy methodArgumentCount = 0 <br>
-               ifFalse:[^interpreterProxy primitiveFail].<br>
- <br>
        "Get the answer."<br>
+       cDeviceName := self getDefaultSoundRecorder.<br>
+       cDeviceName = 0 ifTrue:<br>
+               [^interpreterProxy methodReturnValue: interpreterProxy nilObject].<br>
-       cDeviceName := self cCode: 'getDefaultSoundRecorder()'.<br>
-       cDeviceName == 0 ifTrue: [<br>
-               ^interpreterProxy pop: 1 thenPush: interpreterProxy nilObject<br>
-               ].<br>
<br>
+       ^interpreterProxy methodReturnString: cDeviceName!<br>
-       "Copy the answer to a Squeak String."<br>
-       sz :=  self cCode: 'strlen(cDeviceName)'.<br>
-       newString := interpreterProxy <br>
-                                                               instantiateClass: interpreterProxy classString<br>
-                                                               indexableSize: sz.<br>
-       newStringPtr := interpreterProxy firstIndexableField: newString.<br>
-       self cCode: 'strncpy(newStringPtr, cDeviceName, sz)'.<br>
- <br>
-       self touch: newStringPtr.<br>
-       self touch: cDeviceName.<br>
-       "Pop the receiver, and answer the new string."<br>
-       ^interpreterProxy pop: 1 thenPush: newString!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveGetNumberOfSoundPlayerDevices (in category 'primitives') -----<br>
  primitiveGetNumberOfSoundPlayerDevices<br>
-       "arguments: name(type, stack offset)<br>
-               dialString(String, 0)"<br>
-       "answers an Integer"<br>
-       | result |<br>
        <export: true><br>
<br>
+       ^interpreterProxy methodReturnInteger: self getNumberOfSoundPlayerDevices!<br>
-       "Parse arguments"<br>
-       interpreterProxy methodArgumentCount = 0 <br>
-               ifFalse:[^interpreterProxy primitiveFail].<br>
-       <br>
-       "get result"<br>
-       result := self cCode: 'getNumberOfSoundPlayerDevices()'.<br>
- <br>
-       "answer it"<br>
-       result := interpreterProxy signed32BitIntegerFor: result.<br>
-       ^interpreterProxy pop: 1 thenPush: result. "pop receiver, return result"!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveGetNumberOfSoundRecorderDevices (in category 'primitives') -----<br>
  primitiveGetNumberOfSoundRecorderDevices<br>
-       "arguments: name(type, stack offset)<br>
-               dialString(String, 0)"<br>
-       "answers an Integer"<br>
-       | result |<br>
        <export: true><br>
<br>
+       ^interpreterProxy methodReturnInteger: self getNumberOfSoundRecorderDevices!<br>
-       "Parse arguments"<br>
-       interpreterProxy methodArgumentCount = 0 <br>
-               ifFalse:[^interpreterProxy primitiveFail].<br>
-       <br>
-       "get result"<br>
-       result := self cCode: 'getNumberOfSoundRecorderDevices()'.<br>
- <br>
-       "answer it"<br>
-       result := interpreterProxy signed32BitIntegerFor: result.<br>
-       ^interpreterProxy pop: 1 thenPush: result. "pop receiver, return result"!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveGetSoundPlayerDeviceName (in category 'primitives') -----<br>
  primitiveGetSoundPlayerDeviceName<br>
        "arguments: name(type, stack offset)<br>
                deviceNumber(Integer, 0)"<br>
        "answers a string or nil"<br>
-       | deviceNumber sz cDeviceName newString newStringPtr |<br>
        <export: true><br>
+       | deviceNumber cDeviceName |<br>
+       <var: #cDeviceName type: #'char *'><br>
-       <var: #cDeviceName type: 'char*'><br>
-       <var: #newStringPtr type: 'char*'><br>
<br>
        "Parse arguments" <br>
+       interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadNumArgs].<br>
-       interpreterProxy methodArgumentCount = 1 <br>
-               ifFalse:[^interpreterProxy primitiveFail].<br>
<br>
        deviceNumber := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 0).<br>
+       interpreterProxy failed ifTrue:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
-       interpreterProxy failed ifTrue: [^nil].<br>
<br>
        "Get the answer."<br>
+       cDeviceName := self getSoundPlayerDeviceName: deviceNumber - 1.<br>
+       cDeviceName = 0 ifTrue:<br>
+               [^interpreterProxy methodReturnValue: interpreterProxy nilObject].<br>
-       cDeviceName := self cCode: 'getSoundPlayerDeviceName(deviceNumber - 1)'.<br>
-       cDeviceName == 0 ifTrue: [<br>
-               ^interpreterProxy pop: 2 thenPush: interpreterProxy nilObject<br>
-               ].<br>
<br>
+       ^interpreterProxy methodReturnString: cDeviceName!<br>
-       "Copy the answer to a Squeak String."<br>
-       sz :=  self cCode: 'strlen(cDeviceName)'.<br>
-       newString := interpreterProxy <br>
-                                                               instantiateClass: interpreterProxy classString<br>
-                                                               indexableSize: sz.<br>
-       newStringPtr := interpreterProxy firstIndexableField: newString.<br>
-       self cCode: 'strncpy(newStringPtr, cDeviceName, sz)'.<br>
- <br>
-       self touch: deviceNumber.<br>
-       self touch: newStringPtr.<br>
-       self touch: cDeviceName.<br>
-       "Pop the receiver and arg, and answer the new string."<br>
-       ^interpreterProxy pop: 2 thenPush: newString!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveGetSoundRecorderDeviceName (in category 'primitives') -----<br>
  primitiveGetSoundRecorderDeviceName<br>
        "arguments: name(type, stack offset)<br>
                deviceNumber(Integer, 0)"<br>
        "answers a string or nil"<br>
-       | deviceNumber sz cDeviceName newString newStringPtr |<br>
        <export: true><br>
+       | deviceNumber cDeviceName |<br>
+       <var: #cDeviceName type: #'char *'><br>
-       <var: #cDeviceName type: 'char*'><br>
-       <var: #newStringPtr type: 'char*'><br>
<br>
        "Parse arguments" <br>
+       interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadNumArgs].<br>
-       interpreterProxy methodArgumentCount = 1 <br>
-               ifFalse:[^interpreterProxy primitiveFail].<br>
<br>
- <br>
        deviceNumber := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 0).<br>
+       interpreterProxy failed ifTrue:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
-       interpreterProxy failed ifTrue: [^nil].<br>
<br>
        "Get the answer."<br>
+       cDeviceName := self getSoundRecorderDeviceName: deviceNumber - 1.<br>
+       cDeviceName = 0 ifTrue:<br>
+               [^interpreterProxy methodReturnValue: interpreterProxy nilObject].<br>
-       cDeviceName := self cCode: 'getSoundRecorderDeviceName(deviceNumber - 1)'.<br>
-       cDeviceName == 0 ifTrue: [<br>
-               ^interpreterProxy pop: 2 thenPush: interpreterProxy nilObject<br>
-               ].<br>
<br>
+       ^interpreterProxy methodReturnString: cDeviceName!<br>
-       "Copy the answer to a Squeak String."<br>
-       sz :=  self cCode: 'strlen(cDeviceName)'.<br>
-       newString := interpreterProxy <br>
-                                                               instantiateClass: interpreterProxy classString<br>
-                                                               indexableSize: sz.<br>
-       newStringPtr := interpreterProxy firstIndexableField: newString.<br>
-       self cCode: 'strncpy(newStringPtr, cDeviceName, sz)'.<br>
- <br>
-       self touch: deviceNumber.<br>
-       self touch: newStringPtr.<br>
-       self touch: cDeviceName.<br>
-       "Pop the receiver and arg, and answer the new string."<br>
-       ^interpreterProxy pop: 2 thenPush: newString!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSetDefaultSoundPlayer (in category 'primitives') -----<br>
  primitiveSetDefaultSoundPlayer<br>
        "Tell the operating system to use the specified device name as the output device for sound."<br>
        "arg at top of stack is the String"<br>
-       | deviceName obj srcPtr sz |<br>
        <export: true><br>
+       | deviceName obj srcPtr sz |<br>
        <var: 'deviceName' declareC: 'char deviceName[257]'><br>
        <var: 'srcPtr' type: #'char *'><br>
+       self cCode: [] inSmalltalk: [deviceName := ByteString new: 257].<br>
- <br>
        "Parse arguments"<br>
        interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadNumArgs].<br>
+       ((interpreterProxy isBytes: (obj := interpreterProxy stackValue: 0))<br>
+       and: [(sz := interpreterProxy byteSizeOf: obj) <= 256]) ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
+ <br>
+       srcPtr := self cCoerce: (interpreterProxy firstIndexableField: obj) to: #'char *'.<br>
+       self strncpy: deviceName _: srcPtr _: sz.<br>
+       deviceName at: sz put: 0.<br>
+       self setDefaultSoundPlayer: deviceName.<br>
+ <br>
+       interpreterProxy failed ifFalse:<br>
+               [interpreterProxy methodReturnReceiver]!<br>
-               [^interpreterProxy primitiveFail].<br>
-       obj := interpreterProxy stackValue: 0.<br>
-       (interpreterProxy isBytes: obj) ifFalse:<br>
-               [^interpreterProxy primitiveFail].<br>
-       (sz := interpreterProxy byteSizeOf: obj) <= 256 ifFalse:<br>
-               [^interpreterProxy primitiveFail].<br>
-       srcPtr := interpreterProxy firstIndexableField: obj.<br>
-       self touch: srcPtr.<br>
-       self touch: deviceName.<br>
-       self touch: sz.<br>
-       self cCode: 'strncpy(deviceName, srcPtr, sz)'.<br>
-       self cCode: 'deviceName[sz] = 0'.<br>
-       <br>
-       "do the work"<br>
-       self cCode: 'setDefaultSoundPlayer(deviceName)'.<br>
-       interpreterProxy failed ifFalse: "pop arg, leave receiver"<br>
-               [interpreterProxy pop: 1]!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSetDefaultSoundRecorder (in category 'primitives') -----<br>
  primitiveSetDefaultSoundRecorder<br>
        "Tell the operating system to use the specified device name as the input device for sound."<br>
        "arg at top of stack is the String"<br>
-       | deviceName obj srcPtr sz |<br>
        <export: true><br>
+       | deviceName obj srcPtr sz |<br>
        <var: 'deviceName' declareC: 'char deviceName[257]'><br>
        <var: 'srcPtr' type: #'char *'><br>
+       self cCode: [] inSmalltalk: [deviceName := ByteString new: 257].<br>
- <br>
        "Parse arguments"<br>
        interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadNumArgs].<br>
+       ((interpreterProxy isBytes: (obj := interpreterProxy stackValue: 0))<br>
+       and: [(sz := interpreterProxy byteSizeOf: obj) <= 256]) ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
+ <br>
+       srcPtr := self cCoerce: (interpreterProxy firstIndexableField: obj) to: #'char *'.<br>
+       self strncpy: deviceName _: srcPtr _: sz.<br>
+       deviceName at: sz put: 0.<br>
+       self setDefaultSoundRecorder: deviceName.<br>
+ <br>
+       interpreterProxy failed ifFalse:<br>
+               [interpreterProxy methodReturnReceiver]!<br>
-               [^interpreterProxy primitiveFail].<br>
-       obj := interpreterProxy stackValue: 0.<br>
-       (interpreterProxy isBytes: obj) ifFalse:<br>
-               [^interpreterProxy primitiveFail].<br>
-       (sz := interpreterProxy byteSizeOf: obj) <= 256 ifFalse:<br>
-               [^interpreterProxy primitiveFail].<br>
-       srcPtr := interpreterProxy firstIndexableField: obj.<br>
-       self touch: srcPtr.<br>
-       self touch: deviceName.<br>
-       self touch: sz.<br>
-       self cCode: 'strncpy(deviceName, srcPtr, sz)'.<br>
-       self cCode: 'deviceName[sz] = 0'.<br>
-       <br>
-       "do the work"<br>
-       self cCode: 'setDefaultSoundRecorder(deviceName)'.<br>
-       interpreterProxy failed ifFalse: "pop arg, leave receiver"<br>
-               [interpreterProxy pop: 1]!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundAvailableSpace (in category 'primitives') -----<br>
  primitiveSoundAvailableSpace<br>
+       "Returns the number of bytes of available sound output buffer space.<br>
+        This should be (frames*4) if the device is in stereo mode, or (frames*2) otherwise"<br>
-       "Returns the number of bytes of available sound output buffer space.  This should be (frames*4) if the device is in stereo mode, or (frames*2) otherwise"<br>
<br>
+       <export: true><br>
        | frames |<br>
+       frames := self snd_AvailableSpace.  "-1 if sound output not started"<br>
+       frames >= 0<br>
+               ifTrue: [interpreterProxy methodReturnInteger: frames]<br>
+               ifFalse: [interpreterProxy primitiveFail]!<br>
-       self primitive: 'primitiveSoundAvailableSpace'.<br>
-       frames := self cCode: 'snd_AvailableSpace()'.  "-1 if sound output not started"<br>
-       interpreterProxy success: frames >= 0.<br>
-       ^frames asPositiveIntegerObj!<br>
<br>
Item was added:<br>
+ ----- Method: SoundPlugin>>primitiveSoundEnableAEC (in category 'primitives') -----<br>
+ primitiveSoundEnableAEC<br>
+       "Enable or disable acoustic echo-cancellation (AEC).<br>
+        Arg is a boolean or 1 for true and 0 for false."<br>
+       <export: true><br>
+       | arg trueOrFalse errorCode |<br>
+       interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadNumArgs].<br>
+       "Parse arguments"<br>
+       (interpreterProxy isIntegerObject: (arg := interpreterProxy stackValue: 0))<br>
+               ifTrue:<br>
+                       [arg := interpreterProxy integerValueOf: arg.<br>
+                        (interpreterProxy cCoerce: arg to: #unsigned) > 1 ifTrue:<br>
+                               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
+                        trueOrFalse := arg = 1]<br>
+               ifFalse:<br>
+                       [(interpreterProxy isBooleanObject: arg) ifFalse:<br>
+                               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
+                       trueOrFalse := interpreterProxy booleanValueOf: arg].<br>
+       "Set AEC"<br>
+       (errorCode := self snd_EnableAEC: trueOrFalse) ~= 0 ifTrue:<br>
+               [interpreterProxy primitiveFailFor: (errorCode < 0 ifTrue: [PrimErrGenericFailure] ifFalse: [errorCode])]!<br>
<br>
Item was removed:<br>
- ----- Method: SoundPlugin>>primitiveSoundEnableAEC: (in category 'primitives') -----<br>
- primitiveSoundEnableAEC: trueOrFalse <br>
-       "Enable or disable acoustic echo-cancellation (AEC).  trueOrFalse should be 0 for false, and 1 for true."<br>
-       | result |<br>
-       self primitive: 'primitiveSoundEnableAEC' parameters: #(SmallInteger ).<br>
-       interpreterProxy failed ifFalse: [<br>
-               result := self cCode: 'snd_EnableAEC(trueOrFalse)'.<br>
-               result == 0 ifFalse: [interpreterProxy primitiveFailFor: result].<br>
-       ].!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundGetRecordLevel (in category 'primitives') -----<br>
  primitiveSoundGetRecordLevel<br>
+       "Get the default input device's volume level in the range 0-1000."<br>
+       <export: true><br>
-       "Get the sound input recording level  in the range 0-1000."<br>
        | level |<br>
        <var: 'level' type: #int><br>
+       level := self snd_GetRecordLevel.<br>
+       ^level >= 0<br>
+               ifTrue: [interpreterProxy methodReturnInteger: level]<br>
+               ifFalse: [interpreterProxy primitiveFail]!<br>
-       self primitive: 'primitiveSoundGetRecordLevel'.<br>
-       level := self cCode: 'snd_GetRecordLevel()'.<br>
-       ^level asPositiveIntegerObj<br>
- !<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundGetRecordingSampleRate (in category 'primitives') -----<br>
  primitiveSoundGetRecordingSampleRate<br>
        "Return a float representing the actual sampling rate during recording. Fail if not currently recording."<br>
<br>
+       <export: true><br>
        | rate |<br>
+       <var: #rate type: #double><br>
+       rate := self snd_GetRecordingSampleRate.  "fails if not recording"<br>
+       interpreterProxy failed ifFalse:<br>
+               [^interpreterProxy methodReturnFloat: rate]!<br>
-       <var: #rate type: 'double '><br>
-       self primitive: 'primitiveSoundGetRecordingSampleRate'.<br>
-       rate := self cCode: 'snd_GetRecordingSampleRate()'.  "fail if not recording"<br>
-       ^rate asFloatObj!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundGetVolume (in category 'primitives') -----<br>
  primitiveSoundGetVolume<br>
+       "Get the default output device's volume level as a left/right pair of floats in the range 0-1."<br>
+       <export: true><br>
+       | left right leftOop rightOop results |<br>
-       "Get the sound input recording level."<br>
-       | left right results |<br>
        <var: #left type: #double><br>
        <var: #right type: #double><br>
+       left := 0.0.<br>
+       right := 0.0.<br>
+       self snd_Volume: (self addressOf: left put: [:v| left := v]) _: (self addressOf: right put: [:v| right := v]).<br>
+       interpreterProxy failed ifTrue:<br>
+               [^self].<br>
+       results := interpreterProxy instantiateClass: interpreterProxy classArray indexableSize: 2.<br>
+       results ifNil:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrNoMemory].<br>
+       self remapOop: results in:<br>
+               [leftOop := interpreterProxy floatObjectOf: left.<br>
+                self remapOop: leftOop in:<br>
+                       [rightOop := interpreterProxy floatObjectOf: right]].<br>
+       interpreterProxy<br>
+               storePointer: 0 ofObject: results withValue: leftOop;<br>
+               storePointer: 1 ofObject: results withValue: rightOop;<br>
+               methodReturnValue: results!<br>
-       self primitive: 'primitiveSoundGetVolume'<br>
-               parameters: #( ).<br>
-       left := 0.<br>
-       right := 0.<br>
-       self cCode: 'snd_Volume((double *) &left,(double *) &right)'.<br>
-       interpreterProxy pushRemappableOop: (right asOop: Float).<br>
-       interpreterProxy pushRemappableOop: (left asOop: Float).<br>
-       results := interpreterProxy instantiateClass: (interpreterProxy classArray) indexableSize: 2.<br>
-       interpreterProxy storePointer: 0 ofObject: results withValue: interpreterProxy popRemappableOop.<br>
-       interpreterProxy storePointer: 1 ofObject: results withValue: interpreterProxy popRemappableOop.<br>
-       ^results!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundInsertSamples:from:leadTime: (in category 'primitives') -----<br>
  primitiveSoundInsertSamples: frameCount from: buf leadTime: leadTime <br>
        "Insert a buffer's worth of sound samples into the currently playing  <br>
        buffer. Used to make a sound start playing as quickly as possible. The  <br>
        new sound is mixed with the previously buffered sampled."<br>
        "Details: Unlike primitiveSoundPlaySamples, this primitive always starts  <br>
        with the first sample the given sample buffer. Its third argument  <br>
        specifies the number of samples past the estimated sound output buffer  <br>
        position the inserted sound should start. If successful, it returns the  <br>
        number of samples inserted."<br>
        | framesPlayed |<br>
        self primitive: 'primitiveSoundInsertSamples'<br>
+               parameters: #(SmallInteger WordArray SmallInteger).<br>
+       frameCount <= (interpreterProxy slotSizeOf: buf cPtrAsOop) ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
-               parameters: #(SmallInteger WordArray SmallInteger ).<br>
-       interpreterProxy success: frameCount <= (interpreterProxy slotSizeOf: buf cPtrAsOop).<br>
<br>
+       framesPlayed := self snd_InsertSamplesFromLeadTime: frameCount _: buf _: leadTime.<br>
+       framesPlayed >= 0<br>
+               ifTrue: [interpreterProxy methodReturnInteger: framesPlayed]<br>
+               ifFalse: [interpreterProxy primitiveFail]!<br>
-       interpreterProxy failed<br>
-               ifFalse: [framesPlayed := self cCode: 'snd_InsertSamplesFromLeadTime(frameCount, (void *)buf, leadTime)'.<br>
-                       interpreterProxy success: framesPlayed >= 0].<br>
-       ^ framesPlayed asPositiveIntegerObj!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundPlaySamples:from:startingAt: (in category 'primitives') -----<br>
  primitiveSoundPlaySamples: frameCount from: buf startingAt: startIndex <br>
        "Output a buffer's worth of sound samples."<br>
        | framesPlayed |<br>
        self primitive: 'primitiveSoundPlaySamples'<br>
+               parameters: #(SmallInteger WordArray SmallInteger).<br>
+       (startIndex >= 1 and: [startIndex + frameCount - 1 <= (interpreterProxy slotSizeOf: buf cPtrAsOop)]) ifTrue:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadArgument].<br>
-               parameters: #(SmallInteger WordArray SmallInteger ).<br>
-       interpreterProxy success: (startIndex >= 1 and: [startIndex + frameCount - 1 <= (interpreterProxy slotSizeOf: buf cPtrAsOop)]).<br>
<br>
+       framesPlayed := self snd_PlaySamplesFromAtLength: frameCount _: buf _: startIndex - 1.<br>
+       framesPlayed >= 0<br>
+               ifTrue: [interpreterProxy methodReturnInteger: framesPlayed]<br>
+               ifFalse: [interpreterProxy primitiveFail]!<br>
-       interpreterProxy failed<br>
-               ifFalse: [framesPlayed := self cCode: 'snd_PlaySamplesFromAtLength(frameCount, (void *)buf, startIndex - 1)'.<br>
-                       interpreterProxy success: framesPlayed >= 0].<br>
-       ^ framesPlayed asPositiveIntegerObj!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundPlaySilence (in category 'primitives') -----<br>
  primitiveSoundPlaySilence<br>
        "Output a buffer's worth of silence. Returns the number of sample frames played."<br>
<br>
+       <export: true><br>
        | framesPlayed |<br>
+       framesPlayed := self snd_PlaySilence.  "-1 if sound output not started"<br>
+       framesPlayed >= 0<br>
+               ifTrue: [interpreterProxy methodReturnInteger: framesPlayed]<br>
+               ifFalse: [interpreterProxy primitiveFail]!<br>
-       self primitive: 'primitiveSoundPlaySilence'.<br>
-       framesPlayed := self cCode: 'snd_PlaySilence()'.  "-1 if sound output not started"<br>
-       interpreterProxy success: framesPlayed >= 0.<br>
-       ^framesPlayed asPositiveIntegerObj!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundRecordSamplesInto:startingAt: (in category 'primitives') -----<br>
  primitiveSoundRecordSamplesInto: buf startingAt: startWordIndex <br>
        "Record a buffer's worth of 16-bit sound samples."<br>
+       | bufSizeInBytes samplesRecorded bufPtr byteOffset |<br>
-       | bufSizeInBytes samplesRecorded bufPtr byteOffset bufLen |<br>
        <var: #bufPtr type: #'char*'><br>
        self primitive: 'primitiveSoundRecordSamples'<br>
+               parameters: #(WordArray SmallInteger).<br>
-               parameters: #(WordArray SmallInteger ).<br>
<br>
+       bufSizeInBytes := (interpreterProxy slotSizeOf: buf cPtrAsOop) * 4.<br>
+       byteOffset := (startWordIndex - 1) * 2.<br>
-       interpreterProxy failed ifFalse:<br>
-               [bufSizeInBytes := (interpreterProxy slotSizeOf: buf cPtrAsOop) * 4.<br>
-                interpreterProxy success: (startWordIndex >= 1 and: [startWordIndex - 1 * 2 < bufSizeInBytes])].<br>
<br>
+       (startWordIndex >= 1 and: [byteOffset < bufSizeInBytes]) ifFalse:<br>
+               [^interpreterProxy primitiveFailFor: PrimErrBadIndex].<br>
-       interpreterProxy failed ifFalse:[<br>
-               byteOffset := (startWordIndex - 1) * 2.<br>
-               bufPtr := (self cCoerce: buf to: 'char*') + byteOffset.<br>
-               bufLen := bufSizeInBytes - byteOffset.<br>
-               samplesRecorded := self cCode: 'snd_RecordSamplesIntoAtLength(bufPtr, 0, bufLen)' inSmalltalk:[bufPtr. bufLen. 0].<br>
-       ].<br>
<br>
+       bufPtr := (self cCoerce: buf to: #'char *') + byteOffset.<br>
+       samplesRecorded := self snd_RecordSamplesIntoAtLength: bufPtr _: 0 _: bufSizeInBytes - byteOffset.<br>
+       interpreterProxy failed ifFalse:<br>
+               [^samplesRecorded asPositiveIntegerObj]!<br>
-       ^ samplesRecorded asPositiveIntegerObj!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundSetLeftVolume:rightVolume: (in category 'primitives') -----<br>
  primitiveSoundSetLeftVolume: aLeftVolume rightVolume: aRightVolume<br>
        "Set the sound input recording level."<br>
<br>
        self primitive: 'primitiveSoundSetLeftVolume'<br>
                parameters: #(Float Float).<br>
+       self snd_SetVolume: aLeftVolume _: aRightVolume!<br>
-       interpreterProxy failed ifFalse: [self cCode: 'snd_SetVolume(aLeftVolume,aRightVolume)'].<br>
- !<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundSetRecordLevel: (in category 'primitives') -----<br>
  primitiveSoundSetRecordLevel: level <br>
        "Set the sound input recording level."<br>
        self primitive: 'primitiveSoundSetRecordLevel'<br>
+               parameters: #(SmallInteger).<br>
+       self snd_SetRecordLevel: level!<br>
-               parameters: #(SmallInteger ).<br>
-       interpreterProxy failed ifFalse: [self cCode: 'snd_SetRecordLevel(level)']!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundStartBufferSize:rate:stereo: (in category 'primitives') -----<br>
  primitiveSoundStartBufferSize: bufFrames rate: samplesPerSec stereo: stereoFlag<br>
        "Start the double-buffered sound output with the given buffer size, sample rate, and stereo flag."<br>
<br>
        self primitive: 'primitiveSoundStart'<br>
                parameters: #(SmallInteger SmallInteger Boolean).<br>
+       interpreterProxy success: (self snd_Start: bufFrames _: samplesPerSec _: stereoFlag _: 0)!<br>
-       interpreterProxy success: (self cCode: 'snd_Start(bufFrames, samplesPerSec, stereoFlag, 0)')!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundStartBufferSize:rate:stereo:semaIndex: (in category 'primitives') -----<br>
  primitiveSoundStartBufferSize: bufFrames rate: samplesPerSec stereo: stereoFlag semaIndex: semaIndex<br>
        "Start the double-buffered sound output with the given buffer size, sample rate, stereo flag, and semaphore index."<br>
<br>
        self primitive: 'primitiveSoundStartWithSemaphore'<br>
                parameters: #(SmallInteger SmallInteger Boolean SmallInteger).<br>
+       interpreterProxy success: (self snd_Start: bufFrames _: samplesPerSec _: stereoFlag _: semaIndex)!<br>
-       interpreterProxy success: (self cCode: 'snd_Start(bufFrames, samplesPerSec, stereoFlag, semaIndex)')!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundStartRecordingDesiredSampleRate:stereo:semaIndex: (in category 'primitives') -----<br>
  primitiveSoundStartRecordingDesiredSampleRate: desiredSamplesPerSec stereo: stereoFlag semaIndex: semaIndex<br>
        "Start recording sound with the given parameters."<br>
<br>
        self primitive: 'primitiveSoundStartRecording'<br>
                parameters: #(SmallInteger Boolean SmallInteger).<br>
+       self snd_StartRecording: desiredSamplesPerSec _: stereoFlag _: semaIndex!<br>
-       self cCode: 'snd_StartRecording(desiredSamplesPerSec, stereoFlag, semaIndex)'!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundStop (in category 'primitives') -----<br>
  primitiveSoundStop<br>
        "Stop double-buffered sound output."<br>
+       <export: true><br>
+       self snd_Stop!<br>
- <br>
-       self primitive: 'primitiveSoundStop'.<br>
- <br>
-       self cCode: 'snd_Stop()'.  "leave rcvr on stack"!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundStopRecording (in category 'primitives') -----<br>
  primitiveSoundStopRecording<br>
        "Stop recording sound."<br>
+       <export: true><br>
+       self snd_StopRecording!<br>
- <br>
-       self primitive: 'primitiveSoundStopRecording'.<br>
-       self cCode: 'snd_StopRecording()'.  "leave rcvr on stack"!<br>
<br>
Item was changed:<br>
  ----- Method: SoundPlugin>>primitiveSoundSupportsAEC (in category 'primitives') -----<br>
  primitiveSoundSupportsAEC<br>
+       "Answer if the OS/hardware supports echo-cancellation."<br>
+       <export: true><br>
-       "Answer true if the OS/hardware supports echo-cancellation, and false otherwise."<br>
        | result |<br>
+       result := self snd_SupportsAEC.<br>
+       interpreterProxy failed ifFalse:<br>
+               [interpreterProxy methodReturnBool: result ~= 0]!<br>
-       self primitive: 'primitiveSoundSupportsAEC'.<br>
-       interpreterProxy failed ifFalse: [<br>
-               result := self cCode: 'snd_SupportsAEC()'.<br>
-               result == 0 ifTrue: [^interpreterProxy falseObject] ifFalse: [^interpreterProxy trueObject]<br>
-       ].<br>
- !<br>
<br>
Item was changed:<br>
  ----- Method: VMClass>>cCoerce:to: (in category 'memory access') -----<br>
  cCoerce: value to: cTypeString<br>
+       "Type coercion. For translation a cast will be emitted. When running in Smalltalk<br>
-       "Type coercion. For translation a cast will be emmitted. When running in Smalltalk<br>
          answer a suitable wrapper for correct indexing."<br>
        <doNotGenerate><br>
        ^value<br>
                ifNil: [value]<br>
                ifNotNil: [value coerceTo: cTypeString sim: self]!<br>
<br>
Item was changed:<br>
  ----- Method: VMPluginCodeGenerator>>shouldGenerateAsInterpreterProxySend: (in category 'utilities') -----<br>
  shouldGenerateAsInterpreterProxySend: aSendNode<br>
        "Answer if this send should be generated as interpreterProxy->foo or its moral equivalent (*).<br>
         (*) since we now use function pointers declared in each external plugin we only indirect through<br>
         interopreterProxy at plugin initialization.  But we still have to find the set of sends a plugin uses."<br>
        | selector |<br>
        (aSendNode receiver isVariable and: ['interpreterProxy' = aSendNode receiver name]) ifFalse: [^false].<br>
        selector := aSendNode selector.<br>
        "baseHeaderSize, minSmallInteger et al are #defined in each VM's interp.h"<br>
        (VMBasicConstants mostBasicConstantSelectors includes: selector) ifTrue: [^false].<br>
        "Only include genuine InterpreterProxy methods, excluding things not understood<br>
+        by InterpreterProxy and things in its initialize, private and simulation protocols."<br>
+       ^(#(initialize private #'simulation only') includes: (InterpreterProxy compiledMethodAt: selector ifAbsent: [^false]) protocol) not!<br>
-        by InterpreterProxy and things in its initialize and private protocols."<br>
-       ^(#(initialize private) includes: (InterpreterProxy compiledMethodAt: selector ifAbsent: [^false]) protocol) not!<br>
<br>
</blockquote></div>