3838import displayio
3939import framebufferio
4040import picodvi
41+ import simpleio
4142import storage
4243import supervisor
4344from digitalio import DigitalInOut , Direction , Pull
@@ -133,13 +134,16 @@ def get_display_config():
133134class 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