// Text of project SoundStudio written on 4/17/97 at 4:02 PM // Beginning of file SoundStudio form // Before Script for "app" // Copyright © 1996 by Apple Computer, Inc. All rights reserved. constant kSizeSliderUnits := 256; //¥¥ app := {viewBounds: {left: 8, top: 8, right: -8, bottom: 296}, viewJustify: 48, viewSetupDoneScript: func() begin // get the sliders to update their textual representations sizeSlider:changedSlider(); gainSlider:changedSlider(); end, debug: "app", _proto: @180 }; _view000 := {title: "Sound Studio", viewBounds: {left: -4, top: 4, right: 100, bottom: 20}, _proto: @229 }; AddStepForm(app, _view000); separatorLines := {viewBounds: {left: -5, top: 53, right: 5, bottom: 89}, viewFlags: 1, viewFormat: 337, viewJustify: 48, debug: "separatorLines", viewClass: 74 }; AddStepForm(app, separatorLines); // After Script for "separatorLines" thisView := separatorLines; // This view only exists to provide lines between the three method sections. // That's why it has its viewFormat slot set to create a black frame. // Note that it is first in the child order so that it doesn't obscure // the other views. -----method 1----- := {text: "Method #1:", viewBounds: {left: 6, top: 32, right: 73, bottom: 48}, debug: "-----method 1-----", _proto: @218 }; AddStepForm(app, -----method 1-----); soundRecorder := { buttonClickScript: func() begin sr:=getroot().soundrecorder; if sr then begin sr:openrecord(self.callback); end else :notify(knotifyalert, kAppName, "SoundRecorder doesn't seem to exist..."); end, text: "Show soundRecorder slip", viewBounds: {left: 78, top: 28, right: 211, bottom: 46}, callback: func(a) begin getroot():notify(kNotifyQAlert, "sound1 callback", "SoundRecorder returned an array with " & length(a) & " element(s), coming soon to a speaker near you."); // play the sounds we got back. foreach v in a do PlaySoundIrregardless(v); end, debug: "soundRecorder", _proto: @226 }; AddStepForm(app, soundRecorder); // After Script for "soundRecorder" thisView := soundRecorder; // This button implements method #1 of recording sound: it simply // opens the SoundRecorder slip and has a callback which is given the sound // that the user recorded. Minimal code, standard UI, but no special control // and it opens a separate view instead of being imbedded in your app. -----method 2----- := {text: "Method #2:", viewBounds: {left: 6, top: 66, right: 73, bottom: 82}, debug: "-----method 2-----", _proto: @218 }; AddStepForm(app, -----method 2-----); prv := {viewBounds: {left: 76, top: 60, right: 264, bottom: 84}, viewJustify: 0, debug: "prv", _proto: @853 }; AddStepForm(app, prv); StepDeclare(app, prv, 'prv); // After Script for "prv" thisView := prv; // This view implements method #2 of recording sound: it is a // protoRecorderView, which gives the user everything they need // to record sound. This is designed to be embedded inside your // application, as opposed to the soundRecorder view which floats. // The resulting recorded sound array is returned by the :GetSounds() method. prvPlayer := { buttonClickScript: func() begin foreach snd in prv:GetSounds() do PlaySound(snd); end, text: "Play", viewBounds: {left: 273, top: 64, right: 302, bottom: 78}, debug: "prvPlayer", _proto: @226 }; AddStepForm(app, prvPlayer); -----method 3----- := {text: "Method #3:", viewBounds: {left: 6, top: 101, right: 73, bottom: 117}, debug: "-----method 3-----", _proto: @218 }; AddStepForm(app, -----method 3-----); soundManager := {viewBounds: {left: 5, top: 45, right: 6, bottom: 46}, viewFlags: 1, viewFormat: 0, theChannel: nil, record: func(howmany, doLoop) begin local channel, callbackGenerator, i, thisCallback; channel:= protoSoundChannel:newrecording(); channel.inputDevice := kInternalMic; // this overrides the default (user's) setting... channel.inputGain := gainSlider.viewValue; channel.inputBlockSize := sizeSlider.viewvalue * kSizeSliderUnits; channel:open(); callbackGenerator := func(theview, index, reschedule) begin func(state, result) begin theview:recordingCallback(state, result, index, reschedule) end end; blocks := array(howmany, nil); for i:= 0 to howmany-1 do begin thisCallback := call callbackGenerator with (self, i, doLoop); blocks[i] := channel:newInputBlock(thisCallback); blocks[i].compressiontype := 0; channel:schedule(blocks[i]); end; if nil and doLoop then for i:= 0 to howmany -1 do channel:schedule(blocks[i]); theChannel := channel; channel:start(true); end, blocks: nil, play: func(howmany) begin // close down the channel if it's already active if theChannel and theChannel:IsOpen() then begin theChannel:stop(); // necessary? theChannel:close(); end else theChannel:={_proto: protoSoundChannel}; theChannel.direction := 'play; theChannel:open(); if howmany > length(blocks) then howmany := length(blocks); for i:= 1 to howmany do begin blocks[i-1].callback := nil; theChannel:schedule(blocks[i-1]); end; theChannel:start(true); // can we ask theChannel what its state is? (playing, recording, doing nothing) // perhaps IsActive along with theChannel.direction tells us? end, recordingCallback: func(state, result, blockIndex, reschedule) begin local displayType; // don't do anything if we've been stopped manually if state <> 0 then return; if reschedule then // reschedule this block so it gets recorded into again! theChannel:schedule(blocks[blockIndex]); // does the user want a click after each block? if ClickCheckbox.viewValue then Clicker(); // Is theChannel stopped when all the blocks are completed? For now I'll stop it manually. if (reschedule = nil) and (blockIndex = length(blocks) -1) then theChannel:stop(); // generate the appropriate graphical view of the sound displayType := ('[createFilledWave, createHollowWave, createFFTDisplay])[displayPicker.viewValue]; Perform(displayView, displayType, [blocks[blockIndex].samples, zoomPicker:getvalue()]); displayView:dirty(); RefreshViews(); end, viewQuitScript: // must return the value of inherited:?viewQuitScript(); func() begin if theChannel then begin theChannel:stop(); // necessary? theChannel:close(); theChannel := nil; end; inherited:?viewQuitScript(); // ¥¥Êthis method is defined internally end, debug: "soundManager", viewClass: 74 }; AddStepForm(app, soundManager); StepDeclare(app, soundManager, 'soundManager); recSingle := { buttonClickScript: func() begin soundManager:record(1, nil); end, text: "1 block", viewBounds: {left: 78, top: 100, right: 131, bottom: 116}, debug: "recSingle", _proto: @226 }; AddStepForm(app, recSingle); recDouble := { buttonClickScript: func() begin soundManager:record(2, nil); end, text: "2 blocks", viewBounds: {left: 78, top: 124, right: 131, bottom: 140}, debug: "recDouble", _proto: @226 }; AddStepForm(app, recDouble); recLoop := { buttonClickScript: func() begin if soundManager.theChannel and soundManager.theChannel:IsActive() then begin soundManager.theChannel:stop(); setvalue(self, 'text, "loop"); end else begin soundManager:record(2, true); setvalue(self, 'text, "stop"); end; end, text: "loop", viewBounds: {left: 78, top: 148, right: 131, bottom: 163}, debug: "recLoop" , _proto: @226 }; AddStepForm(app, recLoop); playFirst := { buttonClickScript: func() begin soundManager:Play(1); end, text: "Play first", viewBounds: {left: 142, top: 100, right: 212, bottom: 116}, debug: "playFirst", _proto: @226 }; AddStepForm(app, playFirst); playBoth := { buttonClickScript: func() begin soundManager:Play(2); end, text: "Play both", viewBounds: {left: 142, top: 124, right: 212, bottom: 140}, debug: "playBoth", _proto: @226 }; AddStepForm(app, playBoth); sizeSlider := { changedSlider: func() begin setvalue(sizedisplay, 'text, NumberStr(viewValue * kSizeSliderUnits)); end, viewBounds: {left: 76, top: 170, right: 189, bottom: 181}, viewValue: 16384, trackSlider: func() begin setvalue(sizedisplay, 'text, NumberStr(viewValue * kSizeSliderUnits)); refreshviews(); end, maxValue: 65536, minValue: 0, viewSetupFormScript: func() begin // convert to the units we want the user to select with foreach slot in '[maxvalue, minvalue, viewValue] do self.(slot) := self.(slot) div kSizeSliderUnits; end, debug: "sizeSlider", _proto: @212 }; AddStepForm(app, sizeSlider); StepDeclare(app, sizeSlider, 'sizeSlider); _view001 := {text: "blocksize:", viewBounds: {left: 8, top: 170, right: 72, bottom: 186}, viewJustify: 8388609, _proto: @218 }; AddStepForm(app, _view001); sizedisplay := {text: "", viewBounds: {left: 76, top: 181, right: 189, bottom: 195}, debug: "sizedisplay", _proto: @218 }; AddStepForm(app, sizedisplay); StepDeclare(app, sizedisplay, 'sizedisplay); gainSlider := { changedSlider: func() begin setvalue(gaindisplay, 'text, NumberStr(viewValue)); end, viewBounds: {left: 76, top: 198, right: 189, bottom: 209}, viewValue: 127, trackSlider: func() begin setvalue(gaindisplay, 'text, NumberStr(viewValue)); refreshviews(); end, maxValue: 255, minValue: 0, viewSetupFormScript: func() begin // set the initial values for the slider end, debug: "gainSlider", _proto: @212 }; AddStepForm(app, gainSlider); StepDeclare(app, gainSlider, 'gainSlider); _view002 := {text: "gain:", viewBounds: {left: 8, top: 198, right: 72, bottom: 214}, viewJustify: 8388609, _proto: @218 }; AddStepForm(app, _view002); gainDisplay := {text: "", viewBounds: {left: 76, top: 209, right: 189, bottom: 222}, debug: "gainDisplay", _proto: @218 }; AddStepForm(app, gainDisplay); StepDeclare(app, gainDisplay, 'gainDisplay); zoomPicker := {labelCommands: ["32","64","128","256","512"], text: "Zoom", viewBounds: {left: 183, top: 230, right: 255, bottom: 244}, textSetup: func() // return first one as default if labelCommands then labelCommands[viewValue] else "", viewValue: 2, labelActionScript: func(cmd) begin self.viewValue := cmd; end, getValue: func() begin floor(stringToNumber(labelCommands[viewValue])) end, debug: "zoomPicker", _proto: @190 }; AddStepForm(app, zoomPicker); StepDeclare(app, zoomPicker, 'zoomPicker); displayPicker := {labelCommands: ["Wave", "Hollow wave", "FFT"], text: "Display", viewBounds: {left: 7, top: 230, right: 164, bottom: 244}, textSetup: func() // return first one as default if labelCommands then labelCommands[viewValue] else "", viewValue: 0, labelActionScript: func(cmd) begin self.viewValue := cmd; end, debug: "displayPicker", _proto: @190 }; AddStepForm(app, displayPicker); StepDeclare(app, displayPicker, 'displayPicker); displayView := {viewBounds: {left: 7, top: 251, right: -15, bottom: 283}, viewFlags: 1, viewFormat: 337, viewJustify: 48, myShape: nil, viewDrawScript: func() begin if myShape then :DrawShape(myShape,nil); end, viewSetupDoneScript: func() begin self.width := self:localbox().right - self:localbox().left; end, width: nil, createFilledWave: func native (samps, chunksize) begin local i,j,k,t,min,max; local arr; i:=length(samps) div chunksize; if i> width then i:= width; // let go of the old shapes and allocate the new array self.myShape := nil; arr := Array(i,0); self.myShape := arr; while (i>0) do begin i:=i-1; k:=i*chunksize; j:=k+chunksize-2; max:=ExtractByte(samps,j+1); min:=max; while (j>=k) do begin t:= ExtractByte(samps,j); if t > max then max:=t; else if t self.width then i:= self.width; // let go of the old shapes and allocate the new array self.myShape := nil; arrMax := Array(i * 2,0); arrMin := Array(i * 2,0); while (i>0) do begin i:=i-1; k:=i*chunksize; j:=k+chunksize-2; max:=ExtractByte(samps,j+1); min:=max; while (j>=k) do begin t:= ExtractByte(samps,j); if t > max then max:=t; else if t