Merge pull request #13 from FoamyGuy/volume_api
Some checks failed
Build CI / test (push) Has been cancelled
Some checks failed
Build CI / test (push) Has been cancelled
Volume and output interface API
This commit is contained in:
commit
8ee337c686
6 changed files with 110 additions and 25 deletions
|
|
@ -194,6 +194,8 @@ class FruitJam(PortalBase):
|
|||
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
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import framebufferio
|
|||
import picodvi
|
||||
import storage
|
||||
import supervisor
|
||||
from adafruit_simplemath import map_range
|
||||
from digitalio import DigitalInOut, Direction, Pull
|
||||
from neopixel import NeoPixel
|
||||
|
||||
|
|
@ -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 = []
|
||||
|
|
@ -155,11 +159,14 @@ class Peripherals:
|
|||
# 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
|
||||
|
|
@ -252,3 +259,61 @@ class Peripherals:
|
|||
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
|
||||
|
|
|
|||
|
|
@ -5,21 +5,16 @@ import time
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -5,21 +5,16 @@ import time
|
|||
|
||||
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)
|
||||
|
|
|
|||
27
examples/fruitjam_synthio_speaker.py
Normal file
27
examples/fruitjam_synthio_speaker.py
Normal file
|
|
@ -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)
|
||||
|
|
@ -15,3 +15,4 @@ adafruit-circuitpython-display-text
|
|||
adafruit-circuitpython-sd
|
||||
adafruit-circuitpython-ntp
|
||||
adafruit-circuitpython-connectionmanager
|
||||
adafruit-circuitpython-simplemath
|
||||
|
|
|
|||
Loading…
Reference in a new issue