package vafusion.music; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.util.HashMap; import java.util.LinkedList; import java.util.List; public class Measure { private List<Note> notes; private boolean tieIn; private boolean tieOut; private boolean defaultFlat; private int num = 4; private int denom = 4; private double timeSignature; private double duration = 0; private int x, y, width, height; private int noteSeparation; public static double NOTE_SEPARATION_CONSTANT = 4.25; protected enum Clef {TREBLE, BASS}; private Clef clef; protected HashMap<Integer, Integer> treblePositionsFlat; protected HashMap<Integer, Integer> bassPositionsFlat; protected HashMap<Integer, Integer> treblePositionsSharp; protected HashMap<Integer, Integer> bassPositionsSharp; @SuppressWarnings("unused") private int[] noteRange; public Measure(int clef, boolean defaultFlat, int lineSeparation) { timeSignature = (double)num / denom; if(clef == 1){ this.clef = Clef.TREBLE; }else{ this.clef = Clef.BASS; } this.noteRange = getNoteRange(this.clef); this.defaultFlat = defaultFlat; this.treblePositionsFlat = initTreblePositionsFlat(); this.bassPositionsFlat = initBassPositionsFlat(); this.treblePositionsSharp = initTreblePositionsSharp(); this.bassPositionsSharp = initBassPositionsSharp(); this.noteSeparation = (int)(NOTE_SEPARATION_CONSTANT * lineSeparation); notes = new LinkedList<Note>(); } Measure(boolean tieIn){ this.tieIn = tieIn; } public boolean isTieIn() { return tieIn; } public void setTieIn(boolean tieIn) { this.tieIn = tieIn; } public boolean isTieOut() { return tieOut; } public void setTieOut(boolean tieOut) { this.tieOut = tieOut; } public jm.music.data.Note addNote(jm.music.data.Note n){ if(duration == timeSignature) //this measure is already full! return n; if(n.getRhythmValue() / denom + duration <= timeSignature) { //we're golden, add the note directly and return null int pos; if(n.isRest()) pos = 4; else pos = getPos(n.getPitch()); double rhythm = n.getRhythmValue(); notes.add(new Note(pos, rhythm, !n.isRest(), getAccidental(n.getPitch()))); duration += rhythm / denom; return null; } else { //the note needs to be split jm.music.data.Note remainder = new jm.music.data.Note(n.getPitch(), (duration + n.getRhythmValue() / denom) - timeSignature); jm.music.data.Note keep = new jm.music.data.Note(n.getPitch(), timeSignature - duration); int pos; if(n.isRest()) pos = 4; else pos = getPos(n.getPitch()); double rhythm = keep.getRhythmValue(); notes.add(new Note(pos, rhythm, !keep.isRest(), getAccidental(n.getPitch()))); duration += rhythm / denom; setTieOut(true); return remainder; } } private int getAccidental(int pitch) { if(defaultFlat){ if(clef == Clef.TREBLE){ switch(pitch){ case 62: case 64: case 67: case 69: case 71: case 74: case 76: case 79: return -1; default: return 0; } }else{ switch(pitch){ case 64: case 67: case 69: case 71: case 74: case 76: case 79: case 81: return 1; default: return 0; } } }else{ if(clef == Clef.TREBLE){ switch(pitch){ case 42: case 44: case 46: case 49: case 51: case 54: case 56: case 58: return -1; default: return 0; } }else{ switch(pitch){ case 42: case 44: case 46: case 49: case 51: case 54: case 56: case 58: return 1; default: return 0; } } } } public int getPos(int note){ if(defaultFlat){ if(this.clef == Clef.TREBLE){ System.out.println("note: " + note); return treblePositionsFlat.get(note); }else{ return bassPositionsFlat.get(note); } }else{ if(this.clef == Clef.TREBLE){ return treblePositionsSharp.get(note); }else{ return bassPositionsSharp.get(note); } } } public int getWidth() { return width; } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D)g; for(Note n : notes){ n.paint(g2d); //System.out.println(n); } g2d.setColor(Color.BLACK); System.out.println("Measure line: x1: " + (this.x + this.getWidth()) + " y1: " + this.y + " y2: " + (this.y + this.height)); g2d.fillRect(this.x + this.getWidth() - 5, this.y, 3, this.height); //FIXME doesn't work...why not? } public List<Note> getNotes() { return notes; } public void update(int x, int y, int height) { width = getWidth(); this.height = height; //System.out.println(notes.size()); this.noteSeparation = (int)(height/5 * NOTE_SEPARATION_CONSTANT); for(int i = 0; i < notes.size(); i++) notes.get(i).update(x + i * noteSeparation, y); } private HashMap<Integer, Integer> initTreblePositionsFlat(){ HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(61, 10); //C map.put(62, 9); //Db map.put(63, 9); //D map.put(64, 8); //Eb map.put(65, 8); //E map.put(66, 7); //F map.put(67, 6); //Gb map.put(68, 6); //G map.put(69, 5); //Ab map.put(70, 5); //A map.put(71, 4); //Bb map.put(72, 4); //B map.put(73, 3); //C map.put(74, 2); //Db map.put(75, 2); //D map.put(76, 1); //Eb map.put(77, 1); //E map.put(78, 0); //F map.put(79, 0); //Gb return map; } //TODO: Fix the rest of the hashes. ^^ has been fixed, but the only one. //TODO: Just kidding, ^^ is severely borked, but the rest of the program works as if it was correct //TODO: if anyone ever cares about this code again, please fix it... private HashMap<Integer, Integer> initTreblePositionsSharp(){ HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(62, 10); //D map.put(63, 10); //D# map.put(64, 9); //E map.put(65, 8); //F map.put(66, 8); //F# map.put(67, 7); //G map.put(68, 7); //G# map.put(69, 6); //A map.put(70, 6); //A# map.put(71, 5); //B map.put(72, 4); //C map.put(73, 4); //C# map.put(74, 3); //D map.put(75, 3); //D# map.put(76, 2); //E map.put(77, 1); //F map.put(78, 1); //F# map.put(79, 0); //G map.put(80, 0); //G# return map; } private HashMap<Integer, Integer> initBassPositionsFlat(){ HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(41, 10); //F map.put(42, 9); //Gb map.put(43, 9); //G map.put(44, 8); //Ab map.put(45, 8); //A map.put(46, 7); //Bb map.put(47, 7); //B map.put(48, 6); //C map.put(49, 5); //Db map.put(50, 5); //D map.put(51, 4); //Eb map.put(52, 4); //E map.put(53, 3); //F map.put(54, 2); //Gb map.put(55, 2); //G map.put(56, 1); //Ab map.put(57, 1); //A map.put(58, 0); //Bb map.put(59, 0); //B return map; } private HashMap<Integer, Integer> initBassPositionsSharp(){ HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(41, 10); //F map.put(42, 10); //F# map.put(43, 9); //G map.put(44, 9); //G# map.put(45, 8); //A map.put(46, 8); //A# map.put(47, 7); //B map.put(48, 6); //C map.put(49, 6); //C# map.put(50, 5); //D map.put(51, 5); //D# map.put(52, 4); //E map.put(53, 3); //F map.put(54, 3); //F# map.put(55, 2); //G map.put(56, 1); //G# map.put(57, 1); //A map.put(58, 0); //A# map.put(59, 0); //B return map; } int[] getNoteRange(Clef clef){ int[] range = new int[2]; switch (clef){ case TREBLE: range[0] = 61; // numeric value of CS4/DF4 range[1] = 80; // value of GS5/AF5 break; case BASS: range[0] = 41; // value of F2 range[1] = 59; // value of B3 break; default: System.out.println("Unrecognized clef in getNoteRange()"); } return range; } public int getX() { return x; } public Note getNote(int x) { System.out.println("Measure.getNote x: " + x); for(Note n : notes) if(x >= n.getX() && x <= n.getX() + n.getWidth()) return n; return null; } }