android-midi-lib icon indicating copy to clipboard operation
android-midi-lib copied to clipboard

How to play all instruments of a midi file?

Open ghopretz opened this issue 6 years ago • 6 comments

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();

ghopretz avatar Nov 14 '19 19:11 ghopretz

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.

bzeeman avatar Jan 14 '20 19:01 bzeeman

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 avatar Jan 15 '20 00:01 ghopretz

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 avatar Jan 15 '20 02:01 ghopretz

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 avatar Jan 15 '20 14:01 ghopretz

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 ?

ghopretz avatar Jan 23 '20 02:01 ghopretz

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.

jenkshow avatar Aug 15 '22 09:08 jenkshow