LeffelMania / android-midi-lib

MIT License
255 stars 72 forks source link

How to play all instruments of a midi file? #25

Open ghopretz opened 4 years ago

ghopretz commented 4 years ago

I already use the MIDI driver and Android MIDI library to play MIDI with Soundfont on Android. It works, but I only hear the piano. Even though my soundfont & midi file consists of many instruments, not just piano.

private int channel = 0;

SF2Soundbank sf = new SF2Soundbank(getAssets().open("test.sf2"));
        synth = new SoftSynthesizer();
        synth.open();
        synth.loadAllInstruments(sf);
        synth.getChannels()[0].programChange(0);
        synth.getChannels()[1].programChange(1);
        recv = synth.getReceiver();

        synth.getChannels()[1].programChange(1);
        recv = synth.getReceiver();

//To Play the Midi notes from midi file

MidiFile midiFile = new MidiFile(getAssets().open("test.mid"));

// Create a new MidiProcessor:
MidiProcessor processor = new MidiProcessor(midiFile);

// listen for all midi events:
processor.registerEventListener(new MidiEventListener() {
    @Override
    public void onStart(boolean fromBeginning) {

    }

    @Override
    public void onEvent(MidiEvent event, long ms) {

        if (event.getClass() == NoteOn.class) {

                NoteOn noteOn = ((NoteOn) event);

                try {
                    ShortMessage msg = new ShortMessage();
                    msg.setMessage(ShortMessage.NOTE_ON, channel, noteOn.getNoteValue(), noteOn.getVelocity());
                    recv.send(msg, ms);
                } catch (InvalidMidiDataException e) {
                    e.printStackTrace();
                }

            } else if (event.getClass() == NoteOff.class) {

                NoteOff noteOff = ((NoteOff) event);

                try {
                    ShortMessage msg = new ShortMessage();
                    msg.setMessage(ShortMessage.NOTE_ON, channel, noteOff.getNoteValue(), noteOff.getVelocity());
                    recv.send(msg, ms);
                } catch (InvalidMidiDataException e) {
                    e.printStackTrace();
                }

            }
    }

    @Override
    public void onStop(boolean finished) {

    }
}, MidiEvent.class);

// Start the processor:
processor.start();
bzeeman commented 4 years ago

Just figured this out. While the event listener listens for MidiEvents, most of them are also ChannelEvents, which is an detention of MidiEvent. The ChannelEvents have the info you need to load the channels of your synth.

Here is an example in Kotlin, within the onEvent callback.

                var channel = 0
                if(event is ChannelEvent){
                    Log.i("ChannelEvent", "${event.getChannel()}")
                    channel = event.channel
                }
                if (event is ProgramChange){
                    synth.channels[event.channel].programChange(event.programNumber)
                }

The ProgramChange event has the channel number and instrument voice to load your synth.

ghopretz commented 4 years ago

Just figured this out. While the event listener listens for MidiEvents, most of them are also ChannelEvents, which is an detention of MidiEvent. The ChannelEvents have the info you need to load the channels of your synth.

Here is an example in Kotlin, within the onEvent callback.

                var channel = 0
                if(event is ChannelEvent){
                    Log.i("ChannelEvent", "${event.getChannel()}")
                    channel = event.channel
                }
                if (event is ProgramChange){
                    synth.channels[event.channel].programChange(event.programNumber)
                }

The ProgramChange event has the channel number and instrument voice to load your synth.

It is work. Thank you 👍👍

ghopretz commented 4 years ago

I have a problem again. When i force stop MidiProcessor. There is an instrument that continues to play. How to stop that instrument from playing?

processor = new MidiProcessor(midiFile);

        processor.registerEventListener(new MidiEventListener() {
            @Override
            public void onStart(boolean fromBeginning) {
                buatnotif(judul);
            }

            @Override
            public void onEvent(MidiEvent event, long ms) {

                int channel = 0;
                if(event instanceof ChannelEvent){
                    channel = ((ChannelEvent) event).getChannel();
                }
                if (event instanceof ProgramChange){
                    synth.getChannels()[((ProgramChange) event).getChannel()].programChange(((ProgramChange) event).getProgramNumber());
                }

                if (event.getClass() == NoteOn.class) {

                    NoteOn noteOn = ((NoteOn) event);

                    try {
                        ShortMessage msg = new ShortMessage();
                        msg.setMessage(ShortMessage.NOTE_ON, channel, noteOn.getNoteValue(), noteOn.getVelocity());
                        recv.send(msg, ms);
                    } catch (InvalidMidiDataException e) {
                    }

                } else if (event.getClass() == NoteOff.class) {

                    NoteOff noteOff = ((NoteOff) event);

                    try {
                        ShortMessage msg = new ShortMessage();
                        msg.setMessage(ShortMessage.NOTE_OFF, channel, noteOff.getNoteValue(), noteOff.getVelocity());
                        recv.send(msg, ms);
                    } catch (InvalidMidiDataException e) {
                    }

                }
            }

            @Override
            public void onStop(boolean finished) {
                stopSelf();
            }
        }, MidiEvent.class);

        processor.start();

    @Override
    public void onDestroy() {
        if(processor != null && (processor.isRunning() || processor.isStarted())){
            processor.stop();
        }
        super.onDestroy();
    }
ghopretz commented 4 years ago

I have a problem again. When i force stop MidiProcessor. There is an instrument that continues to play. How to stop that instrument from playing?

processor = new MidiProcessor(midiFile);

      processor.registerEventListener(new MidiEventListener() {
          @Override
          public void onStart(boolean fromBeginning) {
              buatnotif(judul);
          }

          @Override
          public void onEvent(MidiEvent event, long ms) {

              int channel = 0;
              if(event instanceof ChannelEvent){
                  channel = ((ChannelEvent) event).getChannel();
              }
              if (event instanceof ProgramChange){
                  synth.getChannels()[((ProgramChange) event).getChannel()].programChange(((ProgramChange) event).getProgramNumber());
              }

              if (event.getClass() == NoteOn.class) {

                  NoteOn noteOn = ((NoteOn) event);

                  try {
                      ShortMessage msg = new ShortMessage();
                      msg.setMessage(ShortMessage.NOTE_ON, channel, noteOn.getNoteValue(), noteOn.getVelocity());
                      recv.send(msg, ms);
                  } catch (InvalidMidiDataException e) {
                  }

              } else if (event.getClass() == NoteOff.class) {

                  NoteOff noteOff = ((NoteOff) event);

                  try {
                      ShortMessage msg = new ShortMessage();
                      msg.setMessage(ShortMessage.NOTE_OFF, channel, noteOff.getNoteValue(), noteOff.getVelocity());
                      recv.send(msg, ms);
                  } catch (InvalidMidiDataException e) {
                  }

              }
          }

          @Override
          public void onStop(boolean finished) {
              stopSelf();
          }
      }, MidiEvent.class);

      processor.start();

  @Override
  public void onDestroy() {
      if(processor != null && (processor.isRunning() || processor.isStarted())){
          processor.stop();
      }
      super.onDestroy();
  }

Problem solved:

```
    @Override
public void onDestroy() {
    if(processor != null && (processor.isRunning() || processor.isStarted())){
        processor.stop();
        processor.unregisterAllEventListeners();
        processor.reset();
    }
    if(synth != null && synth.isOpen()){
        synth.close();
    }
    if(recv != null){
        recv.close();
    }
    super.onDestroy();
}
ghopretz commented 4 years ago

Just figured this out. While the event listener listens for MidiEvents, most of them are also ChannelEvents, which is an detention of MidiEvent. The ChannelEvents have the info you need to load the channels of your synth.

Here is an example in Kotlin, within the onEvent callback.

                var channel = 0
                if(event is ChannelEvent){
                    Log.i("ChannelEvent", "${event.getChannel()}")
                    channel = event.channel
                }
                if (event is ProgramChange){
                    synth.channels[event.channel].programChange(event.programNumber)
                }

The ProgramChange event has the channel number and instrument voice to load your synth.

How to handle Pitch Bend ?

jenkshow commented 2 years ago

Just figured this out. While the event listener listens for MidiEvents, most of them are also ChannelEvents, which is an detention of MidiEvent. The ChannelEvents have the info you need to load the channels of your synth. Here is an example in Kotlin, within the onEvent callback.

                var channel = 0
                if(event is ChannelEvent){
                    Log.i("ChannelEvent", "${event.getChannel()}")
                    channel = event.channel
                }
                if (event is ProgramChange){
                    synth.channels[event.channel].programChange(event.programNumber)
                }

The ProgramChange event has the channel number and instrument voice to load your synth.

It is work. Thank you 👍👍

Hello, could you tell me where the specific code for switching SoundFont Presets should be implemented? I've been trying for a long time with only piano sounds.