From ce4c56b6c8df52767a35d11ee4c9f283719c69f7 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 9 Aug 2025 14:44:04 -0400 Subject: [PATCH 1/7] Add audio output option file to Larsio and Chip's Challenge --- Fruit_Jam/Larsio_Paint_Music/sound_manager.py | 66 +++++++++++++++---- Metro/Metro_RP2350_Chips_Challenge/code.py | 11 +--- .../definitions.py | 52 ++++++++++++++- 3 files changed, 107 insertions(+), 22 deletions(-) diff --git a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py index 5520669d5..ff88b8a3b 100755 --- a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py +++ b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py @@ -9,6 +9,7 @@ import math import time import array +import json import gc import os import digitalio @@ -22,6 +23,7 @@ import audiomixer import synthio import board +import adafruit_pathlib as pathlib import adafruit_tlv320 from adafruit_midi.note_on import NoteOn from adafruit_midi.note_off import NoteOff @@ -63,6 +65,15 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): # Setup PWM audio output on D10 self.audio = audiopwmio.PWMAudioOut(board.D10) else: # i2s + # optional configuration file for speaker/headphone setting, check current and root directory + launcher_config = {} + if pathlib.Path("launcher.conf.json").exists(): + with open("launcher.conf.json", "r") as f: + launcher_config = json.load(f) + elif pathlib.Path("/launcher.conf.json").exists(): + with open("/launcher.conf.json", "r") as f: + launcher_config = json.load(f) + try: # Import libraries needed for I2S #check for Metro RP2350 vs. Fruit Jam @@ -96,20 +107,49 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): wsel_pin = board.I2S_WS din_pin = board.I2S_DIN + # Check if DAC is connected + while not i2c.try_lock(): + time.sleep(0.01) + if 0x18 in i2c.scan(): + ltv320_present = True + else: + ltv320_present = False + i2c.unlock() + # Initialize TLV320 - self.tlv = adafruit_tlv320.TLV320DAC3100(i2c) - self.tlv.configure_clocks(sample_rate=11025, bit_depth=16) - self.tlv.headphone_output = True - self.tlv.headphone_volume = -15 # dB - - # Setup I2S audio output - important to do this AFTER configuring the DAC - self.audio = audiobusio.I2SOut( - bit_clock=bclck_pin, - word_select=wsel_pin, - data=din_pin - ) - - print("TLV320 I2S DAC initialized successfully") + if ltv320_present: + self.tlv = adafruit_tlv320.TLV320DAC3100(i2c) + + # set sample rate & bit depth + self.tlv.configure_clocks(sample_rate=11025, bit_depth=16) + + if "sound" in launcher_config: + if launcher_config["sound"] == "speaker": + # use speaker + self.tlv.speaker_output = True + self.tlv.speaker_volume = -60 + else: + # use headphones + self.tlv.headphone_output = True + self.tlv.headphone_volume = -15 # dB + else: + # default to headphones + self.tlv.headphone_output = True + self.tlv.headphone_volume = -15 # dB + + # Setup I2S audio output - important to do this AFTER configuring the DAC + self.audio = audiobusio.I2SOut( + bit_clock=bclck_pin, + word_select=wsel_pin, + data=din_pin + ) + + print("TLV320 I2S DAC initialized successfully") + + else: + print("TLV320 DAC not found, falling back to PWM audio output") + self.audio = audiopwmio.PWMAudioOut(board.D10) + except Exception as e: print(f"Error initializing TLV320 DAC: {e}") print("Falling back to PWM audio output") diff --git a/Metro/Metro_RP2350_Chips_Challenge/code.py b/Metro/Metro_RP2350_Chips_Challenge/code.py index e9b7d3f4f..8071dea90 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/code.py +++ b/Metro/Metro_RP2350_Chips_Challenge/code.py @@ -7,7 +7,6 @@ import picodvi import framebufferio import displayio -import adafruit_tlv320 import audiobusio from audio import Audio from game import Game @@ -39,16 +38,12 @@ displayio.release_displays() -i2c = board.I2C() -dac = adafruit_tlv320.TLV320DAC3100(i2c) -dac.configure_clocks(sample_rate=44100, bit_depth=16) -dac.headphone_output = True -dac.headphone_volume = -15 # dB - if hasattr(board, "I2S_BCLK"): audio_bus = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN) -else: +elif hasattr(board, "D9") and hasattr(board, "D10") and hasattr(board, "D11"): audio_bus = audiobusio.I2SOut(board.D9, board.D10, board.D11) +else: + audio_bus = None audio = Audio(audio_bus, SOUND_EFFECTS) fb = picodvi.Framebuffer(320, 240, clk_dp=board.CKP, clk_dn=board.CKN, diff --git a/Metro/Metro_RP2350_Chips_Challenge/definitions.py b/Metro/Metro_RP2350_Chips_Challenge/definitions.py index df53dc158..b18597945 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/definitions.py +++ b/Metro/Metro_RP2350_Chips_Challenge/definitions.py @@ -1,10 +1,60 @@ # SPDX-FileCopyrightText: 2025 Melissa LeBlanc-Williams # # SPDX-License-Identifier: MIT +import json +import board from micropython import const +import adafruit_pathlib as pathlib +import adafruit_tlv320 + +# optional configuration file for speaker/headphone setting, check current and root directory +launcher_config = {} +if pathlib.Path("launcher.conf.json").exists(): + with open("launcher.conf.json", "r") as f: + launcher_config = json.load(f) +elif pathlib.Path("/launcher.conf.json").exists(): + with open("/launcher.conf.json", "r") as f: + launcher_config = json.load(f) + +# Check if DAC is connected +i2c = board.I2C() +while not i2c.try_lock(): + time.sleep(0.01) +if 0x18 in i2c.scan(): + ltv320_present = True +else: + ltv320_present = False +i2c.unlock() + +if ltv320_present: + dac = adafruit_tlv320.TLV320DAC3100(i2c) + + # set sample rate & bit depth + dac.configure_clocks(sample_rate=44100, bit_depth=16) + + if "sound" in launcher_config: + if launcher_config["sound"] == "speaker": + # use speaker + dac.speaker_output = True + dac.speaker_volume = -40 + elif launcher_config["sound"] != "mute": + # use headphones + dac.headphone_output = True + dac.headphone_volume = -15 # dB + else: + # default to headphones + dac.headphone_output = True + dac.headphone_volume = -15 # dB # Settings -PLAY_SOUNDS = True +if ltv320_present: + PLAY_SOUNDS = True +else: + PLAY_SOUNDS = False + +if "sound" in launcher_config: + if launcher_config["sound"] == "mute": + PLAY_SOUNDS = False # Timing Constants TICKS_PER_SECOND = const(20) From 986f736d17922591e75d1ce3a4a605cb18924849 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 9 Aug 2025 15:21:41 -0400 Subject: [PATCH 2/7] pylint fixes --- Fruit_Jam/Larsio_Paint_Music/sound_manager.py | 3 ++- Metro/Metro_RP2350_Chips_Challenge/definitions.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py index ff88b8a3b..ce5159b7c 100755 --- a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py +++ b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py @@ -65,7 +65,8 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): # Setup PWM audio output on D10 self.audio = audiopwmio.PWMAudioOut(board.D10) else: # i2s - # optional configuration file for speaker/headphone setting, check current and root directory + # optional configuration file for speaker/headphone setting + # check current directory first and then root directory launcher_config = {} if pathlib.Path("launcher.conf.json").exists(): with open("launcher.conf.json", "r") as f: diff --git a/Metro/Metro_RP2350_Chips_Challenge/definitions.py b/Metro/Metro_RP2350_Chips_Challenge/definitions.py index b18597945..447f21bac 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/definitions.py +++ b/Metro/Metro_RP2350_Chips_Challenge/definitions.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: MIT import json +import time import board from micropython import const import adafruit_pathlib as pathlib From 5b726750dbb6b8c8ca42641d94807196778ef166 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 9 Aug 2025 15:56:20 -0400 Subject: [PATCH 3/7] Replace possible infinite loops with 5 second max --- Fruit_Jam/Larsio_Paint_Music/sound_manager.py | 4 +++- Metro/Metro_RP2350_Chips_Challenge/definitions.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py index ce5159b7c..1702d5b29 100755 --- a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py +++ b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py @@ -109,7 +109,9 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): din_pin = board.I2S_DIN # Check if DAC is connected - while not i2c.try_lock(): + for i in range(500): # try for 5 seconds + if i2c.try_lock(): + break time.sleep(0.01) if 0x18 in i2c.scan(): ltv320_present = True diff --git a/Metro/Metro_RP2350_Chips_Challenge/definitions.py b/Metro/Metro_RP2350_Chips_Challenge/definitions.py index 447f21bac..6194cf2ff 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/definitions.py +++ b/Metro/Metro_RP2350_Chips_Challenge/definitions.py @@ -19,7 +19,9 @@ # Check if DAC is connected i2c = board.I2C() -while not i2c.try_lock(): +for i in range(500): # try for 5 seconds + if i2c.try_lock(): + break time.sleep(0.01) if 0x18 in i2c.scan(): ltv320_present = True From 33eceaf3fa353d770a03099c71cd7fb36bed0f37 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 15 Aug 2025 18:29:55 -0400 Subject: [PATCH 4/7] remove guard against missing DAC --- Fruit_Jam/Larsio_Paint_Music/sound_manager.py | 63 +++++++------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py index 1702d5b29..5a11be571 100755 --- a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py +++ b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py @@ -108,51 +108,34 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): wsel_pin = board.I2S_WS din_pin = board.I2S_DIN - # Check if DAC is connected - for i in range(500): # try for 5 seconds - if i2c.try_lock(): - break - time.sleep(0.01) - if 0x18 in i2c.scan(): - ltv320_present = True - else: - ltv320_present = False - i2c.unlock() - # Initialize TLV320 - if ltv320_present: - self.tlv = adafruit_tlv320.TLV320DAC3100(i2c) - - # set sample rate & bit depth - self.tlv.configure_clocks(sample_rate=11025, bit_depth=16) - - if "sound" in launcher_config: - if launcher_config["sound"] == "speaker": - # use speaker - self.tlv.speaker_output = True - self.tlv.speaker_volume = -60 - else: - # use headphones - self.tlv.headphone_output = True - self.tlv.headphone_volume = -15 # dB + self.tlv = adafruit_tlv320.TLV320DAC3100(i2c) + + # set sample rate & bit depth + self.tlv.configure_clocks(sample_rate=11025, bit_depth=16) + + if "sound" in launcher_config: + if launcher_config["sound"] == "speaker": + # use speaker + self.tlv.speaker_output = True + self.tlv.speaker_volume = -60 else: - # default to headphones + # use headphones self.tlv.headphone_output = True self.tlv.headphone_volume = -15 # dB - - # Setup I2S audio output - important to do this AFTER configuring the DAC - self.audio = audiobusio.I2SOut( - bit_clock=bclck_pin, - word_select=wsel_pin, - data=din_pin - ) - - print("TLV320 I2S DAC initialized successfully") - else: - print("TLV320 DAC not found, falling back to PWM audio output") - self.audio = audiopwmio.PWMAudioOut(board.D10) - + # default to headphones + self.tlv.headphone_output = True + self.tlv.headphone_volume = -15 # dB + + # Setup I2S audio output - important to do this AFTER configuring the DAC + self.audio = audiobusio.I2SOut( + bit_clock=bclck_pin, + word_select=wsel_pin, + data=din_pin + ) + + print("TLV320 I2S DAC initialized successfully") except Exception as e: print(f"Error initializing TLV320 DAC: {e}") print("Falling back to PWM audio output") From 118806a614964c6c0682370de6a92c63a3d83aab Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 15 Aug 2025 23:13:26 -0400 Subject: [PATCH 5/7] seperate PLAY_SOUNDS definition from default logic --- Metro/Metro_RP2350_Chips_Challenge/definitions.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Metro/Metro_RP2350_Chips_Challenge/definitions.py b/Metro/Metro_RP2350_Chips_Challenge/definitions.py index 6194cf2ff..31d9f1d40 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/definitions.py +++ b/Metro/Metro_RP2350_Chips_Challenge/definitions.py @@ -49,15 +49,17 @@ dac.headphone_output = True dac.headphone_volume = -15 # dB -# Settings if ltv320_present: - PLAY_SOUNDS = True + _default_play_sounds = True else: - PLAY_SOUNDS = False + _default_play_sounds = False if "sound" in launcher_config: if launcher_config["sound"] == "mute": - PLAY_SOUNDS = False + _default_play_sounds = False + +# Settings +PLAY_SOUNDS = _default_play_sounds # Timing Constants TICKS_PER_SECOND = const(20) From 5c59c048bda4712a42e754e4701e3ac0f37c81ba Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 16 Aug 2025 10:01:47 -0400 Subject: [PATCH 6/7] Add guard for boards without I2C defined --- .../definitions.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Metro/Metro_RP2350_Chips_Challenge/definitions.py b/Metro/Metro_RP2350_Chips_Challenge/definitions.py index 31d9f1d40..4302150a4 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/definitions.py +++ b/Metro/Metro_RP2350_Chips_Challenge/definitions.py @@ -18,16 +18,19 @@ launcher_config = json.load(f) # Check if DAC is connected -i2c = board.I2C() -for i in range(500): # try for 5 seconds - if i2c.try_lock(): - break - time.sleep(0.01) -if 0x18 in i2c.scan(): - ltv320_present = True +if "I2C" in dir(board): + i2c = board.I2C() + for i in range(500): # try for 5 seconds + if i2c.try_lock(): + break + time.sleep(0.01) + if 0x18 in i2c.scan(): + ltv320_present = True + else: + ltv320_present = False + i2c.unlock() else: ltv320_present = False -i2c.unlock() if ltv320_present: dac = adafruit_tlv320.TLV320DAC3100(i2c) From 83d33d6641d2f77a2ccea7d181c9250f91db8d5f Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 17 Aug 2025 15:54:20 -0400 Subject: [PATCH 7/7] eliminate app dictionay, use only root config file and fix tlv320 variable name --- Fruit_Jam/Larsio_Paint_Music/sound_manager.py | 17 ++++------ .../definitions.py | 32 +++++++++---------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py index 5a11be571..ddb0276a4 100755 --- a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py +++ b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py @@ -66,12 +66,8 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): self.audio = audiopwmio.PWMAudioOut(board.D10) else: # i2s # optional configuration file for speaker/headphone setting - # check current directory first and then root directory launcher_config = {} - if pathlib.Path("launcher.conf.json").exists(): - with open("launcher.conf.json", "r") as f: - launcher_config = json.load(f) - elif pathlib.Path("/launcher.conf.json").exists(): + if pathlib.Path("/launcher.conf.json").exists(): with open("/launcher.conf.json", "r") as f: launcher_config = json.load(f) @@ -110,23 +106,24 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): # Initialize TLV320 self.tlv = adafruit_tlv320.TLV320DAC3100(i2c) + self.tlv.reset() # set sample rate & bit depth self.tlv.configure_clocks(sample_rate=11025, bit_depth=16) - if "sound" in launcher_config: - if launcher_config["sound"] == "speaker": + if "tlv320" in launcher_config: + if launcher_config["tlv320"].get("output") == "speaker": # use speaker self.tlv.speaker_output = True - self.tlv.speaker_volume = -60 + self.tlv.dac_volume = launcher_config["tlv320"].get("volume",5) # dB else: # use headphones self.tlv.headphone_output = True - self.tlv.headphone_volume = -15 # dB + self.tlv.dac_volume = launcher_config["tlv320"].get("volume",0) # dB else: # default to headphones self.tlv.headphone_output = True - self.tlv.headphone_volume = -15 # dB + self.tlv.dac_volume = 0 # dB # Setup I2S audio output - important to do this AFTER configuring the DAC self.audio = audiobusio.I2SOut( diff --git a/Metro/Metro_RP2350_Chips_Challenge/definitions.py b/Metro/Metro_RP2350_Chips_Challenge/definitions.py index 4302150a4..1e44c0707 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/definitions.py +++ b/Metro/Metro_RP2350_Chips_Challenge/definitions.py @@ -8,12 +8,9 @@ import adafruit_pathlib as pathlib import adafruit_tlv320 -# optional configuration file for speaker/headphone setting, check current and root directory +# optional configuration file for speaker/headphone setting launcher_config = {} -if pathlib.Path("launcher.conf.json").exists(): - with open("launcher.conf.json", "r") as f: - launcher_config = json.load(f) -elif pathlib.Path("/launcher.conf.json").exists(): +if pathlib.Path("/launcher.conf.json").exists(): with open("/launcher.conf.json", "r") as f: launcher_config = json.load(f) @@ -25,40 +22,41 @@ break time.sleep(0.01) if 0x18 in i2c.scan(): - ltv320_present = True + tlv320_present = True else: - ltv320_present = False + tlv320_present = False i2c.unlock() else: - ltv320_present = False + tlv320_present = False -if ltv320_present: +if tlv320_present: dac = adafruit_tlv320.TLV320DAC3100(i2c) + dac.reset() # set sample rate & bit depth dac.configure_clocks(sample_rate=44100, bit_depth=16) - if "sound" in launcher_config: - if launcher_config["sound"] == "speaker": + if "tlv320" in launcher_config: + if launcher_config["tlv320"].get("output") == "speaker": # use speaker dac.speaker_output = True - dac.speaker_volume = -40 - elif launcher_config["sound"] != "mute": + dac.dac_volume = launcher_config["tlv320"].get("volume",5) # dB + else: # use headphones dac.headphone_output = True - dac.headphone_volume = -15 # dB + dac.dac_volume = launcher_config["tlv320"].get("volume",0) # dB else: # default to headphones dac.headphone_output = True - dac.headphone_volume = -15 # dB + dac.dac_volume = 0 # dB -if ltv320_present: +if tlv320_present: _default_play_sounds = True else: _default_play_sounds = False if "sound" in launcher_config: - if launcher_config["sound"] == "mute": + if launcher_config["tlv320"].get("output") == "mute": _default_play_sounds = False # Settings