Example #1
0
 /**
  * Turns a NoteToken into a Pitch by equating their representations and then enforcing the Key of
  * the Piece and the Accidentals encountered in the given meter
  *
  * @param workingNote, the NoteToken from which to make a Pitch
  * @return A Pitch that represents the given input NoteToken
  */
 private Pitch constructNote(NoteToken workingNote) {
   Pitch workingPitch;
   int octave = 0;
   // Add the note initally
   if ('A' <= workingNote.getNote() && workingNote.getNote() <= 'G') {
     workingPitch = new Pitch(workingNote.getNote());
   } else {
     octave++;
     workingPitch =
         new Pitch(Character.toUpperCase(workingNote.getNote())).transpose(Pitch.OCTAVE);
   }
   // Transpose by the octaves
   octave += workingNote.getOctave();
   workingPitch = workingPitch.octaveTranspose(workingNote.getOctave());
   // Collect accidentals, use them to modify the accidental list
   switch (workingNote.getNote()) {
     case 'A':
     case 'a':
       if (workingNote.getAccidental() == Integer.MIN_VALUE) {
         keyAccidental.put("A" + octave, 0);
       } else if (workingNote.getAccidental() == 0) {
         keyAccidental.put("A" + octave, -1 * keySignature.get('A'));
       } else {
         keyAccidental.put("A" + octave, -1 * keySignature.get('A') + workingNote.getAccidental());
       }
       break;
     case 'B':
     case 'b':
       if (workingNote.getAccidental() == Integer.MIN_VALUE) {
         keyAccidental.put("B" + octave, 0);
       } else if (workingNote.getAccidental() == 0) {
         keyAccidental.put("B" + octave, -1 * keySignature.get('B'));
       } else {
         keyAccidental.put("B" + octave, -1 * keySignature.get('B') + workingNote.getAccidental());
       }
       break;
     case 'C':
     case 'c':
       if (workingNote.getAccidental() == Integer.MIN_VALUE) {
         keyAccidental.put("C" + octave, 0);
       } else if (workingNote.getAccidental() == 0) {
         keyAccidental.put("C" + octave, -1 * keySignature.get('C'));
       } else {
         keyAccidental.put("C" + octave, -1 * keySignature.get('C') + workingNote.getAccidental());
       }
       break;
     case 'D':
     case 'd':
       if (workingNote.getAccidental() == Integer.MIN_VALUE) {
         keyAccidental.put("D" + octave, 0);
       } else if (workingNote.getAccidental() == 0) {
         keyAccidental.put("D" + octave, -1 * keySignature.get('D'));
       } else {
         keyAccidental.put("D" + octave, -1 * keySignature.get('D') + workingNote.getAccidental());
       }
       break;
     case 'E':
     case 'e':
       if (workingNote.getAccidental() == Integer.MIN_VALUE) {
         keyAccidental.put("E" + octave, 0);
       } else if (workingNote.getAccidental() == 0) {
         keyAccidental.put("E" + octave, -1 * keySignature.get('E'));
       } else {
         keyAccidental.put("E" + octave, -1 * keySignature.get('E') + workingNote.getAccidental());
       }
       break;
     case 'F':
     case 'f':
       if (workingNote.getAccidental() == Integer.MIN_VALUE) {
         keyAccidental.put("F" + octave, 0);
       } else if (workingNote.getAccidental() == 0) {
         keyAccidental.put("F" + octave, -1 * keySignature.get('F'));
       } else {
         keyAccidental.put("F" + octave, -1 * keySignature.get('F') + workingNote.getAccidental());
       }
       break;
     case 'G':
     case 'g':
       if (workingNote.getAccidental() == Integer.MIN_VALUE) {
         keyAccidental.put("G" + octave, 0);
       } else if (workingNote.getAccidental() == 0) {
         keyAccidental.put("G" + octave, -1 * keySignature.get('G'));
       } else {
         keyAccidental.put("G" + octave, -1 * keySignature.get('G') + workingNote.getAccidental());
       }
       break;
   }
   // Now modify by the accidental and key signature in tandem
   switch (workingNote.getNote()) {
     case 'A':
     case 'a':
       workingPitch = workingPitch.accidentalTranspose(keyAccidental.get("A" + octave));
       workingPitch = workingPitch.accidentalTranspose(keySignature.get('A'));
       break;
     case 'B':
     case 'b':
       workingPitch = workingPitch.accidentalTranspose(keyAccidental.get("B" + octave));
       workingPitch = workingPitch.accidentalTranspose(keySignature.get('B'));
       break;
     case 'C':
     case 'c':
       workingPitch = workingPitch.accidentalTranspose(keyAccidental.get("C" + octave));
       workingPitch = workingPitch.accidentalTranspose(keySignature.get('C'));
       break;
     case 'D':
     case 'd':
       workingPitch = workingPitch.accidentalTranspose(keyAccidental.get("D" + octave));
       workingPitch = workingPitch.accidentalTranspose(keySignature.get('D'));
       break;
     case 'E':
     case 'e':
       workingPitch = workingPitch.accidentalTranspose(keyAccidental.get("E" + octave));
       workingPitch = workingPitch.accidentalTranspose(keySignature.get('E'));
       break;
     case 'F':
     case 'f':
       workingPitch = workingPitch.accidentalTranspose(keyAccidental.get("F" + octave));
       workingPitch = workingPitch.accidentalTranspose(keySignature.get('F'));
       break;
     case 'G':
     case 'g':
       workingPitch = workingPitch.accidentalTranspose(keyAccidental.get("G" + octave));
       workingPitch = workingPitch.accidentalTranspose(keySignature.get('G'));
       break;
   }
   return workingPitch;
 }
Example #2
0
  /**
   * Transforms the Voices of the Piece into Pitches which are then subscribed according to timing
   * to an internal SequencePlayer, then plays that SequencePlayer to play the Piece out loud
   *
   * <p>Catches SequencePlayer Errors internally to avoid them being part of the ABCMusic's
   * responsibility
   *
   * <p>Note: because tempo must be converted to quarter notes, tempos that are not divisible by 4
   * will have rounding errors that propagate into the music itself, its fairly negligible, but it
   * exists.
   *
   * <p>Note: Assumes that 1/16 of the default note length is the shortest note that would be
   * desired to play, any note shorter will play for 1/16 of a default note duration
   */
  public void PlayMusic() {
    try {
      // Right now it converts bpm to be based on the number of 1/4 given how many L*Q notes exist
      // with the quantized time being
      int quartersPerMin = (getTempo() * getNoteLength()[0] * 4) / (getNoteLength()[1]);
      int ticksPerQuarter = (getNoteLength()[1] * 16) / (getNoteLength()[0] * 4);
      player = new SequencePlayer(quartersPerMin, ticksPerQuarter);
      // Figure out what the Key means, and keep it as a map to edit notes as they are added
      keySignature = getKeySignature();
      // Initialize the accidentals as none, then use that as what gets edited through a meter
      resetKeyAccidental();

      for (Iterator<Voice> i = VoicesList.iterator(); i.hasNext(); ) {
        int startTick = 0;
        for (Iterator<Bars> j = i.next().BarsList.iterator(); j.hasNext(); ) {
          for (Iterator<Meters> k = j.next().MetersList.iterator(); k.hasNext(); ) {
            for (Iterator<NoteToken> l = k.next().getElts().iterator(); l.hasNext(); ) {
              NoteToken workingNote = l.next();
              if (workingNote.getType() == Tokens.Type.CHORD) {
                int noteTicks = 0;
                for (int m = 0; m < workingNote.getElts().length; m++) {
                  Pitch newNote = constructNote(workingNote.getElts()[m]);
                  // Length of a note in terms of quarters
                  double noteLength =
                      ((double) workingNote.getElts()[m].getLength()[0]
                              / (double) workingNote.getElts()[m].getLength()[1])
                          * ((double) (getNoteLength()[0] * 4) / ((double) getNoteLength()[1]));
                  // Convert the quarters to valid ticks
                  noteTicks = (int) (noteLength * ticksPerQuarter);
                  player.addNote(newNote.toMidiNote(), startTick, noteTicks);
                }
                startTick += noteTicks;
              } else if (workingNote.getType() == Tokens.Type.REST) {
                // Don't need to make rests
                // Length of a note in terms of quarters
                double noteLength =
                    ((double) workingNote.getLength()[0] / (double) workingNote.getLength()[1])
                        * ((double) (getNoteLength()[0] * 4) / ((double) getNoteLength()[1]));
                // Convert the quarters to valid ticks
                // Add ticks for the rests skipping time
                int noteTicks = (int) (noteLength * ticksPerQuarter);
                startTick += noteTicks;
              } else {
                Pitch newNote = constructNote(workingNote);
                // Length of a note in terms of quarters
                double noteLength =
                    ((double) workingNote.getLength()[0] / (double) workingNote.getLength()[1])
                        * ((double) (getNoteLength()[0] * 4) / ((double) getNoteLength()[1]));
                // Convert the quarters to valid ticks
                int noteTicks = (int) (noteLength * ticksPerQuarter);
                player.addNote(newNote.toMidiNote(), startTick, noteTicks);
                startTick += noteTicks;
              }
            }
            // Reset Accidentals at the end of the Meter
            resetKeyAccidental();
          }
        }
      }
      // Play the finished SequencePlayer with all Voice there-in
      player.play();
      // Catch undesirable errors
    } catch (MidiUnavailableException e) {
      e.printStackTrace();
    } catch (InvalidMidiDataException e) {
      e.printStackTrace();
    }
  }
 // Transforms a Song in to language that can be fed to the MIDI Sequencer
 public void play() throws MidiUnavailableException, InvalidMidiDataException {
   // LCM calculations that ultimately give us how many ticks per beat we should have
   Fraction lcmCalc = defaultNoteLength;
   int lcm = 0;
   for (String voiceName : this.musicForVoiceName.keySet()) {
     for (Music m : this.musicForVoiceName.get(voiceName)) {
       lcmCalc =
           new Fraction(
               1, Fraction.LCM(m.getDuration().getDenominator(), lcmCalc.getDenominator()));
     }
     if (lcmCalc.getDenominator() > lcm) {
       lcm = lcmCalc.getDenominator();
     }
   }
   LyricListener listener =
       new LyricListener() {
         public void processLyricEvent(String text) {
           System.out.println(text);
         }
       };
   // Initializes a new SequencePlayer that will make our notes audible
   SequencePlayer seqPlayer = new SequencePlayer(this.beatsPerMinute, lcm, listener);
   int startTick = 0;
   int duration;
   // Considers every Voice in our Song
   for (String voiceName : this.musicForVoiceName.keySet()) {
     String lyricsPrefix = "";
     if (!voiceName.equals("THE_DEFAULT_VOICE")) lyricsPrefix = voiceName;
     startTick = 0;
     // Considers every Music element in our Voice
     for (Music m : this.musicForVoiceName.get(voiceName)) {
       duration =
           (int)
               (m.getDuration().toDouble()
                   * defaultNoteLength.toDouble()
                   / tempoBeat.toDouble()
                   * lcm);
       if (m instanceof Note) {
         Note mNote = (Note) m;
         Pitch pitch =
             new Pitch(mNote.getNote().toString().charAt(0))
                 .transpose(mNote.getAccidental().getSemitoneOffset() + 12 * mNote.getOctave());
         if (mNote.getSyllable() != null && !mNote.getSyllable().equals("")) {
           seqPlayer.addLyricEvent(lyricsPrefix + mNote.getSyllable(), startTick);
         }
         seqPlayer.addNote(pitch.toMidiNote(), startTick, duration);
         startTick += duration;
       } else if (m instanceof Chord) {
         Chord mChord = (Chord) m;
         if (mChord.getSyllable() != null && !mChord.getSyllable().equals("")) {
           seqPlayer.addLyricEvent(lyricsPrefix + mChord.getSyllable(), startTick);
         }
         for (Note n : mChord.getNotes()) {
           Pitch pitch =
               new Pitch(n.getNote().toString().charAt(0))
                   .transpose(n.getAccidental().getSemitoneOffset() + 12 * n.getOctave());
           seqPlayer.addNote(pitch.toMidiNote(), startTick, duration);
         }
         startTick += duration;
       } else if (m instanceof Tuplet) {
         Tuplet mTuplet = (Tuplet) m;
         int tupleNoteDur = duration;
         tupleNoteDur = getTupleNoteDur(mTuplet.getType(), tupleNoteDur);
         for (Music tupletElem : mTuplet.getNotes()) {
           if (tupletElem instanceof Note) {
             Pitch pitch1 = null;
             Note n = (Note) tupletElem;
             pitch1 =
                 new Pitch(n.getNote().toString().charAt(0))
                     .transpose(n.getAccidental().getSemitoneOffset() + 12 * n.getOctave());
             if (n.getSyllable() != null && !n.getSyllable().equals("")) {
               seqPlayer.addLyricEvent(lyricsPrefix + n.getSyllable(), startTick);
             }
             seqPlayer.addNote(pitch1.toMidiNote(), startTick, tupleNoteDur);
             startTick += tupleNoteDur;
           } else if (tupletElem instanceof Chord) {
             Chord c = (Chord) tupletElem;
             if (c.getSyllable() != null && !c.getSyllable().equals("")) {
               seqPlayer.addLyricEvent(lyricsPrefix + c.getSyllable(), startTick);
             }
             for (Note note : c.getNotes()) {
               Pitch pitch2 = null;
               pitch2 =
                   new Pitch(note.getNote().toString().charAt(0))
                       .transpose(
                           note.getAccidental().getSemitoneOffset() + 12 * note.getOctave());
               seqPlayer.addNote(pitch2.toMidiNote(), startTick, tupleNoteDur);
             }
             startTick += tupleNoteDur;
           } else if (tupletElem instanceof Rest) {
             startTick += tupleNoteDur;
           } else {
             throw new RuntimeException(
                 "You cannot build a tuple out of anything but a Note, Chord, or Rest");
           }
         }
       } else if (m instanceof Rest) {
         startTick += duration;
       }
     }
   }
   seqPlayer.play();
 }