Author Topic: Non-realtime sequenced music playback using BASS_MIDI_StreamCreate?  (Read 574 times)

SonoSooS

  • Posts: 4
Hi!


I'm new to BASS and BASSMIDI, but I have kinda figured out how to use it.

When I use
Code: [Select]
BASS_MIDI_StreamCreateFile(filename, 0, 0, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | BASS_MIDI_DECAYEND | BASS_SAMPLE_MONO, samplerate);, everything works as expected when repeatedly calling
Code: [Select]
BASS_ChannelGetData(handle, buf, buflen);.

However, if I use
Code: [Select]
BASS_MIDI_StreamCreate(16, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | BASS_SAMPLE_MONO, samplerate); and do some
Code: [Select]
BASS_MIDI_StreamEvents(handle, BASS_MIDI_EVENTS_STRUCT, bufcopy), I always get -1 when doing
Code: [Select]
BASS_ChannelGetData(handle, buf, buflen);, and the error is
Code: [Select]
BASS_ERROR_ENDED.

I can't figure out what I'm doing wrong, because all I changed is I replaced StreamCreateFile with StreamCreate, and I'm manually sending the data instead of BASSMIDI handling the MIDI file parsing for me.
I used a debugger, and the events sent via StreamEvents are correct.

I'm using BASS.NET 2.4.13.3, BASS 2.4.13.27, and BASSMIDI 2.4.11.5 on Windows x86_64, if that matters.

Ian @ un4seen

  • Administrator
  • Posts: 22121
I suspect there is a MIDI_EVENT_END event in the array passed to BASS_MIDI_StreamEvents, which is ending the stream and resulting in the BASS_ERROR_ENDED error from BASS_ChannelGetData. MIDI_EVENT_END is defined as 0, so it could be that one of the events in the array is uninitialized.

SonoSooS

  • Posts: 4
Oh, I haven't thought of that.

But then again, I used a debugger to look at each element, and none of them were null or MIDI_EVENT_END, so it doesn't make too much sense :/

SonoSooS

  • Posts: 4
I've been debugging for hours, and the only conclusion I came to is that somehow BASS.NET is somehow broken. When using
Code: [Select]
public static int BASS_MIDI_StreamEvents(int handle, BASSMIDIEventMode flags, BASS_MIDI_EVENT[] events); I would not only get weird results, but also sometimes I'd get AccessViolationException with a non-null and non-empty array.

After looking at BASS_MIDI_EVENT, I noticed that it has been defined as a class for some reason instead of a struct, which makes stuff even more suspicios as to why it's been crashing.

After copypasting some code, I concluded that BASS.NET has this error where BASS_MIDI_EVENT is defined as a class, which is causing Marshaling issues due to a class having an undocumented structure in memory.
My version which calls into bassmidi directly works perfectly, so it turns out I was using BASS.NET right  ;D

Here's what I'm using to make stuff work
Code: [Select]
[StructLayout(LayoutKind.Sequential)]
private struct BASSMIDI_EVENT
{
    public int eventtype;
    public int param;
    public int chan;
    public int tick;
    public int pos;

    public BASSMIDI_EVENT(BASSMIDIEvent EventType, int Param, int Chan, int Tick, int Pos)
    {
        this.eventtype = (int)EventType;
        this.param = Param;
        this.chan = Chan;
        this.tick = Tick;
        this.pos = Pos;
    }

    public override string ToString()
    {
        return string.Format("Event={0}, Param={1}, Chan={2}, Tick={3} ({4})", new object[]
    {
    (BASSMIDIEvent)this.eventtype,
    this.param.ToString("X4"),
    this.chan,
    this.tick,
    this.pos
    });
    }
}

[DllImport("bassmidi")]
private static extern int BASS_MIDI_StreamEvents(int handle, int mode, IntPtr events, int length);

private static int BassStreamEvents(int handle, BASSMIDIEventMode mode, BASSMIDI_EVENT[] events)
{
    GCHandle h = GCHandle.Alloc(events, GCHandleType.Pinned);
    IntPtr addr = h.AddrOfPinnedObject();
    int ret = BASS_MIDI_StreamEvents(handle, (int)mode, addr, events.Length);
    h.Free();
    return ret;
}

You *could* use pointers too, but I wanted to avoid using the /unsafe flag just for this one function call. I hope this helps in fixing this issue!

But be aware that changing the type of BASS_MIDI_EVENT from a class to a struct will break previously built programs, so all of them will have to be rebuilt with a newer dll.

Also, in my opinion I'd love to see some privately declared functions (like in this example, BASS_MIDI_StreamEvents with the IntPtr overload) to be opened up the public, so we wouldn't have to copypaste the private DllImport declaration into our programs with a different name to cause confusion on why we can't find the function we just declared. This is just an opinion though.

Besides this minor problem I love BASSMIDI, especially that you can use the BASS_MIDI_EVENT struct to recreate what a proprietary file format does, including preparsed MIDI files :)

radio42

  • Posts: 4667
What BASS.NET version are you using? - as in the lastest BASS.NET version (related to BASS v2.4.14.x), BASS_MIDI_EVENT is already defined as a struct!
So might it be, that you are using an older Bass.Net version?

Note: I just realized, that I accidentially never updated the v2.4.14.0 BASS.NET version to the FTP!
I just did right now ... so please redownload and try again.
Sorry for the confusion.
« Last Edit: 12 Apr '19 - 08:05 by radio42 »

SonoSooS

  • Posts: 4
Thank you for the update, the new BASS.NET dll works perfectly!

Also, as I stated in the first post, I was using BASS.NET 2.4.13.3, but it doesn't matter anymore, because this new update works :P