summaryrefslogtreecommitdiffstats
path: root/rtttl.py
blob: dfc3f863955dc125e1115cf746ee7f698a867ef4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/python
"""
play a rtttl file by sending the tone lengths to the arduino

protocol information taken from http://merwin.bespin.org/t4a/specs/nokia_rtttl.txt
"""
from __future__ import division
import re
import serial
import struct
import sys
import time

BAUDRATE = 9600
PAUSE = .001

_rtttl_re = re.compile('^\s*([0-9]+)?([a-hp]#?)(\.?)([0-9]?)\s*$')

freqs = {
    'c':  261.626,
    'c#': 277.183,
    'd':  293.665,
    'd#': 311.127,
    'e':  329.628,
    'f':  349.228,
    'f#': 369.994,
    'g':  391.995,
    'g#': 415.305,
    'a':  440.000,
    'a#': 466.164,
    'b':  493.883,
    'h':  493.883,
    'p':  0,
}

def parse_defaults(s):
    defaults = {
        'd': 4,  # duration
        'o': 6,  # octave
        'b': 63, # beats per minute
    }

    if s == '':
        return defaults

    for option in s.split(','):
        k,v = option.split('=')
        defaults[k] = v

    return defaults

def parse(s):
    name, defs, music = s.split(':')
    defaults = parse_defaults(defs)
    defaultlength = int(defaults['d'])
    defaultoctave = int(defaults['o'])
    timefactor = 60/int(defaults['b'])

    # for m in _rtttl_re.finditer(music):
    for tone in music.split(","):

        m = _rtttl_re.match(tone)

        if not m:
            print("Didn't match:", tone)
            continue

        l, t, ld, o = m.groups()

        if l:
            length = 1/int(l)
        else:
            length = 1/defaultlength
        if ld:
            length = length * 1.5
        length = length*timefactor

        tone = freqs[t.lower()]
        if o:
            octave = o
        else:
            octave = defaultoctave
        tone = tone * 2**(int(octave) - 4)


        yield (length, tone)

def send(sounds, device):
    ser = serial.Serial(device, BAUDRATE)
    try:
        for length, freq in sounds:
            sys.stderr.write('len: %.3f, ton: %9.3f, send: %d' % (length, freq, freq2t(freq)))
            ser.write(struct.pack('>H', freq2t(freq)))
            time.sleep(length)
            sys.stderr.write('\n')
            ser.write('\0\0')
            time.sleep(PAUSE)
    finally:
        ser.write('\0\0')
        ser.close()


def freq2t(freq):
    if freq == 0:
        return 0
    return 2000000/freq

if __name__ == '__main__':
    if len(sys.argv) > 1:
        if sys.argv[1] == '-d':
            for length, freq in parse(sys.stdin.read()):
                print 'len: %.3f, ton: %9.3f' % (length, freq)
        else:
            send(parse(sys.stdin.read()), sys.argv[1])
    else:
        print >>sys.stderr, "Usage: %s -d|/dev/ttyUSBx" % sys.argv[0]