DirectMusic API Frequently Asked Questions

Chanel Summers
Microsoft Corporation

January 12, 2000

Summary: This article answers the most frequently asked questions about the Microsoft DirectMusic API and is based on Microsoft DirectX, version 7.0. (21 printed pages)

Contents

General DirectMusic Issues
Developer/Composer Communication Issues
Programming 3-D Music
Miscellaneous Issues
Messages and Tools
Notifications
IDirectMusicTrack and Patterns
The Composition Engine
Troubleshooting Issues

General DirectMusic Issues

What are some recommendations for optimization?

Table 1. CPU usage regarding reverb and sampling rate

Reverb Status Sampling Rate CPU Usage
Reverb off 22 kHz Least CPU
Reverb on 22 kHz Better sounding
Reverb off 44.1 kHz Probably not that useful; 22 kHz with reverb on usually sounds better, but use your own taste
Reverb on 44.1 kHz Best sounding if you are using 44.1-kHz samples

You could also give the user ultimate control through the audio control panel in your game. Of course, if all your samples are 22kHz, you should run the synthesizer no faster than 22 kHz.

Why is it bad practice to use AutoDownload? Would you use it for anything?

If you’re working on a typical game application, you probably don’t want to use AutoDownload, as it can cause a performance hit when playing back Segments. Instead, manually download with Segment->SetParam( GUID_Download, pIPerformance) to tell the Segment to download the DLS instruments associated with the Segment. This should be called at a convenient time (like a scene change) or you can call it in a separate thread prior to playback. The Band should be placed in a Band Track in your Segment to ensure that this will work properly.

After playing the Segment, call Segment->SetParam(GUID_Unload, pIPerformance) when you’re done with the Segment. For this to work, all collections must be referenced properly from within the Band. When you load the Segment, the Band Track reads the name, file name, and GUID for each referenced collection and asks the Loader to load those as well. The easiest way for the Loader to know where to find them is to rely on file names. If you store your data in a resource, then you should call SetObject on each resource chunk first so the Loader will know where to find it.

When using AutoDownload, if you are using only the instruments from the default collection (GM.DLS) in your primary Segment and the Band in your Secondary Segment references only instruments from a custom collection (replacing GM.DLS), then the instruments from the default collection should be returned automatically when the Secondary Segment playback stops, if the primary Segment is still playing.

If you write a basic Play Segment/MIDI file app, you can use AutoDownload so you don’t have to manage downloading the instruments. However, in a typical game situation, AutoDownload incurs a performance hit if you ever play a Segment more than once. It also causes the downloading of instruments to occur right at the start of Segment playback, causing a blip in CPU at that point and potential delay in performance. Downloading and unloading repeatedly (which AutoDownload may do) takes time, and can potentially degrade performance. If you are concerned about CPU performance in your application, consider turning AutoDownload off.

Relying on AutoDownload can cause other problems. You might also want to turn off AutoDownload if you have a Band in a Secondary Segment (Secondary Segments are played on top of a primary Segment). Otherwise the instruments in the Band may be downloaded automatically when the Secondary Segment starts, changing your instruments. If the Secondary Segment stops playing before the primary Segment stops, AutoDownload will then unload the Band. If this happens, you will not revert to the original instruments, as you may expect. Rather, you may lose sound output entirely because you now have no Bands loaded.

Further information on downloading custom collections can be found under What is the best way to download a custom collection?

Does DirectMusic support EAX?

Yes. EAX can be supported with our software synthesizer the same way that three-dimensional (3-D) audio is. You create your own DirectSound buffer, pass it to the DirectMusic software synthesizer, and then you can manipulate the DirectSound buffer the same way you would any DirectSound buffer. There’s an example of doing 3-D sound on the DirectX SDK, but the same concepts can apply to anything that DirectSound can do, including EAX.

Developer/Composer Communication Issues

What parameters does the composer need to communicate to me?

How do I set the reverb parameters?

After you get the reverb settings from the composer, you can set the reverb manually using KSControl. Please note that the composer may specify different reverb settings per Segment.

DMUS_WAVES_REVERB_PARAMS Params;
Params.fInGain      = m_fReverbIn;
Params.fHighFreqRTRatio   = m_fReverbHigh;
Params.fReverbMix      = m_fReverbMix;
Params.fReverbTime      = m_fReverbTime;
 
IKsControl *pControl;
// Query for IKsControl interface
HRESULT hr = m_pPort->QueryInterface(IID_IKsControl,
   (void**)&pControl);
if (SUCCEEDED(hr)) 
{
   KSPROPERTY ksp;
   ULONG cb;
 
   ZeroMemory(&ksp, sizeof(ksp));
   ksp.Set   = GUID_DMUS_PROP_WavesReverb;
   ksp.Id    = 0;
   ksp.Flags = KSPROPERTY_TYPE_SET;
 
   pControl->KsProperty(&ksp, sizeof(ksp),
      (LPVOID)&Params, sizeof(Params), &cb);
   pControl->Release();

Problems Loading Media: Why Don’t I Hear Anything or Why Am I Hearing the Wrong Instruments?

Here is a checklist of suggestions/caveats/gotchas to be aware of:

Gotchas

What is the best way to download a custom collection?

Use Bands to reference the instruments in the collection. If you want to use the Band explicitly, call Band->Download(pIDirectMusicPerformance) to cause the instruments selected by the Band to be downloaded. Later, call Band->Unload.

Even better, the Band should be placed in a Band Track in your Segment. There, it sets the volume, pan, priorities, and instruments for the channels. Before playing the segment, call Segment->SetParam( GUID_Download, pIPerformance) to tell the Segment to download the DLS instruments associated with the Segment. This should be called at a convenient time (like a scene change) or you can call in a separate thread. After playing the Segment call Segment->SetParam( GUID_Unload, pIPerformance) when you’re done with the Segment.

For this to work, all collections must be referenced properly from within the Band. When you load the Segment, the Band Track reads the name, file name, and GUID for each referenced collection and asks the Loader to load that as well. So, it’s important that the Loader know where to find these. The easiest is to rely on file names. If you store your data in a resource, then you should call SetObject on each resource chunk first so the Loader will know where to find it.

If you use GetObject from the Performance Layer to get a collection object, does that automatically override the default GM/GS set (even with AutoDownloading on)?

No, you can have any number of collections active at the same time. A Band can reference collections in addition to the GM collection (which doesn’t require a specific linkage).

However, you can override the GM collection, if you want to.

Here’s a function to set up the Loader to find an object from memory in response to a later GetObject call from the application, or an internal reference from another object that is being loaded by the application. This is particularly important for having Segments reference DLS collections. But, it also applies to Segments referencing Styles.

The function passes a chunk of memory and the object class of the object.

HRESULT SetMemoryObject(IDirectMusicLoader *pLoader,BYTE *pData,DWORD dwLength,GUID guidClassID)

{ 
    DMUS_OBJECTDESC ObjDesc;     
    ObjDesc.pbMemData = pData;
    ObjDesc.llMemLength = (LONGLONG) dwLength;
    ObjDesc.guidClass = guidClassID;
    ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
    ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_MEMORY;
    return pLoader->SetObject( &ObjDesc);
}

Here’s a function that does the same thing with a resource ID:

HRESULT SetResourceObject(IDirectMusicLoader *pLoader,UINT uiResourceID,GUID guidClassID)
{
    DMUS_OBJECTDESC ObjDesc;     
    HRSRC hFound = FindResource(NULL,MAKEINTRESOURCE(uiResourceID), RT_RCDATA);
    ObjDesc.llMemLength = SizeofResource(NULL,hFound);
    HGLOBAL hRes = LoadResource(NULL, hFound);   
    ObjDesc.pbMemData = (BYTE *) LockResource(hRes);
    ObjDesc.guidClass = guidClassID;
    ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
    ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_MEMORY;
    return m_pLoader->SetObject( &ObjDesc);
}

Once the objects have been set in the Loader, it’s very easy to access them by calling to GetObject.

And now, the Segment downloading code, which you can call right after you load the Segment:

   pSegment->SetParam(GUID_Download,-1,0,0,(void *) pPerformance);

and the Segment unloading code, which you can call just before you release the Segment:

   pSegment->SetParam(GUID_Unload,-1,0,0,(void *) pPerformance);

What does it mean when I get the following error: DMSYNTH: No instrument was downloaded for patch # 0, 0, 0”)?

These errors are occurring because the DLS instruments have not been downloaded. You need to turn off AutoDownload and manually download your Bands. 0,0,0 is the default value for patch selection in a synthesizer when no program change/bank select has been received. So, if you are seeing this message, it would imply that no program changes or bank selects are making it to the synthesizer on the channel. The notes arrive and are told to play the default, 0,0,0, but no instrument with the address 0,0,0 was ever downloaded.

Remember, this error message is caused by no program change on the channel that selects a currently downloaded instrument. So, three things can cause it:

Additionally, you could get this error if a range of the instrument was downloaded and you sent patch changes, but are playing notes out of the downloaded range. This can also happen when using the default collection (GM.DLS) and you are playing drums but there is no drum instrument assigned to a particular MIDI value in the collection. The Standard Drums in GM.DLS has an extended range from D#2 (value 27) to D#7 (value 87), but the GM Standard for drums is B2 to D#7 (values 36 to 87). This can easily happen with converted MIDI files that have be created using external drum modules as the sound source.

Also, this debug output is the most likely one you will see that will make it necessary to turn debugging statements off. There will be one debug statement per MIDI note, which can add up to a lot of output.

What happens when you try to load from a DLS collection and there’s not enough RAM available?

Since both DirectMusic software and hardware synthesizers use system RAM (as opposed to on-board RAM), it is not very likely that you will run out of RAM. But in the unlikely case that you do, you may receive E_OUTOFMEMORY or DMUS_S_PARTIALDOWNLOAD, depending on method of download.

How do you load a MIDI file from a resource?

This function will play a Segment directly from a resource, assuming all the right DirectMusic setup has been done.

You can also call IDirectMusicLoader::SetObject() and later use IDirectMusicLoader::GetObject() to load it, just as you would if the segment was on disk. SetObject puts the Segment in the Loader list as if it was found by a call to ScanDirectory.

//==================================================================
HRESULT PlaySegmentFromResource(int ID,char* type)
{
   HRESULT hr = E_FAIL;
   DMUS_OBJECTDESC desc;
   IDirectMusicSegment* pSegment = NULL;
   HRSRC hResource = NULL;
   HGLOBAL hData = NULL;

   //must have a properly setup loader and a performance
   if(m_pLoader && m_pPerformance)
   {
      hResource = 
FindResource(GetModuleHandle(NULL),MAKEINTRESOURCE(ID),type);
      if(hResource != NULL)
      {
         hData = LoadResource(GetModuleHandle(NULL),hResource); 
         if (hData != NULL)
         {
            ZeroMemory(&desc,sizeof(desc));
            desc.pbMemData = (BYTE*)LockResource(hData);
            desc.llMemLength = SizeofResource(GetModuleHandle(NULL),hResource);
            desc.guidClass = CLSID_DirectMusicSegment;
            desc.dwSize = sizeof(desc);
            desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_MEMORY;
            hr = m_pLoader-
>GetObject(&desc,IID_IDirectMusicSegment,(void**)&pSegment);
            if(SUCCEEDED(hr))
            {
               hr = m_pPerformance->PlaySegment(pSegment,0,0,NULL);
               pSegment->Release();
               pSegment = NULL;
            }
         }
      }
   }
   return hr;
}
//=================================================================
//=================================================================
HRESULT SetSegmentObjectFromResource(int ID,char* type)
{
   HRESULT hr = E_FAIL;
   DMUS_OBJECTDESC desc;
   IDirectMusicSegment* pSegment = NULL;
   HRSRC hResource = NULL;
   HGLOBAL hData = NULL;

   //must have a loader and a performance
   if(m_pLoader && m_pPerformance)
   {
      hResource = 
FindResource(GetModuleHandle(NULL),MAKEINTRESOURCE(ID),type);
      if(hResource != NULL)
      {
         hData = LoadResource(GetModuleHandle(NULL),hResource); 
         if (hData != NULL)
         {
            ZeroMemory(&desc,sizeof(desc));
            desc.pbMemData = (BYTE*)LockResource(*hData);
            desc.llMemLength = 
SizeofResource(GetModuleHandle(NULL),(*hResource));
            desc.guidClass = (*guid);
            desc.dwSize = sizeof(desc);
            desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_MEMORY;
            return m_pLoader->SetObject(&desc);
         }
      }
   }
   return hr;
}
//===================================================================

Programming 3-D Music

How do I go about spatializing my music in 3-D?

Verify that the IDirectMusicPort supports DirectSound by checking port caps:

   DMUS_PORTCAPS.dwFlags & DMUS_PC_DIRECTSOUND

Create IDirectSound, then pass to DirectMusic:

   pDMusic->SetDirectSound(pDSound, hWnd)

Query required wave format and length:

   pPort->GetFormat(&WFormat,&dwWFXSize,&dwBufferSize)

Create IDirectSoundBuffer using format and length

Set Port to render into DirectSound buffer:

   pPort->SetDirectSound(pDSound, pDSBuffer)

Activate Port

   pPort->Activate( TRUE )

There is a sample in the SDK called, “3DMusic.” It shows how to play a MIDI file into a custom DirectSound 3D buffer. The default location is C:\mssdk\samples\multimedia\dmusic\src\3dmusic.

Miscellaneous Issues

I need to play multiple Segments in a game, each with different tempos and volumes, and each Segment is to use a different Channel Group. Is it viable to have a Performance Object for each Segment so that they are all primary Segments?

For separate tempos, yes. The overhead of having multiple performances is perfectly fine. They are effectively separate pieces of music, which is why we introduced the concept of multiple performances.

Global volume on the Performance really is control over the volume of the port. So, the problem there is that you would need multiple ports. You could also programmatically send volume or CC11 (Expression) controls on a channel by channel basis, or use Bands.

Is there a global volume or do I need to build a Tool to adjust volume?

Call IDirectMusicPerformance::SetGlobalParam() method. This sets the global value for the Performance. Call IDirectMusicPerformance::GetGlobalParam() method. This retrieves the global value from the Performance. Each Global Parameter is defined by a GUID and data structure, for example: GUID_PerfMasterVolume, (long) lVolume.

The master volume is an amplification or attenuation factor, in hundredths of decibels, applied to the default volume of the entire performance. The range of permitted values is determined by the port. Legacy hardware MIDI ports do not support changing master volume.

Is there a way to “fade in” a Motif on top of the song, or to control the volume of a Motif?

You can send MIDI expression events for each PChannel in the Segment. Even better, send curves, which can have expression start and end values as well as curve shape. You can also write an IDirectMusicTool that sits in the Performance and monitors volume and expression events as they pass through it. This Tool also has a method that the application uses to control the overall volume through a multiplier that is applied to the volume and expression events as they flow through the Tool.

In both cases, you need to know which PChannels belong to which Segments. This can be avoided with the Tool solution if the Tool is placed in the Segment’s Tool Graph.

Is there a way to do a “fade out”?

Yes. You create a Secondary Segment with volume controller curves for each of your PChannels that fade to zero over the length of the time you want the fade to occur. You would then play the Secondary Segment when you want to do the fadeout and voila—instant fadeout.

What types of things do Control Parameters include?

They include chord, time signature, Groove Level, and mute control. It is also extensible so you can add new controls. Control Parameters do not include tempo, volume, instrument patch, pan, or any other performance information that is communicated by a Performance Message.

How can you read the key and scale of the piece that’s playing?

You can call GetParam to get the chord and read it.

// declare the chord
DMUS_CHORD_KEY Chord;
// Call GetParam with the time in lTime.
pPerformance->GetParam(GUID_ChordParam,-1,0,lTime,NULL,&Chord);
// Now, pull the key and scale. The root of the key is in bKey. The
 scale is a bit pattern, stored in dwScale. 
// The bit pattern starts at position 0 for the first note of the scale
 and moves up fom there. 
// Each set bit represents a semitone that is on, a member of the
 scale. There should be 7 set bits per 
// octave (every twelve bits). 

Here’s the structure: 

typedef struct _DMUS_CHORD_KEY
{
    WCHAR           wszName[16];        /* Name of the chord */
    WORD            wMeasure;           /* Measure this falls on */
    BYTE            bBeat;              /* Beat this falls on */
    BYTE            bSubChordCount;     /* Number of chords in the list
 of subchords */
    DMUS_SUBCHORD   SubChordList[DMUS_MAXSUBCHORD]; /* List of sub
 chords */
    DWORD           dwScale;            /* Scale underlying the entire
 chord */
    BYTE            bKey;               /* Key underlying the entire
 chord */
} DMUS_CHORD_KEY;

Try it and write some code to print out the values and see experimentally what happens when different chords and keys are defined in DirectMusic Producer.

How do I globally transpose the entire performance (except the drum tracks) to another key? For example: it’s in Db major and I want it to play in C?

To change the key, there are several techniques:

How do you transpose all the chords in a Segment’s Chord Track by a given amount?

Here’s some code that will do this:

// Code to transpose all chords in a Segment’s Chord Track. 

// Transpose chord or key root by a given amount, clamped to a two-
octave range.
inline void AddAndClamp(BYTE& rbRoot, int nAmount)
{
   if (nAmount < -24 || nAmount > 24) return; // only allow 2-octave 
transposition
   char chRoot = (char) rbRoot;
   chRoot += nAmount;
   while (chRoot < 0) chRoot += 12;
   while (chRoot > 23) chRoot -= 12;
   rbRoot = (BYTE) chRoot;
}

// Transpose all chords in a Segment’s Chord Track by a given amount. 
// NOTE: this function assumes that the first chord occurs at time 0 in 
the Chord Track.
// If it does not, the time of the first chord (use in the call to
 SetParam) needs to be 
// computed from its measure and beat, along with time signature
 information from the Segment.
HRESULT Transpose(int nAmount, IDirectMusicPerformance* pPerformance,
 IDirectMusicSegment* pSegment)
{
   if (!pPerformance || !pSegment) return E_INVALIDARG;

   HRESULT hr = S_OK;
   DMUS_CHORD_KEY ChordKey;
   MUSIC_TIME mtChord = 0;
   MUSIC_TIME mtNextChord = 0;
   MUSIC_TIME mtSegmentLength = 0;

   // Get the length of the Segment
   hr = pSegment->GetLength(&mtSegmentLength);
   if (S_OK != hr) return hr;

   do
   {
      // Get the next chord.
      hr = pSegment->GetParam(GUID_ChordParam, 0xffffffff, 0, mtChord, 
&mtNextChord, &ChordKey);
      if (S_OK != hr) return hr;

      if (ChordKey.bSubChordCount <= 0) return E_FAIL;
 
      // Change the chord.
      AddAndClamp(ChordKey.bKey, nAmount);
      for(int i = 0; i < ChordKey.bSubChordCount; i++)
      {
         AddAndClamp(ChordKey.SubChordList[i].bChordRoot, nAmount);
         AddAndClamp(ChordKey.SubChordList[i].bScaleRoot, nAmount);
      }
 
      // Plunk the chord back in.
      hr = pSegment->SetParam(GUID_ChordParam, 0xffffffff, 0, mtChord, 
&ChordKey);
      if (S_OK != hr) return hr;

      mtChord += mtNextChord;
   } while (mtChord < mtSegmentLength);

   return hr;
}

How do I look ahead to see what notes are playing in a sequence?

Write a Tool that intercepts the notes, then passes them on. The Tool can run DMUS_PMSGF_TOOL_IMMEDIATE, in which case it gets all events half a second before they are heard.

Messages and Tools

I’m trying to send user events in DirectMusic – that is, DMUS_PMSG objects whose dwType field is DMUS_PMSGT_USER. It works fine, except that I can’t attach any user data to it. So I can fire it and receive it later, but I don’t know what I fired it for. I realize that there is an IUnknown* in the struct, but in this instance, all I really want to add is a Boolean or maybe a small struct.

The way to send your own data is to create a COM interface for the data with, at minimum, just IUnknown on it. Then, you can handle memory allocation and freeing yourself. If the message is deleted out of the blue, it can safely do so because it calls Release() on the interface.

What are Tools?

Tools are tiny pieces of code that intercept music Messages (including MIDI data) after they are generated by Segments. Tools can modify, add, or remove these Messages (called “PMsg”s) before they are passed on to the port. Tools are accessed through the IDirectMusicTool interface. Tools can read the current controlling track information through calls to GetParam():

Some example Tools include:

There is a sample in the SDK called, “EchoTool,” which shows how to implement a Tool in DirectMusic. The default location is C:\mssdk\samples\multimedia\dmusic\src\EchoTool.

Notifications

When is DMUS_NOTIFICATION_SEGALMOSTEND sent?

About half a second before the Segment finishes playing (or, more precisely, Prepare Time away from end). This gives you the chance to queue up another Segment to replace it. Note that this Message is sent for both primary and Secondary Segments, so you need to check the SegmentState that comes with it. You can Get and Set the PrepareTime by calling the appropriate Performance methods (GetPrepareTime, SetPrepareTime).

What’s the order of notifications I should expect and what do they really mean?

If a Segment is not interrupted, you should receive SEGSTART, then SEGALMOSTEND (at the time described above), then SEGEND. If you are currently playing a primary Segment and play another primary Segment at QUEUE time, the current primary Segment will not receive a SEGEND notification. A Segment may be interrupted by a call to IDirectMusicPerformance->Stop(), or the primary Segment may be interrupted by calling IDirectMusicPerformance->PlaySegment() to start another primary Segment before the current primary Segment ends. In these cases, a SEGABORT notification will be sent when the interrupted Segment stops. SEGALMOSTEND will not be sent for it.

IDirectMusicTrack and Patterns

In a DirectMusic Producer Segment, there is no ability to add a Motif Track. Is there a way to do this programmatically and not through DirectMusic Producer?

Correct, Motif Tracks are created “on the fly” by DirectMusic when you call the Style->GetMotif() API. This creates a Segment with a Motif Track in it. There is no way to stick a Motif Track into a Segment in DirectMusic Producer because there’s no file format for saving Motif Tracks. We have several features coming in a future version of DirectX that address this in different ways.

Is there any way to add your own Parts in a Pattern?

No, there is no way to access the Parts in a Pattern in a Style, other than by actually parsing the Style yourself. You can create the representation of the Style (with any Parts or Patterns that you put in it) as a file and dynamically stream it into the Style. The easiest way to do this is just to write the whole thing into a chunk of memory, point the Loader to it with SetObject(), then call GetObject() to create a new Style with it. If you continue by using the same object, be sure to ReleaseObject() before reloading the Style or the Loader will simply return the previous object.

Is there any way to add your own Track types in a Pattern?

No, because Patterns are not composed of Tracks. They are composed of Parts, which are internal to Patterns. Tracks, on the other hand, are plug-ins that can be placed within Segments, but only Segments.

How about the addition of new Part types to Patterns then? Is this possible?

Sorry, it’s a fixed file format. There’s no plug-in technology within a Style.

The Composition Engine

What is the difference between a Segment and a Template Segment?

A Template is a special type of a Segment. It is designed for Style playback, but without a chord progression. It carries the “roadmap” for building a Style playback Segment. It is used in conjunction with a Style and Chordmap to compose music at run-time. Unlike other Segments, a Template Segment is never played directly by an application; instead, it is passed to the Composer object to be used in creating a musical Segment.

How do I go about creating transitions?

There are two methods for composing transitional Segments:

Both these methods take a Chordmap, a command, and a set of flags as parameters.

What is a Shape?

A Shape is a predefined pattern of intensity. It uses predefined Groove Level behavior to create a Segment that serves a specific musical purpose. Some types of Shapes are:

What is the difference between IDirectMusicComposer::ComposeSegmentfromTemplate() and IDirectMusicComposer::ComposeSegementfromShape()?

IDirectMusicComposer::ComposeSegmentfromTemplate() uses a Template Segment and a Chordmap to build a new Style Segment. IDirectMusicComposer:: ComposeSegmentfromTemplate() uses a Shape, Style, and Chordmap to build a new Style Segment.

ComposeSegmentfromTemplate() gives you a lot more control than ComposeSegmentfromShape(). It gives you control over where chords start and stop. But it assumes that your Template is a specific length. With ComposeSegmentfromShape(), you don’t start with a Segment, you build it. You can also have Segments with different lengths. Basically, you can have lots of different kinds of behaviors without having to write different Templates.

Troubleshooting Issues

I am trying to load a MIDI file with DirectMusic. It is a small file (about 80 kilobytes [KB]), yet I am experiencing long load times. Why is this?

This could be happening if you are running debug bits. If you have debug bits and the debug level is turned up, loading can indeed take a long time.

Every so often I get a stuck note in DirectMusic. The stuck notes occur during the game, using the Microsoft software synthesizer. I am only playing primary Segments, and I invalidate the currently playing Segment when a new Segment is played on the Performance. What can I do to prevent getting a stuck note?

Invalidating the Performance is not good practice. The invalidation is probably overloading the Microsoft synthesizer’s event queue.

I am trying to create a DirectMusicPort, but I get the following return value:   DMUS_E_DSOUND_NOT_SET. What am I doing wrong?

You received DMUS_E_DSOUND_NOT_SET because the port could not be created, because no DirectSound object has been specified. On non-accelerated systems, DirectMusic uses DirectSound for output. If your application uses both DirectSound and DirectMusic—when you initialize your Performance, you should pass the DirectSound object you have previously created. If a NULL parameter is used, DirectMusic will create its own DirectSound object and this can cause problems. So, DO this:

// Init the performance using the DirectSound object
hr = g_pPerformance->Init( &pDM, pDSound, hWnd );

Not this:

// Init the performance using the NULL object
hr = g_pPerformance->Init( &pDM, NULL, NULL );

I am trying to change the Groove Level of a Segment. I am using the following code:

DMUS_COMMAND_PARAM GrooveCommand;

  GrooveCommand.bCommand=DMUS_COMMANDT_GROOVE;
  GrooveCommand.bGrooveLevel=100;
  GrooveCommand.bGrooveRange=5;

  HRESULT hr=m_pSegment->SetParam(GUID_CommandParam, 0xFFFFFFFF, 0, 0,(void
*)(&GrooveCommand) );

However, the Segment continues playing as is. What am I doing wrong?

This command actually changes the value in the Command Track of the Segment; it doesn’t issue a “change the Groove Level now” command. To do that, you need to use:

char gl;
Performance->SetGlobalParam(GUID_PerfMasterGrooveLevel, &gl, sizeof(gl));
Where gl = desired Groove Level offset.

Make sure your composer has a Groove Level Track in his or her Segment. Have the composer set the Groove Level to some value that you decide upon.

Important Note   The Groove Level you set is relative to the Groove Level set by the composer, not an absolute setting. So, if the segment was created with a Groove Level of 30 and you use the above with gl = 20, then Patterns with Groove Level 50 will play.