Skip to content

Commit f35068b

Browse files
committed
audio volume and interface APIs
1 parent a0327ac commit f35068b

File tree

1 file changed

+70
-5
lines changed

1 file changed

+70
-5
lines changed

adafruit_fruitjam/peripherals.py

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import displayio
3939
import framebufferio
4040
import picodvi
41+
import simpleio
4142
import storage
4243
import supervisor
4344
from digitalio import DigitalInOut, Direction, Pull
@@ -133,13 +134,16 @@ def get_display_config():
133134
class Peripherals:
134135
"""Peripherals Helper Class for the FruitJam Library
135136
137+
:param audio_output: The audio output interface to use 'speaker' or 'headphone'
138+
:param safe_volume_limit: The maximum volume allowed for the audio output. Default is 15
139+
Using higher values can damage some speakers, change at your own risk.
136140
137141
Attributes:
138142
neopixels (NeoPxiels): The NeoPixels on the Fruit Jam board.
139143
See https://circuitpython.readthedocs.io/projects/neopixel/en/latest/api.html
140144
"""
141145

142-
def __init__(self):
146+
def __init__(self, audio_output="headphone", safe_volume_limit=15):
143147
self.neopixels = NeoPixel(board.NEOPIXEL, 5)
144148

145149
self._buttons = []
@@ -155,11 +159,13 @@ def __init__(self):
155159
# set sample rate & bit depth
156160
self._dac.configure_clocks(sample_rate=11030, bit_depth=16)
157161

158-
# use headphones
159-
self._dac.headphone_output = True
160-
self._dac.headphone_volume = -15 # dB
161-
162+
self.audio_output = audio_output
162163
self._audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
164+
if safe_volume_limit < 1 or safe_volume_limit > 20:
165+
raise ValueError("safe_volume_limit must be between 1 and 20")
166+
self.safe_volume_limit = safe_volume_limit
167+
self._volume = 13
168+
self._apply_volume()
163169

164170
self._sd_mounted = False
165171
sd_pins_in_use = False
@@ -252,3 +258,62 @@ def stop_play(self):
252258
self.audio.stop()
253259
if self.wavfile is not None:
254260
self.wavfile.close()
261+
262+
@property
263+
def volume(self) -> int:
264+
"""
265+
The volume level of the Fruit Jam audio output. Valid values are 1-20.
266+
"""
267+
return self._volume
268+
269+
@volume.setter
270+
def volume(self, volume_level: int) -> None:
271+
"""
272+
:param volume_level: new volume level 1-20
273+
:return: None
274+
"""
275+
if volume_level < 1 or volume_level > 20:
276+
raise ValueError("Volume level must be between 1 and 20")
277+
278+
if volume_level > self.safe_volume_limit:
279+
raise ValueError(
280+
f"Volume level must be less than or equal to "
281+
+ f"safe_volume_limit: {self.safe_volume_limit}. "
282+
+ f"Using higher values could damage speakers. "
283+
+ f"To override this limitation pass a value larger than 15 "
284+
+ f"for the safe_volume_limit argument of the constructor."
285+
)
286+
287+
self._volume = volume_level
288+
self._apply_volume()
289+
290+
@property
291+
def audio_output(self) -> str:
292+
"""
293+
The audio output interface. 'speaker' or 'headphone'
294+
:return:
295+
"""
296+
return self._audio_output
297+
298+
@audio_output.setter
299+
def audio_output(self, audio_output: str) -> None:
300+
"""
301+
302+
:param audio_output: The audio interface to use 'speaker' or 'headphone'.
303+
:return: None
304+
"""
305+
if audio_output == "headphone":
306+
self._dac.headphone_output = True
307+
self._dac.speaker_output = False
308+
elif audio_output == "speaker":
309+
self._dac.headphone_output = False
310+
self._dac.speaker_output = True
311+
else:
312+
raise ValueError("audio_output must be either 'headphone' or 'speaker'")
313+
314+
def _apply_volume(self) -> None:
315+
"""
316+
Map the basic volume level to a db value and set it on the DAC.
317+
"""
318+
db_val = simpleio.map_range(self._volume, 1, 20, -63, 23)
319+
self._dac.dac_volume = db_val

0 commit comments

Comments
 (0)