midiOutUnprepareHeader access violation

Discussion in 'MIDI' started by markwa, Mar 11, 2005.

  1. markwa

    markwa Guest

    This question is for an experienced MIDI software programmer. I'm
    adding SysEx support to MidiNotate, using the Windows Multimedia
    midiIn/midiOut APIs. Everything is working except I'm getting an
    access violation on a call to midiOutUnprepareHeader.

    A sketch of the revelant code is this:
    1. Upon opening a MIDI out device, create some reusable
    MIDIHDR objects, and prepare each with midiOutPrepareHeader.
    2. Call midiOutLongMsg to send a sysex message in as many
    MIDIHDR(->lpData) objects as are needed given the lpData
    buffer size.
    3. Upon completed delivery of each MIDIHDR (in the MOM_DONE
    case of MidiOutCallback), re-prepare the MIDIHDR object
    with another call to midiOutPrepareHeader.
    4. Before calling midiOutClose, call midiOutUnprepareHeader
    for each reusable MIDIHDR object.

    It is in step#4 that midiOutUnprepareHeader access violates for every
    MIDIHDR.

    I've removed the called to midiOutUnprepareHeader, and the problem goes
    away. I don't know what harm is done if
    midiOutUnprepareHeader isn't called. Perhaps the worse that happens is
    some leakage of memory inside the Multimedia system. I don't know how
    much leakage that would be. The application allocates and destroys the
    MIDIHDR object itself, as well as the MIDIHDR->lpData, so I don't know
    what memory, if any, the MM System would be hanging onto without an
    application call to midiOutUnprepareHeader.

    Do you know what might be causing the access violation in the call to
    midiOutUnprepareHeader? Or, do you want what the consequence is for
    not calling it?

    The best source code I could find as a prototype for this is Paul
    Messick's Maximum MIDI. I think I'm generally doing it the same way he
    is.

    Thanking in advance for any tip.
    -- Mark Walsen

    Notation Software, Inc.
    www.notation.com
     
    markwa, Mar 11, 2005
    #1
    1. Advertisements

  2. You're calling PrepareHeader twice without calling UnprepareHeader in
    between?
    You should call midiOutReset to abort pending buffers, if there are
    any.

    Did you modify or free any buffer before calling
    midiOutUnprepareHeader?


    HTH
    Clemens
     
    Clemens Ladisch, Mar 11, 2005
    #2
    1. Advertisements

  3. I'll have to check my source code when i get home...
    But you've probably still got it "in use" SOMEhow...
    I remember running into that.

    On a totally unrelated topic, somewhat spamlike:
    I've moved the "mididev" yahoogroup I started over
    to google groups. Nicer threading and less obtrusive
    ads.

    So all you developers who are actually READING this
    topic can sign up for a list with a LOT less traffic
    about ONLY midi programming questions.
    (Well, and whatever else we programmers like to talk about.)
    Talkin' bout writing code, not editing patches here...

    http://groups.google.com/group/mididev

    There were quite a few people who actually went thru
    the ugly yahoo signup process to join it, so I'm thinkin
    this list will get decent traffic.

    And, I'll (of course) listen up in here as well.
    But, man, I can only handle SO many messages
    about moogs :)
    JUST kiddin' - I've never touched the things but
    wish I woulda had the money back when I got
    the catalog in the mail...
    My mouth watered, but I had no cash...:(
    (I needed my Commodore 64 more!)

    ....Steve http://shazware.com


    PS: load "*",8,1
     
    stephen.hazel, Mar 11, 2005
    #3
  4. stephen.hazel, Mar 11, 2005
    #4
  5. markwa

    jokeboy3 Guest

    Also use midiInAddBuffer and midiInUnprepareHeader. You are correct
    that a memory leak will occur if you take shortcuts to solve the
    problem. I will assume you already allocated MIDIHDR properly. Here is
    what I use.

    Initialize:
    - open with midiInOpen() using callback function
    - prepare each object with midiInPrepareHeader() for next sysex
    - buffer each object with midiInAddBuffer() for next sysex
    - start with midiInStart()

    In callback function:
    - unprepare current object with midiInUnprepareHeader() old sysex
    - analyze or use the sysex data retrieved
    - prepare current object with midiInPrepareHeader() for next sysex
    - buffer current object with midiInAddBuffer() for next sysex
    - exit

    When closing application:
    - stop with midiInStop()
    - reset with midiInReset()
    - unprepare each object with midiInUnprepareHeader() old sysex
    - close with midiInClose()
     
    jokeboy3, Mar 12, 2005
    #5
  6. Well, I looked at the code i use.
    I have code for doing sysex, but haven't
    used it in ditty. So it isn't totally tested.
    One thing that bit me was marking the
    correct sysex header as "done" when
    the callback got it's "done" message.
    I only had 2 buffers, but was sometimes
    marking the wrong one as "done".

    Sorry, that's all i got.
    Did ya get the bug swatted yet?

    ....Steve
     
    stephen.hazel, Mar 13, 2005
    #6
  7. markwa

    markwa Guest

    Hello Clemens,

    Thanks for your help on the above problem.
    I checked this carefully with run-time trace statements, and that is
    not happening.
    I was not calling midiOutReset before calling midiOutUnprepareHeader.
    I have now added the call, in this sequence:
    1. midiOutReset
    2. midiOutUnprepareHeader for heac MIDIHDR
    3. midiOutClose
    I'm still getting the access violations calling midiOutUnprepareHeader

    _Before_ calling midiOutPrepareHeader, I allocated the buffer for
    MIDIHDR->lpData.

    _After_ calling midiOutUnprepareHeader, I deallocated the buffer for
    MIDIHDR->lpData and set MIDIHDR->lpData to NULL. I also have tried
    deallocating MIDIHDR->lpData and setting MIDIHDR->lpData to NULL
    _before_ calling midiOutUnprepareHeader. In either case, I still get
    the access violation calling midiOutUnprepareHeader.

    Your suggestions looked promising, but I'm still having the problem.
    Any more ideas?

    Thanks!
    -- Mark
     
    markwa, Mar 14, 2005
    #7
  8. markwa

    markwa Guest

    Hello JokeB,

    I'm having trouble with midiOut sysex, not midiIn sysex. The
    Multimedia APIs for midiIn and midiOut are similar, but not completely
    parallel. For example, there is a midiInAddBuffer, but not a
    midiOutAddBuffer.

    Cheers
    -- Mark
     
    markwa, Mar 14, 2005
    #8
  9. markwa

    markwa Guest

    Hi Steve,

    I'm getting the access violations calling midiOutUnprepareHeader even
    when I haven't not even played back any sysex. That is, I'll run the
    app, and then immediately close it. That test will do the
    midiOutPrepareHeader calls upon startup, and midiOutUnprepareHeader
    calls upon closing. I still get the access violation calling
    midiOutUnprepareHeader.

    Cheers
    -- Mark
    when the callback got it's "done" message.
     
    markwa, Mar 14, 2005
    #9
  10. markwa

    jokeboy3 Guest

    Midi out, duh !
     
    jokeboy3, Mar 15, 2005
    #10
  11. markwa

    jokeboy3 Guest

    Your steps resembled a process midi input would use. Perhaps consider
    steps for midi output that I use. One buffer to prepare sysex for the
    output before sending, and is unprepared immediately after sending the
    sysex. Closing the application does not unprepare since already done. I
    have not detected a memory leakage or reduction in speed. The
    MidiOutFunc() callback function is optional and not shown below. For
    example,

    // Send one sysex message to the output.
    // Repeat for each sysex message to send.

    CString csBuffer;
    HANDLE hData;
    LPVOID lpData;
    HANDLE hMidiHdr;
    LPMIDIHDR lpMidiHdr;
    USHORT lenData;

    // Enter sysex message here.
    // Alternatives to CString buffer are optional.
    csBuffer = "";
    lenData = csBuffer.GetLength();

    // Allocate memory for buffer, and buffer information
    hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, lenData);
    lpData = GlobalLock(hData);
    memcpy(lpData, (LPCTSTR)csBuffer, lenData);
    GlobalUnlock(hData); // not needed after copy

    hMidiHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof
    (MIDIHDR));
    lpMidiHdr = (LPMIDIHDR)GlobalLock(hMidiHdr);
    memset(lpMidiHdr, 0, sizeof (MIDIHDR));
    GlobalUnlock(hMidiHdr); // not needed after copy

    lpMidiHdr->lpData = (char *)lpData;
    lpMidiHdr->dwBufferLength = lenData;
    lpMidiHdr->dwFlags = 0; // must be zero
    lpMidiHdr->dwUser = 0; // not needed

    // Prepare.
    wErr = midiOutPrepareHeader((HMIDIOUT)m_hMidiDevice, lpMidiHdr,
    sizeof (MIDIHDR));
    wErr = midiOutLongMsg((HMIDIOUT)m_hMidiDevice, lpMidiHdr, sizeof
    (MIDIHDR));

    // Unprepare.
    while (1) {
    wErr = midiOutUnprepareHeader((HMIDIOUT)m_hMidiDevice,
    lpMidiHdr, sizeof (MIDIHDR));
    if (wErr == 0) { // okay
    break;
    } else if (wErr == MIDIERR_STILLPLAYING) {
    } else if (wErr != 0) { // unknown error
    break;
    }
    //Sleep(1L); // optional, to release resources during one sysex
    message
    }
    //Sleep(2L); // optional, to release resources between multiple
    sysex messages

    // Free.
    GlobalFree(hData);
    GlobalFree(hMidiHdr);
     
    jokeboy3, Mar 15, 2005
    #11
  12. Could you try it with a minimal test program that just opens the
    device and then calls midiOutPrepareHeader and midiOutUnprepareHeader?

    Does the access violation happen with all drivers?


    Regards,
    Clemens
     
    Clemens Ladisch, Mar 15, 2005
    #12
  13. markwa

    markwa Guest

    Hello Clemens,

    Of course, I should have already done what you suggested. The minimal
    case worked fine. That led me to discover that the error was not in
    the sequence of calls I was making to midiOutPrepareHeader and
    midiOutUnprepareHeader. The error was basically a coding error, which
    is now fixed.

    Thanks for your help, and for everyone else's help in this thread. I
    appreciate it.

    Cheers
    -- Mark
    midiOutUnprepareHeader?
     
    markwa, Mar 15, 2005
    #13
  14. did you call any critical function (e.g. memory allocation) inside a
    callback function (e.g. multimedia timer callback, midiOutProc,
    midiInProc)?


    if so then see MSDN
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midioutproc.asp
    which system functions are allowed to be called.

    The Windows sysex buffering system is designed so difficult that it is
    possible to add and remove buffers asynchron while playing/recording
    and is necessary that unlimitted sysex size can be sent or received in
    undefined number of blocks (a block does not necessarily contain a
    complete sysex message).

    Now when I look at your source code that you posted before I see an
    other more critical programming fault:

    lpData = GlobalLock(hData);
    memcpy(lpData, (LPCTSTR)csBuffer, lenData);
    GlobalUnlock(hData); // not needed after copy

    hMidiHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof
    (MIDIHDR));
    lpMidiHdr = (LPMIDIHDR)GlobalLock(hMidiHdr);
    memset(lpMidiHdr, 0, sizeof (MIDIHDR));
    GlobalUnlock(hMidiHdr); // not needed after copy

    lpMidiHdr->lpData = (char *)lpData;


    ===> you use lpData after you unlocked it?

    for moveable allocated data the pointer lpData is only valid between
    GlobalLock and GlobalUnlock calls (that is the idea of using global
    memory handles). lpData points to nirwana when system moves block away
    which can cause access violation.
    Your comment "// not needed after copy" is wrong when you still use it
    some lines later.

    Günter
    http://www.gnmidi.com
     
    Guenter Nagler, Mar 16, 2005
    #14
  15. Hi Gunter,
    I'm not positive about this, but I THINK you can call anything in the
    callback now adays. There's nothing special about how the callback
    runs anymore (since the move to winNTx from win9x).
    You shouldn't be taking a huge amount of time or anything, but as
    long as it's quick...

    If you want to still run on win9x, you'll have to be careful, but if
    you're
    using winNTx, I think you can do anything in there and also don't need
    to bother with GlobalLock, etc, which i THINK is deprecated...

    If anyone could confirm or deny this, that'd be cool...:)

    ....Steve
     
    stephen.hazel, Mar 16, 2005
    #15
  16. markwa

    markwa Guest

    Hello Gunter,
    The GlobalLock/Unlock code was posted by someone else, not me.

    Actually, years ago I totally weeded out all Global allocations for
    Multimedia subsystem calls. It has been so many years since I did
    that, that I forget what source of information I relied on to base that
    decision. Is there any official word anywhere, other than Microsoft
    Multimedia API documentation that hasn't been updated for 15 years,
    that says that Global allocations are needed for Multimedia MIDI APIs?

    Cheers
    -- Mark
     
    markwa, Mar 17, 2005
    #16
  17. markwa

    jokeboy3 Guest

    The code was mine, regarding GlobalUnlock(). Thanks for the correction,
    Guenter, so I can move them after the API.

    BTW. Sysex greater than 64k is split in my code. For 128k:
    Run once : sysex = "F0 ... 64k of data"
    Run again : sysex = "... another 64k of data ... F7"

    I have not seen an official word. Runs fine on my fast computer without
    it, but who knows. So, I continue the tradition that sharing data
    between applications or manipulating the windows heap needs protection.
    In this case, the app is sharing data with external soundcard drivers.
    Heap functions are a new alternative to GlobalAlloc() and can handle
    larger data and more handles fast. Not used by me considering sysex is
    small.

    Joseph
     
    jokeboy3, Mar 17, 2005
    #17
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.