/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseDuration( String s, int index, NoteContext noteContext, StaccatoParserContext parserContext) { if (index < s.length()) { switch (s.charAt(index)) { case '/': index = parseNumericDuration(s, index, noteContext); break; case 'W': case 'H': case 'Q': case 'I': case 'S': case 'T': case 'X': case 'O': case '-': index = parseLetterDuration(s, index, noteContext, parserContext); break; default: noteContext.decimalDuration = DefaultNoteSettingsManager.getInstance().getDefaultDuration(); noteContext.isDurationExplicitlySet = false; break; // Could get here if the next character is a velocity char ("a" or "d") } index = parseTuplet(s, index, noteContext); } else { noteContext.decimalDuration = DefaultNoteSettingsManager.getInstance().getDefaultDuration(); noteContext.isDurationExplicitlySet = false; } logger.info("Decimal duration is " + noteContext.decimalDuration); return index; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseNumericDuration(String s, int index, NoteContext context) { // The duration has come in as a number, like 0.25 for a quarter note. // Advance pointer past the initial slash (/) index++; // If first character before the numeric value is a dash, we're ending a tie if (s.charAt(index) == '-') { context.isEndOfTie = true; index++; } context.isDurationExplicitlySet = true; // Get the duration value int endingIndex = seekToEndOfDecimal(s, index); String durationNumberString = s.substring(index, endingIndex); context.decimalDuration += Double.parseDouble(durationNumberString); logger.info("Decimal duration is " + context.decimalDuration); index = endingIndex; // If the character after all of the value parsing is a dash, we're starting a tie if ((index < s.length()) && (s.charAt(index) == '-')) { context.isStartOfTie = true; index++; } return index; }
private int parseNoteElement(String s, int index, StaccatoParserContext parserContext) { boolean repeat = false; NoteContext noteContext = new NoteContext(); do { // Begin the voyage of creating a note by populating the NoteContext index = parseNoteElement(s, index, noteContext, parserContext); if (noteContext.isChord) { Chord chord = noteContext.createChord(parserContext); parserContext.getParser().fireChordParsed(chord); } else { Note note = noteContext.createNote(parserContext); parserContext.getParser().fireNoteParsed(note); } // // If the note is a chord, fire all the note events for each note in the chord // if (noteContext.isChord) { // Note[] notes = new Chord(note, noteContext.intervals).getNotes(); // for (int i=1; i < notes.length; i++) { // NoteContext context2 = noteContext.createChordNoteContext(); // context2.noteNumber = notes[i].getValue(); // logger.info("Next note in chord is "+context2.noteNumber); // parserContext.fireNoteParsed(context2.createNote(parserContext)); // } // } // Determine if there is another note to parse, and set up the next NoteContext repeat = noteContext.thereIsAnother; noteContext = noteContext.createNextNoteContext(); } while (repeat); return index; }
/** * Must be called (instead of the constructor) for notes other than the first note being parsed * * @return */ public NoteContext createNextNoteContext() { NoteContext noteContext = new NoteContext(); noteContext.isFirstNote = false; noteContext.isMelodicNote = anotherNoteIsMelodic; noteContext.isHarmonicNote = anotherNoteIsHarmonic; return noteContext; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseChord(String s, int index, NoteContext context) { // Don't parse chord for a rest if (context.isRest) { return index; } int lengthOfChordString = 0; boolean chordFound = false; String[] chordNames = Chord.getChordNames(); for (String chordName : chordNames) { if (!chordFound && (s.length() >= index + chordName.length()) && chordName.equals(s.substring(index, index + chordName.length()))) { chordFound = true; lengthOfChordString = chordName.length(); context.isChord = true; context.intervals = Chord.getIntervals(chordName); context.chordName = chordName; logger.info( "Chord: " + chordName + " Interval Pattern: " + Chord.getIntervals(chordName)); break; } } return index + lengthOfChordString; }
// // Methods from NoteProvider // @Override public Note createNote(String noteString) { StaccatoParserContext parserContext = new StaccatoParserContext(new StaccatoParser()); NoteContext noteContext = new NoteContext(); parseNoteElement(noteString, 0, noteContext, parserContext); return noteContext.createNote(parserContext); }
public NoteContext createChordNoteContext() { NoteContext noteContext = new NoteContext(); noteContext.isFirstNote = false; noteContext.isMelodicNote = false; noteContext.isHarmonicNote = true; noteContext.decimalDuration = decimalDuration; return noteContext; }
private int parseBracketedNote(String s, int index, NoteContext context) { int indexOfEndBracket = s.indexOf(']', index); String stringInBrackets = s.substring(index + 1, indexOfEndBracket); context.noteValueAsString = stringInBrackets; context.isNumericNote = true; logger.info( "This note is a note represented by the dictionary value " + context.noteValueAsString); return indexOfEndBracket + 1; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseOctave(String s, int index, NoteContext context) { context.isOctaveExplicitlySet = false; // Don't parse an octave for a rest or a numeric note if (context.isRest || context.isNumericNote) { return index; } // Check for octave. Remember that octaves are optional. // Octaves can be two digits, which is what this next bit is testing for. // But, there could be no octave here as well. char possibleOctave1 = '.'; char possibleOctave2 = '.'; if (index < s.length()) { possibleOctave1 = s.charAt(index); } if (index + 1 < s.length()) { possibleOctave2 = s.charAt(index + 1); } byte definiteOctaveLength = 0; if ((possibleOctave1 >= '0') && (possibleOctave1 <= '9')) { definiteOctaveLength = 1; if (possibleOctave2 == '0') { definiteOctaveLength = 2; } // if ((possibleOctave1 == '-') && (definiteOctaveLength == 1)) { // // That '-' isn't representing a -1 octave, it's probably representing the // end of a tie! // // It's a duration character, not an octave character! // return index; // } logger.info("Octave is " + definiteOctaveLength + " digits long"); String octaveNumberString = s.substring(index, index + definiteOctaveLength); logger.info("Octave string value is " + octaveNumberString); try { context.octaveNumber = Integer.parseInt(octaveNumberString) + context.octaveBias; context.isOctaveExplicitlySet = true; } catch (NumberFormatException e) { throw new ParserException(StaccatoMessages.OCTAVE_OUT_OF_RANGE, s); } if (context.octaveNumber > Note.MAX_OCTAVE) { throw new ParserException(StaccatoMessages.OCTAVE_OUT_OF_RANGE, s); } if (context.octaveNumber < Note.MIN_OCTAVE) { throw new ParserException(StaccatoMessages.OCTAVE_OUT_OF_RANGE, s); } context.originalString = context.originalString + octaveNumberString; } return index + definiteOctaveLength; }
private void setDefaultOctave(NoteContext context) { logger.info("No octave string found, setting default octave"); if (context.isChord) { context.octaveNumber = DefaultNoteSettingsManager.getInstance().getDefaultBassOctave() + context.octaveBias; } else { context.octaveNumber = DefaultNoteSettingsManager.getInstance().getDefaultOctave() + context.octaveBias; } }
public Chord createChord(String chordString) { // If the user requested a chord like "C" or "Ab" without providing any additional details, // assume it's MAJOR if (chordString.length() <= 2) { chordString = chordString + "MAJ"; } StaccatoParserContext parserContext = new StaccatoParserContext(new StaccatoParser()); NoteContext noteContext = new NoteContext(); parseNoteElement(chordString, 0, noteContext, parserContext); return noteContext.createChord(parserContext); }
/** This method does a variety of calculations to get the actual value of the note. */ private void computeNoteValue(NoteContext noteContext, StaccatoParserContext parserContext) { // Don't compute note value for a rest if (noteContext.isRest) { return; } // Adjust for Key Signature if (parserContext.getKey() != null) { int keySig = KeyProviderFactory.getKeyProvider().convertKeyToByte(parserContext.getKey()); if ((keySig != 0) && (!noteContext.isNatural)) { if ((keySig <= -1) && (noteContext.noteNumber == 11)) noteContext.noteNumber = 10; if ((keySig <= -2) && (noteContext.noteNumber == 4)) noteContext.noteNumber = 3; if ((keySig <= -3) && (noteContext.noteNumber == 9)) noteContext.noteNumber = 8; if ((keySig <= -4) && (noteContext.noteNumber == 2)) noteContext.noteNumber = 1; if ((keySig <= -5) && (noteContext.noteNumber == 7)) noteContext.noteNumber = 6; if ((keySig <= -6) && (noteContext.noteNumber == 0)) { noteContext.noteNumber = 11; noteContext.octaveNumber--; } if ((keySig <= -7) && (noteContext.noteNumber == 5)) noteContext.noteNumber = 4; if ((keySig >= +1) && (noteContext.noteNumber == 5)) noteContext.noteNumber = 6; if ((keySig >= +2) && (noteContext.noteNumber == 0)) noteContext.noteNumber = 1; if ((keySig >= +3) && (noteContext.noteNumber == 7)) noteContext.noteNumber = 8; if ((keySig >= +4) && (noteContext.noteNumber == 2)) noteContext.noteNumber = 3; if ((keySig >= +5) && (noteContext.noteNumber == 9)) noteContext.noteNumber = 10; if ((keySig >= +6) && (noteContext.noteNumber == 4)) noteContext.noteNumber = 5; if ((keySig >= +7) && (noteContext.noteNumber == 11)) { noteContext.noteNumber = 0; noteContext.octaveNumber++; } logger.info( "After adjusting for Key Signature, noteNumber=" + noteContext.noteNumber + " octave=" + noteContext.octaveNumber); } } // Compute the actual note number, based on octave and note if (!noteContext.isNumericNote) { int intNoteNumber = ((noteContext.octaveNumber) * 12) + noteContext.noteNumber + noteContext.internalInterval; if (intNoteNumber > 127) { throw new ParserException( StaccatoMessages.CALCULATED_NOTE_OUT_OF_RANGE, Integer.toString(intNoteNumber)); } noteContext.noteNumber = (byte) intNoteNumber; logger.info("Computed note number: " + noteContext.noteNumber); } }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseNumericNote(String s, int index, NoteContext context) { int numCharsInNumber = 0; while (numCharsInNumber < s.length() && (s.charAt(index + numCharsInNumber) >= '0') && (s.charAt(index + numCharsInNumber) <= '9')) { numCharsInNumber++; } String numericNoteString = s.substring(index, index + numCharsInNumber); context.noteNumber = Byte.parseByte(numericNoteString); context.isNumericNote = true; logger.info("This note is a numeric note with value " + context.noteNumber); return index + numCharsInNumber; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseInternalInterval(String s, int index, NoteContext context) { if (context.isRest) { return index; } // An internal interval is indicated by a single quote if ((index < s.length()) && (s.charAt(index) == '\'')) { int intervalLength = 0; // Verify that index+1 is a number representing the interval. if (index + 1 < s.length() && ((s.charAt(index + 1) >= '0') && (s.charAt(index + 1) <= '9'))) { intervalLength = 1; } // We'll allow for the possibility of double-sharps and double-flats. if ((intervalLength == 1) && (index + 2 < s.length()) && ((s.charAt(index + 2) == '#') || (s.charAt(index + 2) == 'B'))) { intervalLength = 2; } if ((intervalLength == 2) && (index + 3 < s.length()) && ((s.charAt(index + 3) == '#') || (s.charAt(index + 3) == 'B'))) { intervalLength = 3; } context.internalInterval = Intervals.getHalfsteps(s.substring(index + 1, index + intervalLength + 1)); return index + intervalLength + 1; } else { return index; } }
/** * Returns the String of the next sub-token (the parts after + or _), if one exists; otherwise, * returns null */ private int parseConnector(String s, int index, NoteContext context) { context.thereIsAnother = false; // See if there's another note to process if ((index < s.length()) && ((s.charAt(index) == '+') || (s.charAt(index) == '_'))) { logger.info("Another note: string = " + s.substring(index, s.length() - 1)); if (s.charAt(index) == '_') { context.anotherNoteIsMelodic = true; logger.info("Next note will be melodic"); } else { context.anotherNoteIsHarmonic = true; logger.info("Next note will be harmonic"); } index++; context.thereIsAnother = true; } return index; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseTuplet(String s, int index, NoteContext context) { if (index < s.length()) { if (s.charAt(index) == '*') { logger.info("Note is a tuplet"); index++; // Figure out tuplet ratio, or figure out when to stop looking for tuplet info boolean stopTupletParsing = false; int indexOfUnitsToMatch = 0; int indexOfNumNotes = 0; int counter = -1; while (!stopTupletParsing) { counter++; if (s.length() > index + counter) { if (s.charAt(index + counter) == ':') { indexOfNumNotes = index + counter + 1; } else if ((s.charAt(index + counter) >= '0') && (s.charAt(index + counter) <= '9')) { if (indexOfUnitsToMatch == 0) { indexOfUnitsToMatch = index + counter; } } else if ((s.charAt(index + counter) == '*')) { // no op... artifact of parsing } else { stopTupletParsing = true; } } else { stopTupletParsing = true; } } index += counter; double numerator = 2.0d; double denominator = 3.0d; if ((indexOfUnitsToMatch > 0) && (indexOfNumNotes > 0)) { numerator = Double.parseDouble(s.substring(indexOfUnitsToMatch, indexOfNumNotes - 1)); denominator = Double.parseDouble(s.substring(indexOfNumNotes, index)); } logger.info("Tuplet ratio is " + numerator + ":" + denominator); double tupletRatio = numerator / denominator; context.decimalDuration = context.decimalDuration * (1.0d / tupletRatio); logger.info("Decimal duration after tuplet is " + context.decimalDuration); } } return index; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseQuantityDuration(String s, int index, NoteContext context) { // A quantity is associated with the duration, like the '24' in "w24" int endingIndex = seekToEndOfDecimal(s, index); String quantityNumberString = s.substring(index, endingIndex); context.decimalDuration += (1.0f / context.mostRecentDuration) * (Double.parseDouble(quantityNumberString) - 1.0D); // Subtract 1, because mostRecentDuration has already been added to the // total duration logger.info( "Quantity duration calculation: Duration of 1/" + context.mostRecentDuration + " * " + quantityNumberString + " = " + (1.0 / context.mostRecentDuration) * Double.parseDouble(quantityNumberString)); return endingIndex; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseLetterNote(String s, int index, NoteContext context) { context.isNumericNote = false; int originalIndex = index; switch (s.charAt(index)) { case 'C': context.noteNumber = 0; break; case 'D': context.noteNumber = 2; break; case 'E': context.noteNumber = 4; break; case 'F': context.noteNumber = 5; break; case 'G': context.noteNumber = 7; break; case 'A': context.noteNumber = 9; break; case 'B': context.noteNumber = 11; break; default: break; } index++; // Check for #, b, or n (sharp, flat, or natural) modifier boolean checkForModifiers = true; while (checkForModifiers) { if (index < s.length()) { switch (s.charAt(index)) { case '#': index++; context.noteNumber++; if (context.noteNumber == 12) { context.noteNumber = 0; context.octaveBias++; } break; case 'B': index++; context.noteNumber--; if (context.noteNumber == -1) { context.noteNumber = 11; context.octaveBias--; } break; case 'N': index++; context.isNatural = true; checkForModifiers = false; break; default: checkForModifiers = false; break; } } else { checkForModifiers = false; } } context.originalString = s.substring(originalIndex, index); logger.info( "Note number within an octave (C=0, B=11): " + context.noteNumber + " (with octaveBias = " + context.octaveBias + ")"); return index; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseRest(String s, int index, NoteContext context) { context.isRest = true; logger.info("This note is a Rest"); return index + 1; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseVelocity(String s, int index, NoteContext context) { // Don't compute note velocity for a rest if (context.isRest) { return index; } // Process velocity attributes, if they exist while (index < s.length()) { int startPoint = index + 1; int endPoint = startPoint; char velocityChar = s.charAt(index); int lengthOfByte = 0; if ((velocityChar == '+') || (velocityChar == '_') || (velocityChar == ' ')) break; logger.info("Identified Velocity character " + velocityChar); boolean byteDone = false; while (!byteDone && (index + lengthOfByte + 1 < s.length())) { char possibleByteChar = s.charAt(index + lengthOfByte + 1); if ((possibleByteChar >= '0') && (possibleByteChar <= '9')) { lengthOfByte++; } else { byteDone = true; } } endPoint = index + lengthOfByte + 1; if (startPoint == endPoint) { return endPoint; } byte velocityNumber = Byte.parseByte(s.substring(startPoint, endPoint)); // Or maybe a bracketed string was passed in, instead of a byte String velocityString = null; if ((index + 1 < s.length()) && (s.charAt(index + 1) == '[')) { endPoint = s.indexOf(']', startPoint) + 1; velocityString = s.substring(startPoint, endPoint); } switch (velocityChar) { case 'A': if (velocityString == null) { context.noteOnVelocity = velocityNumber; } else { context.noteOnVelocityValueAsString = velocityString; } context.hasNoteOnVelocity = true; break; case 'D': if (velocityString == null) { context.noteOffVelocity = velocityNumber; } else { context.noteOffVelocityValueAsString = velocityString; } context.hasNoteOffVelocity = true; break; default: throw new ParserException( StaccatoMessages.VELOCITY_CHARACTER_NOT_RECOGNIZED, s.substring(startPoint, endPoint)); } index = endPoint; } if (context.hasNoteOnVelocity) { logger.info("Attack velocity = " + context.noteOnVelocity); } if (context.hasNoteOffVelocity) { logger.info("Decay velocity = " + context.noteOffVelocity); } return index; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseLetterDuration( String s, int index, NoteContext context, StaccatoParserContext parserContext) { boolean moreDurationCharsToParse = true; boolean isDotted = false; while (moreDurationCharsToParse == true) { int durationNumber = 0; // See if the note has a duration. Duration is optional for a note. if (index < s.length()) { char durationChar = s.charAt(index); switch (durationChar) { case '-': if ((context.decimalDuration == 0.0D) && (!context.isEndOfTie)) { context.isEndOfTie = true; logger.info("Note is end of tie"); } else { context.isStartOfTie = true; logger.info("Note is start of tie"); } break; case 'W': durationNumber = 1; break; case 'H': durationNumber = 2; break; case 'Q': durationNumber = 4; break; case 'I': durationNumber = 8; break; case 'S': durationNumber = 16; break; case 'T': durationNumber = 32; break; case 'X': durationNumber = 64; break; case 'O': durationNumber = 128; break; default: index--; moreDurationCharsToParse = false; break; } index++; if ((index < s.length()) && (s.charAt(index) == '.')) { isDotted = true; index++; } if (durationNumber > 0) { context.isDurationExplicitlySet = true; double d = 1.0 / durationNumber; if (isDotted) { context.decimalDuration += d + (d / 2.0); } else { context.decimalDuration += d; } } context.mostRecentDuration = durationNumber; if ((index < s.length()) && (s.charAt(index) >= '0') && (s.charAt(index) <= '9')) { index = parseQuantityDuration(s, index, context); } } else { moreDurationCharsToParse = false; } } return index; }
/** * Returns the index with which to start parsing the next part of the string, once this method is * done with its part */ private int parseChordInversion(String s, int index, NoteContext context) { if (!context.isChord) { return index; } int inversionCount = 0; boolean bassNote = false; int startIndex = index; boolean checkForInversion = true; while (checkForInversion) { if (index < s.length()) { switch (s.charAt(index)) { case '^': index++; inversionCount++; break; case 'C': index++; bassNote = true; break; case 'D': index++; bassNote = true; break; case 'E': index++; bassNote = true; break; case 'F': index++; bassNote = true; break; case 'G': index++; bassNote = true; break; case 'A': index++; bassNote = true; break; case 'B': index++; bassNote = true; break; case '#': index++; break; // presumably the sharp mark followed a note // For '0', need to differentiate between initial 0 and 0 as a second digit (i.e., 10) case '0': index++; inversionCount = (inversionCount == -1) ? 0 : inversionCount + 10; break; case '1': index++; inversionCount = 1; break; case '2': index++; inversionCount = 2; break; case '3': index++; inversionCount = 3; break; case '4': index++; inversionCount = 4; break; case '5': index++; inversionCount = 5; break; case '6': index++; inversionCount = 6; break; case '7': index++; inversionCount = 7; break; case '8': index++; inversionCount = 8; break; case '9': index++; inversionCount = 9; break; // For '[', we're checking for a note number after the inversion marker case '[': int indexEndBracket = s.indexOf(']', index); context.inversionBassNote = Note.getToneString(Byte.parseByte(s.substring(index + 1, indexEndBracket - 1))); index = indexEndBracket + 1; break; default: checkForInversion = false; break; } } else { checkForInversion = false; } } // Modify the note values based on the inversion if (bassNote) { context.inversionBassNote = context.inversionBassNote = s.substring(startIndex + 1, index); } else if (inversionCount > 0) { context.inversionCount = inversionCount; } return index; }