Compare commits

..

22 commits
1.0.0 ... main

Author SHA1 Message Date
foamyguy
1c7d358468
Merge pull request #11 from samblenny/fix-example-link
Some checks failed
Build CI / test (push) Has been cancelled
fix broken "Volume test" example link in docs
2025-08-25 19:51:14 -05:00
sam blenny
0063bdc27e fix broken example link in docs
The old way worked fine on my local server, but it gave me a 404
when I checked the production version on the RTD server. Hopefully
this will fix it?
2025-08-25 22:18:31 +00:00
foamyguy
49f95b2a6a
Merge pull request #10 from samblenny/volume-fixes
Some checks failed
Build CI / test (push) Has been cancelled
Overhaul Volume Control Implementation
2025-08-25 16:37:38 -05:00
sam blenny
bc20f0b3c0 more docs, mostly usage examples
Hopefully, this should make it clear how to use the API for setting
volume for speakers, headphones, or line-level output.
2025-08-25 19:17:03 +00:00
sam blenny
81c00085ec remove doc-comment backslash escapes
I added these several commits back because they seemed to resolve
a weird Sphinx code-block rendering bug in the html docs. But, now
I can't reproduce the bug, so there's no reason to keep the
escapes.
2025-08-25 15:04:27 +00:00
sam blenny
bd24ba5e46 fix speaker_volume & tune gain defaults
This fixes a bug that I found while testing the speaker output. I
also balanced the default speaker_volume and headphone_volume gain
levels so they sound about the same loudness (to me, with my
earbuds). That way you can use dac_volume for runtime volume
adjustments and it will hopefully work about the same for the
speaker or the headphones.

To set the board up for different headphones or speakers, you could
experiment with suitable values for speaker_volume and
headphone_volume to set in an initialization function, like a
trimpot. After that, you could use dac_volume to set volume.
2025-08-25 06:47:57 +00:00
sam blenny
12d09409be fix headphone route, dial in default gain
This fixes the headphone_output property setter to use the
DAC_ROUTE_MIXER option so the headphone_volume attenuation stage
doesn't get bypassed (as it did previously). This makes it a lot
easier to set non-ear-bleedy volume levels for headphones.
2025-08-25 04:44:02 +00:00
sam blenny
522f615ffe fix silly bugs from blind edits
This is what needed fixing when I actually started running some
Fruit Jam test code with the headphone output. Haven't tried the
speaker yet.
2025-08-25 03:34:03 +00:00
sam blenny
76d1cad3f4 fix Pylance problems
Mostly these were type annotations referring to non-existant types.
There was also a misspelled function name.
2025-08-25 00:21:28 +00:00
sam blenny
e1f10ab7df revise doc-comments to fix missing info
Thought I was done with doc-comments, but then I noticed that there
wasn't a clear indication in the html about which properties were
settable and which were read only. I also discovered that several
properties that could raise ValueError weren't marked as such.
(technically they were in the code, but Sphinx was ignoring that)

Changes:
1. Systematic :getter: and :setter: attributes for properties
2. Move `:raises: ...` from setter comments to getter comments so
   they show up in the html doc. This particularly applies to
   setters that use constants.
3. Change `:return: ...` to `:getter: ...` for getter functions
2025-08-25 00:04:06 +00:00
sam blenny
5e076772c1 fix failing pre-commit linter checks
This gets ruff to stop complaining. Haven't tested the code yet
though. That's next.
2025-08-24 22:37:20 +00:00
sam blenny
f694d14682 level up documentation comments
This fixes assorted Sphinx documentation stuff:
1. Add CSS workaround for horizontal stacking glitch in rtd theme
2. Convert plain comments for public constants to doc-comment style
   so they will get included in the html docs
3. Change Sphinx autodoc's default sort order from alphabetical to
   groupwise. Now the class methods come first, the properties come
   next, and the constants go at the end. It's much easier to read
   this way.
2025-08-24 21:44:10 +00:00
sam blenny
71294d0145 first pass at volume control overhaul
This commit makes several interrelated changes at once:
1. There's a new lookup table based dB to int7 conversion mechansim
   for the analog volume setting properties (based on Table 6-24)
2. Major docs comment revisions for properties involved in DAC
   volume, speaker volume, headphone volume, speaker gain, and
   headphone gain
3. Added "_" prefix to private helper classes to stop them from
   cluttering up the Sphinx html docs build
4. Merged setter & getter comments into the setter comment for
   the properties I modified. NOTE: Sphinx does not render docs
   comments on property setters!
4. Assorted small-ish revisions to exception handling and
   arguments (convert SPK_GAIN_* constants to dB) to resolve
   inconsistent or surprising behavior discovered while revising
   docs comments

Overall, the goals here are:
1. Make volume setting implementation work and be non-surprising
2. Document how it works
3. Make the TLV320 html docs more readable and complete. A lot of
   the docs comment info wasn't making it through into the html
   docs build because Sphinx ignores setter comments.

The comments build fine, but it's possible the code has errors.
Saving that testing for another day.
2025-08-24 09:44:50 +00:00
Liz
f24788d94a
Merge pull request #7 from adafruit/add_reset
Some checks failed
Build CI / test (push) Has been cancelled
add hardware reset to examples
2025-05-07 16:47:45 -04:00
Liz
1fa7603d77 precommit 2025-05-07 16:44:39 -04:00
Liz
ef1cc7633f add hardware reset to examples 2025-05-07 16:41:25 -04:00
Liz
995ae32c2a
Merge pull request #6 from adafruit/volume_update
create dac_volume
2025-05-06 08:11:21 -04:00
Liz
588bcfecff Update README.rst 2025-05-06 08:08:51 -04:00
Liz
1c7a72dd05 create dac_volume 2025-05-05 12:15:33 -04:00
Liz
b4108f53e4
check for built-in i2s in example
Some checks failed
Build CI / test (push) Has been cancelled
2025-04-25 18:43:30 -04:00
Liz
8d5541065e make speaker louder by default
Some checks failed
Build CI / test (push) Has been cancelled
2025-04-07 08:40:31 -04:00
Liz
51c14aaab3
Update README.rst 2025-04-02 10:27:28 -04:00
8 changed files with 905 additions and 291 deletions

View file

@ -94,23 +94,27 @@ Usage Example
import audiobusio
import audiocore
import board
import digitalio
import adafruit_tlv320
# Reset the DAC before use
reset_pin = digitalio.DigitalInOut(board.D12)
reset_pin.direction = digitalio.Direction.OUTPUT
reset_pin.value = False # Set low to reset
time.sleep(0.1) # Pause 100ms
reset_pin.value = True # Set high to release from reset
i2c = board.I2C()
dac = adafruit_tlv320.TLV320DAC3100(i2c)
# set mclk, sample rate & bit depth
dac.configure_clocks(mclk_freq=12000000, sample_rate=44100, bit_depth=16)
dac.configure_clocks(sample_rate=44100, bit_depth=16)
# use headphones
# helper function for default settings
dac.headphone_output = True
dac.headphone_volume = -20 # dB
# or use speaker
# helper function for default settings
# dac.speaker_output = True
# dac.speaker_volume = -15 # dB
dac.dac_volume = -20 # dB
audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)

File diff suppressed because it is too large Load diff

8
docs/_static/custom.css vendored Normal file
View file

@ -0,0 +1,8 @@
/* SPDX-FileCopyrightText: 2025 Sam Blenny
* SPDX-License-Identifier: MIT
*/
/* Monkey patch the rtd theme to prevent horizontal stacking of short items
* see https://github.com/readthedocs/sphinx_rtd_theme/issues/1301
*/
.py.property{display: block !important;}

View file

@ -29,6 +29,9 @@ autodoc_mock_imports = ["digitalio", "busio", "adafruit_bus_device", "micropytho
autodoc_preserve_defaults = True
# Override the default config in which autodoc sorts things alphabetically
autodoc_member_order = "groupwise"
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"BusDevice": ("https://docs.circuitpython.org/projects/busdevice/en/latest/", None),
@ -117,6 +120,9 @@ html_theme = "sphinx_rtd_theme"
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# Include extra css to work around rtd theme glitches
html_css_files = ["custom.css"]
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.

View file

@ -15,3 +15,12 @@ Demos advanced features of the library.
.. literalinclude:: ../examples/tlv320_fulltest.py
:caption: examples/tlv320_fulltest.py
:linenos:
Volume test
-----------
Test tone generator with interactive serial console volume controls
.. literalinclude:: ../examples/tlv320_volumetest.py
:caption: examples/tlv320_volumetest.py
:linenos:

View file

@ -13,6 +13,7 @@ in simpletest.
import time
import board
import digitalio
from adafruit_tlv320 import (
DAC_PATH_MIXED,
@ -41,6 +42,13 @@ from adafruit_tlv320 import (
VOL_RIGHT_TO_LEFT,
)
# Reset the DAC before use
reset_pin = digitalio.DigitalInOut(board.D12)
reset_pin.direction = digitalio.Direction.OUTPUT
reset_pin.value = False # Set low to reset
time.sleep(0.1) # Pause 100ms
reset_pin.value = True # Set high to release from reset
print("Initializing I2C and TLV320DAC3100...")
i2c = board.I2C()
dac = TLV320DAC3100(i2c)

View file

@ -9,9 +9,17 @@ import time
import audiobusio
import audiocore
import board
import digitalio
import adafruit_tlv320
# Reset the DAC before use
reset_pin = digitalio.DigitalInOut(board.D12)
reset_pin.direction = digitalio.Direction.OUTPUT
reset_pin.value = False # Set low to reset
time.sleep(0.1) # Pause 100ms
reset_pin.value = True # Set high to release from reset
i2c = board.I2C()
dac = adafruit_tlv320.TLV320DAC3100(i2c)
@ -20,12 +28,15 @@ dac.configure_clocks(sample_rate=44100, bit_depth=16)
# use headphones
dac.headphone_output = True
dac.headphone_volume = -15 # dB
dac.dac_volume = -10 # dB
# or use speaker
# dac.speaker_output = True
# dac.speaker_volume = -10 # dB
# dac.speaker_volume = -20 # dB
audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
if "I2S_BCLK" and "I2S_WS" in dir(board):
audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
else:
audio = audiobusio.I2SOut(board.D9, board.D10, board.D11)
# generate a sine wave
tone_volume = 0.5
frequency = 440

View file

@ -0,0 +1,171 @@
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: Copyright 2025 Sam Blenny
#
import gc
import os
import sys
import time
import displayio
import supervisor
import synthio
from audiobusio import I2SOut
from board import I2C, I2S_BCLK, I2S_DIN, I2S_MCLK, I2S_WS, PERIPH_RESET
from digitalio import DigitalInOut, Direction, Pull
from micropython import const
from adafruit_tlv320 import TLV320DAC3100
# DAC and Synthesis parameters
SAMPLE_RATE = const(11025)
CHAN_COUNT = const(2)
BUFFER_SIZE = const(1024)
# DAC volume limits
DV_MIN = -63.5
DV_MAX = 24.0
# Headphone volume limits
HV_MIN = -78.3
HV_MAX = 0
# Headphone gain limits
HG_MIN = 0
HG_MAX = 9
# Speaker volume limits
SV_MIN = -78.3
SV_MAX = 0
# Speaker amp gain limits
SG_MIN = 6
SG_MAX = 24
SG_STEP = 6
def init_dac_audio_synth(i2c):
"""Configure TLV320 I2S DAC for audio output and make a Synthesizer.
:param i2c: a reference to board.I2C()
:return: tuple(dac: TLV320DAC3100, audio: I2SOut, synth: Synthesizer)
"""
# 1. Reset DAC (reset is active low)
rst = DigitalInOut(PERIPH_RESET)
rst.direction = Direction.OUTPUT
rst.value = False
time.sleep(0.1)
rst.value = True
time.sleep(0.05)
# 2. Configure sample rate, bit depth, and output port
dac = TLV320DAC3100(i2c)
dac.configure_clocks(sample_rate=SAMPLE_RATE, bit_depth=16)
dac.speaker_output = True
dac.headphone_output = True
# 4. Initialize I2S for Fruit Jam rev D
audio = I2SOut(bit_clock=I2S_BCLK, word_select=I2S_WS, data=I2S_DIN)
# 5. Configure synthio patch to generate audio
vca = synthio.Envelope(
attack_time=0, decay_time=0, sustain_level=1.0, release_time=0, attack_level=1.0
)
synth = synthio.Synthesizer(sample_rate=SAMPLE_RATE, channel_count=CHAN_COUNT, envelope=vca)
return (dac, audio, synth)
def main(): # noqa: PLR0912, PLR0915, allow long function and long if statement
# Turn off the default DVI display to free up CPU
displayio.release_displays()
gc.collect()
# Set up the audio stuff for a basic synthesizer
i2c = I2C()
(dac, audio, synth) = init_dac_audio_synth(i2c)
audio.play(synth)
dv = dac.dac_volume # default DAC volume
hv = dac.headphone_volume # default headphone analog volume
hg = dac.headphone_left_gain # default headphone amp gain
sv = dac.speaker_volume # default speaker analog volume
sg = dac.speaker_gain # default speaker amp gain
note = 60
synth.press(note)
# Check for unbuffered keystroke input on the USB serial console
print("""
=== TLV320DAC Volume Tester ===
Controls:
q/z: dac_volume +/- 1
w/x: headphone_volume +/- 1
e/c: headphone_left_gain headphone_right_gain +/- 1
r/v: speaker_volume +/- 1
t/b: speaker_gain +/- 6
space: toggle speaker_output (amp power), this will reset volume & gain
For less headphone noise, turn off the speaker amp (spacebar)
""")
while True:
time.sleep(0.01)
if supervisor.runtime.serial_bytes_available:
while supervisor.runtime.serial_bytes_available:
c = sys.stdin.read(1)
if c == "q":
# Q = DAC Volume UP
dv = min(DV_MAX, max(DV_MIN, dv + 1))
dac.dac_volume = dv
print(f"dv = {dv:.1f} ({dac.dac_volume:.1f})")
elif c == "z":
# Z = DAC Volume DOWN
dv = min(DV_MAX, max(DV_MIN, dv - 1))
dac.dac_volume = dv
print(f"dv = {dv:.1f} ({dac.dac_volume:.1f})")
elif c == "w":
# W = Headphone Volume UP
hv = min(HV_MAX, max(HV_MIN, hv + 1))
dac.headphone_volume = hv
print(f"hv = {hv:.1f} ({dac.headphone_volume:.1f})")
elif c == "x":
# X = Headphone Volume DOWN
hv = min(HV_MAX, max(HV_MIN, hv - 1))
dac.headphone_volume = hv
print(f"hv = {hv:.1f} ({dac.headphone_volume:.1f})")
elif c == "e":
# E = Headphone Amp Gain UP
hg = min(HG_MAX, max(HG_MIN, hg + 1))
dac.headphone_left_gain = hg
dac.headphone_right_gain = hg
print(f"hg = {hg:.1f} ({dac.headphone_left_gain})")
elif c == "c":
# C = Headphone Amp Gain DOWN
hg = min(HG_MAX, max(HG_MIN, hg - 1))
dac.headphone_left_gain = hg
dac.headphone_right_gain = hg
print(f"hg = {hg:.1f} ({dac.headphone_left_gain})")
if c == "r":
# R = Speaker Volume UP
sv = min(SV_MAX, max(SV_MIN, sv + 1))
dac.speaker_volume = sv
print(f"sv = {sv:.1f} ({dac.speaker_volume:.1f})")
elif c == "v":
# V = Speaker Volume DOWN
sv = min(SV_MAX, max(SV_MIN, sv - 1))
dac.speaker_volume = sv
print(f"sv = {sv:.1f} ({dac.speaker_volume:.1f})")
elif c == "t":
# T = Speaker Amp Gain UP
sg = min(SG_MAX, max(SG_MIN, sg + SG_STEP))
dac.speaker_gain = sg
print(f"sg = {sg:.1f} ({dac.speaker_gain})")
elif c == "b":
# B = Speaker Amp Gain DOWN
sg = min(SG_MAX, max(SG_MIN, sg - SG_STEP))
dac.speaker_gain = sg
print(f"sg = {sg:.1f} ({dac.speaker_gain})")
elif c == " ":
# Space = Toggle speaker amp enable/disable
en = not dac.speaker_output
dac.speaker_output = en
print(f"speaker_output = {en}")
main()