diff --git a/adafruit_rtttl.py b/adafruit_rtttl.py index f98561c..43badd6 100644 --- a/adafruit_rtttl.py +++ b/adafruit_rtttl.py @@ -32,37 +32,76 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RTTTL" import time -from adafruit_waveform import sine -import audioio - -PIANO = {"4a#": 466.16, - "4b" : 493.88, - "5c" : 523.25, - "5c#": 554.37, - "5d" : 587.33, - "5d#": 622.25, - "5e" : 659.26, - "5f" : 698.46, - "5f#": 739.99, - "5g" : 783.99, - "5g#": 830.61, +import pulseio + +AUDIOIO_AVAILABLE = False +try: + import audioio + from adafruit_waveform import sine + AUDIOIO_AVAILABLE = True +except ImportError: + pass + +PIANO = {"4c" : 261.626, + "4c#": 277.183, + "4d" : 293.665, + "4d#": 311.127, + "4e" : 329.628, + "4f" : 349.228, + "4f#": 369.994, + "4g" : 391.995, + "4g#": 415.305, + "4a" : 440, + "4a#": 466.164, + "4b" : 493.883, + "5c" : 523.251, + "5c#": 554.365, + "5d" : 587.330, + "5d#": 622.254, + "5e" : 659.255, + "5f" : 698.456, + "5f#": 739.989, + "5g" : 783.991, + "5g#": 830.609, "5a" : 880, - "5a#": 932.33, - "5b" : 987.77, - "6c" : 1046.5, - "6c#": 1108.7, - "6d" : 1174.7, - "6d#": 1244.5, - "6e" : 1318.5, - "6f" : 1396.9, - "6f#": 1480, - "6g" : 1568, - "6g#": 1661.2, + "5a#": 932.328, + "5b" : 987.767, + "6c" : 1046.50, + "6c#": 1108.73, + "6d" : 1174.66, + "6d#": 1244.51, + "6e" : 1318.51, + "6f" : 1396.91, + "6f#": 1479.98, + "6g" : 1567.98, + "6g#": 1661.22, "6a" : 1760, - "6a#": 1864.7, - "6b" : 1975.5, + "6a#": 1864.66, + "6b" : 1975.53, "7c" : 2093, - "7c#": 2217.5} + "7c#": 2217.46} + +def _parse_note(note, duration=2, octave="6"): + note = note.strip() + piano_note = None + note_duration = duration + if note[0].isdigit() and note[1].isdigit(): + note_duration = int(note[:2]) + piano_note = note[2] + elif note[0].isdigit(): + note_duration = int(note[0]) + piano_note = note[1] + else: + piano_note = note[0] + if "." in note: + note_duration *= 1.5 + if "#" in note: + piano_note += "#" + note_octave = octave + if note[-1].isdigit(): + note_octave = note[-1] + piano_note = note_octave + piano_note + return piano_note, note_duration def _get_wave(tune, octave): """Returns the proper waveform to play the song along with the minimum @@ -71,17 +110,7 @@ def _get_wave(tune, octave): min_freq = 13000 for note in tune.split(","): - piano_note = None - if note[0].isdigit(): - piano_note = note[1] - else: - piano_note = note[0] - if "#" in note: - piano_note += "#" - note_octave = octave - if note[-1].isdigit(): - note_octave = note[-1] - piano_note = note_octave + piano_note + piano_note, _ = _parse_note(note, octave=octave) if piano_note[-1] != "p" and PIANO[piano_note] < min_freq: min_freq = PIANO[piano_note] return sine.sine_wave(16000, min_freq), min_freq @@ -91,28 +120,22 @@ def _get_wave(tune, octave): def _play_to_pin(tune, base_tone, min_freq, duration, octave, tempo): """Using the prepared input send the notes to the pin """ + pwm = isinstance(base_tone, pulseio.PWMOut) for note in tune.split(","): - piano_note = None - note_duration = duration - if note[0].isdigit(): - note_duration = int(note[0]) - piano_note = note[1] - else: - piano_note = note[0] - if "." in note: - note_duration *= 1.5 - if "#" in note: - piano_note += "#" - note_octave = octave - if note[-1].isdigit(): - note_octave = note[-1] - piano_note = note_octave + piano_note + piano_note, note_duration = _parse_note(note, duration, octave) if piano_note in PIANO: - base_tone.frequency = int(16000 * (PIANO[piano_note] / min_freq)) - base_tone.play(loop=True) + if pwm: + base_tone.frequency = int(PIANO[piano_note]) + base_tone.duty_cycle = 2 ** 15 + else: + base_tone.frequency = int(16000 * (PIANO[piano_note] / min_freq)) + base_tone.play(loop=True) print(piano_note, note_duration) time.sleep(4 / note_duration * 60 / tempo) - base_tone.stop() + if pwm: + base_tone.duty_cycle = 0 + else: + base_tone.stop() time.sleep(0.02) #pylint: disable-msg=too-many-arguments @@ -124,7 +147,7 @@ def play(pin, rtttl, octave=None, duration=None, tempo=None): :param int duration: length of notes (default 4 quarter note) :param int tempo: how fast (default 63 beats per minute) """ - _, defaults, tune = rtttl.split(":") + _, defaults, tune = rtttl.lower().split(":") for default in defaults.split(","): if default[0] == "d" and not duration: duration = int(default[2:]) @@ -141,8 +164,18 @@ def play(pin, rtttl, octave=None, duration=None, tempo=None): print("tempo", tempo, "octave", octave, "duration", duration) - wave, min_freq = _get_wave(tune, octave) + base_tone = None + min_freq = 440 + if AUDIOIO_AVAILABLE: + wave, min_freq = _get_wave(tune, octave) + try: + base_tone = audioio.AudioOut(pin, wave) + except ValueError: + # No DAC on the pin so use PWM. + pass - base_tone = audioio.AudioOut(pin, wave) + # Fall back to PWM + if not base_tone: + base_tone = pulseio.PWMOut(pin, duty_cycle=0, variable_frequency=True) _play_to_pin(tune, base_tone, min_freq, duration, octave, tempo) diff --git a/docs/conf.py b/docs/conf.py index 00f5e77..ff33645 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,7 +15,7 @@ 'sphinx.ext.viewcode', ] -autodoc_mock_imports = ["audioio", "adafruit_waveform"] +autodoc_mock_imports = ["audioio", "adafruit_waveform", "pulseio"] intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)}