001 package org.bukkit; 002 003 import java.util.Map; 004 005 import org.apache.commons.lang.Validate; 006 007 import com.google.common.collect.Maps; 008 009 /** 010 * A note class to store a specific note. 011 */ 012 public class Note { 013 014 /** 015 * An enum holding tones. 016 */ 017 public enum Tone { 018 G(0x1, true), 019 A(0x3, true), 020 B(0x5, false), 021 C(0x6, true), 022 D(0x8, true), 023 E(0xA, false), 024 F(0xB, true); 025 026 private final boolean sharpable; 027 private final byte id; 028 029 private static final Map<Byte, Note.Tone> BY_DATA = Maps.newHashMap(); 030 /** The number of tones including sharped tones. */ 031 public static final byte TONES_COUNT = 12; 032 033 private Tone(int id, boolean sharpable) { 034 this.id = (byte) (id % TONES_COUNT); 035 this.sharpable = sharpable; 036 } 037 038 /** 039 * Returns the not sharped id of this tone. 040 * 041 * @return the not sharped id of this tone. 042 * @deprecated Magic value 043 */ 044 @Deprecated 045 public byte getId() { 046 return getId(false); 047 } 048 049 /** 050 * Returns the id of this tone. These method allows to return the 051 * sharped id of the tone. If the tone couldn't be sharped it always 052 * return the not sharped id of this tone. 053 * 054 * @param sharped Set to true to return the sharped id. 055 * @return the id of this tone. 056 * @deprecated Magic value 057 */ 058 @Deprecated 059 public byte getId(boolean sharped) { 060 byte id = (byte) (sharped && sharpable ? this.id + 1 : this.id); 061 062 return (byte) (id % TONES_COUNT); 063 } 064 065 /** 066 * Returns if this tone could be sharped. 067 * 068 * @return if this tone could be sharped. 069 */ 070 public boolean isSharpable() { 071 return sharpable; 072 } 073 074 /** 075 * Returns if this tone id is the sharped id of the tone. 076 * 077 * @param id the id of the tone. 078 * @return if the tone id is the sharped id of the tone. 079 * @throws IllegalArgumentException if neither the tone nor the 080 * semitone have the id. 081 * @deprecated Magic value 082 */ 083 @Deprecated 084 public boolean isSharped(byte id) { 085 if (id == getId(false)) { 086 return false; 087 } else if (id == getId(true)) { 088 return true; 089 } else { 090 // The id isn't matching to the tone! 091 throw new IllegalArgumentException("The id isn't matching to the tone."); 092 } 093 } 094 095 /** 096 * Returns the tone to id. Also returning the semitones. 097 * 098 * @param id the id of the tone. 099 * @return the tone to id. 100 * @deprecated Magic value 101 */ 102 @Deprecated 103 public static Tone getById(byte id) { 104 return BY_DATA.get(id); 105 } 106 107 static { 108 for (Tone tone : values()) { 109 int id = tone.id % TONES_COUNT; 110 BY_DATA.put((byte) id, tone); 111 112 if (tone.isSharpable()) { 113 id = (id + 1) % TONES_COUNT; 114 BY_DATA.put((byte) id, tone); 115 } 116 } 117 } 118 } 119 120 private final byte note; 121 122 /** 123 * Creates a new note. 124 * 125 * @param note Internal note id. {@link #getId()} always return this 126 * value. The value has to be in the interval [0; 24]. 127 */ 128 public Note(int note) { 129 Validate.isTrue(note >= 0 && note <= 24, "The note value has to be between 0 and 24."); 130 131 this.note = (byte) note; 132 } 133 134 /** 135 * Creates a new note. 136 * 137 * @param octave The octave where the note is in. Has to be 0 - 2. 138 * @param tone The tone within the octave. If the octave is 2 the note has 139 * to be F#. 140 * @param sharped Set if the tone is sharped (e.g. for F#). 141 */ 142 public Note(int octave, Tone tone, boolean sharped) { 143 if (sharped && !tone.isSharpable()) { 144 tone = Tone.values()[tone.ordinal() + 1]; 145 sharped = false; 146 } 147 if (octave < 0 || octave > 2 || (octave == 2 && !(tone == Tone.F && sharped))) { 148 throw new IllegalArgumentException("Tone and octave have to be between F#0 and F#2"); 149 } 150 151 this.note = (byte) (octave * Tone.TONES_COUNT + tone.getId(sharped)); 152 } 153 154 /** 155 * Creates a new note for a flat tone, such as A-flat. 156 * 157 * @param octave The octave where the note is in. Has to be 0 - 1. 158 * @param tone The tone within the octave. 159 * @return The new note. 160 */ 161 public static Note flat(int octave, Tone tone) { 162 Validate.isTrue(octave != 2, "Octave cannot be 2 for flats"); 163 tone = tone == Tone.G ? Tone.F : Tone.values()[tone.ordinal() - 1]; 164 return new Note(octave, tone, tone.isSharpable()); 165 } 166 167 /** 168 * Creates a new note for a sharp tone, such as A-sharp. 169 * 170 * @param octave The octave where the note is in. Has to be 0 - 2. 171 * @param tone The tone within the octave. If the octave is 2 the note has 172 * to be F#. 173 * @return The new note. 174 */ 175 public static Note sharp(int octave, Tone tone) { 176 return new Note(octave, tone, true); 177 } 178 179 /** 180 * Creates a new note for a natural tone, such as A-natural. 181 * 182 * @param octave The octave where the note is in. Has to be 0 - 1. 183 * @param tone The tone within the octave. 184 * @return The new note. 185 */ 186 public static Note natural(int octave, Tone tone) { 187 Validate.isTrue(octave != 2, "Octave cannot be 2 for naturals"); 188 return new Note(octave, tone, false); 189 } 190 191 /** 192 * @return The note a semitone above this one. 193 */ 194 public Note sharped() { 195 Validate.isTrue(note < 24, "This note cannot be sharped because it is the highest known note!"); 196 return new Note(note + 1); 197 } 198 199 /** 200 * @return The note a semitone below this one. 201 */ 202 public Note flattened() { 203 Validate.isTrue(note > 0, "This note cannot be flattened because it is the lowest known note!"); 204 return new Note(note - 1); 205 } 206 207 /** 208 * Returns the internal id of this note. 209 * 210 * @return the internal id of this note. 211 * @deprecated Magic value 212 */ 213 @Deprecated 214 public byte getId() { 215 return note; 216 } 217 218 /** 219 * Returns the octave of this note. 220 * 221 * @return the octave of this note. 222 */ 223 public int getOctave() { 224 return note / Tone.TONES_COUNT; 225 } 226 227 private byte getToneByte() { 228 return (byte) (note % Tone.TONES_COUNT); 229 } 230 231 /** 232 * Returns the tone of this note. 233 * 234 * @return the tone of this note. 235 */ 236 public Tone getTone() { 237 return Tone.getById(getToneByte()); 238 } 239 240 /** 241 * Returns if this note is sharped. 242 * 243 * @return if this note is sharped. 244 */ 245 public boolean isSharped() { 246 byte note = getToneByte(); 247 return Tone.getById(note).isSharped(note); 248 } 249 250 @Override 251 public int hashCode() { 252 final int prime = 31; 253 int result = 1; 254 result = prime * result + note; 255 return result; 256 } 257 258 @Override 259 public boolean equals(Object obj) { 260 if (this == obj) 261 return true; 262 if (obj == null) 263 return false; 264 if (getClass() != obj.getClass()) 265 return false; 266 Note other = (Note) obj; 267 if (note != other.note) 268 return false; 269 return true; 270 } 271 272 @Override 273 public String toString() { 274 return "Note{" + getTone().toString() + (isSharped() ? "#" : "") + "}"; 275 } 276 }