DirectMusic engine problem

This is a discussion on DirectMusic engine problem within the Game Programming forums, part of the General Programming Boards category; I really need your guys help on this one. Shakti and I have both attempted to debug this code and ...

  1. #1
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598

    DirectMusic engine problem

    I really need your guys help on this one. Shakti and I have both attempted to debug this code and we simpy cannot figure out what is going on.

    We keep getting an access violation error when the engine shuts down, but only in debug mode. The engine shuts down fine when in retail builds and you can also re-fire up DirectX and play any game you want. So to me....it seems like my code is ok.

    I've attached the code.

    The header CQuickCom.h defines SAFE_RELEASE as:
    Code:
    if (COMobject) COMobject->Release();

    Forgive the mess - this engine is still a work in progress.

    I hope you can see how this system works from the code, but just in case...here is how you use it. No latency is supported in this version of the code...I have a newer version that does. You would set the latency by passing a 2nd param to Create() that specifies the desired latency in ms.

    Code:
    //Base DirectMusic object
    CDXAudio SFX;
    
    //Create using 1 pchannel
    SFX.Create(1);
    
    //Sound emitter object
    CDXSoundEmitter SFXSystem;
    
    //Create system using CDXAudio object and its properties
    SFXSystem.Create(SFX.GetPerformance(),SFC.GetLoader());
    
    //Structure to track sound IDs
    struct tagTestSounds
    {
      DWORD sndHello;
      DWORD sndTest;
    }TestSounds;
    
    
    TestSounds.sndHello=SFXSystem.LoadSound(L"Hello.wav");
    TestSounds.sndTest=SFXSystem.LoadSound(L"Test.wav");
    
    //Play the sounds as secondary segments
    //Primary segments will stop all currently playing segments
    //Secondary segments will be mixed - thus good for sound effects
    SFXSystem.Play(TestSounds.sndHello,CDX_SECONDARY_SEG);
    SFXSystem.Play(TestSounds.sndTest,CDX_SECONDARY_SEG);
    And that's it. Really simple to use.


    Now for a description of the classes:

    CDXAudio is the base IDirectMusic8 object. It creates the performance, creates the loader, sets the number of pchannels, and sets the latency in ms.

    CDXSoundEmitter is an object that emits sounds -> it requires a CDXAudio object, an IDirectMusicPerformance8 object, and an IDirectMusicLoader8 object.

    CDXSoundEmitter has a vector of CDXSoundSegments.
    CDXSoundSegment encapsulates one complete IDirectMusicSoundSegment8 along with segment state information. Sound segments are added to the vector by calling LoadSound() which in turn calls IDirectMusicLoader8::LoadSound which actually creates a valid segment interface pointer. The temporary in this function is suspect. However we have both tested the pointers and the pointers in the temporary and the pointers in the copy DO NOT point to the same memory. They are not the same. So this can't be the problem. Besides if this was the case, SAFE_RELEASE() would figure out that the segment pointer was invalid and would not call Release().

    The problem according to the debugger is the destructor in CDXSoundSegment when we try to release SoundSegment and SoundSegState. Now get this.

    • Remove SAFE_RELEASE() from the CDXSoundSegment destructor.
    • In the CDXSoundEmitter destructor immediately after unload is called, call the Release() function on the segment and the segstate. This works perfect - no errors.
    • This is the current state of the code. The commented lines in the CDXSoundSegment destructor are the lines that are causing the error. The new code can be seen in the destructor for CDXSoundEmitter.


    Now why would releasing the segment and segstate from outside of the object work, but not work inside of the object? The only thing you are changing is the order in which things happen.

    If you call Release() in the destructor here is what happens.

    Event:....CDXSoundEmitter is going out of scope.
    • CDXSoundEmitter's destructor is called
    • The destructor iterates through the vector and calls unload on all the sound segments. According to DXSDK Unload() will work in all cases, even if no data has been downloaded.
    • Vector is then emptied, which calls the destructor for each object in the vector.
    • When object is deleted by vector code, CDXSoundSegment destructor is called.
    • CDXSoundSegment releases the segment and the segstate COM interfaces. ---> MSVC flags access violation error.



    If you make the changes I stated earlier to CDXSoundSegment and CDXSoundEmitter, here is what happens.

    Event:....CDXSoundEmitter is going out of scope.
    • CDXSoundEmitter's destructor is called
    • The destructor iterates through the vector and calls unload on all the sound segments. According to DXSDK Unload() will work in all cases, even if no data has been downloaded.
    • Iterator code then calls Release() on Segment object inside of CDXSoundSegment
    • Iterator code then calls Release() on SegmentState object inside of CDXSoundSegment
    • Vector is then emptied, which calls the destructor for each object in the vector.
    • When object is deleted by vector code, CDXSoundSegment destructor is called -> no actions are performed.



    I'm totally lost as to why the error happens when you release the segment and segstate inside of the destructor for CDXSoundSegment.

    Anyone have any ideas as to why this might be happening? Salem, Prelude, --- I'm clueless.

    Sorry so long but this system is complex so I posted all the code.

    BTW feel free to use this code.
    Last edited by VirtualAce; 03-14-2005 at 03:40 PM.

  2. #2
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    I see this line:

    Code:
    s_obj->SegState->Release();
    In the destructor of CDXSoundEmitter, however I do not see where you ever allocate for SegState. I get a crash when you try to dereference the NULL pointer. You don't use SAFE_RELEASE there.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

  3. #3
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Code:
    if (Playing==S_FALSE)
      {
          Performance->PlaySegmentEx(Sounds[_ID].Segment,
                                 NULL,
                                 NULL,
                                 flags,
                                 0,
                                 (IDirectMusicSegmentState **)Sounds[_ID].SegState,
                                 NULL,
                                 NULL);
      }
    This creates the segment state.

    From the SDK
    The IDirectMusicSegmentState8 interface represents a playing instance of a segment. When the IDirectMusicPerformance8::PlaySegment or IDirectMusicPerformance8::PlaySegmentEx method is called, the performance creates a segment state object that tracks the state of the playing segment. This object be passed toIDirectMusicPerformance8::StopEx.

    IDirectMusicSegmentState8 supersedes IDirectMusicSegmentState and adds new methods.

    In addition to the methods inherited from IUnknown, the IDirectMusicSegmentState8 interface exposes the following methods.
    Hence the interface is created by DirectMusic not by me. But the SDK does not say anything about releasing the seg state, but my book on DirectX audio does. Besides, the seg state release is not causing the problem. Take it out and you will still get an access violation.

    The C++ code looks fine to me, there must be a bug in DirectMusic or something else is going on.

    When PlayEx creates the segment state you store it for later use when you call StopEx and you pass in the created seg state.

    The only thing I can think of is perhaps StopEx releases the seg state but I don't think so because I don't call Stop() ever and I still get the violation error.

  4. #4
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    if (Playing==S_FALSE)
    {
    Performance->PlaySegmentEx(Sounds[_ID].Segment,
    NULL,
    NULL,
    flags,
    0,
    (IDirectMusicSegmentState **)Sounds[_ID].SegState,
    NULL,
    NULL);
    }
    SegState is IDirectMusicSegmentState *! You shouldn't have to use a cast here!

    Perhaps this could solve the problem?
    Code:
    Performance->PlaySegmentEx(Sounds[_ID].Segment,
                                 NULL,
                                 NULL,
                                 flags,
                                 0,
                                 &Sounds[_ID].SegState,
                                 NULL,
                                 NULL);
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  5. #5
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Yes you do have to use a cast there.

    The function requires a pointer to a COM interface of type IUnknown *. This allows you to pass any object that is derived from IUnknown. StopEx can also take a pointer to an IDirectMusicAudioPath8 interface in that same slot. You must cast it in order to get the correct instance created.

  6. #6
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    But you're casting a * to a **. You're just passing the pointer value (0) to the function. SegState will be unchanged after the call.
    Last edited by Sang-drax; 03-17-2005 at 04:56 PM.
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  7. #7
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    That's how the SDK shows it and that's how the book DirectX9: Audio Exposed shows it as well.

  8. #8
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    The only place SegState is modified in your code is in the constructor where it's assigned NULL. That seems wrong to me, at least.

    Doesn't it work if you add that ampersand to the PlaySegmentEx call? That way the function will actually be able to initialize SegState.
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Problem with game code.
    By ajdspud in forum C++ Programming
    Replies: 5
    Last Post: 02-14-2006, 06:39 PM
  2. Weird problem on '02 3.4L V6 auto
    By VirtualAce in forum A Brief History of Cprogramming.com
    Replies: 8
    Last Post: 01-12-2006, 12:05 AM
  3. Replies: 5
    Last Post: 11-07-2005, 11:34 PM
  4. searching problem
    By DaMenge in forum C Programming
    Replies: 9
    Last Post: 09-12-2005, 02:04 AM
  5. half ADT (nested struct) problem...
    By CyC|OpS in forum C Programming
    Replies: 1
    Last Post: 10-26-2002, 09:37 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21