'From OLPC2.0 of ''24 October 2006'' [latest update: #1358] on 1 June 2007 at 6:56:10 pm'! "Change Set: WsRangeFinder-ka Date: 1 June 2007 Author: Kazuhiro Abe Enable to use a range finder (GP2D12) with a World-Stethoscope. And fixed some miner bugs."! Object subclass: #WsWorldStethoscope instanceVariableNames: 'soundInput fft sounds offset cutOffLevel sortType listening passOver passUnder rangeFinderMode ' classVariableNames: '' poolDictionaries: '' category: 'WS-Sound-Interface'! AlignmentMorph subclass: #WsWorldStethoscopeMorph instanceVariableNames: 'statusLight sortType model ' classVariableNames: '' poolDictionaries: '' category: 'WS-Sound-Interface'! !Player methodsFor: '*WS-Sound-Interface' stamp: 'ka 6/1/2007 18:39'! getCutOffLevel ^ self getValueFromCostume: #cutOffLevel! ! !Player methodsFor: '*WS-Sound-Interface' stamp: 'ka 6/1/2007 18:39'! setCutOffLevel: number self setCostumeSlot: #cutOffLevel: toValue: number! ! !StandardScriptingSystem methodsFor: '*WS-Sound-Override' stamp: 'ka 6/1/2007 18:41'! wordingForOperator: aString "Answer the wording to be seen by the user for the given operator symbol/string" | toTest | toTest _ aString asString. #( (append: 'include at end') (arrowheadsOnAllPens 'arrowheads on all pens') (beep: 'make sound') (bounce: 'bounce') (clearTurtleTrails 'clear pen trails') (clearOwnersPenTrails 'clear all pen trails') (colorSees 'color sees') (color:sees: 'color sees') (doMenuItem: 'do menu item') (doScript: 'do') (forward: 'forward by') (goToRightOf: 'align after') (includeAtCursor: 'include at cursor') (isDivisibleBy: 'is divisible by') (liftAllPens 'lift all pens') (lowerAllPens 'lower all pens') (makeNewDrawingIn: 'start painting in') (max: 'max') (min: 'min') (moveToward: 'move toward') (noArrowheadsOnAllPens 'no arrowheads on pens') (overlapsAny 'overlaps any') (pauseAll: 'pause all') (pauseScript: 'pause script') (prepend: 'include at beginning') (seesColor: 'is over color') (startAll: 'start all') (startScript: 'start script') (stopProgramatically 'stop') (stopAll: 'stop all') (stopScript: 'stop script') (tellAllSiblings: 'tell all siblings') (tellSelfAndAllSiblings: 'send to all') (turn: 'turn by') (turnToward: 'turn toward') (wearCostumeOf: 'look like') (dial: 'dial to') (dialNumber 'dial number') (playSound: 'play frequency of') (stopSound 'stop sound') (soundPitch 'sound pitch') (soundLevel 'sound level') (soundListening 'listening') (passOver 'pass over') (passUnder 'pass under') (cutOffLevel 'cut off level') (appendVertex 'add a vertex at end') (insertVertexAtCursor 'insert a vertex at cursor') (prependVertex 'add a vertex at beginning') (removeAllButCursor 'remove all vertices but cursor') (removeVertexAtCursor 'remove the vertex at cursor') (shuffleVertices 'shuffle vertices') (lineCurved 'line is curved') (lineOpened 'line is opened') (showingHandles 'showing handle') (vertexCursor 'vertex cursor') (verticesCount 'vertices count') (xAtCursor 'x at cursor') (yAtCursor 'y at cursor') ) do: [:pair | toTest = pair first ifTrue: [^ pair second]]. ^ toTest "StandardScriptingSystem initialize" ! ! !WsWorldStethoscope methodsFor: 'accessing' stamp: 'ka 5/31/2007 23:26'! rangeFindeMode: boolean rangeFinderMode _ boolean == true! ! !WsWorldStethoscope methodsFor: 'accessing' stamp: 'ka 5/31/2007 23:59'! rangeFinderModeOff rangeFinderMode := false. self cutOffLevel: self defaultCutOffLevel. self passOver: self defaultPassOver. self passUnder: self defaultPassUnder! ! !WsWorldStethoscope methodsFor: 'accessing' stamp: 'ka 6/1/2007 18:55'! rangeFinderModeOn rangeFinderMode := true. self cutOffLevel: 150000. self passOver: 600. self passUnder: 4000. self inform: 'You should adjust ''cut off level'' to find range well.'! ! !WsWorldStethoscope methodsFor: 'accessing' stamp: 'ka 5/31/2007 23:27'! rangeFinderMode: boolean rangeFinderMode _ boolean == true! ! !WsWorldStethoscope methodsFor: 'accessing' stamp: 'ka 5/31/2007 23:46'! toggleRangeFinderMode self isRangeFinderMode ifTrue: [self rangeFinderModeOff] ifFalse: [self rangeFinderModeOn]! ! !WsWorldStethoscope methodsFor: 'constants' stamp: 'ka 6/1/2007 00:51'! cutOffLevelMaxValue ^ 200000! ! !WsWorldStethoscope methodsFor: 'initialization' stamp: 'ka 5/31/2007 23:41'! initialize fft _ FFT new: 512. self cutOffLevel: self defaultCutOffLevel. self offset: self defaultOffset. self passOver: self defaultPassOver. self passUnder: self defaultPassUnder. sounds _ Array with: WsSound new with: WsSound new with: WsSound new. soundInput _ SoundInputStream new samplingRate: 22050. self sortByLevel. self rangeFinderModeOff! ! !WsWorldStethoscope methodsFor: 'processing' stamp: 'ka 6/1/2007 18:47'! updateSounds: analizedData | factor passUnderValue passOverValue deltas | self isRangeFinderMode ifTrue: [#(1 2 3 4 5 6 22 23 24 25 45 46 47 68 69 70 90 91 92 113 114 115 135 136 137 159 ) do: [:index | analizedData at: index put: 0]]. factor := soundInput samplingRate / fft n. passUnderValue := self passUnder - self offset / factor. passOverValue := self passOver - self offset / factor. deltas := Array new: analizedData size - 1. 1 to: deltas size do: [:index | | delta | delta := (passOverValue < index and: [index < passUnderValue]) ifTrue: [(analizedData at: index + 1) - (analizedData at: index)] ifFalse: [0]. deltas at: index put: index -> delta]. deltas := deltas asSortedCollection: [:a :b | a value < b value]. 1 to: 3 do: [:index | | n level frequency | n := (deltas at: index) key - 1 max: 1. level := analizedData at: n. frequency := level < self cutOffLevel ifTrue: [frequency := 0. level := 0] ifFalse: [frequency := (n * factor) truncated]. self isRangeFinderMode ifTrue: [frequency isZero ifFalse: [(sounds at: index) frequency: frequency + self offset level: level]] ifFalse: [(sounds at: index) frequency: frequency + self offset level: level]]. sounds := self isSortByLevel ifTrue: [(sounds asSortedCollection: [:a :b | a level > b level]) asArray] ifFalse: [(sounds asSortedCollection: [:a :b | a frequency > b frequency]) asArray]! ! !WsWorldStethoscope methodsFor: 'testing' stamp: 'ka 5/31/2007 23:25'! isRangeFinderMode ^ rangeFinderMode == true! ! !WsWorldStethoscopeMorph methodsFor: 'accessing' stamp: 'ka 6/1/2007 18:37'! cutOffLevel ^ model cutOffLevel! ! !WsWorldStethoscopeMorph methodsFor: 'accessing' stamp: 'ka 6/1/2007 18:37'! cutOffLevel: number model cutOffLevel: number! ! !WsWorldStethoscopeMorph methodsFor: 'initialization' stamp: 'ka 5/31/2007 18:10'! initialize super initialize. model _ WsWorldStethoscope default. model initialize. self hResizing: #shrinkWrap; vResizing: #shrinkWrap. borderWidth := 2. self listDirection: #topToBottom. self addButtonRow. self addSliders! ! !WsWorldStethoscopeMorph methodsFor: 'menus' stamp: 'ka 5/31/2007 23:44'! addCustomMenuItems: aMenu hand: aHandMorph "Add morph-specific items to the given menu which was invoked by the given hand." super addCustomMenuItems: aMenu hand: aHandMorph. aMenu addLine. aMenu addUpdating: #sortByLevelString action: #sortByLevel. aMenu addUpdating: #sortByFrequencyString action: #sortByFrequency. aMenu addLine. aMenu addUpdating: #rangeFinderModeString action: #toggleRangeFinderMode.! ! !WsWorldStethoscopeMorph methodsFor: 'menus' stamp: 'ka 5/31/2007 23:44'! rangeFinderModeString | label | label _ 'range finder mode' translated. ^ self isRangeFinderMode ifTrue: ['' , label] ifFalse: ['' , label]! ! !WsWorldStethoscopeMorph methodsFor: 'menus' stamp: 'ka 3/25/2005 21:01'! sortByLevelString | label | label _ 'sort by level' translated. ^ self isSortByLevel ifTrue: ['' , label] ifFalse: ['' , label]! ! !WsWorldStethoscopeMorph methodsFor: 'private' stamp: 'ka 5/31/2007 21:53'! buildSliderFor: aspectSymbol min: minNumber max: maxNumber label: labelString | slider row | slider _ SimpleSliderMorph new color: Color gray; extent: 100 @ 10; target: self; actionSelector: aspectSymbol , ':'; minVal: minNumber; maxVal: maxNumber; adjustToValue: (self perform: aspectSymbol); name: aspectSymbol. row _ AlignmentMorph newRow color: color; layoutInset: 0; wrapCentering: #center; hResizing: #spaceFill; vResizing: #rigid; height: 24. row addMorphBack: (StringMorph contents: labelString translated). row addMorphBack: (Morph new extent: 4 @ 1; color: Color transparent). row addMorphBack: (self buttonName: '-' action: aspectSymbol , 'Decrease'). row addMorphBack: (Morph new extent: 4 @ 1; color: Color transparent). row addMorphBack: slider. row addMorphBack: (Morph new extent: 4 @ 1; color: Color transparent). row addMorphBack: (self buttonName: '+' action: aspectSymbol , 'Increase'). ^row! ! !WsWorldStethoscopeMorph class methodsFor: 'scripting' stamp: 'ka 6/1/2007 18:42'! additionsToViewerCategories ^ # ((sound ( (slot frequency1 'frequency-1 of sound' Number readOnly player getFrequency1 unused unused) (slot level1 'level-1 of sound' Number readOnly player getLevel1 unused unused) (slot frequency2 'frequency-2 of sound' Number readOnly player getFrequency2 unused unused) (slot level2 'level-2 of sound' Number readOnly player getLevel2 unused unused) (slot frequency3 'frequency-3 of sound' Number readOnly player getFrequency3 unused unused) (slot level3 'level-3 of sound' Number readOnly player getLevel3 unused unused) (slot dialNumber 'dial number of sound' String readOnly player getDialNumber unused unused) (slot soundListening 'whether the stethoscope is listening' Boolean readWrite Player getListening Player setListening:) (slot passOver 'high pass filter value' Number readWrite player getPassOver player setPassOver:) (slot passUnder 'low pass filter value' Number readWrite player getPassUnder player setPassUnder:) (slot cutOffLevel 'cut off level' Number readWrite player getCutOffLevel player setCutOffLevel:) ))) ! ! WsWorldStethoscopeMorph removeSelector: #isSortByFrequency! WsWorldStethoscopeMorph removeSelector: #isSortByLevel! WsWorldStethoscopeMorph removeSelector: #sortByFrequency! WsWorldStethoscopeMorph removeSelector: #sortByLevel! AlignmentMorph subclass: #WsWorldStethoscopeMorph instanceVariableNames: 'statusLight model' classVariableNames: '' poolDictionaries: '' category: 'WS-Sound-Interface'! Object subclass: #WsWorldStethoscope instanceVariableNames: 'soundInput fft sounds offset cutOffLevel sortType listening passOver passUnder rangeFinderMode' classVariableNames: '' poolDictionaries: '' category: 'WS-Sound-Interface'!