com.rockhoppertech.music.midi.js.MIDIEvent.java Source code

Java tutorial

Introduction

Here is the source code for com.rockhoppertech.music.midi.js.MIDIEvent.java

Source

/*
 * $Id$
 *
 * Copyright 1998,1999,2000,2001 by Rockhopper Technologies, Inc.,
 * 75 Trueman Ave., Haddonfield, New Jersey, 08033-2529, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Rockhopper Technologies, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with RTI.
 */

package com.rockhoppertech.music.midi.js;

/*
 * #%L
 * Rocky Music Core
 * %%
 * Copyright (C) 1996 - 2013 Rockhopper Technologies
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.io.Serializable;
import java.util.Arrays;
import java.util.Locale;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rockhoppertech.music.midi.gm.MIDIGMPatch;

/**
 * Class <code>MIDIEvent</code> wraps and adds to Java Sound's MidiEvent class.
 * 
 * Sun's MidiEvent class is not Serializable. It is also useful to schedule
 * events at beats instead of ticks.
 * 
 * @author <a href="mailto:gene@rockhoppertech.com">Gene De Lisa</a>
 * @since 1.0
 * @see Serializable
 */

public class MIDIEvent implements Serializable, Comparable<MIDIEvent> {
    /**
     * 
     */
    private static final long serialVersionUID = -7468463852929825843L;

    private static final Logger logger = LoggerFactory.getLogger(MIDIEvent.class);

    private long division = 256;

    /**
     * the time in beats when this occurs
     */
    private double startBeat;
    /**
     * <code>tick</code> is the time in ticks when this occurs
     */
    private long tick;
    /**
     * <code>bytes</code> are the data
     */
    private byte[] bytes;
    /**
     * <code>status</code> is the MIDI status
     */
    private int status;
    /**
     * <code>type</code> is for meta events
     */
    private int metaMessageType;

    /**
     * The track to which this belongs.
     */
    private MIDITrack track;

    public MIDIEvent(MidiEvent e, MIDITrack track) {
        this(e);
        this.setTrack(track);
    }

    /**
     * Copy constructor. clone is evil.
     * 
     * @param e
     *            the event to copy
     */
    public MIDIEvent(MIDIEvent e) {
        this.tick = e.getTick();
        this.startBeat = e.getStartBeat();
        this.bytes = ArrayUtils.clone(e.getBytes());
        this.status = e.getStatus();
        this.metaMessageType = e.getMetaMessageType();
    }

    /**
     * Creates a new <code>MIDIEvent</code> instance from JavaSound's MidiEvent.
     * 
     * @param e
     *            a <code>MidiEvent</code> object.
     */
    public MIDIEvent(MidiEvent e) {
        tick = e.getTick();
        startBeat = (double) (tick) / (double) (division);
        MidiMessage mm = e.getMessage();
        byte[] ba = mm.getMessage();
        logger.debug("Original message: " + ba.length);
        for (byte element : ba) {
            logger.debug(Integer.toHexString(element & 0xf0) + " ");
        }

        if (mm instanceof ShortMessage) {
            logger.debug("Is short message");
            ShortMessage se = (ShortMessage) mm;
            bytes = new byte[2];
            bytes[0] = (byte) se.getData1();
            bytes[1] = (byte) se.getData2();
        } else if (mm instanceof SysexMessage) {
            logger.debug("is sysex message");
            SysexMessage sex = (SysexMessage) mm;
            bytes = sex.getData();
        } else if (mm instanceof MetaMessage) {
            logger.debug("is meta message");
            MetaMessage meta = (MetaMessage) mm;
            bytes = meta.getData();
        }

        status = mm.getStatus();
        logger.debug("Status: " + Integer.toHexString(status));
        if (mm instanceof MetaMessage) {
            metaMessageType = ((MetaMessage) mm).getType();
        }

        logger.debug("Constructed: " + toString());
    }

    /*
     * private void copy(int i, int j, int k) throws InvalidMidiDataException {
     * int len = 2; try { ByteArrayOutputStream baos = new
     * ByteArrayOutputStream(); DataOutputStream dos = new
     * DataOutputStream(baos); dos.writeByte(i); if(len >= 1) dos.writeByte(j);
     * if(len >= 2) dos.writeByte(k); this.bytes = baos.toByteArray(); }
     * catch(IOException ioexception) { } }
     */

    /**
     * Create a MIDIEvent from a JavaSound MidiMessage instance.
     * 
     * @param mm
     *            a JavaSound message
     * @param tick
     *            the tick this occurs
     */
    public MIDIEvent(MidiMessage mm, long tick) {
        this.tick = tick;
        startBeat = this.tick * division;

        if (mm instanceof ShortMessage) {
            // ShortMessage se = (ShortMessage) mm;
            bytes = new byte[2];
            bytes[0] = bytes[0];
            bytes[1] = bytes[1];
        } else if (mm instanceof SysexMessage) {
            SysexMessage sex = (SysexMessage) mm;
            bytes = sex.getData();
        } else if (mm instanceof MetaMessage) {
            MetaMessage meta = (MetaMessage) mm;
            bytes = meta.getData();
        }

        status = mm.getStatus();
        if (mm instanceof MetaMessage) {
            metaMessageType = ((MetaMessage) mm).getType();
            bytes = ((MetaMessage) mm).getData();
            // or getMessage which has the status. nah.
        }
        if (mm instanceof SysexMessage) {
            bytes = ((SysexMessage) mm).getData();
        }
    }

    /**
     * <code>isNote</code> checks the status byte to see if this event is a Note
     * event.
     * 
     * @return a <code>boolean</code> value
     */
    public boolean isNote() {
        logger.debug("isnote " + Integer.toHexString(status));
        if ((status & 0xF0) == 0x90 || (status & 0xF0) == 0x80) {
            return true;
        }
        return false;
    }

    /**
     * <code>getTick</code> returns the time scheduled for this event.
     * 
     * @return a <code>long</code> value
     */
    public long getTick() {
        return tick;
    }

    /**
     * <code>toMidiEvent</code> turns this well written class into JavaSound's
     * MidiEvent.
     * 
     * @return a <code>MidiEvent</code> value
     */
    public MidiEvent toMidiEvent() {
        MidiMessage mm = createMidiMessage();
        logger.debug("creating event at tick " + tick);
        return new MidiEvent(mm, tick);
    }

    /**
     * <code>toMidiEvent</code> turns this well written :) class into
     * JavaSound's MidiEvent.
     * 
     * @param division
     *            the resolution
     * @return a <code>MidiEvent</code> value
     */
    public MidiEvent toMidiEvent(int division) {
        MidiMessage mm = createMidiMessage();
        setDivision(division);
        logger.debug("creating event at tick " + tick);
        return new MidiEvent(mm, tick);
    }

    /**
     * Create a JavaSound MidiMessage from this instance.
     * 
     * @param mm
     * @return
     */
    private MidiMessage createMidiMessage() {
        MidiMessage mm = null;
        if (MIDIUtils.isChannelMessage(status)) {
            logger.debug("isChannelMessage for " + Integer.toHexString(status));
            mm = MIDIUtils.createShortMessage(status, bytes);

        } else if (MIDIUtils.isMetaMessage(status)) {
            logger.debug("MetaMessage: " + Integer.toHexString(status));
            MetaMessage meta = new MetaMessage();
            try {
                meta.setMessage(metaMessageType, bytes, bytes.length);
            } catch (InvalidMidiDataException e) {
                e.printStackTrace();
            }
            mm = meta;

        } else if (MIDIUtils.isSysexMessage(status)) {
            logger.debug("Sysex message: " + Integer.toHexString(status));
            SysexMessage sex = new SysexMessage();
            try {
                sex.setMessage(bytes, bytes.length);
            } catch (InvalidMidiDataException e) {
                e.printStackTrace();
            }
            mm = sex;
        } else {
            logger.debug("Unknown status " + Integer.toHexString(status));
        }
        return mm;
    }

    // private int readUnsigned() {
    // int i = -1;
    // try {
    // ByteArrayInputStream bais = new ByteArrayInputStream(this.bytes, 0,
    // this.bytes.length);
    // DataInputStream dis = new DataInputStream(bais);
    // i = dis.readUnsignedByte();
    // } catch (IOException ioexception) {
    // }
    // return i;
    // }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("MIDIEvent [");
        sb.append(" tick=").append(tick);
        sb.append(" beat=").append(startBeat);
        sb.append(" division=").append(division);
        sb.append(" status=").append(Integer.toHexString(status)).append(' ');
        sb.append(MIDIUtils.getCommandName(status));

        StringBuilder data = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            data.append("0x").append(Integer.toHexString(bytes[i]).toUpperCase(Locale.ENGLISH));
            if (i != bytes.length - 1) {
                data.append(',');
            }
        }
        sb.append(" metatype=").append(metaMessageType);
        if (status == MetaMessage.META) {
            sb.append("\nMeta text:").append(MIDIUtils.bytesToText(bytes));
        } else {
            sb.append("data=\"").append(data.toString()).append("\" ");
        }

        sb.append("]");

        return sb.toString();
    }

    public String toReadableString() {
        StringBuilder sb = new StringBuilder();

        // switch(se.getType()) {
        // case MidiEvent.CHANNEL_VOICE_MESSAGE:

        switch (status & 0xF0) {
        case ShortMessage.NOTE_OFF:
            sb.append("Note Off Key=").append(bytes[0]).append(" Velocity=").append(bytes[1]);
            break;

        case ShortMessage.NOTE_ON:
            sb.append("Note On Key=").append(bytes[0]).append(" Velocity=").append(bytes[1]);
            break;

        case ShortMessage.POLY_PRESSURE:
            sb.append("Polyphonic key pressure key=").append(bytes[0]).append(" pressure=").append(bytes[1]);
            break;

        case ShortMessage.CONTROL_CHANGE:
            sb.append("Control Change controller=").append(bytes[0]).append(" value=").append(bytes[1]);
            sb.append(" ").append(MIDIControllers.getControllerName((int) bytes[0]));

            break;

        case ShortMessage.PROGRAM_CHANGE:
            sb.append("Program Change program=").append(bytes[0]).append(" name=")
                    .append(MIDIGMPatch.getName(bytes[0]));
            break;

        case ShortMessage.CHANNEL_PRESSURE:
            sb.append("Channel Pressure pressure=").append(bytes[0]);
            break;

        case ShortMessage.PITCH_BEND:
            // int val = (this.bytes[0] & 0x7f) | ((this.bytes[1] & 0x7f) << 7);
            // short centered = 0x2000;
            short s14bit;
            s14bit = bytes[1];
            s14bit <<= 7;
            s14bit |= bytes[0];

            sb.append("Pitch Bend one=").append(bytes[0]).append(" two=").append(bytes[1]);
            sb.append(" val=").append(s14bit);
            break;
        default:
            sb.append(" Unhandled=").append(status & 0xF0);
            break;
        }
        sb.append(" Channel=").append(status & 0x0F);
        sb.append(" status=").append(Integer.toHexString(status));

        /*
         * case MidiEvent.CHANNEL_MODE_MESSAGE: printChannelModeMessage(se);
         * break;
         * 
         * case MidiEvent.SYSTEM_COMMON_MESSAGE : System.out.print(" system
         * common message "); break;
         * 
         * case MidiEvent.SYSTEM_REALTIME_MESSAGE : System.out.print(" system
         * realtime message "); break;
         * 
         * default: strMessage = "unknown event: status = " + se.getStatus() +
         * ", byte1 = " + this.bytes[0] + ", byte2 = " + this.bytes[1];
         */
        // System.out.print(sw.toString());
        return sb.toString();

    }

    public String toXMLString() {
        StringBuilder sb = new StringBuilder();
        sb.append("<MIDIEvent ");
        sb.append("beat=\"").append(startBeat).append("\" ");
        sb.append("type=\"").append(MIDIUtils.getCommandName(status)).append("\" ");

        StringBuilder data = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            data.append("0x").append(Integer.toHexString(bytes[i]).toUpperCase(Locale.ENGLISH));
            if (i != bytes.length - 1) {
                data.append(',');
            }
        }
        sb.append("data=\"").append(data.toString()).append("\" ");

        /*
         * sb.append("data=\""); if (this.bytes.length > 0)
         * sb.append("0x").append(Integer.toHexString(this.bytes[0])); if
         * (this.bytes.length > 1)
         * sb.append(", 0x").append(Integer.toHexString(this.bytes[1]));
         * 
         * sb.append("\" ");
         */
        sb.append("metatype=\"").append(metaMessageType).append("\"");
        sb.append("/>");
        return sb.toString();
    }

    /**
     * @return the beat
     */
    public double getStartBeat() {
        return startBeat;
    }

    /**
     * @param beat
     *            the beat to set
     */
    public void setStartBeat(double beat) {
        this.startBeat = beat;
    }

    /**
     * @return the track
     */
    public MIDITrack getTrack() {
        return track;
    }

    /**
     * @param track
     *            the track to set
     */
    public void setTrack(MIDITrack track) {
        this.track = track;
        if (this.track != null) {
            this.division = track.getResolution();
        }
        this.startBeat = (double) (this.tick) / (double) (this.division);
    }

    /**
     * @return the division
     */
    public long getDivision() {
        return division;
    }

    /**
     * @param division
     *            the division to set
     */
    public void setDivision(long division) {
        this.division = division;
        tick = (long) (startBeat * division);
    }

    public byte[] getBytes() {
        return Arrays.copyOf(bytes, bytes.length);
    }

    public void setBytes(byte[] bytes) {
        this.bytes = Arrays.copyOf(bytes, bytes.length);
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setTick(long tick) {
        this.tick = tick;
    }

    public int getMetaMessageType() {
        return metaMessageType;
    }

    public void setMetaMessageType(int metaMessageType) {
        this.metaMessageType = metaMessageType;
    }

    /*
     * 
     * @see java.lang.Object#clone()
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return super.clone();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + Arrays.hashCode(bytes);
        result = prime * result + (int) (division ^ (division >>> 32));
        result = prime * result + status;
        result = prime * result + (int) (tick ^ (tick >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        MIDIEvent other = (MIDIEvent) obj;
        if (!Arrays.equals(bytes, other.bytes)) {
            return false;
        }
        if (division != other.division) {
            return false;
        }
        if (status != other.status) {
            return false;
        }
        if (tick != other.tick) {
            return false;
        }
        return true;
    }

    /**
     * <code>compareTo</code> checks the startBeat.
     * 
     * 
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    @Override
    public int compareTo(final MIDIEvent n) {
        final double nt = n.getStartBeat();
        int r = 0;
        if (this.startBeat < nt) {
            r = -1;
            // } else if (this.startBeat == nt) {
        } else if (Math.abs(this.startBeat - nt) < .0000001) {
            r = 0;
        } else if (this.startBeat > nt) {
            r = 1;
        }
        return r;
    }

    public MIDIEvent duplicate() {
        return new MIDIEvent(this);
    }
}