|
Art60
Posts: 135
|
 |
« on: 10 May '09 - 20:32 » |
Quote
|
Hi Ian and all. It's been a while since I went here, My project is slowly growing and I'm facing a new problem, please apologize if an answer's already here, I didn't find it. I'm trying to find which midi channel is the melody one, comparing to the lyrics events, in order to automatically mute it (it's a Karaoke player). So first, I build an array with the LYRICS or TEXT events. For each one, I have a mark.pos I translate to midi ticks using the following VB6 statements : BASS_ChannelSetPosition chan, mark.pos, BASS_POS_BYTE nTick = BASS_ChannelGetPosition(chan, BASS_POS_MIDI_TICK) Then I build a fast decode procedure with no soundfont to get every Note-On event from the midi file BASS_ChannelSetSync nChan, BASS_SYNC_MIDI_EVENT Or BASS_SYNC_MIXTIME, MIDI_EVENT_NOTE, AddressOf NoteEvent, 0 The problem is: If I get a large buffer to decode, My NoteEvent procedure asking BASS_ChannelGetPosition gets the same value for a lot of consecutive notes. So I the decided to have a small buffer (a single pair of bytes) The result is better, every call has a different Tick value, but the value is still erroneous (from 1 to some hundreds of ticks). I can't achieve in having ticks matching lyrics and notes, though I do know they do in the midi file, for I inspected it with a midi editor. May BASSMIDI help me in this way or do I have to build a separate midifile decoder to find which is the corresponding melody midi channel ? Thank you for any help. Regards, Arthur.
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15259
|
 |
« Reply #1 on: 11 May '09 - 15:43 » |
Quote
|
The issue there is that BASS_ChannelGetPosition is returning the position at the start of the chunk of data that is being rendered (containing the note), so rendering smaller chunks should help to separate the notes. I did spot a little tempo event processing bug though; they were being applied 1 tick late, so perhaps that could explain the notes being slightly off target. Here's an update to try... www.un4seen.com/stuff/bassmidi.dllIf you still find that you aren't getting accurate tick positions reported even when rendering a single sample at a time, please provide an example file to reproduce that with, and point out a note that is incorrect if the problem doesn't affect them all.
|
|
|
|
|
Logged
|
|
|
|
|
Art60
Posts: 135
|
 |
« Reply #2 on: 11 May '09 - 19:52 » |
Quote
|
Good evening Ian, Thank you for answering, Sorry, the DLL you made for me doesn't help much. I think I have a bug in my MIDI_EVENT_NOTE procedure. Before I send anything to you, could you please point out where I can find the exact detail for the parameters an event procedure receives : i.e. midi channel, velocity, note value ,etc. The only thing I saw about MIDI_EVENT_NOTE is on the BASS_MIDI_StreamEvent chm page. I think I may have messed high and low words/bytes somewhere. Sub CalcullNote(ByVal handle As Long, ByVal channel As Long, ByVal data As Long, ByVal user As Long) MidiChannel = data \ 65536 ' High Word eventparam = data Mod 65536 ' Low Word NoteValue = eventparam \ 256 ' High Byte Velocity = eventparam Mod 256 ' Low Byte If MidiChannel >= 0 And MidiChannel < 16 Then If Velocity > 0 Then ' Here I got a Note On message for note # NoteValue on Midi channel # MidiChannel End If End If
Thanks a lot for your support.
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15259
|
 |
« Reply #3 on: 12 May '09 - 17:25 » |
Quote
|
The SYNCPROC "data" parameter will be as the BASS_MIDI_StreamEvent "param" parameter for the same event, with the addition of the channel number in the HIWORD. With the MIDI_EVENT_NOTE event, the note is in the LOBYTE and the velocity is in the HIBYTE, so those need to be swapped around in your code above.
|
|
|
|
|
Logged
|
|
|
|
|
Art60
Posts: 135
|
 |
« Reply #4 on: 12 May '09 - 18:48 » |
Quote
|
Thank you so much Ian for your explanations, It does confirm my latest tests, I was wrong on various points. So the final result is following: (Corresponding test1.kar file in incoming folder) The decode procedure asks for 2 bytes at a time ( I tried length=1 but BASS_ChannelGetData returns 0 ): Ticks for Note On for midi chan#3 in the midi file: 5562 5664 5760 6046 6146 6332 6524 6718 6910 7196 7394 7486 7680 7872 8064 10172 10270 10366 10654 10754 Ticks returned by BASS_ChannelGetPosition(channel, BASS_POS_MIDI_TICK): 5561 Err=1 5663 Err=1 5759 Err=1 6045 Err=1 6145 Err=1 6331 Err=1 6523 Err=1 6713 Err=5 6905 Err=5 7195 Err=1 7393 Err=1 7486 Err=0 7679 Err=1 7869 Err=3 8061 Err=3 10171 Err=1 10269 Err=1 10359 Err=7 10653 Err=1 10751 Err=3 So, I got much less errors with my bugs removed and I think this will suffice for my search for melody channel. But there's still some non glitch errors like the one at 10359 instead of 10366... Don't hurry for it, I'll wait for v2.5 (with tick in mark structure  ) Thank you so much for your efforts in debugging our apps. Most regards. Arthur.
|
|
|
|
|
Logged
|
|
|
|
|
|
|
Art60
Posts: 135
|
 |
« Reply #6 on: 13 May '09 - 17:44 » |
Quote
|
Good evening Ian, For the sample file I sent you yesterday, this midifile.dll gave me more 0 error count where I had 1 tick difference previously. So I tested it with another midifile. mf2t.exe from MidiOx tells me lyrics are exactly on the same tick than the midi channel #3 notes. But with BassMidi, I haven't 0 errors any more with this new file, most of time it is 1. I could live with it. But there are still some cases where the position tells 41027 instead of 41040, for instance. 13 ticks error is a bit more than a rounded tick  I launch my software several times, the same tick erros occur at the same time, I don't think it is a latency bug due to time my software takes to process the buffer, but who really knows ? I just ran my software once in my VB6 IDE then in exe mode and obtained exactly the same differencies, so I don't think this is due to execution time. So I'll rely on an external software to list notes tick, waiting for a BassMidi solution. I tried to upload a file to incoming but it seems to be locked  Thank you very much again for your support.
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15259
|
 |
« Reply #7 on: 14 May '09 - 14:00 » |
Quote
|
I tried to upload a file to incoming but it seems to be locked  Strange, perhaps there was a file with the same name already there. Please try renaming the file and then uploading it again.
|
|
|
|
|
Logged
|
|
|
|
|
Art60
Posts: 135
|
 |
« Reply #8 on: 15 May '09 - 17:27 » |
Quote
|
Sorry Ian, I was not using a regular FTP client for this attempt, thought WXP was good enough, silly thought, isn't it? So you'll find Luis.kar in incoming folder. As told earlier, I rely on mf2t from MidiOx to have an "impartial" vision of ticks and I found these (midichannel are numbered from 1 to 16): 36240 On ch=4 n=67 v=105 36480 On ch=4 n=67 v=94 36720 On ch=4 n=67 v=96 39360 On ch=4 n=67 v=101 39720 On ch=4 n=69 v=97 40080 On ch=4 n=70 v=100 40320 On ch=4 n=74 v=96 41040 On ch=4 n=72 v=96 44160 On ch=4 n=72 v=102 44520 On ch=4 n=72 v=94 44880 On ch=4 n=72 v=90 45120 On ch=4 n=74 v=100 45480 On ch=4 n=74 v=88 45840 On ch=4 n=74 v=90 46080 On ch=4 n=75 v=100 46440 On ch=4 n=75 v=98 46800 On ch=4 n=75 v=62 47040 On ch=4 n=78 v=99 47400 On ch=4 n=79 v=102 47760 On ch=4 n=81 v=97
I wrote similar decoding using BassMidi library (I stripped off if then end if statements to make it clearer: .../... BASS_ChannelSetSync nChan, BASS_SYNC_MIDI_EVENT Or BASS_SYNC_MIXTIME, MIDI_EVENT_NOTE, AddressOf CalculNote, 0 Do nCount = BASS_ChannelGetData(nChan, aBuffer(0), 2) ' aBuffer is an array of integers DoEvents Loop While (nCount = nLength) And BASS_ChannelIsActive(nChan) .../...
Sub CalculNote(ByVal handle As Long, ByVal channel As Long, ByVal data As Long, ByVal user As Long) midichan = data \ 65536 eventparam = data Mod 65536 nValue = eventparam Mod 256 nVelocity = eventparam \ 256 nTick = BASS_ChannelGetPosition(channel, BASS_POS_MIDI_TICK) debug.print nTick;" On ch=";midichan;"n=";nValue;" v=";nVelocity End Sub And I get this timing (midichan lies between 0 and 15): 36241 On ch=3 n=67 v=105 36481 On ch=3 n=67 v=94 36721 On ch=3 n=67 v=96 39361 On ch=3 n=67 v=101 39714 On ch=3 n=69 v=97 40081 On ch=3 n=70 v=100 40321 On ch=3 n=74 v=96 41027 On ch=3 n=72 v=96 44161 On ch=3 n=72 v=102 44521 On ch=3 n=72 v=94 44867 On ch=3 n=72 v=90 45121 On ch=3 n=74 v=100 45481 On ch=3 n=74 v=88 45841 On ch=3 n=74 v=90 46081 On ch=3 n=75 v=100 46441 On ch=3 n=75 v=98 46787 On ch=3 n=75 v=62 47039 On ch=3 n=78 v=99 47389 On ch=3 n=79 v=102 47761 On ch=3 n=81 v=97
As you can see, there is more than 1 tick difference at ticks # 39714/39720, 41027/41040, 44867/44880, 46787/46800, 47389/47400, ... Thanks again for your support.  Regards, Arthur.
|
|
|
|
« Last Edit: 15 May '09 - 18:16 by Art60 »
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15259
|
 |
« Reply #9 on: 18 May '09 - 15:17 » |
Quote
|
I tried to reproduce the problem (doing basically the same as your code above), but the notes appeared to be at their correct positions  Perhaps you are using a low sample rate for the MIDI stream? BASSMIDI will process a minimum of 16 samples's worth per update, so a combination of a low sample rate and a high tempo/PPQN could mean that the minimum step is more than 1 tick (ie. a tick is less than 16 samples). Here's an update to try, in which that minimum processing unit has been reduced to just 1 sample... www.un4seen.com/stuff/bassmidi.dll
|
|
|
|
|
Logged
|
|
|
|
|
Art60
Posts: 135
|
 |
« Reply #10 on: 18 May '09 - 17:27 » |
Quote
|
Good evening Ian, you are totally true, as usual. First, I put your new DLL in my developpement folder, and it does work! There are no more such big tick count differencies ! The "error" is now constant and equals to 1. In a second time, I had a look at my code: Effectively, as told in another thread, to speed up this decoding to null, with no soundfont, to get every event, I had lowered the sample frequency to 1kHz : BASS_MIDI_StreamCreateFile(BASSFALSE, StrPtr(sFileName), 0, 0, BASS_STREAM_DECODE Or BASS_SAMPLE_MONO Or BASS_MIDI_NOFX, 1000) This was the faulty parameter ! When I put it at 44100, tick "errors" get all down to 0 and it's now accurate ! But the whole thing is v-e-r-y slow  So I really do thank you for helping me pointing out the cause of this problem, I never thought of this 1ms stupid sampling rate! So, to resume, there is no more problem but it does take a lot of time. I'm currently trying to decode the midifile myself (in standard midifile format) to see if I can get these informations faster. I'll get back with further information if I succeed. Grateful thanks, Mr Ian ! Regards, Arthur.
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15259
|
 |
« Reply #11 on: 19 May '09 - 16:29 » |
Quote
|
Just to be sure that there is no soundfont loaded (no default or matching soundfont), you could do this: midi=BASS_MIDI_StreamCreateFile(...); // load MIDI file BASS_MIDI_StreamSetFonts(midi, NULL, 0); // remove any soundfonts from it
Without a soundfont, BASSMIDI will just be rendering silence, but even then it'll probably still be quite slow when rendering 1 sample at a time, as most of the time will be taken by the overheads rather than the rendering. So another thing you could try is adapting the rendering unit to the length of a tick. You can calculate the number of samples in a tick like this: samples = rate*tempo/ppqn/1000000 Where "tempo" is the MIDI tempo event value, eg. as given by BASS_MIDI_StreamGetEvent. Be sure to multiply the "samples" value by the number of bytes per sample in the BASS_ChannelGetData call  MIDI streams are rendered in floating-point, and then converted to 16-bit if necessary. So you could also save a tiny bit of time by making the stream floating-point too (use the BASS_SAMPLE_FLOAT flag), so that BASSMIDI doesn't have to bother with the conversion.
|
|
|
|
|
Logged
|
|
|
|
|
Art60
Posts: 135
|
 |
« Reply #12 on: 19 May '09 - 17:33 » |
Quote
|
Good evening Ian,
Thanks for keeping on helping me.
1) Yes, soundfonts were removed, I saw it in the other thread.
2) I'm wondering if calculating number of samples in a tick would be accurate when handling a midi file embedding tempo changes, but this could be a good idea.
3) As far as I don't render for real, I'm asking myself if I have to call BASS_ChannelGetData with floating point width in order to match the BASS_MIDI_StreamCreateFile BASS_SAMPLE_FLOAT flag. Anyway, I'll give it a try.
Most regards, Arthur.
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15259
|
 |
« Reply #13 on: 20 May '09 - 14:58 » |
Quote
|
2) I'm wondering if calculating number of samples in a tick would be accurate when handling a midi file embedding tempo changes, but this could be a good idea.
You could re-calculate the number samples to render whenever the tempo changes, which you could detect via a BASS_SYNC_MIDI_EVENT (MIDI_EVENT_TEMPO) sync. 3) As far as I don't render for real, I'm asking myself if I have to call BASS_ChannelGetData with floating point width in order to match the BASS_MIDI_StreamCreateFile BASS_SAMPLE_FLOAT flag. Anyway, I'll give it a try.
You will indeed need to double the amount of data requested when switching from 16-bit to floating-point. If the stream is mono floating-point, that would be 4 bytes per sample (multiply the "samples" value by 4).
|
|
|
|
|
Logged
|
|
|
|
|