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
 

Luca1967

Active Member
Thanks. You're great.

I have tested your example and it works.
The only problem is that the base, now in format 1, has a different tempo from the original base.
:-(
 
Upvote 0

Luca1967

Active Member
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.
I have tested and it works. Great !!!
If I read the converted file with VANBASCO it is correct, if I read it with the library the time is accelerated.
Am I wrong something?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Can you post the Midi file you are using, you'll probably have to zip it.

It worked OK with the file I tested it with, so there is probably something different about the file.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
OK, there are some meta events that should be on track 1 at all times, I presume that VanBasco automatically moves them when the file is loaded.

Try this version:

Moved project to post #19
 
Last edited:
Upvote 0

Luca1967

Active Member
Unfortunately, not having a detailed description of how your wonderful library works, I find myself in difficulty.
If I asked you for examples to mute the volume in the tracks and to change the instruments in the tracks ... am I asking you too much?
You are very kind and very good.
Really thank you.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
am I asking you too much?
The library is a wrapper for the JavaxMidi library and reflects the complexity of the midi specification, which can take time to get your head around.

It would be a lot of work to build a project / layout to demonstrate these requirements as you would need to be able to select a track to mute, and select an instrument from a list of some sort and specify where in the track the program change is to occur.

Assuming you have the midi file playing through the sequencer object the process of muting tracks is simple:
  • Identify the track you want to mute and get it from the sequence.
  • Call Sequencer SetTrackMute(Track,Mute) Where Mute is a boolean value.

Changing the instrument is a little trickier as you would need to make sure that the only program change messages in the track are the ones you want, so it would depend on what is already in the midi file. It is not uncommon for files to change instruments during the sequence, so there are a lot of variables depending on what you want to achieve.

The simplest way would be to loop through all of the events in a track and identify and remove any program change events using Track.Remove(Evt), then insert the one you want at the beginning if you are sure that you don't want it to change half way through.

You can use this sub to check if the MidiEvent is a Program Change:

B4X:
Private Sub IsProgramChange(evt As MidiEvent) As Boolean
    If evt.GetMessage.IsShortMessage Then
        If evt.GetMessage.AsShortMessage.GetCommand = MidiStatus.PROGRAM_CHANGE Then Return True
    End If
    Return False
End Sub

Use MidiMessageBuilder.ProgramChange method to create a new program change message. And:

B4X:
Dim PCEvt as MidiEvent
PCEvt.Initialize
PCEvt.CreateShort(ProgramChangeMsg, Tick)

To create the event, then add it to the track.
Where Tick is the location in the track so right at the beginning would be 0.

Midi is a complex beast, which is why good midi sequencers cost an arm and a leg when they were first introduced.

The xml file in the Library thread contains the documentation for the library which you can view with one of the document viewers available on the forum. Alternatively you can unzip the b4xlib and look through the code if you prefer that.
 
Last edited:
Upvote 0

Luca1967

Active Member
Can you help me?


I cannot retrieve the names of the instruments from the sound bank.
It always gives me the error: "java.lang.ClassCastException: class com.sun.media.sound.DLSSoundbank cannot be cast to class b4j.example.midisoundbank (com.sun.media.sound.DLSSoundbank is in module java.desktop of loader 'bootstrap'; b4j.example.midisoundbank is in unnamed module of loader 'app')

Do you have any idea why?
 
Upvote 0

Luca1967

Active Member
HI, I follow your instruction for change instrument.
This is my code.
But ...not function.
Can you help me please?


Dim TX As MidiTrack = Seq.GetTracks(4)
Dim i As Int

For i = 0 To TX.Size -1
Dim Mev As MidiEvent = TX.Get(i)

If IsProgramChange(Mev) = True Then
Log("Program Change:" & Mev.ToString(Mev.GetMessage) )
'TX.Remove(TX.Get(i))
TX.Remove(Mev)
Dim mm As MidiShortMessage = MidiMessageBuilder.ProgramChange(4,1,0)
Dim PCEvt As MidiEvent
PCEvt.Initialize
PCEvt.CreateShort(mm, 0)
Return
End If

Next
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
OK, A couple of things are wrong.

Firstly removing an event within the loop will cause an issue as there will be less events in the track and the Track.Get(Index) will fail at the end of the loop, so you need to keep a list of events to remove then remove them afterwards.

Secondly, you are not adding the new event to the track. Try this:

B4X:
Private Sub ReplaceProgramChange(T As MidiTrack,Channel As Int, ProgNo As Int, Tick As Long)
    Dim ToRemove As List
    ToRemove.Initialize
    For i = 0 To T.Size -1
        Dim Mev As MidiEvent = T.Get(i)
   
        If IsProgramChange(Mev) = True Then
            Log("Program Change:" & Mev.ToString(Mev.GetMessage) )
            'TX.Remove(TX.Get(i))
            ToRemove.add(Mev)
        End If
    Next
    For Each Mevt As MidiEvent In ToRemove
        T.Remove(Mevt)
    Next
           
    Dim mm As MidiShortMessage = MidiMessageBuilder.ProgramChange(Channel,ProgNo,Tick)
    Dim PCEvt As MidiEvent
    PCEvt.Initialize
    PCEvt.CreateShort(mm, Tick)
    T.Add(PCEvt)
End Sub

Private Sub IsProgramChange(evt As MidiEvent) As Boolean
    If evt.GetMessage.IsShortMessage Then
        If evt.GetMessage.AsShortMessage.GetCommand = MidiStatus.PROGRAM_CHANGE Then Return True
    End If
    Return False
End Sub

And call it with:

B4X:
Dim T As MidiTrack = Seq.GetTracks(4)
ReplaceProgramChange(T,4,1,0)
 
Upvote 0

Luca1967

Active Member
yes, Thanks.
But if i send ticks from sequence, the instrument not change

ReplaceProgramChange(TX,4,1,Seqr.GetTickPosition)
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Are you trying to do that while the sequence is playing?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
To control patch change in real time, you need to use the Synth object. If you are using soundfonts you already have the Synth object and you can change the program using:

B4X:
Synth.GetChannels(2).ProgramChange(6)

If you are using the internal synth you need to connect it using:

B4X:
Seqr = MidiSystemStatic.GetSequencer2(False)
Dim Synth As MidiSynthesizer = MidiSystemStatic.GetSynthesizer
Synth.Open
Seqr.GetTransmitter.SetReceiver(Synth.GetReceiver)
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Be aware that any program changes in the file will still change the program, so they will need to be removed / replaced as appropriate. If you want to make the changes permanent, you also need to write the change to the track at the appropriate time (Tick) so that it will play them next time.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Can you zip and post your project?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Here is a basic example.

View the track you want to change the program for, enter "pc" in the filter box to show only patch change events. Select the channel related to the track in the Ch Dropdown and the required ProgramChange in the Prog Dropdown.

Play the file, and press apply when you want the Program to change.

Stop the playback. Rewind it and play it again.

The Program will change when you press apply, and on playback.

You will need to change the location to point to your soundfont if you are using one.

This is a simple example, you will need to build more logic into your player to make it usable.

If you can't make it work in your app, please zip and post it.
 

Attachments

  • PChangeExample.zip
    5.3 KB · Views: 133
Last edited:
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I have change the sub CambiaStrumento with the code from my PChange example. This works with a soundfont loaded. You will need to change the way that the internal synth is initialized as detailed in my post #34 for it to work with that.
 

Attachments

  • stevel-1.zip
    55.3 KB · Views: 137
Upvote 0
Top