/* -*- c-basic-offset: 2; indent-tabs-mode: nil; -*- */ /* * FreeDots -- MusicXML to braille music transcription * * Copyright 2008-2010 Mario Lang All Rights Reserved. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as * published by the Free Software Foundation. * * This code 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 (a copy is included in the LICENSE.txt file that * accompanied this code). * * You should have received a copy of the GNU General Public License * along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * This file is maintained by Mario Lang <mlang@delysid.org>. */ package freedots.braille; import java.util.logging.Logger; import freedots.Options; import freedots.music.AbstractPitch; import freedots.music.Accidental; import freedots.music.Articulation; import freedots.music.AugmentedPowerOfTwo; import freedots.music.Fingering; import freedots.music.Ornament; import freedots.music.Slur; import freedots.music.Tuplet; import freedots.musicxml.Note; /** The braille representation of a note or rest. * <p> * This includes all signs which must immediately follow or proceed the actual * value or rest sign. * * @see <a href="http://brl.org/music/code/bmb/chap01/index.html">Chapter 1: * Notes and Values</a> */ public class BrailleNote extends BrailleList { private final Note note; /** Constructs a braille note and all of its composing signs. * @param note refers to the MusicXML Note object * @param lastPitch is used to decide if an octave sign needs to be inserted */ public BrailleNote(final Note note, final AbstractPitch lastPitch) { this(note, lastPitch, true); } /** Constructs a braille note and all its composing signs. * @param note refers to the MusicXML Note object * @param lastPitch is used to decide if an octave sign needs to be inserted * @param allowTieSign is false if ties should not be inserted. This is * used to support {@link BrailleChord.ChordTieSign}. */ BrailleNote(final Note note, final AbstractPitch lastPitch, final boolean allowTieSign) { super(); this.note = note; final Options options = Options.getInstance(); Tuplet tuplet = note.getTuplet(); if (tuplet != null) { // We are part of some tuplet group if (tuplet.isFirstOfTuplet(note)) { add(new TupletSign(tuplet)); Tuplet parent = tuplet.getParent(); while (parent != null && parent.isFirstOfTuplet(tuplet)) { addFirst(new TupletSign(parent)); //FIXME: This assignment seems unnecessary //tuplet = parent; parent = parent.getParent(); } } // else do nothing since only first note of a tuplet needs prefix } if (note.isGrace()) add(new GraceSign()); for (Ornament ornament: note.getOrnaments()) add(createOrnamentSign(ornament)); for (Articulation articulation: note.getArticulations()) add(createArticulationSign(articulation)); Accidental accidental = note.getAccidental(); if (accidental != null) add(new AccidentalSign(accidental)); AbstractPitch pitch = (AbstractPitch)note.getPitch(); if (pitch == null) /* A hack to support unpitched notes */ pitch = (AbstractPitch)note.getUnpitched(); final AugmentedPowerOfTwo value = note.getAugmentedFraction(); if (pitch != null) { /* A sounding note */ if (isOctaveSignRequired(pitch, lastPitch)) add(new OctaveSign(pitch.getOctave())); add(new PitchAndValueSign(pitch, value)); } else { add(new RestSign(value)); } for (int i = 0; i < value.dots(); i++) add(new Dot()); if (options.getShowFingering()) { final Fingering fingering = note.getFingering(); if (!fingering.getFingers().isEmpty()) { add(new BrailleFingering(fingering)); } } boolean addSingleSlur = false; boolean addDoubledSlur = false; for (Slur<Note> slur: note.getSlurs()) { if (slur.countArcs(note) >= options.getSlurDoublingThreshold()) { if (slur.isFirst(note)) { addDoubledSlur = true; addSingleSlur = false; } else if (slur.isLastArc(note)) { addDoubledSlur = false; addSingleSlur = true; } else { addDoubledSlur = false; addSingleSlur = false; } break; } else { if (!slur.lastNote(note)) { addDoubledSlur = false; addSingleSlur = true; break; } } } if (addDoubledSlur) { add(new SlurSign()); add(new SlurSign()); } else if (addSingleSlur) { add(new SlurSign()); } if (allowTieSign && note.isTieStart()) add(new TieSign()); } @Override public String getDescription() { return "A note."; } @Override public Object getScoreObject() { return note; } public AbstractPitch getPitch() { return note.getPitch(); } private static boolean isOctaveSignRequired(final AbstractPitch pitch, final AbstractPitch lastPitch) { if (lastPitch != null) { final int halfSteps = Math.abs(pitch.getMIDIPitch() - lastPitch.getMIDIPitch()); if ((halfSteps < 5) || (halfSteps >= 5 && halfSteps <= 7 && pitch.getOctave() == lastPitch.getOctave())) return false; } return true; } public static class GraceSign extends Sign { GraceSign() { super(braille(5, 26)); } public String getDescription() { return "Indicates a grace note"; } } private static OrnamentSign createOrnamentSign(Ornament ornament) { switch (ornament) { case mordent: return new MordentSign(); case invertedMordent: return new InvertedMordentSign(); case trill: return new TrillSign(); case turn: return new TurnSign(); default: throw new AssertionError(ornament); } } public abstract static class OrnamentSign extends Sign { OrnamentSign(final String data) { super(data); } } public static class MordentSign extends OrnamentSign { MordentSign() { super(braille(5, 235, 123)); } public String getDescription() { return "A mordent sign"; } } public static class InvertedMordentSign extends OrnamentSign { InvertedMordentSign() { super(braille(6, 235, 123)); } public String getDescription() { return "A inverted mordent sign"; } } public static class TrillSign extends OrnamentSign { TrillSign() { super(braille(235)); } public String getDescription() { return "A trill sign"; } } public static class TurnSign extends OrnamentSign { TurnSign() { super(braille(6, 256)); } public String getDescription() { return "A turn sign"; } } private static ArticulationSign createArticulationSign(Articulation articulation) { switch (articulation) { case accent: return new AccentSign(); case strongAccent: return new MartellatoSign(); case breathMark: return new BreathSign(); case staccato: return new StaccatoSign(); case mezzoStaccato: return new MezzoStaccatoSign(); case staccatissimo: return new StaccatissimoSign(); case tenuto: return new TenutoSign(); default: throw new AssertionError(articulation); } } public abstract static class ArticulationSign extends Sign { ArticulationSign(final String data) { super(data); } } public static class AccentSign extends ArticulationSign { AccentSign() { super(braille(46, 236)); } public String getDescription() { return "An accent sign"; } } public static class MartellatoSign extends ArticulationSign { MartellatoSign() { super(braille(56, 236)); } public String getDescription() { return "A martellato (strong accent) sign"; } } public static class BreathSign extends ArticulationSign { BreathSign() { super(braille(6, 34)); } public String getDescription() { return "A breath mark"; } } public static class StaccatoSign extends ArticulationSign { StaccatoSign() { super(braille(236)); } public String getDescription() { return "A staccato sign"; } } public static class MezzoStaccatoSign extends ArticulationSign { MezzoStaccatoSign() { super(braille(5, 236)); } public String getDescription() { return "A mezzo staccato sign"; } } public static class StaccatissimoSign extends ArticulationSign { StaccatissimoSign() { super(braille(6, 236)); } public String getDescription() { return "A staccatissimo sign"; } } public static class TenutoSign extends ArticulationSign { TenutoSign() { super(braille(456, 236)); } public String getDescription() { return "A tenuto sign"; } } }