Skip to content

Commit 9ab7c66

Browse files
committed
Check if dac is present and add parameters for sample rate and bit depth
1 parent e67239b commit 9ab7c66

File tree

1 file changed

+69
-46
lines changed

1 file changed

+69
-46
lines changed

adafruit_fruitjam/peripherals.py

Lines changed: 69 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"""
2828

2929
import os
30+
import time
3031

3132
import adafruit_sdcard
3233
import adafruit_tlv320
@@ -135,15 +136,23 @@ class Peripherals:
135136
"""Peripherals Helper Class for the FruitJam Library
136137
137138
: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+
:param safe_volume_limit: The maximum volume allowed for the audio output. Default is 12.
139140
Using higher values can damage some speakers, change at your own risk.
141+
:param sample_rate: The sample rate to play back audio data in hertz. Default is 11025.
142+
:param bit_depth: The bits per sample of the audio data. Supports 8 and 16 bits. Default is 16.
140143
141144
Attributes:
142145
neopixels (NeoPxiels): The NeoPixels on the Fruit Jam board.
143146
See https://circuitpython.readthedocs.io/projects/neopixel/en/latest/api.html
144147
"""
145148

146-
def __init__(self, audio_output="headphone", safe_volume_limit=12):
149+
def __init__(
150+
self,
151+
audio_output: str = "headphone",
152+
safe_volume_limit: int = 12,
153+
sample_rate: int = 11025,
154+
bit_depth: int = 16,
155+
):
147156
self.neopixels = NeoPixel(board.NEOPIXEL, 5)
148157

149158
self._buttons = []
@@ -154,19 +163,24 @@ def __init__(self, audio_output="headphone", safe_volume_limit=12):
154163
self._buttons.append(switch)
155164

156165
i2c = board.I2C()
157-
self._dac = adafruit_tlv320.TLV320DAC3100(i2c)
166+
while not i2c.try_lock():
167+
time.sleep(0.01)
168+
self._dac_present = 0x18 in i2c.scan()
158169

159-
# set sample rate & bit depth
160-
self._dac.configure_clocks(sample_rate=11030, bit_depth=16)
170+
if self._dac_present:
171+
self._dac = adafruit_tlv320.TLV320DAC3100(i2c)
172+
173+
# set sample rate & bit depth
174+
self._dac.configure_clocks(sample_rate=sample_rate, bit_depth=bit_depth)
175+
176+
self._audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
161177

162-
self._audio_output = audio_output
163-
self.audio_output = audio_output
164-
self._audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
165178
if safe_volume_limit < 1 or safe_volume_limit > 20:
166179
raise ValueError("safe_volume_limit must be between 1 and 20")
167180
self.safe_volume_limit = safe_volume_limit
168-
self._volume = 7
169-
self._apply_volume()
181+
182+
self.audio_output = audio_output
183+
self._apply_volume(7)
170184

171185
self._sd_mounted = False
172186
sd_pins_in_use = False
@@ -227,14 +241,18 @@ def any_button_pressed(self) -> bool:
227241
return True in [not button.value for (i, button) in enumerate(self._buttons)]
228242

229243
@property
230-
def dac(self):
231-
return self._dac
244+
def dac_present(self) -> bool:
245+
return self._dac_present
246+
247+
@property
248+
def dac(self) -> adafruit_tlv320.TLV320DAC3100 | None:
249+
return self._dac if self._dac_present else None
232250

233251
@property
234-
def audio(self):
235-
return self._audio
252+
def audio(self) -> audiobusio.I2SOut | None:
253+
return self._audio if self._dac_present else None
236254

237-
def sd_check(self):
255+
def sd_check(self) -> bool:
238256
return self._sd_mounted
239257

240258
def play_file(self, file_name, wait_to_finish=True):
@@ -244,34 +262,36 @@ def play_file(self, file_name, wait_to_finish=True):
244262
:param bool wait_to_finish: flag to determine if this is a blocking call
245263
246264
"""
247-
248-
# can't use `with` because we need wavefile to remain open after return
249-
self.wavfile = open(file_name, "rb")
250-
wavedata = audiocore.WaveFile(self.wavfile)
251-
self.audio.play(wavedata)
252-
if not wait_to_finish:
253-
return
254-
while self.audio.playing:
255-
pass
256-
self.wavfile.close()
265+
if self._dac_present:
266+
# can't use `with` because we need wavefile to remain open after return
267+
self.wavfile = open(file_name, "rb")
268+
wavedata = audiocore.WaveFile(self.wavfile)
269+
self.audio.play(wavedata)
270+
if not wait_to_finish:
271+
return
272+
while self.audio.playing:
273+
pass
274+
self.wavfile.close()
257275

258276
def play_mp3_file(self, filename):
259-
if self._mp3_decoder is None:
260-
from audiomp3 import MP3Decoder # noqa: PLC0415, import outside top-level
277+
if self._dac_present:
278+
if self._mp3_decoder is None:
279+
from audiomp3 import MP3Decoder # noqa: PLC0415, import outside top-level
261280

262-
self._mp3_decoder = MP3Decoder(filename)
263-
else:
264-
self._mp3_decoder.open(filename)
281+
self._mp3_decoder = MP3Decoder(filename)
282+
else:
283+
self._mp3_decoder.open(filename)
265284

266-
self.audio.play(self._mp3_decoder)
267-
while self.audio.playing:
268-
pass
285+
self.audio.play(self._mp3_decoder)
286+
while self.audio.playing:
287+
pass
269288

270289
def stop_play(self):
271290
"""Stops playing a wav file."""
272-
self.audio.stop()
273-
if self.wavfile is not None:
274-
self.wavfile.close()
291+
if self._dac_present:
292+
self.audio.stop()
293+
if self.wavfile is not None:
294+
self.wavfile.close()
275295

276296
@property
277297
def volume(self) -> int:
@@ -297,8 +317,7 @@ def volume(self, volume_level: int) -> None:
297317
for the safe_volume_limit with the constructor or property."""
298318
)
299319

300-
self._volume = volume_level
301-
self._apply_volume()
320+
self._apply_volume(volume_level)
302321

303322
@property
304323
def audio_output(self) -> str:
@@ -311,22 +330,26 @@ def audio_output(self) -> str:
311330
@audio_output.setter
312331
def audio_output(self, audio_output: str) -> None:
313332
"""
314-
315333
:param audio_output: The audio interface to use 'speaker' or 'headphone'.
316334
:return: None
317335
"""
318336
if audio_output == "headphone":
319-
self._dac.headphone_output = True
320-
self._dac.speaker_output = False
337+
if self._dac_present:
338+
self._dac.headphone_output = True
339+
self._dac.speaker_output = False
321340
elif audio_output == "speaker":
322-
self._dac.headphone_output = False
323-
self._dac.speaker_output = True
341+
if self._dac_present:
342+
self._dac.headphone_output = False
343+
self._dac.speaker_output = True
324344
else:
325345
raise ValueError("audio_output must be either 'headphone' or 'speaker'")
346+
self._audio_output = audio_output
326347

327-
def _apply_volume(self) -> None:
348+
def _apply_volume(self, volume_level: int) -> None:
328349
"""
329350
Map the basic volume level to a db value and set it on the DAC.
330351
"""
331-
db_val = map_range(self._volume, 1, 20, -63, 23)
332-
self._dac.dac_volume = db_val
352+
self._volume = volume_level
353+
if self._dac_present:
354+
db_val = map_range(self._volume, 1, 20, -63, 23)
355+
self._dac.dac_volume = db_val

0 commit comments

Comments
 (0)