Package jpianotrain.midi

Source Code of jpianotrain.midi.MidiThread

/* For License see bottom */
package jpianotrain.midi;

import javax.sound.midi.Instrument;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Synthesizer;

import jpianotrain.ApplicationContext;
import jpianotrain.VersionInformation;
import jpianotrain.staff.Note;

import org.apache.log4j.Logger;

/**
* Separate Thread to send Midi messages to
* a configured receiver. This is necessary
* to have a time source and to avoid a
* lock for the GUI while playing.
*
* @since 0
* @author Alexander Methke
*/
@VersionInformation (
  lastChanged="$LastChangedDate: 2008-11-20 15:23:22 -0600 (Thu, 20 Nov 2008) $",
  authors={"Alexander Methke"},
  revision="$LastChangedRevision: 26 $",
  lastEditor="$LastChangedBy: onkobu $",
  id="$Id"
)
public class MidiThread extends Thread {
  private static final Logger log=Logger.getLogger(MidiThread.class);
  public static ShortMessage CLOCK_MESSAGE;
  public static ShortMessage SYSTEM_RESET_MESSAGE;
  public static ShortMessage ALIVE_MESSAGE;

    public static enum TEST_MODES {
    TEST_CHORD,
    TEST_DRUMS,
    TEST_MELODY
    };

  protected MidiThread(MidiDevice dIn, MidiDevice dOut) {
    this();
      setKeyboardInDevice(dIn);
    setMidiOutDevice(dOut);
  }

  protected MidiThread() {
    if (CLOCK_MESSAGE==null) {
      CLOCK_MESSAGE=new ShortMessage();
      SYSTEM_RESET_MESSAGE=new ShortMessage();
      ALIVE_MESSAGE=new ShortMessage();
      try {
        CLOCK_MESSAGE.setMessage(ShortMessage.TIMING_CLOCK);
        SYSTEM_RESET_MESSAGE.setMessage(ShortMessage.SYSTEM_RESET);
        ALIVE_MESSAGE.setMessage(ShortMessage.ACTIVE_SENSING);
      } catch (Exception ex) {
      }
    }
  }

  public static MidiThread getInstance() {
      if (instance==null) {
      instance=new MidiThread();
      instance.start();
//      readerInstance=new MidiReader();
//      readerInstance.start();
      }
      return instance;
  }

  public static MidiThread getTestInstance(Synthesizer synth,
      Instrument instrument, TEST_MODES tm) {
    MidiThread t=new MidiThread();
    t.setSynthesizer(synth);
    t.setInstrument(instrument);
    t.setTestMode(tm);
    t.initTestMessages();
    return t;
  }
 
  public static MidiThread getTestInstance(MidiDevice dev,
      int channel, TEST_MODES tm, int progNo) {
    MidiThread t=new MidiThread();
    t.setMidiOutDevice(dev);
    t.setChannel(channel);
    t.setTestMode(tm);
    t.setProgramNumber(progNo);
    t.initTestMessages();
    return t;
  }

  public void run() {
    while (runFlag) {
      try {
        sleep(100);
      } catch (Exception ex) {
      }
      if (runOnce) {
        break;
      }
    }
    cleanUp();
  }

  public void dataReceived() {
    receivedData=true;
  }

  public boolean isDataReceived() {
    return receivedData;
  }
 
  public void setRunFlag(boolean state) {
    runFlag=state;
  }

  public boolean isRunning() {
    return runFlag;
  }

  public void play(Note[] notes) throws Exception {
    for(Note n:notes) {
      play(n);
    }
  }
 
  public void setBPM(double bpm) {
    this.bpm=bpm;
  }
 
  public double getBPM() {
    return bpm;
  }
 
  public double getMillisecondsPerWholeNote() {
    // One minute has 60 seconds and
    // each second contains 1000 ms.
    // Each bpm is a quarter note,
    // so 4 bpm make up a whole note
    return (getBPM()*60*1000)/4;
  }
 
  public void play(Note n) throws Exception {
    emitNoteOn(n.getPitch());
    try {
      sleep(Math.round(getMillisecondsPerWholeNote()*n.getDuration().getFactor()));
    } catch (InterruptedException ex) {
     
    }
    emitNoteOff(n.getPitch());
  }
 
  protected void cleanUp() {
    if (getMidiOutDevice()!=null) {
      getMidiOutDevice().close();
    }
    if (getSynthesizer()!=null) {
      getSynthesizer().getChannels()[0].allNotesOff();
    }
  }

  public void setChannel(int ch) {
    channel=ch;
  }

  public int getChannel() {
    return channel;
  }

  public boolean initTestMessages() {
      try {
      ShortMessage sm=new ShortMessage();
      sm.setMessage(ShortMessage.SYSTEM_RESET,0,0);
      enqueueEvent(sm);
      if (programNumber!=-1) {
        sm=new ShortMessage();
        sm.setMessage(ShortMessage.PROGRAM_CHANGE, programNumber, 0);
        enqueueEvent(sm);
      }
      switch (testMode) {
        case TEST_DRUMS: {
          addDrumEvents(channel);
        }break;
        case TEST_CHORD: {
          addChordEvents(channel);
        }break;
        case TEST_MELODY: {
          addMelodyEvents(channel);
        }break;
      }
      } catch (Exception ex) {
      ex.printStackTrace();
      return false;
      }
      return true;
  }

  public boolean init(MidiDevice inDev, MidiDevice outDev) {
    try {
      setKeyboardInDevice(inDev);
      setMidiOutDevice(outDev);
    } catch (Exception ex) {
      ex.printStackTrace();
      return false;
    }
    return true;
  }

  public boolean init(MidiDevice keyDev,
    MidiDevice outDev,
    MidiDevice metroDev) {
    try {
      setMetronomeDevice(metroDev);
      setMidiOutDevice(outDev);
      setKeyboardInDevice(keyDev);
      setInstrument(null);
      setSynthesizer(null);
    } catch (Exception ex) {
      log.error("failed to hook onto midi device(s)", ex);
      return false;
    }
    return true;
  }

  public boolean init(MidiDevice keyDev,
      Synthesizer synth,
      Instrument inst) {
      try {
        setSynthesizer(synth);
        setInstrument(inst);
        setKeyboardInDevice(keyDev);
        setMidiOutDevice(null);
        setMetronomeDevice(null);
      } catch (Exception ex) {
        log.error("failed to hook onto synthesizer", ex);
        return false;
      }
      return true;
    }

  public void emitNoteOn(int note) throws Exception {
    assertValidMidiValue(note, "note pitch");
    ShortMessage sm=new ShortMessage();
    try {
      sm.setMessage(ShortMessage.NOTE_ON, note, 100);
      enqueueEvent(sm);
    } catch (Exception ex) {
      log.error("failed emitting note on", ex);
      throw ex;
    }
  }

  public void emitNoteOff(int note) {
    assertValidMidiValue(note, "note pitch");
    ShortMessage sm=new ShortMessage();
    try {
      sm.setMessage(ShortMessage.NOTE_OFF, note, 100);
      enqueueEvent(sm);
    } catch (Exception ex) {
      log.error("failed emitting note off", ex);
    }
  }

  protected void assertValidMidiValue(int val, String valueName) {
    if (val<0 || val>128) {
      throw new IllegalArgumentException("Value for "+valueName+" may range only from 0..127, "+val+" is therefore invalid");
    }
  }

  private void addDrumEvents(int channel) {
    ShortMessage sm;
    try {
      for (int i=0;i<30; i++) {
        sm=new MessageWrapper(250);
        sm.setMessage(ShortMessage.NOTE_ON, channel, 40+i, 100);
        enqueueEvent(sm);
        enqueueEvent(new MessageWrapper(50, true));

        sm=new ShortMessage();
        sm.setMessage(ShortMessage.NOTE_OFF, channel, 40+i, 100);
        enqueueEvent(sm);
      }
    } catch (Exception ex) {
      log.error("failed to add drum events", ex);
    }
  }

  private void addChordEvents(int channel) {
    try {
      MultiMessage mm=new MultiMessage(1000, ShortMessage.NOTE_ON);
      mm.addMessage(channel, 64, 100);
      mm.addMessage(channel, 67, 100);
      mm.addMessage(channel, 70, 100);
      enqueueEvent(mm);

      mm=new MultiMessage(0, ShortMessage.NOTE_OFF);
      mm.addMessage(channel, 64, 100);
      mm.addMessage(channel, 67, 100);
      mm.addMessage(channel, 70, 100);
      enqueueEvent(mm);
    } catch (Exception ex) {
      log.error("failed to add chord events", ex);
    }
  }

  private void addMelodyEvents(int channel) {
    try {
      ShortMessage sm=null;
      sm=new MessageWrapper(750);
      sm.setMessage(ShortMessage.NOTE_ON, channel, 64, 100);
      enqueueEvent(sm);

      sm=new MessageWrapper(0,true);
      sm.setMessage(ShortMessage.NOTE_OFF, channel, 64, 100);
      enqueueEvent(sm);

      sm=new MessageWrapper(500);
      sm.setMessage(ShortMessage.NOTE_ON, channel, 70, 100);
      enqueueEvent(sm);

      sm=new MessageWrapper(0,true);
      sm.setMessage(ShortMessage.NOTE_OFF, channel, 70, 100);
      enqueueEvent(sm);

      sm=new MessageWrapper(250);
      sm.setMessage(ShortMessage.NOTE_ON, channel, 70, 100);
      enqueueEvent(sm);

      sm=new MessageWrapper(0,true);
      sm.setMessage(ShortMessage.NOTE_OFF, channel, 70, 100);
      enqueueEvent(sm);

      sm=new MessageWrapper(1000);
      sm.setMessage(ShortMessage.NOTE_ON, channel, 67, 100);
      enqueueEvent(sm);

      sm=new MessageWrapper(0,true);
      sm.setMessage(ShortMessage.NOTE_OFF, channel, 67, 100);
      enqueueEvent(sm);
    } catch (Exception ex) {
      log.error("failed to add melody events", ex);
    }
  }

  protected void setMetronomeDevice(MidiDevice dev) {
    if (metronomeDevice!=null &&
        dev!=metronomeDevice &&
        metronomeDevice.isOpen()) {
      try {
        metronomeDevice.close();
      } catch (Exception ex) {
        log.error("failed to close old metronome device", ex);
      }
    }

    metronomeDevice=dev;

    if (metronomeDevice!=null &&
        !metronomeDevice.isOpen()) {
      try {
        metronomeDevice.open();
      } catch (Exception ex) {
        log.error("failed to init new metronome device",  ex);
      }
    }
  }

  public MidiDevice getMetronomeDevice() {
    return metronomeDevice;
  }
 
  public MidiDevice getKeyboardDevice() {
    return readerInstance.getKeyboardInDevice();
  }

  public MidiDevice getMidiOutDevice() {
    return outDevice;
  }

  protected void setMidiOutDevice(MidiDevice dev) {
    if (outDevice!=null &&
      dev!=outDevice &&
      outDevice.isOpen()) {
      try {
        outDevice.close();
      } catch (Exception ex) {
        log.error("failed to close old out device", ex);
      }
    }

      outDevice=dev;

    if (outDevice!=null &&
      !outDevice.isOpen()) {
      try {
        outDevice.open();
      } catch (Exception ex) {
        log.error("failed to open new out device", ex);
      }
    }
  }

  protected synchronized void enqueueEvent(MidiMessage msg) throws Exception {
    MidiDevice dev=getMidiOutDevice();
    if (ApplicationContext.getInstance().isSpoolMidi()) {
      log.debug("sending "+MidiReader.formatMidiMessage(msg));
    }
    if (receiver==null) {
      if (dev==null) {
        if (!synthesizer.isOpen()) {
          synthesizer.open();
        }
        receiver=synthesizer.getReceiver();
        if (instrument!=null) {
          synthesizer.getChannels()[0].programChange(
              instrument.getPatch().getBank(),
              instrument.getPatch().getProgram());
        }
      } else {
        receiver=dev.getReceiver();
      }
    }
    if (msg instanceof MultiMessage) {
      MultiMessage mMsg=(MultiMessage)msg;
      MessageWrapper lastMsg=null;
      for(MessageWrapper mw:mMsg.getMessages()) {
        receiver.send(mw, -1);
        lastMsg=mw;
      }
      if (lastMsg!=null &&
        lastMsg.getCommand()==ShortMessage.NOTE_ON) {
        sleep(lastMsg.getDurationMs());
      }
    } else if (msg instanceof MessageWrapper) {
      MessageWrapper mw=(MessageWrapper)msg;
      if (mw.isPause()) {
        sleep(mw.getDurationMs());
      } else {
        receiver.send(mw, -1);
        if (mw.getCommand()==ShortMessage.NOTE_ON) {
          sleep(mw.getDurationMs());
        }
      }
    } else {
      log.debug("raw midi message");
      receiver.send(msg, -1);
    }
  }

  /**
   * Getter for property testMode.
   * @return Value of property testMode.
   */
  public TEST_MODES getTestMode() {
    return this.testMode;
  }

  /**
   * Setter for property testMode.
   * @param testMode New value of property testMode.
   */
  public void setTestMode(TEST_MODES testMode) {
    this.testMode = testMode;
  }

  /**
   * Getter for property programNumber.
   * @return Value of property programNumber.
   */
  public int getProgramNumber() {
    return this.programNumber;
  }

  /**
   * Setter for property programNumber.
   * @param programNumber New value of property programNumber.
   */
  public void setProgramNumber(int programNumber) {
    this.programNumber = programNumber;
  }

  /**
   * Getter for property runOnce.
   * @return Value of property runOnce.
   */
  public boolean isRunOnce() {
    return this.runOnce;
  }

  /**
   * Setter for property runOnce.
   * @param runOnce New value of property runOnce.
   */
  public void setRunOnce(boolean runOnce) {
    this.runOnce = runOnce;
  }

  /**
   * Getter for property keyInDevice.
   * @return Value of property keyInDevice.
   */
  public MidiDevice getKeyInDevice() {
    return this.keyInDevice;
  }

  /**
   * Setter for property keyInDevice.
   * @param keyInDevice New value of property keyInDevice.
   */
  public void setKeyboardInDevice(MidiDevice keyInDevice) {
    if (readerInstance==null) {
      readerInstance=new MidiReader();
      readerInstance.start();
    }
    readerInstance.setKeyboardInDevice(keyInDevice);
    this.keyInDevice = keyInDevice;
  }

  public Synthesizer getSynthesizer() {
    return synthesizer;
  }

  public void setSynthesizer(Synthesizer synthesizer) {
    this.synthesizer = synthesizer;
  }

  public Instrument getInstrument() {
    return instrument;
  }

  public void setInstrument(Instrument instrument) {
    this.instrument = instrument;
  }
 
  private static MidiThread instance;
  private static MidiReader readerInstance;

  private boolean runFlag=true;

  private int channel;

  private MidiDevice outDevice;
  private MidiDevice metronomeDevice;

  /**
   * Holds value of property testMode.
   */
  private TEST_MODES testMode;

  /**
   * Holds value of property programNumber.
   */
  private int programNumber=-1;

  /**
   * Holds value of property runOnce.
   */
  private boolean runOnce=false;

  private double bpm;
  /**
   * Holds value of property keyInDevice.
   */
  private MidiDevice keyInDevice;

  private boolean receivedData;

  private Receiver receiver;
 
  private Synthesizer synthesizer;
  private Instrument instrument;
}
/*
    Copyright (C) 2008  Alexander Methke

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program (gplv3.txt).
*/
TOP

Related Classes of jpianotrain.midi.MidiThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.