#!/usr/bin/python

# tzmidi.py
#       --copyright--                   Copyright 2007 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       August 30, 2006         bar
#       November 18, 2007       bar     turn on doxygen
#       November 20, 2007       bar     comments
#       November 27, 2007       bar     insert boilerplate copyright
#       May 17, 2008            bar     email adr
#       July 19, 2011           bar     name_to_midi_note
#       May 27, 2012            bar     doxygen namespace
#       --eodstamps--
##      \file
#       \namespace              tzpython.tzmidi
#
#
#       Various MIDI things.
#
#

import  math
import  re



##                  ASCII note names
note_names          = "A A#B C C#D D#E F F#G G#"


##                  How many MIDI notes there are in an octave.
notes_per_octave    = 12

##                  The log of frequency each note is from the note below it.
note_step_log       = (math.log(440.0) - math.log(220.0)) / notes_per_octave


##                  Array we well put all MIDI note frequencies in.
midi_note_freqs     = []


##                  The frequency of each note we calculate.
nfreq               = math.log(55.0 / 8.0) + note_step_log + note_step_log + note_step_log
for n in xrange(0, 128) :
    midi_note_freqs.append(math.exp(nfreq))
    nfreq          += note_step_log



def midi_note_freq(midi_note) :
    """ Return the MIDI note's frequency. """

    return(midi_note_freqs[midi_note])


def midi_note_name(midi_note) :
    """ Return the MIDI note's ASCII name. """

    i   = ((midi_note + 3) % notes_per_octave) * 2
    return(note_names[i : i + 2])


def name_to_midi_note(name) :
    """
        Return the MIDI note for the given name.
        Name is in form: [a-g][b#]?\d*
        Assume octave 6 if not given.
        Return outside -1 if not a valid note name.
    """

    g       = re.match(r"([a-g])\s*([b#]|flat|sharp)?\s*(\d*)$", name.lower())
    if not g :
        return(-1)

    n   = [ 0, 2, 4, 5, 7, 9, 11, ]["cdefgab".find(g.group(1).lower())]
    fs  = g.group(2)
    fs  = (fs and (fs[0] in 'bf') and -1) or (fs and (fs[0] in 's#') and 1) or 0
    oc  = (g.group(3) and (int(g.group(3)) + 1)) or 7
    oc -= 2
    n  += (notes_per_octave * oc) + fs
    # print "@@@@", g.group(1), g.group(2), g.group(3), n, midi_note_name(n)
    if  not (0 <= n < 128) :
        return(-1)
    return(n)


#
#
#
if __name__ == '__main__' :

    for i in xrange(len(midi_note_freqs)) :
      print "%3u %8.2f %s" % ( i, midi_note_freq(i), midi_note_name(i) )

    pass




##      Public things.
__ALL__ = [
            'midi_note_freq',
            'midi_note_name',

            'midi_note_freqs',
            'notes_per_octave',
            'note_step_log',
          ]

#
#
#
# eof
