Performance Notifications


The standard mechanism for managing notifications is pretty comprehensive. DirectMusic's Performance Engine provides different categories of notifications, which you can turn on or off as needed. These range from notifications that tell the application when different sounds and music clips start and stop to notifications that indicate when musical events, such as changes in time signature or groove level, occur. The notifications pass through the Performance in pmsg form, much like note or wave messages. However, instead of sending these messages to the synthesizer, DirectMusic delivers them to the application.

The application can even choose how to receive the notifications; it can set up an event and be signaled when one arrives, or it can simply poll for the notification messages.

Notification Categories

The notification pmsgs are broken into categories, defined via GUIDs. Many of the categories have subtypes, which are in turn defined by a constant DWORD. The categories include:

  • GUID_NOTIFICATION_CHORD: This notification indicates that a chord has changed.

  • GUID_NOTIFICATION_COMMAND: This notification indicates whenever a groove level track broadcasts a new groove level or embellishment command.

  • GUID_NOTIFICATION_MEASUREANDBEAT: One of these notifications is sent every beat. It carries the measure and beat number with it.

  • GUID_NOTIFICATION_PERFORMANCE: This notification indicates a change in the status of all Segments in the Performance. For example, it signals when sound or music first starts as well as when the last Segment finishes playing.

  • GUID_NOTIFICATION_RECOMPOSE: This notification indicates when a Track is recomposed, which can automatically occur on the fly. This is primarily used by authoring tools (in other words, DirectMusic Producer).

  • GUID_NOTIFICATION_SEGMENT: This notification indicates a change in the playback status of a Segment, including starting, looping, and stopping.

Of all of these, the Segment commands are by far the most useful. These indicate when a Segment starts, stops, or loops and even when it is stopped prematurely. The notification mechanism also hands the application a pointer to the IDirectMusicSegmentState interface associated with the playing Segment.

The Notification Pmsg

The DMUS_NOTIFICATION_PMSG starts with the DMUS_ PMSG_PART core, which provides the necessary infrastructure for pmsg delivery and follows with several fields used to identify the type of the notification:

 typedef struct _DMUS_NOTIFICATION_PMSG {     /* begin DMUS_PMSG_PART */     DMUS_PMSG_PART     /* end DMUS_PMSG_PART */     GUID    guidNotificationType;   // GUID indicating notification category.   DWORD dwNotificationOption;   // Subtype of the notification.   DWORD dwField1;               // Optional data field.   DWORD dwField2;               // Second data field. } DMUS_NOTIFICATION_PMSG; 

guidNotificationType indicates the category of notification. These are the notification categories listed earlier. A GUID is used to ensure that new notification categories can be introduced by different parties without any danger of clashing. Each category can optionally supply more than one subtype, identified in dwNotificationOption. The following two fields, dwField1 and dwField2, carry notification-specific data. When used, their functionality is defined by the notification category and option.

Additional relevant information is stored in the DMUS_PMSG_ PART portion. Of particular interest are:

    REFERENCE_TIME     rtTime;     // Clock time this pmsg occurs.    MUSIC_TIME         mtTime;     // Music time of this pmsg.    IUnknown*          punkUser;   // Optional COM pointer to referenced object. 

rtTime and mtTime carry the specific time that the notification occurs. punkUser carries a pointer to a COM interface, allowing the notification to drag along a pointer to an associated object, if appropriate. For example, the GUID_NOTIFICATION_SEGMENT command uses punkUser to carry a pointer to the IDirectMusic-SegmentState associated with the playing Segment.

Enabling a Notification

By default, DirectMusic does not generate any notifications. To activate a notification, you must call the performance's AddNotificationType() method:

    // Enable Segment notifications.    pPerformance->AddNotificationType(GUID_NOTIFICATION_SEGMENT); 

In this example, setting GUID_NOTIFICATION_SEGMENT tells DirectMusic to generate a notification every time a Segment starts, loops, or stops.

To disable a notification, call the inverse method:

    // Disable time signature notifications    pPerformance->RemoveNotificationType(GUID_NOTIFICATION_MEASUREANDBEAT); 

Receiving a Notification

There are several ways to receive the notification pmsg. You can poll for it. You can set up an event and wait to be signaled. Or you can stick a Tool in the Performance and use the Tool to intercept it.

Polling for Notifications

Polling is the easiest way to receive a notification and typically the most useful. If the application has a regular loop of things that it does once per frame, then it can check for notifications as part of that loop. The Performance provides a method, GetNotificationPMsg(), that retrieves any due notification pmsgs.

    DMUS_NOTIFICATION_PMSG *pPmsg;    while (pPerformance->GetNotificationPMsg(&pPmsg) == S_OK)    {      // Do something here with the data from the notification, and then free it.      pPerformance->FreePMsg((DMUS_PMSG*)pPmsg);    } 

GetNotificationPMsg() returns S_OK for every notification it has in its queue; then it returns S_FALSE when there are none left. Be careful: S_FALSE and S_OK are both considered success codes, so don't use the SUCCEEDED(hr) macro because it will always succeed. Instead, check specifically for S_OK.

Note

Notifications don't wait around forever. If the application does not call GetNotificationPMsg() within a reasonable time (by default, two seconds), DirectMusic discards the notification.

Waiting on an Event

If you would prefer, DirectMusic can signal your application exactly when a notification is ready. This option uses the standard event signaling Windows APIs.

First, create an event handle with a call to the Windows Create-Event() function.

 HANDLE hNotify = CreateEvent(NULL, FALSE, FALSE, NULL); 

After creating the event, hand it to the Performance via its SetNotificationHandle() method. This tells the Performance to signal the event whenever a notification is due.

 pPerformance->SetNotificationHandle(hNotify, 4000); 

The second parameter specifies how long, in milliseconds, the Performance should hold onto the notification if it is not retrieved. This keeps old notifications from piling up if the application is temporarily not listening to them. A value of 0 in this parameter indicates that the default time of two seconds should be used.

Once the event is prepared, the application calls the Windows API WaitForSingleObject() to wait for a notification. It's important to understand that this call stops (or "blocks") the calling thread until a notification arrives. Because of this, it doesn't work to place this call in a thread that needs to be doing anything while waiting, so be sure to place it in a thread that can afford to wait. For example, it would not work to call WaitForSingleObject() in the middle of a graphics/ UI loop.

 WaitForSingleObject(hNotify, 1000); 

In this code, the caller waits for a notification for up to one second. Once the notification arrives, the caller retrieves the notification via a call to GetNotificationPMsg().

Capturing Notifications with a Tool

There's a third way to receive notification pmsgs. Create a Tool with an IDirectMusicTool interface, set it up to handle DMUS_PMSGT_ NOTIFICATION message type, and place it in the Performance. When a notification comes down the pike, the DirectMusic calls the Tool's ProcessPMsg() method, and it can then notify the application in whatever way it wants to.

This approach requires a little more work, but it buys extra flexibility:

  • You have three options for delivery timing. Choose DMUS_PMSGF_TOOL_IMMEDIATE, and the Tool gets called the moment the notification is generated, which is typically 500ms ahead of the time stamp. Choose DMUS_PMSGF_TOOL_QUEUE, and the Tool is called a little early, typically about 50ms. Choose DMUS_PMSGF_TOOL_ATTIME, and the Tool is called as close to the exact time stamp as possible, which is what the regular notification system does. As it turns out, DMUS_PMSGF_TOOL_QUEUE is particularly useful because being slightly early (but still with the correct time stamp) results in a response that might feel more accurate.

  • You control where the notifications are sent by placing the Tool in the appropriate object. You can insert Tools in Segments, AudioPaths, and the Performance. A notification tool placed in a Segment only receives notifications generated by that Segment, which makes it very easy to understand the context of the notification.

  • If you plan to also use lyric notifications (we discuss that next), you need to write a Tool anyway, and so you might as well use the same mechanism for both and avoid the extra code.

For the last reason, we are using a Tool to capture notifications in Jones.

Setting up a Tool is not as hard as it may seem. We learned this in the last chapter when we created a Tool to lengthen wave pmsgs. Define a C++ class based on the IDirectMusicTool interface. Set it up to only receive DMUS_PMSGT_NOTIFICATION messages and receive them at DMUS_PMSGF_TOOL_QUEUE time (which means they arrive about 50ms or roughly a frame early). This chapter's Jones implementation covers installing such a Tool in detail, so we can skip the code for defining and installing the Tool for now.

The active part of the Tool is the method ProcessPMsg(), which receives the message. For processing notifications, it verifies that the pmsg is of type DMUS_PMSGT_NOTIFICATION, reads the parameters from the notification pmsg, does whatever it needs to do, and then returns.

 HRESULT CNotifyTool::ProcessPMsg(IDirectMusicPerformance* pPerf,                                   DMUS_PMSG* pPMSG) {    if (pPMSG->dwType == DMUS_PMSGT_NOTIFICATION)    {         // Read the notification and do something.    }    // No need to send this on down the line.    return DMUS_S_FREE; } 

As you can see, the code to receive a notification via a Tool is remarkably simple.

Note

Be sure not to linger long inside the Tool's ProcessPMsg() method. This is a high-priority thread and should not be used for UI or file I/O work, which could destroy the timing. Instead, set a flag or place a message in a queue and then butt out immediately.

Interpreting the Notification

Once we have the notification in hand, we need to make sense of it. This involves reading the dwType, guidNotificationType, and dwNotificationOption fields to glean the specific notification.

When working with both lyrics and notifications, it's important to identify the pmsg type by checking the dwType field of the pmsg.

    // Is this a notification? (Only applied to reading from a Tool.)    if (pMsg->dwType == DMUS_PMSGT_NOTIFICATION)    {        DMUS_NOTIFICATION_PMSG *pNotify = (DMUS_NOTIFICATION_PMSG *) pMsg;        // It's a notification!    } 

If it is a notification, check both guidNotificationType for the category of notification and dwNotificationOption for the subtype. In this example, the application wants to know when the music ends, so it can fade out the scene.

       DMUS_NOTIFICATION_PMSG *pNotify = (DMUS_NOTIFICATION_PMSG *) pMsg;       DWORD dwNotificationOption = pNotify->dwNotificationOption;       if (pNotify->guidNotificationType == GUID_NOTIFICATION_PERFORMANCE)       {           if (dwNotificationOption == DMUS_NOTIFICATION_MUSICSTOPPED)           {               // Music's over. Fade to black.           }       } 




DirectX 9 Audio Exposed(c) Interactive Audio Development
DirectX 9 Audio Exposed: Interactive Audio Development
ISBN: 1556222882
EAN: 2147483647
Year: 2006
Pages: 170

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net