Java MIDI -ControllerEventListener (как изменить инструмент)

#java #event-handling #javax.sound.midi

#java #обработка событий #javax.sound.midi

Вопрос:

Я работаю над программой, которая генерирует и воспроизводит MIDI-события. Я реализовал интерфейс ControllerEventListener для того, чтобы печатать сообщения каждый раз, когда воспроизводится одна из 15 нот.

Проблема в том, что я больше не могу изменить инструмент, на котором воспроизводятся ноты (этим я должен сказать, что мой компьютер использует не пианино по умолчанию, а барабан или что-то подобное, если я добавлю ControllerEvent к дорожке) Я уже просмотрел задействованные методы в спецификациях Java API, но я не нашел ничего, что могло бы решить мою проблему, так же мало я нашел на этом сайте или в Google. Что я также сделал, так это попытался изменить инструмент всего канала (добавив сообщение об изменении программы), но это было точно так же, как и раньше.

Код следующий:

 import javax.sound.midi.*;

public class MusikPlayer implements ControllerEventListener {
    public static void main(String[] args) {
        MusikPlayer mini = new MusikPlayer();
        mini.go();
    }

    public void go() {
        try {
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();

            int[] wishedEvents = {127};
            sequencer.addControllerEventListener(this, wishedEvents);

            Sequence seq = new Sequence(Sequence.PPQ, 4);
            Track track = seq.createTrack();

            for (int i = 5; i < 61; i =4) {
                track.add(generateEvent(144, 1, i, 100, i));
                track.add(generateEvent(128, 1, i, 100, i 2));
                track.add(generateEvent(176, 1, 127, 0, i));
            }

            sequencer.setSequence(seq);
            sequencer.setTempoInBPM(220);
            sequencer.start();
            Thread.sleep(5000);
            sequencer.close();

        } catch (Exception e) {e.printStackTrace();}
    }

    public void controlChange(ShortMessage event) {
        System.out.println("la");
    }

    public MidiEvent generateEvent(int comd, int chan, int one, int two, int tick) {
        MidiEvent event = null;

        try {
            ShortMessage a = new ShortMessage();
            a.setMessage(comd, chan, one, two);
            event = new MidiEvent(a, tick);
        } catch (Exception e) {}

        return event;
    }
}
  

Теперь мой вопрос: как я могу изменить инструмент без изменения моей процедуры обработки событий?

Ответ №1:

Проверьте этот код

 import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.sound.midi.*;

public class TestMidi {

  private static final int TYPE_SINGLE_TRACK = 0;
  private static final int TYPE_PARALLEL_TRACKS = 1;
  private static final int TYPE_SERIAL_TRACKS = 2;

  public static void main(String[] args) {

    String filename = TestMidi.class.getName()   "_"   new SimpleDateFormat("yyyyMMdd-HHmmss.SSSS").format(new Date())   ".mid";
    System.out.println("filename:"   filename);
    File midiOutputFile = new File(filename);
    System.out.println("midiOutputFile.getAbsolutePath():"   midiOutputFile.getAbsolutePath());
    Sequence sequence = null;
    Synthesizer synthesizer = null;
    final Sequencer sequencer;
    List<Instrument> listInstrument;
    List<MidiChannel> listMidiChannel;
    Track trackPiano;
    Track trackViolin;
    try {
      synthesizer = MidiSystem.getSynthesizer();
      synthesizer.open();
      synthesizer.loadAllInstruments(synthesizer.getDefaultSoundbank());
      listInstrument = Arrays.asList(synthesizer.getLoadedInstruments());
      listMidiChannel = Arrays.asList(synthesizer.getChannels());
      listInstrument
          .stream()
          .map(instrument -> instrument.getPatch())
          .forEach(patch -> System.out.println("patch.getBank():"   patch.getBank()
            "tpatch.getProgram():"   patch.getProgram()
            "tlistInstrument.get(patch.getProgram()).getName():"   listInstrument.get(patch.getProgram()).getName()));

      Instrument instrumentPiano = listInstrument
          .stream()
          .filter(instrument -> instrument.getName().equals("Acoustic Grand Piano"))
          .findFirst()
          .get();
      Instrument instrumentViolin = listInstrument
          .stream()
          .filter(instrument -> instrument.getName().equals("Violin"))
          .findFirst()
          .get();

      int channelPiano = 0;
      int channelViolin = 1;

      listMidiChannel.get(channelPiano).programChange(instrumentPiano.getPatch().getProgram());
      listMidiChannel.get(channelViolin).programChange(instrumentViolin.getPatch().getProgram());

      sequence = new Sequence(Sequence.PPQ, 10); //10 pulses per quarter note
      trackPiano = sequence.createTrack();
      trackViolin = sequence.createTrack();
      long instant = 0L;
      trackPiano.add(createMidiEvent(ShortMessage.PROGRAM_CHANGE, channelPiano, instrumentPiano.getPatch().getProgram(), 0, instant * sequence.getResolution() / 500));
      trackViolin.add(createMidiEvent(ShortMessage.PROGRAM_CHANGE, channelViolin, instrumentViolin.getPatch().getProgram(), 0, instant * sequence.getResolution() / 500));

      instant = 250L; //250milliSeconds -> 1/4 Second
      int pitchViolin = 63;
      int valueViolin = 127;
      trackViolin.add(createMidiEvent(ShortMessage.NOTE_ON, channelViolin, pitchViolin, valueViolin, instant * sequence.getResolution() / 500));
      instant = 500L; //500milliSeconds -> 1/2 Second
      int pitchPiano = 60;
      int valuePiano = 64;
      trackPiano.add(createMidiEvent(ShortMessage.NOTE_ON, channelPiano, pitchPiano, valuePiano, instant * sequence.getResolution() / 500));
      instant = 1000L; //1000milliSeconds -> 1 Second
      trackPiano.add(createMidiEvent(ShortMessage.NOTE_OFF, channelPiano, pitchPiano, valuePiano, instant * sequence.getResolution() / 500));
      instant = 1250L; //1250milliSeconds

      trackViolin.add(createMidiEvent(ShortMessage.NOTE_OFF, channelViolin, pitchViolin, valueViolin, instant * sequence.getResolution() / 500));

      instant = 1500L; //1500milliSeconds
      pitchPiano = 66;
      trackPiano.add(createMidiEvent(ShortMessage.NOTE_ON, channelPiano, pitchPiano, valuePiano, instant * sequence.getResolution() / 500));
      instant = 1000L; //500milliSeconds
      trackPiano.add(createMidiEvent(ShortMessage.NOTE_OFF, channelPiano, pitchPiano, valuePiano, instant * sequence.getResolution() / 500));

      sequencer = MidiSystem.getSequencer();
      try {
        sequencer.open();
        sequencer.setSequence(sequence);
      } catch (Exception ex) {
        Logger.getLogger(TestMidi.class.getName()).log(Level.SEVERE, null, ex);
      }
      sequencer.start();

      Integer midiFileType;
      if (sequence.getTracks().length == 1) {
        midiFileType = TYPE_SINGLE_TRACK;
      } else {
        midiFileType = TYPE_PARALLEL_TRACKS;
      }

      int[] arrayMidiFileTypes = MidiSystem.getMidiFileTypes(sequence);
      if (arrayMidiFileTypes.length > 0) {
        MidiSystem.write(sequence, arrayMidiFileTypes[0], midiOutputFile);
      }

      sequencer.addMetaEventListener(new MetaEventListener() {
        @Override
        public void meta(MetaMessage metaMsg) {
          if (metaMsg.getType() == 0x2F) {
            sequencer.close();
          }
        }
      });

      /*
      while (sequencer.isRunning()) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException ex) {
          Logger.getLogger(TestMidi.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
      sequencer.close();
       */
    } catch (InvalidMidiDataException e) {
      Logger.getLogger(TestMidi.class.getName()).log(Level.SEVERE, null, e);

    } catch (MidiUnavailableException ex) {
      Logger.getLogger(TestMidi.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
      Logger.getLogger(TestMidi.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  private static MidiEvent createMidiEvent(int command, int channel, int data1, int data2, long instant) {
    ShortMessage shortMessage = new ShortMessage();
    try {
      shortMessage.setMessage(
          command,
          channel,
          data1,
          data2);
    } catch (InvalidMidiDataException e) {
    }
    return new MidiEvent(shortMessage, instant);
  }

}