-
Notifications
You must be signed in to change notification settings - Fork 71
Added square wave functionality to play tone. #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
2759f47
1fa20bb
36bbb56
dd3b5a3
b508e65
eb52cea
74b8dd3
3a4b2b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,6 @@ | ||||||||||||
# SPDX-FileCopyrightText: 2016 Scott Shawcroft for Adafruit Industries | ||||||||||||
# SPDX-FileCopyrightText: 2017-2019 Kattni Rembor for Adafruit Industries | ||||||||||||
# SPDX-FileCopyrightText: 2022 Ryan Keith for Adafruit Industries | ||||||||||||
# | ||||||||||||
# SPDX-License-Identifier: MIT | ||||||||||||
|
||||||||||||
|
@@ -15,7 +16,7 @@ | |||||||||||
* `Circuit Playground Express <https://www.adafruit.com/product/3333>`_ | ||||||||||||
* `Circuit Playground Bluefruit <https://www.adafruit.com/product/4333>`_. | ||||||||||||
|
||||||||||||
* Author(s): Kattni Rembor, Scott Shawcroft | ||||||||||||
* Author(s): Kattni Rembor, Scott Shawcroft, Ryan Keith | ||||||||||||
""" | ||||||||||||
|
||||||||||||
import math | ||||||||||||
|
@@ -102,8 +103,8 @@ def __init__(self): | |||||||||||
self._speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) | ||||||||||||
self._speaker_enable.switch_to_output(value=False) | ||||||||||||
self._sample = None | ||||||||||||
self._sine_wave = None | ||||||||||||
self._sine_wave_sample = None | ||||||||||||
self._wave = None | ||||||||||||
self._wave_sample = None | ||||||||||||
|
||||||||||||
# Initialise tap: | ||||||||||||
self._detect_taps = 1 | ||||||||||||
|
@@ -689,23 +690,38 @@ def red_led(self, value): | |||||||||||
@staticmethod | ||||||||||||
def _sine_sample(length): | ||||||||||||
tone_volume = (2**15) - 1 | ||||||||||||
# Amplitude shift up in order to not have negative numbers | ||||||||||||
shift = 2**15 | ||||||||||||
for i in range(length): | ||||||||||||
yield int(tone_volume * math.sin(2 * math.pi * (i / length)) + shift) | ||||||||||||
|
||||||||||||
def _generate_sample(self, length=100): | ||||||||||||
@staticmethod | ||||||||||||
def _square_sample(length): | ||||||||||||
# Square waves are MUCH louder than then sine | ||||||||||||
tone_volume = (2**16) - 1 | ||||||||||||
half_length = length // 2 | ||||||||||||
for _ in range(half_length): | ||||||||||||
yield tone_volume | ||||||||||||
for _ in range(half_length): | ||||||||||||
yield 0 | ||||||||||||
|
||||||||||||
def _generate_sample(self, length=100, waveform="sine"): | ||||||||||||
if self._sample is not None: | ||||||||||||
return | ||||||||||||
self._sine_wave = array.array("H", self._sine_sample(length)) | ||||||||||||
if waveform == "square": | ||||||||||||
self._wave = array.array("H", self._square_sample(length)) | ||||||||||||
else: | ||||||||||||
self._wave = array.array("H", self._sine_sample(length)) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may shrink things enough to fit
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There may be similar refactorings in the Python code that would save a few bytes here and there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't sure if ternary operator worked in Circuit Python. I wrote it this way if anyone else wanted to add different shaped waves down the line. If it works, great but to me the sound difference isn't that big. Square definitely sounds more ragged than sine but that could be that the code is driving it rather hard. |
||||||||||||
self._sample = self._audio_out(board.SPEAKER) # pylint: disable=not-callable | ||||||||||||
self._sine_wave_sample = audiocore.RawSample(self._sine_wave) | ||||||||||||
self._wave_sample = audiocore.RawSample(self._wave) | ||||||||||||
|
||||||||||||
def play_tone(self, frequency, duration): | ||||||||||||
def play_tone(self, frequency, duration, waveform="sine"): | ||||||||||||
"""Produce a tone using the speaker. Try changing frequency to change | ||||||||||||
the pitch of the tone. | ||||||||||||
|
||||||||||||
:param int frequency: The frequency of the tone in Hz | ||||||||||||
:param float duration: The duration of the tone in seconds | ||||||||||||
:param str waveform: Type of waveform to be generated [sine, square]. Default = sine. | ||||||||||||
|
||||||||||||
.. image :: ../docs/_static/speaker.jpg | ||||||||||||
:alt: Onboard speaker | ||||||||||||
|
@@ -719,15 +735,16 @@ def play_tone(self, frequency, duration): | |||||||||||
cp.play_tone(440, 1) | ||||||||||||
""" | ||||||||||||
# Play a tone of the specified frequency (hz). | ||||||||||||
self.start_tone(frequency) | ||||||||||||
self.start_tone(frequency, waveform) | ||||||||||||
time.sleep(duration) | ||||||||||||
self.stop_tone() | ||||||||||||
|
||||||||||||
def start_tone(self, frequency): | ||||||||||||
def start_tone(self, frequency, waveform="sine"): | ||||||||||||
"""Produce a tone using the speaker. Try changing frequency to change | ||||||||||||
the pitch of the tone. | ||||||||||||
|
||||||||||||
:param int frequency: The frequency of the tone in Hz | ||||||||||||
:param str waveform: Type of waveform to be generated [sine, square]. Default = sine. | ||||||||||||
|
||||||||||||
.. image :: ../docs/_static/speaker.jpg | ||||||||||||
:alt: Onboard speaker | ||||||||||||
|
@@ -750,11 +767,11 @@ def start_tone(self, frequency): | |||||||||||
length = 100 | ||||||||||||
if length * frequency > 350000: | ||||||||||||
length = 350000 // frequency | ||||||||||||
self._generate_sample(length) | ||||||||||||
self._generate_sample(length, waveform) | ||||||||||||
# Start playing a tone of the specified frequency (hz). | ||||||||||||
self._sine_wave_sample.sample_rate = int(len(self._sine_wave) * frequency) | ||||||||||||
self._wave_sample.sample_rate = int(len(self._wave) * frequency) | ||||||||||||
if not self._sample.playing: | ||||||||||||
self._sample.play(self._sine_wave_sample, loop=True) | ||||||||||||
self._sample.play(self._wave_sample, loop=True) | ||||||||||||
|
||||||||||||
def stop_tone(self): | ||||||||||||
"""Use with start_tone to stop the tone produced. | ||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
waveform could take constant values instead of a string.
I don't know if strictly better one way or the other but I tend to try to use constant number values with descriptively named variables instead of strings when I write code like this.
I'm not sure my tendencies are worth holding this up over though so we can see if anyone else has thoughts on it.
i.e usage would look something like:
and the value can then be a constant int rather than a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great idea as well. Is there an enum type in circuit python?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have enum, but we do have a const that is inherited from micropython: https://docs.circuitpython.org/en/latest/docs/library/micropython.html?#micropython.const
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since the variable names may take some space as well it may be worthwhile to go with a more shorthand version like
SINE_WAVE
instead ofSINE_WAVEFORM
as well. Every bit counts to an extent here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good idea. I will look over variable names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SINE_WAVEFORM
, etc. are going to be the equivalent of strings, so you will not gain space, unfortunately, I think. The names need to be available at run time.