# Author: qiren123
# This file is part of MicroPython MIDI Music
# Copyright (c) 2018 qiren123
#
# Licensed under the MIT license:
#   http://www.opensource.org/licenses/mit-license.php
#

DADADADUM = ['r4:2', 'g', 'g', 'g', 'eb:8', 'r:2', 'f', 'f', 'f', 'd:8']

ENTERTAINER = [
    'd4:1', 'd#', 'e', 'c5:2', 'e4:1', 'c5:2', 'e4:1', 'c5:3', 'c:1', 'd',
    'd#', 'e', 'c', 'd', 'e:2', 'b4:1', 'd5:2', 'c:4'
]

PRELUDE = [
    'c4:1', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e', 'c4', 'e', 'g', 'c5', 'e',
    'g4', 'c5', 'e', 'c4', 'd', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'c4', 'd',
    'g', 'd5', 'f', 'g4', 'd5', 'f', 'b3', 'd4', 'g', 'd5', 'f', 'g4', 'd5',
    'f', 'b3', 'd4', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'c4', 'e', 'g', 'c5',
    'e', 'g4', 'c5', 'e', 'c4', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e'
]

ODE = [
    'e4', 'e', 'f', 'g', 'g', 'f', 'e', 'd', 'c', 'c', 'd', 'e', 'e:6', 'd:2',
    'd:8', 'e:4', 'e', 'f', 'g', 'g', 'f', 'e', 'd', 'c', 'c', 'd', 'e', 'd:6',
    'c:2', 'c:8'
]

NYAN = [
    'f#5:1', 'g#', 'c#:1', 'd#:2', 'b4:1', 'd5:1', 'c#', 'b4:2', 'b', 'c#5',
    'd', 'd:1', 'c#', 'b4:1', 'c#5:1', 'd#', 'f#', 'g#', 'd#', 'f#', 'c#', 'd',
    'b4', 'c#5', 'b4', 'd#5:2', 'f#', 'g#:1', 'd#', 'f#', 'c#', 'd#', 'b4',
    'd5', 'd#', 'd', 'c#', 'b4', 'c#5', 'd:2', 'b4:1', 'c#5', 'd#', 'f#', 'c#',
    'd', 'c#', 'b4', 'c#5:2', 'b4', 'c#5', 'b4', 'f#:1', 'g#', 'b:2', 'f#:1',
    'g#', 'b', 'c#5', 'd#', 'b4', 'e5', 'd#', 'e', 'f#', 'b4:2', 'b', 'f#:1',
    'g#', 'b', 'f#', 'e5', 'd#', 'c#', 'b4', 'f#', 'd#', 'e', 'f#', 'b:2',
    'f#:1', 'g#', 'b:2', 'f#:1', 'g#', 'b', 'b', 'c#5', 'd#', 'b4', 'f#', 'g#',
    'f#', 'b:2', 'b:1', 'a#', 'b', 'f#', 'g#', 'b', 'e5', 'd#', 'e', 'f#',
    'b4:2', 'c#5'
]

RINGTONE = [
    'c4:1', 'd', 'e:2', 'g', 'd:1', 'e', 'f:2', 'a', 'e:1', 'f', 'g:2', 'b',
    'c5:4'
]

FUNK = [
    'c2:2', 'c', 'd#', 'c:1', 'f:2', 'c:1', 'f:2', 'f#', 'g', 'c', 'c', 'g',
    'c:1', 'f#:2', 'c:1', 'f#:2', 'f', 'd#'
]

BLUES = [
    'c2:2', 'e', 'g', 'a', 'a#', 'a', 'g', 'e', 'c2:2', 'e', 'g', 'a', 'a#',
    'a', 'g', 'e', 'f', 'a', 'c3', 'd', 'd#', 'd', 'c', 'a2', 'c2:2', 'e', 'g',
    'a', 'a#', 'a', 'g', 'e', 'g', 'b', 'd3', 'f', 'f2', 'a', 'c3', 'd#',
    'c2:2', 'e', 'g', 'e', 'g', 'f', 'e', 'd'
]

BIRTHDAY = [
    'c4:4', 'c:1', 'd:4', 'c:4', 'f', 'e:8', 'c:3', 'c:1', 'd:4', 'c:4', 'g',
    'f:8', 'c:3', 'c:1', 'c5:4', 'a4', 'f', 'e', 'd', 'a#:3', 'a#:1', 'a:4',
    'f', 'g', 'f:8'
]

WEDDING = [
    'c4:4', 'f:3', 'f:1', 'f:8', 'c:4', 'g:3', 'e:1', 'f:8', 'c:4', 'f:3',
    'a:1', 'c5:4', 'a4:3', 'f:1', 'f:4', 'e:3', 'f:1', 'g:8'
]

FUNERAL = [
    'c3:4', 'c:3', 'c:1', 'c:4', 'd#:3', 'd:1', 'd:3', 'c:1', 'c:3', 'b2:1',
    'c3:4'
]

PUNCHLINE = ['c4:3', 'g3:1', 'f#', 'g', 'g#:3', 'g', 'r', 'b', 'c4']

PYTHON = [
    'd5:1', 'b4', 'r', 'b', 'b', 'a#', 'b', 'g5', 'r', 'd', 'd', 'r', 'b4',
    'c5', 'r', 'c', 'c', 'r', 'd', 'e:5', 'c:1', 'a4', 'r', 'a', 'a', 'g#',
    'a', 'f#5', 'r', 'e', 'e', 'r', 'c', 'b4', 'r', 'b', 'b', 'r', 'c5', 'd:5',
    'd:1', 'b4', 'r', 'b', 'b', 'a#', 'b', 'b5', 'r', 'g', 'g', 'r', 'd', 'c#',
    'r', 'a', 'a', 'r', 'a', 'a:5', 'g:1', 'f#:2', 'a:1', 'a', 'g#', 'a',
    'e:2', 'a:1', 'a', 'g#', 'a', 'd', 'r', 'c#', 'd', 'r', 'c#', 'd:2', 'r:3'
]

BADDY = ['c3:3', 'r', 'd:2', 'd#', 'r', 'c', 'r', 'f#:8']

CHASE = [
    'a4:1', 'b', 'c5', 'b4', 'a:2', 'r', 'a:1', 'b', 'c5', 'b4', 'a:2', 'r',
    'a:2', 'e5', 'd#', 'e', 'f', 'e', 'd#', 'e', 'b4:1', 'c5', 'd', 'c',
    'b4:2', 'r', 'b:1', 'c5', 'd', 'c', 'b4:2', 'r', 'b:2', 'e5', 'd#', 'e',
    'f', 'e', 'd#', 'e'
]

BA_DING = ['b5:1', 'e6:3']

WAWAWAWAA = ['e3:3', 'r:1', 'd#:3', 'r:1', 'd:4', 'r:1', 'c#:8']

JUMP_UP = ['c5:1', 'd', 'e', 'f', 'g']

JUMP_DOWN = ['g5:1', 'f', 'e', 'd', 'c']

POWER_UP = ['g4:1', 'c5', 'e4', 'g5:2', 'e5:1', 'g5:3']

POWER_DOWN = ['g5:1', 'd#', 'c', 'g4:2', 'b:1', 'c5:3']

normal_tone = {
    'A1': 55, 'B1': 62, 'C1': 33, 'D1': 37, 'E1': 41, 'F1': 44, 'G1': 49,
    'A2': 110, 'B2': 123, 'C2': 65, 'D2': 73, 'E2': 82, 'F2': 87, 'G2': 98,
    'A3': 220, 'B3': 247, 'C3': 131, 'D3': 147, 'E3': 165, 'F3': 175, 'G3': 196,
    'A4': 440, 'B4': 494, 'C4': 262, 'D4': 294, 'E4': 330, 'F4': 349, 'G4': 392,
    'A5': 880, 'B5': 988, 'C5': 523, 'D5': 587, 'E5': 659, 'F5': 698, 'G5': 784,
    'A6': 1760, 'B6': 1976, 'C6': 1047, 'D6': 1175, 'E6': 1319, 'F6': 1397, 'G6': 1568,
    'A7': 3520, 'B7': 3951, 'C7': 2093, 'D7': 2349, 'E7': 2637, 'F7': 2794, 'G7': 3135,
    'A8': 7040, 'B8': 7902, 'C8': 4186, 'D8': 4699, 'E8': 5274, 'F8': 5588, 'G8': 6271,
    'A9': 14080, 'B9': 15804
}

rising_tone = {
    'A1': 58, 'C1': 35, 'D1': 39, 'F1': 46, 'G1': 52,
    'A2': 117, 'C2': 69, 'D2': 78, 'F2': 93, 'G2': 104,
    'A3': 233, 'C3': 139, 'D3': 156, 'F3': 185, 'G3': 208,
    'A4': 466, 'C4': 277, 'D4': 311, 'F4': 370, 'G4': 415,
    'A5': 932, 'C5': 554, 'D5': 622, 'F5': 740, 'G5': 831,
    'A6': 1865, 'C6': 1109, 'D6': 1245, 'F6': 1480, 'G6': 1661,
    'A7': 3729, 'C7': 2217, 'D7': 2489, 'F7': 2960, 'G7': 3322,
    'A8': 7459, 'C8': 4435, 'D8': 4978, 'F8': 5920, 'G8': 6645,
    'A9': 14917
}

falling_tone = {
    'B1': 58, 'D1': 35, 'E1': 39, 'G1': 46, 'A1': 52,
    'B2': 117, 'D2': 69, 'E2': 78, 'G2': 93, 'A2': 104,
    'B3': 233, 'D3': 139, 'E3': 156, 'G3': 185, 'A3': 208,
    'B4': 466, 'D4': 277, 'E4': 311, 'G4': 370, 'A4': 415,
    'B5': 932, 'D5': 554, 'E5': 622, 'G5': 740, 'A5': 831,
    'B6': 1865, 'D6': 1109, 'E6': 1245, 'G6': 1480, 'A6': 1661,
    'B7': 3729, 'D7': 2217, 'E7': 2489, 'G7': 2960, 'A7': 3322,
    'B8': 7459, 'D8': 4435, 'E8': 4978, 'G8': 5920, 'A8': 6645,
    'B9': 14917
}

Letter = 'ABCDEFG#R'

_ticks = 4
_bpm =120
_beat = 60000 / _bpm / _ticks
_octave = 4
_duration = 4

get_tempo = (_ticks, _bpm)

def set_tempo(ticks=4, bpm=120):
    global _ticks, _bpm, _beat
    _ticks = ticks
    _bpm = bpm
    _beat = 60000 / bpm / ticks

def set_octave(octave=4):
    global _octave
    _octave = octave

def set_duration(duration=4):
    global _duration
    _duration = duration

def reset():
    set_duration()
    set_octave()
    set_tempo()

def __init__():
    reset()

def parse(tone, dict):
    # print(tone)
    time = _beat * _duration
    pos = tone.find(':')
    if pos != -1:
        time = _beat * int(tone[(pos + 1):])
        tone = tone[:pos]
    # print(tone)
    freq, tone_size = 1, len(tone)
    if 'R' in tone:
        freq = 1
    elif tone_size == 1:
        freq = dict[tone[0] + str(_octave)]
    elif tone_size == 2:
        freq = dict[tone]
        set_octave(tone[1:])
    # print(int(freq), int(time))
    return int(freq), int(time)

def midi(tone):
    # print(tone)
    pos = tone.find('#')
    if pos != -1:
        return parse(tone.replace('#', ''), rising_tone)
    pos = tone.find('B')
    if pos != -1 and pos != 0:
        return parse(tone.replace('B', ''), falling_tone)
    return parse(tone, normal_tone)

def set_default(tone):
    pos = tone.find(':')
    if pos != -1:
        set_duration(int(tone[(pos + 1):]))
        tone = tone[:pos]

def play(tune, pin=27, duration=None):
    from machine import Pin, PWM
    from utime import sleep_ms

    try:
        pwm = PWM(Pin(pin))
        if duration is None:
            set_default(tune[0])
        else:
            set_duration(duration)
        for tone in tune:
            tone = tone.upper()  # all to upper
            if tone[0] not in Letter:
                continue
            m = midi(tone)
            pwm.freq(m[0])  # set frequency
            pwm.duty(m[1])  # set duty cycle
            sleep_ms(m[1])
    finally:
        pwm.deinit()

def pitch(freq, pin=27, tim=1000):
    from machine import Pin, PWM
    from utime import sleep_ms

    try:
        pwm = PWM(Pin(pin))
        pwm.freq(freq)  # set frequency
        pwm.duty(tim)  # set duty cycle
        sleep_ms(tim)
    finally:
        pwm.deinit()

def stop(pin=27):
    from machine import Pin, PWM
    try:
        pwm = PWM(Pin(pin))
        pwm.duty(0)
        pwm.freq(1)
    finally:
        pwm.deinit()