001package net.kreatious.pianoleopard.midi.event; 002 003import javax.sound.midi.ShortMessage; 004 005/** 006 * Represents a note on or off event that is associated with a particular key 007 * (note) number. 008 * 009 * @author Jay-R Studer 010 */ 011public class NoteEvent extends Event { 012 private final int key; 013 private final int velocity; 014 private final boolean on; 015 private transient Slot slot; 016 017 /** 018 * Array of flags indicating if a raw MIDI key (note) modulus 12 is sharp or 019 * not. 020 * <p> 021 * The first element corresponds to C, second is C#, third is D, etc. 022 */ 023 private static final boolean[] SHARP_KEYS = { false, true, false, true, false, false, true, false, true, false, 024 true, false }; 025 026 /** 027 * Constructs a new {@link NoteEvent} with the specified data. 028 * <p> 029 * The channel will be 0 and velocity will be 127. 030 * 031 * @param key 032 * the raw MIDI key (note) for this event, between 0 and 127 033 * inclusive 034 * @param on 035 * {@code true} if this event is a note on event, otherwise 036 * {@code false} 037 * @param time 038 * the time that this event occurs, in microseconds 039 */ 040 public NoteEvent(int key, boolean on, long time) { 041 super(0, time); 042 043 if (key < 0 || key > 127) { 044 throw new IllegalArgumentException("Key " + key + " is out of range [0, 127]"); 045 } 046 047 this.key = key; 048 this.on = on; 049 velocity = 127; 050 slot = new Slot(getChannel(), key); 051 } 052 053 NoteEvent(ShortMessage message, long time) { 054 super(message, time); 055 056 key = message.getData1(); 057 velocity = message.getData2(); 058 slot = new Slot(message.getChannel(), key); 059 060 if (message.getCommand() == ShortMessage.NOTE_OFF) { 061 on = false; 062 } else if (velocity == 0) { 063 on = false; 064 } else if (message.getCommand() == ShortMessage.NOTE_ON) { 065 on = true; 066 } else { 067 throw new IllegalArgumentException("message " + message.getCommand() + " is not a note on/off message"); 068 } 069 } 070 071 private NoteEvent(int channel, long time, int key, int velocity, boolean on, Slot slot) { 072 super(channel, time); 073 this.key = key; 074 this.velocity = velocity; 075 this.on = on; 076 this.slot = slot; 077 } 078 079 static boolean canCreate(ShortMessage message) { 080 return message.getCommand() == ShortMessage.NOTE_OFF || message.getCommand() == ShortMessage.NOTE_ON; 081 } 082 083 /** 084 * Returns the note number associated with this event. 085 * <p> 086 * MIDI note numbers range from 0 to 127, inclusive. Note C4 is number 60. 087 * 088 * @return the raw key (note) number associated with this event 089 */ 090 public int getKey() { 091 return key; 092 } 093 094 /** 095 * Returns the velocity associated with this event. 096 * <p> 097 * Velocity ranges from 0 to 127, inclusive. 098 * 099 * @return the velocity (loudness) associated with this event. 100 */ 101 public int getVelocity() { 102 return velocity; 103 } 104 105 /** 106 * Returns if this note is considered sharp or not. 107 * 108 * @return {@code true} if this note event is a sharp note, {@code false} 109 * otherwise. 110 */ 111 public boolean isSharp() { 112 return SHARP_KEYS[key % 12]; 113 } 114 115 @Override 116 public boolean isOn() { 117 return on; 118 } 119 120 @Override 121 public Slot getSlot() { 122 return slot; 123 } 124 125 @Override 126 @SuppressWarnings("unchecked") 127 public NoteEvent createOff(long offTime) { 128 return new NoteEvent(getChannel(), offTime, key, 127, false, slot); 129 } 130}