Skip to content

Commit 89b09db

Browse files
authored
Merge pull request #5 from tannewt/pwm_support
Support PWM output.
2 parents 1b12b6e + d23c007 commit 89b09db

File tree

2 files changed

+94
-61
lines changed

2 files changed

+94
-61
lines changed

adafruit_rtttl.py

Lines changed: 93 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -32,37 +32,76 @@
3232
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RTTTL"
3333

3434
import time
35-
from adafruit_waveform import sine
36-
import audioio
37-
38-
PIANO = {"4a#": 466.16,
39-
"4b" : 493.88,
40-
"5c" : 523.25,
41-
"5c#": 554.37,
42-
"5d" : 587.33,
43-
"5d#": 622.25,
44-
"5e" : 659.26,
45-
"5f" : 698.46,
46-
"5f#": 739.99,
47-
"5g" : 783.99,
48-
"5g#": 830.61,
35+
import pulseio
36+
37+
AUDIOIO_AVAILABLE = False
38+
try:
39+
import audioio
40+
from adafruit_waveform import sine
41+
AUDIOIO_AVAILABLE = True
42+
except ImportError:
43+
pass
44+
45+
PIANO = {"4c" : 261.626,
46+
"4c#": 277.183,
47+
"4d" : 293.665,
48+
"4d#": 311.127,
49+
"4e" : 329.628,
50+
"4f" : 349.228,
51+
"4f#": 369.994,
52+
"4g" : 391.995,
53+
"4g#": 415.305,
54+
"4a" : 440,
55+
"4a#": 466.164,
56+
"4b" : 493.883,
57+
"5c" : 523.251,
58+
"5c#": 554.365,
59+
"5d" : 587.330,
60+
"5d#": 622.254,
61+
"5e" : 659.255,
62+
"5f" : 698.456,
63+
"5f#": 739.989,
64+
"5g" : 783.991,
65+
"5g#": 830.609,
4966
"5a" : 880,
50-
"5a#": 932.33,
51-
"5b" : 987.77,
52-
"6c" : 1046.5,
53-
"6c#": 1108.7,
54-
"6d" : 1174.7,
55-
"6d#": 1244.5,
56-
"6e" : 1318.5,
57-
"6f" : 1396.9,
58-
"6f#": 1480,
59-
"6g" : 1568,
60-
"6g#": 1661.2,
67+
"5a#": 932.328,
68+
"5b" : 987.767,
69+
"6c" : 1046.50,
70+
"6c#": 1108.73,
71+
"6d" : 1174.66,
72+
"6d#": 1244.51,
73+
"6e" : 1318.51,
74+
"6f" : 1396.91,
75+
"6f#": 1479.98,
76+
"6g" : 1567.98,
77+
"6g#": 1661.22,
6178
"6a" : 1760,
62-
"6a#": 1864.7,
63-
"6b" : 1975.5,
79+
"6a#": 1864.66,
80+
"6b" : 1975.53,
6481
"7c" : 2093,
65-
"7c#": 2217.5}
82+
"7c#": 2217.46}
83+
84+
def _parse_note(note, duration=2, octave="6"):
85+
note = note.strip()
86+
piano_note = None
87+
note_duration = duration
88+
if note[0].isdigit() and note[1].isdigit():
89+
note_duration = int(note[:2])
90+
piano_note = note[2]
91+
elif note[0].isdigit():
92+
note_duration = int(note[0])
93+
piano_note = note[1]
94+
else:
95+
piano_note = note[0]
96+
if "." in note:
97+
note_duration *= 1.5
98+
if "#" in note:
99+
piano_note += "#"
100+
note_octave = octave
101+
if note[-1].isdigit():
102+
note_octave = note[-1]
103+
piano_note = note_octave + piano_note
104+
return piano_note, note_duration
66105

67106
def _get_wave(tune, octave):
68107
"""Returns the proper waveform to play the song along with the minimum
@@ -71,17 +110,7 @@ def _get_wave(tune, octave):
71110
min_freq = 13000
72111

73112
for note in tune.split(","):
74-
piano_note = None
75-
if note[0].isdigit():
76-
piano_note = note[1]
77-
else:
78-
piano_note = note[0]
79-
if "#" in note:
80-
piano_note += "#"
81-
note_octave = octave
82-
if note[-1].isdigit():
83-
note_octave = note[-1]
84-
piano_note = note_octave + piano_note
113+
piano_note, _ = _parse_note(note, octave=octave)
85114
if piano_note[-1] != "p" and PIANO[piano_note] < min_freq:
86115
min_freq = PIANO[piano_note]
87116
return sine.sine_wave(16000, min_freq), min_freq
@@ -91,28 +120,22 @@ def _get_wave(tune, octave):
91120
def _play_to_pin(tune, base_tone, min_freq, duration, octave, tempo):
92121
"""Using the prepared input send the notes to the pin
93122
"""
123+
pwm = isinstance(base_tone, pulseio.PWMOut)
94124
for note in tune.split(","):
95-
piano_note = None
96-
note_duration = duration
97-
if note[0].isdigit():
98-
note_duration = int(note[0])
99-
piano_note = note[1]
100-
else:
101-
piano_note = note[0]
102-
if "." in note:
103-
note_duration *= 1.5
104-
if "#" in note:
105-
piano_note += "#"
106-
note_octave = octave
107-
if note[-1].isdigit():
108-
note_octave = note[-1]
109-
piano_note = note_octave + piano_note
125+
piano_note, note_duration = _parse_note(note, duration, octave)
110126
if piano_note in PIANO:
111-
base_tone.frequency = int(16000 * (PIANO[piano_note] / min_freq))
112-
base_tone.play(loop=True)
127+
if pwm:
128+
base_tone.frequency = int(PIANO[piano_note])
129+
base_tone.duty_cycle = 2 ** 15
130+
else:
131+
base_tone.frequency = int(16000 * (PIANO[piano_note] / min_freq))
132+
base_tone.play(loop=True)
113133
print(piano_note, note_duration)
114134
time.sleep(4 / note_duration * 60 / tempo)
115-
base_tone.stop()
135+
if pwm:
136+
base_tone.duty_cycle = 0
137+
else:
138+
base_tone.stop()
116139
time.sleep(0.02)
117140

118141
#pylint: disable-msg=too-many-arguments
@@ -124,7 +147,7 @@ def play(pin, rtttl, octave=None, duration=None, tempo=None):
124147
:param int duration: length of notes (default 4 quarter note)
125148
:param int tempo: how fast (default 63 beats per minute)
126149
"""
127-
_, defaults, tune = rtttl.split(":")
150+
_, defaults, tune = rtttl.lower().split(":")
128151
for default in defaults.split(","):
129152
if default[0] == "d" and not duration:
130153
duration = int(default[2:])
@@ -141,8 +164,18 @@ def play(pin, rtttl, octave=None, duration=None, tempo=None):
141164

142165
print("tempo", tempo, "octave", octave, "duration", duration)
143166

144-
wave, min_freq = _get_wave(tune, octave)
167+
base_tone = None
168+
min_freq = 440
169+
if AUDIOIO_AVAILABLE:
170+
wave, min_freq = _get_wave(tune, octave)
171+
try:
172+
base_tone = audioio.AudioOut(pin, wave)
173+
except ValueError:
174+
# No DAC on the pin so use PWM.
175+
pass
145176

146-
base_tone = audioio.AudioOut(pin, wave)
177+
# Fall back to PWM
178+
if not base_tone:
179+
base_tone = pulseio.PWMOut(pin, duty_cycle=0, variable_frequency=True)
147180

148181
_play_to_pin(tune, base_tone, min_freq, duration, octave, tempo)

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
'sphinx.ext.viewcode',
1616
]
1717

18-
autodoc_mock_imports = ["audioio", "adafruit_waveform"]
18+
autodoc_mock_imports = ["audioio", "adafruit_waveform", "pulseio"]
1919

2020
intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)}
2121

0 commit comments

Comments
 (0)