TweetFollow Us on Twitter

Zoundz
Volume Number:5
Issue Number:9
Column Tag:Intermediate Mac'ing

Related Info: Sound Manager

'snd ' Zoundz Great!

By Kirk Chase, Anaheim, CA

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Piano Lessons--Yech!

When I was a boy, my mother made me take piano lessons (a fate that has fallen upon many young boys). I wasn’t too interested in the piano, so I soon traded the ivory keyboard for the Qwerty keyboard. I can still hear my mother telling me that I’ll regret the day I stopped playing the piano; I didn’t think so-until now.

I wanted to add sound to an application I was writing. Not knowing anything about sound other than the simple beep, I decided the Macintosh ‘snd ‘ resource was my next attack of study. So after an evening with Inside Macintosh, Vol. 5, I decided I had to be a MIDI maniac to understand a lot of the information found there, but I did manage to get a grasp on some of the simpler elements. So here is a simple overview.

To make a sound, you basically create a sound channel to a particular synthesizer and pass it a list of sound commands which, among other things, generate sounds--simple. A synthesizer allows music to be played in a certain way; there is currently a note synthesizer, a wave synthesizer, a sampled sound synthesizer, and some MIDI synthesizers. Which one you use depends on how you are going to generate the sound. I chose the note synthesizer for my sound generating application.

A sound channel is a record that holds information for processing the sound. It contains modifiers, call-back procedures, and a queue of sound commands. The sound commands are in a ‘snd ‘ resource format.

Figure 1. ‘snd ‘ Resource Format

The ‘snd ‘ Resource

The ‘snd ‘ resource has currently two types. The second type is used for representing an instrument or digitally recorded sound. Type one is used by us non-MIDI people. The first word in the ‘snd ‘ resource specifies this format type, 1 or 2. I use format type 1.

The second word tells how many synthesizers and modifiers that follow. A modifier, for a quick explination, is some code that alters the sound generated in some manner. I use this second word to open up a note synthesizer. This word is followed by a word identifying the synthesizer and a long integer giving the initialization procedure, if any. This is repeated for each synthesizer and modifier as specified in the second word.

After that, there is a word telling how many sound commands are to follow. Then the sound commands and any needed data are given. The format of the sound command is a word specifying which sound command, another word for param1, and a long for param2. If this is all confusing, see figure 1.

Sound Commands

For my application, I use the simple note synthesizer. Out of all the commands that generate sound, the note synthesizer recognizes only the note, rest, quiet, frequency, amplitude, and timbre commands. I use only the note, timbre, rest, and quiet commands in my application.

The note command has the ability to specify the pitch, amplitude, and duration. To further confuse matters, the pitch may be specified in two ways. The first way is to use a value between 0 and 127. This is then converted to a piano key (60 is middle C; 61 is C#). The other way is to give the actual frequency. I use the first way. The amplitude (0 255) is also added to the pitch and stored in the long (See figure 2). The duration, param1, is specified in milliseconds.

Figure 2. Combining Pitch and Amplitude

Zoundz 1.0

Zoundz gives the user the ability to draw a wave type description of the sound. There are four graphs the user draws to generate the sound--frequency, amplitude, duration, and timbre. Each vertical column is a note. You may also control the display of each graph (the drawing goes faster if you turn off the drawing of the other graphs).

The user may specify the exact values of a particular note. Use the scroll bar below the graphs to move the note you wish to modify to the left edge of the rectangle. The individual values may then be adjusted using the scroll bars in the upper right corner of the window. You may also specify which note to start and end your selection. When you are ready, just press the “Play Sound” button (be sure to specify the selection range). Figure 3 shows the Zoundz window.

Figure 3. Zoundz Window

Some editing shortcuts can be found under the “Extend” menu. Just set the selection range and select one of the menu items under this menu. The value selected in the menu of the current note (left edge) is given to the notes in the selection range.

When you save the sound, the values for each of the 100 possible notes are saved in the data fork. In the resource fork, a ‘snd ‘ resource is created with your sound. The name is the same as the one you gave to the file, and its ID=9000. Just use ResEdit to paste the sound into the system file (you may wish to give it a new ID number). You may also get a printed dump of your ‘snd ' resource by selecting the print option from the “File” menu.

Zoundz is pretty nice. It handles Finder startups for the documents to either print or open. It gives us non-musicians the chance to sketch our sound. Zoundz is not very good at producing composed music. I did however get it to play “Happy Birthday”. Try drawing patterns and listen to the outcome. You might create something that zoundz good.

Figure 4. Zoundz Build Order

Listing:  MyGlobals

unit MyGlobals;
interface
 uses
 PrintTraps, Sound;
 const
 L_Apple = 1001; {Menu list}
 MI_About_Zoundz = 1;
 L_File = 1002;  {Menu list}
 MI_New = 1;
 MI_Open = 2;
 MI_Close = 4;
 MI_Save = 5;
 MI_Save_As = 6;
 MI_Page_Setup = 8;
 MI_Print = 9;
 MI_Quit = 11;
 L_Edit = 1003;  {Menu list}
 MI_Undo = 1;
 MI_Cut = 3;
 MI_Copy = 4;
 MI_Paste = 5;
 L_Extend = 1004;{Menu list}
 MI_Frequency = 1;
 MI_Amplitude = 2;
 MI_Duration = 3;
 MI_Timbre = 4;
 I_Yes = 1;
 I_Cancel = 2;
 I_No = 3;
 type
 DocPtr = ^DocRec;
 DocRec = record {Sound Doc Structure}
 Freq, Amp, Dur, Timbre: array[1..100] of integer;
 EndValue, StartValue: integer;
 end;
 MySoundRec = packed record {Snd structure}
 format: integer;
 SynthCount: integer;
 SynthType: integer;
 SynthInit: longint;
 CommandCount: integer;
 MySounds: array[1..202] of SndCommand;
 end;
 MySoundPtr = ^MySoundRec;
 MySoundHdl = ^MySoundPtr;
 var
 FreqText, AmpText, DurText, TimbreText: Str255; 
 NoteText, StartText, EndText: Str255;
 NoteIndex, DrawTool: integer; {Indices}
 MyDoc: DocPtr; {Sound Document}
 Dirty: boolean;
 NoteRect, FreqRect, AmpRect, DurRect, TimbreRect: Rect;
 EndRect, StartRect, NotePallete: Rect;
 MyHandle: Handle;  {Various Handles}
 MySoundHandle: MySoundHdl;
 AppleMenu: MenuHandle;   {Menu handle}
 M_File: MenuHandle;
 M_Edit: MenuHandle;
 M_Extend: MenuHandle;
 MyWindow: WindowPtr;     {Window pointer}
 FileName: str255; {File Stuff}
 volRefNum, FileStatus: integer;
 theSquare, theWatch: CursHandle; {Cursor Stuff}
 ThePrintRec: THPrint; {Printing Stuff}
 ThePrintPort: TPPrPort;
 PrintStatus: TPrStatus;
 PageRect: rect;
implementation
end.
Listing:  MySound.pas

unit MySound;
interface
 uses
 PrintTraps, Sound, MyGlobals;
 {creates a ‘snd ‘ resource}
 procedure CreateSndResource (StartSel, EndSel: integer);
implementation
 procedure CreateSndResource;
 var
 i, j: integer;
 amplong, freqlong: longint;
 lastTimbre: integer;
 theErr: OSErr;
 theSize: integer;
 begin
 lastTimbre := -1;
 if Handle(MySoundHandle) <> nil then
 begin
 DisposHandle(Handle(MySoundHandle));
 DisposHandle(MyHandle);
 end;

 MySoundHandle := MySoundHdl(NewHandle(sizeof(MySoundRec)));
 with MySoundHandle^^ do
 begin
 format := 1; {set up header stuff}
 SynthCount := 1;
 SynthType := 1;
 SynthInit := 0;
 with MyDoc^ do
 begin
 j := 0;
 for i := StartSel to EndSel do
 begin {get sound commands}
 j := j + 1;
 if timbre[i] <> lastTimbre then 
 begin
 MySounds[j].cmd := timbreCmd;
 MySounds[j].param1 := timbre[i];
 MySounds[j].param2 := 0;
 lastTimbre := timbre[i];
 j := j + 1;
 end; {of timbre command}

 if freq[i] = 128 then {is it a rest?}
 begin
 MySounds[j].cmd := restCmd;
 MySounds[j].param1 := dur[i];
 MySounds[j].param2 := 0;
 end
 else {no, regular note}
 begin
 ampLong := amp[i];
 ampLong := BitAnd(BitShift(ampLong, 24), $FF000000);
 freqLong := BitAnd(freq[i], $000000FF);
 MySounds[j].cmd := noteCmd;
 MySounds[j].param1 := dur[i];
 MySounds[j].param2 := ampLong + freqLong;
 end;
 end; {of for loop}
 end; {with MyDoc}
 j := j + 1;
 MySounds[j].cmd := noteCmd; {turn off sound}
 MySounds[j].param1 := 0;
 MySounds[j].param2 := 0;
 j := j + 1;
 MySounds[j].cmd := quietCmd;
 MySounds[j].param1 := 0;
 MySounds[j].param2 := 0;
 CommandCount := j;
 end; { of with MySoundHandle}
 theSize := 12 + (j * 8);
 MyHandle := Handle(MySoundHandle);
 theErr := HandToHand(MyHandle);
 SetHandleSize(MyHandle, Size(theSize));
 end; { of CreateSndResource}
end.
Listing:  InitTheMenus.pas
unit InitTheMenus;
interface
 uses
 PrintTraps, Sound, MyGlobals;
 procedure Init_My_Menus;     {Initialize the menus}
implementation
 procedure Init_My_Menus;     {Initialize the menus}
 const
 Menu1 = 1001;   {Menu resource ID}
 Menu2 = 1002;   {Menu resource ID}
 Menu3 = 1003;   {Menu resource ID}
 Menu4 = 1004;
 begin    {Start of Init_My_Menus}
 ClearMenuBar;   {Clear any old menu bars}
 AppleMenu := GetMenu(Menu1);
 InsertMenu(AppleMenu, 0);
 AddResMenu(AppleMenu, ‘DRVR’);
 M_File := GetMenu(Menu2);
 InsertMenu(M_File, 0);
 M_Edit := GetMenu(Menu3);
 InsertMenu(M_Edit, 0);
 M_Extend := GetMenu(Menu4);
 InsertMenu(M_Extend, 0);
 DrawMenuBar;
 end;   {End of procedure Init_My_Menus}
end.
Listing:  Message.pas
unit Message;
interface
 procedure A_Message (s0, s1, s2, s3: str255; var theItem: integer);
implementation
 procedure A_Message;
 var
 itemHit: Integer;
 AlertResHandle: AlertTHndl;
 tempRect: Rect;
 begin    {Start of alert handler}
 ParamText(s0, s1, s2, s3);
 AlertResHandle := AlertTHndl(GetResource(‘ALRT’, 4));
 HLock(Handle(AlertResHandle));
 tempRect := AlertResHandle^^.boundsRect;
 tempRect.Left := ((screenBits.Bounds.Right - screenBits.Bounds.Left) 
- (tempRect.Right - tempRect.Left)) div 2;{Center Horz}
 tempRect.Bottom := tempRect.Top + (AlertResHandle^^.boundsRect.Bottom 
- AlertResHandle^^.boundsRect.Top);
 tempRect.Right := tempRect.Left + (AlertResHandle^^.boundsRect.Right 
- AlertResHandle^^.boundsRect.Left);
 AlertResHandle^^.boundsRect := tempRect;
 theItem := NoteAlert(4, nil);
 HUnLock(Handle(AlertResHandle));
 end;     {End of procedure}
end.    {End of unit}
Listing:  save_changes.pas
unit save_changes;
interface
 function D_save_changes: integer;
implementation
 const  
 I_Yes = 1;
 I_Cancel = 2;
 I_No = 3;
 I_x = 4;
 var
 ExitDialog: boolean; 
 DoubleClick: boolean;
 MyPt: Point;
 MyErr: OSErr;
 function D_save_changes;
 var
 GetSelection: DialogPtr;
 tempRect: Rect;
 DType: Integer;
 DItem: Handle;
 itemHit: Integer;
 procedure Refresh_Dialog;
 var
 rTempRect: Rect;
 begin
 SetPort(GetSelection);   {Point to our dialog window}
 GetDItem(GetSelection, I_Yes, DType, DItem, tempRect);
 PenSize(3, 3);
 InsetRect(tempRect, -4, -4);
 FrameRoundRect(tempRect, 16, 16);
 PenSize(1, 1);
 end;
 begin    {Start of dialog handler}
 GetSelection := GetNewDialog(3, nil, Pointer(-1));
 ShowWindow(GetSelection);
 SelectWindow(GetSelection);
 SetPort(GetSelection);
 Refresh_Dialog;
 ExitDialog := FALSE;
 repeat
 ModalDialog(nil, itemHit);
 D_save_changes := itemHit;
 if (ItemHit = I_Yes) or (ItemHit = I_Cancel) or (ItemHit = I_No) then
 ExitDialog := TRUE;
 until ExitDialog;
 DisposDialog(GetSelection);
 end;
end.    {End of unit}
Listing:  MyFileStuff.pas
unit MyFileStuff;
interface
 uses
 PrintTraps, Sound, MyGlobals, Message, MySound;
 procedure doSave;
 procedure doSaveAs;
 procedure OpenFile;
 procedure doOpen;
implementation
 const
 SFPutLeft = 82;
 SFPutTop = 50;
 myType = ‘ZZDC’;
 myCreator = ‘KCZZ’;
 var
 SFPutPt: Point;
 theReply: SFReply;
 refNum, resRef: integer;
 bytes: longint;
 title: str255;
 theLength: longint;
 theErr: OSErr;
 theItem: integer;
 oldHandle: Handle;

 function HandleError (theStr: Str255; theError: OSErr; chk, chksum: 
integer; CloseIt: boolean): boolean;
 var
 STemp: str255;
 begin
 if (theError <> noErr) then
 begin
 A_Message(theStr, ‘’, ‘’, ‘’, theItem);
 if CloseIt then
 begin
 theErr := FSClose(refNum);
 theErr := FlushVol(nil, VolRefNum);
 end;
 HandleError := true;
 end
 else if (chk <> chksum) then
 begin
 A_Message(theStr, ‘Checksum Error’, ‘’, ‘’, theItem);
 if CloseIt then
 begin
 theErr := FSClose(refNum);
 theErr := FlushVol(nil, VolRefNum);
 end;
 HandleError := true;
 end
 else
 HandleError := false;
 end;

 procedure doSave;
 begin
 if VolRefNum = 0 then
 begin {bad volume reference number}
 A_Message(‘Bad Volume Number’, ‘’, ‘’, ‘’, theItem);
 Exit(doSave);
 end
 else {good volume reference number}
 begin
 theErr := FSOpen(FileName, VolRefNum, refNum);
 if HandleError(‘Could Not Open File’, theErr, 0, 0, false) then
 Exit(doSave)
 else {file was open ok}
 begin
 theErr := SetFPos(refNum, FSFromStart, 0);
 if HandleError(‘Could Not Open File Position’, theErr, 0, 0, true) then
 Exit(doSave)
 else {ready to go}
 begin
 bytes := sizeof(DocRec);
 theLength := bytes;
 theErr := FSWrite(refNum, bytes, ptr(MyDoc));
 if HandleError(‘Trouble Writing To File’, theErr, bytes, theLength, 
true) then
 Exit(doSave);
 end;
 end; {of file open ok}
 theErr := FSClose(refNum);
 theErr := FlushVol(nil, VolRefNum);
 theErr := SetVol(nil, VolRefNum);
 CreateResFile(FileName);
 resRef := OpenResFile(FileName);
 if resRef = -1 then
 begin {could not be opened}
 A_Message(‘Could not write sound to’, FileName, ‘’, ‘’, theItem);
 Exit(doSave);
 end
 else {ready to write out sound}
 begin
 OldHandle := GetResource(‘snd ‘, 9000);
 if OldHandle <> nil then
 begin {existing sound to remove}
 RmveResource(OldHandle);
 DisposHandle(OldHandle);
 UpdateResFile(resRef);
 end;
 CreateSndResource(MyDoc^.StartValue, MyDoc^.EndValue);
 AddResource(MyHandle, ‘snd ‘, 9000, FileName);
 UpdateResFile(resRef);
 CloseResFile(resRef);
 DisposHandle(MyHandle);
 DisposHandle(Handle(MySoundHandle));
 end;
 end;{ of good vol ref num}
 end;  {of doSave}

 procedure doSaveAs;
 begin
 SetPt(SFPutPt, SFPutLeft, SFPutTop);
 GetWTitle(MyWindow, title);
 SFPutFile(SFPutPt, ‘Save Zoundz as ’, title, nil, theReply);
 if theReply.good then
 begin
 theErr := Create(theReply.fname, theReply.vRefNum, myCreator, myType);
 if theErr <> noErr then {duplicate or problem}
 begin
 if theErr = dupFNerr then
 begin {duplicate}
 theErr := FSDelete(theReply.fname, theReply.vRefNum);
 if theErr <> noErr then
 begin {cannot delete file}
 A_Message(‘Cannot Delete File.’, ‘’, ‘’, ‘’, theItem);
 Exit(doSaveAs);
 end;
 theErr := Create(theReply.fname, theReply.vRefNum, myCreator, myType);
 if theErr <> noErr then
 begin {error in creating after deleting duplicate}
 A_Message(‘Cannot Create’, theReply.fname, ‘’, ‘’, theItem);
 Exit(doSaveAs);
 end;
 end
 else { a problem}
 begin
 A_Message(‘Cannot Create’, theReply.fname, ‘’, ‘’, theItem);
 Exit(doSaveAs);
 end;
 end {duplicate or problem}
 else {ready to save}
 begin
 VolRefNum := theReply.vRefNum;
 FileName := theReply.fname;
 SetWTitle(MyWindow, FileName);
 theErr := FlushVol(nil, VolRefNum);
 doSave;
 end; {ready to save}
 end; {good reply}
 end;

 procedure OpenFile;
 begin
 theErr := FSOpen(FileName, VolRefNum, refNum);
 if HandleError(‘Could Not Open File’, theErr, 0, 0, false) then
 Exit(OpenFile)
 else {file was open ok}
 begin
 theErr := SetFPos(refNum, FSFromStart, 0);
 if HandleError(‘Could Not Open File Position’, theErr, 0, 0, true) then
 begin
 VolRefNum := 0;
 Exit(OpenFile);
 end
 else {ready to go}
 begin
 bytes := sizeof(DocRec);
 theLength := bytes;

 theErr := FSRead(refNum, bytes, ptr(MyDoc));
 if HandleError(‘Trouble Reading File’, theErr, bytes, theLength, true) 
then
 begin
 VolRefNum := 0;
 Exit(OpenFile);
 end;
 end;
 end; {of file open ok}
 theErr := FSClose(refNum);
 theErr := FlushVol(nil, VolRefNum);
 MyWindow := nil;
 NoteText := ‘1’;
 NumToString(MyDoc^.StartValue, StartText);
 NumToString(MyDoc^.EndValue, EndText);
 NumToString(MyDoc^.freq[1], FreqText);
 NumToString(MyDoc^.amp[1], AmpText);
 NumToString(MyDoc^.dur[1], DurText);
 NumToString(MyDoc^.timbre[1], TimbreText);
 NoteIndex := 1;
 DrawTool := 1;
 MySoundHandle := nil;
 MyHandle := nil;
 end;

 procedure doOpen;
 var
 theTypes: SFTypeList;
 begin
 SetPt(SFPutPt, SFPutLeft, SFPutTop);
 theTypes[0] := myType;
 SFGetFile(SFPutPt, ‘Open Zoundz file ’, nil, 1, theTypes, nil, theReply);
 VolRefNum := 0;
 if theReply.good then
 begin
 VolRefNum := theReply.vRefNum;
 FileName := theReply.fName;
 OpenFile;
 end;
 end;
end.
Listing:  MyPrintStuff.pas
unit MyPrintStuff;
interface
 uses
 PrintTraps, Sound, MyGlobals, MySound, Message;
 procedure doSetUp;
 procedure doPrint;
implementation
 var
 theItem: integer;
 procedure doSetUp;
 var
 confirmed: boolean;
 begin
 PrOpen;
 InitCursor;
 confirmed := PrValidate(ThePrintRec);
 confirmed := PrStlDialog(ThePrintRec);
 if PrError <> noErr then
 A_Message(‘Problem with style dialog’, ‘’, ‘’, ‘’, theItem)
 else
 PageRect := ThePrintRec^^.prInfo.rpage;
 PrClose;
 end;

 procedure PrintIt;
 var
 leftEdge, lineTop, lineBottom, lineSize: integer;
 title: str255;
 i: integer;
 procedure NumToHexString (n: longint; var s: str255);
 var
 d, i: integer;
 begin
 s := ‘’;
 i := 32;
 while i > 0 do
 begin
 d := BitAnd(n, 15);
 n := BitShift(n, -4);
 i := i - 4;
 if d < 10 then
 s := concat(chr(ord(‘0’) + d), s)
 else
 s := concat(chr(ord(‘A’) + d - 10), s);
 end;
 end;

 procedure LineFeed;
 begin
 lineTop := lineTop + lineSize;
 lineBottom := lineBottom + lineSize;
 MoveTo(leftEdge, lineBottom);
 end;

 procedure PrintHeader;
 var
 s1: str255;
 begin
 s1 := ‘Snd  name is “‘;
 s1 := concat(s1, title, ‘“‘);
 MoveTo(leftEdge, lineBottom);
 TextFace([bold]);
 DrawString(s1);
 TextFace([]);
 LineFeed;
 LineFeed;
 end;

 procedure PrintFirstPart;
 var
 s1, s2: str255;
 num: longint;
 begin
 num := MySoundHandle^^.format;
 s1 := ‘Snd  Format = ‘;
 NumToString(num, s2);
 s1 := concat(s1, s2);
 DrawString(s1);
 LineFeed;
 num := MySoundHandle^^.SynthCount;
 s1 := ‘Synthizers = ‘;
 NumToString(num, s2);
 s1 := concat(s1, s2);
 DrawString(s1);
 LineFeed;
 num := MySoundHandle^^.SynthType;
 s1 := ‘Snd  Format = ‘;
 NumToString(num, s2);
 s1 := concat(s1, s2, ‘ (noteSynth)’);
 DrawString(s1);
 LineFeed;
 num := MySoundHandle^^.SynthInit;
 s1 := ‘Snd  Initialization = ‘;
 NumToHexString(num, s2);
 s1 := concat(s1, ‘$’, s2);
 DrawString(s1);
 LineFeed;
 num := MySoundHandle^^.CommandCount;
 s1 := ‘Number of Sound Commands = ‘;
 NumToString(num, s2);
 s1 := concat(s1, s2);
 DrawString(s1);
 LineFeed;
 DrawString(‘  #  cmd     param1     param2  Description’);
 MoveTo(leftEdge, lineBottom + 2);
 LineTo(PageRect.right, lineBottom + 2);
 MoveTo(leftEdge, lineBottom);
 LineFeed;
 end;

 procedure PrintNote (i: integer);
 var
 s1, s2, s3: str255;
 num: longint;
 c, p1: integer;
 p2: longint;
 begin
 c := MySoundHandle^^.MySounds[i].cmd;
 p1 := MySoundHandle^^.MySounds[i].param1;
 p2 := MySoundHandle^^.MySounds[i].param2;
 num := i; {put index number}
 NumToString(num, s1);
 if i < 10 then
 s1 := concat(‘ ’, s1);
 if i < 100 then
 s1 := concat(‘ ’, s1);
 s1 := concat(s1, ‘   ’);
 NumToString(c, s2);
 if c < 10 then
 s2 := concat(‘ ’, s2);
 s1 := concat(s1, s2, ‘  $’);

 NumToHexString(p1, s2);
 NumToHexString(p2, s3);
 s1 := concat(s1, s2, ‘  $’, s3, ‘  ’);
 case c of
 quietCmd: 
 begin
 s1 := concat(s1, ‘quietCmd - The End’);
 end;
 timbreCmd: 
 begin
 s1 := concat(s1, ‘timbreCmd - Value ’);
 NumToString(p1, s2);
 s1 := concat(s1, s2);
 end;
 restCmd: 
 begin
 s1 := concat(s1, ‘restCmd - Rest ’);
 NumToString(p1, s2);
 s1 := concat(s1, s2, ‘ milliseconds’);
 end;
 noteCmd: 
 begin
 s1 := concat(s1, ‘noteCmd - Note ’);
 num := BitAnd(p2, $FF);
 NumToString(num, s2);
 s1 := concat(s1, s2, ‘, Amp. ’);
 num := BitAnd(BitShift(p2, -24), $FF);
 NumToString(num, s2);
 s1 := concat(s1, s2, ‘, Duration ’);
 NumToString(p1, s2);
 s1 := concat(s1, s2, ‘ milliseconds’);
 end;
 otherwise
 begin
 s1 := concat(s1, ‘Unknown sound command’);
 end;
 end;
 DrawString(s1);
 end;

 begin
{set up position}
 PenNormal;
 TextFont(monaco);
 TextFace([]);
 TextSize(9);
 lineTop := PageRect.top;
 lineSize := 12;
 lineBottom := lineTop + lineSize;
 leftEdge := 30;
 GetWTitle(MyWindow, title);
 PrOpenPage(ThePrintPort, nil); {open page}
 PrintHeader; {print header}
 PrintFirstPart; {print first part}
 for i := 1 to MySoundHandle^^.CommandCount do 
 begin
 if lineBottom > PageRect.bottom then
 begin  {if position is too great}
 PrClosePage(ThePrintPort);{close page}
 PrOpenPage(ThePrintPort, nil); {open page}
 lineTop := PageRect.top;
 lineBottom := lineTop + lineSize;
 PrintHeader; {print header}
 DrawString(‘  #  cmd     param1     param2  Description’);
 MoveTo(leftEdge, lineBottom + 2);
 LineTo(PageRect.right, lineBottom + 2);
 MoveTo(leftEdge, lineBottom);
 LineFeed;
 end;
 PrintNote(i);{print note}
 LineFeed;
 end;
 PrClosePage(ThePrintPort);{close page}
 end;

 procedure doPrint;
 var
 DoIt: boolean;
 myPrPort: TPPrPort;
 savePort: GrafPtr;
 copies, count: integer;
 begin
 GetPort(savePort);
 SetCursor(arrow);
 PrOpen;
 if PrError = noErr then
 begin
 DoIt := PrValidate(ThePrintRec);
 DoIt := PrJobDialog(ThePrintRec);
 if PrError <> noErr then
 A_Message(‘Problem with job dialog’, ‘’, ‘’, ‘’, theItem);
 if DoIt then
 begin {print document}
 SetCursor(theWatch^^);
 ThePrintPort := PrOpenDoc(ThePrintRec, nil, nil);
 if PrError = noErr then
 begin {ok port}
 CreateSndResource(MyDoc^.StartValue, MyDoc^.EndValue);
 copies := ThePrintRec^^.prJob.iCopies;
 PageRect := ThePrintRec^^.prInfo.rpage;
 for count := 1 to copies do
 begin {copies loop}
 PrintIt; {print the document}
 end; {copies loop}
 DisposHandle(MyHandle);
 DisposHandle(Handle(MySoundHandle));
 MyHandle := nil;
 MySoundHandle := nil;
 end
 else {bad port}
 A_Message(‘Open Document Error’, ‘’, ‘’, ‘’, theItem);
 PrCloseDoc(ThePrintPort);
 if (ThePrintRec^^.prJob.bJDocLoop = bSpoolLoop) and (PrError = noErr) 
then
 PrPicFile(ThePrintRec, nil, nil, nil, PrintStatus);
 end; {printing document}
 end;
 PrClose;
 SetPort(savePort);
 SetCursor(arrow)
 end;
end.
Listing:  About.pas
unit About;
interface
 procedure D_About;
implementation
 const   
 I_OK = 1;
 I_x = 2;
 I_x3 = 3;
 var
 ExitDialog: boolean;
 DoubleClick: boolean;
 MyPt: Point;
 MyErr: OSErr;

 procedure D_About;
 var
 GetSelection: DialogPtr;
 tempRect: Rect;
 DType: Integer;
 DItem: Handle;
 itemHit: Integer;
 procedure Refresh_Dialog;
 var
 rTempRect: Rect;
 begin
 SetPort(GetSelection);
 GetDItem(GetSelection, I_OK, DType, DItem, tempRect);
 PenSize(3, 3);
 InsetRect(tempRect, -4, -4);
 FrameRoundRect(tempRect, 16, 16);
 PenSize(1, 1);
 end;
 begin
 GetSelection := GetNewDialog(2, nil, Pointer(-1));
 tempRect := GetSelection^.portRect;
 tempRect.Top := ((screenBits.Bounds.Bottom - screenBits.Bounds.Top) 
- (tempRect.Bottom - tempRect.Top)) div 2;
 tempRect.Left := ((screenBits.Bounds.Right - screenBits.Bounds.Left) 
- (tempRect.Right - tempRect.Left)) div 2;
 MoveWindow(GetSelection, tempRect.Left, tempRect.Top, TRUE);
 ShowWindow(GetSelection);
 SelectWindow(GetSelection);
 SetPort(GetSelection);
 Refresh_Dialog;
 ExitDialog := FALSE;

 repeat
 ModalDialog(nil, itemHit);
 GetDItem(GetSelection, itemHit, DType, DItem, tempRect);
 if (ItemHit = I_OK) then
 begin
 ExitDialog := TRUE;
 end;
 until ExitDialog;
 DisposDialog(GetSelection);
 end;
end.    {End of unit}
Listing:  Untitled.pas
unit Untitled;
interface
 uses
 PrintTraps, Sound, MyGlobals, MySound;
 {Initialize us so all our routines can be activated}
 procedure Init_Untitled;
 {Close our window}
 procedure Close_Untitled (whichWindow: WindowPtr);
 {Open our window and draw everything}
 procedure Open_Untitled;
 {Update our window, someone uncovered a part of us}
 procedure Update_Untitled (whichWindow: WindowPtr);
 {Handle action to our window, like controls}
 procedure Do_Untitled (myEvent: EventRecord);
implementation
 const
 B_Play_Sound = 26;  {Button ID}
 CB_Timbre = 16;   {Checkbox IDs}
 CB_Duration = 15;
 CB_Amplitude = 14;
 CB_Frequency = 13;
 RB_Timbre = 20; {Radio IDs}
 RB_Duration = 19;
 RB_Amplitude = 18;
 RB_Frequency = 17;
 I_DurationScrollbar = 41;    {Scroll bar IDs}
 I_AmplitudeScrollbar = 40;
 I_FrequencyScrollbar = 39;
 I_TimbreScrollbar = 38;
 I_EndScrollbar = 31;
 I_StartScrollbar = 27;
 I_NoteScrollbar = 12;
 var
 tempRect: Rect;   {Temporary rectangle}
 sTemp: Str255;  {Get text entered, temp holding}
 C_EndScrollbar: ControlHandle;
 C_StartScrollbar: ControlHandle;
 C_NoteScrollbar: ControlHandle;
 R1Control: array[1..4] of ControlHandle;
 C_Play_Sound: ControlHandle;
 C_Timbre: ControlHandle;
 C_Duration: ControlHandle;
 C_Amplitude: ControlHandle;
 C_Frequency: ControlHandle;
 C_DurationScrollbar: ControlHandle;
 C_AmplitudeScrollbar: ControlHandle;
 C_FrequencyScrollbar: ControlHandle;
 C_TimbreScrollbar: ControlHandle;

{=================================}
 {Initialize us so all our routines can be activated}
 procedure Init_Untitled;
 var
 i: integer;
 begin    {Start of Window initialize routine}
 MyWindow := nil;
 NoteText := ‘1’; {Init Texts}
 StartText := ‘1’;
 EndText := ‘1’;
 FreqText := ‘0’;
 AmpText := ‘0’;
 DurText := ‘0’;
 TimbreText := ‘0’;
 NoteIndex := 1; {Init Miscellaneous}
 DrawTool := 1;
 MyDoc^.EndValue := 1;
 MyDoc^.StartValue := 1;
 MySoundHandle := nil;
 MyHandle := nil;
 volRefNum := 0;
 for i := 1 to 100 do {initialize arrays}
 begin
 MyDoc^.freq[i] := 0;
 MyDoc^.amp[i] := 0;
 MyDoc^.dur[i] := 0;
 MyDoc^.timbre[i] := 0;
 end;
 end; {End of Init_Untitled}

{=================================}
 {Close our window}
 procedure Close_Untitled;
 begin
 if (MyWindow <> nil) and ((MyWindow = whichWindow) or (ord4(whichWindow) 
= -1)) then
 begin
 DisposeWindow(MyWindow);
 MyWindow := nil;
 end;
 end; {End of Close_Untitled}

{=================================}
 {draws a square according to the pattern}
 procedure DrawSquare (vert, horiz: integer; thePat: pattern);
 var
 theSquare: rect;
 begin
 SetRect(theSquare, horiz, vert, horiz + 10, vert + 10);
 InSetRect(theSquare, 1, 1);
 PenNormal;
 FillRect(theSquare, thePat);
 FrameRect(theSquare);
 end; {of DrawSquare}

{=================================}
 {Takes a point and returns vertical and horizontal values}
 procedure Unconvert (thePoint: Point; range: integer; var value, column: 
integer);
 var
 Lvalue, Lrange: longint;
 begin
 if thePoint.h < 5 then {out of range}
 column := -1
 else {get column}
 column := (thePoint.h - 5) div 10;
 if column > 22 then {out of range}
 column := -1;
 if (thePoint.v < 5) or (thePoint.v > 260) then
 value := -1 {out of range}
 else
 begin {get value}
 Lvalue := thePoint.v - 5;
 Lrange := range;
 Lvalue := Lvalue * Lrange;
 value := LValue div 255;
 end;
 end; {of unconvert}

{=================================}
{returns vertical position}
 function Convert (value, range: integer): integer;
 var
 Lvalue, Lrange: longint;
 begin
 Lvalue := value;
 LRange := range;
 Convert := ((Lvalue * 245) div Lrange) + 5;
 end; {of Convert}

{=================================}
 {Update our window, someone uncovered a part of us}
 procedure UpDate_Untitled;
 var
 SavePort: WindowPtr;     {Place to save the last port}
 theValue, theTop, i, rangeStop, RangeStart: integer;
 begin
 if (MyWindow <> nil) and (MyWindow = whichWindow) then
 begin
 GetPort(SavePort);  {Save the current port}
 SetPort(MyWindow);  {Set the port to my window}
 TextFont(systemFont);{Set the font to draw in}

 { Draw DrawGraph Stuff}
 SetRect(TempRect, 245, 20, 355, 105);
 FrameRect(TempRect);{Frame this rectangle area}
 SetRect(tempRect, 265, 5, 345, 20);
 sTemp := ‘Draw Graph’;
 TextBox(Pointer(ord(@sTemp) + 1), length(sTemp), tempRect, teJustLeft);
 SetRect(tempRect, 248, 28, 258, 38);
 FillRect(tempRect, black);
 FrameRect(tempRect);
 SetRect(tempRect, 248, 48, 258, 58);
 FillRect(tempRect, dkgray);
 FrameRect(tempRect);
 SetRect(tempRect, 248, 68, 258, 78);
 FillRect(tempRect, gray);
 FrameRect(tempRect);
 SetRect(tempRect, 248, 88, 258, 98);
 FillRect(tempRect, ltgray);
 FrameRect(tempRect);
 FrameRect(tempRect);
 TextBox(Pointer(ord(@FreqText) + 1), length(FreqText), FreqRect, teJustLeft);
 TextBox(Pointer(ord(@AmpText) + 1), length(AmpText), AmpRect, teJustLeft);
 TextBox(Pointer(ord(@DurText) + 1), length(DurText), DurRect, teJustLeft);
 TextBox(Pointer(ord(@TimbreText) + 1), length(TimbreText), TimbreRect, 
teJustLeft);

 { Draw a rectangle, ViewGraphRect }
 SetRect(TempRect, 390, 190, 490, 275);{left,top,right,bottom}
 FrameRect(TempRect);{Frame this rectangle area}
 SetRect(tempRect, 400, 175, 475, 190);
 sTemp := ‘View Graph’;
 TextBox(Pointer(ord(@sTemp) + 1), length(sTemp), tempRect, teJustLeft);

 { Draw a rectangle, NotePallete }
 PenPat(white);
 PaintRect(NotePallete);
 PenNormal;
 FrameRect(NotePallete);{Frame this rectangle area}
 RangeStart := GetCtlValue(C_NoteScrollbar);
 RangeStop := RangeStart + 22;
 if RangeStop > 100 then
 RangeStop := 100;
 for i := RangeStart to RangeStop do
 begin
 if GetCtlValue(C_Timbre) = 1 then
 begin
 theValue := MyDoc^.timbre[i];
 theTop := Convert(theValue, 254);
 DrawSquare(theTop, (i - RangeStart) * 10 + 5, ltgray);
 end;
 if GetCtlValue(C_Duration) = 1 then
 begin
 theValue := MyDoc^.dur[i];
 theTop := Convert(theValue, 250);
 DrawSquare(theTop, (i - RangeStart) * 10 + 5, gray);
 end;
 if GetCtlValue(C_Amplitude) = 1 then
 begin
 theValue := MyDoc^.amp[i];
 theTop := Convert(theValue, 255);
 DrawSquare(theTop, (i - RangeStart) * 10 + 5, dkgray);
 end;
 if GetCtlValue(C_Frequency) = 1 then
 begin
 theValue := MyDoc^.freq[i];
 theTop := Convert(theValue, 128);
 DrawSquare(theTop, (i - RangeStart) * 10 + 5, black);
 end;
 end;

 {Music Selection Stuff}
 SetRect(tempRect, 255, 175, 365, 190);
 sTemp := ‘Music Selection’;
 TextBox(Pointer(ord(@sTemp) + 1), length(sTemp), tempRect, teJustLeft);
 SetRect(TempRect, 245, 190, 375, 275);
 FrameRect(TempRect);{Frame this rectangle area}
 TextBox(Pointer(ord(@EndText) + 1), length(EndText), EndRect, teJustLeft);
 TextBox(Pointer(ord(@StartText) + 1), length(StartText), StartRect, 
teJustLeft);
 SetRect(tempRect, 250, 235, 280, 250);
 sTemp := ‘End:’;
 TextBox(Pointer(ord(@sTemp) + 1), length(sTemp), tempRect, teJustLeft);
 SetRect(tempRect, 250, 195, 290, 210);
 sTemp := ‘Start:’;
 TextBox(Pointer(ord(@sTemp) + 1), length(sTemp), tempRect, teJustLeft);

 {Draw NoteIndex}
 SetRect(tempRect, 370, 5, 410, 20);
 sTemp := ‘Note:’;
 TextBox(Pointer(ord(@sTemp) + 1), length(sTemp), tempRect, teJustLeft);
 TextBox(Pointer(ord(@NoteText) + 1), length(NoteText), NoteRect, teJustLeft);
 TextFont(applFont);{Set the default application font}

 DrawControls(MyWindow);{Draw all the controls}
 SetPort(SavePort);  {Restore the old port}
 end;     {End for if (MyWindow<>nil)}
 end; {End of Update_Untitled}

{=================================}
 {Open our window and draw everything}
 procedure Open_Untitled;
 begin
 if (MyWindow = nil) then
 begin
 MyWindow := GetNewWindow(1, nil, Pointer(-1));
 SetPort(MyWindow);
 { Make a button, Play Sound }
 C_Play_Sound := GetNewControl(B_Play_Sound, MyWindow);
 { Make a checkboxes }
 C_Timbre := GetNewControl(CB_Timbre, MyWindow);
 C_Duration := GetNewControl(CB_Duration, MyWindow);
 C_Amplitude := GetNewControl(CB_Amplitude, MyWindow);
 C_Frequency := GetNewControl(CB_Frequency, MyWindow);
 { Make a radio buttons }
 R1Control[4] := GetNewControl(RB_Timbre, MyWindow);
 R1Control[3] := GetNewControl(RB_Duration, MyWindow);
 R1Control[2] := GetNewControl(RB_Amplitude, MyWindow);
 R1Control[1] := GetNewControl(RB_Frequency, MyWindow);
 { Make a scroll bars }
 C_DurationScrollbar := GetNewControl(I_DurationScrollbar, MyWindow);
 SetCtlValue(C_DurationScrollbar, MyDoc^.dur[NoteIndex]);
 C_AmplitudeScrollbar := GetNewControl(I_AmplitudeScrollbar, MyWindow);
 SetCtlValue(C_AmplitudeScrollbar, MyDoc^.amp[NoteIndex]);
 C_FrequencyScrollbar := GetNewControl(I_FrequencyScrollbar, MyWindow);
 SetCtlValue(C_FrequencyScrollbar, MyDoc^.freq[NoteIndex]);
 C_TimbreScrollbar := GetNewControl(I_TimbreScrollbar, MyWindow);
 SetCtlValue(C_TimbreScrollbar, MyDoc^.timbre[NoteIndex]);
 C_EndScrollbar := GetNewControl(I_EndScrollbar, MyWindow);
C_StartScrollbar := GetNewControl(I_StartScrollbar, MyWindow);
 SetCtlValue(C_EndScrollbar, MyDoc^.EndValue);
 SetCtlValue(C_StartScrollbar, MyDoc^.StartValue);
 C_NoteScrollbar := GetNewControl(I_NoteScrollbar, MyWindow);
 ShowWindow(MyWindow);
 SelectWindow(MyWindow);
 end
 else
 SelectWindow(MyWindow);
 end; {End of Open_Untitled}

{=================================}
 {Handle action to our window, like controls}
 procedure Do_Untitled;
 var
 RefCon: longint;
 code: integer;
 theValue: integer;
 whichWindow: WindowPtr;
 myPt: Point;
 theControl: ControlHandle;
 MyErr: OSErr;
 tempRect: rect;
 newValue, NewPosition: integer;

 procedure Do_A_Button;
 begin
 HiliteControl(theControl, 10);
 RefCon := GetCRefCon(theControl);
 case RefCon of  {Select correct button}
 B_Play_Sound:{Play Sound, button}
 begin    {start for this button}
 CreateSndResource(GetCtlValue(C_StartScrollbar), GetCtlValue(C_EndScrollbar));
 MyErr := SndPlay(nil, MyHandle, false);
 DisposHandle(MyHandle);
 DisposHandle(Handle(MySoundHandle));
 MyHandle := nil;
 MySoundHandle := nil;
 end;     {end for this button}
 otherwise 
 begin    {start}
 end;     {end of otherwise}
 end;     {end of case}
 HiliteControl(theControl, 0);{Lighten the button}
 end;     {Handle a button being pressed}

 procedure Do_A_Checkbox; 
 var
 Index: integer;   {Index used for radios}
 procedure Clear1RadioGroup;
 var
 Index: integer;   {Index used for radios}
 begin    {Start of the clear routine}
 for Index := 1 to 4 do   {Step thru all radios}
 SetCtlValue(R1Control[Index], 0);
 end;     {End of the clear routine}
 begin    {Handle a checkbox being pressed}
 RefCon := GetCRefCon(theControl);
 theValue := GetCtlValue(theControl);
 theValue := (theValue + 1) mod 2;
 InvalRect(NotePallete);
 case RefCon of 
 CB_Timbre, CB_Duration, CB_Amplitude, CB_Frequency:
 begin
 SetCtlValue(theControl, theValue);
 end;     {end for this checkbox}
 RB_Timbre:    {Timbre , radio button}
 begin    {start for this radio button}
 DrawTool := 4;
 Clear1RadioGroup;
 SetCtlValue(theControl, 1);{Select this Radio}
 SetCtlValue(C_Timbre, 1);
 end;
 RB_Duration:{Duration , radio button}
 begin    {start for this radio button}
 DrawTool := 3;
 Clear1RadioGroup;{Clear Radio values}
 SetCtlValue(theControl, 1);{Select this Radio}
 SetCtlValue(C_Duration, 1);
 end;
 RB_Amplitude:{Amplitude , radio button}
 begin    {start for this radio button}
 DrawTool := 2;
 Clear1RadioGroup;
 SetCtlValue(theControl, 1);{Select this Radio}
 SetCtlValue(C_Amplitude, 1);
 end;
 RB_Frequency:{Frequency , radio button}
 begin    {start for this radio button}
 DrawTool := 1;
 Clear1RadioGroup;
 SetCtlValue(theControl, 1);{Select this Radio}
 SetCtlValue(C_Frequency, 1);
 end;
 otherwise
 begin
 end; 
 end;     {end of case}
 end; {Handle a checkbox being pressed}

 procedure Do_A_ScrollBar (code: integer);
 procedure HandleWScrollBar (code, Start, Stop, Increment, LIncrement: 
integer; theControl: ControlHandle);
 var
 theValue: integer;{Value of the scrollbar}
 MaxTick: longint; {Timer for repeat scrolling}
 FirstTime: boolean;      {Flag to start scrolling}
 begin
 FirstTime := TRUE;
 while (StillDown or FirstTime) do
 begin    {Timer used for repeat scrolling}
 FirstTime := FALSE;
 HiliteControl(theControl, code);
 theValue := GetCtlValue(theControl);
 if (code = inUpButton) then
 begin
 theValue := theValue - Increment;
 if (theValue < Start) then
 theValue := Start;
 end;
 if (code = inDownButton) then
 begin
 theValue := theValue + Increment;
 if (theValue > Stop) then
 theValue := Stop;{Bump at the stop value}
 end;
 if (code = inPageUp) then
 begin
 theValue := theValue - LIncrement;
 if (theValue < Start) then
 theValue := Start;
 end;
 if (code = inPageDown) then
 begin
 theValue := theValue + LIncrement;
 if (theValue > Stop) then
 theValue := Stop;{Bump at the Stop value}
 end;
 if (code = inThumb) then
 begin
 code := TrackControl(theControl, myPt, nil);{Let the OS drag it around}
 theValue := GetCtlValue(theControl);
 end;
 SetCtlValue(theControl, theValue);{Set new state}
 MaxTick := TickCount + 9;
 repeat {Start of delay routine}
 until not (Button) or (TickCount > MaxTick);{Exit when time up or mouse 
button up}
 HiliteControl(theControl, 0);{Lighten the arrow}
 end;   {End for StillDown}
 end;{End of handle scroll bar}

 begin   {Handle a ScrollBar being pressed}
 RefCon := GetCRefCon(theControl);{get control refcon}
 TempRect := NotePallete;
 TempRect.left := 6;
 TempRect.right := TempRect.left + 8;
 case RefCon of  {Select correct scrollbar}
 I_DurationScrollbar:{DurationScrollbar, scroll bar}
 begin
 dirty := true;
 HandleWScrollBar(code, 0, 250, 1, 10, theControl);
 theValue := GetCtlValue(theControl);
 MyDoc^.dur[NoteIndex] := theValue;
 NumToString(theValue, DurText);
 InvalRect(DurRect);
 end;
 I_AmplitudeScrollbar:{AmplitudeScrollbar, scroll bar}
 begin 
 dirty := true;
 HandleWScrollBar(code, 0, 255, 1, 10, theControl);
 theValue := GetCtlValue(theControl);
 MyDoc^.amp[NoteIndex] := theValue;
 NumToString(theValue, AmpText);
 InvalRect(AmpRect);
 end;
 I_FrequencyScrollbar:{FrequencyScrollbar, scroll bar}
 begin 
 dirty := true;
 HandleWScrollBar(code, 0, 128, 1, 10, theControl);
 theValue := GetCtlValue(theControl);
 MyDoc^.freq[NoteIndex] := theValue;
 if theValue < 128 then
 NumToString(theValue, FreqText)
 else
 FreqText := ‘Rest’;
 InvalRect(FreqRect);
 end;
 I_TimbreScrollbar:{TimbreScrollbar, scroll bar}
 begin
 dirty := true;
 HandleWScrollBar(code, 0, 254, 1, 10, theControl);
 theValue := GetCtlValue(theControl);
 MyDoc^.timbre[NoteIndex] := theValue;
 NumToString(theValue, TimbreText);
 InvalRect(TimbreRect);
 end; 
 I_EndScrollbar:{EndScrollbar, scroll bar}
 begin 
 dirty := true;
 HandleWScrollBar(code, GetCtlValue(C_StartScrollbar), 100, 1, 10, theControl);
 theValue := GetCtlValue(theControl);
 MyDoc^.EndValue := theValue;
 NumToString(theValue, EndText);
 TempRect := EndRect;
 end;
 I_StartScrollbar:{StartScrollbar, scroll bar}
 begin    {start for this scroll bar}
 dirty := true;
 HandleWScrollBar(code, 1, 100, 1, 10, theControl);
 theValue := GetCtlValue(theControl);
 MyDoc^.StartValue := theValue;
 NumToString(theValue, StartText);
 InvalRect(StartRect);
 SetCtlMin(C_EndScrollbar, theValue);
 theValue := GetCtlValue(C_EndScrollbar);
 MyDoc^.EndValue := theValue;
 NumToString(theValue, EndText);
 TempRect := EndRect;
 end;
 I_NoteScrollbar:{NoteScrollbar, scroll bar}
 begin 
 HandleWScrollBar(code, 1, 100, 1, 10, theControl);
 theValue := GetCtlValue(theControl);
 NoteIndex := theValue;
 NumToString(theValue, NoteText);
 InvalRect(NoteRect);
 SetCtlValue(C_DurationScrollbar, MyDoc^.dur[NoteIndex]);
 SetCtlValue(C_FrequencyScrollbar, MyDoc^.freq[NoteIndex]);
 SetCtlValue(C_AmplitudeScrollbar, MyDoc^.amp[NoteIndex]);
 SetCtlValue(C_TimbreScrollbar, MyDoc^.timbre[NoteIndex]);
 NumToString(MyDoc^.timbre[NoteIndex], TimbreText);
 NumToString(MyDoc^.dur[NoteIndex], durText);
 NumToString(MyDoc^.amp[NoteIndex], ampText);
 if MyDoc^.freq[NoteIndex] < 128 then
 NumToString(MyDoc^.freq[NoteIndex], FreqText)
 else
 FreqText := ‘Rest’;
 InvalRect(durRect);
 InvalRect(freqRect);
 InvalRect(ampRect);
 InvalRect(timbreRect);
 TempRect := NotePallete;
 InsetRect(TempRect, 1, 1);
 end;     {end for this scroll bar}
 otherwise
 begin 
 end;
 end;     {end of case}
 InvalRect(TempRect);
 end;    {Handle a ScrollBar being pressed}

 begin    {Start of Window handler}
 if (MyWindow <> nil) then
 begin
 code := FindWindow(myEvent.where, whichWindow);
 if (myEvent.what = MouseDown) and (MyWindow = whichWindow) then
 begin
 myPt := myEvent.where;{Get mouse position}
 GlobalToLocal(myPt);
 end;
 if (MyWindow = whichWindow) and (code = inContent) then
 begin
 code := FindControl(myPt, whichWindow, theControl);
 if (code = inUpButton) or (code = inDownButton) or (code = inThumb) 
or (code = inPageDown) or (code = inPageUp) then
 Do_A_ScrollBar(code);{Do scrollbars}
 if (code <> 0) then{Check type of control}
 code := TrackControl(theControl, myPt, nil);{Track the control}
 if code = inButton then
 Do_A_Button;{Do buttons}
 if code = inCheckBox then
 Do_A_Checkbox;{Do checkboxes}
 if PtInRect(myPt, NotePallete) then
 repeat
 dirty := true;
 case DrawTool of
 1: 
 UnConvert(myPt, 128, newValue, NewPosition);
 2: 
 UnConvert(myPt, 255, newValue, NewPosition);
 3: 
 UnConvert(myPt, 250, newValue, NewPosition);
 4: 
 UnConvert(myPt, 254, newValue, NewPosition);
 end;
 if (newValue <> -1) and (newPosition <> -1) then
 begin {still in NotePallete?}
 case DrawTool of
 1: 
 begin
MyDoc^.freq[GetCtlValue(C_NoteScrollbar) + NewPosition] := newValue;
 if NewPosition = 0 then
 begin
 SetCtlValue(C_FrequencyScrollbar, newValue);
 if newValue <> 128 then
 NumToString(newValue, FreqText)
 else
 FreqText := ‘Rest’;
 InvalRect(FreqRect);
 end;
 end;
 2: 
 begin
 MyDoc^.amp[GetCtlValue(C_NoteScrollbar) + NewPosition] := newValue;
 if NewPosition = 0 then
 begin
 SetCtlValue(C_AmplitudeScrollbar, newValue);
 NumToString(newValue, AmpText);
 InvalRect(AmpRect);
 end;
 end;
 3: 
 begin
 MyDoc^.dur[GetCtlValue(C_NoteScrollbar) + NewPosition] := newValue;
 if NewPosition = 0 then
 begin
 SetCtlValue(C_DurationScrollbar, newValue);
 NumToString(newValue, DurText);
 InvalRect(DurRect);
 end;
 end;
 4: 
 begin  MyDoc^.timbre[GetCtlValue(C_NoteScrollbar) + NewPosition] := 
newValue;
 if NewPosition = 0 then
 begin
 SetCtlValue(C_TimbreScrollbar, newValue);
 NumToString(newValue, TimbreText);
 InvalRect(TimbreRect);
 end;
 end;
 end; { of Case}
 SetRect(TempRect, (NewPosition * 10 + 6), 6, (NewPosition * 10 + 5) 
+ 9, 259);
 InvalRect(TempRect);
 end; {end of still in NotePallete}
 BeginUpdate(MyWindow);
 Update_Untitled(MyWindow);
 EndUpdate(MyWindow);
 GetMouse(myPt);
 until not (StillDown);
 end;     {End for if (MyWindow=whichWindow)}
 end;     {End for if (MyWindow<>nil)}
 end;     {End of procedure}
end. {End of unit}
Listing:  HandleTheMenus.pas
unit HandleTheMenus;
interface
 uses
 PrintTraps, Message, save_changes, About, Untitled, Sound, MyGlobals, 
MyFileStuff, MyPrintStuff;
 procedure AdjustMenus;
 procedure Handle_My_Menu (var doneFlag: boolean; theMenu, theItem: integer);{Handle 
menu selection}
implementation
 procedure AdjustMenus;
 begin
 if (FrontWindow <> MyWindow) then
 begin {Something up there}
 DisableItem(M_Extend, 0);
 EnableItem(M_Edit, 0);
 DisableItem(M_File, MI_Open);
 DisableItem(M_File, MI_New);
 DisableItem(M_File, MI_Close);
 DisableItem(M_File, MI_Save);
 DisableItem(M_File, MI_Save_As);
 DisableItem(M_File, MI_Page_Setup);
 DisableItem(M_File, MI_Print);
 end
 else if MyWindow <> nil then
 begin {My Window up there}
 EnableItem(M_Extend, 0);
 DisableItem(M_Edit, 0);
 DisableItem(M_File, MI_Open);
 DisableItem(M_File, MI_New);
 EnableItem(M_File, MI_Close);
 EnableItem(M_File, MI_Save);
 EnableItem(M_File, MI_Save_As);
 EnableItem(M_File, MI_Page_Setup);
 EnableItem(M_File, MI_Print);
 end
 else
 begin {nothing up there}
 VolRefNum := 0; {no need to save any changes}
 DisableItem(M_Extend, 0);
 DisableItem(M_Edit, 0);
 EnableItem(M_File, MI_Open);
 EnableItem(M_File, MI_New);
 DisableItem(M_File, MI_Close);
 DisableItem(M_File, MI_Save);
 DisableItem(M_File, MI_Save_As);
 DisableItem(M_File, MI_Page_Setup);
 DisableItem(M_File, MI_Print);
 end;
 end;
 procedure Handle_My_Menu;
 var
 DNA: integer;
 BoolHolder: boolean;
 DAName, title: Str255;
 SavePort: GrafPtr;
 i, theValue: integer;
 begin    {Start of procedure}
 case theMenu of {Do selected menu list}
 L_Apple: 
 begin
 case theItem of
 MI_About_Zoundz: 
 begin
 D_About;
 end;
 otherwise{Handle the DAs}
 begin
 GetPort(SavePort);
 GetItem(AppleMenu, theItem, DAName);
 DNA := OpenDeskAcc(DAName);
 SetPort(SavePort);
 end;
 end;     {End of item case}
 end;     {End for Apple Menu list}
 L_File: 
 begin
 case theItem of
 MI_New: 
 begin
 Init_Untitled;
 Open_Untitled;
 dirty := false;
 end;
 MI_Open: 
 begin
 doOpen;
 if VolRefNum <> 0 then
 begin
 Open_Untitled;
 SetWTitle(MyWindow, FileName);
 dirty := false;
 end;
 end;
 MI_Close: 
 begin
 if dirty then
 begin {need saving}
 GetWTitle(MyWindow, title);
 ParamText(title, ‘’, ‘’, ‘’);
 theValue := D_save_changes;
 if (theValue = I_Yes) then
 begin {wants to save changes}
 if VolRefNum = 0 then
 doSaveAs
 else
 doSave;
 if VolRefNum <> 0 then 
 Close_Untitled(MyWindow);
 end
 else if theValue = I_No then
 Close_Untitled(MyWindow);
 end {of needing saved}
 else {no need to save}
 Close_Untitled(MyWindow);
 end;
 MI_Save: 
 begin
 if VolRefNum = 0 then
 doSaveAs
 else
 doSave;
 if VolRefNum <> 0 then
 dirty := false;
 end;
 MI_Save_As: 
 begin
 doSaveAs;
 if VolRefNum <> 0 then
 dirty := false;
 end;
 MI_Page_Setup: 
 begin
 doSetUp;
 end;
 MI_Print: 
 begin
 doPrint;
 end;
 MI_Quit: 
 begin
 if (MyWindow <> nil) and dirty then
 begin {need saving}
 GetWTitle(MyWindow, title);
 ParamText(title, ‘’, ‘’, ‘’);
 theValue := D_save_changes;
 case theValue of
 I_Yes: 
 begin
 if VolRefNum = 0 then
 doSaveAs
 else
 doSave;
 if VolRefNum <> 0 then 
 doneFlag := TRUE;
 end; {of Yes}
 I_No: 
 doneFlag := True;
 otherwise
 ;
 end; {of case}
 end {of needing saved}
 else {no need to save}
 doneFlag := TRUE;
 end;
 otherwise
 ;
 end; {of item list}
 end; {of File Menu List }
 L_Edit: 
 begin
 BoolHolder := SystemEdit(theItem - 1); {DA Editing}
 end;
 L_Extend: 
 begin
 case theItem of{Handle extending values}
 MI_Frequency: 
 begin
 theValue := MyDoc^.freq[NoteIndex];
 for i := MyDoc^.StartValue to MyDoc^.EndValue do
 MyDoc^.freq[i] := theValue;
 end; {of Frequency}
 MI_Amplitude: 
 begin
 theValue := MyDoc^.amp[NoteIndex];
 for i := MyDoc^.StartValue to MyDoc^.EndValue do
 MyDoc^.amp[i] := theValue;
 end; {of Amplitude}
 MI_Duration: 
 begin
 theValue := MyDoc^.dur[NoteIndex];
 for i := MyDoc^.StartValue to MyDoc^.EndValue do
 MyDoc^.dur[i] := theValue;
 end; {of duration}
 MI_Timbre: 
 begin
 theValue := MyDoc^.timbre[NoteIndex];
 for i := MyDoc^.StartValue to MyDoc^.EndValue do
 MyDoc^.timbre[i] := theValue;
 end; {of timbre}
 otherwise
 ;
 end; { of items in Extend Menu}
 InvalRect(NotePallete);
 end; {of Extend Menu List}
 otherwise
 ;
 end;     {End for the Menus}

 HiliteMenu(0);  {Turn menu selection off}
 end;     {End of procedure Handle_My_Menu}
end.    {End of unit}

Listing:  Zoundz.pas
program Zoundz;
{Program name:  Zoundz.Pas  }
{Function:  Allows creation of ‘snd ‘ resource ID=9000.  }
{History: 4/17/89 Original by Prototyper.   }
{Modified: 5/1/89 by Kirk Chase}
 uses
 PrintTraps, Message, save_changes, About, Untitled, InitTheMenus, HandleTheMenus, 
Sound, MyGlobals, MyFileStuff, MyPrintStuff;
 const
 WNETrapNum = $60;
 UnImplTrapNum = $9F;
 MultiEvt = 15;
 bit0 = 31;
 GrayRgnLowMemGlobal = $9EE;
 var    {Main variables}
 myEvent: EventRecord;
 ResumePeek: WindowPeek;
 theWorld: SysEnvRec;
 doneFlag, DoIt, WNE, sysResult: boolean;
 code, theValue: integer;
 whichWindow: WindowPtr;
 dragRect: Rect;
 mResult: longint;
 theMenu, theItem: integer;
 chCode: integer;
 ch: char;
 myPt: Point;
 title: str255;
 nDocs, message, index, sleep: integer;
 theFile: AppFile;
 theErr: OSErr;

 procedure InitMac; {initializes Macintosh}
 begin
 MoreMasters;    {This reserves space for more handles}
 InitGraf(@thePort); {Quickdraw Init}
 InitFonts;      {Font manager init}
 InitWindows;    {Window manager init}
 InitMenus;      {Menu manager init}
 TEInit;  {Text edit init}
 InitDialogs(nil);   {Dialog manager}
 FlushEvents(everyEvent, 0);{Clear out all events}
 InitCursor;     {Make an arrow cursor}
 end;

 procedure InitApp; {initialize application}
 var
 tempHandle: handle;
 begin
 doneFlag := FALSE;  {Do not exit program yet}
 Init_My_Menus;  {Initialize menu bar}
 MyDoc := DocPtr(NewPtr(sizeof(DocRec))); {get doc}
 ThePrintRec := nil; {print initialization}
 PrOpen;
 tempHandle := NewHandle(sizeof(TPrint));
 ThePrintRec := THPrint(tempHandle);
 PrintDefault(ThePrintRec);
 PageRect := ThePrintRec^^.prInfo.rpage;
 PrClose;
 dragRect := screenbits.bounds;{Get drag area}
 SetRect(dragRect, dragRect.Left + 10, dragRect.Top + 25, dragRect.Right 
- 10, dragRect.Bottom - 10);
 SetRect(NotePallete, 5, 5, 235, 260);{set rectangles}
 SetRect(EndRect, 345, 235, 370, 250);
 SetRect(StartRect, 345, 195, 370, 210);
 SetRect(TimbreRect, 465, 85, 490, 100);
 SetRect(DurRect, 465, 65, 490, 80);
 SetRect(AmpRect, 465, 45, 490, 60);
 SetRect(FreqRect, 465, 25, 500, 40);
 SetRect(NoteRect, 420, 5, 445, 20);
 Dirty := false;
 theWatch := GetCursor(watchCursor);
 HLock(Handle(theWatch));
 theSquare := GetCursor(crossCursor);
 HLock(Handle(theSquare));
 theErr := SysEnvirons(1, theWorld);
 if (theWorld.machineType >= 0) and (NGetTrapAddress(WNETrapNum, ToolTrap) 
= NGetTrapAddress(UnImplTrapNum, ToolTrap)) then
 WNE := false
 else
 WNE := true;
 sleep := 10;
 end;

 procedure AdjustCursor; {set cursor}
 begin
 if (FrontWindow = MyWindow) and (MyWindow <> nil) then
 begin {our window in front}
 GetMouse(myPt);
 if PtInRect(myPt, NotePallete) then
 SetCursor(theSquare^^) {over note pallete}
 else
 SetCursor(arrow); {over other stuff}
 end
 else
 SetCursor(arrow);
 end;

begin   {Start of main body}
 InitMac;
 InitApp;
 {finder startup}
 CountAppFiles(message, nDocs);
 if nDocs = 0 then
 begin {no files to open}
 Init_Untitled;  {Initialize the window routines}
 Open_Untitled;  {Open window routines at program start}
 end
 else
 begin {files to print or open}
 if message = appPrint then
 begin {print docs}
 for index := 1 to nDocs do
 begin {Loop through docs}
 GetAppFiles(index, theFile);
 if theFile.fType = ‘ZZDC’ then
 begin {my file}
 VolRefNum := theFile.vRefNum;
 FileName := theFile.fName;
 OpenFile;
 if VolRefNum <> 0 then
 begin
 Open_Untitled;
 SetWTitle(MyWindow, FileName);
 doPrint;
 Close_Untitled(MyWindow);
 end;
 end;
 ClrAppFiles(index);
 end; {Loop through docs}
 doneFlag := true; {quit when done}
 end {print docs}
 else
 begin {open first file, can’t open multiple}
 GetAppFiles(1, theFile);
 if theFile.fType = ‘ZZDC’ then
 begin
 VolRefNum := theFile.vRefNum;
 FileName := theFile.fName;
 OpenFile;
 if VolRefNum = 0 then
 begin
 Init_Untitled; {Init window routines}
 FileName := ‘Untitled’;
 end;
 Open_Untitled;  {Open window routines}
 SetWTitle(MyWindow, FileName);
 for index := 1 to nDocs do
 ClrAppFiles(index);
 end;
 end;
 end;

 repeat   {Start of main event loop}
 AdjustMenus;
 AdjustCursor;
 if WNE then
 DoIt := WaitNextEvent(everyEvent, myEvent, sleep, nil)
 else
 begin
 SystemTask;
 DoIt := GetNextEvent(everyEvent, myEvent);
 end;
 if DoIt then{If event then...}
 begin    {Start handling the event}
 code := FindWindow(myEvent.where, whichWindow);
 case myEvent.what of{Decide type of event}
 MouseDown:{Mouse button pressed}
 begin
 if (code = inMenuBar) then 
 begin
 mResult := MenuSelect(myEvent.Where);
 theMenu := HiWord(mResult);
 theItem := LoWord(mResult);
 Handle_My_Menu(doneFlag, theMenu, theItem);
 end;{End of inMenuBar}
 if (code = InDrag) then
 begin
 DragWindow(whichWindow, myEvent.where, dragRect);
 end;
 if (code = inGoAway) then
 begin
 if TrackGoAway(whichWindow, myEvent.where) then
 begin
 if dirty then
 begin {need saving}
 GetWTitle(MyWindow, title);
 ParamText(title, ‘’, ‘’, ‘’);
 theValue := D_save_changes;
 if (theValue = I_Yes) then
 begin {wants to save changes}
 if VolRefNum = 0 then
 doSaveAs
 else
 doSave;
 if VolRefNum <> 0 then
 Close_Untitled(MyWindow);
 end
 else if theValue = I_No then
 Close_Untitled(MyWindow);
 end {of needing saved}
 else {no need to save}
 Close_Untitled(MyWindow);
 end;
 end;{End of InGoAway}
 if (code = inContent) then
 begin
 if (whichWindow <> FrontWindow) then
 SelectWindow(whichWindow)
 else
 begin
 SetPort(whichWindow);
 Do_Untitled(myEvent);
 end;
 end;{End of inContent}
 if (code = inSysWindow) then
 SystemClick(myEvent, whichWindow);
 end;     {End of MouseDown}
 KeyDown, AutoKey:{Handle key inputs}
 begin
 with myevent do
 begin
 chCode := BitAnd(message, CharCodeMask);
 ch := CHR(chCode);
 if (Odd(modifiers div CmdKey)) then
 begin
 mResult := MenuKey(ch);
 theMenu := HiWord(mResult);
 theItem := LoWord(mResult);
 if (theMenu <> 0) then
 Handle_My_Menu(doneFlag, theMenu, theItem);
 end;
 end;
 end;     {End for KeyDown,AutoKey}
 UpDateEvt:{Update event for a window}
 begin
 whichWindow := WindowPtr(myEvent.message);
 BeginUpdate(whichWindow);
 Update_Untitled(whichWindow);
 EndUpdate(whichWindow);
 end;     {End of UpDateEvt}
 DiskEvt:   {Disk inserted event}
 begin
 if (HiWord(myevent.message) <> noErr) then
 begin{due to unformatted diskette inserted}
 myEvent.where.h := ((screenbits.bounds.Right - screenbits.bounds.Left) 
div 2) - (304 div 2);
 myEvent.where.v := ((screenbits.bounds.Bottom - screenbits.bounds.Top) 
div 3) - (104 div 2);
 InitCursor;
 chCode := DIBadMount(myEvent.where, myevent.message);{Let the OS handle 
the diskette}
 end;
 end;   {End of DiskEvt}
 ActivateEvt:{Window activated event}
 begin
 whichWindow := WindowPtr(myevent.message);
 if odd(myEvent.modifiers) then
 begin{Handle the activate}
 SelectWindow(whichWindow);
 end;
 end;     {End of ActivateEvt}
 MultiEvt: 
 begin
 if Odd(myEvent.message) then
 begin {resume event}
 if FrontWindow = MyWindow then
 begin
 SetPort(MyWindow);
 InvalRect(MyWindow^.portRect);
 end
 else if FrontWindow <> nil then
 begin
 ResumePeek := WindowPeek(FrontWindow);
 if ResumePeek^.windowKind < 0 then
 begin
 myEvent.what := activateEvt;
 BitSet(@myEvent.modifiers, bit0);
 sysResult := SystemEvent(myEvent);
 end;
 end;
 end {of resume event}
 else {suspend event}
 begin
 if FrontWindow = MyWindow then
 begin
 SetPort(MyWindow);
 InvalRect(MyWindow^.portRect);
 end
 else if FrontWindow <> nil then
 begin
 ResumePeek := WindowPeek(FrontWindow);
 if ResumePeek^.windowKind < 0 then
 begin
 myEvent.what := activateEvt;
 BitClr(@myEvent.modifiers, bit0);
 sysResult := SystemEvent(myEvent);
 end;
 end;
 end;
 end; {of MultiEvt}
 otherwise
 ;
 end;     {End of case}
 end;     {end of GetNextEvent}
 until doneFlag; {End of the event loop}
end.    {End of the program}
Listing:  Zoundz.r
* RMaker resource file sources.
* File: Zoundz.R

Zoundz.RSRC
????????

include CursIcons
include AppStuff

Type DLOG

 ,3     ;;Resource ID
save changes     ;;Dialog title
50  120  188  365    ;;Top Left Bottom Right
Visible NoGoAway     ;;Visible GoAway 
1       ;;ProcID, dialog def ID
3       ;;Refcon, reference value
3       ;;ID of item list

Type DITL

 ,3     ;;Resource ID
4       ;;Number of controls in list

Button  Enabled  ;;Push button
70  30  90  100  ;;Top Left Bottom Right
Yes     ;;message

Button  Enabled  ;;Push button
100  150  120  220   ;;Top Left Bottom Right
Cancel  ;;message

Button  Enabled  ;;Push button
100  30  120  100    ;;Top Left Bottom Right
No      ;;message

StaticText  ;;Static text
10  30  55  220  ;;Top Left Bottom Right
Save changes to ^0?  ;;message

Type WIND

 ,1     ;;Resource ID
Untitled    ;;Window title
42  5  337  506  ;;Top Left Bottom Right
InVisible  GoAway    ;;Visible GoAway 
4       ;;ProcID, Window def ID
1       ;;Refcon, reference value

Type CNTL

   ,26  ;;Resource ID
Play Sound  ;;Title for a Button
120  245  160  490   ;;Top Left Bottom Right
Visible ;;Initially visible 
0       ;;ProcID (Control definition ID)
26      ;;RefCon (reference value)
0 1 0   ;;Min Max Value

   ,16  ;;Resource ID
Timbre  ;;Title for a Checkbox
255  395  270  485   ;;Top Left Bottom Right
Visible ;;Initially visible 
1       ;;ProcID (Control definition ID)
16      ;;RefCon (reference value)
0 1 1   ;;Min Max Value

   ,15  ;;Resource ID
Duration    ;;Title for a Checkbox
235  395  250  485   ;;Top Left Bottom Right
Visible ;;Initially visible 
1       ;;ProcID (Control definition ID)
15      ;;RefCon (reference value)
0 1 1   ;;Min Max Value

   ,14  ;;Resource ID
Amplitude   ;;Title for a Checkbox
215  395  230  485   ;;Top Left Bottom Right
Visible ;;Initially visible 
1       ;;ProcID (Control definition ID)
14      ;;RefCon (reference value)
0 1 1   ;;Min Max Value

   ,13  ;;Resource ID
Frequency   ;;Title for a Checkbox
195  395  210  485   ;;Top Left Bottom Right
Visible ;;Initially visible 
1       ;;ProcID (Control definition ID)
13      ;;RefCon (reference value)
0 1 1   ;;Min Max Value

   ,20  ;;Resource ID
Timbre  ;;Title for a RadioButton
85  260  100  350    ;;Top Left Bottom Right
Visible ;;Initially visible 
2       ;;ProcID (Control definition ID)
20      ;;RefCon (reference value)
0 1 0   ;;Min Max Value

   ,19  ;;Resource ID
Duration    ;;Title for a RadioButton
65  260  80  350     ;;Top Left Bottom Right
Visible ;;Initially visible 
2       ;;ProcID (Control definition ID)
19      ;;RefCon (reference value)
0 1 0   ;;Min Max Value

   ,18  ;;Resource ID
Amplitude   ;;Title for a RadioButton
45  260  60  350     ;;Top Left Bottom Right
Visible ;;Initially visible 
2       ;;ProcID (Control definition ID)
18      ;;RefCon (reference value)
0 1 0   ;;Min Max Value

   ,17  ;;Resource ID
Frequency   ;;Title for a RadioButton
25  260  40  350     ;;Top Left Bottom Right
Visible ;;Initially visible 
2       ;;ProcID (Control definition ID)
17      ;;RefCon (reference value)
0 1 1   ;;Min Max Value

   ,41  ;;Resource ID
DurationScrollbar    ;;Title for a Scrollbar
65  360  81  460     ;;Top Left Bottom Right
Visible ;;Initially visible 
16      ;;ProcID (Control definition ID)
41      ;;RefCon (reference value)
0  250  0   ;;Min Max Value

   ,40  ;;Resource ID
AmplitudeScrollbar   ;;Title for a Scrollbar
45  360  61  460     ;;Top Left Bottom Right
Visible ;;Initially visible 
16      ;;ProcID (Control definition ID)
40      ;;RefCon (reference value)
0  255  0   ;;Min Max Value

   ,39  ;;Resource ID
FrequencyScrollbar   ;;Title for a Scrollbar
25  360  41  460     ;;Top Left Bottom Right
Visible ;;Initially visible 
16      ;;ProcID (Control definition ID)
39      ;;RefCon (reference value)
0  128  0   ;;Min Max Value

   ,38  ;;Resource ID
TimbreScrollbar  ;;Title for a Scrollbar
85  360  101  460    ;;Top Left Bottom Right
Visible ;;Initially visible 
16      ;;ProcID (Control definition ID)
38      ;;RefCon (reference value)
0  254  0   ;;Min Max Value

   ,31  ;;Resource ID
EndScrollbar     ;;Title for a Scrollbar
255  250  271  370   ;;Top Left Bottom Right
Visible ;;Initially visible 
16      ;;ProcID (Control definition ID)
31      ;;RefCon (reference value)
1  100  1   ;;Min Max Value

   ,27  ;;Resource ID
StartScrollbar   ;;Title for a Scrollbar
215  250  231  370   ;;Top Left Bottom Right
Visible ;;Initially visible 
16      ;;ProcID (Control definition ID)
27      ;;RefCon (reference value)
1  100  1   ;;Min Max Value

   ,12  ;;Resource ID
NoteScrollbar    ;;Title for a Scrollbar
266  5  282  231     ;;Top Left Bottom Right
Visible ;;Initially visible 
16      ;;ProcID (Control definition ID)
12      ;;RefCon (reference value)
1  100  1   ;;Min Max Value

Type ALRT

 ,4     ;;Resource ID
75  124  210  385    ;;Top Left Bottom Right
4       ;;ID of item list
CCCC    ;;stages of alert in hexadecimal

Type DITL

 ,4     ;;Resource ID
2       ;;Number of controls in list

StaticText  ;;Static text
10  55  80  245  ;;Top Left Bottom Right
^0\0D^1\0D^2\0D^3    ;;message


Button  Enabled  ;;Push button
90  140  110  200    ;;Top Left Bottom Right
OK      ;;message

Type DLOG

 ,2     ;;Resource ID
About   ;;Dialog title
112  136  210  373   ;;Top Left Bottom Right
Visible NoGoAway     ;;Visible GoAway 
1       ;;ProcID, dialog def ID
2       ;;Refcon, reference value
2       ;;ID of item list

Type DITL

 ,2     ;;Resource ID
3       ;;Number of controls in list

Button  Enabled  ;;Push button
60  75  87  155  ;;Top Left Bottom Right
OK      ;;message

StaticText  ;;Static text
10  35  25  205  ;;Top Left Bottom Right
Zoundz 1.0 By Kirk Chase    ;;message

StaticText  ;;Static text
35  10  50  225  ;;Top Left Bottom Right
© 1989 Kirk Chase and MacTutor     ;;message

Type MENU

 ,1001    ;;Resource ID
\14     ;;APPLE menu title
About Zoundz...  ;;item title
(-      ;;

 ,1002    ;;Resource ID
File    ;;menu title
(New/N  ;;item title
(Open.../O  ;;item title
(-      ;;
Close   ;;item title
Save/S  ;;item title
Save As...  ;;item title
(-      ;;
Page Setup...    ;;item title
Print...    ;;item title
(-      ;;
Quit/Q  ;;item title

 ,1003    ;;Resource ID
Edit    ;;menu title
Undo/Z  ;;item title
(-      ;;
Cut/X   ;;item title
Copy/C  ;;item title
Paste/V     ;;item title

 ,1004
Extend
Frequency
Amplitude
Duration
Timbre

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for up to $150 off Apple’s new MSRP, starting at only $849. Free 1-2 day delivery is available to most US... Read more
M2 Mac minis on sale for $100-$200 off MSRP,...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100-$200 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $... Read more
Mac Studios with M2 Max and M2 Ultra CPUs on...
B&H Photo has standard-configuration Mac Studios with Apple’s M2 Max & Ultra CPUs in stock today and on Easter sale for $200 off MSRP. Their prices are the lowest available for these models... Read more
Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.