Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions adafruit_fruitjam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ def __init__( # noqa: PLR0912,PLR0913,Too many branches,Too many arguments in f
self.sd_check = self.peripherals.sd_check
self.play_file = self.peripherals.play_file
self.stop_play = self.peripherals.stop_play
self.volume = self.peripherals.volume
self.audio_output = self.peripherals.audio_output

self.image_converter_url = self.network.image_converter_url
self.wget = self.network.wget
Expand Down
75 changes: 70 additions & 5 deletions adafruit_fruitjam/peripherals.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import picodvi
import storage
import supervisor
from adafruit_simplemath import map_range
from digitalio import DigitalInOut, Direction, Pull
from neopixel import NeoPixel

Expand Down Expand Up @@ -133,13 +134,16 @@ def get_display_config():
class Peripherals:
"""Peripherals Helper Class for the FruitJam Library

:param audio_output: The audio output interface to use 'speaker' or 'headphone'
:param safe_volume_limit: The maximum volume allowed for the audio output. Default is 15
Using higher values can damage some speakers, change at your own risk.

Attributes:
neopixels (NeoPxiels): The NeoPixels on the Fruit Jam board.
See https://circuitpython.readthedocs.io/projects/neopixel/en/latest/api.html
"""

def __init__(self):
def __init__(self, audio_output="headphone", safe_volume_limit=12):
self.neopixels = NeoPixel(board.NEOPIXEL, 5)

self._buttons = []
Expand All @@ -155,11 +159,14 @@ def __init__(self):
# set sample rate & bit depth
self._dac.configure_clocks(sample_rate=11030, bit_depth=16)

# use headphones
self._dac.headphone_output = True
self._dac.headphone_volume = -15 # dB

self._audio_output = audio_output
self.audio_output = audio_output
self._audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
if safe_volume_limit < 1 or safe_volume_limit > 20:
raise ValueError("safe_volume_limit must be between 1 and 20")
self.safe_volume_limit = safe_volume_limit
self._volume = 7
self._apply_volume()

self._sd_mounted = False
sd_pins_in_use = False
Expand Down Expand Up @@ -252,3 +259,61 @@ def stop_play(self):
self.audio.stop()
if self.wavfile is not None:
self.wavfile.close()

@property
def volume(self) -> int:
"""
The volume level of the Fruit Jam audio output. Valid values are 1-20.
"""
return self._volume

@volume.setter
def volume(self, volume_level: int) -> None:
"""
:param volume_level: new volume level 1-20
:return: None
"""
if not (1 <= volume_level <= 20):
raise ValueError("Volume level must be between 1 and 20")

if volume_level > self.safe_volume_limit:
raise ValueError(
f"""Volume level must be less than or equal to
safe_volume_limit: {self.safe_volume_limit}. Using higher values could damage speakers.
To override this limitation set a larger value than {self.safe_volume_limit}
for the safe_volume_limit with the constructor or property."""
)

self._volume = volume_level
self._apply_volume()

@property
def audio_output(self) -> str:
"""
The audio output interface. 'speaker' or 'headphone'
:return:
"""
return self._audio_output

@audio_output.setter
def audio_output(self, audio_output: str) -> None:
"""

:param audio_output: The audio interface to use 'speaker' or 'headphone'.
:return: None
"""
if audio_output == "headphone":
self._dac.headphone_output = True
self._dac.speaker_output = False
elif audio_output == "speaker":
self._dac.headphone_output = False
self._dac.speaker_output = True
else:
raise ValueError("audio_output must be either 'headphone' or 'speaker'")

def _apply_volume(self) -> None:
"""
Map the basic volume level to a db value and set it on the DAC.
"""
db_val = map_range(self._volume, 1, 20, -63, 23)
self._dac.dac_volume = db_val
15 changes: 5 additions & 10 deletions examples/fruitjam_headphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@

import adafruit_fruitjam

pobj = adafruit_fruitjam.peripherals.Peripherals()
dac = pobj.dac # use Fruit Jam's codec

# Route once for headphones
dac.headphone_output = True
dac.speaker_output = False
pobj = adafruit_fruitjam.peripherals.Peripherals(audio_output="headphone")

FILES = ["beep.wav", "dip.wav", "rise.wav"]
VOLUMES_DB = [12, 6, 0, -6, -12]
VOLUMES = [5, 7, 10, 11, 12]

while True:
print("\n=== Headphones Test ===")
for vol in VOLUMES_DB:
dac.dac_volume = vol
print(f"Headphones volume: {vol} dB")
for vol in VOLUMES:
pobj.volume = vol
print(f"Headphones volume: {vol}")
for f in FILES:
print(f" -> {f}")
pobj.play_file(f)
Expand Down
15 changes: 5 additions & 10 deletions examples/fruitjam_speaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@

import adafruit_fruitjam

pobj = adafruit_fruitjam.peripherals.Peripherals()
dac = pobj.dac # use Fruit Jam's codec

# Route once for speaker
dac.headphone_output = False
dac.speaker_output = True
pobj = adafruit_fruitjam.peripherals.Peripherals(audio_output="speaker")

FILES = ["beep.wav", "dip.wav", "rise.wav"]
VOLUMES_DB = [12, 6, 0, -6, -12]
VOLUMES = [5, 7, 10, 11, 12]

while True:
print("\n=== Speaker Test ===")
for vol in VOLUMES_DB:
dac.dac_volume = vol
print(f"Speaker volume: {vol} dB")
for vol in VOLUMES:
pobj.volume = vol
print(f"Speaker volume: {vol}")
for f in FILES:
print(f" -> {f}")
pobj.play_file(f)
Expand Down
27 changes: 27 additions & 0 deletions examples/fruitjam_synthio_speaker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time

import synthio

import adafruit_fruitjam

pobj = adafruit_fruitjam.peripherals.Peripherals(audio_output="headphone")

synth = synthio.Synthesizer(sample_rate=44100)
pobj.audio.play(synth)
VOLUMES = [5, 7, 10, 11, 12]
C_major_scale = [60, 62, 64, 65, 67, 69, 71, 72, 71, 69, 67, 65, 64, 62, 60]
while True:
print("\n=== Synthio Test ===")
for vol in VOLUMES:
pobj.volume = vol
print(f"Volume: {vol}")
for note in C_major_scale:
synth.press(note)
time.sleep(0.1)
synth.release(note)
time.sleep(0.05)

time.sleep(1.0)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ adafruit-circuitpython-display-text
adafruit-circuitpython-sd
adafruit-circuitpython-ntp
adafruit-circuitpython-connectionmanager
adafruit-circuitpython-simplemath