B4J Question JavaxMidi-B4xLib for B4j

Luca1967

Active Member
Hi,

I am trying to use this nice library but I am having problems
1) I can't read the instruments in the tracks
2) I can't mute a channel
3) Are there any explanations or examples online? If so, where can I get them?
Thank you
 

stevel05

Expert
Licensed User
Longtime User
You can get the track name directly from the track Class. you could get the instrument name from the Program change events contained in the track if the instrument is a General Midi instrument, alternatively instrument names may or may not be stored in the meta data of the track as a MidiMetaMessage. There is a GetInstrumentName method in the Track class to help with that.

You can mute a track using the MidiSequencer SetTrackMute method.

The only example I have written is provided in the library post, which demonstrates loading, playing and getting the track name, selecting a track from the list of tracks and looping all of the events in a track.

The B4j library is a wrapper for the javax midi library, so you should be able to convert any java examples you may find on the internet without too much effort.
 
Upvote 0

Luca1967

Active Member
You can get the track name directly from the track Class. you could get the instrument name from the Program change events contained in the track if the instrument is a General Midi instrument, alternatively instrument names may or may not be stored in the meta data of the track as a MidiMetaMessage. There is a GetInstrumentName method in the Track class to help with that.

You can mute a track using the MidiSequencer SetTrackMute method.

The only example I have written is provided in the library post, which demonstrates loading, playing and getting the track name, selecting a track from the list of tracks and looping all of the events in a track.

The B4j library is a wrapper for the javax midi library, so you should be able to convert any java examples you may find on the internet without too much effort.
 
Upvote 0

Luca1967

Active Member
Thanks.

I was able to extract the words from the midi / kar file.
I can't load the soundfont and I can't manage the instruments and the change of instruments.
Do you have any suggestions?
Thank you very much.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
There was a bug in the soundfont initialization, fixed in V1.3. JavaxMidiLib

The initialization for the sequencer is different than for using the internal midi device. If the internal midi device is initialized then that will be used in preference to the soundfont so we need to make sure it is not initialized.

B4X:
Dim Soundfont As MidiSoundbank = MidiSystemStatic.GetSoundbank("Path-to-soundfont-file","soundfontfile.sf2")
 
        Dim Synth As MidiSynthesizer = MidiSystemStatic.GetSynthesizer
        Synth.Open
        Synth.LoadAllInstruments(Soundfont)
    
 
        Seqr = MidiSystemStatic.GetSequencer2(False) 'Do not connect to default device.
        Seqr.Open
    
        Seqr.GetTransmitter.SetReceiver(Synth.GetReceiver)

The attached is the example project with the option to use a soundfont.
 

Attachments

  • MidiSoundfontTest.zip
    4.6 KB · Views: 194
Last edited:
Upvote 0

stevel05

Expert
Licensed User
Longtime User
To change the instruments you need to replace the program change message(s) in the relevant tracks. Please provide an example of what you have tried.
 
Upvote 0

Luca1967

Active Member
Thanks for your kind replies.
I changed the "MIDIEVENT" class because it did not extrapolate the words for the text and the lyrics.
Now it returns correctly if there is any text in the files.
I changed the TOSTRING and TOSTRINGBBT subs.
I put the changes here. If I have been useful I am happy, and if you want to implement these changes of mine in your library, I would be very happy with them.

ToString:
Else If Bit.And(eMsg.AsMidiMetaMessage.GetType_,0xFF) = MidiMetaMessage_Static.LYRIC Then
Data(6) = eMsg.AsMidiMetaMessage.GetMetaText("UTF-8")
Else If Bit.And(eMsg.AsMidiMetaMessage.GetType_,0xFF) = MidiMetaMessage_Static.TEXT Then
Data(6) = eMsg.AsMidiMetaMessage.GetMetaText("UTF-8")

TostringBBT:


Else If Bit.And(eMsg.AsMidiMetaMessage.GetType_,0xFF) = MidiMetaMessage_Static.LYRIC Then
Data(6) = eMsg.AsMidiMetaMessage.GetMetaText("UTF-8")
Dim nn As String
Else If Bit.And(eMsg.AsMidiMetaMessage.GetType_,0xFF) = MidiMetaMessage_Static.TEXT Then
Data(6) = eMsg.AsMidiMetaMessage.GetMetaText("UTF-8")
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Thanks for the code, I won't update the library as that code is only intended as an example for the example app. These are static methods so you can copy the subs to a different code module with your amendments, then you won't have to modify the library if it is updated again.

Feel free to post them on the forum as code snippets for others to use.
 
Upvote 0

Luca1967

Active Member
Hello. I'm trying to make a little karaoke software with your library.
My question is the following.
How can I read the events in real time while the sequencer is playing in order to capture the text to be played?
Do you have any idea?
Thank you.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Yes, Use MidiSequencer AddMetaEventListener. It will pass all meta events as they happen.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Add this line after the sequencer is opened. (assuming your sequencer object is called Seqr)

B4X:
Seqr.AddMetaEventListener(Me,"Meta")

Then add a sub to handle the messages with a name prefix matching the Eventname passed above, in this case "Meta":

B4X:
Private Sub Meta_MetaEvent(Msg As MidiMetaMessage)
    ' Do what you need with the messages when they arrive.
    Log(Msg.ToString)
End Sub

No timer required.
 
Upvote 0

Luca1967

Active Member
Add this line after the sequencer is opened. (assuming your sequencer object is called Seqr)

B4X:
Seqr.AddMetaEventListener(Me,"Meta")

Then add a sub to handle the messages with a name prefix matching the Eventname passed above, in this case "Meta":

B4X:
Private Sub Meta_MetaEvent(Msg As MidiMetaMessage)
    ' Do what you need with the messages when they arrive.
    Log(Msg.ToString)
End Sub

No timer required.
Thanks. It's Ok.
Another little question.
I have a midi file with only one track but which contains all the information.
SetTrackMute and SetTrackSolo (if I indicate not to play the melody which is normally on track 4) does not work.
If I have a midi file where there are all the separate tracks then they work.
How can I do in your opinion?

If in files with complete tracks I mute the track, I also lose the information for the synchronization of the lyrics.
I could bypass this by setting the volume of the track to 0 (zero).
But I don't know how.
 
Last edited:
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I have a midi file with only one track but which contains all the information.
SetTrackMute and SetTrackSolo
This is the expected behaviour, it is much better to work with type 1 midi files where each track is separate. While it is possible for any track to contain lyrics, it is better for the lyrics to be on a separate track for this reason.

You could pre-process the file and separate the tracks as required on load.

Track volume is controlled by continuous controller 7, you would need to remove any existing cc7 messages from the track and insert a cc message at the beginning of the track with a volume of 0 or just overwrite all cc 7 messages on the track with a value of 0.

If you want to keep 1 track, then amend the cc 7 messages for the appropriate channel.
 
Last edited:
Upvote 0

Luca1967

Active Member
You write : "You could pre-process the file and separate the tracks as required on load."
But where ? Which is the command for separate track?
This command is in your library?

Thanks a lot
 
Upvote 0

kimstudio

Active Member
Licensed User
Longtime User
You write : "You could pre-process the file and separate the tracks as required on load."

I know there are some midi tools can do this to convert midi files from single to multiple tracks. Maybe can refer to their soure codes.

I also want to learn how to process midi files as I want to make an accompanyment application with midi but seems it is quite complex.
BTW: Now I am using this to play midi files with virtualmidisynth loaded soundfont, very nice freeware!

屏幕截图 2022-06-22 183829.png
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Creating a midi type 1 file from a midi type 0 file should be fairly straight forward. You just need to Create a new sequence, then loop through all events on track 0 of the original sequence (only one track in a type 0 midi file). Create a new track in the new sequence for each different channel you find and create a new copy of each event and insert it onto the relevant track.

There may be some issues, but that is the basic process.

Then you can set the new sequence to the sequencer using the SetSequence2 method, or save it to a file using MidiSystemStatic Write method.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Here is an example of converting a midi track from type 0 (1 track) to type 1 multiple tracks. There is no validation that it is actually a type 0 Midi File. That can be done using MidiSystemStatic.GetFileFormat.GetType_ method. Added to project zip

You will need to change the file Input and output locations in the Main Module.

Edit: Updated with latest version
 

Attachments

  • ConvertMidi.zip
    3.4 KB · Views: 202
Last edited:
Upvote 0

Luca1967

Active Member
Ecco un esempio di conversione di una traccia midi dal tipo 0 (1 traccia) al tipo 1 tracce multiple. Non c'è alcuna convalida che si tratti effettivamente di un Midi File di tipo 0. Questo può essere fatto usando il metodo MidiSystemStatic.GetFileFormat.GetType_. Aggiunto al progetto zip

Sarà necessario modificare le posizioni di input e output del file nel modulo principale.
Grazie
.
Here is an example of converting a midi track from type 0 (1 track) to type 1 multiple tracks. There is no validation that it is actually a type 0 Midi File. That can be done using MidiSystemStatic.GetFileFormat.GetType_ method. Added to project zip

You will need to change the file Input and output locations in the Main Module.
Thanks. You're great.
 
Upvote 0
Top