text: # Copyright 2012 Justas Sadzevicius
  # Licensed under the MIT license: http://www.opensource.org/licenses/MIT
  # Notes library for Finch buzzer
  
  #("Middle C" is C4 )
  
  frequencies = {
      'C0': 16.35,
      'C#0': 17.32,
      'Db0': 17.32,
      'D0': 18.35,
      'D#0': 19.45,
      'Eb0': 19.45,
      'E0': 20.6,
      'F0': 21.83,
      'F#0': 23.12,
      'Gb0': 23.12,
      'G0': 24.5,
      'G#0': 25.96,
      'Ab0': 25.96,
      'A0': 27.5,
      'A#0': 29.14,
      'Bb0': 29.14,
      'B0': 30.87,
      'C1': 32.7,
      'C#1': 34.65,
      'Db1': 34.65,
      'D1': 36.71,
      'D#1': 38.89,
      'Eb1': 38.89,
      'E1': 41.2,
      'F1': 43.65,
      'F#1': 46.25,
      'Gb1': 46.25,
      'G1': 49.0,
      'G#1': 51.91,
      'Ab1': 51.91,
      'A1': 55.0,
      'A#1': 58.27,
      'Bb1': 58.27,
      'B1': 61.74,
      'C2': 65.41,
      'C#2': 69.3,
      'Db2': 69.3,
      'D2': 73.42,
      'D#2': 77.78,
      'Eb2': 77.78,
      'E2': 82.41,
      'F2': 87.31,
      'F#2': 92.5,
      'Gb2': 92.5,
      'G2': 98.0,
      'G#2': 103.83,
      'Ab2': 103.83,
      'A2': 110.0,
      'A#2': 116.54,
      'Bb2': 116.54,
      'B2': 123.47,
      'C3': 130.81,
      'C#3': 138.59,
      'Db3': 138.59,
      'D3': 146.83,
      'D#3': 155.56,
      'Eb3': 155.56,
      'E3': 164.81,
      'F3': 174.61,
      'F#3': 185.0,
      'Gb3': 185.0,
      'G3': 196.0,
      'G#3': 207.65,
      'Ab3': 207.65,
      'A3': 220.0,
      'A#3': 233.08,
      'Bb3': 233.08,
      'B3': 246.94,
      'C4': 261.63,
      'C#4': 277.18,
      'Db4': 277.18,
      'D4': 293.66,
      'D#4': 311.13,
      'Eb4': 311.13,
      'E4': 329.63,
      'F4': 349.23,
      'F#4': 369.99,
      'Gb4': 369.99,
      'G4': 392.0,
      'G#4': 415.3,
      'Ab4': 415.3,
      'A4': 440.0,
      'A#4': 466.16,
      'Bb4': 466.16,
      'B4': 493.88,
      'C5': 523.25,
      'C#5': 554.37,
      'Db5': 554.37,
      'D5': 587.33,
      'D#5': 622.25,
      'Eb5': 622.25,
      'E5': 659.26,
      'F5': 698.46,
      'F#5': 739.99,
      'Gb5': 739.99,
      'G5': 783.99,
      'G#5': 830.61,
      'Ab5': 830.61,
      'A5': 880.0,
      'A#5': 932.33,
      'Bb5': 932.33,
      'B5': 987.77,
      'C6': 1046.5,
      'C#6': 1108.73,
      'Db6': 1108.73,
      'D6': 1174.66,
      'D#6': 1244.51,
      'Eb6': 1244.51,
      'E6': 1318.51,
      'F6': 1396.91,
      'F#6': 1479.98,
      'Gb6': 1479.98,
      'G6': 1567.98,
      'G#6': 1661.22,
      'Ab6': 1661.22,
      'A6': 1760.0,
      'A#6': 1864.66,
      'Bb6': 1864.66,
      'B6': 1975.53,
      'C7': 2093.0,
      'C#7': 2217.46,
      'Db7': 2217.46,
      'D7': 2349.32,
      'D#7': 2489.02,
      'Eb7': 2489.02,
      'E7': 2637.02,
      'F7': 2793.83,
      'F#7': 2959.96,
      'Gb7': 2959.96,
      'G7': 3135.96,
      'G#7': 3322.44,
      'Ab7': 3322.44,
      'A7': 3520.0,
      'A#7': 3729.31,
      'Bb7': 3729.31,
      'B7': 3951.07,
      'C8': 4186.01,
      'C#8': 4434.92,
      'Db8': 4434.92,
      'D8': 4698.64,
      'D#8': 4978.03,
      'Eb8': 4978.03,
  }
  
  
  class Command(object):
      octave = 4
      note = ''
      duration = 0
      sym = ''
  
      def reset(self):
          self.note = ''
          self.duration = 0
          self.sym = ''
  
      def emit(self):
          if not self.note or not self.duration:
              return None
          if self.note == '-':
              return (0, self.duration)
          ref = self.note.upper()+self.sym+str(self.octave)
          frequency = frequencies.get(ref)
          if not frequency:
              return None
          return (frequency, self.duration)
  
  
  def parse(sheet, speed=0.25):
      """Parse a sheet of notes to a sequence of (duration, frequency) commands.
  
      speed is a duration of a "tick" in seconds.
      Each *symbol* in the sheet takes up one tick.
  
      Valid notes are C, D, E, F, G, A, B and - (which means silence).
      Sheet can also contain semitones:  C#, Eb, etc.
      You can change active octave with numbers 0-8.
  
      Here are some examples:
  
      To play Do for four ticks and then Re for eight ticks, pass:
      'C   D       '
  
      To play Do for two ticks, then silence for two ticks, then Do again:
      'C - C '
  
      To play Do Re Mi, but octave higher:
      'C5D E '
  
      parse('C3E G E ', speed=0.25)
      will play C major chord over 2 seconds
  
      parse('C3  Eb  G   Eb   ', speed=0.125)
      will play C minor chord over 2 seconds also
      """
      sheet = list(sheet)
      commands = []
  
      next = Command()
      while sheet:
          #pop off a token
          token = sheet.pop(0)
  
          #if next note is reached, append the current note onto the commands
          if (token in 'CDEFGAB-' and next.note):
              entry = next.emit()
              if entry:
                  commands.append(entry)
                  next.reset()
              #set the next note equal to the token
              next.note = token
              
          #special case for the first note
          if (token in 'CDEFGAB-' and not next.note):
              next.note = token
  
          #handle octaves
          elif token.isdigit():
              next.octave = int(token)
              next.duration -= speed
          #handle flats and sharps
          elif ((token == '#' or token == 'b') and next.note):
              next.sym = token
              next.duration -= speed
          #rhythm
          next.duration += speed
      #append the last note into the commands
      entry = next.emit()
      if entry:
          commands.append(entry)
          next.reset()
  
      return commands
  
  def sing(finch, sheet, speed=0.05):
      """Sing a melody.
  
      sheet - a string of notes.  For the format see parse method in notes.py
      speed - speed of a single tick in seconds.
  
      Example: sing('C D E F G A B C5', speed=0.1)
      """
      music = parse(sheet, speed=speed)
      for freq, duration in music:
          if duration:
              finch.buzzer_with_delay(duration, int(freq))
  