Author Topic: BASS.NET: AccessViolationException when copying all the MIDI events to memory  (Read 347 times)

KaleidonKep99

  • Posts: 191
Ever since the marshalling issue has been fixed (http://www.un4seen.com/forum/?topic=18102.0), the old code Falcosoft made for the real-time simulation is broken.

Code: [Select]
                BASS_MIDI_EVENT[] eventChunk;
                try
                {
                    // Thank you so much Falcosoft for helping me here!!!
                    // Visit his website: http://falcosoft.hu/index.html#start
                    MessageBox.Show("i'm about to get the events");
                    MainWindow.KMCGlobals.eventc = (UInt32)BassMidi.BASS_MIDI_StreamGetEvents(MainWindow.KMCGlobals._recHandle, -1, 0, null); // Counts all the events in the MIDI

                    MessageBox.Show("now i'll copy them");
                    MainWindow.KMCGlobals.events = new BASS_MIDI_EVENT[MainWindow.KMCGlobals.eventc]; // Creates an array with the events count as size

                    for (int i = 0; i <= (MainWindow.KMCGlobals.eventc / 50000000); i++)
                    {
                        MessageBox.Show("getting subcount");
                        int subCount = Math.Min(50000000, (int)MainWindow.KMCGlobals.eventc - (i * 50000000));

                        MessageBox.Show("allocating chunk");
                        eventChunk = new BASS_MIDI_EVENT[subCount];

                        MessageBox.Show("copying events to chunk");
                        BassMidi.BASS_MIDI_StreamGetEvents(MainWindow.KMCGlobals._recHandle, -1, 0, eventChunk, i * 50000000, subCount); //Falcosoft: to avoid marshalling errors pass the smaller local buffer to the function
                        ^^^^ Where it does something weird, forcing CLR to throw an AccessViolationException

                        MessageBox.Show("copying temp chunk to main chunk");
                        eventChunk.CopyTo(MainWindow.KMCGlobals.events, i * 50000000); //Falcosoft: copy local buffer to global one at each iteration

                        MessageBox.Show("done");
                    }
                    eventChunk = null;
                }
                catch
                {
                    BASSCloseStreamException(new MIDILoadError("This MIDI is too big for the real-time conversion process.\n\nMake sure you're using the 64-bit version of the converter."));
                }

What could be the cause of this issue?

Falcosoft

  • Posts: 13
Hi,
I do not know what changes have been made in Bass/Bass.net that break the current code but you can try to experiment with smaller chunks.
If I were you I would introduce a maxChunkSize variable and try different  smaller values. With smaller chunks the processing will be slower but the end result will be the same:

Code: [Select]
BASS_MIDI_EVENT[] eventChunk;
int maxChunkSize = 1000000;
                try
                {
                    // Thank you so much Falcosoft for helping me here!!!
                    // Visit his website: http://falcosoft.hu/index.html#start
                    MessageBox.Show("i'm about to get the events");
                    MainWindow.KMCGlobals.eventc = (UInt32)BassMidi.BASS_MIDI_StreamGetEvents(MainWindow.KMCGlobals._recHandle, -1, 0, null); // Counts all the events in the MIDI

                    MessageBox.Show("now i'll copy them");
                    MainWindow.KMCGlobals.events = new BASS_MIDI_EVENT[MainWindow.KMCGlobals.eventc]; // Creates an array with the events count as size

                    for (int i = 0; i <= (MainWindow.KMCGlobals.eventc / maxChunkSize); i++)
                    {
                        //MessageBox.Show("getting subcount");
                        int subCount = Math.Min(maxChunkSize, (int)MainWindow.KMCGlobals.eventc - (i * maxChunkSize));

                       //MessageBox.Show("allocating chunk");
                        eventChunk = new BASS_MIDI_EVENT[subCount];

                        //MessageBox.Show("copying events to chunk");
                        BassMidi.BASS_MIDI_StreamGetEvents(MainWindow.KMCGlobals._recHandle, -1, 0, eventChunk, i * maxChunkSize, subCount); //Falcosoft: to avoid marshalling errors pass the smaller local buffer to the function
                        ^^^^ Where it does something weird, forcing CLR to throw an AccessViolationException

                       // MessageBox.Show("copying temp chunk to main chunk");
                        eventChunk.CopyTo(MainWindow.KMCGlobals.events, i * maxChunkSize); //Falcosoft: copy local buffer to global one at each iteration

                       
                    }
                    MessageBox.Show("done");
                    eventChunk = null;
                }
                catch
                {
                    BASSCloseStreamException(new MIDILoadError("This MIDI is too big for the real-time conversion process.\n\nMake sure you're using the 64-bit version of the converter."));
                }
« Last Edit: 28 Oct '18 - 06:39 by Falcosoft »

radio42

  • Posts: 4636
The only change made is, that BASS_MIDI_EVENT is now a real class - before it was declared as a struct.
That's all.

KaleidonKep99

  • Posts: 191
It has to be an issue with BASS.NET.

Here's what happen when I use your new code, Falcosoft:


And radio42, downgrading to BASS.NET does get rid of the issue (while breaking MidiFilterProc), so I think the code I had before is fine.

radio42

  • Posts: 4636
Which exact function/method call does cause the error?

KaleidonKep99

  • Posts: 191
Which exact function/method call does cause the error?
The one I highlighted here.

Code: [Select]
                BASS_MIDI_EVENT[] eventChunk;
                try
                {
                    // Thank you so much Falcosoft for helping me here!!!
                    // Visit his website: http://falcosoft.hu/index.html#start
                    MessageBox.Show("i'm about to get the events");
                    MainWindow.KMCGlobals.eventc = (UInt32)BassMidi.BASS_MIDI_StreamGetEvents(MainWindow.KMCGlobals._recHandle, -1, 0, null); // Counts all the events in the MIDI

                    MessageBox.Show("now i'll copy them");
                    MainWindow.KMCGlobals.events = new BASS_MIDI_EVENT[MainWindow.KMCGlobals.eventc]; // Creates an array with the events count as size

                    for (int i = 0; i <= (MainWindow.KMCGlobals.eventc / 50000000); i++)
                    {
                        MessageBox.Show("getting subcount");
                        int subCount = Math.Min(50000000, (int)MainWindow.KMCGlobals.eventc - (i * 50000000));

                        MessageBox.Show("allocating chunk");
                        eventChunk = new BASS_MIDI_EVENT[subCount];

                        MessageBox.Show("copying events to chunk");
                        // This function here is the culprit
CULPRIT >>>>>>          BassMidi.BASS_MIDI_StreamGetEvents(MainWindow.KMCGlobals._recHandle, -1, 0, eventChunk, i * 50000000, subCount); //Falcosoft: to avoid marshalling errors pass the smaller local buffer to the function
                        // This function here is the culprit

                        MessageBox.Show("copying temp chunk to main chunk");
                        eventChunk.CopyTo(MainWindow.KMCGlobals.events, i * 50000000); //Falcosoft: copy local buffer to global one at each iteration

                        MessageBox.Show("done");
                    }
                    eventChunk = null;
                }
                catch
                {
                    BASSCloseStreamException(new MIDILoadError("This MIDI is too big for the real-time conversion process.\n\nMake sure you're using the 64-bit version of the converter."));
                }
As I said before, downgrading to the previous verison of BASS.NET gets rid of the issue, but breaks MidiFilterProc as expected.

radio42

  • Posts: 4636
Please try this version:
 www.un4seen.com/filez/4/Bass24.Net_pre.zip

I changes the BASS_MIDI_EVENT back again to a struct, but therefore changes the signature of the MIDIFILTERPROC delegate:
Code: [Select]
public delegate bool MIDIFILTERPROC(int handle, int track, IntPtr eventPtr, [In][MarshalAs(UnmanagedType.Bool)] bool seeking, IntPtr user);As you can see, the midievent is now in IntPtr!
To convert this back to an BASS_MIDI_EVENT, you can use the new static BASS_MIDI_EVENT.FromIntPtr method, like this:
Code: [Select]
private void MidiFilterProc(int handle, int track, IntPtr eventPtr, bool seeking, IntPtr user)
{
    var midievent = BASS_MIDI_EVENT.FromIntPtr(eventPtr);

if (midievent.eventtype == BASSMIDIEvent.MIDI_EVENT_NOTE)
{
    // got a note
    int vel = Utils.HighWord32(midievent.param); // extract the velocity
    if (vel < 10 && vel > 0)
        return false; // drop the note if velocity is below 10 and not 0 (note off)
}
return true; // process the event
}


KaleidonKep99

  • Posts: 191
Please try this version:
 www.un4seen.com/filez/4/Bass24.Net_pre.zip

I changes the BASS_MIDI_EVENT back again to a struct, but therefore changes the signature of the MIDIFILTERPROC delegate:
Code: [Select]
public delegate bool MIDIFILTERPROC(int handle, int track, IntPtr eventPtr, [In][MarshalAs(UnmanagedType.Bool)] bool seeking, IntPtr user);As you can see, the midievent is now in IntPtr!
To convert this back to an BASS_MIDI_EVENT, you can use the new static BASS_MIDI_EVENT.FromIntPtr method, like this:
Code: [Select]
private void MidiFilterProc(int handle, int track, IntPtr eventPtr, bool seeking, IntPtr user)
{
    var midievent = BASS_MIDI_EVENT.FromIntPtr(eventPtr);

if (midievent.eventtype == BASSMIDIEvent.MIDI_EVENT_NOTE)
{
    // got a note
    int vel = Utils.HighWord32(midievent.param); // extract the velocity
    if (vel < 10 && vel > 0)
        return false; // drop the note if velocity is below 10 and not 0 (note off)
}
return true; // process the event
}

It seems to work fine!

I was able to use both the MidiFilterProc and real-time simulation at the same time.