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

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom 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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.