Source code for adafruit_rtttl

# The MIT License (MIT)
#
# Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_rtttl`
====================================================

Play notes to a digialio pin using ring tone text transfer language (rtttl).

* Author(s): Scott Shawcroft
"""

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RTTTL"

import time
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.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.66,
         "6b" : 1975.53,
         "7c" : 2093,
         "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
    frequency in the song.
    """
    min_freq = 13000

    for note in tune.split(","):
        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


#pylint: disable-msg=too-many-arguments
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, note_duration = _parse_note(note, duration, octave)
        if piano_note in PIANO:
            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)

        time.sleep(4 / note_duration * 60 / tempo)
        if pwm:
            base_tone.duty_cycle = 0
        else:
            base_tone.stop()
        time.sleep(0.02)

#pylint: disable-msg=too-many-arguments
[docs]def play(pin, rtttl, octave=None, duration=None, tempo=None): """Play notes to a digialio pin using ring tone text transfer language (rtttl). :param ~digitalio.DigitalInOut pin: the speaker pin :param rtttl: string containing rtttl :param int octave: represents octave number (default 6 starts at middle c) :param int duration: length of notes (default 4 quarter note) :param int tempo: how fast (default 63 beats per minute) """ _, defaults, tune = rtttl.lower().split(":") for default in defaults.split(","): if default[0] == "d" and not duration: duration = int(default[2:]) elif default[0] == "o" and not octave: octave = default[2:] elif default[0] == "b" and not tempo: tempo = int(default[2:]) if not octave: octave = 6 if not duration: duration = 4 if not tempo: tempo = 63 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 # 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) base_tone.deinit()