TweetFollow Us on Twitter

MIDI Music
Volume Number:10
Issue Number:10
Column Tag:New Apple Technology

Related Info: Sound Manager

Making MIDI Music

Using QuickTime 2.0 to make some music of your own

By Glenn Andreas, Fridley, MN

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

About The Author

Glenn Andreas - Glenn started Mac programming in the Fall of 84 when he conned his boss into buying him a Lisa and the original IM. Since then, he’s written the game Theldrow, worked for Palomar Software writing printer drivers, and done various freelance programming jobs, including a sped up version of the Stylewriter driver (which never shipped), and some work on the printing portions of Bedrock (which also never shipped). He’s currently working a “day job” which involves hacking the BSD kernel, while trying to finish his latest game Chimera (which will hopefully ship one day).

Editor’s Introduction

Glenn tried to use Apple docs to figure out how to generate MIDImusic with QuickTime 2.0. He ran into the common insufficient documentation (what’s that error number, again?) roadblock, and dove in to figure out how things really operate. In doing so, he ran some risk of learning how to do things the wrong way, so we gave some good folks at Apple a crack at his article. While calling the approach a bit “hackish” because he defines his own headers, they say he pretty much got it right. While there are easier ways to put MIDItunes into QuickTime movies and play them as background music, this article shows you how to drive from the APIlevel. It’s rather like using the Sound Manager with QuickTime.

Two caveats - the flags in TuneStop are not implemented (the article suggests they are) and TuneResume doesn’t.

Be sure to check out the real documentation when it comes available, and enjoy MusicTest in the meantime!

The Motivation

As I worked on my latest project, I wanted to have background music. I was originally planning on writing some sort of “auto mixing my own sound buffers from hell” sound manager hack, and while these are fun to write, it would take a great deal of resources to get it correct. Then I heard that QuickTime 2.0 would include the ability to play music, as music. “Sort of like MIDI” I heard. Call me a crazy, but I’d rather just make a few component manager calls rather than spend months writing my own music playing routines (besides having all my work done for me, QT 2.0 can play the music through an external MIDI device as well for even better quality with almost no system load). So, I got a beta of QT 2.0, and immediately dove into the “Macintosh Music Architecture” document. This was rather like diving in the shallow end of the pool. Given that the header code and the documentation didn’t synch all that well, and assuming that the underlying code followed yet another convention, I did what any resourceful programmer would do - I dropped into MacsBug.

Fortunately for me, MoviePlayer was able to correctly play some of the sample music movies included on the beta CD. And, also fortunately for me, the whole music architecture is build uses the component manager (which, by the way for those who have never really looked at it, is really cool). So I set a breakpoint at the beginning of the “Tune Player Component”, and watched every call made by QuickTime as it played music. And finally, after countless reboots, I was able to make my Mac play music from my own programs.

At this point, I saw this as an opportunity for a little fortune and glory. Given that the only existing document I had on how to play music was inaccurate, I figured that I would write this article explaining just what it will take for you to easily add background music to your current or future project. Note that this article is in no way official documentation, it is simply what I have discovered on how things works. I’ll only present a subset of the routines available (see the header files for QuickTime 2.0 which appear in the August Developer CD for more details), and all examples will be based on what I’ve found in snooping around (so if I say that this parameter is zero, that means that I’ve always seen this parameter as zero, not that it has to be - but unless you like rebooting, you might want to leave it as zero). Also, I will attempt to avoid as much “music theory” as possible, since not only are there good books on this already, my knowledge is weak in that area, and I wouldn’t want to provide misleading information. This will be in Pascal, and I’ll simplify the header file so we don’t have to include dozens of other files (because QuickTime wants Aliases which wants AppleTalk which wants, well, you get the idea). First, however, let’s look at a quick bit of pseudo-code and see what it is we are going to do.

Overview

In order to play music, we need two important pieces of information - what notes to play, and what instruments to play them on. This first part is the body of the tune, while the second is what is called the header of the tune. In QuickTime, the tune header, along with some additional information, is stored in the media handler information (in the resource fork), while the tune body is stored in the actual data (on the data fork). For this article, we will store both in a single resource, starting with what the media handler information would be (as defined in MusicDescription record), with the tune body appended onto the end.

Here, then, are the basic steps we will use to play music:

• Create a tune player component

• Feed the music header the header from our resource

• Tell it to start playing the body from our resource

• Wait until it finishes

• Make sure to tell it to stop playing

• Dispose and clean up.

But before we get to the code, let’s look how the music is stored (this is important, so don’t skip ahead).

Storage of Musical Notes

Music is stored as a series of commands. Each command usually takes one longint, but can take two or more. Examples of these commands are to play a given note on a given instrument at a given pitch and given volume for a given duration, or to have a given instrument wait a given duration. And thanks to the “Time” part of QuickTime, multiple instruments can all be synched together, or their tempo can just as easily be changed (but for you QuickTime junkies, I will not be getting into time bases).

These commands are similar to MIDI commands, if you are familiar with them. If you aren’t, by the end of this section you’ll know that MIDI commands are similar to QuickTime music commands. These commands are tagged with what the command is in the high three or four bits, with the remaining bits (or following long word or words) providing the parameters. Note that all commands are multiples of four bytes long, so you can easily scan them as an array of LongInts.

Before we get to the commands themselves, we need to look at some of the parameters first. Almost all commands require a integer parameter to specify which instrument the command affects. An instrument is just that - a single instrument. QuickTime provides 30 some odd instruments to choose from. You can have more than one instrument playing in a given song (so you can have “Dueling -insert your favorite instrument here-”). Also, a single instrument can play more than one note at a time (being able to play a chord). Before playing the song, you tell the tune player component how many instruments there are and what they are, but we’ll get into this later.

Other parameters are fairly self explanatory.

Volume (also called velocity, because it refers to how hard you strike the key on a keyboard), ranges from 0 (silent) to 63.

Duration is specified in units that are specific to the tune component (in our examples we use 1/600ths of a second). Warning, some music theory follows: Based on the default time units, and assuming 4/4 time (which means that a quarter note is 1/2 second long), below is a quick table of duration values and the length of notes they produce:

Units at 600/second Note produced

75 sixteenth note

150 eight note

300 quarter note

600 half note

1200 whole note

Pitch, which ranges from 0 to 127, corresponds to the same values MIDI uses. Pitch ranges from C five octaves below middle C (0) to G five octaves above middle C (127). Middle C has the value of 60. A complete table of these values can be found on page 2-43 of the recently published IM:Sound. While it is possible to play what is called “microtonal values”, which can be just about any pitch (they are represented with fixed point numbers), this is done via a different, much less convenient, interface.

One other thing to be aware of is that most commands have an “extended” form that usually takes two long words instead of one. This allows more bits for each parameter, but in all my “snooping” I’ve yet to come across anything that uses them. For the sake of simplicity, I’ll also restrict myself to just the commands that are commonly used. In the commands below, I’ll show all thirty two bits, with the most significant bit on the left, with a space between each four bits for easier reading.

Our first command is the “rest” command:

 000- ---- dddd dddd dddd dddd dddd dddd

The 000 is the rest command. The next five bits would normally be for the instrument, but for the rest command are unused (and should be set to zero). Those are followed by twenty-four bits of duration d. This just instructs the instrument not to start any more commands until that time has passed.

The next command is the “note” command:

 001i iiii pppp ppvv vvvv vddd dddd dddd

The 001 is the note command, the i’s are five bits representing what instrument (0-31), and the d’s are again duration (though for this command there are only 11 bits, so the value ranges from 0 to 2047, or in our examples, over 3 seconds). The six bits of p’s are our pitch, again as a MIDI note value, and the seven bits of v’s are the volume.

These two commands are enough to start playing music. However, if you wanted to play a scale, and you just issued eight note commands with increasing pitches, you would get one short polyphonic cacophony - all the notes would play at the same time, as a chord, rather than as eight sequential notes. To achieve the desired effect (each note in sequence), you need to issue eight note-rest command pairs, to allow time for one note to play before starting the next.

One final command in this example is needed - a command to have it stop. Without it, it will start playing whatever’s in memory, and soon you’ll be rebooting. The command to stop playing is the “marker” command:

 011- ---- ssss ssss xxxx xxxx xxxx xxxx

The 011 is the marker command, the ‘-’s are unused (and should be set to zero). The eight bits of s are the marker event subtype, and the x’s are the marker event value. The only values I’ve seen are zero for both, which is used to mark the end of the playing - all music command lists end with the value $60000000.

For the sake of completeness, here are the rest of the commands (though we will only be using one of them):

 010i iiii cccc cccc xxxx xxxx xxxx xxxx

Control: i is instrument, c is controller, x is the value to set that controller to.

 1011 iiii iiii iiii hhhh hhhh hhhh hhhh
 10kk kkkk kkkk kkkk llll llll llll llll

Knob: i is instrument, k is what knob, h is the high word and l is the low word of what value to set that knob to.

 1001 iiii iiii iiii pppp pppp pppp pppp
 10-- ---- --vv vvvv vvvv vvvv vvvv vvvv

Extended note, with the same parameters as note.

 1010 iiii iiii iiii cccc cccc cccc cccc
 10-- ---- ---- ---- xxxx xxxx xxxx xxxx

Extended control, with the same parameters as control.

 1111 iiii iiii iiii llll llll llll llll
 ....
 10tt tttt tttt tttt llll llll llll llll

This is the general command, which isn’t used in playing the music, but rather is used when you set up the tune player component. It is this command that is used to tell the player what instruments are what. The i’s, of course, are the instrument, the t’s represent one of the following:

1 Note request

2 Instrument

3 Flat Instrument

4 Part Name

5 Part Key

I’ve only seen “Note Request”, so that is all that I will talk about. The l’s represent the length of the entire command, including any general data. This value is in long words, and includes both long words of the command. Finally, between the two commands is some amount of additional data. Here’s an example to help explain this - this is the command from the header portion of some music, and it instructs the tune player that instrument zero is a normal piano:

F0000017 00000001 00010000 00000000 0F416E79 2053796E 74686573 697A6572 
AAE20000 000000AC AAE60000 00130018 0B486172 70736963 686F7264 69616E6F 
5069616E 6F000000 00000000 00000000 00000007 00000007 C0010017

The $F0000017 says that this is a general event for instrument zero, and the whole thing is $17 (23) long words long. Following that is $15 long words, which actually are a data structure known as a NoteRequest. The last long word $C0010017 says that it is, surprisingly enough, a “note request”, and the whole thing was $17 long words long. Why this value is repeated in both places I’m not sure - perhaps there is some sort of integrity check.

Before we get to the code, let’s make some Rez definitions so we can create our music - I am currently working on a music editor that will produce these music resources, but I can hardly include the source for that as well in this article (especially since it isn’t done yet). It should be done and available in a variety of online places by the time you read this. [Check out our online places. See p.2 for details - Ed stb]

‘Musi’ Resources

I made a simple “Music.r” file (see listing 1) which will allow us to use Rez to create and edit music. Here is our sample input:

resource 'Musi' (128) {
 { /* array header: 1 elements */
 /* [1] */
 0,
 1,
 0x10000,
 kAnyComponentType,
 "Any Synthesizer",
 "Acoustic Grand Piano",
 1,
 1
 },
 { /* array: 10 elements */
 noteCommand { 0, 37, 64, 1800 },
 restCommand { 1800 },
 noteCommand { 0, 11, 64, 900 },
 restCommand { 900 },
 noteCommand { 0, 28, 64, 1800 },
 noteCommand { 0, 31, 64, 1800 },
 noteCommand { 0, 35, 64, 1800 },
 noteCommand { 0, 4, 64, 1800 },
 restCommand { 3300 },
 markerCommand { 0, 0 }
 }
};

This says that we have one instrument, the grand piano, to be played on any synthesizer. If you want to just play around, you can change the last two ‘1’s to other values to play other instruments without having to change the name. I’d advise leaving the polyphony ‘1’ and ‘0x10000’ as is, as well as the synthesizer type and name.

After the header we play a C# in octave 3 (37) at average volume (64) for 3 seconds (1800, since our time scale will have 600 units per second). We rest for those three seconds to let the note play. We then play a B in octave 2 (11) for 1 1/2 second, and also rest to let the note play. We next play a chord of four notes for 3 seconds: E in octave 3 (28), G in octave 3 (31), B in octave 4 (35), and E in octave 1 (4). We let that play and rest for an additional 2 1/2 seconds. We end the thing with our marker command (we could have made that last command part of the rez template like we did for the header, but we might as well mark the end explicitly).

Here’s the hex dump of that resource, formatted to show what is going on a little better:

00000074offset to body
6D757369musi
000000000000000100000000  flags, etc...
F0000017instrument 1
 00000001 00010000 Polyphony, etc...
 00000000 Any component
 0F416E792053796E74686573697A6572
 00000000000000000000000000000000 
 “Any Synthesizer”
 1441636F7573746963204772616E6420
 5069616E6F0000000000000000000000
 “Acoustic Grand Piano”
 00000001 00000001 
C0010017
60000000End of Commands

Body starts
20960708Play C# in octave 3
00000708Rest
202E0384Play B in octave 2
00000384Rest
20720708 207E0708 208E0708 20120708Play Chord
00000CE4Rest
60000000End of Commands

The Interface

There are many more calls in “QuickTimeComponents.h” than I’ll document here, since the focus is to show what it takes to play music in the background of your application. Instead we will just look at the major routines for the tune player component, and ignore both the note allocator and low level music component. This is all from listing 2, my Music.p interface file derived from QuickTimeComponents.h. I’m also going to assume that you’ve got access to header files and documentation for the Component Manager (found in IM:More Macintosh Toolbox).

FUNCTION TuneSetHeader( tp: TunePlayer; 
 header: Ptr): ComponentResult;

This tells the newly created tune player what instruments will be used. We will pass in the header data from our resource.

FUNCTION TuneSetTimeScale(tp: TunePlayer;
 scale: LongInt): ComponentResult;

This specifies how many units per second the duration parameter in the music commands stand for. QuickTime uses 600, we use 600. The parameter is actually a TimeScale, but we don’t want to have to include all of the QuickTime interface files to find out that it is a longint.

FUNCTION TuneGetTimeScale(tp: TunePlayer;
 VAR scale: LongInt): ComponentResult;

This call will return what the current time scale is for the tune player.

 FUNCTION TuneQueue( tp: TunePlayer;
 tune: MusicOpWordPtr;
 tuneRate: Fixed;
 tuneStartPosition: LongInt;
 tuneStopPosition: LongInt;
 queueFlags: LongInt;
 callBackProc: ProcPtr;
 refCon: LongInt): ComponentResult;

This is the magic call to actually start playing. You pass in the tune player in tp, and a pointer to the start of the music opwords (make sure that everything is locked down) in tune. TuneRate contains a fixed value which lets you adjust how fast or slow the resulting tune is played. TuneStartPosition and TuneStopPosition specify, in time units, what section of the music to play. The music starts at zero, so to play everything, we pass in 0 and $7FFFFFFF. QueueFlags have the following values:

CONST
  kTuneStartNow = 1;
  kTuneDontClipNotes = 2;
  kTuneExcludeEdgeNotes = 4;
  kTuneStartNewMaster = 16384;

If no flags are specified, the tune starts playing as soon as any currently playing tune stops (or immediately if no music is currently being played). Up to eight tunes can be queued up at a time.

CallBackProc and refCon are used to help you queue up the next sequence chunk. It would be declared as:

PROCEDURE MyTuneCallBackProc(status:TuneStatus; refCon:LongInt);

where status is the same as is used in TuneGetStatus, and refCon is whatever you pass into the call of TuneQueue.

 TYPE
 TuneStatus = RECORD
 tune, tunePtr: ^LongInt;
 time: longint;
 queueCount, queueSpots: Integer;
 queueTime: LongInt;
 reserved: ARRAY[1..3] OF LongInt;
 END;
 FUNCTION TuneGetStatus (tp: TunePlayer;
 VAR status: TuneStatus): ComponentResult;

This routine will give you the status of the currently playing tune. Tune is the current tune, while TunePtr points to where in that tune we currently are. Time is how many time units have passed. QueueCount is how many tunes, including this one are currently queued up. QueueTime is how many time units worth of tunes are queued up waiting to be played.

FUNCTION TuneStop( tp: TunePlayer; 
 stopFlags: LongInt): ComponentResult;

This routine is used to stop the specified tune. StopFlags can be one of the following:

CONST
 kStopSustain = 1;
 kStopFadeout = 2;

This allows you to either let the currently playing note keep playing (which can be annoying), or to let it fade out in a nice manner. If you specify 0, then the music stops abruptly.

 FUNCTION TuneResume (tp: TunePlayer): ComponentResult;

After you have stopped a tune, this call will let you resume it.

 FUNCTION TuneFlush (tp: TunePlayer): ComponentResult;

If you decide to not resume a tune, you can call this routine and the next tune queued up should start.

 FUNCTION TuneSetVolume (tp: TunePlayer; volume: Fixed): ComponentResult;
 FUNCTION TuneGetVolume (tp: TunePlayer): ComponentResult;

This pair of routines allows you to change the volume of the playing tune. Don’t ask me how the volume is returned in TuneGetVolume, since there is no fixed value returned and no fixed var parameter, but that is what the call is.

 FUNCTION TunePreroll (tp: TunePlayer): ComponentResult;

This call is important, because it will reserve all the note channels for the instruments, load everything it can into memory, and do any other possible preparation for playing the given music.

 FUNCTION TuneUnroll (tp: TunePlayer): ComponentResult;

This is the opposite of TunePreroll in that it unreserves all the note channels that have been locked down. This call is typically called before suspending your application (after stopping the current music), so other applications can play their music.

Putting It All Together

Now that we’ve got the basic calls, and a simple little bit of music to play, let’s look at what calls need to made and in what order. This is the code from a simple application that just gets the music resource and plays it, busy waiting while playing, and quitting when done. We will just comment on the basic calls - to see all the details (where we actually check errors), see listing 3, MusicTest.p.

The first thing to do is to load in the resource we are playing and lock it down:

 h := MusicDescriptionHandle(GetResource('Musi', 128));
 HLock(Handle(h));

We then call the component manager to have it make a default tune player:

 tp := OpenDefaultComponent(kTunePlayerType,
 ResType(LongInt(kAnyComponentType)));

The next step is to tell it the time units. We use the value 600 since that is what QuickTime movies normally use.

 result := TuneSetTimeScale(tp, 600);

We then need to tell the tune component what instruments are used. We get this data from our resource.

 result := TuneSetHeader(tp, @h^^.headerData);

Now that we’ve told it what instruments to use, we want to reserve those instruments and do any other sort of pre-play allocation we can.

 result := TunePreRoll(tp);

We can now say how loud we want to play it. We will use normal volume, which is a fixed point one.

 result := TuneSetVolume(tp, $10000);

Now we make the magic call to actually start the music playing. It will automatically play in the background, there is no extra stuff needed to do (unlike the sound manager). We pass in the start of the body of music, as based on our resource, tell it to play at normal tempo, play everything, start playing now, and we don’t have any callbacks.

 result := TuneQueue(tp, Pointer(ORD4(h^) + h^^.size),
 $10000, 0, $7FFFFFFF, kTuneStartNow, NIL, 0);

We can then wait and check to see how we doing. When the music stops, the queueCount field of the TuneStatus record will drop from 1 to 0. There are other ways to determine if we are done, but we won’t get into them, since the queueCount field is simple enough.

 REPEAT
 result := TuneGetStatus(tp, theTuneStatus);
 UNTIL Button | (theTuneStatus.queueCount = 0);

That’s all there is to playing. When we are done, (or in our example, when the user clicks the button), we need to free up everything we allocate.

 result := TuneStop(tp, kStopFadeout);
 err := CloseComponent(tp);

That’s all there is to playing music. There are many more calls, include ones to present a dialog to the user to allow them to select an instrument, but we don’t have space to get into those here. The calls are all found in the QuickTime 2.0 interface files, while the documentation should be in a tech note or future article.

Listing 1 - Music.r

This is our combined header/body resource for playing music with QuickTime 
2.0's tune player component.

/* 1 */
type 'Musi' {
/* First the length of the header & MusicDescription record.  The MusicDescription 
*/
/* record isn't actually used by us or passed as a parameter, but I kept 
this info */
/* just so it would be easy to convert QuickTime music tracks to 'Musi' 
resources. */
start:  longint = $$CountOf(header) * $17 * 4 + 20 + 4;
 /* next, the media handler type used by QuickTime */
 longint = 'musi';
 longint = 0; /* Reserved 1 */
 integer = 0; /* Reserved 2 */
 integer = 1; /* dataRefIndex */
 longint = 0; /* music flags */
/* and here is the actual start of the music header */
/* we just currently define NoteRequest general events */
 array header {
 bitstring[4] = $F;  /* general event */
 bitstring[12];  /* What instrument */
 bitstring[16] = $0017;   /* length of note request general event */
 /* here is the noteRequest record */
 longint; /* Polyphony, usually 1 */
 hex longint;    /* TypicalPolyphony, fixed, usually $10000 */
 longint kAnyComponentType = 0; /* OSType of synth component */
 pstring[31];    /* Synthesizer name such as "Any Synthesizer" */
 pstring[31];    /* Preferred Instrument name for human use*/
 longint; /* instrument number if synth-type matches */
 longint; /* gm number - best matching general MIDI number */
 longint = $C0010017;     /* this was a note request */
 };
 longint = $60000000;     /* The marker at the end of the header */
/* Here is the body of the music */
bodystart:
 array {
 switch {
 case restCommand:
 key bitstring[3] = $0;
 bitstring[5] = 0; /* unused */
 bitstring[24];  /* duration */
 case noteCommand:
 key bitstring[3] = $1;
 unsigned bitstring[5];   /* instrument */
 unsigned bitstring[6];   /* pitch */
 unsigned bitstring[7];   /* volume */
 unsigned bitstring[11];  /* duration */
 case markerCommand:
 key bitstring[3] = $3;
 bitstring[5] = 0; /* unused */
 unsigned bitstring[8];   /* subtype */
 bitstring[16];  /* value */
 case controlCommand:
 key bitstring[3] = $2;
 unsigned bitstring[5];   /* instrument */
 unsigned bitstring[8];   /* control number */
 bitstring[16];  /* value */
 };
 };
};

Listing 2 - Music.p


/* 2 */
UNIT Music;
INTERFACE
 USES
 Components;
 CONST
 kMusicComponentType = 'musi';
 TYPE
 MusicDescription = RECORD
 size: LongInt; { including header }
 musicType: LongInt; { 'musi' }
 resvd1: LongInt; { 0 }
 resvd2: Integer;{ 0 }
 dataRefIndex: Integer; { 1 }
 musicFlags: LongInt; { 0 }
 headerData: ARRAY[1..1] OF LongInt; 
 { actually, some sort of tone descriptions }
 END;
 MusicDescriptionPtr = ^MusicDescription;
 MusicDescriptionHandle = ^MusicDescriptionPtr;

 TunePlayer = ComponentInstance;
 CONST
 kTuneQueueDepth = 8;
 TYPE
 TuneStatus = RECORD
 tune, tunePtr: ^LongInt;
 time: longint;
 queueCount, queueSpots: Integer;
 queueTime: LongInt;
 reserved: ARRAY[1..3] OF LongInt;
 END;
 CONST
 kStopTuneFade = 1;
 kStopTuneSustain = 2;
 kStopTuneInstant = 4;
 kStopTuneReleaseChannels = 8;
 TYPE
 MusicOpWord = LongInt;
 MusicOpWordPtr = ^MusicOpWord;
 CONST
 kMaxTunePlayerParts = 32;
 tunePlayerRunning = -2100;
 kTunePlayerType = 'tune';

 FUNCTION TuneSetHeader (tp: TunePlayer;
 header: Ptr): ComponentResult;
 INLINE $2F3C, $4, 4, $7000, $A82A;

 FUNCTION TuneSetTimeScale (tp: TunePlayer;
 scale: LongInt): ComponentResult;
 INLINE $2F3C, $4, 6, $7000, $A82A;

 FUNCTION TuneGetTimeScale (tp: TunePlayer;
 VAR scale: LongInt): ComponentResult;
 INLINE $2F3C, $4, 7, $7000, $A82A;

 CONST
 kTuneStartNow = 1;
 kTuneDontClipNotes = 2;
 kTuneExcludeEdgeNotes = 4;
 kTuneStartNewMaster = 16384;

 FUNCTION TuneQueue (tp: TunePlayer;
 tune: MusicOpWordPtr;
 tuneRate: Fixed;
 tuneStartPosition: LongInt;
 tuneStopPosition: LongInt;
 queueFlags: LongInt;
 callBackProc: ProcPtr;
 refCon: LongInt): ComponentResult;
 INLINE $2F3C, $1C, 10, $7000, $A82A;

 FUNCTION TuneGetStatus (tp: TunePlayer;
 VAR status: TuneStatus): ComponentResult;
 INLINE $2F3C, $4, 12, $7000, $A82A;

 CONST
 kStopSustain = 1;
 kStopFadeout = 2;

 FUNCTION TuneStop (tp: TunePlayer;
 stopFlags: LongInt): ComponentResult;
 INLINE $2F3C, $4, 13, $7000, $A82A;

 FUNCTION TuneResume (tp: TunePlayer): ComponentResult;
 INLINE $2F3C, 0, 14, $7000, $A82A;

 FUNCTION TuneFlush (tp: TunePlayer): ComponentResult;
 INLINE $2F3C, 0, 15, $7000, $A82A;

 FUNCTION TuneSetVolume (tp: TunePlayer;
 volume: Fixed): ComponentResult;
 INLINE $2F3C, $4, 16, $7000, $A82A;

 FUNCTION TuneGetVolume (tp: TunePlayer): ComponentResult;
 INLINE $2F3C, 0, 17, $7000, $A82A;

 FUNCTION TunePreroll (tp: TunePlayer): ComponentResult;
 INLINE $2F3C, 0, 18, $7000, $A82A;

 FUNCTION TuneUnroll (tp: TunePlayer): ComponentResult;
 INLINE $2F3C, 0, 19, $7000, $A82A;

IMPLEMENTATION
END.

Listing 3 - MusicTest.p


/* 3 */
PROGRAM MusicTest;
 USES
 Components, Music;

 VAR
 h: MusicDescriptionHandle;
 tp: TunePlayer;
 result: ComponentResult;
 theTuneStatus: TuneStatus;
 err: OSErr;

 LABEL
 10; { used for error cleanup }
BEGIN
 { Get the music resource and lock it down }
 h := MusicDescriptionHandle(GetResource('Musi', 128));
 IF h = NIL THEN
 ExitToShell;
 HLock(Handle(h));

 { open the default tune player }
 tp := OpenDefaultComponent(kTunePlayerType,
 ResType(LongInt(kAnyComponentType)));
 IF tp = NIL THEN
 ExitToShell;

 { tell that we have 600 units per second }
 result := TuneSetTimeScale(tp, 600);
 IF result  noErr THEN
 GOTO 10;

 { Set the header, to tell what instruments are used }
 result := TuneSetHeader(tp, @h^^.headerData);
 IF result  noErr THEN
 GOTO 10;

 { Have it allocate whatever resources are needed }
 result := TunePreRoll(tp);
 IF result  noErr THEN
 GOTO 10;

 { We want to play at normal volume }
 result := TuneSetVolume(tp, $10000);
 IF result  noErr THEN
 GOTO 10;

 { Queue up the music, normal tempo, play everything now }
 result := TuneQueue(tp, Pointer(ORD4(h^) + h^^.size),
 $10000, 0, $7FFFFFFF, kTuneStartNow, NIL, 0);
 IF result  noErr THEN
 GOTO 10;

 REPEAT
 result := TuneGetStatus(tp, theTuneStatus);
 IF result  noErr THEN
 GOTO 10;
 { spin until we click the button or no music left queued up }
 UNTIL Button | (theTuneStatus.queueCount = 0);

 { We get here either by getting an error or having everything finish 
}
 { Regardless, we need to stop and clean up everything }
10:
 IF result  noErr THEN
 DebugStr('Music result');
 IF tp  NIL THEN
 result := TuneStop(tp, kStopFadeout);
 IF tp  NIL THEN
 err := CloseComponent(tp);

 { And we are done }
END.







  
 
AAPL
$102.47
Apple Inc.
+2.71
MSFT
$44.88
Microsoft Corpora
+0.80
GOOG
$526.54
Google Inc.
+5.70

MacTech Search:
Community Search:

Software Updates via MacUpdate

EyeTV 3.6.6 - Watch and record TV on you...
EyeTV brings a rich TV experience to your Mac. Watch live TV on your Mac. Pause, rewind, and record whenever you want. EyeTV gives you powerful control over what you watch and how you watch it. Put... Read more
RapidWeaver 6.0 - Create template-based...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
NTFS 12.0.39 - Provides full read and wr...
Paragon NTFS breaks down the barriers between Windows and OS X. Paragon NTFS effectively solves the communication problems between the Mac system and NTFS, providing full read and write access to... Read more
RestoreMeNot 2.0.3 - Disable window rest...
RestoreMeNot provides a simple way to disable the window restoration for individual applications so that you can fine-tune this behavior to suit your needs. Please note that RestoreMeNot is designed... Read more
Macgo Blu-ray Player 2.10.9.1750 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple iOS 8.1 - The latest version of Ap...
The latest version of iOS can be downloaded through iTunes. Apple iOS 8 comes with big updates to apps you use every day, like Messages and Photos. A whole new way to share content with your family.... Read more
TechTool Pro 7.0.5 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PDFKey Pro 4.0.2 - Edit and print passwo...
PDFKey Pro can unlock PDF documents protected for printing and copying when you've forgotten your password. It can now also protect your PDF files with a password to prevent unauthorized access and/... Read more
Yasu 2.9.1 - System maintenance app; per...
Yasu was originally created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks... Read more
Hazel 3.3 - Create rules for organizing...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a... Read more

Latest Forum Discussions

See All

Swap Heroes (Games)
Swap Heroes 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: **Half price for a limited time only** Swap Heroes is a casual turn-based strategy adventure. Form a group of heroes and guide them... | Read more »
Ghost Blade (Games)
Ghost Blade 1.1 Device: iOS Universal Category: Games Price: $4.99, Version: 1.1 (iTunes) Description: Get the most outstanding 3D Action Game from App Store NOW! For those who dare pursue dreams. Masterpiece of Yu Shi Game ---China... | Read more »
Fiete – A Day on a Farm Review
Fiete – A Day on a Farm Review By Amy Solomon on October 21st, 2014 Our Rating: :: A MEMORABLE EXPERIENCEUniversal App - Designed for iPhone and iPad Fiete – A day on a farm in an interactive app for young children full of... | Read more »
Tilt to Live: Gauntlet’s Revenge is Almo...
Tilt to Live: Gauntlet’s Revenge is Almost Here Posted by Jessica Fisher on October 21st, 2014 [ permalink ] One Man Left has announced the official release date of Tilt to Live: Gauntlet’s Re | Read more »
Sago Mini Monsters Celebrates Halloween...
Sago Mini Monsters Celebrates Halloween with Fun Costumes and Special Treats. Posted by Jessica Fisher on October 21st, 2014 [ permal | Read more »
Inferno 2 Review
Inferno 2 Review By Andrew Fisher on October 21st, 2014 Our Rating: :: TWIN STICK GOODNESSUniversal App - Designed for iPhone and iPad With tight controls and awesome, stark visuals, Inferno 2 is loads of fun.   | Read more »
Clips Review
Clips Review By Jennifer Allen on October 21st, 2014 Our Rating: :: CONVENIENT PASTINGUniversal App - Designed for iPhone and iPad Making copying and pasting more powerful than usual, Clips is a great way to move stuff around.   | Read more »
MonSense Review
MonSense Review By Jennifer Allen on October 21st, 2014 Our Rating: :: ORGANIZED FINANCESiPhone App - Designed for the iPhone, compatible with the iPad Organize your finances with the quick and easy to use, MonSense.   | Read more »
This Week at 148Apps: October 13-17, 201...
Expert App Reviewers   So little time and so very many apps. What’s a poor iPhone/iPad lover to do? Fortunately, 148Apps is here to give you the rundown on the latest and greatest releases. And we even have a tremendous back catalog of reviews; just... | Read more »
Angry Birds Transformers Review
Angry Birds Transformers Review By Jennifer Allen on October 20th, 2014 Our Rating: :: TRANSFORMED BIRDSUniversal App - Designed for iPhone and iPad Transformed in a way you wouldn’t expect, Angry Birds Transformers is a quite... | Read more »

Price Scanner via MacPrices.net

Select MacBook Airs $100 off MSRP, free shipp...
B&H Photo has 2014 a couple of MacBook Airs on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels Desktop and LoJack for... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
Strong iPhone, Mac And App Store Sales Drive...
Apple on Monday announced financial results for its fiscal 2014 fourth quarter ended September 27, 2014. The Company posted quarterly revenue of $42.1 billion and quarterly net profit of $8.5 billion... Read more
Apple Posts How-To For OS X Recovery
OS X 10.7 Lion and later include OS X Recovery. This feature includes all of the tools you need to reinstall OS X, repair your disk, and even restore from a Time Machine backup. OS X Recovery... Read more
Mac OS X Versions (Builds) Supported By Vario...
Apple Support has posted a handy resource explaining which Mac OS X versions (builds) originally shipped with or are available for your computer via retail discs, downloads, or Software Update. Apple... Read more
Deals on 2011 13-inch MacBook Airs, from $649
Daily Steals has the Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB) available for $699 with a 90 day warranty. The Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB SSD) is available for $649 at Other... Read more
2013 15-inch 2.0GHz Retina MacBook Pro availa...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Updated iPad Prices
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers, including the new iPad Air 2 and the... Read more
Apple Pay Available to Millions of Visa Cardh...
Visa Inc. brings secure, convenient payments to iPad Air 2 and iPad mini 3as well as iPhone 6 and 6 Plus. Starting October 20th, eligible Visa cardholders in the U.S. will be able to use Apple Pay,... Read more
Textkraft Pocket – the missing TextEdit for i...
infovole GmbH has announced the release and immediate availability of Textkraft Pocket 1.0, a professional text editor and note taking app for Apple’s iPhone. In March 2014 rumors were all about... Read more

Jobs Board

Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.