From 59aacb4b4ea71f0c908dc8c711f63c09371fc5e7 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 07:18:33 -0500 Subject: [PATCH 01/56] Converted Arduino library from https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library for use with CircuitPython. --- adafruit_wm8960.py | 1453 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1427 insertions(+), 26 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 43aa597..71342dd 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -1,14 +1,18 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics # SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple # # SPDX-License-Identifier: MIT """ `adafruit_wm8960` ================================================================================ -Barebones CircuitPython driver for WM8960 Stereo CODEC +CircuitPython driver for WM8960 Stereo CODEC +* Author(s): Scott Shawcroft, Cooper Dalrymple -* Author(s): Scott Shawcroft +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library Implementation Notes -------------------- @@ -21,48 +25,1445 @@ # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ -from adafruit_bus_device import i2c_device - -import time +from busio import I2C +from adafruit_bus_device.i2c_device import I2CDevice __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960.git" +# I2C address (7-bit format for Wire library) +WM8960_ADDR = 0x1A + +# WM8960 register addresses +WM8960_REG_LEFT_INPUT_VOLUME = 0x00 +WM8960_REG_RIGHT_INPUT_VOLUME = 0x01 +WM8960_REG_LOUT1_VOLUME = 0x02 +WM8960_REG_ROUT1_VOLUME = 0x03 +WM8960_REG_CLOCKING_1 = 0x04 +WM8960_REG_ADC_DAC_CTRL_1 = 0x05 +WM8960_REG_ADC_DAC_CTRL_2 = 0x06 +WM8960_REG_AUDIO_INTERFACE_1 = 0x07 +WM8960_REG_CLOCKING_2 = 0x08 +WM8960_REG_AUDIO_INTERFACE_2 = 0x09 +WM8960_REG_LEFT_DAC_VOLUME = 0x0A +WM8960_REG_RIGHT_DAC_VOLUME = 0x0B +WM8960_REG_RESET = 0x0F +WM8960_REG_3D_CONTROL = 0x10 +WM8960_REG_ALC1 = 0x11 +WM8960_REG_ALC2 = 0x12 +WM8960_REG_ALC3 = 0x13 +WM8960_REG_NOISE_GATE = 0x14 +WM8960_REG_LEFT_ADC_VOLUME = 0x15 +WM8960_REG_RIGHT_ADC_VOLUME = 0x16 +WM8960_REG_ADDITIONAL_CONTROL_1 = 0x17 +WM8960_REG_ADDITIONAL_CONTROL_2 = 0x18 +WM8960_REG_PWR_MGMT_1 = 0x19 +WM8960_REG_PWR_MGMT_2 = 0x1A +WM8960_REG_ADDITIONAL_CONTROL_3 = 0x1B +WM8960_REG_ANTI_POP_1 = 0x1C +WM8960_REG_ANTI_POP_2 = 0x1D +WM8960_REG_ADCL_SIGNAL_PATH = 0x20 +WM8960_REG_ADCR_SIGNAL_PATH = 0x21 +WM8960_REG_LEFT_OUT_MIX_1 = 0x22 +WM8960_REG_RIGHT_OUT_MIX_2 = 0x25 +WM8960_REG_MONO_OUT_MIX_1 = 0x26 +WM8960_REG_MONO_OUT_MIX_2 = 0x27 +WM8960_REG_LOUT2_VOLUME = 0x28 +WM8960_REG_ROUT2_VOLUME = 0x29 +WM8960_REG_MONO_OUT_VOLUME = 0x2A +WM8960_REG_INPUT_BOOST_MIXER_1 = 0x2B +WM8960_REG_INPUT_BOOST_MIXER_2 = 0x2C +WM8960_REG_BYPASS_1 = 0x2D +WM8960_REG_BYPASS_2 = 0x2E +WM8960_REG_PWR_MGMT_3 = 0x2F +WM8960_REG_ADDITIONAL_CONTROL_4 = 0x30 +WM8960_REG_CLASS_D_CONTROL_1 = 0x31 +WM8960_REG_CLASS_D_CONTROL_3 = 0x33 +WM8960_REG_PLL_N = 0x34 +WM8960_REG_PLL_K_1 = 0x35 +WM8960_REG_PLL_K_2 = 0x36 +WM8960_REG_PLL_K_3 = 0x37 + +# PGA input selections +WM8960_PGAL_LINPUT2 = 0 +WM8960_PGAL_LINPUT3 = 1 +WM8960_PGAL_VMID = 2 +WM8960_PGAR_RINPUT2 = 0 +WM8960_PGAR_RINPUT3 = 1 +WM8960_PGAR_VMID = 2 + +# Mic (aka PGA) BOOST gain options +WM8960_MIC_BOOST_GAIN_0DB = 0 +WM8960_MIC_BOOST_GAIN_13DB = 1 +WM8960_MIC_BOOST_GAIN_20DB = 2 +WM8960_MIC_BOOST_GAIN_29DB = 3 + +''' +Boost Mixer gain options +These are used to control the gain (aka volume) at the following settings: +LIN2BOOST +LIN3BOOST +RIN2BOOST +RIN3BOOST +''' +WM8960_BOOST_MIXER_GAIN_MUTE = 0 +WM8960_BOOST_MIXER_GAIN_NEG_12DB = 1 +WM8960_BOOST_MIXER_GAIN_NEG_9DB = 2 +WM8960_BOOST_MIXER_GAIN_NEG_6DB = 3 +WM8960_BOOST_MIXER_GAIN_NEG_3DB = 4 +WM8960_BOOST_MIXER_GAIN_0DB = 5 +WM8960_BOOST_MIXER_GAIN_3DB = 6 +WM8960_BOOST_MIXER_GAIN_6DB = 7 + +''' +Output Mixer gain options +These are used to control the gain (aka volume) at the following settings: +LI2LOVOL +LB2LOVOL +RI2LOVOL +RB2LOVOL +These are useful as analog bypass signal path options. +''' +WM8960_OUTPUT_MIXER_GAIN_0DB = 0 +WM8960_OUTPUT_MIXER_GAIN_NEG_3DB = 1 +WM8960_OUTPUT_MIXER_GAIN_NEG_6DB = 2 +WM8960_OUTPUT_MIXER_GAIN_NEG_9DB = 3 +WM8960_OUTPUT_MIXER_GAIN_NEG_12DB = 4 +WM8960_OUTPUT_MIXER_GAIN_NEG_15DB = 5 +WM8960_OUTPUT_MIXER_GAIN_NEG_18DB = 6 +WM8960_OUTPUT_MIXER_GAIN_NEG_21DB = 7 + +# Mic Bias voltage options +WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD = 0 +WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD = 1 + +# SYSCLK divide +WM8960_SYSCLK_DIV_BY_1 = 0 +WM8960_SYSCLK_DIV_BY_2 = 2 +WM8960_CLKSEL_MCLK = 0 +WM8960_CLKSEL_PLL = 1 +WM8960_PLL_MODE_INTEGER = 0 +WM8960_PLL_MODE_FRACTIONAL = 1 +WM8960_PLLPRESCALE_DIV_1 = 0 +WM8960_PLLPRESCALE_DIV_2 = 1 + +# Class d clock divide +WM8960_DCLKDIV_16 = 7 + +# Word length settings (aka bits per sample) +# Audio Data Word Length +WM8960_WL_16BIT = 0 +WM8960_WL_20BIT = 1 +WM8960_WL_24BIT = 2 +WM8960_WL_32BIT = 3 + +''' +Additional Digital Audio Interface controls +LRP (aka left-right-polarity) +Right, left and I2S modes – LRCLK polarity +0 = normal LRCLK polarity +1 = inverted LRCLK polarity +''' +WM8960_LR_POLARITY_NORMAL = 0 +WM8960_LR_POLARITY_INVERT = 1 + +''' +ALRSWAP (aka ADC left/right swap) +Left/Right ADC channel swap +1 = Swap left and right ADC data in audio interface +0 = Output left and right data as normal +''' +WM8960_ALRSWAP_NORMAL = 0 +WM8960_ALRSWAP_SWAP = 1 + +# Gain mins, maxes, offsets and step-sizes for all the amps within the codec. +WM8960_PGA_GAIN_MIN = -17.25 +WM8960_PGA_GAIN_MAX = 30.00 +WM8960_PGA_GAIN_OFFSET = 17.25 +WM8960_PGA_GAIN_STEPSIZE = 0.75 +WM8960_HP_GAIN_MIN = -73.00 +WM8960_HP_GAIN_MAX = 6.00 +WM8960_HP_GAIN_OFFSET = 121.00 +WM8960_HP_GAIN_STEPSIZE = 1.00 +WM8960_SPEAKER_GAIN_MIN = -73.00 +WM8960_SPEAKER_GAIN_MAX = 6.00 +WM8960_SPEAKER_GAIN_OFFSET = 121.00 +WM8960_SPEAKER_GAIN_STEPSIZE = 1.00 +WM8960_ADC_GAIN_MIN = -97.00 +WM8960_ADC_GAIN_MAX = 30.00 +WM8960_ADC_GAIN_OFFSET = 97.50 +WM8960_ADC_GAIN_STEPSIZE = 0.50 +WM8960_DAC_GAIN_MIN = -97.00 +WM8960_DAC_GAIN_MAX = 30.00 +WM8960_DAC_GAIN_OFFSET = 97.50 +WM8960_DAC_GAIN_STEPSIZE = 0.50 + +# Automatic Level Control Modes +WM8960_ALC_MODE_OFF = 0 +WM8960_ALC_MODE_RIGHT_ONLY = 1 +WM8960_ALC_MODE_LEFT_ONLY = 2 +WM8960_ALC_MODE_STEREO = 3 + +# Automatic Level Control Target Level dB +WM8960_ALC_TARGET_LEVEL_NEG_22_5DB = 0 +WM8960_ALC_TARGET_LEVEL_NEG_21DB = 1 +WM8960_ALC_TARGET_LEVEL_NEG_19_5DB = 2 +WM8960_ALC_TARGET_LEVEL_NEG_18DB = 3 +WM8960_ALC_TARGET_LEVEL_NEG_16_5DB = 4 +WM8960_ALC_TARGET_LEVEL_NEG_15DB = 5 +WM8960_ALC_TARGET_LEVEL_NEG_13_5DB = 6 +WM8960_ALC_TARGET_LEVEL_NEG_12DB = 7 +WM8960_ALC_TARGET_LEVEL_NEG_10_5DB = 8 +WM8960_ALC_TARGET_LEVEL_NEG_9DB = 9 +WM8960_ALC_TARGET_LEVEL_NEG_7_5DB = 10 +WM8960_ALC_TARGET_LEVEL_NEG_6DB = 11 +WM8960_ALC_TARGET_LEVEL_NEG_4_5DB = 12 +WM8960_ALC_TARGET_LEVEL_NEG_3DB = 13 +WM8960_ALC_TARGET_LEVEL_NEG_1_5DB = 14 + +# Automatic Level Control Max Gain Level dB +WM8960_ALC_MAX_GAIN_LEVEL_NEG_12DB = 0 +WM8960_ALC_MAX_GAIN_LEVEL_NEG_6DB = 1 +WM8960_ALC_MAX_GAIN_LEVEL_0DB = 2 +WM8960_ALC_MAX_GAIN_LEVEL_6DB = 3 +WM8960_ALC_MAX_GAIN_LEVEL_12DB = 4 +WM8960_ALC_MAX_GAIN_LEVEL_18DB = 5 +WM8960_ALC_MAX_GAIN_LEVEL_24DB = 6 +WM8960_ALC_MAX_GAIN_LEVEL_30DB = 7 + +# Automatic Level Control Min Gain Level dB +WM8960_ALC_MIN_GAIN_LEVEL_NEG_17_25DB = 0 +WM8960_ALC_MIN_GAIN_LEVEL_NEG_11_25DB = 1 +WM8960_ALC_MIN_GAIN_LEVEL_NEG_5_25DB = 2 +WM8960_ALC_MIN_GAIN_LEVEL_0_75DB = 3 +WM8960_ALC_MIN_GAIN_LEVEL_6_75DB = 4 +WM8960_ALC_MIN_GAIN_LEVEL_12_75DB = 5 +WM8960_ALC_MIN_GAIN_LEVEL_18_75DB = 6 +WM8960_ALC_MIN_GAIN_LEVEL_24_75DB = 7 + +# Automatic Level Control Hold Time (MS and SEC) +WM8960_ALC_HOLD_TIME_0MS = 0 +WM8960_ALC_HOLD_TIME_3MS = 1 +WM8960_ALC_HOLD_TIME_5MS = 2 +WM8960_ALC_HOLD_TIME_11MS = 3 +WM8960_ALC_HOLD_TIME_21MS = 4 +WM8960_ALC_HOLD_TIME_43MS = 5 +WM8960_ALC_HOLD_TIME_85MS = 6 +WM8960_ALC_HOLD_TIME_170MS = 7 +WM8960_ALC_HOLD_TIME_341MS = 8 +WM8960_ALC_HOLD_TIME_682MS = 9 +WM8960_ALC_HOLD_TIME_1365MS = 10 +WM8960_ALC_HOLD_TIME_3SEC = 11 +WM8960_ALC_HOLD_TIME_5SEC = 12 +WM8960_ALC_HOLD_TIME_10SEC = 13 +WM8960_ALC_HOLD_TIME_23SEC = 14 +WM8960_ALC_HOLD_TIME_44SEC = 15 + +# Automatic Level Control Decay Time (MS and SEC) +WM8960_ALC_DECAY_TIME_24MS = 0 +WM8960_ALC_DECAY_TIME_48MS = 1 +WM8960_ALC_DECAY_TIME_96MS = 2 +WM8960_ALC_DECAY_TIME_192MS = 3 +WM8960_ALC_DECAY_TIME_384MS = 4 +WM8960_ALC_DECAY_TIME_768MS = 5 +WM8960_ALC_DECAY_TIME_1536MS = 6 +WM8960_ALC_DECAY_TIME_3SEC = 7 +WM8960_ALC_DECAY_TIME_6SEC = 8 +WM8960_ALC_DECAY_TIME_12SEC = 9 +WM8960_ALC_DECAY_TIME_24SEC = 10 + +# Automatic Level Control Attack Time (MS and SEC) +WM8960_ALC_ATTACK_TIME_6MS = 0 +WM8960_ALC_ATTACK_TIME_12MS = 1 +WM8960_ALC_ATTACK_TIME_24MS = 2 +WM8960_ALC_ATTACK_TIME_482MS = 3 +WM8960_ALC_ATTACK_TIME_964MS = 4 +WM8960_ALC_ATTACK_TIME_1928MS = 5 +WM8960_ALC_ATTACK_TIME_3846MS = 6 +WM8960_ALC_ATTACK_TIME_768MS = 7 +WM8960_ALC_ATTACK_TIME_1536MS = 8 +WM8960_ALC_ATTACK_TIME_3SEC = 9 +WM8960_ALC_ATTACK_TIME_6SEC = 10 + +# Speaker Boost Gains (DC and AC) +WM8960_SPEAKER_BOOST_GAIN_0DB = 0 +WM8960_SPEAKER_BOOST_GAIN_2_1DB = 1 +WM8960_SPEAKER_BOOST_GAIN_2_9DB = 2 +WM8960_SPEAKER_BOOST_GAIN_3_6DB = 3 +WM8960_SPEAKER_BOOST_GAIN_4_5DB = 4 +WM8960_SPEAKER_BOOST_GAIN_5_1DB = 5 + +# VMIDSEL settings +WM8960_VMIDSEL_DISABLED = 0 +WM8960_VMIDSEL_2X50KOHM = 1 +WM8960_VMIDSEL_2X250KOHM = 2 +WM8960_VMIDSEL_2X5KOHM = 3 + +''' +VREF to Analogue Output Resistance +(Disabled Outputs) +0 = 500 VMID to output +1 = 20k VMID to output +''' +WM8960_VROI_500 = 0 +WM8960_VROI_20K = 1 + +''' +Analogue Bias Optimisation +00 = Reserved +01 = Increased bias current optimized for +AVDD=2.7V +1X = Lowest bias current, optimized for +AVDD=3.3V +''' + +WM8960_VSEL_INCREASED_BIAS_CURRENT = 1 +WM8960_VSEL_LOWEST_BIAS_CURRENT = 3 + +WM8960_REGISTER_DEFAULTS = [ + 0x0097, # R0 (0x00) + 0x0097, # R1 (0x01) + 0x0000, # R2 (0x02) + 0x0000, # R3 (0x03) + 0x0000, # R4 (0x04) + 0x0008, # F5 (0x05) + 0x0000, # R6 (0x06) + 0x000A, # R7 (0x07) + 0x01C0, # R8 (0x08) + 0x0000, # R9 (0x09) + 0x00FF, # R10 (0x0a) + 0x00FF, # R11 (0x0b) + 0x0000, # R12 (0x0C) RESERVED + 0x0000, # R13 (0x0D) RESERVED + 0x0000, # R14 (0x0E) RESERVED + 0x0000, # R15 (0x0F) RESERVED + 0x0000, # R16 (0x10) + 0x007B, # R17 (0x11) + 0x0100, # R18 (0x12) + 0x0032, # R19 (0x13) + 0x0000, # R20 (0x14) + 0x00C3, # R21 (0x15) + 0x00C3, # R22 (0x16) + 0x01C0, # R23 (0x17) + 0x0000, # R24 (0x18) + 0x0000, # R25 (0x19) + 0x0000, # R26 (0x1A) + 0x0000, # R27 (0x1B) + 0x0000, # R28 (0x1C) + 0x0000, # R29 (0x1D) + 0x0000, # R30 (0x1E) RESERVED + 0x0000, # R31 (0x1F) RESERVED + 0x0100, # R32 (0x20) + 0x0100, # R33 (0x21) + 0x0050, # R34 (0x22) + 0x0000, # R35 (0x23) RESERVED + 0x0000, # R36 (0x24) RESERVED + 0x0050, # R37 (0x25) + 0x0000, # R38 (0x26) + 0x0000, # R39 (0x27) + 0x0000, # R40 (0x28) + 0x0000, # R41 (0x29) + 0x0040, # R42 (0x2A) + 0x0000, # R43 (0x2B) + 0x0000, # R44 (0x2C) + 0x0050, # R45 (0x2D) + 0x0050, # R46 (0x2E) + 0x0000, # R47 (0x2F) + 0x0002, # R48 (0x30) + 0x0037, # R49 (0x31) + 0x0000, # R50 (0x32) RESERVED + 0x0080, # R51 (0x33) + 0x0008, # R52 (0x34) + 0x0031, # R53 (0x35) + 0x0026, # R54 (0x36) + 0x00e9, # R55 (0x37) +] class WM8960: - def __init__(self, i2c_bus, address=0x1a): - self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) + def __init__(self, i2c_bus:I2C, address:int = WM8960_ADDR): + self.i2c_device = I2CDevice(i2c_bus, address) self._buf = bytearray(2) - def _write_reg(self, reg, value): + ''' + The WM8960 does not support I2C reads + This means we must keep a local copy of all the register values + We will instantiate with default values + As we write to the device, we will also make sure + To update our local copy as well, stored here in this array. + Each register is 9-bits, so we will store them as a uint16_t + They are in order from R0-R55, and we even keep blank spots for the + "reserved" registers. This way we can use the register address macro + defines above to easiy access each local copy of each register. + Example: _registerLocalCopy[WM8960_REG_LEFT_INPUT_VOLUME] + ''' + self._registerLocalCopy = [ + 0x0097, # R0 (0x00) + 0x0097, # R1 (0x01) + 0x0000, # R2 (0x02) + 0x0000, # R3 (0x03) + 0x0000, # R4 (0x04) + 0x0008, # F5 (0x05) + 0x0000, # R6 (0x06) + 0x000A, # R7 (0x07) + 0x01C0, # R8 (0x08) + 0x0000, # R9 (0x09) + 0x00FF, # R10 (0x0a) + 0x00FF, # R11 (0x0b) + 0x0000, # R12 (0x0C) RESERVED + 0x0000, # R13 (0x0D) RESERVED + 0x0000, # R14 (0x0E) RESERVED + 0x0000, # R15 (0x0F) RESERVED + 0x0000, # R16 (0x10) + 0x007B, # R17 (0x11) + 0x0100, # R18 (0x12) + 0x0032, # R19 (0x13) + 0x0000, # R20 (0x14) + 0x00C3, # R21 (0x15) + 0x00C3, # R22 (0x16) + 0x01C0, # R23 (0x17) + 0x0000, # R24 (0x18) + 0x0000, # R25 (0x19) + 0x0000, # R26 (0x1A) + 0x0000, # R27 (0x1B) + 0x0000, # R28 (0x1C) + 0x0000, # R29 (0x1D) + 0x0000, # R30 (0x1E) RESERVED + 0x0000, # R31 (0x1F) RESERVED + 0x0100, # R32 (0x20) + 0x0100, # R33 (0x21) + 0x0050, # R34 (0x22) + 0x0000, # R35 (0x23) RESERVED + 0x0000, # R36 (0x24) RESERVED + 0x0050, # R37 (0x25) + 0x0000, # R38 (0x26) + 0x0000, # R39 (0x27) + 0x0000, # R40 (0x28) + 0x0000, # R41 (0x29) + 0x0040, # R42 (0x2A) + 0x0000, # R43 (0x2B) + 0x0000, # R44 (0x2C) + 0x0050, # R45 (0x2D) + 0x0050, # R46 (0x2E) + 0x0000, # R47 (0x2F) + 0x0002, # R48 (0x30) + 0x0037, # R49 (0x31) + 0x0000, # R50 (0x32) RESERVED + 0x0080, # R51 (0x33) + 0x0008, # R52 (0x34) + 0x0031, # R53 (0x35) + 0x0026, # R54 (0x36) + 0x00e9, # R55 (0x37) + ] + + def isConnected(self) -> bool: + # TODO: Check I2C or I2CDevice + return True + + ''' + Necessary for all other functions of the CODEC + VREF is a single bit we can flip in Register 25 (19h), WM8960_REG_PWR_MGMT_1 + VREF is bit 6, 0 = power down, 1 = power up + Returns 1 if successful, 0 if something failed (I2C error) + ''' + def enableVREF(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 1) + + ''' + Use this to save power + VREF is a single bit we can flip in Register 25 (19h), WM8960_REG_PWR_MGMT_1 + VREF is bit 6, 0 = power down, 1 = power up + Returns 1 if successful, 0 if something failed (I2C error) + ''' + def disableVREF(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 0) + + # Resets all registers to their default state + def reset(self) -> None: + # Doesn't matter which bit we flip, writing anything will cause the reset + self._writeRegisterBit(WM8960_REG_RESET, 7, 1) + # Update our local copy of the registers to reflect the reset + for i in range(len(WM8960_REGISTER_DEFAULTS)): + self._registerLocalCopy[i] = WM8960_REGISTER_DEFAULTS[i] + + def enableAINL(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 1) + def disableAINL(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 0) + + def enableAINR(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 1) + def disableAINR(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 0) + + def enableLMIC(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) + def disableLMIC(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) + + def enableRMIC(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) + def disableRMIC(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + + def enableLMICBOOST(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) + def disableLMICBOOST(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) + + def enableRMICBOOST(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) + def disableRMICBOOST(self) -> None: + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + + # PGA input signal select + # Each PGA (left and right) has a switch on its non-inverting input. + # On PGA_LEFT: + # You can select between VMID, LINPUT2 or LINPUT3 + # Note, the inverting input of PGA_LEFT is perminantly connected to + # LINPUT1 + # On PGA_RIGHT: + # You can select between VMIN, RINPUT2 or RINPUT3 + # Note, the inverting input of PGA_RIGHT is perminantly connected to + # RINPUT1 + + # 3 options: WM8960_PGAL_LINPUT2, WM8960_PGAL_LINPUT3, WM8960_PGAL_VMID + def pgaLeftNonInvSignalSelect(self, signal:int): + ''' + Clear LMP2 and LMP3 + Necessary because the previous setting could have either set, + And we don't want to confuse the codec. + Only 1 input can be selected. + ''' + + # LMP3 + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 0) + + # LMP2 + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 0) + + if signal == WM8960_PGAL_LINPUT2: + # LMP2 + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 1) + elif signal == WM8960_PGAL_LINPUT3: + # LMP3 + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 1) + elif signal == WM8960_PGAL_VMID: + # Don't set any bits. When both LMP2 and LMP3 are cleared, then the signal is set to VMID + pass + + # 3 options: WM8960_PGAR_RINPUT2, WM8960_PGAR_RINPUT3, WM8960_PGAR_VMID + def pgaRightNonInvSignalSelect(self, signal:int): + ''' + Clear RMP2 and RMP3 + Necessary because the previous setting could have either set, + And we don't want to confuse the codec. + Only 1 input can be selected. + ''' + + # RMP3 + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 0) + + # RMP2 + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 0) + + if signal == WM8960_PGAR_RINPUT2: + # RMP2 + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 1) + elif signal == WM8960_PGAR_RINPUT3: + # RMP3 + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 1) + elif signal == WM8960_PGAR_VMID: + # Don't set any bits. When both RMP2 and RMP3 are cleared, then the signal is set to VMID + pass + + # Connections from each INPUT1 to the inverting input of its PGA + + # Connect LINPUT1 to inverting input of Left Input PGA + def connectLMN1(self): + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 1) + + # Disconnect LINPUT1 from inverting input of Left Input PGA + def disconnectLMN1(self): + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 0) + + # Connect RINPUT1 to inverting input of Right Input PGA + def connectRMN1(self): + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 1) + + # Disconnect RINPUT1 from inverting input of Right Input PGA + def disconnectRMN1(self): + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 0) + + # Connection from output of PGAs to downstream "boost mixers" + + # Connect Left Input PGA to Left Input Boost mixer + def connectLMIC2B(self): + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 1) + + # Disconnect Left Input PGA to Left Input Boost mixer + def disconnectLMIC2B(self): + self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 0) + + # Connect Right Input PGA to Right Input Boost mixer + def connectRMIC2B(self): + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 1) + + # Disconnect Right Input PGA to Right Input Boost mixer + def disconnectRMIC2B(self): + self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 0) + + # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) + def setLINVOL(self, volume:int): + # Limit incoming values max + if volume > 63: + volume = 63 + self._writeRegisterMultiBits(WM8960_REG_LEFT_INPUT_VOLUME, 5, 0, volume) + self.pgaLeftIPVUSet() + + ''' + Sets the volume of the PGA input buffer amp to a specified dB value + passed in as a float argument. + Valid dB settings are -17.25 up to +30.00 + -17.25 = -17.25dB (MIN) + ... 0.75dB steps ... + 30.00 = +30.00dB (MAX) + ''' + def setLINVOLDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setLINVOL() + volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) + self.setLINVOL(volume) + + # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) + def setRINVOL(self, volume:int): + # Limit incoming values max + if volume > 63: + volume = 63 + self._writeRegisterMultiBits(WM8960_REG_RIGHT_INPUT_VOLUME, 5, 0, volume) + self.pgaRightIPVUSet() + + ''' + Sets the volume of the PGA input buffer amp to a specified dB value + passed in as a float argument. + Valid dB settings are -17.25 up to +30.00 + -17.25 = -17.25dB (MIN) + ... 0.75dB steps ... + 30.00 = +30.00dB (MAX) + ''' + def setRINVOLDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setLINVOL() + volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) + self.setRINVOL(volume) + + # Zero Cross prevents zipper sounds on volume changes + # Sets both left and right PGAs + def enablePgaZeroCross(self): + self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 1) + self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 1) + def disablePgaZeroCross(self): + self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 0) + self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 0) + + def enableLINMUTE(self): + self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 1) + def disableLINMUTE(self): + self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 0) + self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) + + def enableRINMUTE(self): + self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 1) + def disableRINMUTE(self): + self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 0) + self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) + + # Causes left and right input PGA volumes to be updated + # (LINVOL and RINVOL) + def pgaLeftIPVUSet(self): + self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) + + # Causes left and right input PGA volumes to be updated + # (LINVOL and RINVOL) + def pgaRightIPVUSet(self): + self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) + + # Boosts + + # WM8960_MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB + def setLMICBOOST(self, boost_gain:int): + # Limit incoming values max + if boost_gain > 3: + boost_gain = 3 + self._writeRegisterMultiBits(WM8960_REG_ADCL_SIGNAL_PATH, 5, 4, boost_gain) + + # WM8960_MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB + def setRMICBOOST(self, boost_gain:int): + # Limit incoming values max + if boost_gain > 3: + boost_gain = 3 + self._writeRegisterMultiBits(WM8960_REG_ADCR_SIGNAL_PATH, 5, 4, boost_gain) + + # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + def setLIN3BOOST(self, boost_gain:int): + # Limit incoming values max + if boost_gain > 7: + boost_gain = 7 + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) + + # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + def setLIN2BOOST(self, boost_gain:int): + # Limit incoming values max + if boost_gain > 7: + boost_gain = 7 + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) + + # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + def setRIN3BOOST(self, boost_gain:int): + # Limit incoming values max + if boost_gain > 7: + boost_gain = 7 + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) + + # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + def setRIN2BOOST(self, boost_gain:int): + # Limit incoming values max + if boost_gain > 7: + boost_gain = 7 + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) + + # Mic Bias control + def enableMicBias(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 1) + def disableMicBias(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 0) + + # WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) + # or WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) + def setMicBiasVoltage(self, voltage:bool): + self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_4, 0, voltage) + + ## ADC + + def enableAdcLeft(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 1) + def disableAdcLeft(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 0) + + def enableAdcRight(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 1) + def disableAdcRight(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 0) + + # ADC digital volume + # Note, also needs to handle control of the ADCVU bits (volume update). + # Valid inputs are 0-255 + # 0 = mute + # 1 = -97dB + # ... 0.5dB steps up to + # 195 = 0dB + # 255 = +30dB + def setAdcLeftDigitalVolume(self, volume:int): + self._writeRegisterMultiBits(WM8960_REG_LEFT_ADC_VOLUME, 7, 0, volume) + self.adcLeftADCVUSet() + def setAdcRightDigitalVolume(self, volume:int): + self._writeRegisterMultiBits(WM8960_REG_RIGHT_ADC_VOLUME, 7, 0, volume) + self.adcRightADCVUSet() + + ''' + ADC digital volume DB + Sets the volume of the ADC to a specified dB value passed in as a float + argument. + Valid dB settings are -97.00 up to +30.0 (0.5dB steps) + -97.50 (or lower) = MUTE + -97.00 = -97.00dB (MIN) + ... 0.5dB steps ... + 30.00 = +30.00dB (MAX) + ''' + def setAdcLeftDigitalVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() + volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) + self.setAdcLeftDigitalVolume(volume) + def setAdcRightDigitalVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setAdcRightDigitalVolume() + volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) + self.setAdcRightDigitalVolume(volume) + + # Causes left and right input ADC volumes to be updated + def adcLeftADCVUSet(self): + self._writeRegisterBit(WM8960_REG_LEFT_ADC_VOLUME, 8, 1) + + # Causes left and right input ADC volumes to be updated + def adcRightADCVUSet(self): + self._writeRegisterBit(WM8960_REG_RIGHT_ADC_VOLUME, 8, 1) + + ## ALC + + # Automatic Level Control + # Note that when the ALC function is enabled, the settings of + # Registers 0 and 1 (LINVOL, IPVU, LIZC, LINMUTE, RINVOL, RIZC and + # RINMUTE) are ignored. + + # Also sets alc sample rate to match global sample rate. + def enableAlc(self, mode:int = WM8960_ALC_MODE_STEREO): + bit8 = mode >> 1 + bit7 = mode & 0x1 + self._writeRegisterBit(WM8960_REG_ALC1, 8, bit8) + self._writeRegisterBit(WM8960_REG_ALC1, 7, bit7) + + def disableAlc(self): + self._writeRegisterBit(WM8960_REG_ALC1, 8, 0) + self._writeRegisterBit(WM8960_REG_ALC1, 7, 0) + + # Valid inputs are 0-15 + # 0 = -22.5dB FS ... 1.5dB steps ... 15 = -1.5dB FS + def setAlcTarget(self, target:int): + # Limit incoming values max + if target > 15: + target = 15 + self._writeRegisterMultiBits(WM8960_REG_ALC1,3,0,target) + + # Valid inputs are 0-10, 0 = 24ms, 1 = 48ms ... 10 = 24.58seconds + def setAlcDecay(self, decay:int): + # Limit incoming values max + if decay > 10: + decay = 10 + self._writeRegisterMultiBits(WM8960_REG_ALC3, 7, 4, decay) + + # Valid inputs are 0-10, 0 = 6ms, 1 = 12ms, 2 = 24ms ... + # 10 = 6.14seconds + def setAlcAttack(self, attack:int): + # Limit incoming values max + if attack > 10: + attack = 10 + self._writeRegisterMultiBits(WM8960_REG_ALC3, 3, 0, attack) + + # Valid inputs are 0-7, 0 = -12dB, ... 7 = +30dB + def setAlcMaxGain(self, maxGain:int): + # Limit incoming values max + if maxGain > 7: + maxGain = 7 + self._writeRegisterMultiBits(WM8960_REG_ALC1, 6, 4, maxGain) + + # Valid inputs are 0-7, 0 = -17.25dB, ... 7 = +24.75dB + def setAlcMinGain(self, minGain:int): + # Limit incoming values max + if minGain > 7: + minGain = 7 + self._writeRegisterMultiBits(WM8960_REG_ALC2, 6, 4, minGain) + + # Valid inputs are 0-15, 0 = 0ms, ... 15 = 43.691s + def setAlcHold(self, hold:int): + # Limit incoming values max + if hold > 15: + hold = 15 + self._writeRegisterMultiBits(WM8960_REG_ALC2, 3, 0, hold) + + # Peak Limiter + def enablePeakLimiter(self): + self._writeRegisterBit(WM8960_REG_ALC3, 8, 1) + + def disablePeakLimiter(self): + self._writeRegisterBit(WM8960_REG_ALC3, 8, 0) + + # Noise Gate + def enableNoiseGate(self): + self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 1) + def disableNoiseGate(self): + self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 0) + + # 0-31, 0 = -76.5dBfs, 31 = -30dBfs + def setNoiseGateThreshold(self, threshold:int): + # TODO + pass + + ## DAC + + # Enable/disble each channel + def enableDacLeft(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 1) + def disableDacLeft(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 0) + + def enableDacRight(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 1) + def disableDacRight(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 0) + + # DAC digital volume + # Valid inputs are 0-255 + # 0 = mute + # 1 = -127dB + # ... 0.5dB steps up to + # 255 = 0dB + def setDacLeftDigitalVolume(self, volume:int): + self._writeRegisterMultiBits(WM8960_REG_LEFT_DAC_VOLUME, 7, 0, volume) + self.dacLeftDACVUSet() + + def setDacRightDigitalVolume(self, volume:int): + self._writeRegisterMultiBits(WM8960_REG_RIGHT_DAC_VOLUME, 7, 0, volume) + self.dacRightDACVUSet() + + ''' + DAC digital volume DB + Sets the volume of the DAC to a specified dB value passed in as a float argument. + Valid dB settings are -97.00 up to +30.0 (0.5dB steps) + -97.50 (or lower) = MUTE + -97.00 = -97.00dB (MIN) + ... 0.5dB steps ... + 30.00 = +30.00dB (MAX) + ''' + def setDacLeftDigitalVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setDacLeftDigitalVolume() + volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) + self.setDacLeftDigitalVolume(volume) + + def setDacRightDigitalVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() + volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) + self.setDacRightDigitalVolume(volume) + + # Causes left and right input DAC volumes to be updated + def dacLeftDACVUSet(self): + self._writeRegisterBit(WM8960_REG_LEFT_DAC_VOLUME, 8, 1) + + # Causes left and right input DAC volumes to be updated + def dacRightDACVUSet(self): + self._writeRegisterBit(WM8960_REG_RIGHT_DAC_VOLUME, 8, 1) + + # DAC mute + def enableDacMute(self): + self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 1) + def disableDacMute(self): + self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 0) + + # DE-Emphasis + + # 3D Stereo Enhancement + # 3D enable/disable + def enable3d(self): + self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 1) + def disable3d(self): + self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 0) + def set3dDepth(self, depth:int): # 0 = 0%, 15 = 100% + # Limit incoming values max + if depth > 15: + depth = 15 + self._writeRegisterMultiBits(WM8960_REG_3D_CONTROL, 4, 1, depth) + + # 3D upper/lower cut-off frequencies. + + # DAC output -6dB attentuation enable/disable + def enableDac6dbAttenuation(self): + self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 1) + def disableDac6dbAttentuation(self): + self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 0) + + ## OUTPUT mixers + + # What's connected to what? Oh so many options... + # LOMIX Left Output Mixer + # ROMIX Right Output Mixer + # OUT3MIX Mono Output Mixer + + # Enable/disable left and right output mixers + def enableLOMIX(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 1) + def disableLOMIX(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 0) + + def enableROMIX(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 1) + def disableROMIX(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 0) + + def enableOUT3MIX(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 1) + def disableOUT3MIX(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 0) + + # Enable/disable audio path connections/vols to/from output mixers + # See datasheet page 35 for a nice image of all the connections. + + def enableLI2LO(self): + self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 1) + def disableLI2LO(self): + self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 0) + + # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB + def setLI2LOVOL(self, volume:int): + self._writeRegisterMultiBits(WM8960_REG_LEFT_OUT_MIX_1, 6, 4, volume) + + def enableLB2LO(self): + self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 1) + def disableLB2LO(self): + self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 0) + + # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB + def setLB2LOVOL(self, volume:int): + # Limit incoming values max + if volume > 7: + volume = 7 + self._writeRegisterMultiBits(WM8960_REG_BYPASS_1, 6, 4, volume) + + def enableLD2LO(self): + self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 1) + def disableLD2LO(self): + self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 0) + + def enableRI2RO(self): + self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 1) + def disableRI2RO(self): + self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 0) + + # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB + def setRI2ROVOL(self, volume:int): + # Limit incoming values max + if volume > 7: + volume = 7 + self._writeRegisterMultiBits(WM8960_REG_RIGHT_OUT_MIX_2, 6, 4, volume) + + def enableRB2RO(self): + self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 1) + def disableRB2RO(self): + self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 0) + + # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB + def setRB2ROVOL(self, volume:int): + # Limit incoming values max + if volume > 7: + volume = 7 + self._writeRegisterMultiBits(WM8960_REG_BYPASS_2, 6, 4, volume) + + def enableRD2RO(self): + self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 1) + def disableRD2RO(self): + self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 0) + + # Mono Output mixer. + # Note, for capless HPs, we'll want this to output a buffered VMID. + # To do this, we need to disable both of these connections. + def enableLI2MO(self): + self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 1) + def disableLI2MO(self): + self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 0) + + def enableRI2MO(self): + self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 1) + def disableRI2MO(self): + self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 0) + + # Sets the VMID signal to one of three possible settings. + # 4 options: + # WM8960_VMIDSEL_DISABLED + # WM8960_VMIDSEL_2X50KOHM (playback / record) + # WM8960_VMIDSEL_2X250KOHM (for low power / standby) + # WM8960_VMIDSEL_2X5KOHM (for fast start-up) + def setVMID(self, setting:int = WM8960_VMIDSEL_2X50KOHM): + self._writeRegisterMultiBits(WM8960_REG_PWR_MGMT_1, 8, 7, setting) + + # Enables VMID in the WM8960_REG_PWR_MGMT_2 register, and set's it to + # playback/record settings of 2*50Kohm. + # Note, this function is only here for backwards compatibility with the + # original releases of this library. It is recommended to use the + # setVMID() function instead. + def enableVMID(self): + self.setVMID(WM8960_VMIDSEL_2X50KOHM) + def disableVMID(self): + self.setVMID(WM8960_VMIDSEL_DISABLED) + + # This will disable both connections, thus enable VMID on OUT3. Note, + # to enable VMID, you also need to enable OUT3 in the + # WM8960_REG_PWR_MGMT_2 [1] + def enableOUT3asVMID(self): + self.disableLI2MO() + self.disableRI2MO() + self.enableOUT3MIX() + self.enableVMID() + + ## Headphones + + # Enable and disable headphones (mute) + def enableHeadphones(self): + self.enableRightHeadphone() + self.enableLeftHeadphone() + def disableHeadphones(self): + self.disableRightHeadphone() + self.disableLeftHeadphone() + + def enableRightHeadphone(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 1) + def disableRightHeadphone(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 0) + def enableLeftHeadphone(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 1) + def disableLeftHeadphone(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 0) + + def enableHeadphoneStandby(self): + self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 1) + def disableHeadphoneStandby(self): + self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 0) + + # Set headphone volume + # Although you can control each headphone output independently, here + # we are going to assume you want both left and right to do the same + # thing. + + # Valid inputs are 47-127. 0-47 = mute, 48 = -73dB ... 1dB steps ... + # 127 = +6dB + def setHeadphoneVolume(self, volume:int): + # Updates both left and right channels + # Handles the OUT1VU (volume update) bit control, so that it happens at the + # same time on both channels. Note, we must also make sure that the outputs + # are enabled in the WM8960_REG_PWR_MGMT_2 [6:5] + # Grab local copy of register + # Modify the bits we need to + # Write register in device, including the volume update bit write + # If successful, save locally. + + # Limit inputs + if volume > 127: + volume = 127 + + # LEFT + self._writeRegisterMultiBits(WM8960_REG_LOUT1_VOLUME, 6, 0, volume) + + # RIGHT + self._writeRegisterMultiBits(WM8960_REG_ROUT1_VOLUME, 6, 0, volume) + + # UPDATES + # Updated left channel + self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 8, 1) + + # Updated right channel + self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 8, 1) + + # Set headphone volume dB + # Sets the volume of the headphone output buffer amp to a speicified + # dB value passed in as a float argument. + # Valid dB settings are -74.0 up to +6.0 + # User input will be rounded to nearest whole integer + # -74 (or lower) = MUTE + # -73 = -73dB (MIN) + # ... 1dB steps ... + # 0 = 0dB + # ... 1dB steps ... + # 6 = +6dB (MAX) + def setHeadphoneVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setHeadphoneVolume() + volume = self.convertDBtoSetting(dB, WM8960_HP_GAIN_OFFSET, WM8960_HP_GAIN_STEPSIZE, WM8960_HP_GAIN_MIN, WM8960_HP_GAIN_MAX) + self.setHeadphoneVolume(volume) + + # Zero Cross prevents zipper sounds on volume changes + # Sets both left and right Headphone outputs + def enableHeadphoneZeroCross(self): + # Left + self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 1) + # Right + self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 1) + + def disableHeadphoneZeroCross(self): + # Left + self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 0) + # Right + self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 0) + + ## Speakers + + # Enable and disable speakers (mute) + def enableSpeakers(self): + self.enableRightSpeaker() + self.enableLeftSpeaker() + def disableSpeakers(self): + self.disableRightSpeaker() + self.disableLeftSpeaker() + + def enableRightSpeaker(self): + # SPK_OP_EN + self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 1) + # SPKR + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 1) + + def disableRightSpeaker(self): + # SPK_OP_EN + self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 0) + # SPKR + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 0) + + def enableLeftSpeaker(self): + # SPK_OP_EN + self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 1) + # SPKL + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 1) + + def disableLeftSpeaker(self): + # SPK_OP_EN + self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 0) + # SPKL + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 0) + + # Set Speaker output volume + # Although you can control each Speaker output independently, here we + # are going to assume you want both left and right to do the same thing. + # Valid inputs are 47-127. 0-47 = mute, 48 = -73dB ... 1dB steps ... + # 127 = +6dB + + def setSpeakerVolume(self, volume:int): + # Updates both left and right channels + # Handles the SPKVU (volume update) bit control, so that it happens at the + # same time on both channels. Note, we must also make sure that the outputs + # are enabled in the WM8960_REG_PWR_MGMT_2 [4:3], and the class D control + # reg WM8960_REG_CLASS_D_CONTROL_1 [7:6] + + # Limit inputs + if volume > 127: + volume = 127 + + # LEFT + self._writeRegisterMultiBits(WM8960_REG_LOUT2_VOLUME, 6, 0, volume) + # RIGHT + self._writeRegisterMultiBits(WM8960_REG_ROUT2_VOLUME, 6, 0, volume) + + # SPKVU + # Updated left channel + self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 8, 1) + # Updated right channel + self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 8, 1) + + def setSpeakerVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setSpeakerVolume() + volume = self.convertDBtoSetting(dB, WM8960_SPEAKER_GAIN_OFFSET, WM8960_SPEAKER_GAIN_STEPSIZE, WM8960_SPEAKER_GAIN_MIN, WM8960_SPEAKER_GAIN_MAX) + self.setSpeakerVolume(volume) + + # Zero Cross prevents zipper sounds on volume changes + # Sets both left and right Speaker outputs + def enableSpeakerZeroCross(self): + # Left + self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 1) + # Right + self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 1) + + def disableSpeakerZeroCross(self): + # Left + self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 0) + # Right + self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 0) + + # DC and AC gain - allows signal to be higher than the DACs swing + # (use only if your SPKVDD is high enough to handle a larger signal) + # Valid inputs are 0-5 + # 0 = +0dB (1.0x boost) ... up to ... 5 = +5.1dB (1.8x boost) + def setSpeakerDcGain(self, gain:int): + # Limit incoming values max + if gain > 5: + gain = 5 + self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 5, 3, gain) + + def setSpeakerAcGain(self, gain:int): + # Limit incoming values max + if gain > 5: + gain = 5 + self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 2, 0, gain) + + ## Digital audio interface control + + # Defaults to I2S, peripheral-mode, 24-bit word length + + # Loopback + # When enabled, the output data from the ADC audio interface is fed + # directly into the DAC data input. + def enableLoopBack(self): + self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 1) + def disableLoopBack(self): + self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 0) + + ## Clock controls + + # Getting the Frequency of SampleRate as we wish + # Our MCLK (an external clock on the SFE breakout board) is 24.0MHz. + # According to table 40 (DS pg 58), we want SYSCLK to be 11.2896 for a + # SR of 44.1KHz. To get that Desired Output (SYSCLK), we need the + # following settings on the PLL stuff: + # As found on table 45 (ds pg 61). + # PRESCALE DIVIDE (PLLPRESCALE): 2 + # POSTSCALE DVIDE (SYSCLKDIV[1:0]): 2 + # FIXED POST-DIVIDE: 4 + # R: 7.5264 + # N: 7h + # K: 86C226h + + # Example at bottom of table 46, shows that we should be in fractional + # mode for a 44.1KHz. + + # In terms of registers, this is what we want for 44.1KHz + # PLLEN=1 (PLL enable) + # PLLPRESCALE=1 (divide by 2) *This get's us from MCLK (24MHz) down + # to 12MHZ for F2 + # PLLN=7h (PLL N value) *this is "int R" + # PLLK=86C226h (PLL K value) *this is int ( 2^24 * (R- intR)) + # SDM=1 (Fractional mode) + # CLKSEL=1 (PLL select) + # MS=0 (Peripheral mode) + # WL=00 (16 bits) + # SYSCLKDIV=2 (Divide by 2) + # ADCDIV=000 (Divide by 1) = 44.1kHz + # DACDIV=000 (Divide by 1) = 44.1kHz + # BCLKDIV=0100 (Divide by 4) = 64fs + # DCLKDIV=111 (Divide by 16) = 705.6kHz + + # And now for the functions that will set these registers... + def enablePLL(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 1) + def disablePLL(self): + self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 0) + + # Valid options are WM8960_PLLPRESCALE_DIV_1, WM8960_PLLPRESCALE_DIV_2 + def setPLLPRESCALE(self, div:bool): + self._writeRegisterBit(WM8960_REG_PLL_N, 4, div) + + def setPLLN(self, n:int): + self._writeRegisterMultiBits(WM8960_REG_PLL_N, 3, 0, n) + + # Send each nibble of 24-bit value for value K + def setPLLK(self, one:int, two:int, three:int): + self._writeRegisterMultiBits(WM8960_REG_PLL_K_1, 5, 0, one) + self._writeRegisterMultiBits(WM8960_REG_PLL_K_2, 8, 0, two) + self._writeRegisterMultiBits(WM8960_REG_PLL_K_3, 8, 0, three) + + # 0=integer, 1=fractional + def setSMD(self, mode:bool): + self._writeRegisterBit(WM8960_REG_PLL_N, 5, mode) + + # 0=MCLK, 1=PLL_output + def setCLKSEL(self, sel:bool): + self._writeRegisterBit(WM8960_REG_CLOCKING_1, 0, sel) + + # (0=divide by 1), (2=div by 2) *1 and 3 are "reserved" + def setSYSCLKDIV(self, div:int): + self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 2, 1, div) + + # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options + def setADCDIV(self, div:int): + self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 8, 6, div) + + # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options + def setDACDIV(self, div:int): + self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 5, 3, div) + + # 0100 (4) = sufficiently high for 24bit, div by 4 allows for max word + # length of 32bit + def setBCLKDIV(self, div:int): + self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 3, 0, div) + + # Class D amp, 111= SYSCLK/16, so 11.2896MHz/16 = 705.6KHz + def setDCLKDIV(self, div:int): + self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 8, 6, div) + + # Set LR clock to be the same for ADC & DAC (needed for loopback mode) + def setALRCGPIO(self): + self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 6, 1) + + def enableMasterMode(self): + self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 1) + + def enablePeripheralMode(self): + self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 0) + + def setWL(self, word_length:int): + self._writeRegisterMultiBits(WM8960_REG_AUDIO_INTERFACE_1, 3, 2, word_length) + + def setLRP(self, polarity:bool): + self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 4, polarity) + + def setALRSWAP(self, swap:bool): + self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 8, swap) + + def setVROI(self, setting:bool): + self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_3, 6, setting) + + def setVSEL(self, setting:int): + self._writeRegisterMultiBits(WM8960_REG_ADDITIONAL_CONTROL_1, 7, 6, setting) + + # General-purpose register write + def writeRegister(self, reg:int, value:int) -> None: self._buf[0] = reg << 1 | value >> 8 self._buf[1] = value & 0xff with self.i2c_device as i2c: i2c.write(self._buf) + self._registerLocalCopy[reg] = value + + # **The WM8960 does not support reading registers!!! + + # Writes a 0 or 1 to the desired bit in the desired register + def _writeRegisterBit(self, registerAddress:int, bitNumber:int, bitValue:bool) -> None: + regvalue = self._registerLocalCopy[registerAddress] + if bitValue: + regvalue |= 1< int: + ''' + Limit incoming dB values to acceptable range. Note, the minimum limit we + want to limit this too is actually one step lower than the minDB, because + that is still an acceptable dB level (it is actually "true mute"). + Note, the PGA amp does not have a "true mute" setting available, so we + must check for its unique minDB of -17.25. + ''' - self._write_reg(0x1a, 0x180) # DAC L + R + # Limit max. This is the same for all amps. + if dB > maxDB: + dB = maxDB - self._write_reg(0x22, 0x100) # Left DAC to Left Mixer - self._write_reg(0x25, 0x100) # Right DAC to Right Mixer + ''' + PGA amp doesn't have mute setting, so minDB should be limited to minDB + Let's check for the PGAs unique minDB (-17.25) to know we are currently + converting a PGA setting. + ''' + if minDB == WM8960_PGA_GAIN_MIN: + if dB < minDB: + dB = minDB + else: # Not PGA. All other amps have a mute setting below minDb + if dB < minDB - stepSize: + dB = minDB - stepSize - self._write_reg(0x2f, 0xc) # L + R Mixer output + ''' + Adjust for offset + Offset is the number that gets us from the minimum dB option of an amp + up to the minimum setting value in the register. + ''' + dB = dB + offset - self._write_reg(0x1a, 0x1e0) # DACL, DACR, LOUT1, ROUT1 + ''' + Find out how many steps we are above the minimum (at this point, our + minimum is "0". Note, because dB comes in as a float, the result of this + division (volume) can be a partial number. We will round that next. + ''' + volume = dB / stepSize + volume = round(volume) # round to the nearest setting value. - # Unmute DAC - self._write_reg(0x5, 0) + return int(volume) & 0xff # cast from float to integer From cfd27d9f6ed8f90e4f78ad3fe5ee5da0c80bffb4 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 07:21:20 -0500 Subject: [PATCH 02/56] Updated copyright in documentation and minor changes. --- .pre-commit-config.yaml | 4 +++ CODE_OF_CONDUCT.md | 70 ++++++++++++++++++++++++----------------- LICENSE | 3 +- README.rst | 2 +- README.rst.license | 1 + pyproject.toml | 12 ++++--- 6 files changed, 58 insertions(+), 34 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ade69..c97e574 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,6 +2,10 @@ # # SPDX-License-Identifier: Unlicense +default_language_version: + # force all unspecified python hooks to run python3.11 + python: python3.11 + repos: - repo: https://github.com/python/black rev: 23.3.0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 01a7515..ddf57b0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,10 +1,10 @@ -# Adafruit Community Code of Conduct +# CircuitPython Community Code of Conduct ## Our Pledge @@ -20,35 +20,52 @@ race, religion, or sexual identity and orientation. We are committed to providing a friendly, safe and welcoming environment for all. -Examples of behavior that contributes to creating a positive environment +Examples of behavior that contributes to creating and maintaining a positive environment include: * Be kind and courteous to others * Using welcoming and inclusive language +* Respecting the identity of every community member, including asking for their + pronouns if uncertain * Being respectful of differing viewpoints and experiences * Collaborating with other community members +* Providing desired assistance and knowledge to other community members +* Being open to new information and ideas * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior by community members include: * The use of sexualized language or imagery and sexual attention or advances * The use of inappropriate images, including in a community member's avatar -* The use of inappropriate language, including in a community member's nickname +* The use of inappropriate language or profanity, including in a community member's nickname * Any spamming, flaming, baiting or other attention-stealing behavior * Excessive or unwelcome helping; answering outside the scope of the question asked * Discussion or promotion of activities or projects that intend or pose a risk of significant harm -* Trolling, insulting/derogatory comments, and personal or political attacks +* Trolling, insulting/derogatory comments, and attacks of any nature (including, + but not limited to, personal or political attacks) * Promoting or spreading disinformation, lies, or conspiracy theories against a person, group, organisation, project, or community * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission +* Engaging in behavior that creates an unwelcoming or uninclusive environment * Other conduct which could reasonably be considered inappropriate +The CircuitPython Community welcomes everyone and strives to create a safe space for all. It is built +around sharing and contributing to technology. We encourage discussing your thoughts, experiences, +and feelings within the scope of the community. However, there are topics that can sometimes stray +from that scope, and can lead to hurting others and create an unwelcoming, uninclusive environment. + +Examples of discussion topics that have been known to stray outside the scope of the CircuitPython +Community include, but are not limited to: + +* Discussions regarding religion and related topics +* Discussions regarding politics and related topics + The goal of the standards and moderation guidelines outlined here is to build and maintain a respectful community. We ask that you don’t just aim to be "technically unimpeachable", but rather try to be your best self. @@ -72,25 +89,21 @@ inappropriate, threatening, offensive, or harmful. ## Moderation -Instances of behaviors that violate the Adafruit Community Code of Conduct +Instances of behaviors that violate the CircuitPython Community Code of Conduct may be reported by any member of the community. Community members are encouraged to report these situations, including situations they witness involving other community members. You may report in the following ways: -In any situation, you may email . - -On the Adafruit Discord, you may send an open message from any channel -to all Community Moderators by tagging @community moderators. You may -also send an open message from any channel, or a direct message to -any Community Moderator. +In any situation, you may email the project maintainer. -Email and direct message reports will be kept confidential. +The source of email and direct message reports will be kept confidential. -In situations on Discord where the issue is particularly offensive, possibly -illegal, requires immediate action, or violates the Discord terms of service, -you should also report the message directly to [Discord](https://discord.com/safety). +In situations on GitHub where the issue is particularly offensive, possibly +illegal, requires immediate action, or violates the GitHub terms of service, +you should also report the message directly to GitHub via the comment, or via +[GitHub Support](https://support.github.com/contact/report-abuse?category=report-abuse&report=other&report_type=unspecified). These are the steps for upholding our community’s standards of conduct. @@ -109,19 +122,18 @@ These are the steps for upholding our community’s standards of conduct. banned. 6. Disciplinary actions (warnings, bans, etc) for Code of Conduct violations apply to the platform where the violation occurred. However, depending on the severity - of the violation, the disciplinary action may be applied across Adafruit's other - community platforms. For example, a severe violation on the Adafruit Discord - server may result in a ban on not only the Adafruit Discord server, but also on - the Adafruit GitHub organisation, Adafruit Forums, Adafruit Twitter, etc. + of the violation, the disciplinary action may be applied across CircuitPython's + other community platforms. For example, a severe violation in one Community forum + may result in a ban on not only the CircuitPython GitHub organisation, + but also on the CircuitPython Twitter, live stream text chats, etc. ## Scope This Code of Conduct and the enforcement policies listed above apply to all -Adafruit Community venues. This includes but is not limited to any community -spaces (both public and private), the entire Adafruit Discord server, and -Adafruit GitHub repositories. Examples of Adafruit Community spaces include -but are not limited to meet-ups, audio chats on the Adafruit Discord, or -interaction at a conference. +CircuitPython Community venues. This includes but is not limited to any community +spaces (both public and private), and CircuitPython GitHub repositories. Examples of +CircuitPython Community spaces include but are not limited to meet-ups, issue +threads on GitHub, text chats during a live stream, or interaction at a conference. This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. As a community @@ -130,11 +142,13 @@ accordingly. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), +This Code of Conduct is adapted from the +[Adafruit Community Code of Conduct](https://github.com/adafruit/Adafruit_Community_Code_of_Conduct), +which is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available on [contributor-covenant.org](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html), and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). -For other projects adopting the Adafruit Community Code of +For other projects adopting the CircuitPython Community Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your diff --git a/LICENSE b/LICENSE index dfb5d22..c4f9866 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ -The MIT License (MIT) +MIT License Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +Copyright (c) 2024 Cooper Dalrymple Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.rst b/README.rst index 81b49d7..43220c3 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ Introduction :target: https://github.com/psf/black :alt: Code Style: Black -Barebones CircuitPython driver for WM8960 Stereo CODEC +CircuitPython driver for WM8960 Stereo CODEC Dependencies diff --git a/README.rst.license b/README.rst.license index bb3baba..1f067fa 100644 --- a/README.rst.license +++ b/README.rst.license @@ -1,3 +1,4 @@ SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple SPDX-License-Identifier: MIT diff --git a/pyproject.toml b/pyproject.toml index 926c046..6cf5bab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ # SPDX-FileCopyrightText: 2022 Alec Delaney, written for Adafruit Industries # SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple # # SPDX-License-Identifier: MIT @@ -12,19 +13,22 @@ requires = [ [project] name = "adafruit-circuitpython-wm8960" -description = "Barebones CircuitPython driver for WM8960 Stereo CODEC" +description = "CircuitPython driver for WM8960 Stereo CODEC" version = "0.0.0+auto.0" readme = "README.rst" authors = [ - {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} + {name = "Adafruit Industries", email = "circuitpython@adafruit.com"}, + {name = "Cooper Dalrymple", email = "me@dcdalrymple.com"} ] -urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960"} +urls = {Homepage = "https://github.com/dcooperdalrymple/Adafruit_CircuitPython_WM8960"} keywords = [ "adafruit", "blinka", "circuitpython", "micropython", - "wm8960", + "codec", + "dac", + "adc", "wm8960", ] license = {text = "MIT"} From 89036d3880a1a49fb000bab4a0af67b1ce019b3e Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 09:19:38 -0500 Subject: [PATCH 03/56] Transferred and converted examples from Arduino library. --- examples/wm8960_01_Volume.py | 96 +++++++++ examples/wm8960_02_INPUT2.py | 88 +++++++++ examples/wm8960_03_INPUT1.py | 101 ++++++++++ examples/wm8960_04_Speaker.py | 118 +++++++++++ examples/wm8960_05_Loopback.py | 135 +++++++++++++ examples/wm8960_06_3D_Enhance.py | 156 +++++++++++++++ examples/wm8960_07_MicBias.py | 61 ++++++ examples/wm8960_08_I2S_Passthrough.py | 171 ++++++++++++++++ examples/wm8960_09_I2S_Bluetooth.py | 119 ++++++++++++ examples/wm8960_10_AdcGain.py | 181 +++++++++++++++++ examples/wm8960_11_VolumePlotter.py | 169 ++++++++++++++++ examples/wm8960_12_AutomaticLevelControl.py | 181 +++++++++++++++++ examples/wm8960_13_DacGain.py | 182 +++++++++++++++++ examples/wm8960_14_ElectretMics.py | 135 +++++++++++++ ..._15_VolumePlotter_MEMS_Mic_Differential.py | 183 ++++++++++++++++++ 15 files changed, 2076 insertions(+) create mode 100644 examples/wm8960_01_Volume.py create mode 100644 examples/wm8960_02_INPUT2.py create mode 100644 examples/wm8960_03_INPUT1.py create mode 100644 examples/wm8960_04_Speaker.py create mode 100644 examples/wm8960_05_Loopback.py create mode 100644 examples/wm8960_06_3D_Enhance.py create mode 100644 examples/wm8960_07_MicBias.py create mode 100644 examples/wm8960_08_I2S_Passthrough.py create mode 100644 examples/wm8960_09_I2S_Bluetooth.py create mode 100644 examples/wm8960_10_AdcGain.py create mode 100644 examples/wm8960_11_VolumePlotter.py create mode 100644 examples/wm8960_12_AutomaticLevelControl.py create mode 100644 examples/wm8960_13_DacGain.py create mode 100644 examples/wm8960_14_ElectretMics.py create mode 100644 examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py diff --git a/examples/wm8960_01_Volume.py b/examples/wm8960_01_Volume.py new file mode 100644 index 0000000..15125c6 --- /dev/null +++ b/examples/wm8960_01_Volume.py @@ -0,0 +1,96 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates analog audio input, volume control, and headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT3" inputs, they are labeled "RIN3" and "LIN3" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. + +It will output the sound on the headphone outputs. +It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3"vand this provides a buffered VMID. + +You can now control the volume of the codecs built in headphone buffers using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT3 ----- TRS INPUT TIP *left audio +RINPUT3 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board, time +import adafruit_wm8960 + +print("Example 1 - Volume") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow through the analog audio bypass connections + +# Enable left output mixer +codec.enableLOMIX() + +# Enable bypass connection from Left INPUT3 to Left output mixer, note, the default gain on this input (LI2LOVOL) is -15dB +codec.enableLI2LO() + +# Sets volume control between "left input" to "left output mixer" +codec.setLI2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +# Now for the right channel of INPUT3 +codec.enableROMIX() +codec.enableRI2RO() +codec.setRI2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Volume set to +6.00dB (max)") +codec.setHeadphoneVolumeDB(6.00) +time.sleep(5.0) + +print("Volume set to +0.00dB") +codec.setHeadphoneVolumeDB(0.00) +time.sleep(5.0) + +print("Volume set to -12.00dB") +codec.setHeadphoneVolumeDB(-12.00) +time.sleep(5.0) + +print("Volume set to -74.00dB, aka MUTE") +codec.setHeadphoneVolumeDB(-74.00) +time.sleep(5.0) + +print("Example complete") diff --git a/examples/wm8960_02_INPUT2.py b/examples/wm8960_02_INPUT2.py new file mode 100644 index 0000000..ec7fdee --- /dev/null +++ b/examples/wm8960_02_INPUT2.py @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates analog audio input (on INPUT2s), sets volume control, and headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT2" inputs, they are labeled "RIN2" and "LIN2" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. + +It will output the sound on the headphone outputs. +It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. + +You can now control the volume of the codecs built in headphone buffers using this function: +codec.setHeadphoneVolumeDB(6.00); Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT2 ----- TRS INPUT TIP *left audio +RINPUT2 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import adafruit_wm8960 + +print("Example 2 - INPUT2") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow through the analog audio bypass connections + +# Set input boosts to get INPUT2 (both left and right) to the boost mixers +codec.setLIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB) +codec.setRIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB) + +# Enable input boost mixers +codec.enableAINL() +codec.enableAINR() + +# Connect LB2LO (booster to output mixer [aka analog bypass]) +codec.enableLB2LO() +codec.enableRB2RO() + +# Set gainstage between boost mixer and output mixers (analog bypass) +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Example complete. Listen to inputs 2 on headphone outputs.") diff --git a/examples/wm8960_03_INPUT1.py b/examples/wm8960_03_INPUT1.py new file mode 100644 index 0000000..cb68c35 --- /dev/null +++ b/examples/wm8960_03_INPUT1.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates analog audio input (on INPUT1s), sets volume control, and headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. + +It will output the sound on the headphone outputs. It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. + +You can now control the volume of the codecs built in headphone buffers using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import adafruit_wm8960 + +print("Example 3 - INPUT1") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow through the analog audio bypass connections +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Connect LB2LO (booster to output mixer (analog bypass) +codec.enableLB2LO() +codec.enableRB2RO() + +# Set gainstage between booster mixer and output mixer +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Example complete. Listen to INPUT1 on headphone outputs.") diff --git a/examples/wm8960_04_Speaker.py b/examples/wm8960_04_Speaker.py new file mode 100644 index 0000000..d53dc6f --- /dev/null +++ b/examples/wm8960_04_Speaker.py @@ -0,0 +1,118 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates analog audio input (on INPUT1s), sets volume control, and Speaker output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. + +It will output the sound on the Speaker outputs. + +You can now control the volume of the codecs built in class-d amp using this function: +codec.setSpeakerVolumeDB(6.00) +Valid inputs are -73.00 to 6.00 (1.00 dB steps) + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +SL+ --------- Left Speaker + +SL- --------- Left Speaker - +SR+ --------- Right Speaker + +SR- --------- Right Speaker - + +*Note, with a class-d speaker amp like this, you need to connections like above. +Each speaker must be connected to its correct + and -. +You cannot connect the "-" side of the speaker to GND. +You cannot share the "-" side of two speakers (like with common "TRS-wired" +headphones). + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import adafruit_wm8960 + +print("Example 4 - Speaker") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow through the analog audio bypass connections +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Connect LB2LO (booster to output mixer (analog bypass) +codec.enableLB2LO() +codec.enableRB2RO() + +# Set gainstage between booster mixer and output mixer +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h + +codec.enableSpeakers() + +print("Volume set to +0dB") +codec.setSpeakerVolumeDB(0.00) + +print("Example complete. Listen to left/right INPUT1 on Speaker outputs.") diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_05_Loopback.py new file mode 100644 index 0000000..067a31a --- /dev/null +++ b/examples/wm8960_05_Loopback.py @@ -0,0 +1,135 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). +Then send the output of the DAC to the headphone outs. + +You can now control the volume of the codecs built in headphone amp using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import adafruit_wm8960 + +print("Example 5 - Loopback") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Disconnect LB2LO (booster to output mixer (analog bypass) +# For this example, we are going to pass audio throught the ADC and DAC +codec.disableLB2LO() +codec.disableRB2RO() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Set gainstage between booster mixer and output mixer +# For this loopback example, we are going to keep these as low as they go +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) + +codec.enableMasterMode() +codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs and DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.enableDacLeft() +codec.enableDacRight() +codec.disableDacMute() + +codec.enableLoopBack() # Loopback sends ADC data directly into DAC + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Example complete. Listen to left/right INPUT1 on Headphone outputs.") diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py new file mode 100644 index 0000000..f5ca9cc --- /dev/null +++ b/examples/wm8960_06_3D_Enhance.py @@ -0,0 +1,156 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates 3D Enhance feature of WM8960 Codec. + +Toggles on/off 3D Enhance every 5 seconds, so you can hear the difference. + +Note, it also sets the 3D enhance Depth setting to 15 (max). +You can change this to your desired depth from 0-15. + +Note, this is very similar to the Loopback example, but also demos the 3D Enhance feature. + +Sets up analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). +Then send the output of the DAC to the headphone outs. + +You can now control the volume of the codecs built in headphone amp using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board, time +import adafruit_wm8960 + +print("Example 6 - 3D Enhance") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC + +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Disconnect LB2LO (booster to output mixer (analog bypass) +# For this example, we are going to pass audio throught the ADC and DAC +codec.disableLB2LO() +codec.disableRB2RO() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Set gainstage between booster mixer and output mixer +# For this loopback example, we are going to keep these as low as they go +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) + +codec.enableMasterMode() +codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs and DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.enableDacLeft() +codec.enableDacRight() +codec.disableDacMute() + +codec.enableLoopBack() # Loopback sends ADC data directly into DAC + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) +print("3D Enhance Depth set to 15 (max)") +codec.set3dDepth(15) + +print("Listen to left/right INPUT1 on Headphone outputs with toggling 3D enhance!") + +while True: + codec.enable3d() + print("3D Enhance enabled") + time.sleep(5.0) + + codec.disable3d() + print("3D Enhance disabled") + time.sleep(5.0) diff --git a/examples/wm8960_07_MicBias.py b/examples/wm8960_07_MicBias.py new file mode 100644 index 0000000..8f89681 --- /dev/null +++ b/examples/wm8960_07_MicBias.py @@ -0,0 +1,61 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +This example demonstrates control of the mic bias feature of WM8960 Codec. + +Electret Mics are powered with a mic bias voltage applied to their signal line - usually with a 2.2K resistor in series. This Codec can provide a clean mic bias. + +This example turns on the mic bias, set's it to each available output voltage, and then turns it off to demonstrate disable. + +Measure the voltage with a multimeter to verfy you are getting the correct voltages you desire on mic bias. + +You can later use this mic bias voltage to power an electret mic in a more advanced example (example 14). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board, time +import adafruit_wm8960 + +print("Example 7 - MicBias Control") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +codec.enableMicBias() + +# WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or +# WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) +codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD) +print("Mic Bias enabled (0.9*AVDD)") +delay(3000) + +# WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or +# WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) +codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD) +print("Mic Bias enabled (0.65*AVDD)") +delay(3000) + +codec.disableMicBias() +print("Mic Bias disabled") + +print("Example Complete. Hit reset to begin again.") diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py new file mode 100644 index 0000000..9400a4a --- /dev/null +++ b/examples/wm8960_08_I2S_Passthrough.py @@ -0,0 +1,171 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates reading I2S audio from the ADC, and passing that back to the DAC. + +This example sets up analog audio input (on INPUT1s), ADC/DAC enabled as I2S peripheral, sets volume control, and Headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Read the audio from the ADC via I2S. Then send audio immediately back to the DAC via I2S. Then send the output of the DAC to the headphone outs. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) +AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. +AUDIO_RXD --- ADAT *aka ADC_DATA/I2S_SD/"serial data in", this carries the I2S audio data from codec's ADC to MCU I2S bus. +AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. +AUDIO_SYNC -- ALR *for this example WS is shared for both the ADC WS (ALR) and the DAC WS (DLRC) + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +You can now control the volume of the codecs built in headphone amp using this fuction: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import adafruit_wm8960 +import audiobusio + +print("Example 8 - I2S Passthough") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC + +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set pga volumes +codec.setLINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) +codec.setRINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +# Connect from MIC inputs (aka pga output) to boost mixers +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Disconnect LB2LO (booster to output mixer (analog bypass) +# For this example, we are going to pass audio throught the ADC and DAC +codec.disableLB2LO() +codec.disableRB2RO() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Set gainstage between booster mixer and output mixer +# For this loopback example, we are going to keep these as low as they go +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) + +codec.enablePeripheralMode() +#codec.enableMasterMode() +#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs and DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.enableDacLeft() +codec.enableDacRight() +codec.disableDacMute() + +#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +codec.disableLoopBack() + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Codec Setup complete. Listen to left/right INPUT1 on Headphone outputs.") + +# Set up I2S +audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) + +while True: + # Get I2S data and place in data buffer + # TODO: Read from I2SIn and loopback into I2SOut + pass + + # DelayMicroseconds(300) # Only hear to demonstrate how much time you have + # to do things. + # Do not do much in this main loop, or the audio won't pass through correctly. + # With default settings (64 samples in buffer), you can spend up to 300 + # microseconds doing something in between passing each buffer of data + # You can tweak the buffer length to get more time if you need it. + # When bufferlength is 64, then you get ~300 microseconds + # When bufferlength is 128, then you get ~600 microseconds + # Note, as you increase bufferlength, then you are increasing latency between + # ADC input to DAC output. + # Latency may or may not be desired, depending on the project. diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py new file mode 100644 index 0000000..6071b10 --- /dev/null +++ b/examples/wm8960_09_I2S_Bluetooth.py @@ -0,0 +1,119 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates how to receive audio via Bluetooth and play it back via I2S to the codec DAC. + +This example sets up the MCU as a bluetooth sink device, with its output set +to I2S audio. + +This example sets up the WM8960 codec as an I2S peripheral, sets volume +control, and Headphone output. + +A bluetooth device, such as your phone or laptop, can connect to the MCU and +then begin playing an audio file. + +The MCU will send the I2S audio to the DAC of the codec. The DAC is +connected to the HP outputs. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) +AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. +AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Note, once connected and playing a sound file, your bluetooth source device +(i.e. your phone) can control volume with its own volume control. + +You can also control the volume of the codecs built in headphone amp using this fuction: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import audiobusio +import adafruit_wm8960 + +print("Example 9 - Bluetooth Audio") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Set gainstage between booster mixer and output mixer +# For this loopback example, we are going to keep these as low as they go +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) + +codec.enablePeripheralMode() +#codec.enableMasterMode() +#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable DACs +codec.enableDacLeft() +codec.enableDacRight() + +#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +codec.disableLoopBack() + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Codec Setup complete. Connect via Bluetooth, play music, and listen on Headphone outputs.") + +# Set up I2S +audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) + +# TODO: Bluetooth setup diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py new file mode 100644 index 0000000..1d40d63 --- /dev/null +++ b/examples/wm8960_10_AdcGain.py @@ -0,0 +1,181 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates how to control the volume using the codec's ADC digital volume control. + +Attach a potentiomenter to GND/A0/3V3 to actively adjust the setting. + +This example sets up the codec for analog audio input (on INPUT1s), ADC/DAC Loopback, sets hp volume, and Headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). +Then send the output of the DAC to the headphone outs. + +We will use the gain stage at the ADC to control the volume of the signal. +This is capable of more precision, with 255 available settings. + +** ADC digital volume +** Valid dB settings are -97.00 up to +30.0 (0.5dB steps) +** -97.50 (or lower) = MUTE +** -97.00 = -97.00dB (MIN) +** ... 0.5dB steps ... +** 30.00 = +30.00dB (MAX) + +You can also control the volume of the codecs built in headphone amp using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +MCU --------- POTENTIOMTER (aka blue little trimpot) +********************** +GND --------- "right-side pin" +A0 ---------- center pin *aka center tap connection +3V3 --------- "left-side pin" + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board, time +from analogio import AnalogIn +from adafruit_simplemath import map_range +import adafruit_wm8960 + +# Used to store incoming potentiometer settings to set ADC digital volume setting +userInputA0 = 0 +analog_in = AnalogIn(board.A0) + +print("Example 10 - ADC Digital Volume Control") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC + +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Disconnect LB2LO (booster to output mixer (analog bypass) +# For this example, we are going to pass audio throught the ADC and DAC +codec.disableLB2LO() +codec.disableRB2RO() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Set gainstage between booster mixer and output mixer +# For this loopback example, we are going to keep these as low as they go +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) + +codec.enableMasterMode() +codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs and DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.enableDacLeft() +codec.enableDacRight() +codec.disableDacMute() + +codec.enableLoopBack() # Loopback sends ADC data directly into DAC + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Headphone Amp Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") + +while True: + # Take a bunch of readings and average them, to smooth out the value + for i in range(250): + userInputA0 += analog_in.value + time.sleep(0.001) + + # After taking a bunch of samples, divide down to the average single reading + userInputA0 /= 250.0 + + # Map it from 0-4096, to a dB value that is acceptable in the ADC digital volume control (-97.50 [MUTE] to +30dB) + adcVolumeDB = map_range(userInputA0, 0.0, 65536.0, 30.0, -97.5) + + print("adcVolumeDB: ", adcVolumeDB) + + codec.setAdcLeftDigitalVolumeDB(adcVolumeDB) # -97.50 to +30.00dB + codec.setAdcRightDigitalVolumeDB(adcVolumeDB) # -97.50 to +30.00dB + + time.sleep(0.05) diff --git a/examples/wm8960_11_VolumePlotter.py b/examples/wm8960_11_VolumePlotter.py new file mode 100644 index 0000000..77b8250 --- /dev/null +++ b/examples/wm8960_11_VolumePlotter.py @@ -0,0 +1,169 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates reading I2S audio from the ADC, and plotting the audio samples on the Arduino Serial Plotter. + +This example sets up analog audio input (on INPUT1s), ADC enabled as I2S peripheral, sets volume control, and Headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Read the audio from the ADC via I2S. + +The analog bypass paths is also setup, so your audio will pass through the codec and playback on HP outs. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. +AUDIO_RXD --- ADAT *aka ADC_DATA/I2S_SD/"serial data in", this carries the I2S audio data from codec's ADC to MCU I2S bus. +AUDIO_SYNC -- ALR *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +You can now control the volume of the codecs built in headphone amp using this fuction: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board, time +import audiobusio +import adafruit_wm8960 + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC + +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set pga volumes +codec.setLINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) +codec.setRINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +# Connect from MIC inputs (aka pga output) to boost mixers +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Connect LB2LO (booster to output mixer (analog bypass) +codec.enableLB2LO() +codec.enableRB2RO() + +# Disconnect from DAC outputs to output mixer +codec.disableLD2LO() +codec.disableRD2RO() + +# Set gainstage between booster mixer and output mixer +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) + +codec.enablePeripheralMode() +#codec.enableMasterMode() +#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs, and disable DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.disableDacLeft() +codec.disableDacRight() +codec.disableDacMute() + +#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +codec.disableLoopBack() + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.enableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +codec.setHeadphoneVolumeDB(0.00) + +# Set up I2S +audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) + +sBuffer = [0 for i in range(64)] + +while True: + # False print statements to "lock range" on serial plotter display + # Change rangelimit value to adjust "sensitivity" + rangelimit = 3000 + print(rangelimit * -1, " ", rangelimit, " ", end=None) + + # Get I2S data and place in data buffer + # TODO: Read from I2SIn + + # Read I2S data buffer + mean = 0.0 + for i in range(64): + mean += sBuffer[i] + + # Average the data reading + mean /= 64.0 + + # Print to serial plotter + print(mean) diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_12_AutomaticLevelControl.py new file mode 100644 index 0000000..9994ac7 --- /dev/null +++ b/examples/wm8960_12_AutomaticLevelControl.py @@ -0,0 +1,181 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates how to use the automatic level control feature of the WM8960 Codec. + +Attach a potentiomenter to GND/A0/3V3 to actively adjust the ALC target setting. + +This example sets up the codec for analog audio input (on INPUT1s), ADC/DAC Loopback, sets hp volume, and Headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). +Then send the output of the DAC to the headphone outs. + +We will use the user input via potentiometer on A0 to set the ALC target value. The ALC will adjust the gain of the pga input buffer to try and keep the signal level at the target. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +MCU --------- POTENTIOMTER (aka blue little trimpot) +********************** +GND --------- "right-side pin" +A0 ---------- center pin *aka center tap connection +3V3 --------- "left-side pin" + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC ------- AUDIO OUT +********************** +OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL --------- TRS OUTPUT TIP *left HP output +HPR --------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board, time +from analogio import AnalogIn +from adafruit_simplemath import map_range +import adafruit_wm8960 + +print("Example 12 - Automatic Level Control") + +# Used to store incoming potentiometer settings to set ADC digital volume setting +userInputA0 = 0 +analog_in = AnalogIn(board.A0) + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC + +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Disconnect LB2LO (booster to output mixer (analog bypass) +# For this example, we are going to pass audio throught the ADC and DAC +codec.disableLB2LO() +codec.disableRB2RO() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Set gainstage between booster mixer and output mixer +# For this loopback example, we are going to keep these as low as they go +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) + +codec.enableMasterMode() +codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs and DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.enableDacLeft() +codec.enableDacRight() +codec.disableDacMute() + +codec.enableLoopBack() # Loopback sends ADC data directly into DAC + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +# Automatic Level control stuff + +# Only allows pga gain stages at a "zero crossover" point in audio stream. +# Minimizes "zipper" noise when chaning gains. +codec.enablePgaZeroCross() + +codec.enableAlc(adafruit_wm8960.WM8960_ALC_MODE_STEREO) +codec.setAlcTarget(adafruit_wm8960.WM8960_ALC_TARGET_LEVEL_NEG_6DB) +codec.setAlcDecay(adafruit_wm8960.WM8960_ALC_DECAY_TIME_192MS) +codec.setAlcAttack(adafruit_wm8960.WM8960_ALC_ATTACK_TIME_24MS) +codec.setAlcMaxGain(adafruit_wm8960.WM8960_ALC_MAX_GAIN_LEVEL_30DB) +codec.setAlcMinGain(adafruit_wm8960.WM8960_ALC_MIN_GAIN_LEVEL_NEG_17_25DB) +codec.setAlcHold(adafruit_wm8960.WM8960_ALC_HOLD_TIME_0MS) + +print("Headphopne Amp Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") + +while True: + # Take a bunch of readings and average them, to smooth out the value + for i in range(250): + userInputA0 += analog_in.value + time.sleep(0.001) + userInputA0 /= 250.0 + + # Map it from 0-4096, to a value that is acceptable for the setting + alcTarget = map_range(userInputA0, 0.0, 65536.0, 15.0, 0.0) + + print("alcTarget: ", alcTarget) + + codec.setAlcTarget(alcTarget) # Valid inputs are 0-15, 0 = -22.5dB FS, ... 1.5dB steps ... , 15 = -1.5dB FS + + time.sleep(1.0) diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py new file mode 100644 index 0000000..15c5264 --- /dev/null +++ b/examples/wm8960_13_DacGain.py @@ -0,0 +1,182 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates how to control the volume using the codec's DAC digital volume control. + +Attach a potentiomenter to GND/A0/3V3 to actively adjust the setting. + +This example sets up the codec for analog audio input (on INPUT1s), ADC/DAC Loopback, sets hp volume, and Headphone output on the WM8960 Codec. + +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). +Then send the output of the DAC to the headphone outs. + +We will use the gain stage at the DAC to control the volume of the signal. +This is capable of more precision, with 255 available settings. + +** DAC digital volume +** Valid dB settings are -97.00 up to +30.0 (0.5dB steps) +** -97.50 (or lower) = MUTE +** -97.00 = -97.00dB (MIN) +** ... 0.5dB steps ... +** 30.00 = +30.00dB (MAX) + +You can also control the volume of the codecs built in headphone amp using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +MCU --------- POTENTIOMTER (aka blue little trimpot) +********************** +GND --------- "right-side pin" +A0 ---------- center pin *aka center tap connection +3V3 --------- "left-side pin" + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC ------- AUDIO OUT +********************** +OUT3 -------- TRS OUTPUT SLEEVE +HPL --------- TRS OUTPUT TIP *left HP output +HPR --------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board, time +from analogio import AnalogIn +from adafruit_simplemath import map_range +import adafruit_wm8960 + +print("Example 13 - DAC Digital Volume Control") + +# Used to store incoming potentiometer settings to set ADC digital volume setting +userInputA0 = 0 +analog_in = AnalogIn(board.A0) + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC + +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Disconnect LB2LO (booster to output mixer (analog bypass) +# For this example, we are going to pass audio throught the ADC and DAC +codec.disableLB2LO() +codec.disableRB2RO() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Set gainstage between booster mixer and output mixer +# For this loopback example, we are going to keep these as low as they go +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) + +codec.enableMasterMode() +codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs and DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.enableDacLeft() +codec.enableDacRight() +codec.disableDacMute() + +codec.enableLoopBack() # Loopback sends ADC data directly into DAC + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Headphopne Amp Volume set to +0dB") +codec.setHeadphoneVolumeDB(0.00) + +print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") + +while True: + # Take a bunch of readings and average them, to smooth out the value + for i in range(250): + userInputA0 += analog_in.value + time.sleep(0.001) + + # After taking a bunch of samples, divide down to the average single reading + userInputA0 /= 250.0 + + # Map it from 0-4096, to a dB value that is acceptable in the DAC digital volume control (-97.50 [MUTE] to +30dB) + dacVolumeDB = map_range(userInputA0, 0.0, 65536.0, 30.0, -97.5) + + print("dacVolumeDB: ", dacVolumeDB) + + codec.setDacLeftDigitalVolumeDB(dacVolumeDB) # -97.50 to +30.00dB + codec.setDacRightDigitalVolumeDB(dacVolumeDB) # -97.50 to +30.00dB + + time.sleep(0.05) diff --git a/examples/wm8960_14_ElectretMics.py b/examples/wm8960_14_ElectretMics.py new file mode 100644 index 0000000..1ece78f --- /dev/null +++ b/examples/wm8960_14_ElectretMics.py @@ -0,0 +1,135 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates electret microphone analog audio input (on INPUT1/INPUT2 as "pseudo-differential MIC configuration"). Sets the PGA gain, sets the non-inverting pga input to INPUT2s, sets volume control, and headphone output on the WM8960 Codec. + +Note, most of the examples in this library set all gain stages at 0dB, but for this electret mic example, we need a bit more gain on the initial input stage (the PGA), so we set it to +24dB (aka "57" as the argument to the function). +We are also bumping up the headphone output gainstage to max, +6dB (aka "127"). + +Electret mics should be connected to left and right inputs. + +MicBias should be provided to the "+" side of each mic via an in-series 2.2K resistor. + +The Mic "-" should be connected to each INPUT2. Note, this is also GND in this example, but enables a "psuedo-differential setup". + +This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. + +It will output the sound on the headphone outputs. It is setup to do a capless headphone setup, so connet your headphones ground to "OUT3" and this provides a buffered VMID. + +You can now control the volume of the codecs built in headphone buffers using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +HARDWARE CONNECTIONS +See Datasheet page 25 for electret mic hardware hookup example diagrams. +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) + +********************** +CODEC ------- AUDIO IN (ELECTRET MICS) +********************** +GND --------- LEFT MIC - +LINPUT1 ----- LEFT MIC - +LINPUT2 ----- LEFT MIC + +MICBIAS ----- 2.2K RESISTOR ----- LEFT MIC+ +GND --------- RIGHT MIC - +RINPUT1 ----- RIGHT MIC - +RINPUT2 ----- RIGHT MIC + +MICBIAS ----- 2.2K RESISTOR ----- RIGHT MIC+ + +********************** +CODEC -------- AUDIO OUT +********************** +OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL ---------- TRS OUTPUT TIP *left HP output +HPR ---------- TRS OUTPUT RING1 *right HP output + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import adafruit_wm8960 + +print("Example 14 - Electret Mics") + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +codec.enableMicBias() + +# WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or +# WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) +codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD) +print("Mic Bias enabled (0.9*AVDD)") + +# Setup signal flow through the analog audio bypass connections +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set pga volumes +codec.setLINVOLDB(24.00) # Valid options are -17.25dB to +30.00dB +codec.setRINVOLDB(24.00) # Valid options are -17.25dB to +30.00dB +print("PGA gain set to +24dB") + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +print("Mic boost stage set to 0dB") + +# For MIC+ signal of differential mic signal +codec.pgaLeftNonInvSignalSelect(adafruit_wm8960.WM8960_PGAL_LINPUT2) + +# For MIC+ signal of differential mic signal +codec.pgaRightNonInvSignalSelect(adafruit_wm8960.WM8960_PGAR_RINPUT2) +print("Pga non-inverting inputs set to INPUT2s") + +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Connect LB2LO (booster to output mixer (analog bypass) +codec.enableLB2LO() +codec.enableRB2RO() + +# Set gainstage between booster mixer and output mixer +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +print("Headphone output buffer volume set to +6dB (max)") +codec.setHeadphoneVolumeDB(6.00) + +print("Example complete. Listen to Electret mics on headphone outputs.") diff --git a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py new file mode 100644 index 0000000..354c513 --- /dev/null +++ b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py @@ -0,0 +1,183 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +This example is very similar to example 11, however it uses a MEMS mic with differential signal as the source of the sound. Here we are using just one of VM2020 Breakouts plugged into the left channel. + +Note, with this VM2020 differential mic signal, we are going to add much more gain than in previous examples. We are going to use a MICBOOST of 29dB (max), and set the PGA to +8.25dB. + +Demonstrates reading I2S audio from the ADC, and plotting the audio samples on the Arduino Serial Plotter. + +This example sets up differential analog audio input (on LINPUT1/LINPUT2), ADC enabled as I2S peripheral, sets volume control, and Headphone output on the WM8960 Codec. + +A MEMS mic should be connected to the left channel INPUT1 and INPUT2, they are labeled "LIN1" and "LIN2" on the board. These will provide the input connections for the positive and negative signals from the MEMS mic. + +This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Read the audio from the ADC via I2S. + +The analog bypass paths is also setup, so your audio will pass through the codec and playback on HP outs. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. +AUDIO_RXD --- ADAT *aka ADC_DATA/I2S_SD/"serial data in", this carries the I2S audio data from codec's ADC to MCU I2S bus. +AUDIO_SYNC -- ALR *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. + +********************** +CODEC ------- MIC IN +********************** +GND --------- GND *Ground +AVDD -------- VCC *3.3V (default on the Codec breakout) +LINPUT1 ----- OUT- *Mic signal "-" +LINPUT2 ----- OUT+ *Mic signal "+" + +********************** +CODEC ------- AUDIO OUT +********************** +OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL --------- TRS OUTPUT TIP *left HP output +HPR --------- TRS OUTPUT RING1 *right HP output + +You can now control the volume of the codecs built in headphone amp using this fuction: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import audiobusio +import adafruit_wm8960 + +sBuffer = [0 for i in range(64)] + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Setup signal flow to the ADC +codec.enableLMIC() +codec.enableRMIC() + +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. +codec.connectLMN1() +codec.connectRMN1() + +# Disable mutes on PGA inputs (aka INTPUT1) +codec.disableLINMUTE() +codec.disableRINMUTE() + +# Set pga volumes +codec.setLINVOLDB(8.25) # Valid options are -17.25dB to +30dB (0.75dB steps) +codec.setRINVOLDB(8.25) # Valid options are -17.25dB to +30dB (0.75dB steps) + +# Set input boosts to get inputs 1 to the boost mixers +codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_29DB) +codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_29DB) + +# For MIC+ signal of differential mic signal +codec.pgaLeftNonInvSignalSelect(adafruit_wm8960.WM8960_PGAL_LINPUT2) + +# For MIC+ signal of differential mic signal +codec.pgaRightNonInvSignalSelect(adafruit_wm8960.WM8960_PGAR_RINPUT2) + +# Connect from MIC inputs (aka pga output) to boost mixers +codec.connectLMIC2B() +codec.connectRMIC2B() + +# Enable boost mixers +codec.enableAINL() +codec.enableAINR() + +# Connect LB2LO (booster to output mixer (analog bypass) +codec.enableLB2LO() +codec.enableRB2RO() + +# Disconnect from DAC outputs to output mixer +codec.disableLD2LO() +codec.disableRD2RO() + +# Set gainstage between booster mixer and output mixer +codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d +# freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) + +codec.enablePeripheralMode() +#codec.enableMasterMode() +#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. + +# Enable ADCs, and disable DACs +codec.enableAdcLeft() +codec.enableAdcRight() +codec.disableDacLeft() +codec.disableDacRight() +codec.disableDacMute() + +#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +codec.disableLoopBack() + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.enableDacMute() + +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground + +codec.setHeadphoneVolumeDB(6.00) + +# Set up I2S +audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) + +while True: + # False print statements to "lock range" on serial plotter display + # Change rangelimit value to adjust "sensitivity" + rangelimit = 3000 + print(rangelimit * -1, " ", rangelimit, " ", end=None) + + # Get I2S data and place in data buffer + # TODO: Read from I2SIn + + # Read I2S data buffer + mean = 0.0 + # Only looking at left signal samples in the buffer (e.g. 0,2,4,6,8...) + # Notice in our for loop here, we are incrementing the index by 2. + for i in range(64): + mean += sBuffer[i] + + # Average the data reading + # We're only concerned with left input for this example. So we must + # divide by "half of samples read" (because it is stereo I2S audio data) + mean /= 64.0 / 2.0 + + # Print to serial plotter + print(mean) From e6b343bcaa2b46e7bd7160ad6b87b2f994413826 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 09:49:08 -0500 Subject: [PATCH 04/56] Reset device on initialization and copy default registers to local variable. --- adafruit_wm8960.py | 73 +++++----------------------------------------- 1 file changed, 7 insertions(+), 66 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 71342dd..7a034a2 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -387,73 +387,14 @@ def __init__(self, i2c_bus:I2C, address:int = WM8960_ADDR): ''' The WM8960 does not support I2C reads This means we must keep a local copy of all the register values - We will instantiate with default values - As we write to the device, we will also make sure - To update our local copy as well, stored here in this array. - Each register is 9-bits, so we will store them as a uint16_t - They are in order from R0-R55, and we even keep blank spots for the - "reserved" registers. This way we can use the register address macro - defines above to easiy access each local copy of each register. - Example: _registerLocalCopy[WM8960_REG_LEFT_INPUT_VOLUME] + We will instantiate with default values by copying from WM8960_REGISTER_DEFAULTS during reset() + As we write to the device, we will also make sure to update our local copy as well, stored here in this array. + Each register is 9-bits + They are in order from R0-R55, and we even keep blank spots for the "reserved" registers. This way we can use the register address macro defines above to easiy access each local copy of each register. + Example: self._registerLocalCopy[WM8960_REG_LEFT_INPUT_VOLUME] ''' - self._registerLocalCopy = [ - 0x0097, # R0 (0x00) - 0x0097, # R1 (0x01) - 0x0000, # R2 (0x02) - 0x0000, # R3 (0x03) - 0x0000, # R4 (0x04) - 0x0008, # F5 (0x05) - 0x0000, # R6 (0x06) - 0x000A, # R7 (0x07) - 0x01C0, # R8 (0x08) - 0x0000, # R9 (0x09) - 0x00FF, # R10 (0x0a) - 0x00FF, # R11 (0x0b) - 0x0000, # R12 (0x0C) RESERVED - 0x0000, # R13 (0x0D) RESERVED - 0x0000, # R14 (0x0E) RESERVED - 0x0000, # R15 (0x0F) RESERVED - 0x0000, # R16 (0x10) - 0x007B, # R17 (0x11) - 0x0100, # R18 (0x12) - 0x0032, # R19 (0x13) - 0x0000, # R20 (0x14) - 0x00C3, # R21 (0x15) - 0x00C3, # R22 (0x16) - 0x01C0, # R23 (0x17) - 0x0000, # R24 (0x18) - 0x0000, # R25 (0x19) - 0x0000, # R26 (0x1A) - 0x0000, # R27 (0x1B) - 0x0000, # R28 (0x1C) - 0x0000, # R29 (0x1D) - 0x0000, # R30 (0x1E) RESERVED - 0x0000, # R31 (0x1F) RESERVED - 0x0100, # R32 (0x20) - 0x0100, # R33 (0x21) - 0x0050, # R34 (0x22) - 0x0000, # R35 (0x23) RESERVED - 0x0000, # R36 (0x24) RESERVED - 0x0050, # R37 (0x25) - 0x0000, # R38 (0x26) - 0x0000, # R39 (0x27) - 0x0000, # R40 (0x28) - 0x0000, # R41 (0x29) - 0x0040, # R42 (0x2A) - 0x0000, # R43 (0x2B) - 0x0000, # R44 (0x2C) - 0x0050, # R45 (0x2D) - 0x0050, # R46 (0x2E) - 0x0000, # R47 (0x2F) - 0x0002, # R48 (0x30) - 0x0037, # R49 (0x31) - 0x0000, # R50 (0x32) RESERVED - 0x0080, # R51 (0x33) - 0x0008, # R52 (0x34) - 0x0031, # R53 (0x35) - 0x0026, # R54 (0x36) - 0x00e9, # R55 (0x37) - ] + self._registerLocalCopy = [0x0000 for i in range(len(WM8960_REGISTER_DEFAULTS))] + self.reset() def isConnected(self) -> bool: # TODO: Check I2C or I2CDevice From 7f548001d4b705a9f84d5c4cd5989552ecedce58 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 09:49:38 -0500 Subject: [PATCH 05/56] Fix bug in `_writeRegisterMultiBits`. --- adafruit_wm8960.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 7a034a2..d9ccbf8 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -1359,7 +1359,7 @@ def _writeRegisterMultiBits(self, registerAddress:int, settingMsbNum:int, settin # Clear bits we care about numOfBits = (settingMsbNum - settingLsbNum) + 1 for i in range(numOfBits): - regvalue &= ~(1 << (settingLsbNum + 1)) + regvalue &= ~(1 << (settingLsbNum + i)) # Shift and set the bits from in incoming desired setting value regvalue |= setting << settingLsbNum From 215deec48b76771ee67269f6b49c4cf6ecb76e41 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 10:06:06 -0500 Subject: [PATCH 06/56] Use single integer for value of PLLK rather than separate nibbles. --- adafruit_wm8960.py | 8 ++++---- examples/wm8960_04_Speaker.py | 5 ++--- examples/wm8960_05_Loopback.py | 5 ++--- examples/wm8960_06_3D_Enhance.py | 5 ++--- examples/wm8960_08_I2S_Passthrough.py | 5 ++--- examples/wm8960_09_I2S_Bluetooth.py | 5 ++--- examples/wm8960_10_AdcGain.py | 2 +- examples/wm8960_11_VolumePlotter.py | 5 ++--- examples/wm8960_12_AutomaticLevelControl.py | 5 ++--- examples/wm8960_13_DacGain.py | 5 ++--- examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py | 5 ++--- 11 files changed, 23 insertions(+), 32 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index d9ccbf8..6657e9e 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -1264,10 +1264,10 @@ def setPLLN(self, n:int): self._writeRegisterMultiBits(WM8960_REG_PLL_N, 3, 0, n) # Send each nibble of 24-bit value for value K - def setPLLK(self, one:int, two:int, three:int): - self._writeRegisterMultiBits(WM8960_REG_PLL_K_1, 5, 0, one) - self._writeRegisterMultiBits(WM8960_REG_PLL_K_2, 8, 0, two) - self._writeRegisterMultiBits(WM8960_REG_PLL_K_3, 8, 0, three) + def setPLLK(self, k:int): + self._writeRegisterMultiBits(WM8960_REG_PLL_K_1, 5, 0, (k >> 16) & 0x1F) + self._writeRegisterMultiBits(WM8960_REG_PLL_K_2, 8, 0, (k >> 8) & 0xFF) + self._writeRegisterMultiBits(WM8960_REG_PLL_K_3, 8, 0, k & 0xFF) # 0=integer, 1=fractional def setSMD(self, mode:bool): diff --git a/examples/wm8960_04_Speaker.py b/examples/wm8960_04_Speaker.py index d53dc6f..572cdfc 100644 --- a/examples/wm8960_04_Speaker.py +++ b/examples/wm8960_04_Speaker.py @@ -99,8 +99,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -108,7 +107,7 @@ codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) codec.enableSpeakers() diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_05_Loopback.py index 067a31a..beb3bd9 100644 --- a/examples/wm8960_05_Loopback.py +++ b/examples/wm8960_05_Loopback.py @@ -97,8 +97,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -107,7 +106,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py index f5ca9cc..9ec2f6e 100644 --- a/examples/wm8960_06_3D_Enhance.py +++ b/examples/wm8960_06_3D_Enhance.py @@ -107,8 +107,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -117,7 +116,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py index 9400a4a..0d5d1eb 100644 --- a/examples/wm8960_08_I2S_Passthrough.py +++ b/examples/wm8960_08_I2S_Passthrough.py @@ -110,8 +110,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -120,7 +119,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py index 6071b10..cfadd5c 100644 --- a/examples/wm8960_09_I2S_Bluetooth.py +++ b/examples/wm8960_09_I2S_Bluetooth.py @@ -76,8 +76,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -86,7 +85,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py index 1d40d63..26b8db7 100644 --- a/examples/wm8960_10_AdcGain.py +++ b/examples/wm8960_10_AdcGain.py @@ -134,7 +134,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) diff --git a/examples/wm8960_11_VolumePlotter.py b/examples/wm8960_11_VolumePlotter.py index 77b8250..8d8ad61 100644 --- a/examples/wm8960_11_VolumePlotter.py +++ b/examples/wm8960_11_VolumePlotter.py @@ -106,8 +106,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -116,7 +115,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_12_AutomaticLevelControl.py index 9994ac7..45c8102 100644 --- a/examples/wm8960_12_AutomaticLevelControl.py +++ b/examples/wm8960_12_AutomaticLevelControl.py @@ -113,8 +113,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -123,7 +122,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py index 15c5264..1d4c1d1 100644 --- a/examples/wm8960_13_DacGain.py +++ b/examples/wm8960_13_DacGain.py @@ -125,8 +125,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -135,7 +134,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) diff --git a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py index 354c513..e323047 100644 --- a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py +++ b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py @@ -118,8 +118,7 @@ codec.enableLOMIX() codec.enableROMIX() -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d -# freq at 705.6kHz +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz codec.enablePLL() # Needed for class-d amp clock codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) @@ -128,7 +127,7 @@ codec.setBCLKDIV(4) codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) codec.setPLLN(7) -codec.setPLLK(0x86, 0xC2, 0x26) # PLLK=86C226h +codec.setPLLK(0x86C226) #codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) #codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) From 4276430e8eed0dc43e0296fe020c4b863ac459b1 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 10:18:45 -0500 Subject: [PATCH 07/56] Add methods to enable/disable both channels of DAC and ADC. --- adafruit_wm8960.py | 14 ++++++++++++++ examples/wm8960_05_Loopback.py | 10 ++++------ examples/wm8960_06_3D_Enhance.py | 10 ++++------ examples/wm8960_08_I2S_Passthrough.py | 9 +++------ examples/wm8960_09_I2S_Bluetooth.py | 5 ++--- examples/wm8960_10_AdcGain.py | 10 ++++------ examples/wm8960_12_AutomaticLevelControl.py | 10 ++++------ examples/wm8960_13_DacGain.py | 10 ++++------ 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 6657e9e..c90e626 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -693,6 +693,13 @@ def enableAdcRight(self): def disableAdcRight(self): self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 0) + def enableAdc(self): + self.enableAdcLeft() + self.enableAdcRight() + def disableAdc(self): + self.disableAdcLeft() + self.disableAdcRight() + # ADC digital volume # Note, also needs to handle control of the ADCVU bits (volume update). # Valid inputs are 0-255 @@ -828,6 +835,13 @@ def enableDacRight(self): def disableDacRight(self): self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 0) + def enableDac(self): + self.enableDacLeft() + self.enableDacRight() + def disableDac(self): + self.disableDacLeft() + self.disableDacRight() + # DAC digital volume # Valid inputs are 0-255 # 0 = mute diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_05_Loopback.py index beb3bd9..ad3b239 100644 --- a/examples/wm8960_05_Loopback.py +++ b/examples/wm8960_05_Loopback.py @@ -114,13 +114,11 @@ codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.enableDacLeft() -codec.enableDacRight() -codec.disableDacMute() +codec.enableAdc() +codec.enableDac() -codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC +codec.enableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py index 9ec2f6e..743cea4 100644 --- a/examples/wm8960_06_3D_Enhance.py +++ b/examples/wm8960_06_3D_Enhance.py @@ -124,13 +124,11 @@ codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.enableDacLeft() -codec.enableDacRight() -codec.disableDacMute() +codec.enableAdc() +codec.enableDac() -codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC +codec.enableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py index 0d5d1eb..b51f36a 100644 --- a/examples/wm8960_08_I2S_Passthrough.py +++ b/examples/wm8960_08_I2S_Passthrough.py @@ -129,13 +129,10 @@ #codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.enableDacLeft() -codec.enableDacRight() -codec.disableDacMute() +codec.enableAdc() +codec.enableDac() -#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC codec.disableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py index cfadd5c..3169ff6 100644 --- a/examples/wm8960_09_I2S_Bluetooth.py +++ b/examples/wm8960_09_I2S_Bluetooth.py @@ -95,10 +95,9 @@ #codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. # Enable DACs -codec.enableDacLeft() -codec.enableDacRight() +codec.enableDac() -#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC codec.disableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py index 26b8db7..c5d8b5a 100644 --- a/examples/wm8960_10_AdcGain.py +++ b/examples/wm8960_10_AdcGain.py @@ -142,13 +142,11 @@ codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.enableDacLeft() -codec.enableDacRight() -codec.disableDacMute() +codec.enableAdc() +codec.enableDac() -codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC +codec.enableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_12_AutomaticLevelControl.py index 45c8102..0d0319d 100644 --- a/examples/wm8960_12_AutomaticLevelControl.py +++ b/examples/wm8960_12_AutomaticLevelControl.py @@ -130,13 +130,11 @@ codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.enableDacLeft() -codec.enableDacRight() -codec.disableDacMute() +codec.enableAdc() +codec.enableDac() -codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC +codec.enableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py index 1d4c1d1..19b9ee5 100644 --- a/examples/wm8960_13_DacGain.py +++ b/examples/wm8960_13_DacGain.py @@ -142,13 +142,11 @@ codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.enableDacLeft() -codec.enableDacRight() -codec.disableDacMute() +codec.enableAdc() +codec.enableDac() -codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC +codec.enableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() From 68923791df5966c15709a282d3466dbf4198540c Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 10:19:07 -0500 Subject: [PATCH 08/56] Updated simpletest example to use new library. --- examples/wm8960_simpletest.py | 86 +++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index 6c7afbe..7ccd03d 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -1,9 +1,41 @@ # SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries # SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple # # SPDX-License-Identifier: Unlicense -# Sounds like an alarm clock. Tested on iMX RT 1060 EVK. +''' +Demonstrates I2C Output on WM8960 Codec by generating a simple tone using synthio. Sounds like an alarm clock. + +It will output the sound on the headphone outputs. +It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) +AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. +AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. + +********************** +CODEC ------- AUDIO OUT +********************** +OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL --------- TRS OUTPUT TIP *left HP output +HPR --------- TRS OUTPUT RING1 *right HP output + +You can now control the volume of the codecs built in headphone buffers using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' import audiobusio import board @@ -12,10 +44,54 @@ import time import digitalio -dac = adafruit_wm8960.WM8960(board.I2C()) -dac.start_i2s_out() -audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD, main_clock=board.AUDIO_MCLK) -synth = synthio.Synthesizer(sample_rate=22050) +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86C226) +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) + +codec.enablePeripheralMode() + +# Enable DACs +codec.enableDac() + +# Loopback sends ADC data directly into DAC +codec.disableLoopBack() + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +# Enable headphone output and provides VMID as buffer for headphone ground +codec.enableHeadphones() +codec.enableOUT3MIX() + +# Adjust headphone volume +codec.setHeadphoneVolumeDB(0.0) + +audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) +synth = synthio.Synthesizer(sample_rate=44100) audio.play(synth) led = digitalio.DigitalInOut(board.LED) From 71c1a070ac1c186073bdfe5c678e3676cf9f97b1 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 10:29:59 -0500 Subject: [PATCH 09/56] Ported over more complex example, eighties_dystopia, by @todbot / Tod Kurt. --- examples/wm8960_eighties_dystopia.py | 152 +++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 examples/wm8960_eighties_dystopia.py diff --git a/examples/wm8960_eighties_dystopia.py b/examples/wm8960_eighties_dystopia.py new file mode 100644 index 0000000..f0041eb --- /dev/null +++ b/examples/wm8960_eighties_dystopia.py @@ -0,0 +1,152 @@ +# SPDX-FileCopyrightText: 2023 Tod Kurt (@todbot) +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT + +''' +Demonstrates I2C Output on WM8960 Codec by generating "a swirling ominous wub that evolves over time" using synthio. + +Modified from original code by @todbot / Tod Kurt: https://github.com/todbot/circuitpython-synthio-tricks/blob/main/examples/eighties_dystopia/code.py + +It will output the sound on the headphone outputs. +It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) +AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. +AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. + +********************** +CODEC ------- AUDIO OUT +********************** +OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL --------- TRS OUTPUT TIP *left HP output +HPR --------- TRS OUTPUT RING1 *right HP output +''' + +import board, audiobusio +import audiomixer, synthio +import adafruit_wm8960 +import time, random +import digitalio +import ulab.numpy as np + +codec = adafruit_wm8960.WM8960(board.I2C()) + +# General setup needed +codec.enableVREF() +codec.enableVMID() + +# Connect from DAC outputs to output mixer +codec.enableLD2LO() +codec.enableRD2RO() + +# Enable output mixers +codec.enableLOMIX() +codec.enableROMIX() + +# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz +codec.enablePLL() # Needed for class-d amp clock +codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) +codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) +codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) +codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) +codec.setBCLKDIV(4) +codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) +codec.setPLLN(7) +codec.setPLLK(0x86C226) +#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) +#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) + +codec.enablePeripheralMode() + +# Enable DACs +codec.enableDacLeft() +codec.enableDacRight() +codec.disableDacMute() + +# Loopback sends ADC data directly into DAC +codec.disableLoopBack() + +# Default is "soft mute" on, so we must disable mute to make channels active +codec.disableDacMute() + +# Enable Headphones +codec.enableHeadphones() +codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground +codec.setHeadphoneVolumeDB(0.0) + +audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) + +led = digitalio.DigitalInOut(board.LED) +led.switch_to_output() +led.value = True + +notes = (33, 34, 31) # possible notes to play MIDI A1, A1#, G1 +note_duration = 15 # how long each note plays for +num_voices = 5 # how many voices for each note +lpf_basef = 500 # filter lowest frequency +lpf_resonance = 1.5 # filter q + +mixer = audiomixer.Mixer(channel_count=2, sample_rate=44100, buffer_size=8192) +synth = synthio.Synthesizer(channel_count=2, sample_rate=44100) +audio.play(mixer) +mixer.voice[0].play(synth) +mixer.voice[0].level = 0.8 + +# our oscillator waveform, a 512 sample downward saw wave going from +/-30k +wave_saw = np.linspace(30000, -30000, num=512, dtype=np.int16) # max is +/-32k but gives us headroom +amp_env = synthio.Envelope(attack_level=1, sustain_level=1) + +# set up the voices (aka "Notes" in synthio-speak) w/ initial values +voices = [] +for i in range(num_voices): + voices.append( synthio.Note( frequency=0, envelope=amp_env, waveform=wave_saw ) ) + +# set all the voices to the "same" frequency (with random detuning) +# zeroth voice is sub-oscillator, one-octave down +def set_notes(n): + for voice in voices: + #f = synthio.midi_to_hz( n ) + random.uniform(0,1.0) # what orig sketch does + f = synthio.midi_to_hz( n + random.uniform(0,0.4) ) # more valid if we move up the scale + voice.frequency = f + voices[0].frequency = voices[0].frequency/2 # bass note one octave down + +# the LFO that modulates the filter cutoff +lfo_filtermod = synthio.LFO(rate=0.05, scale=2000, offset=2000) +# we can't attach this directly to a filter input, so stash it in the blocks runner +synth.blocks.append(lfo_filtermod) + +note = notes[0] +last_note_time = time.monotonic() +last_filtermod_time = time.monotonic() + +# start the voices playing +set_notes(note) +synth.press(voices) + +while True: + # continuosly update filter, no global filter, so update each voice's filter + for v in voices: + v.filter = synth.low_pass_filter( lpf_basef + lfo_filtermod.value, lpf_resonance ) + + if time.monotonic() - last_filtermod_time > 1: + last_filtermod_time = time.monotonic() + # randomly modulate the filter frequency ('rate' in synthio) to make more dynamic + lfo_filtermod.rate = 0.01 + random.random() / 8 + print("filtermod",lfo_filtermod.rate) + + if time.monotonic() - last_note_time > note_duration: + last_note_time = time.monotonic() + # pick new note, but not one we're currently playing + note = random.choice([n for n in notes if n != note]) + set_notes(note) + print("note", note, ["%3.2f" % v.frequency for v in voices] ) From 90a9745bd1fef28c40cec7f1fe10e02613a6a052 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 12:07:11 -0500 Subject: [PATCH 10/56] Enable VREF and VMID on initialization by default. --- adafruit_wm8960.py | 4 ++++ examples/wm8960_01_Volume.py | 4 ---- examples/wm8960_02_INPUT2.py | 4 ---- examples/wm8960_03_INPUT1.py | 4 ---- examples/wm8960_04_Speaker.py | 4 ---- examples/wm8960_05_Loopback.py | 4 ---- examples/wm8960_06_3D_Enhance.py | 4 ---- examples/wm8960_07_MicBias.py | 4 ---- examples/wm8960_08_I2S_Passthrough.py | 4 ---- examples/wm8960_09_I2S_Bluetooth.py | 4 ---- examples/wm8960_10_AdcGain.py | 4 ---- examples/wm8960_11_VolumePlotter.py | 4 ---- examples/wm8960_12_AutomaticLevelControl.py | 4 ---- examples/wm8960_13_DacGain.py | 4 ---- examples/wm8960_14_ElectretMics.py | 4 ---- examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py | 4 ---- examples/wm8960_eighties_dystopia.py | 4 ---- examples/wm8960_simpletest.py | 4 ---- 18 files changed, 4 insertions(+), 68 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index c90e626..9feefbd 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -396,6 +396,10 @@ def __init__(self, i2c_bus:I2C, address:int = WM8960_ADDR): self._registerLocalCopy = [0x0000 for i in range(len(WM8960_REGISTER_DEFAULTS))] self.reset() + # General setup + self.enableVREF() + self.enableVMID() + def isConnected(self) -> bool: # TODO: Check I2C or I2CDevice return True diff --git a/examples/wm8960_01_Volume.py b/examples/wm8960_01_Volume.py index 15125c6..f36831f 100644 --- a/examples/wm8960_01_Volume.py +++ b/examples/wm8960_01_Volume.py @@ -54,10 +54,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow through the analog audio bypass connections # Enable left output mixer diff --git a/examples/wm8960_02_INPUT2.py b/examples/wm8960_02_INPUT2.py index ec7fdee..1e5a571 100644 --- a/examples/wm8960_02_INPUT2.py +++ b/examples/wm8960_02_INPUT2.py @@ -53,10 +53,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow through the analog audio bypass connections # Set input boosts to get INPUT2 (both left and right) to the boost mixers diff --git a/examples/wm8960_03_INPUT1.py b/examples/wm8960_03_INPUT1.py index cb68c35..6cac06e 100644 --- a/examples/wm8960_03_INPUT1.py +++ b/examples/wm8960_03_INPUT1.py @@ -53,10 +53,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow through the analog audio bypass connections codec.enableLMIC() codec.enableRMIC() diff --git a/examples/wm8960_04_Speaker.py b/examples/wm8960_04_Speaker.py index 572cdfc..d78c8a4 100644 --- a/examples/wm8960_04_Speaker.py +++ b/examples/wm8960_04_Speaker.py @@ -60,10 +60,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow through the analog audio bypass connections codec.enableLMIC() codec.enableRMIC() diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_05_Loopback.py index ad3b239..914417b 100644 --- a/examples/wm8960_05_Loopback.py +++ b/examples/wm8960_05_Loopback.py @@ -52,10 +52,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() codec.enableRMIC() diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py index 743cea4..0ffbfda 100644 --- a/examples/wm8960_06_3D_Enhance.py +++ b/examples/wm8960_06_3D_Enhance.py @@ -61,10 +61,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() diff --git a/examples/wm8960_07_MicBias.py b/examples/wm8960_07_MicBias.py index 8f89681..aa84285 100644 --- a/examples/wm8960_07_MicBias.py +++ b/examples/wm8960_07_MicBias.py @@ -37,10 +37,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - codec.enableMicBias() # WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py index b51f36a..e94c161 100644 --- a/examples/wm8960_08_I2S_Passthrough.py +++ b/examples/wm8960_08_I2S_Passthrough.py @@ -59,10 +59,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py index 3169ff6..76abd01 100644 --- a/examples/wm8960_09_I2S_Bluetooth.py +++ b/examples/wm8960_09_I2S_Bluetooth.py @@ -59,10 +59,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Connect from DAC outputs to output mixer codec.enableLD2LO() codec.enableRD2RO() diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py index c5d8b5a..d52688e 100644 --- a/examples/wm8960_10_AdcGain.py +++ b/examples/wm8960_10_AdcGain.py @@ -79,10 +79,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() diff --git a/examples/wm8960_11_VolumePlotter.py b/examples/wm8960_11_VolumePlotter.py index 8d8ad61..0a60b26 100644 --- a/examples/wm8960_11_VolumePlotter.py +++ b/examples/wm8960_11_VolumePlotter.py @@ -57,10 +57,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_12_AutomaticLevelControl.py index 0d0319d..bc393d8 100644 --- a/examples/wm8960_12_AutomaticLevelControl.py +++ b/examples/wm8960_12_AutomaticLevelControl.py @@ -67,10 +67,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py index 19b9ee5..3e465eb 100644 --- a/examples/wm8960_13_DacGain.py +++ b/examples/wm8960_13_DacGain.py @@ -79,10 +79,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() diff --git a/examples/wm8960_14_ElectretMics.py b/examples/wm8960_14_ElectretMics.py index 1ece78f..5be1373 100644 --- a/examples/wm8960_14_ElectretMics.py +++ b/examples/wm8960_14_ElectretMics.py @@ -67,10 +67,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - codec.enableMicBias() # WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or diff --git a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py index e323047..ac0cc7d 100644 --- a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py +++ b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py @@ -64,10 +64,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Setup signal flow to the ADC codec.enableLMIC() codec.enableRMIC() diff --git a/examples/wm8960_eighties_dystopia.py b/examples/wm8960_eighties_dystopia.py index f0041eb..89ad76d 100644 --- a/examples/wm8960_eighties_dystopia.py +++ b/examples/wm8960_eighties_dystopia.py @@ -40,10 +40,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Connect from DAC outputs to output mixer codec.enableLD2LO() codec.enableRD2RO() diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index 7ccd03d..ed929f1 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -46,10 +46,6 @@ codec = adafruit_wm8960.WM8960(board.I2C()) -# General setup needed -codec.enableVREF() -codec.enableVMID() - # Connect from DAC outputs to output mixer codec.enableLD2LO() codec.enableRD2RO() From 40bb1eea3e2b905480c09d8fdcae532f9ee11fd3 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 12:08:10 -0500 Subject: [PATCH 11/56] Paired stereo functions added. --- adafruit_wm8960.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 9feefbd..c2be005 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -746,6 +746,16 @@ def adcLeftADCVUSet(self): def adcRightADCVUSet(self): self._writeRegisterBit(WM8960_REG_RIGHT_ADC_VOLUME, 8, 1) + # Control ADC volume in a stereo pair + def setAdcDigitalVolume(self, volume:int): + self.setAdcLeftDigitalVolume(volume) + self.setAdcRightDigitalVolume(volume) + + def setAdcDigitalVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() + volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) + self.setAdcDigitalVolume(volume) + ## ALC # Automatic Level Control @@ -1009,6 +1019,48 @@ def enableRI2MO(self): def disableRI2MO(self): self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 0) + # Paired stereo functions to enable/disable output mixers + def enableAdc2OutputMixer(self): + self.enableLI2LO() + self.enableRI2RO() + def disableAdc2OutputMixer(self): + self.disableLI2LO() + self.disableRI2RO() + def setAdc2MixerGain(self, volume:int): + self.setLI2LOVOL(volume) + self.setRI2ROVOL(volume) + + def enableBoost2OutputMixer(self): + self.enableLB2LO() + self.enableRB2RO() + def disableBoost2OutputMixer(self): + self.disableLB2LO() + self.disableRB2RO() + def setBoost2MixerGain(self, volume:int): + self.setLB2LOVOL(volume) + self.setRB2ROVOL(volume) + + def enableDac2OutputMixer(self): + self.enableLD2LO() + self.enableRD2RO() + def disableDac2OutputMixer(self): + self.disableLD2LO() + self.disableRD2RO() + + def enableAdc2MonoOutput(self): + self.enableLI2MO() + self.enableRI2MO() + def disableAdc2MonoOutput(self): + self.disableLI2MO() + self.disableRI2MO() + + def enableOutputMixer(self): + self.enableLOMIX() + self.enableROMIX() + def disableOutputMixer(self): + self.disableLOMIX() + self.disableROMIX() + # Sets the VMID signal to one of three possible settings. # 4 options: # WM8960_VMIDSEL_DISABLED From a368bda9c8910dc1613f796d2adbd3b78fea5df1 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 12:21:24 -0500 Subject: [PATCH 12/56] Configuration utilities for I2S, DAC, headphones, and speakers. --- adafruit_wm8960.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index c2be005..216f8f4 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -403,6 +403,86 @@ def __init__(self, i2c_bus:I2C, address:int = WM8960_ADDR): def isConnected(self) -> bool: # TODO: Check I2C or I2CDevice return True + + def _getDivBit(self, src:int, dest:int): + div = (src * 2) // dest + if div < 2 or div > 12: return 0 + if div == 2: div = 0 + elif div == 3: div = 2 + elif div == 10: return 0 + elif div == 11: div = 10 + if div % 2 == 1: return 0 + return div // 2 + + def configureI2S(self, sample_rate:int, word_length:int = WM8960_WL_16BIT, master:bool = False): + # MCLK = 24 MHz + self.enablePLL() # Needed for class-d amp clock + self.setSMD(WM8960_PLL_MODE_FRACTIONAL) + self.setCLKSEL(WM8960_CLKSEL_PLL) + + self.setPLLPRESCALE(WM8960_PLLPRESCALE_DIV_2) + self.setSYSCLKDIV(WM8960_SYSCLK_DIV_BY_2) + self.setBCLKDIV(4) + + self.setDCLKDIV(WM8960_DCLKDIV_16) + + if sample_rate in [8000, 12000, 16000, 24000, 32000, 48000]: + # SYSCLK = 12.288 MHz + # DCLK = 768.0kHz + self.setPLLN(8) + self.setPLLK(0x3126E8) + + div = self._getDivBit(48000, sample_rate) + self.setADCDIV(div) + self.setDACDIV(div) + elif sample_rate in [11025, 22050, 44100]: + # SYSCLK = 11.2896 MHz + # DCLK = 705.6kHz + self.setPLLN(7) + self.setPLLK(0x86C226) + + div = self._getDivBit(44100, sample_rate) + self.setADCDIV(div) + self.setDACDIV(div) + else: + raise Exception("Invalid sample rate") + + self.setWL(word_length) + + if master: + self.enableMasterMode() + else: + self.enablePeripheralMode() + + def configureDAC(self, loopback:bool = False): + # Connect from DAC outputs to output mixer + self.enableDac2OutputMixer() + # Enable output mixers + self.enableOutputMixer() + # Enable DACs + self.enableDac() + # Default is "soft mute" on, so we must disable mute to make channels active + self.disableDacMute() + + # Loopback sends ADC data directly into DAC + if loopback: + self.enableLoopBack() + else: + self.disableLoopBack() + + def configureHeadphones(self, dB:float = 0.0, capless:bool = True): + # Enable headphone output + self.enableHeadphones() + # Provides VMID as buffer for headphone ground on OUT3 + if capless: self.enableOUT3MIX() + # Adjust headphone volume + self.setHeadphoneVolumeDB(dB) + + def configureSpeakers(self, dB:float = 0.0): + # Enable speaker output + self.enableSpeakers() + # Adjust speaker volume + self.setSpeakerVolumeDB(dB) ''' Necessary for all other functions of the CODEC From 5fb70c5ca3d52fcad7d62fd121ca720eedc06646 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 14:00:46 -0500 Subject: [PATCH 13/56] Separate sample rate calculation from configuration method. --- adafruit_wm8960.py | 67 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 216f8f4..3433d83 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -415,40 +415,8 @@ def _getDivBit(self, src:int, dest:int): return div // 2 def configureI2S(self, sample_rate:int, word_length:int = WM8960_WL_16BIT, master:bool = False): - # MCLK = 24 MHz - self.enablePLL() # Needed for class-d amp clock - self.setSMD(WM8960_PLL_MODE_FRACTIONAL) - self.setCLKSEL(WM8960_CLKSEL_PLL) - - self.setPLLPRESCALE(WM8960_PLLPRESCALE_DIV_2) - self.setSYSCLKDIV(WM8960_SYSCLK_DIV_BY_2) - self.setBCLKDIV(4) - - self.setDCLKDIV(WM8960_DCLKDIV_16) - - if sample_rate in [8000, 12000, 16000, 24000, 32000, 48000]: - # SYSCLK = 12.288 MHz - # DCLK = 768.0kHz - self.setPLLN(8) - self.setPLLK(0x3126E8) - - div = self._getDivBit(48000, sample_rate) - self.setADCDIV(div) - self.setDACDIV(div) - elif sample_rate in [11025, 22050, 44100]: - # SYSCLK = 11.2896 MHz - # DCLK = 705.6kHz - self.setPLLN(7) - self.setPLLK(0x86C226) - - div = self._getDivBit(44100, sample_rate) - self.setADCDIV(div) - self.setDACDIV(div) - else: - raise Exception("Invalid sample rate") - + self.setSampleRate(sample_rate) self.setWL(word_length) - if master: self.enableMasterMode() else: @@ -484,6 +452,39 @@ def configureSpeakers(self, dB:float = 0.0): # Adjust speaker volume self.setSpeakerVolumeDB(dB) + def setSampleRate(self, sample_rate:int): + # MCLK = 24 MHz + self.enablePLL() # Needed for class-d amp clock + self.setSMD(WM8960_PLL_MODE_FRACTIONAL) + self.setCLKSEL(WM8960_CLKSEL_PLL) + + self.setPLLPRESCALE(WM8960_PLLPRESCALE_DIV_2) + self.setSYSCLKDIV(WM8960_SYSCLK_DIV_BY_2) + self.setBCLKDIV(4) + + self.setDCLKDIV(WM8960_DCLKDIV_16) + + if sample_rate in [8000, 12000, 16000, 24000, 32000, 48000]: + # SYSCLK = 12.288 MHz + # DCLK = 768.0kHz + self.setPLLN(8) + self.setPLLK(0x3126E8) + + div = self._getDivBit(48000, sample_rate) + self.setADCDIV(div) + self.setDACDIV(div) + elif sample_rate in [11025, 22050, 44100]: + # SYSCLK = 11.2896 MHz + # DCLK = 705.6kHz + self.setPLLN(7) + self.setPLLK(0x86C226) + + div = self._getDivBit(44100, sample_rate) + self.setADCDIV(div) + self.setDACDIV(div) + else: + raise Exception("Invalid sample rate") + ''' Necessary for all other functions of the CODEC VREF is a single bit we can flip in Register 25 (19h), WM8960_REG_PWR_MGMT_1 From 4a22db3f2012bba0a96d6f7e7143aaddc03a2db1 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 14:34:59 -0500 Subject: [PATCH 14/56] Additional stereo pair methods. --- adafruit_wm8960.py | 98 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 3433d83..2348dd5 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -91,6 +91,9 @@ WM8960_PGAR_RINPUT2 = 0 WM8960_PGAR_RINPUT3 = 1 WM8960_PGAR_VMID = 2 +WM8960_PGA_INPUT2 = 0 +WM8960_PGA_INPUT3 = 1 +WM8960_PGA_VMID = 2 # Mic (aka PGA) BOOST gain options WM8960_MIC_BOOST_GAIN_0DB = 0 @@ -419,6 +422,7 @@ def configureI2S(self, sample_rate:int, word_length:int = WM8960_WL_16BIT, maste self.setWL(word_length) if master: self.enableMasterMode() + self.setALRCGPIO() # Note, should not be changed while ADC is enabled. else: self.enablePeripheralMode() @@ -521,6 +525,13 @@ def enableAINR(self) -> None: def disableAINR(self) -> None: self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 0) + def enableAIN(self) -> None: + self.enableAINL() + self.enableAINR() + def disableAIN(self) -> None: + self.disableAINL() + self.disableAINR() + def enableLMIC(self) -> None: self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) def disableLMIC(self) -> None: @@ -531,6 +542,13 @@ def enableRMIC(self) -> None: def disableRMIC(self) -> None: self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + def enableMIC(self) -> None: + self.enableLMIC() + self.enableRMIC() + def disableMIC(self) -> None: + self.disableLMIC() + self.disableRMIC() + def enableLMICBOOST(self) -> None: self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) def disableLMICBOOST(self) -> None: @@ -541,6 +559,13 @@ def enableRMICBOOST(self) -> None: def disableRMICBOOST(self) -> None: self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + def enableMICBOOST(self) -> None: + self.enableLMICBOOST() + self.enableRMICBOOST() + def disableMICBOOST(self) -> None: + self.disableLMICBOOST() + self.disableRMICBOOST() + # PGA input signal select # Each PGA (left and right) has a switch on its non-inverting input. # On PGA_LEFT: @@ -602,6 +627,11 @@ def pgaRightNonInvSignalSelect(self, signal:int): # Don't set any bits. When both RMP2 and RMP3 are cleared, then the signal is set to VMID pass + # 3 options: WM8960_PGA_INPUT2, WM8960_PGA_INPUT3, WM8960_PGA_VMID + def pgaNonInvSignalSelect(self, signal:int): + self.pgaLeftNonInvSignalSelect(signal) + self.pgaRightNonInvSignalSelect(signal) + # Connections from each INPUT1 to the inverting input of its PGA # Connect LINPUT1 to inverting input of Left Input PGA @@ -619,6 +649,13 @@ def connectRMN1(self): # Disconnect RINPUT1 from inverting input of Right Input PGA def disconnectRMN1(self): self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 0) + + def connectMN1(self): + self.connectLMN1() + self.connectRMN1() + def disconnectMN1(self): + self.disconnectLMN1() + self.disconnectRMN1() # Connection from output of PGAs to downstream "boost mixers" @@ -638,6 +675,13 @@ def connectRMIC2B(self): def disconnectRMIC2B(self): self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 0) + def connectMIC2B(self): + self.connectLMIC2B() + self.connectRMIC2B() + def disconnectMIC2B(self): + self.disconnectLMIC2B() + self.disconnectRMIC2B() + # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) def setLINVOL(self, volume:int): # Limit incoming values max @@ -680,6 +724,16 @@ def setRINVOLDB(self, dB:float): volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) self.setRINVOL(volume) + def setINVOL(self, volume:int): + self.setLINVOL(volume) + self.setRINVOL(volume) + + def setINVOLDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setLINVOL() + volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) + self.setLINVOL(volume) + self.setRINVOL(volume) + # Zero Cross prevents zipper sounds on volume changes # Sets both left and right PGAs def enablePgaZeroCross(self): @@ -701,6 +755,13 @@ def disableRINMUTE(self): self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 0) self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) + def enableINMUTE(self): + self.enableLINMUTE() + self.enableRINMUTE() + def disableINMUTE(self): + self.disableLINMUTE() + self.disableRINMUTE() + # Causes left and right input PGA volumes to be updated # (LINVOL and RINVOL) def pgaLeftIPVUSet(self): @@ -727,33 +788,45 @@ def setRMICBOOST(self, boost_gain:int): boost_gain = 3 self._writeRegisterMultiBits(WM8960_REG_ADCR_SIGNAL_PATH, 5, 4, boost_gain) + def setMICBOOST(self, boost_gain:int): + self.setLMICBOOST(boost_gain) + self.setRMICBOOST(boost_gain) + # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setLIN3BOOST(self, boost_gain:int): + def setLIN2BOOST(self, boost_gain:int): # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setLIN2BOOST(self, boost_gain:int): + def setRIN2BOOST(self, boost_gain:int): # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) + + def setIN2BOOST(self, boost_gain:int): + self.setLIN2BOOST(boost_gain) + self.setRIN2BOOST(boost_gain) # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setRIN3BOOST(self, boost_gain:int): + def setLIN3BOOST(self, boost_gain:int): # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setRIN2BOOST(self, boost_gain:int): + def setRIN3BOOST(self, boost_gain:int): # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) + self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) + + def setIN3BOOST(self, boost_gain:int): + self.setLIN3BOOST(boost_gain) + self.setRIN3BOOST(boost_gain) # Mic Bias control def enableMicBias(self): @@ -978,6 +1051,15 @@ def dacLeftDACVUSet(self): def dacRightDACVUSet(self): self._writeRegisterBit(WM8960_REG_RIGHT_DAC_VOLUME, 8, 1) + def setDacDigitalVolume(self, volume:int): + self.setDacLeftDigitalVolume(volume) + self.setDacRightDigitalVolume(volume) + + def setDacDigitalVolumeDB(self, dB:float): + # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() + volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) + self.setDacDigitalVolume(volume) + # DAC mute def enableDacMute(self): self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 1) From 23d81eedaee265677a2d4cfd42bfa45e74d1af40 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 14:35:35 -0500 Subject: [PATCH 15/56] Updated examples to use stereo pair methods. --- examples/wm8960_01_Volume.py | 20 ++---- examples/wm8960_02_INPUT2.py | 20 ++---- examples/wm8960_03_INPUT1.py | 32 +++------ examples/wm8960_04_Speaker.py | 53 +++++--------- examples/wm8960_05_Loopback.py | 51 ++++--------- examples/wm8960_06_3D_Enhance.py | 53 ++++---------- examples/wm8960_07_MicBias.py | 4 +- examples/wm8960_08_I2S_Passthrough.py | 60 ++++------------ examples/wm8960_09_I2S_Bluetooth.py | 35 ++------- examples/wm8960_10_AdcGain.py | 55 ++++---------- examples/wm8960_11_VolumePlotter.py | 68 +++++------------- examples/wm8960_12_AutomaticLevelControl.py | 54 ++++---------- examples/wm8960_13_DacGain.py | 59 +++++---------- examples/wm8960_14_ElectretMics.py | 40 ++++------- ..._15_VolumePlotter_MEMS_Mic_Differential.py | 72 +++++-------------- examples/wm8960_eighties_dystopia.py | 49 +++---------- examples/wm8960_simpletest.py | 49 +++---------- 17 files changed, 204 insertions(+), 570 deletions(-) diff --git a/examples/wm8960_01_Volume.py b/examples/wm8960_01_Volume.py index f36831f..dd828d1 100644 --- a/examples/wm8960_01_Volume.py +++ b/examples/wm8960_01_Volume.py @@ -56,22 +56,16 @@ # Setup signal flow through the analog audio bypass connections -# Enable left output mixer -codec.enableLOMIX() +# Enable output mixer +codec.enableOutputMixer() -# Enable bypass connection from Left INPUT3 to Left output mixer, note, the default gain on this input (LI2LOVOL) is -15dB -codec.enableLI2LO() +# Enable bypass connection from Left/Right INPUT3 to Left/Right output mixer, note, the default gain on these inputs (LI2LOVOL/RI2ROVOL) is -15dB +codec.enableAdc2OutputMixer() -# Sets volume control between "left input" to "left output mixer" -codec.setLI2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +# Sets volume control between "left/right input" to "left/right output mixer" +codec.setAdc2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) -# Now for the right channel of INPUT3 -codec.enableROMIX() -codec.enableRI2RO() -codec.setRI2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) - -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground +codec.configureHeadphones(capless=True) # Capless provides VMID as buffer for headphone ground print("Volume set to +6.00dB (max)") codec.setHeadphoneVolumeDB(6.00) diff --git a/examples/wm8960_02_INPUT2.py b/examples/wm8960_02_INPUT2.py index 1e5a571..d2eaed4 100644 --- a/examples/wm8960_02_INPUT2.py +++ b/examples/wm8960_02_INPUT2.py @@ -56,29 +56,21 @@ # Setup signal flow through the analog audio bypass connections # Set input boosts to get INPUT2 (both left and right) to the boost mixers -codec.setLIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB) -codec.setRIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB) +codec.setIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB)) # Enable input boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Connect LB2LO (booster to output mixer [aka analog bypass]) -codec.enableLB2LO() -codec.enableRB2RO() +codec.enableBoost2OutputMixer() # Set gainstage between boost mixer and output mixers (analog bypass) -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground +codec.enableOutputMixer() print("Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Example complete. Listen to inputs 2 on headphone outputs.") diff --git a/examples/wm8960_03_INPUT1.py b/examples/wm8960_03_INPUT1.py index 6cac06e..42a345a 100644 --- a/examples/wm8960_03_INPUT1.py +++ b/examples/wm8960_03_INPUT1.py @@ -54,44 +54,32 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow through the analog audio bypass connections -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableLB2LO() -codec.enableRB2RO() +codec.enableBoost2OutputMixer() # Set gainstage between booster mixer and output mixer -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground +codec.enableOutputMixer() print("Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Example complete. Listen to INPUT1 on headphone outputs.") diff --git a/examples/wm8960_04_Speaker.py b/examples/wm8960_04_Speaker.py index d78c8a4..c1cf087 100644 --- a/examples/wm8960_04_Speaker.py +++ b/examples/wm8960_04_Speaker.py @@ -61,53 +61,34 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow through the analog audio bypass connections -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +# Disable mutes on PGA inputs (aka INPUT1) +codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) - -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() -# Connect LB2LO (booster to output mixer (analog bypass) -codec.enableLB2LO() -codec.enableRB2RO() +# Connect booster to output mixer (analog bypass) +codec.enableBoost2OutputMixer() # Set gainstage between booster mixer and output mixer -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) - -codec.enableSpeakers() - -print("Volume set to +0dB") -codec.setSpeakerVolumeDB(0.00) +codec.enableOutputMixer() + +# Set up clock for 44.1KHz +codec.configureI2S(44100) + +# Enable speakers at default volume of 0dB +codec.configureSpeakers() print("Example complete. Listen to left/right INPUT1 on Speaker outputs.") diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_05_Loopback.py index 914417b..47aa97a 100644 --- a/examples/wm8960_05_Loopback.py +++ b/examples/wm8960_05_Loopback.py @@ -53,59 +53,37 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) - -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableLB2LO() -codec.disableRB2RO() +codec.disableBoost2OutputMixer() # Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() +codec.enableDac2OutputMixer() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.enableOutputMixer() +# Setup sample rate +codec.setSampleRate(44100) codec.enableMasterMode() codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. @@ -119,10 +97,7 @@ # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - print("Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Example complete. Listen to left/right INPUT1 on Headphone outputs.") diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py index 0ffbfda..8e64863 100644 --- a/examples/wm8960_06_3D_Enhance.py +++ b/examples/wm8960_06_3D_Enhance.py @@ -62,60 +62,37 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC - -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) - -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableLB2LO() -codec.disableRB2RO() +codec.disableBoost2OutputMixer() # Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() +codec.enableDac2OutputMixer() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.enableOutputMixer() +# Setup sample rate +codec.setSampleRate(44100) codec.enableMasterMode() codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. @@ -129,11 +106,9 @@ # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - print("Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground + print("3D Enhance Depth set to 15 (max)") codec.set3dDepth(15) diff --git a/examples/wm8960_07_MicBias.py b/examples/wm8960_07_MicBias.py index aa84285..c6110eb 100644 --- a/examples/wm8960_07_MicBias.py +++ b/examples/wm8960_07_MicBias.py @@ -43,13 +43,13 @@ # WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD) print("Mic Bias enabled (0.9*AVDD)") -delay(3000) +time.sleep(3.0) # WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or # WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD) print("Mic Bias enabled (0.65*AVDD)") -delay(3000) +time.sleep(3.0) codec.disableMicBias() print("Mic Bias disabled") diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py index e94c161..00416e8 100644 --- a/examples/wm8960_08_I2S_Passthrough.py +++ b/examples/wm8960_08_I2S_Passthrough.py @@ -60,69 +60,42 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC - -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set pga volumes -codec.setLINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) -codec.setRINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) +codec.setINVOLDB(0.0) # Valid options are -17.25dB to +30dB (0.75dB steps) # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) # Connect from MIC inputs (aka pga output) to boost mixers -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableLB2LO() -codec.disableRB2RO() +codec.disableBoost2OutputMixer() # Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() +codec.enableDac2OutputMixer() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) -codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) - -codec.enablePeripheralMode() -#codec.enableMasterMode() -#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. +codec.enableOutputMixer() + +# Set sample rate, word length, and mode +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) # Enable ADCs and DACs codec.enableAdc() @@ -134,11 +107,8 @@ # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - print("Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Codec Setup complete. Listen to left/right INPUT1 on Headphone outputs.") diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py index 76abd01..d885213 100644 --- a/examples/wm8960_09_I2S_Bluetooth.py +++ b/examples/wm8960_09_I2S_Bluetooth.py @@ -60,35 +60,17 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() +codec.enableDac2OutputMixer() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) -codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) - -codec.enablePeripheralMode() -#codec.enableMasterMode() -#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. +codec.enableOutputMixer() + +# Setup sample rate, word length, and I2S mode +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) # Enable DACs codec.enableDac() @@ -99,11 +81,8 @@ # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - print("Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Codec Setup complete. Connect via Bluetooth, play music, and listen on Headphone outputs.") diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py index d52688e..8abf07f 100644 --- a/examples/wm8960_10_AdcGain.py +++ b/examples/wm8960_10_AdcGain.py @@ -80,60 +80,37 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC - -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) - -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableLB2LO() -codec.disableRB2RO() +codec.disableBoost2OutputMixer() # Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() +codec.enableDac2OutputMixer() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.enableOutputMixer() +# Setup clock and mode +codec.setSampleRate(44100) codec.enableMasterMode() codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. @@ -147,11 +124,8 @@ # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - print("Headphone Amp Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") @@ -169,7 +143,6 @@ print("adcVolumeDB: ", adcVolumeDB) - codec.setAdcLeftDigitalVolumeDB(adcVolumeDB) # -97.50 to +30.00dB - codec.setAdcRightDigitalVolumeDB(adcVolumeDB) # -97.50 to +30.00dB + codec.setAdcDigitalVolumeDB(adcVolumeDB) # -97.50 to +30.00dB time.sleep(0.05) diff --git a/examples/wm8960_11_VolumePlotter.py b/examples/wm8960_11_VolumePlotter.py index 0a60b26..217fa41 100644 --- a/examples/wm8960_11_VolumePlotter.py +++ b/examples/wm8960_11_VolumePlotter.py @@ -58,85 +58,53 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC - -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set pga volumes -codec.setLINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) -codec.setRINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) +codec.setINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) # Connect from MIC inputs (aka pga output) to boost mixers -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableLB2LO() -codec.enableRB2RO() +codec.enableBoost2OutputMixer() # Disconnect from DAC outputs to output mixer -codec.disableLD2LO() -codec.disableRD2RO() +codec.disableDac2OutputMixer() # Set gainstage between booster mixer and output mixer -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) -codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) - -codec.enablePeripheralMode() -#codec.enableMasterMode() -#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. +codec.enableOutputMixer() + +# Configure I2S +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) # Enable ADCs, and disable DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.disableDacLeft() -codec.disableDacRight() +codec.enableAdc() +codec.disableDac() codec.disableDacMute() -#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC codec.disableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active codec.enableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground # Set up I2S audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_12_AutomaticLevelControl.py index bc393d8..c8cdc9e 100644 --- a/examples/wm8960_12_AutomaticLevelControl.py +++ b/examples/wm8960_12_AutomaticLevelControl.py @@ -68,60 +68,37 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC - -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) - -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableLB2LO() -codec.disableRB2RO() +codec.disableBoost2OutputMixer() # Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() +codec.enableDac2OutputMixer() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.enableOutputMixer() +# Setup clock and mode +codec.setSampleRate(44100) codec.enableMasterMode() codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. @@ -135,8 +112,8 @@ # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground +print("Headphopne Amp Volume set to +0dB") +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground # Automatic Level control stuff @@ -152,9 +129,6 @@ codec.setAlcMinGain(adafruit_wm8960.WM8960_ALC_MIN_GAIN_LEVEL_NEG_17_25DB) codec.setAlcHold(adafruit_wm8960.WM8960_ALC_HOLD_TIME_0MS) -print("Headphopne Amp Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) - print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") while True: diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py index 3e465eb..98328f0 100644 --- a/examples/wm8960_13_DacGain.py +++ b/examples/wm8960_13_DacGain.py @@ -80,60 +80,37 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC - -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) - -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableLB2LO() -codec.disableRB2RO() +codec.disableBoost2OutputMixer() # Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() +codec.enableDac2OutputMixer() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) +codec.enableOutputMixer() +# Set sample rate and mode +codec.setSampleRate(44100) codec.enableMasterMode() codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. @@ -147,19 +124,16 @@ # Default is "soft mute" on, so we must disable mute to make channels active codec.disableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - print("Headphopne Amp Volume set to +0dB") -codec.setHeadphoneVolumeDB(0.00) +codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") while True: # Take a bunch of readings and average them, to smooth out the value for i in range(250): - userInputA0 += analog_in.value - time.sleep(0.001) + userInputA0 += analog_in.value + time.sleep(0.001) # After taking a bunch of samples, divide down to the average single reading userInputA0 /= 250.0 @@ -169,7 +143,6 @@ print("dacVolumeDB: ", dacVolumeDB) - codec.setDacLeftDigitalVolumeDB(dacVolumeDB) # -97.50 to +30.00dB - codec.setDacRightDigitalVolumeDB(dacVolumeDB) # -97.50 to +30.00dB + codec.setDacDigitalVolumeDB(dacVolumeDB) # -97.50 to +30.00dB time.sleep(0.05) diff --git a/examples/wm8960_14_ElectretMics.py b/examples/wm8960_14_ElectretMics.py index 5be1373..dacb0ac 100644 --- a/examples/wm8960_14_ElectretMics.py +++ b/examples/wm8960_14_ElectretMics.py @@ -75,57 +75,41 @@ print("Mic Bias enabled (0.9*AVDD)") # Setup signal flow through the analog audio bypass connections -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set pga volumes -codec.setLINVOLDB(24.00) # Valid options are -17.25dB to +30.00dB -codec.setRINVOLDB(24.00) # Valid options are -17.25dB to +30.00dB +codec.setINVOLDB(24.00) # Valid options are -17.25dB to +30.00dB print("PGA gain set to +24dB") # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) print("Mic boost stage set to 0dB") # For MIC+ signal of differential mic signal -codec.pgaLeftNonInvSignalSelect(adafruit_wm8960.WM8960_PGAL_LINPUT2) - -# For MIC+ signal of differential mic signal -codec.pgaRightNonInvSignalSelect(adafruit_wm8960.WM8960_PGAR_RINPUT2) +codec.pgaNonInvSignalSelect(adafruit_wm8960.WM8960_PGA_INPUT2) print("Pga non-inverting inputs set to INPUT2s") -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableLB2LO() -codec.enableRB2RO() +codec.enableBoost2OutputMixer() # Set gainstage between booster mixer and output mixer -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground +codec.enableOutputMixer() print("Headphone output buffer volume set to +6dB (max)") -codec.setHeadphoneVolumeDB(6.00) +codec.configureHeadphones(dB=6.0, capless=True) # Capless provides VMID as buffer for headphone ground print("Example complete. Listen to Electret mics on headphone outputs.") diff --git a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py index ac0cc7d..8aad9fb 100644 --- a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py +++ b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py @@ -65,90 +65,56 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC -codec.enableLMIC() -codec.enableRMIC() +codec.enableMIC() # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectLMN1() -codec.connectRMN1() +codec.connectMN1() # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableLINMUTE() -codec.disableRINMUTE() +codec.disableINMUTE() # Set pga volumes -codec.setLINVOLDB(8.25) # Valid options are -17.25dB to +30dB (0.75dB steps) -codec.setRINVOLDB(8.25) # Valid options are -17.25dB to +30dB (0.75dB steps) +codec.setINVOLDB(8.25) # Valid options are -17.25dB to +30dB (0.75dB steps) # Set input boosts to get inputs 1 to the boost mixers -codec.setLMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_29DB) -codec.setRMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_29DB) +codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_29DB) # For MIC+ signal of differential mic signal -codec.pgaLeftNonInvSignalSelect(adafruit_wm8960.WM8960_PGAL_LINPUT2) - -# For MIC+ signal of differential mic signal -codec.pgaRightNonInvSignalSelect(adafruit_wm8960.WM8960_PGAR_RINPUT2) +codec.pgaNonInvSignalSelect(adafruit_wm8960.WM8960_PGA_INPUT2) # Connect from MIC inputs (aka pga output) to boost mixers -codec.connectLMIC2B() -codec.connectRMIC2B() +codec.connectMIC2B() # Enable boost mixers -codec.enableAINL() -codec.enableAINR() +codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableLB2LO() -codec.enableRB2RO() +codec.enableBoost2OutputMixer() # Disconnect from DAC outputs to output mixer -codec.disableLD2LO() -codec.disableRD2RO() +codec.disableDac2OutputMixer() # Set gainstage between booster mixer and output mixer -codec.setLB2LOVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) -codec.setRB2ROVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) -codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) - -codec.enablePeripheralMode() -#codec.enableMasterMode() -#codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. +codec.enableOutputMixer() + +# Set sample rate, word length, and mode +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) # Enable ADCs, and disable DACs -codec.enableAdcLeft() -codec.enableAdcRight() -codec.disableDacLeft() -codec.disableDacRight() +codec.enableAdc() +codec.disableDac() codec.disableDacMute() -#codec.enableLoopBack() # Loopback sends ADC data directly into DAC +# Loopback sends ADC data directly into DAC codec.disableLoopBack() # Default is "soft mute" on, so we must disable mute to make channels active codec.enableDacMute() -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground - -codec.setHeadphoneVolumeDB(6.00) +codec.configureHeadphones(dB=6.0, capless=True) # Capless provides VMID as buffer for headphone ground # Set up I2S audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) diff --git a/examples/wm8960_eighties_dystopia.py b/examples/wm8960_eighties_dystopia.py index 89ad76d..9c4815f 100644 --- a/examples/wm8960_eighties_dystopia.py +++ b/examples/wm8960_eighties_dystopia.py @@ -38,47 +38,18 @@ import digitalio import ulab.numpy as np +sample_rate = 44100 + codec = adafruit_wm8960.WM8960(board.I2C()) -# Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() - -# Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) -codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) - -codec.enablePeripheralMode() - -# Enable DACs -codec.enableDacLeft() -codec.enableDacRight() -codec.disableDacMute() - -# Loopback sends ADC data directly into DAC -codec.disableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() - -# Enable Headphones -codec.enableHeadphones() -codec.enableOUT3MIX() # Provides VMID as buffer for headphone ground -codec.setHeadphoneVolumeDB(0.0) +# Set up codec as peripheral device with clock configured for desired sample rate (and 16-bit words) +codec.configureI2S(sample_rate) + +# Enable DAC and output mixer +codec.configureDAC() + +# Enable headphone output with OUT3 as capless buffer for headphone ground, adjust volume with dB +codec.configureHeadphones(dB=0.0, capless=True) audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index ed929f1..142b216 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -44,47 +44,18 @@ import time import digitalio +sample_rate = 44100 + codec = adafruit_wm8960.WM8960(board.I2C()) -# Connect from DAC outputs to output mixer -codec.enableLD2LO() -codec.enableRD2RO() - -# Enable output mixers -codec.enableLOMIX() -codec.enableROMIX() - -# CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d freq at 705.6kHz -codec.enablePLL() # Needed for class-d amp clock -codec.setPLLPRESCALE(adafruit_wm8960.WM8960_PLLPRESCALE_DIV_2) -codec.setSMD(adafruit_wm8960.WM8960_PLL_MODE_FRACTIONAL) -codec.setCLKSEL(adafruit_wm8960.WM8960_CLKSEL_PLL) -codec.setSYSCLKDIV(adafruit_wm8960.WM8960_SYSCLK_DIV_BY_2) -codec.setBCLKDIV(4) -codec.setDCLKDIV(adafruit_wm8960.WM8960_DCLKDIV_16) -codec.setPLLN(7) -codec.setPLLK(0x86C226) -#codec.setADCDIV(0) # Default is 000 (what we need for 44.1KHz) -#codec.setDACDIV(0) # Default is 000 (what we need for 44.1KHz) -codec.setWL(adafruit_wm8960.WM8960_WL_16BIT) - -codec.enablePeripheralMode() - -# Enable DACs -codec.enableDac() - -# Loopback sends ADC data directly into DAC -codec.disableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() - -# Enable headphone output and provides VMID as buffer for headphone ground -codec.enableHeadphones() -codec.enableOUT3MIX() - -# Adjust headphone volume -codec.setHeadphoneVolumeDB(0.0) +# Set up codec as peripheral device with clock configured for desired sample rate (and 16-bit words) +codec.configureI2S(sample_rate) + +# Enable DAC and output mixer +codec.configureDAC() + +# Enable headphone output with OUT3 as capless buffer for headphone ground, adjust volume with dB +codec.configureHeadphones(dB=0.0, capless=True) audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) synth = synthio.Synthesizer(sample_rate=44100) From 248daedcacae4ce9f2e8ee91bd91946cbf403539 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 14:50:16 -0500 Subject: [PATCH 16/56] Updated stereo pairing output mixer method names to better represent the original library. --- adafruit_wm8960.py | 28 +++++++++---------- examples/wm8960_01_Volume.py | 6 ++-- examples/wm8960_02_INPUT2.py | 6 ++-- examples/wm8960_03_INPUT1.py | 6 ++-- examples/wm8960_04_Speaker.py | 6 ++-- examples/wm8960_05_Loopback.py | 8 +++--- examples/wm8960_06_3D_Enhance.py | 8 +++--- examples/wm8960_08_I2S_Passthrough.py | 8 +++--- examples/wm8960_09_I2S_Bluetooth.py | 6 ++-- examples/wm8960_10_AdcGain.py | 8 +++--- examples/wm8960_11_VolumePlotter.py | 8 +++--- examples/wm8960_12_AutomaticLevelControl.py | 8 +++--- examples/wm8960_13_DacGain.py | 8 +++--- examples/wm8960_14_ElectretMics.py | 6 ++-- ..._15_VolumePlotter_MEMS_Mic_Differential.py | 8 +++--- 15 files changed, 64 insertions(+), 64 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 2348dd5..7b99a9e 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -428,9 +428,9 @@ def configureI2S(self, sample_rate:int, word_length:int = WM8960_WL_16BIT, maste def configureDAC(self, loopback:bool = False): # Connect from DAC outputs to output mixer - self.enableDac2OutputMixer() + self.enableD2O() # Enable output mixers - self.enableOutputMixer() + self.enableOMIX() # Enable DACs self.enableDac() # Default is "soft mute" on, so we must disable mute to make channels active @@ -1183,44 +1183,44 @@ def disableRI2MO(self): self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 0) # Paired stereo functions to enable/disable output mixers - def enableAdc2OutputMixer(self): + def enableI2O(self): self.enableLI2LO() self.enableRI2RO() - def disableAdc2OutputMixer(self): + def disableI2O(self): self.disableLI2LO() self.disableRI2RO() - def setAdc2MixerGain(self, volume:int): + def setI2OVOL(self, volume:int): self.setLI2LOVOL(volume) self.setRI2ROVOL(volume) - def enableBoost2OutputMixer(self): + def enableB2O(self): self.enableLB2LO() self.enableRB2RO() - def disableBoost2OutputMixer(self): + def disableB2O(self): self.disableLB2LO() self.disableRB2RO() - def setBoost2MixerGain(self, volume:int): + def setB2OVOL(self, volume:int): self.setLB2LOVOL(volume) self.setRB2ROVOL(volume) - def enableDac2OutputMixer(self): + def enableD2O(self): self.enableLD2LO() self.enableRD2RO() - def disableDac2OutputMixer(self): + def disableD2O(self): self.disableLD2LO() self.disableRD2RO() - def enableAdc2MonoOutput(self): + def enableI2MO(self): self.enableLI2MO() self.enableRI2MO() - def disableAdc2MonoOutput(self): + def disableI2MO(self): self.disableLI2MO() self.disableRI2MO() - def enableOutputMixer(self): + def enableOMIX(self): self.enableLOMIX() self.enableROMIX() - def disableOutputMixer(self): + def disableOMIX(self): self.disableLOMIX() self.disableROMIX() diff --git a/examples/wm8960_01_Volume.py b/examples/wm8960_01_Volume.py index dd828d1..be41964 100644 --- a/examples/wm8960_01_Volume.py +++ b/examples/wm8960_01_Volume.py @@ -57,13 +57,13 @@ # Setup signal flow through the analog audio bypass connections # Enable output mixer -codec.enableOutputMixer() +codec.enableOMIX() # Enable bypass connection from Left/Right INPUT3 to Left/Right output mixer, note, the default gain on these inputs (LI2LOVOL/RI2ROVOL) is -15dB -codec.enableAdc2OutputMixer() +codec.enableI2O() # Sets volume control between "left/right input" to "left/right output mixer" -codec.setAdc2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setI2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) codec.configureHeadphones(capless=True) # Capless provides VMID as buffer for headphone ground diff --git a/examples/wm8960_02_INPUT2.py b/examples/wm8960_02_INPUT2.py index d2eaed4..fbc5197 100644 --- a/examples/wm8960_02_INPUT2.py +++ b/examples/wm8960_02_INPUT2.py @@ -62,13 +62,13 @@ codec.enableAIN() # Connect LB2LO (booster to output mixer [aka analog bypass]) -codec.enableBoost2OutputMixer() +codec.enableB2O() # Set gainstage between boost mixer and output mixers (analog bypass) -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() print("Volume set to +0dB") codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground diff --git a/examples/wm8960_03_INPUT1.py b/examples/wm8960_03_INPUT1.py index 42a345a..58e6359 100644 --- a/examples/wm8960_03_INPUT1.py +++ b/examples/wm8960_03_INPUT1.py @@ -71,13 +71,13 @@ codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableBoost2OutputMixer() +codec.enableB2O() # Set gainstage between booster mixer and output mixer -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() print("Volume set to +0dB") codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground diff --git a/examples/wm8960_04_Speaker.py b/examples/wm8960_04_Speaker.py index c1cf087..cf1221e 100644 --- a/examples/wm8960_04_Speaker.py +++ b/examples/wm8960_04_Speaker.py @@ -77,13 +77,13 @@ codec.enableAIN() # Connect booster to output mixer (analog bypass) -codec.enableBoost2OutputMixer() +codec.enableB2O() # Set gainstage between booster mixer and output mixer -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Set up clock for 44.1KHz codec.configureI2S(44100) diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_05_Loopback.py index 47aa97a..3ba81ba 100644 --- a/examples/wm8960_05_Loopback.py +++ b/examples/wm8960_05_Loopback.py @@ -70,17 +70,17 @@ # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableBoost2OutputMixer() +codec.disableB2O() # Connect from DAC outputs to output mixer -codec.enableDac2OutputMixer() +codec.enableD2O() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Setup sample rate codec.setSampleRate(44100) diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py index 8e64863..c98080c 100644 --- a/examples/wm8960_06_3D_Enhance.py +++ b/examples/wm8960_06_3D_Enhance.py @@ -79,17 +79,17 @@ # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableBoost2OutputMixer() +codec.disableB2O() # Connect from DAC outputs to output mixer -codec.enableDac2OutputMixer() +codec.enableD2O() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Setup sample rate codec.setSampleRate(44100) diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py index 00416e8..ddbc115 100644 --- a/examples/wm8960_08_I2S_Passthrough.py +++ b/examples/wm8960_08_I2S_Passthrough.py @@ -82,17 +82,17 @@ # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableBoost2OutputMixer() +codec.disableB2O() # Connect from DAC outputs to output mixer -codec.enableDac2OutputMixer() +codec.enableD2O() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Set sample rate, word length, and mode codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py index d885213..7874fdb 100644 --- a/examples/wm8960_09_I2S_Bluetooth.py +++ b/examples/wm8960_09_I2S_Bluetooth.py @@ -60,14 +60,14 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Connect from DAC outputs to output mixer -codec.enableDac2OutputMixer() +codec.enableD2O() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Setup sample rate, word length, and I2S mode codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py index 8abf07f..417842e 100644 --- a/examples/wm8960_10_AdcGain.py +++ b/examples/wm8960_10_AdcGain.py @@ -97,17 +97,17 @@ # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableBoost2OutputMixer() +codec.disableB2O() # Connect from DAC outputs to output mixer -codec.enableDac2OutputMixer() +codec.enableD2O() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Setup clock and mode codec.setSampleRate(44100) diff --git a/examples/wm8960_11_VolumePlotter.py b/examples/wm8960_11_VolumePlotter.py index 217fa41..933b7cb 100644 --- a/examples/wm8960_11_VolumePlotter.py +++ b/examples/wm8960_11_VolumePlotter.py @@ -79,16 +79,16 @@ codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableBoost2OutputMixer() +codec.enableB2O() # Disconnect from DAC outputs to output mixer -codec.disableDac2OutputMixer() +codec.disableD2O() # Set gainstage between booster mixer and output mixer -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Configure I2S codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_12_AutomaticLevelControl.py index c8cdc9e..730f9dd 100644 --- a/examples/wm8960_12_AutomaticLevelControl.py +++ b/examples/wm8960_12_AutomaticLevelControl.py @@ -85,17 +85,17 @@ # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableBoost2OutputMixer() +codec.disableB2O() # Connect from DAC outputs to output mixer -codec.enableDac2OutputMixer() +codec.enableD2O() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Setup clock and mode codec.setSampleRate(44100) diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py index 98328f0..0785a0d 100644 --- a/examples/wm8960_13_DacGain.py +++ b/examples/wm8960_13_DacGain.py @@ -97,17 +97,17 @@ # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableBoost2OutputMixer() +codec.disableB2O() # Connect from DAC outputs to output mixer -codec.enableDac2OutputMixer() +codec.enableD2O() # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Set sample rate and mode codec.setSampleRate(44100) diff --git a/examples/wm8960_14_ElectretMics.py b/examples/wm8960_14_ElectretMics.py index dacb0ac..d283b4c 100644 --- a/examples/wm8960_14_ElectretMics.py +++ b/examples/wm8960_14_ElectretMics.py @@ -101,13 +101,13 @@ codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableBoost2OutputMixer() +codec.enableB2O() # Set gainstage between booster mixer and output mixer -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() print("Headphone output buffer volume set to +6dB (max)") codec.configureHeadphones(dB=6.0, capless=True) # Capless provides VMID as buffer for headphone ground diff --git a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py index 8aad9fb..c142a46 100644 --- a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py +++ b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py @@ -89,16 +89,16 @@ codec.enableAIN() # Connect LB2LO (booster to output mixer (analog bypass) -codec.enableBoost2OutputMixer() +codec.enableB2O() # Disconnect from DAC outputs to output mixer -codec.disableDac2OutputMixer() +codec.disableD2O() # Set gainstage between booster mixer and output mixer -codec.setBoost2MixerGain(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) # Enable output mixers -codec.enableOutputMixer() +codec.enableOMIX() # Set sample rate, word length, and mode codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) From d333550b0bc915eea59559dfcb8bf43d5f6e49b1 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 15:29:41 -0500 Subject: [PATCH 17/56] Fix typo in INPUT2 example. --- examples/wm8960_02_INPUT2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/wm8960_02_INPUT2.py b/examples/wm8960_02_INPUT2.py index fbc5197..f06b13b 100644 --- a/examples/wm8960_02_INPUT2.py +++ b/examples/wm8960_02_INPUT2.py @@ -56,7 +56,7 @@ # Setup signal flow through the analog audio bypass connections # Set input boosts to get INPUT2 (both left and right) to the boost mixers -codec.setIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB)) +codec.setIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB) # Enable input boost mixers codec.enableAIN() From bb239181adedcbd8b4e82c4d608a286dc74c869b Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 15:31:37 -0500 Subject: [PATCH 18/56] Basic error checking and boolean response. --- adafruit_wm8960.py | 1024 +++++++++++++++++++++----------------------- 1 file changed, 496 insertions(+), 528 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 7b99a9e..a567858 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -407,7 +407,7 @@ def isConnected(self) -> bool: # TODO: Check I2C or I2CDevice return True - def _getDivBit(self, src:int, dest:int): + def _getDivBit(self, src:int, dest:int) -> int: div = (src * 2) // dest if div < 2 or div > 12: return 0 if div == 2: div = 0 @@ -417,48 +417,54 @@ def _getDivBit(self, src:int, dest:int): if div % 2 == 1: return 0 return div // 2 - def configureI2S(self, sample_rate:int, word_length:int = WM8960_WL_16BIT, master:bool = False): - self.setSampleRate(sample_rate) - self.setWL(word_length) + def configureI2S(self, sample_rate:int, word_length:int = WM8960_WL_16BIT, master:bool = False) -> bool: + if not self.setSampleRate(sample_rate): return False + if not self.setWL(word_length): return False if master: - self.enableMasterMode() - self.setALRCGPIO() # Note, should not be changed while ADC is enabled. + if not self.enableMasterMode(): return False + if not self.setALRCGPIO(): return False # Note, should not be changed while ADC is enabled. else: - self.enablePeripheralMode() + if not self.enablePeripheralMode(): return False + return True - def configureDAC(self, loopback:bool = False): + def configureDAC(self, loopback:bool = False) -> bool: # Connect from DAC outputs to output mixer - self.enableD2O() + if not self.enableD2O(): return False # Enable output mixers - self.enableOMIX() + if not self.enableOMIX(): return False # Enable DACs - self.enableDac() + if not self.enableDac(): return False # Default is "soft mute" on, so we must disable mute to make channels active - self.disableDacMute() + if not self.disableDacMute(): return False # Loopback sends ADC data directly into DAC if loopback: - self.enableLoopBack() + if not self.enableLoopBack(): return False else: - self.disableLoopBack() + if not self.disableLoopBack(): return False + + return True - def configureHeadphones(self, dB:float = 0.0, capless:bool = True): + def configureHeadphones(self, dB:float = 0.0, capless:bool = True) -> bool: # Enable headphone output - self.enableHeadphones() + if not self.enableHeadphones(): return False # Provides VMID as buffer for headphone ground on OUT3 - if capless: self.enableOUT3MIX() + if capless: + if not self.enableOUT3MIX(): return False # Adjust headphone volume - self.setHeadphoneVolumeDB(dB) + if not self.setHeadphoneVolumeDB(dB): return False + return True - def configureSpeakers(self, dB:float = 0.0): + def configureSpeakers(self, dB:float = 0.0) -> bool: # Enable speaker output - self.enableSpeakers() + if not self.enableSpeakers(): return False # Adjust speaker volume - self.setSpeakerVolumeDB(dB) + if not self.setSpeakerVolumeDB(dB): return False + return True - def setSampleRate(self, sample_rate:int): + def setSampleRate(self, sample_rate:int) -> bool: # MCLK = 24 MHz - self.enablePLL() # Needed for class-d amp clock + if not self.enablePLL(): return False # Needed for class-d amp clock self.setSMD(WM8960_PLL_MODE_FRACTIONAL) self.setCLKSEL(WM8960_CLKSEL_PLL) @@ -488,6 +494,8 @@ def setSampleRate(self, sample_rate:int): self.setDACDIV(div) else: raise Exception("Invalid sample rate") + + return True ''' Necessary for all other functions of the CODEC @@ -495,8 +503,8 @@ def setSampleRate(self, sample_rate:int): VREF is bit 6, 0 = power down, 1 = power up Returns 1 if successful, 0 if something failed (I2C error) ''' - def enableVREF(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 1) + def enableVREF(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 1) ''' Use this to save power @@ -504,67 +512,62 @@ def enableVREF(self) -> None: VREF is bit 6, 0 = power down, 1 = power up Returns 1 if successful, 0 if something failed (I2C error) ''' - def disableVREF(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 0) + def disableVREF(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 0) # Resets all registers to their default state - def reset(self) -> None: + def reset(self) -> bool: # Doesn't matter which bit we flip, writing anything will cause the reset - self._writeRegisterBit(WM8960_REG_RESET, 7, 1) + if not self._writeRegisterBit(WM8960_REG_RESET, 7, 1): return False # Update our local copy of the registers to reflect the reset for i in range(len(WM8960_REGISTER_DEFAULTS)): self._registerLocalCopy[i] = WM8960_REGISTER_DEFAULTS[i] + return True - def enableAINL(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 1) - def disableAINL(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 0) - - def enableAINR(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 1) - def disableAINR(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 0) - - def enableAIN(self) -> None: - self.enableAINL() - self.enableAINR() - def disableAIN(self) -> None: - self.disableAINL() - self.disableAINR() - - def enableLMIC(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) - def disableLMIC(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) - - def enableRMIC(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) - def disableRMIC(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) - - def enableMIC(self) -> None: - self.enableLMIC() - self.enableRMIC() - def disableMIC(self) -> None: - self.disableLMIC() - self.disableRMIC() - - def enableLMICBOOST(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) - def disableLMICBOOST(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) - - def enableRMICBOOST(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) - def disableRMICBOOST(self) -> None: - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) - - def enableMICBOOST(self) -> None: - self.enableLMICBOOST() - self.enableRMICBOOST() - def disableMICBOOST(self) -> None: - self.disableLMICBOOST() - self.disableRMICBOOST() + def enableAINL(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 1) + def disableAINL(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 0) + + def enableAINR(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 1) + def disableAINR(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 0) + + def enableAIN(self) -> bool: + return self.enableAINL() and self.enableAINR() + def disableAIN(self) -> bool: + return self.disableAINL() and self.disableAINR() + + def enableLMIC(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) + def disableLMIC(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) + + def enableRMIC(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) + def disableRMIC(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + + def enableMIC(self) -> bool: + return self.enableLMIC() and self.enableRMIC() + def disableMIC(self) -> bool: + return self.disableLMIC() and self.disableRMIC() + + def enableLMICBOOST(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) + def disableLMICBOOST(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) + + def enableRMICBOOST(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) + def disableRMICBOOST(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + + def enableMICBOOST(self) -> bool: + return self.enableLMICBOOST() and self.enableRMICBOOST() + def disableMICBOOST(self) -> bool: + return self.disableLMICBOOST() and self.disableRMICBOOST() # PGA input signal select # Each PGA (left and right) has a switch on its non-inverting input. @@ -578,7 +581,7 @@ def disableMICBOOST(self) -> None: # RINPUT1 # 3 options: WM8960_PGAL_LINPUT2, WM8960_PGAL_LINPUT3, WM8960_PGAL_VMID - def pgaLeftNonInvSignalSelect(self, signal:int): + def pgaLeftNonInvSignalSelect(self, signal:int) -> bool: ''' Clear LMP2 and LMP3 Necessary because the previous setting could have either set, @@ -587,23 +590,25 @@ def pgaLeftNonInvSignalSelect(self, signal:int): ''' # LMP3 - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 0) + if not self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 0): return False # LMP2 - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 0) + if not self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 0): return False if signal == WM8960_PGAL_LINPUT2: # LMP2 - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 1) + return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 1) elif signal == WM8960_PGAL_LINPUT3: # LMP3 - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 1) + return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 1) elif signal == WM8960_PGAL_VMID: # Don't set any bits. When both LMP2 and LMP3 are cleared, then the signal is set to VMID - pass + return True + else: + raise Exception("Invalid PGA signal select") # 3 options: WM8960_PGAR_RINPUT2, WM8960_PGAR_RINPUT3, WM8960_PGAR_VMID - def pgaRightNonInvSignalSelect(self, signal:int): + def pgaRightNonInvSignalSelect(self, signal:int) -> bool: ''' Clear RMP2 and RMP3 Necessary because the previous setting could have either set, @@ -612,83 +617,80 @@ def pgaRightNonInvSignalSelect(self, signal:int): ''' # RMP3 - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 0) + if not self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 0): return False # RMP2 - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 0) + if not self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 0): return False if signal == WM8960_PGAR_RINPUT2: # RMP2 - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 1) + return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 1) elif signal == WM8960_PGAR_RINPUT3: # RMP3 - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 1) + return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 1) elif signal == WM8960_PGAR_VMID: # Don't set any bits. When both RMP2 and RMP3 are cleared, then the signal is set to VMID - pass + return True + else: + raise Exception("Invalid PGA signal select") # 3 options: WM8960_PGA_INPUT2, WM8960_PGA_INPUT3, WM8960_PGA_VMID - def pgaNonInvSignalSelect(self, signal:int): - self.pgaLeftNonInvSignalSelect(signal) - self.pgaRightNonInvSignalSelect(signal) + def pgaNonInvSignalSelect(self, signal:int) -> bool: + return self.pgaLeftNonInvSignalSelect(signal) and self.pgaRightNonInvSignalSelect(signal) # Connections from each INPUT1 to the inverting input of its PGA # Connect LINPUT1 to inverting input of Left Input PGA - def connectLMN1(self): - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 1) + def connectLMN1(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 1) # Disconnect LINPUT1 from inverting input of Left Input PGA - def disconnectLMN1(self): - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 0) + def disconnectLMN1(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 0) # Connect RINPUT1 to inverting input of Right Input PGA - def connectRMN1(self): - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 1) + def connectRMN1(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 1) # Disconnect RINPUT1 from inverting input of Right Input PGA - def disconnectRMN1(self): - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 0) - - def connectMN1(self): - self.connectLMN1() - self.connectRMN1() - def disconnectMN1(self): - self.disconnectLMN1() - self.disconnectRMN1() + def disconnectRMN1(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 0) + + def connectMN1(self) -> bool: + return self.connectLMN1() and self.connectRMN1() + def disconnectMN1(self) -> bool: + return self.disconnectLMN1() and self.disconnectRMN1() # Connection from output of PGAs to downstream "boost mixers" # Connect Left Input PGA to Left Input Boost mixer - def connectLMIC2B(self): - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 1) + def connectLMIC2B(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 1) # Disconnect Left Input PGA to Left Input Boost mixer - def disconnectLMIC2B(self): - self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 0) + def disconnectLMIC2B(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 0) # Connect Right Input PGA to Right Input Boost mixer - def connectRMIC2B(self): - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 1) + def connectRMIC2B(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 1) # Disconnect Right Input PGA to Right Input Boost mixer - def disconnectRMIC2B(self): - self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 0) + def disconnectRMIC2B(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 0) - def connectMIC2B(self): - self.connectLMIC2B() - self.connectRMIC2B() - def disconnectMIC2B(self): - self.disconnectLMIC2B() - self.disconnectRMIC2B() + def connectMIC2B(self) -> bool: + return self.connectLMIC2B() and self.connectRMIC2B() + def disconnectMIC2B(self) -> bool: + return self.disconnectLMIC2B() and self.disconnectRMIC2B() # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) - def setLINVOL(self, volume:int): + def setLINVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 63: volume = 63 - self._writeRegisterMultiBits(WM8960_REG_LEFT_INPUT_VOLUME, 5, 0, volume) - self.pgaLeftIPVUSet() + if not self._writeRegisterMultiBits(WM8960_REG_LEFT_INPUT_VOLUME, 5, 0, volume): return False + return self.pgaLeftIPVUSet() ''' Sets the volume of the PGA input buffer amp to a specified dB value @@ -698,18 +700,18 @@ def setLINVOL(self, volume:int): ... 0.75dB steps ... 30.00 = +30.00dB (MAX) ''' - def setLINVOLDB(self, dB:float): + def setLINVOLDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setLINVOL() volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) - self.setLINVOL(volume) + return self.setLINVOL(volume) # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) - def setRINVOL(self, volume:int): + def setRINVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 63: volume = 63 - self._writeRegisterMultiBits(WM8960_REG_RIGHT_INPUT_VOLUME, 5, 0, volume) - self.pgaRightIPVUSet() + if not self._writeRegisterMultiBits(WM8960_REG_RIGHT_INPUT_VOLUME, 5, 0, volume): return False + return self.pgaRightIPVUSet() ''' Sets the volume of the PGA input buffer amp to a specified dB value @@ -719,144 +721,131 @@ def setRINVOL(self, volume:int): ... 0.75dB steps ... 30.00 = +30.00dB (MAX) ''' - def setRINVOLDB(self, dB:float): + def setRINVOLDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setLINVOL() volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) - self.setRINVOL(volume) + return self.setRINVOL(volume) - def setINVOL(self, volume:int): - self.setLINVOL(volume) - self.setRINVOL(volume) + def setINVOL(self, volume:int) -> bool: + return self.setLINVOL(volume) and self.setRINVOL(volume) - def setINVOLDB(self, dB:float): + def setINVOLDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setLINVOL() volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) - self.setLINVOL(volume) - self.setRINVOL(volume) + return self.setLINVOL(volume) and self.setRINVOL(volume) # Zero Cross prevents zipper sounds on volume changes # Sets both left and right PGAs - def enablePgaZeroCross(self): - self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 1) - self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 1) - def disablePgaZeroCross(self): - self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 0) - self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 0) - - def enableLINMUTE(self): - self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 1) - def disableLINMUTE(self): - self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 0) - self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) - - def enableRINMUTE(self): - self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 1) - def disableRINMUTE(self): - self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 0) - self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) - - def enableINMUTE(self): - self.enableLINMUTE() - self.enableRINMUTE() - def disableINMUTE(self): - self.disableLINMUTE() - self.disableRINMUTE() + def enablePgaZeroCross(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 1) and self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 1) + def disablePgaZeroCross(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 0) and self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 0) + + def enableLINMUTE(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 1) + def disableLINMUTE(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) + + def enableRINMUTE(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 1) + def disableRINMUTE(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) + + def enableINMUTE(self) -> bool: + return self.enableLINMUTE() and self.enableRINMUTE() + def disableINMUTE(self) -> bool: + return self.disableLINMUTE() and self.disableRINMUTE() # Causes left and right input PGA volumes to be updated # (LINVOL and RINVOL) - def pgaLeftIPVUSet(self): - self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) + def pgaLeftIPVUSet(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) # Causes left and right input PGA volumes to be updated # (LINVOL and RINVOL) - def pgaRightIPVUSet(self): - self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) + def pgaRightIPVUSet(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) # Boosts # WM8960_MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB - def setLMICBOOST(self, boost_gain:int): + def setLMICBOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 3: boost_gain = 3 - self._writeRegisterMultiBits(WM8960_REG_ADCL_SIGNAL_PATH, 5, 4, boost_gain) + return self._writeRegisterMultiBits(WM8960_REG_ADCL_SIGNAL_PATH, 5, 4, boost_gain) # WM8960_MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB - def setRMICBOOST(self, boost_gain:int): + def setRMICBOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 3: boost_gain = 3 - self._writeRegisterMultiBits(WM8960_REG_ADCR_SIGNAL_PATH, 5, 4, boost_gain) + return self._writeRegisterMultiBits(WM8960_REG_ADCR_SIGNAL_PATH, 5, 4, boost_gain) - def setMICBOOST(self, boost_gain:int): - self.setLMICBOOST(boost_gain) - self.setRMICBOOST(boost_gain) + def setMICBOOST(self, boost_gain:int) -> bool: + return self.setLMICBOOST(boost_gain) and self.setRMICBOOST(boost_gain) # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setLIN2BOOST(self, boost_gain:int): + def setLIN2BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) + return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setRIN2BOOST(self, boost_gain:int): + def setRIN2BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) + return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) - def setIN2BOOST(self, boost_gain:int): - self.setLIN2BOOST(boost_gain) - self.setRIN2BOOST(boost_gain) + def setIN2BOOST(self, boost_gain:int) -> bool: + return self.setLIN2BOOST(boost_gain) and self.setRIN2BOOST(boost_gain) # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setLIN3BOOST(self, boost_gain:int): + def setLIN3BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) + return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... - def setRIN3BOOST(self, boost_gain:int): + def setRIN3BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) + return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) - def setIN3BOOST(self, boost_gain:int): - self.setLIN3BOOST(boost_gain) - self.setRIN3BOOST(boost_gain) + def setIN3BOOST(self, boost_gain:int) -> bool: + return self.setLIN3BOOST(boost_gain) and self.setRIN3BOOST(boost_gain) # Mic Bias control - def enableMicBias(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 1) - def disableMicBias(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 0) + def enableMicBias(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 1) + def disableMicBias(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 0) # WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) # or WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) - def setMicBiasVoltage(self, voltage:bool): - self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_4, 0, voltage) + def setMicBiasVoltage(self, voltage:bool) -> bool: + return self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_4, 0, voltage) ## ADC - def enableAdcLeft(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 1) - def disableAdcLeft(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 0) + def enableAdcLeft(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 1) + def disableAdcLeft(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 0) - def enableAdcRight(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 1) - def disableAdcRight(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 0) + def enableAdcRight(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 1) + def disableAdcRight(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 0) - def enableAdc(self): - self.enableAdcLeft() - self.enableAdcRight() - def disableAdc(self): - self.disableAdcLeft() - self.disableAdcRight() + def enableAdc(self) -> bool: + return self.enableAdcLeft() and self.enableAdcRight() + def disableAdc(self) -> bool: + return self.disableAdcLeft() and self.disableAdcRight() # ADC digital volume # Note, also needs to handle control of the ADCVU bits (volume update). @@ -866,12 +855,12 @@ def disableAdc(self): # ... 0.5dB steps up to # 195 = 0dB # 255 = +30dB - def setAdcLeftDigitalVolume(self, volume:int): - self._writeRegisterMultiBits(WM8960_REG_LEFT_ADC_VOLUME, 7, 0, volume) - self.adcLeftADCVUSet() - def setAdcRightDigitalVolume(self, volume:int): - self._writeRegisterMultiBits(WM8960_REG_RIGHT_ADC_VOLUME, 7, 0, volume) - self.adcRightADCVUSet() + def setAdcLeftDigitalVolume(self, volume:int) -> bool: + if not self._writeRegisterMultiBits(WM8960_REG_LEFT_ADC_VOLUME, 7, 0, volume): return False + return self.adcLeftADCVUSet() + def setAdcRightDigitalVolume(self, volume:int) -> bool: + if not self._writeRegisterMultiBits(WM8960_REG_RIGHT_ADC_VOLUME, 7, 0, volume): return False + return self.adcRightADCVUSet() ''' ADC digital volume DB @@ -883,32 +872,31 @@ def setAdcRightDigitalVolume(self, volume:int): ... 0.5dB steps ... 30.00 = +30.00dB (MAX) ''' - def setAdcLeftDigitalVolumeDB(self, dB:float): + def setAdcLeftDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) - self.setAdcLeftDigitalVolume(volume) - def setAdcRightDigitalVolumeDB(self, dB:float): + return self.setAdcLeftDigitalVolume(volume) + def setAdcRightDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setAdcRightDigitalVolume() volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) - self.setAdcRightDigitalVolume(volume) + return self.setAdcRightDigitalVolume(volume) # Causes left and right input ADC volumes to be updated - def adcLeftADCVUSet(self): - self._writeRegisterBit(WM8960_REG_LEFT_ADC_VOLUME, 8, 1) + def adcLeftADCVUSet(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_ADC_VOLUME, 8, 1) # Causes left and right input ADC volumes to be updated - def adcRightADCVUSet(self): - self._writeRegisterBit(WM8960_REG_RIGHT_ADC_VOLUME, 8, 1) + def adcRightADCVUSet(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_ADC_VOLUME, 8, 1) # Control ADC volume in a stereo pair - def setAdcDigitalVolume(self, volume:int): - self.setAdcLeftDigitalVolume(volume) - self.setAdcRightDigitalVolume(volume) + def setAdcDigitalVolume(self, volume:int) -> bool: + return self.setAdcLeftDigitalVolume(volume) and self.setAdcRightDigitalVolume(volume) - def setAdcDigitalVolumeDB(self, dB:float): + def setAdcDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) - self.setAdcDigitalVolume(volume) + return self.setAdcDigitalVolume(volume) ## ALC @@ -918,97 +906,93 @@ def setAdcDigitalVolumeDB(self, dB:float): # RINMUTE) are ignored. # Also sets alc sample rate to match global sample rate. - def enableAlc(self, mode:int = WM8960_ALC_MODE_STEREO): + def enableAlc(self, mode:int = WM8960_ALC_MODE_STEREO) -> bool: bit8 = mode >> 1 bit7 = mode & 0x1 - self._writeRegisterBit(WM8960_REG_ALC1, 8, bit8) - self._writeRegisterBit(WM8960_REG_ALC1, 7, bit7) + return self._writeRegisterBit(WM8960_REG_ALC1, 8, bit8) and self._writeRegisterBit(WM8960_REG_ALC1, 7, bit7) - def disableAlc(self): - self._writeRegisterBit(WM8960_REG_ALC1, 8, 0) - self._writeRegisterBit(WM8960_REG_ALC1, 7, 0) + def disableAlc(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ALC1, 8, 0) and self._writeRegisterBit(WM8960_REG_ALC1, 7, 0) # Valid inputs are 0-15 # 0 = -22.5dB FS ... 1.5dB steps ... 15 = -1.5dB FS - def setAlcTarget(self, target:int): + def setAlcTarget(self, target:int) -> bool: # Limit incoming values max if target > 15: target = 15 - self._writeRegisterMultiBits(WM8960_REG_ALC1,3,0,target) + return self._writeRegisterMultiBits(WM8960_REG_ALC1,3,0,target) # Valid inputs are 0-10, 0 = 24ms, 1 = 48ms ... 10 = 24.58seconds - def setAlcDecay(self, decay:int): + def setAlcDecay(self, decay:int) -> bool: # Limit incoming values max if decay > 10: decay = 10 - self._writeRegisterMultiBits(WM8960_REG_ALC3, 7, 4, decay) + return self._writeRegisterMultiBits(WM8960_REG_ALC3, 7, 4, decay) # Valid inputs are 0-10, 0 = 6ms, 1 = 12ms, 2 = 24ms ... # 10 = 6.14seconds - def setAlcAttack(self, attack:int): + def setAlcAttack(self, attack:int) -> bool: # Limit incoming values max if attack > 10: attack = 10 - self._writeRegisterMultiBits(WM8960_REG_ALC3, 3, 0, attack) + return self._writeRegisterMultiBits(WM8960_REG_ALC3, 3, 0, attack) # Valid inputs are 0-7, 0 = -12dB, ... 7 = +30dB - def setAlcMaxGain(self, maxGain:int): + def setAlcMaxGain(self, maxGain:int) -> bool: # Limit incoming values max if maxGain > 7: maxGain = 7 - self._writeRegisterMultiBits(WM8960_REG_ALC1, 6, 4, maxGain) + return self._writeRegisterMultiBits(WM8960_REG_ALC1, 6, 4, maxGain) # Valid inputs are 0-7, 0 = -17.25dB, ... 7 = +24.75dB - def setAlcMinGain(self, minGain:int): + def setAlcMinGain(self, minGain:int) -> bool: # Limit incoming values max if minGain > 7: minGain = 7 - self._writeRegisterMultiBits(WM8960_REG_ALC2, 6, 4, minGain) + return self._writeRegisterMultiBits(WM8960_REG_ALC2, 6, 4, minGain) # Valid inputs are 0-15, 0 = 0ms, ... 15 = 43.691s - def setAlcHold(self, hold:int): + def setAlcHold(self, hold:int) -> bool: # Limit incoming values max if hold > 15: hold = 15 - self._writeRegisterMultiBits(WM8960_REG_ALC2, 3, 0, hold) + return self._writeRegisterMultiBits(WM8960_REG_ALC2, 3, 0, hold) # Peak Limiter - def enablePeakLimiter(self): - self._writeRegisterBit(WM8960_REG_ALC3, 8, 1) + def enablePeakLimiter(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ALC3, 8, 1) - def disablePeakLimiter(self): - self._writeRegisterBit(WM8960_REG_ALC3, 8, 0) + def disablePeakLimiter(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ALC3, 8, 0) # Noise Gate - def enableNoiseGate(self): - self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 1) - def disableNoiseGate(self): - self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 0) + def enableNoiseGate(self) -> bool: + return self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 1) + def disableNoiseGate(self) -> bool: + return self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 0) # 0-31, 0 = -76.5dBfs, 31 = -30dBfs - def setNoiseGateThreshold(self, threshold:int): + def setNoiseGateThreshold(self, threshold:int) -> bool: # TODO - pass + return False ## DAC # Enable/disble each channel - def enableDacLeft(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 1) - def disableDacLeft(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 0) - - def enableDacRight(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 1) - def disableDacRight(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 0) - - def enableDac(self): - self.enableDacLeft() - self.enableDacRight() - def disableDac(self): - self.disableDacLeft() - self.disableDacRight() + def enableDacLeft(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 1) + def disableDacLeft(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 0) + + def enableDacRight(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 1) + def disableDacRight(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 0) + + def enableDac(self) -> bool: + return self.enableDacLeft() and self.enableDacRight() + def disableDac(self) -> bool: + return self.disableDacLeft() and self.disableDacRight() # DAC digital volume # Valid inputs are 0-255 @@ -1016,13 +1000,13 @@ def disableDac(self): # 1 = -127dB # ... 0.5dB steps up to # 255 = 0dB - def setDacLeftDigitalVolume(self, volume:int): - self._writeRegisterMultiBits(WM8960_REG_LEFT_DAC_VOLUME, 7, 0, volume) - self.dacLeftDACVUSet() + def setDacLeftDigitalVolume(self, volume:int) -> bool: + if not self._writeRegisterMultiBits(WM8960_REG_LEFT_DAC_VOLUME, 7, 0, volume): return False + return self.dacLeftDACVUSet() - def setDacRightDigitalVolume(self, volume:int): - self._writeRegisterMultiBits(WM8960_REG_RIGHT_DAC_VOLUME, 7, 0, volume) - self.dacRightDACVUSet() + def setDacRightDigitalVolume(self, volume:int) -> bool: + if not self._writeRegisterMultiBits(WM8960_REG_RIGHT_DAC_VOLUME, 7, 0, volume): return False + return self.dacRightDACVUSet() ''' DAC digital volume DB @@ -1033,60 +1017,59 @@ def setDacRightDigitalVolume(self, volume:int): ... 0.5dB steps ... 30.00 = +30.00dB (MAX) ''' - def setDacLeftDigitalVolumeDB(self, dB:float): + def setDacLeftDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setDacLeftDigitalVolume() volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) - self.setDacLeftDigitalVolume(volume) + return self.setDacLeftDigitalVolume(volume) - def setDacRightDigitalVolumeDB(self, dB:float): + def setDacRightDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) - self.setDacRightDigitalVolume(volume) + return self.setDacRightDigitalVolume(volume) # Causes left and right input DAC volumes to be updated - def dacLeftDACVUSet(self): - self._writeRegisterBit(WM8960_REG_LEFT_DAC_VOLUME, 8, 1) + def dacLeftDACVUSet(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_DAC_VOLUME, 8, 1) # Causes left and right input DAC volumes to be updated - def dacRightDACVUSet(self): - self._writeRegisterBit(WM8960_REG_RIGHT_DAC_VOLUME, 8, 1) + def dacRightDACVUSet(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_DAC_VOLUME, 8, 1) - def setDacDigitalVolume(self, volume:int): - self.setDacLeftDigitalVolume(volume) - self.setDacRightDigitalVolume(volume) + def setDacDigitalVolume(self, volume:int) -> bool: + return self.setDacLeftDigitalVolume(volume) and self.setDacRightDigitalVolume(volume) - def setDacDigitalVolumeDB(self, dB:float): + def setDacDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) - self.setDacDigitalVolume(volume) + return self.setDacDigitalVolume(volume) # DAC mute - def enableDacMute(self): - self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 1) - def disableDacMute(self): - self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 0) + def enableDacMute(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 1) + def disableDacMute(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 0) # DE-Emphasis # 3D Stereo Enhancement # 3D enable/disable - def enable3d(self): - self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 1) - def disable3d(self): - self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 0) + def enable3d(self) -> bool: + return self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 1) + def disable3d(self) -> bool: + return self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 0) def set3dDepth(self, depth:int): # 0 = 0%, 15 = 100% # Limit incoming values max if depth > 15: depth = 15 - self._writeRegisterMultiBits(WM8960_REG_3D_CONTROL, 4, 1, depth) + return self._writeRegisterMultiBits(WM8960_REG_3D_CONTROL, 4, 1, depth) # 3D upper/lower cut-off frequencies. # DAC output -6dB attentuation enable/disable - def enableDac6dbAttenuation(self): - self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 1) - def disableDac6dbAttentuation(self): - self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 0) + def enableDac6dbAttenuation(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 1) + def disableDac6dbAttentuation(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 0) ## OUTPUT mixers @@ -1096,133 +1079,121 @@ def disableDac6dbAttentuation(self): # OUT3MIX Mono Output Mixer # Enable/disable left and right output mixers - def enableLOMIX(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 1) - def disableLOMIX(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 0) + def enableLOMIX(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 1) + def disableLOMIX(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 0) - def enableROMIX(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 1) - def disableROMIX(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 0) + def enableROMIX(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 1) + def disableROMIX(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 0) - def enableOUT3MIX(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 1) - def disableOUT3MIX(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 0) + def enableOUT3MIX(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 1) + def disableOUT3MIX(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 0) # Enable/disable audio path connections/vols to/from output mixers # See datasheet page 35 for a nice image of all the connections. - def enableLI2LO(self): - self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 1) - def disableLI2LO(self): - self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 0) + def enableLI2LO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 1) + def disableLI2LO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setLI2LOVOL(self, volume:int): - self._writeRegisterMultiBits(WM8960_REG_LEFT_OUT_MIX_1, 6, 4, volume) + def setLI2LOVOL(self, volume:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_LEFT_OUT_MIX_1, 6, 4, volume) - def enableLB2LO(self): - self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 1) - def disableLB2LO(self): - self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 0) + def enableLB2LO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 1) + def disableLB2LO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setLB2LOVOL(self, volume:int): + def setLB2LOVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 7: volume = 7 - self._writeRegisterMultiBits(WM8960_REG_BYPASS_1, 6, 4, volume) + return self._writeRegisterMultiBits(WM8960_REG_BYPASS_1, 6, 4, volume) - def enableLD2LO(self): - self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 1) - def disableLD2LO(self): - self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 0) + def enableLD2LO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 1) + def disableLD2LO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 0) - def enableRI2RO(self): - self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 1) - def disableRI2RO(self): - self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 0) + def enableRI2RO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 1) + def disableRI2RO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setRI2ROVOL(self, volume:int): + def setRI2ROVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 7: volume = 7 - self._writeRegisterMultiBits(WM8960_REG_RIGHT_OUT_MIX_2, 6, 4, volume) + return self._writeRegisterMultiBits(WM8960_REG_RIGHT_OUT_MIX_2, 6, 4, volume) - def enableRB2RO(self): - self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 1) - def disableRB2RO(self): - self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 0) + def enableRB2RO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 1) + def disableRB2RO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setRB2ROVOL(self, volume:int): + def setRB2ROVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 7: volume = 7 - self._writeRegisterMultiBits(WM8960_REG_BYPASS_2, 6, 4, volume) + return self._writeRegisterMultiBits(WM8960_REG_BYPASS_2, 6, 4, volume) - def enableRD2RO(self): - self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 1) - def disableRD2RO(self): - self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 0) + def enableRD2RO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 1) + def disableRD2RO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 0) # Mono Output mixer. # Note, for capless HPs, we'll want this to output a buffered VMID. # To do this, we need to disable both of these connections. - def enableLI2MO(self): - self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 1) - def disableLI2MO(self): - self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 0) + def enableLI2MO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 1) + def disableLI2MO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 0) - def enableRI2MO(self): - self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 1) - def disableRI2MO(self): - self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 0) + def enableRI2MO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 1) + def disableRI2MO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 0) # Paired stereo functions to enable/disable output mixers - def enableI2O(self): - self.enableLI2LO() - self.enableRI2RO() - def disableI2O(self): - self.disableLI2LO() - self.disableRI2RO() - def setI2OVOL(self, volume:int): - self.setLI2LOVOL(volume) - self.setRI2ROVOL(volume) - - def enableB2O(self): - self.enableLB2LO() - self.enableRB2RO() - def disableB2O(self): - self.disableLB2LO() - self.disableRB2RO() - def setB2OVOL(self, volume:int): - self.setLB2LOVOL(volume) - self.setRB2ROVOL(volume) - - def enableD2O(self): - self.enableLD2LO() - self.enableRD2RO() - def disableD2O(self): - self.disableLD2LO() - self.disableRD2RO() + def enableI2O(self) -> bool: + return self.enableLI2LO() and self.enableRI2RO() + def disableI2O(self) -> bool: + return self.disableLI2LO() and self.disableRI2RO() + def setI2OVOL(self, volume:int) -> bool: + return self.setLI2LOVOL(volume) and self.setRI2ROVOL(volume) + + def enableB2O(self) -> bool: + return self.enableLB2LO() and self.enableRB2RO() + def disableB2O(self) -> bool: + return self.disableLB2LO() and self.disableRB2RO() + def setB2OVOL(self, volume:int) -> bool: + return self.setLB2LOVOL(volume) and self.setRB2ROVOL(volume) + + def enableD2O(self) -> bool: + return self.enableLD2LO() and self.enableRD2RO() + def disableD2O(self) -> bool: + return self.disableLD2LO() and self.disableRD2RO() - def enableI2MO(self): - self.enableLI2MO() - self.enableRI2MO() - def disableI2MO(self): - self.disableLI2MO() - self.disableRI2MO() - - def enableOMIX(self): - self.enableLOMIX() - self.enableROMIX() - def disableOMIX(self): - self.disableLOMIX() - self.disableROMIX() + def enableI2MO(self) -> bool: + return self.enableLI2MO() and self.enableRI2MO() + def disableI2MO(self) -> bool: + return self.disableLI2MO() and self.disableRI2MO() + + def enableOMIX(self) -> bool: + return self.enableLOMIX() and self.enableROMIX() + def disableOMIX(self) -> bool: + return self.disableLOMIX() and self.disableROMIX() # Sets the VMID signal to one of three possible settings. # 4 options: @@ -1230,51 +1201,46 @@ def disableOMIX(self): # WM8960_VMIDSEL_2X50KOHM (playback / record) # WM8960_VMIDSEL_2X250KOHM (for low power / standby) # WM8960_VMIDSEL_2X5KOHM (for fast start-up) - def setVMID(self, setting:int = WM8960_VMIDSEL_2X50KOHM): - self._writeRegisterMultiBits(WM8960_REG_PWR_MGMT_1, 8, 7, setting) + def setVMID(self, setting:int = WM8960_VMIDSEL_2X50KOHM) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_PWR_MGMT_1, 8, 7, setting) # Enables VMID in the WM8960_REG_PWR_MGMT_2 register, and set's it to # playback/record settings of 2*50Kohm. # Note, this function is only here for backwards compatibility with the # original releases of this library. It is recommended to use the # setVMID() function instead. - def enableVMID(self): - self.setVMID(WM8960_VMIDSEL_2X50KOHM) - def disableVMID(self): - self.setVMID(WM8960_VMIDSEL_DISABLED) + def enableVMID(self) -> bool: + return self.setVMID(WM8960_VMIDSEL_2X50KOHM) + def disableVMID(self) -> bool: + return self.setVMID(WM8960_VMIDSEL_DISABLED) # This will disable both connections, thus enable VMID on OUT3. Note, # to enable VMID, you also need to enable OUT3 in the # WM8960_REG_PWR_MGMT_2 [1] - def enableOUT3asVMID(self): - self.disableLI2MO() - self.disableRI2MO() - self.enableOUT3MIX() - self.enableVMID() + def enableOUT3asVMID(self) -> bool: + return self.disableLI2MO() and self.disableRI2MO() and self.enableOUT3MIX() and self.enableVMID() ## Headphones # Enable and disable headphones (mute) - def enableHeadphones(self): - self.enableRightHeadphone() - self.enableLeftHeadphone() - def disableHeadphones(self): - self.disableRightHeadphone() - self.disableLeftHeadphone() - - def enableRightHeadphone(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 1) - def disableRightHeadphone(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 0) - def enableLeftHeadphone(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 1) - def disableLeftHeadphone(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 0) - - def enableHeadphoneStandby(self): - self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 1) - def disableHeadphoneStandby(self): - self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 0) + def enableHeadphones(self) -> bool: + return self.enableRightHeadphone() and self.enableLeftHeadphone() + def disableHeadphones(self) -> bool: + return self.disableRightHeadphone() and self.disableLeftHeadphone() + + def enableRightHeadphone(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 1) + def disableRightHeadphone(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 0) + def enableLeftHeadphone(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 1) + def disableLeftHeadphone(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 0) + + def enableHeadphoneStandby(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 1) + def disableHeadphoneStandby(self) -> bool: + return self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 0) # Set headphone volume # Although you can control each headphone output independently, here @@ -1283,7 +1249,7 @@ def disableHeadphoneStandby(self): # Valid inputs are 47-127. 0-47 = mute, 48 = -73dB ... 1dB steps ... # 127 = +6dB - def setHeadphoneVolume(self, volume:int): + def setHeadphoneVolume(self, volume:int) -> bool: # Updates both left and right channels # Handles the OUT1VU (volume update) bit control, so that it happens at the # same time on both channels. Note, we must also make sure that the outputs @@ -1298,17 +1264,17 @@ def setHeadphoneVolume(self, volume:int): volume = 127 # LEFT - self._writeRegisterMultiBits(WM8960_REG_LOUT1_VOLUME, 6, 0, volume) + if not self._writeRegisterMultiBits(WM8960_REG_LOUT1_VOLUME, 6, 0, volume): return False # RIGHT - self._writeRegisterMultiBits(WM8960_REG_ROUT1_VOLUME, 6, 0, volume) + if not self._writeRegisterMultiBits(WM8960_REG_ROUT1_VOLUME, 6, 0, volume): return False # UPDATES # Updated left channel - self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 8, 1) + if not self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 8, 1): return False # Updated right channel - self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 8, 1) + return self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 8, 1) # Set headphone volume dB # Sets the volume of the headphone output buffer amp to a speicified @@ -1321,58 +1287,56 @@ def setHeadphoneVolume(self, volume:int): # 0 = 0dB # ... 1dB steps ... # 6 = +6dB (MAX) - def setHeadphoneVolumeDB(self, dB:float): + def setHeadphoneVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setHeadphoneVolume() volume = self.convertDBtoSetting(dB, WM8960_HP_GAIN_OFFSET, WM8960_HP_GAIN_STEPSIZE, WM8960_HP_GAIN_MIN, WM8960_HP_GAIN_MAX) - self.setHeadphoneVolume(volume) + return self.setHeadphoneVolume(volume) # Zero Cross prevents zipper sounds on volume changes # Sets both left and right Headphone outputs - def enableHeadphoneZeroCross(self): + def enableHeadphoneZeroCross(self) -> bool: # Left - self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 1) + if not self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 1): return False # Right - self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 1) + return self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 1) - def disableHeadphoneZeroCross(self): + def disableHeadphoneZeroCross(self) -> bool: # Left - self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 0) + if not self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 0): return False # Right - self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 0) + return self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 0) ## Speakers # Enable and disable speakers (mute) - def enableSpeakers(self): - self.enableRightSpeaker() - self.enableLeftSpeaker() - def disableSpeakers(self): - self.disableRightSpeaker() - self.disableLeftSpeaker() - - def enableRightSpeaker(self): + def enableSpeakers(self) -> bool: + return self.enableRightSpeaker() and self.enableLeftSpeaker() + def disableSpeakers(self) -> bool: + return self.disableRightSpeaker() and self.disableLeftSpeaker() + + def enableRightSpeaker(self) -> bool: # SPK_OP_EN - self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 1) + if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 1): return False # SPKR - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 1) + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 1) - def disableRightSpeaker(self): + def disableRightSpeaker(self) -> bool: # SPK_OP_EN - self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 0) + if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 0): return False # SPKR - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 0) + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 0) - def enableLeftSpeaker(self): + def enableLeftSpeaker(self) -> bool: # SPK_OP_EN - self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 1) + if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 1): return False # SPKL - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 1) + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 1) - def disableLeftSpeaker(self): + def disableLeftSpeaker(self) -> bool: # SPK_OP_EN - self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 0) + if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 0): return False # SPKL - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 0) + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 0) # Set Speaker output volume # Although you can control each Speaker output independently, here we @@ -1380,7 +1344,7 @@ def disableLeftSpeaker(self): # Valid inputs are 47-127. 0-47 = mute, 48 = -73dB ... 1dB steps ... # 127 = +6dB - def setSpeakerVolume(self, volume:int): + def setSpeakerVolume(self, volume:int) -> bool: # Updates both left and right channels # Handles the SPKVU (volume update) bit control, so that it happens at the # same time on both channels. Note, we must also make sure that the outputs @@ -1392,50 +1356,50 @@ def setSpeakerVolume(self, volume:int): volume = 127 # LEFT - self._writeRegisterMultiBits(WM8960_REG_LOUT2_VOLUME, 6, 0, volume) + if not self._writeRegisterMultiBits(WM8960_REG_LOUT2_VOLUME, 6, 0, volume): return False # RIGHT - self._writeRegisterMultiBits(WM8960_REG_ROUT2_VOLUME, 6, 0, volume) + if not self._writeRegisterMultiBits(WM8960_REG_ROUT2_VOLUME, 6, 0, volume): return False # SPKVU # Updated left channel - self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 8, 1) + if not self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 8, 1): return False # Updated right channel - self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 8, 1) + return self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 8, 1) - def setSpeakerVolumeDB(self, dB:float): + def setSpeakerVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setSpeakerVolume() volume = self.convertDBtoSetting(dB, WM8960_SPEAKER_GAIN_OFFSET, WM8960_SPEAKER_GAIN_STEPSIZE, WM8960_SPEAKER_GAIN_MIN, WM8960_SPEAKER_GAIN_MAX) - self.setSpeakerVolume(volume) + return self.setSpeakerVolume(volume) # Zero Cross prevents zipper sounds on volume changes # Sets both left and right Speaker outputs - def enableSpeakerZeroCross(self): + def enableSpeakerZeroCross(self) -> bool: # Left - self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 1) + if not self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 1): return False # Right - self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 1) + return self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 1) - def disableSpeakerZeroCross(self): + def disableSpeakerZeroCross(self) -> bool: # Left - self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 0) + if not self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 0): return False # Right - self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 0) + return self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 0) # DC and AC gain - allows signal to be higher than the DACs swing # (use only if your SPKVDD is high enough to handle a larger signal) # Valid inputs are 0-5 # 0 = +0dB (1.0x boost) ... up to ... 5 = +5.1dB (1.8x boost) - def setSpeakerDcGain(self, gain:int): + def setSpeakerDcGain(self, gain:int) -> bool: # Limit incoming values max if gain > 5: gain = 5 - self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 5, 3, gain) + return self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 5, 3, gain) - def setSpeakerAcGain(self, gain:int): + def setSpeakerAcGain(self, gain:int) -> bool: # Limit incoming values max if gain > 5: gain = 5 - self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 2, 0, gain) + return self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 2, 0, gain) ## Digital audio interface control @@ -1444,10 +1408,10 @@ def setSpeakerAcGain(self, gain:int): # Loopback # When enabled, the output data from the ADC audio interface is fed # directly into the DAC data input. - def enableLoopBack(self): - self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 1) - def disableLoopBack(self): - self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 0) + def enableLoopBack(self) -> bool: + return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 1) + def disableLoopBack(self) -> bool: + return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 0) ## Clock controls @@ -1484,97 +1448,101 @@ def disableLoopBack(self): # DCLKDIV=111 (Divide by 16) = 705.6kHz # And now for the functions that will set these registers... - def enablePLL(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 1) - def disablePLL(self): - self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 0) + def enablePLL(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 1) + def disablePLL(self) -> bool: + return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 0) # Valid options are WM8960_PLLPRESCALE_DIV_1, WM8960_PLLPRESCALE_DIV_2 - def setPLLPRESCALE(self, div:bool): - self._writeRegisterBit(WM8960_REG_PLL_N, 4, div) + def setPLLPRESCALE(self, div:bool) -> bool: + return self._writeRegisterBit(WM8960_REG_PLL_N, 4, div) - def setPLLN(self, n:int): - self._writeRegisterMultiBits(WM8960_REG_PLL_N, 3, 0, n) + def setPLLN(self, n:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_PLL_N, 3, 0, n) # Send each nibble of 24-bit value for value K - def setPLLK(self, k:int): - self._writeRegisterMultiBits(WM8960_REG_PLL_K_1, 5, 0, (k >> 16) & 0x1F) - self._writeRegisterMultiBits(WM8960_REG_PLL_K_2, 8, 0, (k >> 8) & 0xFF) - self._writeRegisterMultiBits(WM8960_REG_PLL_K_3, 8, 0, k & 0xFF) + def setPLLK(self, k:int) -> bool: + if not self._writeRegisterMultiBits(WM8960_REG_PLL_K_1, 5, 0, (k >> 16) & 0x1F): return False + if not self._writeRegisterMultiBits(WM8960_REG_PLL_K_2, 8, 0, (k >> 8) & 0xFF): return False + return self._writeRegisterMultiBits(WM8960_REG_PLL_K_3, 8, 0, k & 0xFF) # 0=integer, 1=fractional - def setSMD(self, mode:bool): - self._writeRegisterBit(WM8960_REG_PLL_N, 5, mode) + def setSMD(self, mode:bool) -> bool: + return self._writeRegisterBit(WM8960_REG_PLL_N, 5, mode) # 0=MCLK, 1=PLL_output - def setCLKSEL(self, sel:bool): - self._writeRegisterBit(WM8960_REG_CLOCKING_1, 0, sel) + def setCLKSEL(self, sel:bool) -> bool: + return self._writeRegisterBit(WM8960_REG_CLOCKING_1, 0, sel) # (0=divide by 1), (2=div by 2) *1 and 3 are "reserved" - def setSYSCLKDIV(self, div:int): - self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 2, 1, div) + def setSYSCLKDIV(self, div:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 2, 1, div) # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options - def setADCDIV(self, div:int): - self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 8, 6, div) + def setADCDIV(self, div:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 8, 6, div) # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options - def setDACDIV(self, div:int): - self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 5, 3, div) + def setDACDIV(self, div:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 5, 3, div) # 0100 (4) = sufficiently high for 24bit, div by 4 allows for max word # length of 32bit - def setBCLKDIV(self, div:int): - self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 3, 0, div) + def setBCLKDIV(self, div:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 3, 0, div) # Class D amp, 111= SYSCLK/16, so 11.2896MHz/16 = 705.6KHz - def setDCLKDIV(self, div:int): - self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 8, 6, div) + def setDCLKDIV(self, div:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 8, 6, div) # Set LR clock to be the same for ADC & DAC (needed for loopback mode) - def setALRCGPIO(self): - self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 6, 1) + def setALRCGPIO(self) -> bool: + return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 6, 1) - def enableMasterMode(self): - self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 1) + def enableMasterMode(self) -> bool: + return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 1) - def enablePeripheralMode(self): - self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 0) + def enablePeripheralMode(self) -> bool: + return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 0) - def setWL(self, word_length:int): - self._writeRegisterMultiBits(WM8960_REG_AUDIO_INTERFACE_1, 3, 2, word_length) + def setWL(self, word_length:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_AUDIO_INTERFACE_1, 3, 2, word_length) - def setLRP(self, polarity:bool): - self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 4, polarity) + def setLRP(self, polarity:bool) -> bool: + return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 4, polarity) - def setALRSWAP(self, swap:bool): - self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 8, swap) + def setALRSWAP(self, swap:bool) -> bool: + return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 8, swap) - def setVROI(self, setting:bool): - self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_3, 6, setting) + def setVROI(self, setting:bool) -> bool: + return self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_3, 6, setting) - def setVSEL(self, setting:int): - self._writeRegisterMultiBits(WM8960_REG_ADDITIONAL_CONTROL_1, 7, 6, setting) + def setVSEL(self, setting:int) -> bool: + return self._writeRegisterMultiBits(WM8960_REG_ADDITIONAL_CONTROL_1, 7, 6, setting) # General-purpose register write - def writeRegister(self, reg:int, value:int) -> None: + def writeRegister(self, reg:int, value:int) -> bool: self._buf[0] = reg << 1 | value >> 8 self._buf[1] = value & 0xff - with self.i2c_device as i2c: - i2c.write(self._buf) + try: + with self.i2c_device as i2c: + i2c.write(self._buf) + except OSError: + return False self._registerLocalCopy[reg] = value + return True # **The WM8960 does not support reading registers!!! # Writes a 0 or 1 to the desired bit in the desired register - def _writeRegisterBit(self, registerAddress:int, bitNumber:int, bitValue:bool) -> None: + def _writeRegisterBit(self, registerAddress:int, bitNumber:int, bitValue:bool) -> bool: regvalue = self._registerLocalCopy[registerAddress] if bitValue: regvalue |= 1< bool: regvalue = self._registerLocalCopy[registerAddress] # Clear bits we care about @@ -1598,7 +1566,7 @@ def _writeRegisterMultiBits(self, registerAddress:int, settingMsbNum:int, settin regvalue |= setting << settingLsbNum # Write modified value to device - self.writeRegister(registerAddress, regvalue) + return self.writeRegister(registerAddress, regvalue) def convertDBtoSetting(self, dB:float, offset:float, stepSize:float, minDB:float, maxDB:float) -> int: ''' From ca541489b9a22a949bda115679ad62347a9d675c Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Tue, 13 Aug 2024 16:00:32 -0500 Subject: [PATCH 19/56] Updated documentation with new examples. --- README.rst | 53 +++++++------- docs/conf.py | 6 +- docs/examples.rst | 130 ++++++++++++++++++++++++++++++++++- examples/wm8960_01_Volume.py | 2 +- 4 files changed, 156 insertions(+), 35 deletions(-) diff --git a/README.rst b/README.rst index 43220c3..7fd8a3e 100644 --- a/README.rst +++ b/README.rst @@ -1,29 +1,23 @@ Introduction ============ - - .. image:: https://readthedocs.org/projects/adafruit-circuitpython-wm8960/badge/?version=latest :target: https://docs.circuitpython.org/projects/wm8960/en/latest/ :alt: Documentation Status - .. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg :target: https://adafru.it/discord :alt: Discord - .. image:: https://github.com/adafruit/Adafruit_CircuitPython_WM8960/workflows/Build%20CI/badge.svg :target: https://github.com/adafruit/Adafruit_CircuitPython_WM8960/actions :alt: Build Status - .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black :alt: Code Style: Black CircuitPython driver for WM8960 Stereo CODEC - Dependencies ============= This driver depends on: @@ -32,27 +26,18 @@ This driver depends on: * `Bus Device `_ Please ensure all dependencies are available on the CircuitPython filesystem. -This is easily achieved by downloading -`the Adafruit library and driver bundle `_ -or individual libraries can be installed using -`circup `_. - - +This is easily achieved by downloading `the Adafruit library and driver bundle `_ or individual libraries can be installed using `circup `_. -.. todo:: Describe the Adafruit product this library works with. For PCBs, you can also add the -image from the assets folder in the PCB's GitHub repo. +This library is designed to help facilite the I2C connection with a WM8960 audio codec to configure it to be used for DAC, ADC, headphone and speaker functionality. -`Purchase one from the Adafruit shop `_ +This library has been tested using an RP2040 on CircuitPython 9.1.1 and the `SparkFun Audio Codec PBreakout - WM8960 `_. Installing from PyPI ===================== -.. note:: This library is not available on PyPI yet. Install documentation is included - as a standard element. Stay tuned for PyPI availability! +.. note:: This library is not available on PyPI yet. Install documentation is included as a standard element. Stay tuned for PyPI availability! -.. todo:: Remove the above note if PyPI version is/will be available at time of release. +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from PyPI `_. -On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from -PyPI `_. To install for current user: .. code-block:: shell @@ -100,19 +85,27 @@ Or the following command to update an existing version: Usage Example ============= -.. todo:: Add a quick, simple example. It and other examples should live in the -examples folder and be included in docs/examples.rst. +.. code-block:: python + + # Monitor Stereo MIC Input: + # MIC (INPUT1) => PGA => Boost Mixer => Output Mixer => Headphones + import board, adafruit_wm8960 + codec = adafruit_wm8960.WM8960(board.I2C()) + codec.enableMIC() + codec.enableMN1() + codec.enableINMUTE() + codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + codec.connectMIC2B() + codec.enableAIN() + codec.enableB2O() + codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + codec.enableOMIX() + codec.configureHeadphones() Documentation ============= -API documentation for this library can be found on `Read the Docs `_. - -For information on building library documentation, please check out -`this guide `_. +API documentation for this library can be found on `Read the Docs `_. Contributing ============ - -Contributions are welcome! Please read our `Code of Conduct -`_ -before contributing to help this project stay welcoming. +Contributions are welcome! Please read our `Code of Conduct `_ before contributing to help this project stay welcoming. diff --git a/docs/conf.py b/docs/conf.py index a5692a4..dd30270 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -# autodoc_mock_imports = ["digitalio", "busio"] +autodoc_mock_imports = ["digitalio", "busio", "analogio"] autodoc_preserve_defaults = True @@ -58,8 +58,8 @@ if current_year == creation_year else creation_year + " - " + current_year ) -copyright = year_duration + " Scott Shawcroft" -author = "Scott Shawcroft" +copyright = year_duration + " Scott Shawcroft, Cooper Dalrymple" +author = "Scott Shawcroft, Cooper Dalrymple" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/examples.rst b/docs/examples.rst index 8078cdb..63be490 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,8 +1,136 @@ Simple test ------------- +----------- Ensure your device works with this simple test. .. literalinclude:: ../examples/wm8960_simpletest.py :caption: examples/wm8960_simpletest.py :linenos: + +Eighties Dystopia +----------------- + +Use synthio to generate a more complex audio output. + +.. literalinclude:: ../examples/wm8960_eighties_dystopia.py + :caption: examples/wm8960_eighties_dystopia.py + :linenos: + +01: Volume +---------- + +Demonstrates analog audio input, volume control, and headphone output on the WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_01_Volume.py + :caption: examples/wm8960_01_Volume.py + :linenos: + +02: INPUT2 +---------- + +Demonstrates analog audio input (on INPUT2s), sets volume control, and headphone output on the WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_02_INPUT2.py + :caption: examples/wm8960_02_INPUT2.py + :linenos: + +03: INPUT1 +---------- + +Demonstrates analog audio input (on INPUT1s), sets volume control, and headphone output on the WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_03_INPUT1.py + :caption: examples/wm8960_03_INPUT1.py + :linenos: + +04: Speaker +----------- + +Demonstrates analog audio input (on INPUT1s), sets volume control, and Speaker output on the WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_04_Speaker.py + :caption: examples/wm8960_03_Speaker.py + :linenos: + +05: Loopback +------------ + +Demonstrates analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone output on the WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_05_Loopback.py + :caption: examples/wm8960_05_Loopback.py + :linenos: + +06: 3D Enhance +-------------- + +Demonstrates 3D Enhance feature of WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_06_3D_Enhance.py + :caption: examples/wm8960_06_3D_Enhance.py + :linenos: + +07: Mic Bias +------------ + +This example demonstrates control of the mic bias feature of WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_07_MicBias.py + :caption: examples/wm8960_07_MicBias.py + :linenos: + +08: I2S Passthrough +------------------- + +.. note:: This example is not complete at this moment. + +09: I2S Bluetooth +----------------- + +.. note:: This example is not complete at this moment. + +10: ADC Gain +------------ + +Demonstrates how to control the volume using the codec's ADC digital volume control. + +.. literalinclude:: ../examples/wm8960_10_AdcGain.py + :caption: examples/wm8960_10_AdcGain.py + :linenos: + +11: Volume Plotter +------------------ + +.. note:: This example is not complete at this moment. + +12: Automatic Level Control +--------------------------- + +Demonstrates how to use the automatic level control feature of the WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_12_AutomaticLevelControl.py + :caption: examples/wm8960_12_AutomaticLevelControl.py + :linenos: + +13: DAC Gain +------------ + +Demonstrates how to control the volume using the codec's DAC digital volume control. + +.. literalinclude:: ../examples/wm8960_13_DacGain.py + :caption: examples/wm8960_13_DacGain.py + :linenos: + +14: Electret Mics +----------------- + +Demonstrates electret microphone analog audio input (on INPUT1/INPUT2 as "pseudo-differential MIC configuration"). Sets the PGA gain, sets the non-inverting pga input to INPUT2s, sets volume control, and headphone output on the WM8960 Codec. + +.. literalinclude:: ../examples/wm8960_14_ElectretMics.py + :caption: examples/wm8960_14_ElectretMics.py + :linenos: + +14: Volume Plotter MEMS Mic Differential +---------------------------------------- + +.. note:: This example is not complete at this moment. diff --git a/examples/wm8960_01_Volume.py b/examples/wm8960_01_Volume.py index be41964..9aeb40c 100644 --- a/examples/wm8960_01_Volume.py +++ b/examples/wm8960_01_Volume.py @@ -4,7 +4,7 @@ # SPDX-License-Identifier: MIT ''' -Demonstrates analog audio input, volume control, and headphone output on the WM8960 Codec. +Demonstrates analog audio input, volume control, and headphone output on the WM8960 Codec. Audio should be connected to both the left and right "INPUT3" inputs, they are labeled "RIN3" and "LIN3" on the board. From 381f022b967bcbf59a8a2d08c21ccfa058c9d227 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Thu, 15 Aug 2024 10:47:16 -0500 Subject: [PATCH 20/56] Removed unimplemented `adafruit_wm8960.WM8960.isConnected` Co-authored-by: Scott Shawcroft --- adafruit_wm8960.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index a567858..8ab5376 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -403,9 +403,6 @@ def __init__(self, i2c_bus:I2C, address:int = WM8960_ADDR): self.enableVREF() self.enableVMID() - def isConnected(self) -> bool: - # TODO: Check I2C or I2CDevice - return True def _getDivBit(self, src:int, dest:int) -> int: div = (src * 2) // dest From e8ea4765ac0c8fb27a091c221e88ff7eb8347311 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 15 Aug 2024 10:53:33 -0500 Subject: [PATCH 21/56] Fixed pyproject.toml Homepage url. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6cf5bab..9c8458b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ authors = [ {name = "Adafruit Industries", email = "circuitpython@adafruit.com"}, {name = "Cooper Dalrymple", email = "me@dcdalrymple.com"} ] -urls = {Homepage = "https://github.com/dcooperdalrymple/Adafruit_CircuitPython_WM8960"} +urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960"} keywords = [ "adafruit", "blinka", From 473b67f20e96bcf1eb51929f2c6486ec05317cfd Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 15 Aug 2024 11:03:45 -0500 Subject: [PATCH 22/56] Updated library constants to use const() and naming conventions (https://docs.circuitpython.org/en/latest/docs/design_guide.html#use-of-micropython-const & https://docs.circuitpython.org/en/latest/docs/design_guide.html#driver-constant-naming) --- README.rst | 4 +- adafruit_wm8960.py | 869 +++++++++--------- examples/wm8960_01_Volume.py | 2 +- examples/wm8960_02_INPUT2.py | 4 +- examples/wm8960_03_INPUT1.py | 4 +- examples/wm8960_04_Speaker.py | 4 +- examples/wm8960_05_Loopback.py | 4 +- examples/wm8960_06_3D_Enhance.py | 4 +- examples/wm8960_07_MicBias.py | 12 +- examples/wm8960_08_I2S_Passthrough.py | 6 +- examples/wm8960_09_I2S_Bluetooth.py | 4 +- examples/wm8960_10_AdcGain.py | 4 +- examples/wm8960_11_VolumePlotter.py | 6 +- examples/wm8960_12_AutomaticLevelControl.py | 18 +- examples/wm8960_13_DacGain.py | 4 +- examples/wm8960_14_ElectretMics.py | 12 +- ..._15_VolumePlotter_MEMS_Mic_Differential.py | 8 +- examples/wm8960_I2SInOut.py | 178 ++++ 18 files changed, 663 insertions(+), 484 deletions(-) create mode 100644 examples/wm8960_I2SInOut.py diff --git a/README.rst b/README.rst index 7fd8a3e..2cf86c6 100644 --- a/README.rst +++ b/README.rst @@ -94,11 +94,11 @@ Usage Example codec.enableMIC() codec.enableMN1() codec.enableINMUTE() - codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) + codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() codec.enableAIN() codec.enableB2O() - codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) + codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) codec.enableOMIX() codec.configureHeadphones() diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 8ab5376..e4b0441 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -12,7 +12,7 @@ * Author(s): Scott Shawcroft, Cooper Dalrymple Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library +https://github.com/sparkfun/SparkFun_Arduino_Library Implementation Notes -------------------- @@ -27,79 +27,80 @@ from busio import I2C from adafruit_bus_device.i2c_device import I2CDevice +from micropython import const __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960.git" -# I2C address (7-bit format for Wire library) -WM8960_ADDR = 0x1A +# I2C address +_DEFAULT_I2C_ADDR = const(0x1A) # WM8960 register addresses -WM8960_REG_LEFT_INPUT_VOLUME = 0x00 -WM8960_REG_RIGHT_INPUT_VOLUME = 0x01 -WM8960_REG_LOUT1_VOLUME = 0x02 -WM8960_REG_ROUT1_VOLUME = 0x03 -WM8960_REG_CLOCKING_1 = 0x04 -WM8960_REG_ADC_DAC_CTRL_1 = 0x05 -WM8960_REG_ADC_DAC_CTRL_2 = 0x06 -WM8960_REG_AUDIO_INTERFACE_1 = 0x07 -WM8960_REG_CLOCKING_2 = 0x08 -WM8960_REG_AUDIO_INTERFACE_2 = 0x09 -WM8960_REG_LEFT_DAC_VOLUME = 0x0A -WM8960_REG_RIGHT_DAC_VOLUME = 0x0B -WM8960_REG_RESET = 0x0F -WM8960_REG_3D_CONTROL = 0x10 -WM8960_REG_ALC1 = 0x11 -WM8960_REG_ALC2 = 0x12 -WM8960_REG_ALC3 = 0x13 -WM8960_REG_NOISE_GATE = 0x14 -WM8960_REG_LEFT_ADC_VOLUME = 0x15 -WM8960_REG_RIGHT_ADC_VOLUME = 0x16 -WM8960_REG_ADDITIONAL_CONTROL_1 = 0x17 -WM8960_REG_ADDITIONAL_CONTROL_2 = 0x18 -WM8960_REG_PWR_MGMT_1 = 0x19 -WM8960_REG_PWR_MGMT_2 = 0x1A -WM8960_REG_ADDITIONAL_CONTROL_3 = 0x1B -WM8960_REG_ANTI_POP_1 = 0x1C -WM8960_REG_ANTI_POP_2 = 0x1D -WM8960_REG_ADCL_SIGNAL_PATH = 0x20 -WM8960_REG_ADCR_SIGNAL_PATH = 0x21 -WM8960_REG_LEFT_OUT_MIX_1 = 0x22 -WM8960_REG_RIGHT_OUT_MIX_2 = 0x25 -WM8960_REG_MONO_OUT_MIX_1 = 0x26 -WM8960_REG_MONO_OUT_MIX_2 = 0x27 -WM8960_REG_LOUT2_VOLUME = 0x28 -WM8960_REG_ROUT2_VOLUME = 0x29 -WM8960_REG_MONO_OUT_VOLUME = 0x2A -WM8960_REG_INPUT_BOOST_MIXER_1 = 0x2B -WM8960_REG_INPUT_BOOST_MIXER_2 = 0x2C -WM8960_REG_BYPASS_1 = 0x2D -WM8960_REG_BYPASS_2 = 0x2E -WM8960_REG_PWR_MGMT_3 = 0x2F -WM8960_REG_ADDITIONAL_CONTROL_4 = 0x30 -WM8960_REG_CLASS_D_CONTROL_1 = 0x31 -WM8960_REG_CLASS_D_CONTROL_3 = 0x33 -WM8960_REG_PLL_N = 0x34 -WM8960_REG_PLL_K_1 = 0x35 -WM8960_REG_PLL_K_2 = 0x36 -WM8960_REG_PLL_K_3 = 0x37 +_REG_LEFT_INPUT_VOLUME = const(0x00) +_REG_RIGHT_INPUT_VOLUME = const(0x01) +_REG_LOUT1_VOLUME = const(0x02) +_REG_ROUT1_VOLUME = const(0x03) +_REG_CLOCKING_1 = const(0x04) +_REG_ADC_DAC_CTRL_1 = const(0x05) +_REG_ADC_DAC_CTRL_2 = const(0x06) +_REG_AUDIO_INTERFACE_1 = const(0x07) +_REG_CLOCKING_2 = const(0x08) +_REG_AUDIO_INTERFACE_2 = const(0x09) +_REG_LEFT_DAC_VOLUME = const(0x0A) +_REG_RIGHT_DAC_VOLUME = const(0x0B) +_REG_RESET = const(0x0F) +_REG_3D_CONTROL = const(0x10) +_REG_ALC1 = const(0x11) +_REG_ALC2 = const(0x12) +_REG_ALC3 = const(0x13) +_REG_NOISE_GATE = const(0x14) +_REG_LEFT_ADC_VOLUME = const(0x15) +_REG_RIGHT_ADC_VOLUME = const(0x16) +_REG_ADDITIONAL_CONTROL_1 = const(0x17) +_REG_ADDITIONAL_CONTROL_2 = const(0x18) +_REG_PWR_MGMT_1 = const(0x19) +_REG_PWR_MGMT_2 = const(0x1A) +_REG_ADDITIONAL_CONTROL_3 = const(0x1B) +_REG_ANTI_POP_1 = const(0x1C) +_REG_ANTI_POP_2 = const(0x1D) +_REG_ADCL_SIGNAL_PATH = const(0x20) +_REG_ADCR_SIGNAL_PATH = const(0x21) +_REG_LEFT_OUT_MIX_1 = const(0x22) +_REG_RIGHT_OUT_MIX_2 = const(0x25) +_REG_MONO_OUT_MIX_1 = const(0x26) +_REG_MONO_OUT_MIX_2 = const(0x27) +_REG_LOUT2_VOLUME = const(0x28) +_REG_ROUT2_VOLUME = const(0x29) +_REG_MONO_OUT_VOLUME = const(0x2A) +_REG_INPUT_BOOST_MIXER_1 = const(0x2B) +_REG_INPUT_BOOST_MIXER_2 = const(0x2C) +_REG_BYPASS_1 = const(0x2D) +_REG_BYPASS_2 = const(0x2E) +_REG_PWR_MGMT_3 = const(0x2F) +_REG_ADDITIONAL_CONTROL_4 = const(0x30) +_REG_CLASS_D_CONTROL_1 = const(0x31) +_REG_CLASS_D_CONTROL_3 = const(0x33) +_REG_PLL_N = const(0x34) +_REG_PLL_K_1 = const(0x35) +_REG_PLL_K_2 = const(0x36) +_REG_PLL_K_3 = const(0x37) # PGA input selections -WM8960_PGAL_LINPUT2 = 0 -WM8960_PGAL_LINPUT3 = 1 -WM8960_PGAL_VMID = 2 -WM8960_PGAR_RINPUT2 = 0 -WM8960_PGAR_RINPUT3 = 1 -WM8960_PGAR_VMID = 2 -WM8960_PGA_INPUT2 = 0 -WM8960_PGA_INPUT3 = 1 -WM8960_PGA_VMID = 2 +PGAL_LINPUT2 = 0 +PGAL_LINPUT3 = 1 +PGAL_VMID = 2 +PGAR_RINPUT2 = 0 +PGAR_RINPUT3 = 1 +PGAR_VMID = 2 +PGA_INPUT2 = 0 +PGA_INPUT3 = 1 +PGA_VMID = 2 # Mic (aka PGA) BOOST gain options -WM8960_MIC_BOOST_GAIN_0DB = 0 -WM8960_MIC_BOOST_GAIN_13DB = 1 -WM8960_MIC_BOOST_GAIN_20DB = 2 -WM8960_MIC_BOOST_GAIN_29DB = 3 +MIC_BOOST_GAIN_0DB = 0 +MIC_BOOST_GAIN_13DB = 1 +MIC_BOOST_GAIN_20DB = 2 +MIC_BOOST_GAIN_29DB = 3 ''' Boost Mixer gain options @@ -109,14 +110,14 @@ RIN2BOOST RIN3BOOST ''' -WM8960_BOOST_MIXER_GAIN_MUTE = 0 -WM8960_BOOST_MIXER_GAIN_NEG_12DB = 1 -WM8960_BOOST_MIXER_GAIN_NEG_9DB = 2 -WM8960_BOOST_MIXER_GAIN_NEG_6DB = 3 -WM8960_BOOST_MIXER_GAIN_NEG_3DB = 4 -WM8960_BOOST_MIXER_GAIN_0DB = 5 -WM8960_BOOST_MIXER_GAIN_3DB = 6 -WM8960_BOOST_MIXER_GAIN_6DB = 7 +BOOST_MIXER_GAIN_MUTE = 0 +BOOST_MIXER_GAIN_NEG_12DB = 1 +BOOST_MIXER_GAIN_NEG_9DB = 2 +BOOST_MIXER_GAIN_NEG_6DB = 3 +BOOST_MIXER_GAIN_NEG_3DB = 4 +BOOST_MIXER_GAIN_0DB = 5 +BOOST_MIXER_GAIN_3DB = 6 +BOOST_MIXER_GAIN_6DB = 7 ''' Output Mixer gain options @@ -127,38 +128,38 @@ RB2LOVOL These are useful as analog bypass signal path options. ''' -WM8960_OUTPUT_MIXER_GAIN_0DB = 0 -WM8960_OUTPUT_MIXER_GAIN_NEG_3DB = 1 -WM8960_OUTPUT_MIXER_GAIN_NEG_6DB = 2 -WM8960_OUTPUT_MIXER_GAIN_NEG_9DB = 3 -WM8960_OUTPUT_MIXER_GAIN_NEG_12DB = 4 -WM8960_OUTPUT_MIXER_GAIN_NEG_15DB = 5 -WM8960_OUTPUT_MIXER_GAIN_NEG_18DB = 6 -WM8960_OUTPUT_MIXER_GAIN_NEG_21DB = 7 +OUTPUT_MIXER_GAIN_0DB = 0 +OUTPUT_MIXER_GAIN_NEG_3DB = 1 +OUTPUT_MIXER_GAIN_NEG_6DB = 2 +OUTPUT_MIXER_GAIN_NEG_9DB = 3 +OUTPUT_MIXER_GAIN_NEG_12DB = 4 +OUTPUT_MIXER_GAIN_NEG_15DB = 5 +OUTPUT_MIXER_GAIN_NEG_18DB = 6 +OUTPUT_MIXER_GAIN_NEG_21DB = 7 # Mic Bias voltage options -WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD = 0 -WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD = 1 +MIC_BIAS_VOLTAGE_0_9_AVDD = 0 +MIC_BIAS_VOLTAGE_0_65_AVDD = 1 # SYSCLK divide -WM8960_SYSCLK_DIV_BY_1 = 0 -WM8960_SYSCLK_DIV_BY_2 = 2 -WM8960_CLKSEL_MCLK = 0 -WM8960_CLKSEL_PLL = 1 -WM8960_PLL_MODE_INTEGER = 0 -WM8960_PLL_MODE_FRACTIONAL = 1 -WM8960_PLLPRESCALE_DIV_1 = 0 -WM8960_PLLPRESCALE_DIV_2 = 1 +SYSCLK_DIV_BY_1 = 0 +SYSCLK_DIV_BY_2 = 2 +CLKSEL_MCLK = 0 +CLKSEL_PLL = 1 +PLL_MODE_INTEGER = 0 +PLL_MODE_FRACTIONAL = 1 +PLLPRESCALE_DIV_1 = 0 +PLLPRESCALE_DIV_2 = 1 # Class d clock divide -WM8960_DCLKDIV_16 = 7 +DCLKDIV_16 = 7 # Word length settings (aka bits per sample) # Audio Data Word Length -WM8960_WL_16BIT = 0 -WM8960_WL_20BIT = 1 -WM8960_WL_24BIT = 2 -WM8960_WL_32BIT = 3 +WL_16BIT = 0 +WL_20BIT = 1 +WL_24BIT = 2 +WL_32BIT = 3 ''' Additional Digital Audio Interface controls @@ -167,8 +168,8 @@ 0 = normal LRCLK polarity 1 = inverted LRCLK polarity ''' -WM8960_LR_POLARITY_NORMAL = 0 -WM8960_LR_POLARITY_INVERT = 1 +LR_POLARITY_NORMAL = 0 +LR_POLARITY_INVERT = 1 ''' ALRSWAP (aka ADC left/right swap) @@ -176,131 +177,131 @@ 1 = Swap left and right ADC data in audio interface 0 = Output left and right data as normal ''' -WM8960_ALRSWAP_NORMAL = 0 -WM8960_ALRSWAP_SWAP = 1 +ALRSWAP_NORMAL = 0 +ALRSWAP_SWAP = 1 # Gain mins, maxes, offsets and step-sizes for all the amps within the codec. -WM8960_PGA_GAIN_MIN = -17.25 -WM8960_PGA_GAIN_MAX = 30.00 -WM8960_PGA_GAIN_OFFSET = 17.25 -WM8960_PGA_GAIN_STEPSIZE = 0.75 -WM8960_HP_GAIN_MIN = -73.00 -WM8960_HP_GAIN_MAX = 6.00 -WM8960_HP_GAIN_OFFSET = 121.00 -WM8960_HP_GAIN_STEPSIZE = 1.00 -WM8960_SPEAKER_GAIN_MIN = -73.00 -WM8960_SPEAKER_GAIN_MAX = 6.00 -WM8960_SPEAKER_GAIN_OFFSET = 121.00 -WM8960_SPEAKER_GAIN_STEPSIZE = 1.00 -WM8960_ADC_GAIN_MIN = -97.00 -WM8960_ADC_GAIN_MAX = 30.00 -WM8960_ADC_GAIN_OFFSET = 97.50 -WM8960_ADC_GAIN_STEPSIZE = 0.50 -WM8960_DAC_GAIN_MIN = -97.00 -WM8960_DAC_GAIN_MAX = 30.00 -WM8960_DAC_GAIN_OFFSET = 97.50 -WM8960_DAC_GAIN_STEPSIZE = 0.50 +PGA_GAIN_MIN = -17.25 +PGA_GAIN_MAX = 30.00 +PGA_GAIN_OFFSET = 17.25 +PGA_GAIN_STEPSIZE = 0.75 +HP_GAIN_MIN = -73.00 +HP_GAIN_MAX = 6.00 +HP_GAIN_OFFSET = 121.00 +HP_GAIN_STEPSIZE = 1.00 +SPEAKER_GAIN_MIN = -73.00 +SPEAKER_GAIN_MAX = 6.00 +SPEAKER_GAIN_OFFSET = 121.00 +SPEAKER_GAIN_STEPSIZE = 1.00 +ADC_GAIN_MIN = -97.00 +ADC_GAIN_MAX = 30.00 +ADC_GAIN_OFFSET = 97.50 +ADC_GAIN_STEPSIZE = 0.50 +DAC_GAIN_MIN = -97.00 +DAC_GAIN_MAX = 30.00 +DAC_GAIN_OFFSET = 97.50 +DAC_GAIN_STEPSIZE = 0.50 # Automatic Level Control Modes -WM8960_ALC_MODE_OFF = 0 -WM8960_ALC_MODE_RIGHT_ONLY = 1 -WM8960_ALC_MODE_LEFT_ONLY = 2 -WM8960_ALC_MODE_STEREO = 3 +ALC_MODE_OFF = 0 +ALC_MODE_RIGHT_ONLY = 1 +ALC_MODE_LEFT_ONLY = 2 +ALC_MODE_STEREO = 3 # Automatic Level Control Target Level dB -WM8960_ALC_TARGET_LEVEL_NEG_22_5DB = 0 -WM8960_ALC_TARGET_LEVEL_NEG_21DB = 1 -WM8960_ALC_TARGET_LEVEL_NEG_19_5DB = 2 -WM8960_ALC_TARGET_LEVEL_NEG_18DB = 3 -WM8960_ALC_TARGET_LEVEL_NEG_16_5DB = 4 -WM8960_ALC_TARGET_LEVEL_NEG_15DB = 5 -WM8960_ALC_TARGET_LEVEL_NEG_13_5DB = 6 -WM8960_ALC_TARGET_LEVEL_NEG_12DB = 7 -WM8960_ALC_TARGET_LEVEL_NEG_10_5DB = 8 -WM8960_ALC_TARGET_LEVEL_NEG_9DB = 9 -WM8960_ALC_TARGET_LEVEL_NEG_7_5DB = 10 -WM8960_ALC_TARGET_LEVEL_NEG_6DB = 11 -WM8960_ALC_TARGET_LEVEL_NEG_4_5DB = 12 -WM8960_ALC_TARGET_LEVEL_NEG_3DB = 13 -WM8960_ALC_TARGET_LEVEL_NEG_1_5DB = 14 +ALC_TARGET_LEVEL_NEG_22_5DB = 0 +ALC_TARGET_LEVEL_NEG_21DB = 1 +ALC_TARGET_LEVEL_NEG_19_5DB = 2 +ALC_TARGET_LEVEL_NEG_18DB = 3 +ALC_TARGET_LEVEL_NEG_16_5DB = 4 +ALC_TARGET_LEVEL_NEG_15DB = 5 +ALC_TARGET_LEVEL_NEG_13_5DB = 6 +ALC_TARGET_LEVEL_NEG_12DB = 7 +ALC_TARGET_LEVEL_NEG_10_5DB = 8 +ALC_TARGET_LEVEL_NEG_9DB = 9 +ALC_TARGET_LEVEL_NEG_7_5DB = 10 +ALC_TARGET_LEVEL_NEG_6DB = 11 +ALC_TARGET_LEVEL_NEG_4_5DB = 12 +ALC_TARGET_LEVEL_NEG_3DB = 13 +ALC_TARGET_LEVEL_NEG_1_5DB = 14 # Automatic Level Control Max Gain Level dB -WM8960_ALC_MAX_GAIN_LEVEL_NEG_12DB = 0 -WM8960_ALC_MAX_GAIN_LEVEL_NEG_6DB = 1 -WM8960_ALC_MAX_GAIN_LEVEL_0DB = 2 -WM8960_ALC_MAX_GAIN_LEVEL_6DB = 3 -WM8960_ALC_MAX_GAIN_LEVEL_12DB = 4 -WM8960_ALC_MAX_GAIN_LEVEL_18DB = 5 -WM8960_ALC_MAX_GAIN_LEVEL_24DB = 6 -WM8960_ALC_MAX_GAIN_LEVEL_30DB = 7 +ALC_MAX_GAIN_LEVEL_NEG_12DB = 0 +ALC_MAX_GAIN_LEVEL_NEG_6DB = 1 +ALC_MAX_GAIN_LEVEL_0DB = 2 +ALC_MAX_GAIN_LEVEL_6DB = 3 +ALC_MAX_GAIN_LEVEL_12DB = 4 +ALC_MAX_GAIN_LEVEL_18DB = 5 +ALC_MAX_GAIN_LEVEL_24DB = 6 +ALC_MAX_GAIN_LEVEL_30DB = 7 # Automatic Level Control Min Gain Level dB -WM8960_ALC_MIN_GAIN_LEVEL_NEG_17_25DB = 0 -WM8960_ALC_MIN_GAIN_LEVEL_NEG_11_25DB = 1 -WM8960_ALC_MIN_GAIN_LEVEL_NEG_5_25DB = 2 -WM8960_ALC_MIN_GAIN_LEVEL_0_75DB = 3 -WM8960_ALC_MIN_GAIN_LEVEL_6_75DB = 4 -WM8960_ALC_MIN_GAIN_LEVEL_12_75DB = 5 -WM8960_ALC_MIN_GAIN_LEVEL_18_75DB = 6 -WM8960_ALC_MIN_GAIN_LEVEL_24_75DB = 7 +ALC_MIN_GAIN_LEVEL_NEG_17_25DB = 0 +ALC_MIN_GAIN_LEVEL_NEG_11_25DB = 1 +ALC_MIN_GAIN_LEVEL_NEG_5_25DB = 2 +ALC_MIN_GAIN_LEVEL_0_75DB = 3 +ALC_MIN_GAIN_LEVEL_6_75DB = 4 +ALC_MIN_GAIN_LEVEL_12_75DB = 5 +ALC_MIN_GAIN_LEVEL_18_75DB = 6 +ALC_MIN_GAIN_LEVEL_24_75DB = 7 # Automatic Level Control Hold Time (MS and SEC) -WM8960_ALC_HOLD_TIME_0MS = 0 -WM8960_ALC_HOLD_TIME_3MS = 1 -WM8960_ALC_HOLD_TIME_5MS = 2 -WM8960_ALC_HOLD_TIME_11MS = 3 -WM8960_ALC_HOLD_TIME_21MS = 4 -WM8960_ALC_HOLD_TIME_43MS = 5 -WM8960_ALC_HOLD_TIME_85MS = 6 -WM8960_ALC_HOLD_TIME_170MS = 7 -WM8960_ALC_HOLD_TIME_341MS = 8 -WM8960_ALC_HOLD_TIME_682MS = 9 -WM8960_ALC_HOLD_TIME_1365MS = 10 -WM8960_ALC_HOLD_TIME_3SEC = 11 -WM8960_ALC_HOLD_TIME_5SEC = 12 -WM8960_ALC_HOLD_TIME_10SEC = 13 -WM8960_ALC_HOLD_TIME_23SEC = 14 -WM8960_ALC_HOLD_TIME_44SEC = 15 +ALC_HOLD_TIME_0MS = 0 +ALC_HOLD_TIME_3MS = 1 +ALC_HOLD_TIME_5MS = 2 +ALC_HOLD_TIME_11MS = 3 +ALC_HOLD_TIME_21MS = 4 +ALC_HOLD_TIME_43MS = 5 +ALC_HOLD_TIME_85MS = 6 +ALC_HOLD_TIME_170MS = 7 +ALC_HOLD_TIME_341MS = 8 +ALC_HOLD_TIME_682MS = 9 +ALC_HOLD_TIME_1365MS = 10 +ALC_HOLD_TIME_3SEC = 11 +ALC_HOLD_TIME_5SEC = 12 +ALC_HOLD_TIME_10SEC = 13 +ALC_HOLD_TIME_23SEC = 14 +ALC_HOLD_TIME_44SEC = 15 # Automatic Level Control Decay Time (MS and SEC) -WM8960_ALC_DECAY_TIME_24MS = 0 -WM8960_ALC_DECAY_TIME_48MS = 1 -WM8960_ALC_DECAY_TIME_96MS = 2 -WM8960_ALC_DECAY_TIME_192MS = 3 -WM8960_ALC_DECAY_TIME_384MS = 4 -WM8960_ALC_DECAY_TIME_768MS = 5 -WM8960_ALC_DECAY_TIME_1536MS = 6 -WM8960_ALC_DECAY_TIME_3SEC = 7 -WM8960_ALC_DECAY_TIME_6SEC = 8 -WM8960_ALC_DECAY_TIME_12SEC = 9 -WM8960_ALC_DECAY_TIME_24SEC = 10 +ALC_DECAY_TIME_24MS = 0 +ALC_DECAY_TIME_48MS = 1 +ALC_DECAY_TIME_96MS = 2 +ALC_DECAY_TIME_192MS = 3 +ALC_DECAY_TIME_384MS = 4 +ALC_DECAY_TIME_768MS = 5 +ALC_DECAY_TIME_1536MS = 6 +ALC_DECAY_TIME_3SEC = 7 +ALC_DECAY_TIME_6SEC = 8 +ALC_DECAY_TIME_12SEC = 9 +ALC_DECAY_TIME_24SEC = 10 # Automatic Level Control Attack Time (MS and SEC) -WM8960_ALC_ATTACK_TIME_6MS = 0 -WM8960_ALC_ATTACK_TIME_12MS = 1 -WM8960_ALC_ATTACK_TIME_24MS = 2 -WM8960_ALC_ATTACK_TIME_482MS = 3 -WM8960_ALC_ATTACK_TIME_964MS = 4 -WM8960_ALC_ATTACK_TIME_1928MS = 5 -WM8960_ALC_ATTACK_TIME_3846MS = 6 -WM8960_ALC_ATTACK_TIME_768MS = 7 -WM8960_ALC_ATTACK_TIME_1536MS = 8 -WM8960_ALC_ATTACK_TIME_3SEC = 9 -WM8960_ALC_ATTACK_TIME_6SEC = 10 +ALC_ATTACK_TIME_6MS = 0 +ALC_ATTACK_TIME_12MS = 1 +ALC_ATTACK_TIME_24MS = 2 +ALC_ATTACK_TIME_482MS = 3 +ALC_ATTACK_TIME_964MS = 4 +ALC_ATTACK_TIME_1928MS = 5 +ALC_ATTACK_TIME_3846MS = 6 +ALC_ATTACK_TIME_768MS = 7 +ALC_ATTACK_TIME_1536MS = 8 +ALC_ATTACK_TIME_3SEC = 9 +ALC_ATTACK_TIME_6SEC = 10 # Speaker Boost Gains (DC and AC) -WM8960_SPEAKER_BOOST_GAIN_0DB = 0 -WM8960_SPEAKER_BOOST_GAIN_2_1DB = 1 -WM8960_SPEAKER_BOOST_GAIN_2_9DB = 2 -WM8960_SPEAKER_BOOST_GAIN_3_6DB = 3 -WM8960_SPEAKER_BOOST_GAIN_4_5DB = 4 -WM8960_SPEAKER_BOOST_GAIN_5_1DB = 5 +SPEAKER_BOOST_GAIN_0DB = 0 +SPEAKER_BOOST_GAIN_2_1DB = 1 +SPEAKER_BOOST_GAIN_2_9DB = 2 +SPEAKER_BOOST_GAIN_3_6DB = 3 +SPEAKER_BOOST_GAIN_4_5DB = 4 +SPEAKER_BOOST_GAIN_5_1DB = 5 # VMIDSEL settings -WM8960_VMIDSEL_DISABLED = 0 -WM8960_VMIDSEL_2X50KOHM = 1 -WM8960_VMIDSEL_2X250KOHM = 2 -WM8960_VMIDSEL_2X5KOHM = 3 +VMIDSEL_DISABLED = 0 +VMIDSEL_2X50KOHM = 1 +VMIDSEL_2X250KOHM = 2 +VMIDSEL_2X5KOHM = 3 ''' VREF to Analogue Output Resistance @@ -308,8 +309,8 @@ 0 = 500 VMID to output 1 = 20k VMID to output ''' -WM8960_VROI_500 = 0 -WM8960_VROI_20K = 1 +VROI_500 = 0 +VROI_20K = 1 ''' Analogue Bias Optimisation @@ -320,10 +321,10 @@ AVDD=3.3V ''' -WM8960_VSEL_INCREASED_BIAS_CURRENT = 1 -WM8960_VSEL_LOWEST_BIAS_CURRENT = 3 +VSEL_INCREASED_BIAS_CURRENT = 1 +VSEL_LOWEST_BIAS_CURRENT = 3 -WM8960_REGISTER_DEFAULTS = [ +_REGISTER_DEFAULTS = [ 0x0097, # R0 (0x00) 0x0097, # R1 (0x01) 0x0000, # R2 (0x02) @@ -383,20 +384,20 @@ ] class WM8960: - def __init__(self, i2c_bus:I2C, address:int = WM8960_ADDR): + def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR): self.i2c_device = I2CDevice(i2c_bus, address) self._buf = bytearray(2) ''' The WM8960 does not support I2C reads This means we must keep a local copy of all the register values - We will instantiate with default values by copying from WM8960_REGISTER_DEFAULTS during reset() + We will instantiate with default values by copying from _REGISTER_DEFAULTS during reset() As we write to the device, we will also make sure to update our local copy as well, stored here in this array. Each register is 9-bits They are in order from R0-R55, and we even keep blank spots for the "reserved" registers. This way we can use the register address macro defines above to easiy access each local copy of each register. - Example: self._registerLocalCopy[WM8960_REG_LEFT_INPUT_VOLUME] + Example: self._registerLocalCopy[_REG_LEFT_INPUT_VOLUME] ''' - self._registerLocalCopy = [0x0000 for i in range(len(WM8960_REGISTER_DEFAULTS))] + self._registerLocalCopy = [0x0000 for i in range(len(_REGISTER_DEFAULTS))] self.reset() # General setup @@ -414,7 +415,7 @@ def _getDivBit(self, src:int, dest:int) -> int: if div % 2 == 1: return 0 return div // 2 - def configureI2S(self, sample_rate:int, word_length:int = WM8960_WL_16BIT, master:bool = False) -> bool: + def configureI2S(self, sample_rate:int, word_length:int = WL_16BIT, master:bool = False) -> bool: if not self.setSampleRate(sample_rate): return False if not self.setWL(word_length): return False if master: @@ -462,14 +463,14 @@ def configureSpeakers(self, dB:float = 0.0) -> bool: def setSampleRate(self, sample_rate:int) -> bool: # MCLK = 24 MHz if not self.enablePLL(): return False # Needed for class-d amp clock - self.setSMD(WM8960_PLL_MODE_FRACTIONAL) - self.setCLKSEL(WM8960_CLKSEL_PLL) + self.setSMD(PLL_MODE_FRACTIONAL) + self.setCLKSEL(CLKSEL_PLL) - self.setPLLPRESCALE(WM8960_PLLPRESCALE_DIV_2) - self.setSYSCLKDIV(WM8960_SYSCLK_DIV_BY_2) + self.setPLLPRESCALE(PLLPRESCALE_DIV_2) + self.setSYSCLKDIV(SYSCLK_DIV_BY_2) self.setBCLKDIV(4) - self.setDCLKDIV(WM8960_DCLKDIV_16) + self.setDCLKDIV(DCLKDIV_16) if sample_rate in [8000, 12000, 16000, 24000, 32000, 48000]: # SYSCLK = 12.288 MHz @@ -496,40 +497,40 @@ def setSampleRate(self, sample_rate:int) -> bool: ''' Necessary for all other functions of the CODEC - VREF is a single bit we can flip in Register 25 (19h), WM8960_REG_PWR_MGMT_1 + VREF is a single bit we can flip in Register 25 (19h), _REG_PWR_MGMT_1 VREF is bit 6, 0 = power down, 1 = power up Returns 1 if successful, 0 if something failed (I2C error) ''' def enableVREF(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 6, 1) ''' Use this to save power - VREF is a single bit we can flip in Register 25 (19h), WM8960_REG_PWR_MGMT_1 + VREF is a single bit we can flip in Register 25 (19h), _REG_PWR_MGMT_1 VREF is bit 6, 0 = power down, 1 = power up Returns 1 if successful, 0 if something failed (I2C error) ''' def disableVREF(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 6, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 6, 0) # Resets all registers to their default state def reset(self) -> bool: # Doesn't matter which bit we flip, writing anything will cause the reset - if not self._writeRegisterBit(WM8960_REG_RESET, 7, 1): return False + if not self._writeRegisterBit(_REG_RESET, 7, 1): return False # Update our local copy of the registers to reflect the reset - for i in range(len(WM8960_REGISTER_DEFAULTS)): - self._registerLocalCopy[i] = WM8960_REGISTER_DEFAULTS[i] + for i in range(len(_REGISTER_DEFAULTS)): + self._registerLocalCopy[i] = _REGISTER_DEFAULTS[i] return True def enableAINL(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 5, 1) def disableAINL(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 5, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 5, 0) def enableAINR(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 4, 1) def disableAINR(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 4, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 4, 0) def enableAIN(self) -> bool: return self.enableAINL() and self.enableAINR() @@ -537,14 +538,14 @@ def disableAIN(self) -> bool: return self.disableAINL() and self.disableAINR() def enableLMIC(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 1) def disableLMIC(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 0) def enableRMIC(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 1) def disableRMIC(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 0) def enableMIC(self) -> bool: return self.enableLMIC() and self.enableRMIC() @@ -552,14 +553,14 @@ def disableMIC(self) -> bool: return self.disableLMIC() and self.disableRMIC() def enableLMICBOOST(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 1) def disableLMICBOOST(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 5, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 0) def enableRMICBOOST(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 1) def disableRMICBOOST(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 4, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 0) def enableMICBOOST(self) -> bool: return self.enableLMICBOOST() and self.enableRMICBOOST() @@ -577,7 +578,7 @@ def disableMICBOOST(self) -> bool: # Note, the inverting input of PGA_RIGHT is perminantly connected to # RINPUT1 - # 3 options: WM8960_PGAL_LINPUT2, WM8960_PGAL_LINPUT3, WM8960_PGAL_VMID + # 3 options: PGAL_LINPUT2, PGAL_LINPUT3, PGAL_VMID def pgaLeftNonInvSignalSelect(self, signal:int) -> bool: ''' Clear LMP2 and LMP3 @@ -587,24 +588,24 @@ def pgaLeftNonInvSignalSelect(self, signal:int) -> bool: ''' # LMP3 - if not self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 0): return False + if not self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 7, 0): return False # LMP2 - if not self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 0): return False + if not self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 6, 0): return False - if signal == WM8960_PGAL_LINPUT2: + if signal == PGAL_LINPUT2: # LMP2 - return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 6, 1) - elif signal == WM8960_PGAL_LINPUT3: + return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 6, 1) + elif signal == PGAL_LINPUT3: # LMP3 - return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 7, 1) - elif signal == WM8960_PGAL_VMID: + return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 7, 1) + elif signal == PGAL_VMID: # Don't set any bits. When both LMP2 and LMP3 are cleared, then the signal is set to VMID return True else: raise Exception("Invalid PGA signal select") - # 3 options: WM8960_PGAR_RINPUT2, WM8960_PGAR_RINPUT3, WM8960_PGAR_VMID + # 3 options: PGAR_RINPUT2, PGAR_RINPUT3, PGAR_VMID def pgaRightNonInvSignalSelect(self, signal:int) -> bool: ''' Clear RMP2 and RMP3 @@ -614,24 +615,24 @@ def pgaRightNonInvSignalSelect(self, signal:int) -> bool: ''' # RMP3 - if not self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 0): return False + if not self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 7, 0): return False # RMP2 - if not self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 0): return False + if not self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 6, 0): return False - if signal == WM8960_PGAR_RINPUT2: + if signal == PGAR_RINPUT2: # RMP2 - return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 6, 1) - elif signal == WM8960_PGAR_RINPUT3: + return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 6, 1) + elif signal == PGAR_RINPUT3: # RMP3 - return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 7, 1) - elif signal == WM8960_PGAR_VMID: + return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 7, 1) + elif signal == PGAR_VMID: # Don't set any bits. When both RMP2 and RMP3 are cleared, then the signal is set to VMID return True else: raise Exception("Invalid PGA signal select") - # 3 options: WM8960_PGA_INPUT2, WM8960_PGA_INPUT3, WM8960_PGA_VMID + # 3 options: PGA_INPUT2, PGA_INPUT3, PGA_VMID def pgaNonInvSignalSelect(self, signal:int) -> bool: return self.pgaLeftNonInvSignalSelect(signal) and self.pgaRightNonInvSignalSelect(signal) @@ -639,19 +640,19 @@ def pgaNonInvSignalSelect(self, signal:int) -> bool: # Connect LINPUT1 to inverting input of Left Input PGA def connectLMN1(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 1) + return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 8, 1) # Disconnect LINPUT1 from inverting input of Left Input PGA def disconnectLMN1(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 8, 0) + return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 8, 0) # Connect RINPUT1 to inverting input of Right Input PGA def connectRMN1(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 1) + return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 8, 1) # Disconnect RINPUT1 from inverting input of Right Input PGA def disconnectRMN1(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 8, 0) + return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 8, 0) def connectMN1(self) -> bool: return self.connectLMN1() and self.connectRMN1() @@ -662,19 +663,19 @@ def disconnectMN1(self) -> bool: # Connect Left Input PGA to Left Input Boost mixer def connectLMIC2B(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 1) + return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 3, 1) # Disconnect Left Input PGA to Left Input Boost mixer def disconnectLMIC2B(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCL_SIGNAL_PATH, 3, 0) + return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 3, 0) # Connect Right Input PGA to Right Input Boost mixer def connectRMIC2B(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 1) + return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 3, 1) # Disconnect Right Input PGA to Right Input Boost mixer def disconnectRMIC2B(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADCR_SIGNAL_PATH, 3, 0) + return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 3, 0) def connectMIC2B(self) -> bool: return self.connectLMIC2B() and self.connectRMIC2B() @@ -686,7 +687,7 @@ def setLINVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 63: volume = 63 - if not self._writeRegisterMultiBits(WM8960_REG_LEFT_INPUT_VOLUME, 5, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_LEFT_INPUT_VOLUME, 5, 0, volume): return False return self.pgaLeftIPVUSet() ''' @@ -699,7 +700,7 @@ def setLINVOL(self, volume:int) -> bool: ''' def setLINVOLDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setLINVOL() - volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) + volume = self.convertDBtoSetting(dB, PGA_GAIN_OFFSET, PGA_GAIN_STEPSIZE, PGA_GAIN_MIN, PGA_GAIN_MAX) return self.setLINVOL(volume) # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) @@ -707,7 +708,7 @@ def setRINVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 63: volume = 63 - if not self._writeRegisterMultiBits(WM8960_REG_RIGHT_INPUT_VOLUME, 5, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_RIGHT_INPUT_VOLUME, 5, 0, volume): return False return self.pgaRightIPVUSet() ''' @@ -720,7 +721,7 @@ def setRINVOL(self, volume:int) -> bool: ''' def setRINVOLDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setLINVOL() - volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) + volume = self.convertDBtoSetting(dB, PGA_GAIN_OFFSET, PGA_GAIN_STEPSIZE, PGA_GAIN_MIN, PGA_GAIN_MAX) return self.setRINVOL(volume) def setINVOL(self, volume:int) -> bool: @@ -728,25 +729,25 @@ def setINVOL(self, volume:int) -> bool: def setINVOLDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setLINVOL() - volume = self.convertDBtoSetting(dB, WM8960_PGA_GAIN_OFFSET, WM8960_PGA_GAIN_STEPSIZE, WM8960_PGA_GAIN_MIN, WM8960_PGA_GAIN_MAX) + volume = self.convertDBtoSetting(dB, PGA_GAIN_OFFSET, PGA_GAIN_STEPSIZE, PGA_GAIN_MIN, PGA_GAIN_MAX) return self.setLINVOL(volume) and self.setRINVOL(volume) # Zero Cross prevents zipper sounds on volume changes # Sets both left and right PGAs def enablePgaZeroCross(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 1) and self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 1) + return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 6, 1) and self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 6, 1) def disablePgaZeroCross(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 6, 0) and self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 6, 0) + return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 6, 0) and self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 6, 0) def enableLINMUTE(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 1) + return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 7, 1) def disableLINMUTE(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 8, 1) def enableRINMUTE(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 1) + return self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 7, 1) def disableRINMUTE(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 8, 1) def enableINMUTE(self) -> bool: return self.enableLINMUTE() and self.enableRINMUTE() @@ -756,88 +757,88 @@ def disableINMUTE(self) -> bool: # Causes left and right input PGA volumes to be updated # (LINVOL and RINVOL) def pgaLeftIPVUSet(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_INPUT_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 8, 1) # Causes left and right input PGA volumes to be updated # (LINVOL and RINVOL) def pgaRightIPVUSet(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_INPUT_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 8, 1) # Boosts - # WM8960_MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB + # MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB def setLMICBOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 3: boost_gain = 3 - return self._writeRegisterMultiBits(WM8960_REG_ADCL_SIGNAL_PATH, 5, 4, boost_gain) + return self._writeRegisterMultiBits(_REG_ADCL_SIGNAL_PATH, 5, 4, boost_gain) - # WM8960_MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB + # MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB def setRMICBOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 3: boost_gain = 3 - return self._writeRegisterMultiBits(WM8960_REG_ADCR_SIGNAL_PATH, 5, 4, boost_gain) + return self._writeRegisterMultiBits(_REG_ADCR_SIGNAL_PATH, 5, 4, boost_gain) def setMICBOOST(self, boost_gain:int) -> bool: return self.setLMICBOOST(boost_gain) and self.setRMICBOOST(boost_gain) - # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... def setLIN2BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) + return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) - # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... def setRIN2BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) + return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) def setIN2BOOST(self, boost_gain:int) -> bool: return self.setLIN2BOOST(boost_gain) and self.setRIN2BOOST(boost_gain) - # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... def setLIN3BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) + return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) - # WM8960_BOOST_MIXER_GAIN_MUTE, WM8960_BOOST_MIXER_GAIN_NEG_12DB, ... + # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... def setRIN3BOOST(self, boost_gain:int) -> bool: # Limit incoming values max if boost_gain > 7: boost_gain = 7 - return self._writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) + return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) def setIN3BOOST(self, boost_gain:int) -> bool: return self.setLIN3BOOST(boost_gain) and self.setRIN3BOOST(boost_gain) # Mic Bias control def enableMicBias(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 1, 1) def disableMicBias(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 1, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 1, 0) - # WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) - # or WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) + # MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) + # or MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) def setMicBiasVoltage(self, voltage:bool) -> bool: - return self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_4, 0, voltage) + return self._writeRegisterBit(_REG_ADDITIONAL_CONTROL_4, 0, voltage) ## ADC def enableAdcLeft(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 3, 1) def disableAdcLeft(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 3, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 3, 0) def enableAdcRight(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 2, 1) def disableAdcRight(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_1, 2, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_1, 2, 0) def enableAdc(self) -> bool: return self.enableAdcLeft() and self.enableAdcRight() @@ -853,10 +854,10 @@ def disableAdc(self) -> bool: # 195 = 0dB # 255 = +30dB def setAdcLeftDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(WM8960_REG_LEFT_ADC_VOLUME, 7, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_LEFT_ADC_VOLUME, 7, 0, volume): return False return self.adcLeftADCVUSet() def setAdcRightDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(WM8960_REG_RIGHT_ADC_VOLUME, 7, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_RIGHT_ADC_VOLUME, 7, 0, volume): return False return self.adcRightADCVUSet() ''' @@ -871,20 +872,20 @@ def setAdcRightDigitalVolume(self, volume:int) -> bool: ''' def setAdcLeftDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() - volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) + volume = self.convertDBtoSetting(dB, ADC_GAIN_OFFSET, ADC_GAIN_STEPSIZE, ADC_GAIN_MIN, ADC_GAIN_MAX) return self.setAdcLeftDigitalVolume(volume) def setAdcRightDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setAdcRightDigitalVolume() - volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) + volume = self.convertDBtoSetting(dB, ADC_GAIN_OFFSET, ADC_GAIN_STEPSIZE, ADC_GAIN_MIN, ADC_GAIN_MAX) return self.setAdcRightDigitalVolume(volume) # Causes left and right input ADC volumes to be updated def adcLeftADCVUSet(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_ADC_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_LEFT_ADC_VOLUME, 8, 1) # Causes left and right input ADC volumes to be updated def adcRightADCVUSet(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_ADC_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_RIGHT_ADC_VOLUME, 8, 1) # Control ADC volume in a stereo pair def setAdcDigitalVolume(self, volume:int) -> bool: @@ -892,7 +893,7 @@ def setAdcDigitalVolume(self, volume:int) -> bool: def setAdcDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() - volume = self.convertDBtoSetting(dB, WM8960_ADC_GAIN_OFFSET, WM8960_ADC_GAIN_STEPSIZE, WM8960_ADC_GAIN_MIN, WM8960_ADC_GAIN_MAX) + volume = self.convertDBtoSetting(dB, ADC_GAIN_OFFSET, ADC_GAIN_STEPSIZE, ADC_GAIN_MIN, ADC_GAIN_MAX) return self.setAdcDigitalVolume(volume) ## ALC @@ -903,13 +904,13 @@ def setAdcDigitalVolumeDB(self, dB:float) -> bool: # RINMUTE) are ignored. # Also sets alc sample rate to match global sample rate. - def enableAlc(self, mode:int = WM8960_ALC_MODE_STEREO) -> bool: + def enableAlc(self, mode:int = ALC_MODE_STEREO) -> bool: bit8 = mode >> 1 bit7 = mode & 0x1 - return self._writeRegisterBit(WM8960_REG_ALC1, 8, bit8) and self._writeRegisterBit(WM8960_REG_ALC1, 7, bit7) + return self._writeRegisterBit(_REG_ALC1, 8, bit8) and self._writeRegisterBit(_REG_ALC1, 7, bit7) def disableAlc(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ALC1, 8, 0) and self._writeRegisterBit(WM8960_REG_ALC1, 7, 0) + return self._writeRegisterBit(_REG_ALC1, 8, 0) and self._writeRegisterBit(_REG_ALC1, 7, 0) # Valid inputs are 0-15 # 0 = -22.5dB FS ... 1.5dB steps ... 15 = -1.5dB FS @@ -917,14 +918,14 @@ def setAlcTarget(self, target:int) -> bool: # Limit incoming values max if target > 15: target = 15 - return self._writeRegisterMultiBits(WM8960_REG_ALC1,3,0,target) + return self._writeRegisterMultiBits(_REG_ALC1,3,0,target) # Valid inputs are 0-10, 0 = 24ms, 1 = 48ms ... 10 = 24.58seconds def setAlcDecay(self, decay:int) -> bool: # Limit incoming values max if decay > 10: decay = 10 - return self._writeRegisterMultiBits(WM8960_REG_ALC3, 7, 4, decay) + return self._writeRegisterMultiBits(_REG_ALC3, 7, 4, decay) # Valid inputs are 0-10, 0 = 6ms, 1 = 12ms, 2 = 24ms ... # 10 = 6.14seconds @@ -932,41 +933,41 @@ def setAlcAttack(self, attack:int) -> bool: # Limit incoming values max if attack > 10: attack = 10 - return self._writeRegisterMultiBits(WM8960_REG_ALC3, 3, 0, attack) + return self._writeRegisterMultiBits(_REG_ALC3, 3, 0, attack) # Valid inputs are 0-7, 0 = -12dB, ... 7 = +30dB def setAlcMaxGain(self, maxGain:int) -> bool: # Limit incoming values max if maxGain > 7: maxGain = 7 - return self._writeRegisterMultiBits(WM8960_REG_ALC1, 6, 4, maxGain) + return self._writeRegisterMultiBits(_REG_ALC1, 6, 4, maxGain) # Valid inputs are 0-7, 0 = -17.25dB, ... 7 = +24.75dB def setAlcMinGain(self, minGain:int) -> bool: # Limit incoming values max if minGain > 7: minGain = 7 - return self._writeRegisterMultiBits(WM8960_REG_ALC2, 6, 4, minGain) + return self._writeRegisterMultiBits(_REG_ALC2, 6, 4, minGain) # Valid inputs are 0-15, 0 = 0ms, ... 15 = 43.691s def setAlcHold(self, hold:int) -> bool: # Limit incoming values max if hold > 15: hold = 15 - return self._writeRegisterMultiBits(WM8960_REG_ALC2, 3, 0, hold) + return self._writeRegisterMultiBits(_REG_ALC2, 3, 0, hold) # Peak Limiter def enablePeakLimiter(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ALC3, 8, 1) + return self._writeRegisterBit(_REG_ALC3, 8, 1) def disablePeakLimiter(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ALC3, 8, 0) + return self._writeRegisterBit(_REG_ALC3, 8, 0) # Noise Gate def enableNoiseGate(self) -> bool: - return self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 1) + return self._writeRegisterBit(_REG_NOISE_GATE, 0, 1) def disableNoiseGate(self) -> bool: - return self._writeRegisterBit(WM8960_REG_NOISE_GATE, 0, 0) + return self._writeRegisterBit(_REG_NOISE_GATE, 0, 0) # 0-31, 0 = -76.5dBfs, 31 = -30dBfs def setNoiseGateThreshold(self, threshold:int) -> bool: @@ -977,14 +978,14 @@ def setNoiseGateThreshold(self, threshold:int) -> bool: # Enable/disble each channel def enableDacLeft(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 8, 1) def disableDacLeft(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 8, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 8, 0) def enableDacRight(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 7, 1) def disableDacRight(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 7, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 7, 0) def enableDac(self) -> bool: return self.enableDacLeft() and self.enableDacRight() @@ -998,11 +999,11 @@ def disableDac(self) -> bool: # ... 0.5dB steps up to # 255 = 0dB def setDacLeftDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(WM8960_REG_LEFT_DAC_VOLUME, 7, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_LEFT_DAC_VOLUME, 7, 0, volume): return False return self.dacLeftDACVUSet() def setDacRightDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(WM8960_REG_RIGHT_DAC_VOLUME, 7, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_RIGHT_DAC_VOLUME, 7, 0, volume): return False return self.dacRightDACVUSet() ''' @@ -1016,57 +1017,57 @@ def setDacRightDigitalVolume(self, volume:int) -> bool: ''' def setDacLeftDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setDacLeftDigitalVolume() - volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) + volume = self.convertDBtoSetting(dB, DAC_GAIN_OFFSET, DAC_GAIN_STEPSIZE, DAC_GAIN_MIN, DAC_GAIN_MAX) return self.setDacLeftDigitalVolume(volume) def setDacRightDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() - volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) + volume = self.convertDBtoSetting(dB, DAC_GAIN_OFFSET, DAC_GAIN_STEPSIZE, DAC_GAIN_MIN, DAC_GAIN_MAX) return self.setDacRightDigitalVolume(volume) # Causes left and right input DAC volumes to be updated def dacLeftDACVUSet(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_DAC_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_LEFT_DAC_VOLUME, 8, 1) # Causes left and right input DAC volumes to be updated def dacRightDACVUSet(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_DAC_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_RIGHT_DAC_VOLUME, 8, 1) def setDacDigitalVolume(self, volume:int) -> bool: return self.setDacLeftDigitalVolume(volume) and self.setDacRightDigitalVolume(volume) def setDacDigitalVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() - volume = self.convertDBtoSetting(dB, WM8960_DAC_GAIN_OFFSET, WM8960_DAC_GAIN_STEPSIZE, WM8960_DAC_GAIN_MIN, WM8960_DAC_GAIN_MAX) + volume = self.convertDBtoSetting(dB, DAC_GAIN_OFFSET, DAC_GAIN_STEPSIZE, DAC_GAIN_MIN, DAC_GAIN_MAX) return self.setDacDigitalVolume(volume) # DAC mute def enableDacMute(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 1) + return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 3, 1) def disableDacMute(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 3, 0) + return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 3, 0) # DE-Emphasis # 3D Stereo Enhancement # 3D enable/disable def enable3d(self) -> bool: - return self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 1) + return self._writeRegisterBit(_REG_3D_CONTROL, 0, 1) def disable3d(self) -> bool: - return self._writeRegisterBit(WM8960_REG_3D_CONTROL, 0, 0) + return self._writeRegisterBit(_REG_3D_CONTROL, 0, 0) def set3dDepth(self, depth:int): # 0 = 0%, 15 = 100% # Limit incoming values max if depth > 15: depth = 15 - return self._writeRegisterMultiBits(WM8960_REG_3D_CONTROL, 4, 1, depth) + return self._writeRegisterMultiBits(_REG_3D_CONTROL, 4, 1, depth) # 3D upper/lower cut-off frequencies. # DAC output -6dB attentuation enable/disable def enableDac6dbAttenuation(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 1) + return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 7, 1) def disableDac6dbAttentuation(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ADC_DAC_CTRL_1, 7, 0) + return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 7, 0) ## OUTPUT mixers @@ -1077,90 +1078,90 @@ def disableDac6dbAttentuation(self) -> bool: # Enable/disable left and right output mixers def enableLOMIX(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 3, 1) def disableLOMIX(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 3, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 3, 0) def enableROMIX(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 2, 1) def disableROMIX(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_3, 2, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_3, 2, 0) def enableOUT3MIX(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 1, 1) def disableOUT3MIX(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 1, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 1, 0) # Enable/disable audio path connections/vols to/from output mixers # See datasheet page 35 for a nice image of all the connections. def enableLI2LO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 1) + return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 7, 1) def disableLI2LO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 7, 0) + return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB def setLI2LOVOL(self, volume:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_LEFT_OUT_MIX_1, 6, 4, volume) + return self._writeRegisterMultiBits(_REG_LEFT_OUT_MIX_1, 6, 4, volume) def enableLB2LO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 1) + return self._writeRegisterBit(_REG_BYPASS_1, 7, 1) def disableLB2LO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_BYPASS_1, 7, 0) + return self._writeRegisterBit(_REG_BYPASS_1, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB def setLB2LOVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 7: volume = 7 - return self._writeRegisterMultiBits(WM8960_REG_BYPASS_1, 6, 4, volume) + return self._writeRegisterMultiBits(_REG_BYPASS_1, 6, 4, volume) def enableLD2LO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 1) + return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 8, 1) def disableLD2LO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_LEFT_OUT_MIX_1, 8, 0) + return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 8, 0) def enableRI2RO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 1) + return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 7, 1) def disableRI2RO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 7, 0) + return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB def setRI2ROVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 7: volume = 7 - return self._writeRegisterMultiBits(WM8960_REG_RIGHT_OUT_MIX_2, 6, 4, volume) + return self._writeRegisterMultiBits(_REG_RIGHT_OUT_MIX_2, 6, 4, volume) def enableRB2RO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 1) + return self._writeRegisterBit(_REG_BYPASS_2, 7, 1) def disableRB2RO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_BYPASS_2, 7, 0) + return self._writeRegisterBit(_REG_BYPASS_2, 7, 0) # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB def setRB2ROVOL(self, volume:int) -> bool: # Limit incoming values max if volume > 7: volume = 7 - return self._writeRegisterMultiBits(WM8960_REG_BYPASS_2, 6, 4, volume) + return self._writeRegisterMultiBits(_REG_BYPASS_2, 6, 4, volume) def enableRD2RO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 1) + return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 8, 1) def disableRD2RO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_RIGHT_OUT_MIX_2, 8, 0) + return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 8, 0) # Mono Output mixer. # Note, for capless HPs, we'll want this to output a buffered VMID. # To do this, we need to disable both of these connections. def enableLI2MO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 1) + return self._writeRegisterBit(_REG_MONO_OUT_MIX_1, 7, 1) def disableLI2MO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_1, 7, 0) + return self._writeRegisterBit(_REG_MONO_OUT_MIX_1, 7, 0) def enableRI2MO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 1) + return self._writeRegisterBit(_REG_MONO_OUT_MIX_2, 7, 1) def disableRI2MO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_MONO_OUT_MIX_2, 7, 0) + return self._writeRegisterBit(_REG_MONO_OUT_MIX_2, 7, 0) # Paired stereo functions to enable/disable output mixers def enableI2O(self) -> bool: @@ -1194,26 +1195,26 @@ def disableOMIX(self) -> bool: # Sets the VMID signal to one of three possible settings. # 4 options: - # WM8960_VMIDSEL_DISABLED - # WM8960_VMIDSEL_2X50KOHM (playback / record) - # WM8960_VMIDSEL_2X250KOHM (for low power / standby) - # WM8960_VMIDSEL_2X5KOHM (for fast start-up) - def setVMID(self, setting:int = WM8960_VMIDSEL_2X50KOHM) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_PWR_MGMT_1, 8, 7, setting) + # VMIDSEL_DISABLED + # VMIDSEL_2X50KOHM (playback / record) + # VMIDSEL_2X250KOHM (for low power / standby) + # VMIDSEL_2X5KOHM (for fast start-up) + def setVMID(self, setting:int = VMIDSEL_2X50KOHM) -> bool: + return self._writeRegisterMultiBits(_REG_PWR_MGMT_1, 8, 7, setting) - # Enables VMID in the WM8960_REG_PWR_MGMT_2 register, and set's it to + # Enables VMID in the _REG_PWR_MGMT_2 register, and set's it to # playback/record settings of 2*50Kohm. # Note, this function is only here for backwards compatibility with the # original releases of this library. It is recommended to use the # setVMID() function instead. def enableVMID(self) -> bool: - return self.setVMID(WM8960_VMIDSEL_2X50KOHM) + return self.setVMID(VMIDSEL_2X50KOHM) def disableVMID(self) -> bool: - return self.setVMID(WM8960_VMIDSEL_DISABLED) + return self.setVMID(VMIDSEL_DISABLED) # This will disable both connections, thus enable VMID on OUT3. Note, # to enable VMID, you also need to enable OUT3 in the - # WM8960_REG_PWR_MGMT_2 [1] + # _REG_PWR_MGMT_2 [1] def enableOUT3asVMID(self) -> bool: return self.disableLI2MO() and self.disableRI2MO() and self.enableOUT3MIX() and self.enableVMID() @@ -1226,18 +1227,18 @@ def disableHeadphones(self) -> bool: return self.disableRightHeadphone() and self.disableLeftHeadphone() def enableRightHeadphone(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 5, 1) def disableRightHeadphone(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 5, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 5, 0) def enableLeftHeadphone(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 6, 1) def disableLeftHeadphone(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 6, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 6, 0) def enableHeadphoneStandby(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 1) + return self._writeRegisterBit(_REG_ANTI_POP_1, 0, 1) def disableHeadphoneStandby(self) -> bool: - return self._writeRegisterBit(WM8960_REG_ANTI_POP_1, 0, 0) + return self._writeRegisterBit(_REG_ANTI_POP_1, 0, 0) # Set headphone volume # Although you can control each headphone output independently, here @@ -1250,7 +1251,7 @@ def setHeadphoneVolume(self, volume:int) -> bool: # Updates both left and right channels # Handles the OUT1VU (volume update) bit control, so that it happens at the # same time on both channels. Note, we must also make sure that the outputs - # are enabled in the WM8960_REG_PWR_MGMT_2 [6:5] + # are enabled in the _REG_PWR_MGMT_2 [6:5] # Grab local copy of register # Modify the bits we need to # Write register in device, including the volume update bit write @@ -1261,17 +1262,17 @@ def setHeadphoneVolume(self, volume:int) -> bool: volume = 127 # LEFT - if not self._writeRegisterMultiBits(WM8960_REG_LOUT1_VOLUME, 6, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_LOUT1_VOLUME, 6, 0, volume): return False # RIGHT - if not self._writeRegisterMultiBits(WM8960_REG_ROUT1_VOLUME, 6, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_ROUT1_VOLUME, 6, 0, volume): return False # UPDATES # Updated left channel - if not self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 8, 1): return False + if not self._writeRegisterBit(_REG_LOUT1_VOLUME, 8, 1): return False # Updated right channel - return self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_ROUT1_VOLUME, 8, 1) # Set headphone volume dB # Sets the volume of the headphone output buffer amp to a speicified @@ -1286,22 +1287,22 @@ def setHeadphoneVolume(self, volume:int) -> bool: # 6 = +6dB (MAX) def setHeadphoneVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setHeadphoneVolume() - volume = self.convertDBtoSetting(dB, WM8960_HP_GAIN_OFFSET, WM8960_HP_GAIN_STEPSIZE, WM8960_HP_GAIN_MIN, WM8960_HP_GAIN_MAX) + volume = self.convertDBtoSetting(dB, HP_GAIN_OFFSET, HP_GAIN_STEPSIZE, HP_GAIN_MIN, HP_GAIN_MAX) return self.setHeadphoneVolume(volume) # Zero Cross prevents zipper sounds on volume changes # Sets both left and right Headphone outputs def enableHeadphoneZeroCross(self) -> bool: # Left - if not self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 1): return False + if not self._writeRegisterBit(_REG_LOUT1_VOLUME, 7, 1): return False # Right - return self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 1) + return self._writeRegisterBit(_REG_ROUT1_VOLUME, 7, 1) def disableHeadphoneZeroCross(self) -> bool: # Left - if not self._writeRegisterBit(WM8960_REG_LOUT1_VOLUME, 7, 0): return False + if not self._writeRegisterBit(_REG_LOUT1_VOLUME, 7, 0): return False # Right - return self._writeRegisterBit(WM8960_REG_ROUT1_VOLUME, 7, 0) + return self._writeRegisterBit(_REG_ROUT1_VOLUME, 7, 0) ## Speakers @@ -1313,27 +1314,27 @@ def disableSpeakers(self) -> bool: def enableRightSpeaker(self) -> bool: # SPK_OP_EN - if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 1): return False + if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 7, 1): return False # SPKR - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 3, 1) def disableRightSpeaker(self) -> bool: # SPK_OP_EN - if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 7, 0): return False + if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 7, 0): return False # SPKR - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 3, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 3, 0) def enableLeftSpeaker(self) -> bool: # SPK_OP_EN - if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 1): return False + if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 6, 1): return False # SPKL - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 4, 1) def disableLeftSpeaker(self) -> bool: # SPK_OP_EN - if not self._writeRegisterBit(WM8960_REG_CLASS_D_CONTROL_1, 6, 0): return False + if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 6, 0): return False # SPKL - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 4, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 4, 0) # Set Speaker output volume # Although you can control each Speaker output independently, here we @@ -1345,42 +1346,42 @@ def setSpeakerVolume(self, volume:int) -> bool: # Updates both left and right channels # Handles the SPKVU (volume update) bit control, so that it happens at the # same time on both channels. Note, we must also make sure that the outputs - # are enabled in the WM8960_REG_PWR_MGMT_2 [4:3], and the class D control - # reg WM8960_REG_CLASS_D_CONTROL_1 [7:6] + # are enabled in the _REG_PWR_MGMT_2 [4:3], and the class D control + # reg _REG_CLASS_D_CONTROL_1 [7:6] # Limit inputs if volume > 127: volume = 127 # LEFT - if not self._writeRegisterMultiBits(WM8960_REG_LOUT2_VOLUME, 6, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_LOUT2_VOLUME, 6, 0, volume): return False # RIGHT - if not self._writeRegisterMultiBits(WM8960_REG_ROUT2_VOLUME, 6, 0, volume): return False + if not self._writeRegisterMultiBits(_REG_ROUT2_VOLUME, 6, 0, volume): return False # SPKVU # Updated left channel - if not self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 8, 1): return False + if not self._writeRegisterBit(_REG_LOUT2_VOLUME, 8, 1): return False # Updated right channel - return self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 8, 1) + return self._writeRegisterBit(_REG_ROUT2_VOLUME, 8, 1) def setSpeakerVolumeDB(self, dB:float) -> bool: # Create an unsigned integer volume setting variable we can send to setSpeakerVolume() - volume = self.convertDBtoSetting(dB, WM8960_SPEAKER_GAIN_OFFSET, WM8960_SPEAKER_GAIN_STEPSIZE, WM8960_SPEAKER_GAIN_MIN, WM8960_SPEAKER_GAIN_MAX) + volume = self.convertDBtoSetting(dB, SPEAKER_GAIN_OFFSET, SPEAKER_GAIN_STEPSIZE, SPEAKER_GAIN_MIN, SPEAKER_GAIN_MAX) return self.setSpeakerVolume(volume) # Zero Cross prevents zipper sounds on volume changes # Sets both left and right Speaker outputs def enableSpeakerZeroCross(self) -> bool: # Left - if not self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 1): return False + if not self._writeRegisterBit(_REG_LOUT2_VOLUME, 7, 1): return False # Right - return self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 1) + return self._writeRegisterBit(_REG_ROUT2_VOLUME, 7, 1) def disableSpeakerZeroCross(self) -> bool: # Left - if not self._writeRegisterBit(WM8960_REG_LOUT2_VOLUME, 7, 0): return False + if not self._writeRegisterBit(_REG_LOUT2_VOLUME, 7, 0): return False # Right - return self._writeRegisterBit(WM8960_REG_ROUT2_VOLUME, 7, 0) + return self._writeRegisterBit(_REG_ROUT2_VOLUME, 7, 0) # DC and AC gain - allows signal to be higher than the DACs swing # (use only if your SPKVDD is high enough to handle a larger signal) @@ -1390,13 +1391,13 @@ def setSpeakerDcGain(self, gain:int) -> bool: # Limit incoming values max if gain > 5: gain = 5 - return self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 5, 3, gain) + return self._writeRegisterMultiBits(_REG_CLASS_D_CONTROL_3, 5, 3, gain) def setSpeakerAcGain(self, gain:int) -> bool: # Limit incoming values max if gain > 5: gain = 5 - return self._writeRegisterMultiBits(WM8960_REG_CLASS_D_CONTROL_3, 2, 0, gain) + return self._writeRegisterMultiBits(_REG_CLASS_D_CONTROL_3, 2, 0, gain) ## Digital audio interface control @@ -1406,9 +1407,9 @@ def setSpeakerAcGain(self, gain:int) -> bool: # When enabled, the output data from the ADC audio interface is fed # directly into the DAC data input. def enableLoopBack(self) -> bool: - return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 1) + return self._writeRegisterBit(_REG_AUDIO_INTERFACE_2, 0, 1) def disableLoopBack(self) -> bool: - return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 0, 0) + return self._writeRegisterBit(_REG_AUDIO_INTERFACE_2, 0, 0) ## Clock controls @@ -1446,76 +1447,76 @@ def disableLoopBack(self) -> bool: # And now for the functions that will set these registers... def enablePLL(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 1) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 0, 1) def disablePLL(self) -> bool: - return self._writeRegisterBit(WM8960_REG_PWR_MGMT_2, 0, 0) + return self._writeRegisterBit(_REG_PWR_MGMT_2, 0, 0) - # Valid options are WM8960_PLLPRESCALE_DIV_1, WM8960_PLLPRESCALE_DIV_2 + # Valid options are PLLPRESCALE_DIV_1, PLLPRESCALE_DIV_2 def setPLLPRESCALE(self, div:bool) -> bool: - return self._writeRegisterBit(WM8960_REG_PLL_N, 4, div) + return self._writeRegisterBit(_REG_PLL_N, 4, div) def setPLLN(self, n:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_PLL_N, 3, 0, n) + return self._writeRegisterMultiBits(_REG_PLL_N, 3, 0, n) # Send each nibble of 24-bit value for value K def setPLLK(self, k:int) -> bool: - if not self._writeRegisterMultiBits(WM8960_REG_PLL_K_1, 5, 0, (k >> 16) & 0x1F): return False - if not self._writeRegisterMultiBits(WM8960_REG_PLL_K_2, 8, 0, (k >> 8) & 0xFF): return False - return self._writeRegisterMultiBits(WM8960_REG_PLL_K_3, 8, 0, k & 0xFF) + if not self._writeRegisterMultiBits(_REG_PLL_K_1, 5, 0, (k >> 16) & 0x1F): return False + if not self._writeRegisterMultiBits(_REG_PLL_K_2, 8, 0, (k >> 8) & 0xFF): return False + return self._writeRegisterMultiBits(_REG_PLL_K_3, 8, 0, k & 0xFF) # 0=integer, 1=fractional def setSMD(self, mode:bool) -> bool: - return self._writeRegisterBit(WM8960_REG_PLL_N, 5, mode) + return self._writeRegisterBit(_REG_PLL_N, 5, mode) # 0=MCLK, 1=PLL_output def setCLKSEL(self, sel:bool) -> bool: - return self._writeRegisterBit(WM8960_REG_CLOCKING_1, 0, sel) + return self._writeRegisterBit(_REG_CLOCKING_1, 0, sel) # (0=divide by 1), (2=div by 2) *1 and 3 are "reserved" def setSYSCLKDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 2, 1, div) + return self._writeRegisterMultiBits(_REG_CLOCKING_1, 2, 1, div) # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options def setADCDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 8, 6, div) + return self._writeRegisterMultiBits(_REG_CLOCKING_1, 8, 6, div) # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options def setDACDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_1, 5, 3, div) + return self._writeRegisterMultiBits(_REG_CLOCKING_1, 5, 3, div) # 0100 (4) = sufficiently high for 24bit, div by 4 allows for max word # length of 32bit def setBCLKDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 3, 0, div) + return self._writeRegisterMultiBits(_REG_CLOCKING_2, 3, 0, div) # Class D amp, 111= SYSCLK/16, so 11.2896MHz/16 = 705.6KHz def setDCLKDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_CLOCKING_2, 8, 6, div) + return self._writeRegisterMultiBits(_REG_CLOCKING_2, 8, 6, div) # Set LR clock to be the same for ADC & DAC (needed for loopback mode) def setALRCGPIO(self) -> bool: - return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_2, 6, 1) + return self._writeRegisterBit(_REG_AUDIO_INTERFACE_2, 6, 1) def enableMasterMode(self) -> bool: - return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 1) + return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 6, 1) def enablePeripheralMode(self) -> bool: - return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 6, 0) + return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 6, 0) def setWL(self, word_length:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_AUDIO_INTERFACE_1, 3, 2, word_length) + return self._writeRegisterMultiBits(_REG_AUDIO_INTERFACE_1, 3, 2, word_length) def setLRP(self, polarity:bool) -> bool: - return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 4, polarity) + return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 4, polarity) def setALRSWAP(self, swap:bool) -> bool: - return self._writeRegisterBit(WM8960_REG_AUDIO_INTERFACE_1, 8, swap) + return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 8, swap) def setVROI(self, setting:bool) -> bool: - return self._writeRegisterBit(WM8960_REG_ADDITIONAL_CONTROL_3, 6, setting) + return self._writeRegisterBit(_REG_ADDITIONAL_CONTROL_3, 6, setting) def setVSEL(self, setting:int) -> bool: - return self._writeRegisterMultiBits(WM8960_REG_ADDITIONAL_CONTROL_1, 7, 6, setting) + return self._writeRegisterMultiBits(_REG_ADDITIONAL_CONTROL_1, 7, 6, setting) # General-purpose register write def writeRegister(self, reg:int, value:int) -> bool: @@ -1548,8 +1549,8 @@ def _writeRegisterBit(self, registerAddress:int, bitNumber:int, bitValue:bool) - For example, to change the LIN2BOOST setting to +6dB, I need to write a setting of 7 (aka +6dB) to the bits [3:1] in the - WM8960_REG_INPUT_BOOST_MIXER_1 register. Like so... - _writeRegisterMultiBits(WM8960_REG_INPUT_BOOST_MIXER_1, 3, 1, 7); + _REG_INPUT_BOOST_MIXER_1 register. Like so... + _writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_1, 3, 1, 7); ''' def _writeRegisterMultiBits(self, registerAddress:int, settingMsbNum:int, settingLsbNum:int, setting:int) -> bool: regvalue = self._registerLocalCopy[registerAddress] @@ -1583,7 +1584,7 @@ def convertDBtoSetting(self, dB:float, offset:float, stepSize:float, minDB:float Let's check for the PGAs unique minDB (-17.25) to know we are currently converting a PGA setting. ''' - if minDB == WM8960_PGA_GAIN_MIN: + if minDB == PGA_GAIN_MIN: if dB < minDB: dB = minDB else: # Not PGA. All other amps have a mute setting below minDb diff --git a/examples/wm8960_01_Volume.py b/examples/wm8960_01_Volume.py index 9aeb40c..47169df 100644 --- a/examples/wm8960_01_Volume.py +++ b/examples/wm8960_01_Volume.py @@ -63,7 +63,7 @@ codec.enableI2O() # Sets volume control between "left/right input" to "left/right output mixer" -codec.setI2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setI2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) codec.configureHeadphones(capless=True) # Capless provides VMID as buffer for headphone ground diff --git a/examples/wm8960_02_INPUT2.py b/examples/wm8960_02_INPUT2.py index f06b13b..c442595 100644 --- a/examples/wm8960_02_INPUT2.py +++ b/examples/wm8960_02_INPUT2.py @@ -56,7 +56,7 @@ # Setup signal flow through the analog audio bypass connections # Set input boosts to get INPUT2 (both left and right) to the boost mixers -codec.setIN2BOOST(adafruit_wm8960.WM8960_BOOST_MIXER_GAIN_0DB) +codec.setIN2BOOST(adafruit_wm8960.BOOST_MIXER_GAIN_0DB) # Enable input boost mixers codec.enableAIN() @@ -65,7 +65,7 @@ codec.enableB2O() # Set gainstage between boost mixer and output mixers (analog bypass) -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_03_INPUT1.py b/examples/wm8960_03_INPUT1.py index 58e6359..e004ab2 100644 --- a/examples/wm8960_03_INPUT1.py +++ b/examples/wm8960_03_INPUT1.py @@ -63,7 +63,7 @@ codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() @@ -74,7 +74,7 @@ codec.enableB2O() # Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_04_Speaker.py b/examples/wm8960_04_Speaker.py index cf1221e..3915c3e 100644 --- a/examples/wm8960_04_Speaker.py +++ b/examples/wm8960_04_Speaker.py @@ -70,7 +70,7 @@ codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() # Enable boost mixers @@ -80,7 +80,7 @@ codec.enableB2O() # Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_05_Loopback.py index 3ba81ba..dd38f80 100644 --- a/examples/wm8960_05_Loopback.py +++ b/examples/wm8960_05_Loopback.py @@ -62,7 +62,7 @@ codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() # Enable boost mixers @@ -77,7 +77,7 @@ # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py index c98080c..7bc272a 100644 --- a/examples/wm8960_06_3D_Enhance.py +++ b/examples/wm8960_06_3D_Enhance.py @@ -71,7 +71,7 @@ codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() # Enable boost mixers @@ -86,7 +86,7 @@ # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_07_MicBias.py b/examples/wm8960_07_MicBias.py index c6110eb..11f33ba 100644 --- a/examples/wm8960_07_MicBias.py +++ b/examples/wm8960_07_MicBias.py @@ -39,15 +39,15 @@ codec.enableMicBias() -# WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or -# WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) -codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD) +# MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or +# MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) +codec.setMicBiasVoltage(adafruit_wm8960.MIC_BIAS_VOLTAGE_0_9_AVDD) print("Mic Bias enabled (0.9*AVDD)") time.sleep(3.0) -# WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or -# WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) -codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD) +# MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or +# MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) +codec.setMicBiasVoltage(adafruit_wm8960.MIC_BIAS_VOLTAGE_0_65_AVDD) print("Mic Bias enabled (0.65*AVDD)") time.sleep(3.0) diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py index ddbc115..18a796b 100644 --- a/examples/wm8960_08_I2S_Passthrough.py +++ b/examples/wm8960_08_I2S_Passthrough.py @@ -72,7 +72,7 @@ codec.setINVOLDB(0.0) # Valid options are -17.25dB to +30dB (0.75dB steps) # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) # Connect from MIC inputs (aka pga output) to boost mixers codec.connectMIC2B() @@ -89,13 +89,13 @@ # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers codec.enableOMIX() # Set sample rate, word length, and mode -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) # Enable ADCs and DACs codec.enableAdc() diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py index 7874fdb..804132f 100644 --- a/examples/wm8960_09_I2S_Bluetooth.py +++ b/examples/wm8960_09_I2S_Bluetooth.py @@ -64,13 +64,13 @@ # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers codec.enableOMIX() # Setup sample rate, word length, and I2S mode -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) # Enable DACs codec.enableDac() diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py index 417842e..73d4a96 100644 --- a/examples/wm8960_10_AdcGain.py +++ b/examples/wm8960_10_AdcGain.py @@ -89,7 +89,7 @@ codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() # Enable boost mixers @@ -104,7 +104,7 @@ # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_11_VolumePlotter.py b/examples/wm8960_11_VolumePlotter.py index 933b7cb..fcaa46b 100644 --- a/examples/wm8960_11_VolumePlotter.py +++ b/examples/wm8960_11_VolumePlotter.py @@ -70,7 +70,7 @@ codec.setINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) # Connect from MIC inputs (aka pga output) to boost mixers codec.connectMIC2B() @@ -85,13 +85,13 @@ codec.disableD2O() # Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) # Enable output mixers codec.enableOMIX() # Configure I2S -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) # Enable ADCs, and disable DACs codec.enableAdc() diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_12_AutomaticLevelControl.py index 730f9dd..1dca92e 100644 --- a/examples/wm8960_12_AutomaticLevelControl.py +++ b/examples/wm8960_12_AutomaticLevelControl.py @@ -77,7 +77,7 @@ codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() # Enable boost mixers @@ -92,7 +92,7 @@ # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers codec.enableOMIX() @@ -121,13 +121,13 @@ # Minimizes "zipper" noise when chaning gains. codec.enablePgaZeroCross() -codec.enableAlc(adafruit_wm8960.WM8960_ALC_MODE_STEREO) -codec.setAlcTarget(adafruit_wm8960.WM8960_ALC_TARGET_LEVEL_NEG_6DB) -codec.setAlcDecay(adafruit_wm8960.WM8960_ALC_DECAY_TIME_192MS) -codec.setAlcAttack(adafruit_wm8960.WM8960_ALC_ATTACK_TIME_24MS) -codec.setAlcMaxGain(adafruit_wm8960.WM8960_ALC_MAX_GAIN_LEVEL_30DB) -codec.setAlcMinGain(adafruit_wm8960.WM8960_ALC_MIN_GAIN_LEVEL_NEG_17_25DB) -codec.setAlcHold(adafruit_wm8960.WM8960_ALC_HOLD_TIME_0MS) +codec.enableAlc(adafruit_wm8960.ALC_MODE_STEREO) +codec.setAlcTarget(adafruit_wm8960.ALC_TARGET_LEVEL_NEG_6DB) +codec.setAlcDecay(adafruit_wm8960.ALC_DECAY_TIME_192MS) +codec.setAlcAttack(adafruit_wm8960.ALC_ATTACK_TIME_24MS) +codec.setAlcMaxGain(adafruit_wm8960.ALC_MAX_GAIN_LEVEL_30DB) +codec.setAlcMinGain(adafruit_wm8960.ALC_MIN_GAIN_LEVEL_NEG_17_25DB) +codec.setAlcHold(adafruit_wm8960.ALC_HOLD_TIME_0MS) print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py index 0785a0d..afb827c 100644 --- a/examples/wm8960_13_DacGain.py +++ b/examples/wm8960_13_DacGain.py @@ -89,7 +89,7 @@ codec.disableINMUTE() # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) codec.connectMIC2B() # Enable boost mixers @@ -104,7 +104,7 @@ # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_NEG_21DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_14_ElectretMics.py b/examples/wm8960_14_ElectretMics.py index d283b4c..734f1f7 100644 --- a/examples/wm8960_14_ElectretMics.py +++ b/examples/wm8960_14_ElectretMics.py @@ -69,9 +69,9 @@ codec.enableMicBias() -# WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or -# WM8960_MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) -codec.setMicBiasVoltage(adafruit_wm8960.WM8960_MIC_BIAS_VOLTAGE_0_9_AVDD) +# MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or +# MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) +codec.setMicBiasVoltage(adafruit_wm8960.MIC_BIAS_VOLTAGE_0_9_AVDD) print("Mic Bias enabled (0.9*AVDD)") # Setup signal flow through the analog audio bypass connections @@ -88,11 +88,11 @@ print("PGA gain set to +24dB") # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_0DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) print("Mic boost stage set to 0dB") # For MIC+ signal of differential mic signal -codec.pgaNonInvSignalSelect(adafruit_wm8960.WM8960_PGA_INPUT2) +codec.pgaNonInvSignalSelect(adafruit_wm8960.PGA_INPUT2) print("Pga non-inverting inputs set to INPUT2s") codec.connectMIC2B() @@ -104,7 +104,7 @@ codec.enableB2O() # Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) # Enable output mixers codec.enableOMIX() diff --git a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py index c142a46..9a27a2b 100644 --- a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py +++ b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py @@ -77,10 +77,10 @@ codec.setINVOLDB(8.25) # Valid options are -17.25dB to +30dB (0.75dB steps) # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.WM8960_MIC_BOOST_GAIN_29DB) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_29DB) # For MIC+ signal of differential mic signal -codec.pgaNonInvSignalSelect(adafruit_wm8960.WM8960_PGA_INPUT2) +codec.pgaNonInvSignalSelect(adafruit_wm8960.PGA_INPUT2) # Connect from MIC inputs (aka pga output) to boost mixers codec.connectMIC2B() @@ -95,13 +95,13 @@ codec.disableD2O() # Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.WM8960_OUTPUT_MIXER_GAIN_0DB) +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) # Enable output mixers codec.enableOMIX() # Set sample rate, word length, and mode -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WM8960_WL_16BIT, master=False) +codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) # Enable ADCs, and disable DACs codec.enableAdc() diff --git a/examples/wm8960_I2SInOut.py b/examples/wm8960_I2SInOut.py new file mode 100644 index 0000000..508f868 --- /dev/null +++ b/examples/wm8960_I2SInOut.py @@ -0,0 +1,178 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: Unlicense + +''' +Demonstrates I2C Input and Output on WM8960 Codec. + +It will output the sound on the headphone outputs. +It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. + +HARDWARE CONNECTIONS + +********************** +MCU --------- CODEC +********************** +QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL +GND --------- GND *optional, but not a bad idea +5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) +AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. +AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. + +********************** +CODEC ------- AUDIO IN +********************** +GND --------- TRS INPUT SLEEVE *ground for line level input +LINPUT1 ----- TRS INPUT TIP *left audio +RINPUT1 ----- TRS INPUT RING1 *right audio + +********************** +CODEC ------- AUDIO OUT +********************** +OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") +HPL --------- TRS OUTPUT TIP *left HP output +HPR --------- TRS OUTPUT RING1 *right HP output + +You can now control the volume of the codecs built in headphone buffers using this function: +codec.setHeadphoneVolumeDB(6.00) +Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). + +For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: +https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf +''' + +import board +import adafruit_wm8960 +import array +import rp2pio +import adafruit_pioasm + +CHANNELS = 2 +BITS = 16 +SAMPLE_RATE = 48000 +BUFFER_SIZE = 2048 +BUFFER_TYPE = "L" +BUFFER_WIDTH = 32 + +import busio +codec = adafruit_wm8960.WM8960(busio.I2C(board.GP17, board.GP16)) + +# Setup INPUT1 +codec.enableMIC() +codec.connectMN1() +codec.disableINMUTE() +codec.setINVOLDB(0.0) +codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) +codec.connectMIC2B() +codec.enableAIN() +codec.disableB2O() +codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) + +# Setup DAC +codec.enableD2O() +codec.enableOMIX() +codec.disableLoopBack() + +# Set up codec as peripheral device with clock configured for desired sample rate (and 16-bit words) +codec.configureI2S(SAMPLE_RATE) + +# Enable ADCs and DACs +codec.enableAdc() +codec.enableDac() +codec.disableDacMute() + +# Enable headphone output with OUT3 as capless buffer for headphone ground, adjust volume with dB +codec.configureHeadphones(dB=0.0, capless=True) + +def i2s_codec( + channels=2, + sample_rate=48000, + bits=16, + bclk_pin=None, + out_pin=None, + in_pin=None, +): + i2s_clock = sample_rate * channels * bits + pio_clock = 4 * i2s_clock + pio_code = """ + .program i2s_codec + .side_set 2 + ; at program start we initialize the bit count top + ; (which may be >32) with data + ; pulled from the input fifo + pull noblock ; first empty the input fifo + pull noblock + pull noblock + pull noblock + out null, 32 ; then clear OSR so we can get a new value + pull block ; then get the bit count top value from the fifo + ; /--- LRCLK + ; |/-- BCLK + ; || + mov x, osr; side 0b01 [1] ; save it in x + out null, 32 side 0b00 [1] + mov y, x side 0b01 [1] ; start of main loop (wrap target=8) + bitloop1: + out pins 1 side 0b00 + in pins 1 side 0b00 + jmp y-- bitloop1 side 0b01 [1] + out pins 1 side 0b10 + in pins 1 side 0b10 + mov y, x side 0b11 [1] + bitloop0: + out pins 1 side 0b10 + in pins 1 side 0b10 + jmp y-- bitloop0 side 0b11 [1] + out pins 1 side 0b00 + in pins 1 side 0b00 + """ + pio_params = { + "frequency": pio_clock, + "first_out_pin": out_pin, + "first_in_pin": in_pin, + "first_sideset_pin": bclk_pin, + "sideset_pin_count": 2, + "auto_pull": True, + "auto_push": True, + "out_shift_right": False, + "in_shift_right": False, + "pull_threshold": bits, + "push_threshold": bits, + "wait_for_txstall": False, + "wrap_target": 8, + } + pio_instructions = adafruit_pioasm.assemble(pio_code) + i2s_clock = sample_rate * channels * bits + pio_clock = 4 * i2s_clock + pio = rp2pio.StateMachine(pio_instructions, **pio_params) + return pio + +def spaced_samples(length, bits): + max_int = (1 << bits) - 1 + if length == 1: + return [0] + step = max_int / (length - 1) + result = [round(i * step) for i in range(length)] + result[0] = 0 + result[-1] = max_int + return result + +# initialize pio bit count top value by sending it at the start of output data +bit_count_top = BITS * (CHANNELS // 2) - 2 + +PIO = i2s_codec( + channels=CHANNELS, + bits=BITS, + sample_rate=SAMPLE_RATE, + out_pin=board.GP20, + in_pin=board.GP21, + bclk_pin=board.GP18, # L/R signal will be one pin higher, i.e. GP19 +) + +buffer_in = array.array(BUFFER_TYPE, [0] * BUFFER_SIZE) +while True: + buffer_out = array.array(BUFFER_TYPE, [bit_count_top] + [d << (BUFFER_WIDTH - BITS) for d in buffer_in]) + PIO.write_readinto(buffer_out, buffer_in) + PIO.clear_rxfifo() + del buffer_out From ef9d64f14e5f23deef86a6da19e0b1e0146066c5 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 16 Aug 2024 12:49:27 -0500 Subject: [PATCH 23/56] Revert code of conduct to original version. --- CODE_OF_CONDUCT.md | 70 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ddf57b0..01a7515 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,10 +1,10 @@ -# CircuitPython Community Code of Conduct +# Adafruit Community Code of Conduct ## Our Pledge @@ -20,52 +20,35 @@ race, religion, or sexual identity and orientation. We are committed to providing a friendly, safe and welcoming environment for all. -Examples of behavior that contributes to creating and maintaining a positive environment +Examples of behavior that contributes to creating a positive environment include: * Be kind and courteous to others * Using welcoming and inclusive language -* Respecting the identity of every community member, including asking for their - pronouns if uncertain * Being respectful of differing viewpoints and experiences * Collaborating with other community members -* Providing desired assistance and knowledge to other community members -* Being open to new information and ideas * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members -Examples of unacceptable behavior by community members include: +Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and sexual attention or advances * The use of inappropriate images, including in a community member's avatar -* The use of inappropriate language or profanity, including in a community member's nickname +* The use of inappropriate language, including in a community member's nickname * Any spamming, flaming, baiting or other attention-stealing behavior * Excessive or unwelcome helping; answering outside the scope of the question asked * Discussion or promotion of activities or projects that intend or pose a risk of significant harm -* Trolling, insulting/derogatory comments, and attacks of any nature (including, - but not limited to, personal or political attacks) +* Trolling, insulting/derogatory comments, and personal or political attacks * Promoting or spreading disinformation, lies, or conspiracy theories against a person, group, organisation, project, or community * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission -* Engaging in behavior that creates an unwelcoming or uninclusive environment * Other conduct which could reasonably be considered inappropriate -The CircuitPython Community welcomes everyone and strives to create a safe space for all. It is built -around sharing and contributing to technology. We encourage discussing your thoughts, experiences, -and feelings within the scope of the community. However, there are topics that can sometimes stray -from that scope, and can lead to hurting others and create an unwelcoming, uninclusive environment. - -Examples of discussion topics that have been known to stray outside the scope of the CircuitPython -Community include, but are not limited to: - -* Discussions regarding religion and related topics -* Discussions regarding politics and related topics - The goal of the standards and moderation guidelines outlined here is to build and maintain a respectful community. We ask that you don’t just aim to be "technically unimpeachable", but rather try to be your best self. @@ -89,21 +72,25 @@ inappropriate, threatening, offensive, or harmful. ## Moderation -Instances of behaviors that violate the CircuitPython Community Code of Conduct +Instances of behaviors that violate the Adafruit Community Code of Conduct may be reported by any member of the community. Community members are encouraged to report these situations, including situations they witness involving other community members. You may report in the following ways: -In any situation, you may email the project maintainer. +In any situation, you may email . + +On the Adafruit Discord, you may send an open message from any channel +to all Community Moderators by tagging @community moderators. You may +also send an open message from any channel, or a direct message to +any Community Moderator. -The source of email and direct message reports will be kept confidential. +Email and direct message reports will be kept confidential. -In situations on GitHub where the issue is particularly offensive, possibly -illegal, requires immediate action, or violates the GitHub terms of service, -you should also report the message directly to GitHub via the comment, or via -[GitHub Support](https://support.github.com/contact/report-abuse?category=report-abuse&report=other&report_type=unspecified). +In situations on Discord where the issue is particularly offensive, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to [Discord](https://discord.com/safety). These are the steps for upholding our community’s standards of conduct. @@ -122,18 +109,19 @@ These are the steps for upholding our community’s standards of conduct. banned. 6. Disciplinary actions (warnings, bans, etc) for Code of Conduct violations apply to the platform where the violation occurred. However, depending on the severity - of the violation, the disciplinary action may be applied across CircuitPython's - other community platforms. For example, a severe violation in one Community forum - may result in a ban on not only the CircuitPython GitHub organisation, - but also on the CircuitPython Twitter, live stream text chats, etc. + of the violation, the disciplinary action may be applied across Adafruit's other + community platforms. For example, a severe violation on the Adafruit Discord + server may result in a ban on not only the Adafruit Discord server, but also on + the Adafruit GitHub organisation, Adafruit Forums, Adafruit Twitter, etc. ## Scope This Code of Conduct and the enforcement policies listed above apply to all -CircuitPython Community venues. This includes but is not limited to any community -spaces (both public and private), and CircuitPython GitHub repositories. Examples of -CircuitPython Community spaces include but are not limited to meet-ups, issue -threads on GitHub, text chats during a live stream, or interaction at a conference. +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. As a community @@ -142,13 +130,11 @@ accordingly. ## Attribution -This Code of Conduct is adapted from the -[Adafruit Community Code of Conduct](https://github.com/adafruit/Adafruit_Community_Code_of_Conduct), -which is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available on [contributor-covenant.org](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html), and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). -For other projects adopting the CircuitPython Community Code of +For other projects adopting the Adafruit Community Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your From e30892ea3797e32911ad559e1522834004ede7ae Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 16 Aug 2024 15:18:13 -0500 Subject: [PATCH 24/56] API updated to use property-driven standards. --- adafruit_wm8960.py | 2579 +++++++++++++++++++------------------------- requirements.txt | 2 +- 2 files changed, 1139 insertions(+), 1442 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index e4b0441..a0f8f37 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -25,12 +25,20 @@ # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960.git" + from busio import I2C from adafruit_bus_device.i2c_device import I2CDevice +from adafruit_simplemath import constrain, map_range +import math from micropython import const -__version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960.git" +try: + from typing import Optional, Type, Protocol + from circuitpython_typing.device_drivers import I2CDeviceDriver +except ImportError: + pass # I2C address _DEFAULT_I2C_ADDR = const(0x1A) @@ -65,8 +73,8 @@ _REG_ANTI_POP_2 = const(0x1D) _REG_ADCL_SIGNAL_PATH = const(0x20) _REG_ADCR_SIGNAL_PATH = const(0x21) -_REG_LEFT_OUT_MIX_1 = const(0x22) -_REG_RIGHT_OUT_MIX_2 = const(0x25) +_REG_LEFT_OUT_MIX = const(0x22) +_REG_RIGHT_OUT_MIX = const(0x25) _REG_MONO_OUT_MIX_1 = const(0x26) _REG_MONO_OUT_MIX_2 = const(0x27) _REG_LOUT2_VOLUME = const(0x28) @@ -86,12 +94,6 @@ _REG_PLL_K_3 = const(0x37) # PGA input selections -PGAL_LINPUT2 = 0 -PGAL_LINPUT3 = 1 -PGAL_VMID = 2 -PGAR_RINPUT2 = 0 -PGAR_RINPUT3 = 1 -PGAR_VMID = 2 PGA_INPUT2 = 0 PGA_INPUT3 = 1 PGA_VMID = 2 @@ -119,175 +121,53 @@ BOOST_MIXER_GAIN_3DB = 6 BOOST_MIXER_GAIN_6DB = 7 -''' -Output Mixer gain options -These are used to control the gain (aka volume) at the following settings: -LI2LOVOL -LB2LOVOL -RI2LOVOL -RB2LOVOL -These are useful as analog bypass signal path options. -''' -OUTPUT_MIXER_GAIN_0DB = 0 -OUTPUT_MIXER_GAIN_NEG_3DB = 1 -OUTPUT_MIXER_GAIN_NEG_6DB = 2 -OUTPUT_MIXER_GAIN_NEG_9DB = 3 -OUTPUT_MIXER_GAIN_NEG_12DB = 4 -OUTPUT_MIXER_GAIN_NEG_15DB = 5 -OUTPUT_MIXER_GAIN_NEG_18DB = 6 -OUTPUT_MIXER_GAIN_NEG_21DB = 7 - # Mic Bias voltage options MIC_BIAS_VOLTAGE_0_9_AVDD = 0 MIC_BIAS_VOLTAGE_0_65_AVDD = 1 # SYSCLK divide -SYSCLK_DIV_BY_1 = 0 -SYSCLK_DIV_BY_2 = 2 -CLKSEL_MCLK = 0 -CLKSEL_PLL = 1 -PLL_MODE_INTEGER = 0 -PLL_MODE_FRACTIONAL = 1 -PLLPRESCALE_DIV_1 = 0 -PLLPRESCALE_DIV_2 = 1 - -# Class d clock divide -DCLKDIV_16 = 7 - -# Word length settings (aka bits per sample) -# Audio Data Word Length -WL_16BIT = 0 -WL_20BIT = 1 -WL_24BIT = 2 -WL_32BIT = 3 +_SYSCLK_DIV_BY_1 = const(0) +_SYSCLK_DIV_BY_2 = const(2) -''' -Additional Digital Audio Interface controls -LRP (aka left-right-polarity) -Right, left and I2S modes – LRCLK polarity -0 = normal LRCLK polarity -1 = inverted LRCLK polarity -''' -LR_POLARITY_NORMAL = 0 -LR_POLARITY_INVERT = 1 +# Gain/Level mins, maxes, offsets and step-sizes +_PGA_GAIN_MIN = -17.25 +_PGA_GAIN_MAX = 30.00 -''' -ALRSWAP (aka ADC left/right swap) -Left/Right ADC channel swap -1 = Swap left and right ADC data in audio interface -0 = Output left and right data as normal -''' -ALRSWAP_NORMAL = 0 -ALRSWAP_SWAP = 1 - -# Gain mins, maxes, offsets and step-sizes for all the amps within the codec. -PGA_GAIN_MIN = -17.25 -PGA_GAIN_MAX = 30.00 -PGA_GAIN_OFFSET = 17.25 -PGA_GAIN_STEPSIZE = 0.75 -HP_GAIN_MIN = -73.00 -HP_GAIN_MAX = 6.00 -HP_GAIN_OFFSET = 121.00 -HP_GAIN_STEPSIZE = 1.00 -SPEAKER_GAIN_MIN = -73.00 -SPEAKER_GAIN_MAX = 6.00 -SPEAKER_GAIN_OFFSET = 121.00 -SPEAKER_GAIN_STEPSIZE = 1.00 -ADC_GAIN_MIN = -97.00 -ADC_GAIN_MAX = 30.00 -ADC_GAIN_OFFSET = 97.50 -ADC_GAIN_STEPSIZE = 0.50 -DAC_GAIN_MIN = -97.00 -DAC_GAIN_MAX = 30.00 -DAC_GAIN_OFFSET = 97.50 -DAC_GAIN_STEPSIZE = 0.50 - -# Automatic Level Control Modes -ALC_MODE_OFF = 0 -ALC_MODE_RIGHT_ONLY = 1 -ALC_MODE_LEFT_ONLY = 2 -ALC_MODE_STEREO = 3 - -# Automatic Level Control Target Level dB -ALC_TARGET_LEVEL_NEG_22_5DB = 0 -ALC_TARGET_LEVEL_NEG_21DB = 1 -ALC_TARGET_LEVEL_NEG_19_5DB = 2 -ALC_TARGET_LEVEL_NEG_18DB = 3 -ALC_TARGET_LEVEL_NEG_16_5DB = 4 -ALC_TARGET_LEVEL_NEG_15DB = 5 -ALC_TARGET_LEVEL_NEG_13_5DB = 6 -ALC_TARGET_LEVEL_NEG_12DB = 7 -ALC_TARGET_LEVEL_NEG_10_5DB = 8 -ALC_TARGET_LEVEL_NEG_9DB = 9 -ALC_TARGET_LEVEL_NEG_7_5DB = 10 -ALC_TARGET_LEVEL_NEG_6DB = 11 -ALC_TARGET_LEVEL_NEG_4_5DB = 12 -ALC_TARGET_LEVEL_NEG_3DB = 13 -ALC_TARGET_LEVEL_NEG_1_5DB = 14 - -# Automatic Level Control Max Gain Level dB -ALC_MAX_GAIN_LEVEL_NEG_12DB = 0 -ALC_MAX_GAIN_LEVEL_NEG_6DB = 1 -ALC_MAX_GAIN_LEVEL_0DB = 2 -ALC_MAX_GAIN_LEVEL_6DB = 3 -ALC_MAX_GAIN_LEVEL_12DB = 4 -ALC_MAX_GAIN_LEVEL_18DB = 5 -ALC_MAX_GAIN_LEVEL_24DB = 6 -ALC_MAX_GAIN_LEVEL_30DB = 7 - -# Automatic Level Control Min Gain Level dB -ALC_MIN_GAIN_LEVEL_NEG_17_25DB = 0 -ALC_MIN_GAIN_LEVEL_NEG_11_25DB = 1 -ALC_MIN_GAIN_LEVEL_NEG_5_25DB = 2 -ALC_MIN_GAIN_LEVEL_0_75DB = 3 -ALC_MIN_GAIN_LEVEL_6_75DB = 4 -ALC_MIN_GAIN_LEVEL_12_75DB = 5 -ALC_MIN_GAIN_LEVEL_18_75DB = 6 -ALC_MIN_GAIN_LEVEL_24_75DB = 7 - -# Automatic Level Control Hold Time (MS and SEC) -ALC_HOLD_TIME_0MS = 0 -ALC_HOLD_TIME_3MS = 1 -ALC_HOLD_TIME_5MS = 2 -ALC_HOLD_TIME_11MS = 3 -ALC_HOLD_TIME_21MS = 4 -ALC_HOLD_TIME_43MS = 5 -ALC_HOLD_TIME_85MS = 6 -ALC_HOLD_TIME_170MS = 7 -ALC_HOLD_TIME_341MS = 8 -ALC_HOLD_TIME_682MS = 9 -ALC_HOLD_TIME_1365MS = 10 -ALC_HOLD_TIME_3SEC = 11 -ALC_HOLD_TIME_5SEC = 12 -ALC_HOLD_TIME_10SEC = 13 -ALC_HOLD_TIME_23SEC = 14 -ALC_HOLD_TIME_44SEC = 15 - -# Automatic Level Control Decay Time (MS and SEC) -ALC_DECAY_TIME_24MS = 0 -ALC_DECAY_TIME_48MS = 1 -ALC_DECAY_TIME_96MS = 2 -ALC_DECAY_TIME_192MS = 3 -ALC_DECAY_TIME_384MS = 4 -ALC_DECAY_TIME_768MS = 5 -ALC_DECAY_TIME_1536MS = 6 -ALC_DECAY_TIME_3SEC = 7 -ALC_DECAY_TIME_6SEC = 8 -ALC_DECAY_TIME_12SEC = 9 -ALC_DECAY_TIME_24SEC = 10 - -# Automatic Level Control Attack Time (MS and SEC) -ALC_ATTACK_TIME_6MS = 0 -ALC_ATTACK_TIME_12MS = 1 -ALC_ATTACK_TIME_24MS = 2 -ALC_ATTACK_TIME_482MS = 3 -ALC_ATTACK_TIME_964MS = 4 -ALC_ATTACK_TIME_1928MS = 5 -ALC_ATTACK_TIME_3846MS = 6 -ALC_ATTACK_TIME_768MS = 7 -ALC_ATTACK_TIME_1536MS = 8 -ALC_ATTACK_TIME_3SEC = 9 -ALC_ATTACK_TIME_6SEC = 10 +_ADC_VOLUME_MIN = -97.00 +_ADC_VOLUME_MAX = 30.00 + +_DAC_VOLUME_MIN = -127.00 +_DAC_VOLUME_MAX = 0.00 + +_ALC_TARGET_MIN = -22.50 +_ALC_TARGET_MAX = -1.50 + +_ALC_MAX_GAIN_MIN = -12.00 +_ALC_MAX_GAIN_MAX = 30.00 + +_ALC_MIN_GAIN_MIN = -17.25 +_ALC_MIN_GAIN_MAX = 24.75 + +_GATE_THRESHOLD_MIN = -76.50 +_GATE_THRESHOLD_MAX = -30.00 + +_OUTPUT_VOLUME_MIN = -21.00 +_OUTPUT_VOLUME_MAX = 0.00 + +_AMP_VOLUME_MIN = -73.00 +_AMP_VOLUME_MAX = 6.00 + +# ALC Time mapping +_ALC_ATTACK_MAX = const(10) +_ALC_ATTACK_TIME_MIN = 0.006 +_ALC_ATTACK_TIME_MAX = 6.140 + +_ALC_DECAY_MAX = const(10) +_ALC_DECAY_TIME_MIN = 0.024 +_ALC_DECAY_TIME_MAX = 24.580 + +_ALC_HOLD_TIME_MIN = 0.00267 +_ALC_HOLD_TIME_MAX = 43.691 # Speaker Boost Gains (DC and AC) SPEAKER_BOOST_GAIN_0DB = 0 @@ -299,1311 +179,1128 @@ # VMIDSEL settings VMIDSEL_DISABLED = 0 -VMIDSEL_2X50KOHM = 1 -VMIDSEL_2X250KOHM = 2 -VMIDSEL_2X5KOHM = 3 - -''' -VREF to Analogue Output Resistance -(Disabled Outputs) -0 = 500 VMID to output -1 = 20k VMID to output -''' -VROI_500 = 0 -VROI_20K = 1 +VMIDSEL_PLAYBACK = 1 # 2X50KOHM +VMIDSEL_LOWPOWER = 2 # 2X250KOHM +VMIDSEL_FASTSTART = 3 # 2X5KOHM + +# Clock Divider Tables +_BCLKDIV = [ + 1.0, + 1.5, + 2.0, + 3.0, + 4.0, + 5.5, + 6.0, + 8.0, + 11.0, + 12.0, + 16.0, + 22.0, + 24.0, + 32.0 +] -''' -Analogue Bias Optimisation -00 = Reserved -01 = Increased bias current optimized for -AVDD=2.7V -1X = Lowest bias current, optimized for -AVDD=3.3V -''' +_DCLKDIV = [ + 1.5, + 2.0, + 3.0, + 4.0, + 6.0, + 8.0, + 12.0, + 16.0 +] -VSEL_INCREASED_BIAS_CURRENT = 1 -VSEL_LOWEST_BIAS_CURRENT = 3 - -_REGISTER_DEFAULTS = [ - 0x0097, # R0 (0x00) - 0x0097, # R1 (0x01) - 0x0000, # R2 (0x02) - 0x0000, # R3 (0x03) - 0x0000, # R4 (0x04) - 0x0008, # F5 (0x05) - 0x0000, # R6 (0x06) - 0x000A, # R7 (0x07) - 0x01C0, # R8 (0x08) - 0x0000, # R9 (0x09) - 0x00FF, # R10 (0x0a) - 0x00FF, # R11 (0x0b) - 0x0000, # R12 (0x0C) RESERVED - 0x0000, # R13 (0x0D) RESERVED - 0x0000, # R14 (0x0E) RESERVED - 0x0000, # R15 (0x0F) RESERVED - 0x0000, # R16 (0x10) - 0x007B, # R17 (0x11) - 0x0100, # R18 (0x12) - 0x0032, # R19 (0x13) - 0x0000, # R20 (0x14) - 0x00C3, # R21 (0x15) - 0x00C3, # R22 (0x16) - 0x01C0, # R23 (0x17) - 0x0000, # R24 (0x18) - 0x0000, # R25 (0x19) - 0x0000, # R26 (0x1A) - 0x0000, # R27 (0x1B) - 0x0000, # R28 (0x1C) - 0x0000, # R29 (0x1D) - 0x0000, # R30 (0x1E) RESERVED - 0x0000, # R31 (0x1F) RESERVED - 0x0100, # R32 (0x20) - 0x0100, # R33 (0x21) - 0x0050, # R34 (0x22) - 0x0000, # R35 (0x23) RESERVED - 0x0000, # R36 (0x24) RESERVED - 0x0050, # R37 (0x25) - 0x0000, # R38 (0x26) - 0x0000, # R39 (0x27) - 0x0000, # R40 (0x28) - 0x0000, # R41 (0x29) - 0x0040, # R42 (0x2A) - 0x0000, # R43 (0x2B) - 0x0000, # R44 (0x2C) - 0x0050, # R45 (0x2D) - 0x0050, # R46 (0x2E) - 0x0000, # R47 (0x2F) - 0x0002, # R48 (0x30) - 0x0037, # R49 (0x31) - 0x0000, # R50 (0x32) RESERVED - 0x0080, # R51 (0x33) - 0x0008, # R52 (0x34) - 0x0031, # R53 (0x35) - 0x0026, # R54 (0x36) - 0x00e9, # R55 (0x37) +_ADCDACDIV = [ + 1.0, + 1.5, + 2.0, + 3.0, + 4.0, + 5.5, + 6.0 ] +class WMBit: + + def __init__( + self, + register_address: int, + bit: int, + default: bool, + ) -> None: + self.register_address = register_address + self.bit = bit + self.bit_mask = 1 << (bit % 8) # the bitmask *within* the byte! + self.default = default + self.byte = 1 - (bit // 8) # the byte number within the buffer + + def _set(self, obj:Optional[I2CDeviceDriver], value:bool) -> None: + if value: + obj._registers[self.register_address][self.byte] |= self.bit_mask + else: + obj._registers[self.register_address][self.byte] &= ~self.bit_mask + + def reset(self, obj: Optional[I2CDeviceDriver]) -> None: + self._set(obj, self.default) + + def __get__( + self, + obj: Optional[I2CDeviceDriver], + objtype: Optional[Type[I2CDeviceDriver]] = None, + ) -> bool: + return bool(obj._registers[self.register_address][self.byte] & self.bit_mask) + + def __set__(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: + self._set(obj, value) + with obj.i2c_device as i2c: + i2c.write(obj._registers[self.register_address]) + +class WMBits: + def __init__( # pylint: disable=too-many-arguments + self, + num_bits: int, + register_address: int, + lowest_bit: int, + default: int, + ) -> None: + self.register_width = 1 + self.register_address = register_address + self.bit_mask = ((1 << num_bits) - 1) << lowest_bit + self.lowest_bit = lowest_bit + self.default = default << self.lowest_bit + + def _set(self, obj:Optional[I2CDeviceDriver], value:int) -> None: + value <<= self.lowest_bit # shift the value over to the right spot + reg = 0 + for i in range(2): + reg = (reg << 8) | obj._registers[self.register_address][i] + reg &= ~self.bit_mask # mask off the bits we're about to change + reg |= value # then or in our new value + for i in range(1, -1, -1): + obj._registers[self.register_address][i] = reg & 0xFF + reg >>= 8 + + # Must call first before using object + def reset(self, obj:Optional[I2CDeviceDriver]) -> None: + self._set(obj, self.default) + + def __get__( + self, + obj: Optional[I2CDeviceDriver], + objtype: Optional[Type[I2CDeviceDriver]] = None, + ) -> int: + # read the number of bytes into a single variable + reg = 0 + for i in range(2): + reg = (reg << 8) | obj._registers[self.register_address][i] + reg = (reg & self.bit_mask) >> self.lowest_bit + return reg + + def __set__(self, obj: Optional[I2CDeviceDriver], value: int) -> None: + self._set(obj, value) + with obj.i2c_device as i2c: + i2c.write(obj._registers[self.register_address]) + class WM8960: - def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR): - self.i2c_device = I2CDevice(i2c_bus, address) - self._buf = bytearray(2) - - ''' - The WM8960 does not support I2C reads - This means we must keep a local copy of all the register values - We will instantiate with default values by copying from _REGISTER_DEFAULTS during reset() - As we write to the device, we will also make sure to update our local copy as well, stored here in this array. - Each register is 9-bits - They are in order from R0-R55, and we even keep blank spots for the "reserved" registers. This way we can use the register address macro defines above to easiy access each local copy of each register. - Example: self._registerLocalCopy[_REG_LEFT_INPUT_VOLUME] - ''' - self._registerLocalCopy = [0x0000 for i in range(len(_REGISTER_DEFAULTS))] - self.reset() - # General setup - self.enableVREF() - self.enableVMID() + # Power + + vref = WMBit(_REG_PWR_MGMT_1, 6, False) + analogInputLeftEnabled = WMBit(_REG_PWR_MGMT_1, 5, False) + analogInputRightEnabled = WMBit(_REG_PWR_MGMT_1, 4, False) + + @property + def analogInputEnabled(self) -> bool: + return self.analogInputLeftEnabled and self.analogInputRightEnabled + @analogInputEnabled.setter + def analogInputEnabled(self, value:bool) -> None: + self.analogInputLeftEnabled = self.analogInputRightEnabled = value + + # PGA - def _getDivBit(self, src:int, dest:int) -> int: - div = (src * 2) // dest - if div < 2 or div > 12: return 0 - if div == 2: div = 0 - elif div == 3: div = 2 - elif div == 10: return 0 - elif div == 11: div = 10 - if div % 2 == 1: return 0 - return div // 2 + ## PWR_MGMT + + pgaLeftEnabled = WMBit(_REG_PWR_MGMT_3, 5, False) + pgaRightEnabled = WMBit(_REG_PWR_MGMT_3, 4, False) + + @property + def pgaEnabled(self) -> bool: + return self.pgaLeftEnabled and self.pgaRightEnabled + @pgaEnabled.setter + def pgaEnabled(self, value:bool) -> None: + self.pgaLeftEnabled = self.pgaRightEnabled = value + + ## SIGNAL_PATH + + _pgaInput1Left = WMBit(_REG_ADCL_SIGNAL_PATH, 8, True) + _pgaInput2Left = WMBit(_REG_ADCL_SIGNAL_PATH, 6, False) + _pgaInput3Left = WMBit(_REG_ADCL_SIGNAL_PATH, 7, False) + + _pgaInput1Right = WMBit(_REG_ADCR_SIGNAL_PATH, 8, True) + _pgaInput2Right = WMBit(_REG_ADCR_SIGNAL_PATH, 6, False) + _pgaInput3Right = WMBit(_REG_ADCR_SIGNAL_PATH, 7, False) + + @property + def pgaNonInvSignalLeft(self) -> int: + if self._pgaInput2Left: + return PGA_INPUT2 + elif self._pgaInput3Left: + return PGA_INPUT3 + else: + return PGA_VMID + @pgaNonInvSignalLeft.setter + def pgaNonInvSignalLeft(self, signal:int) -> None: + self._pgaInput2Left = signal == PGA_INPUT2 + self._pgaInput3Left = signal == PGA_INPUT3 - def configureI2S(self, sample_rate:int, word_length:int = WL_16BIT, master:bool = False) -> bool: - if not self.setSampleRate(sample_rate): return False - if not self.setWL(word_length): return False - if master: - if not self.enableMasterMode(): return False - if not self.setALRCGPIO(): return False # Note, should not be changed while ADC is enabled. + @property + def pgaNonInvSignalRight(self) -> int: + if self._pgaInput2Right: + return PGA_INPUT2 + elif self._pgaInput3Right: + return PGA_INPUT3 else: - if not self.enablePeripheralMode(): return False - return True + return PGA_VMID + @pgaNonInvSignalRight.setter + def pgaNonInvSignalRight(self, signal:int) -> None: + self._pgaInput2Right = signal == PGA_INPUT2 + self._pgaInput3Right = signal == PGA_INPUT3 + + @property + def pgaNonInvSignal(self) -> int: + # NOTE: Not checking right signal + return self.pgaNonInvSignalLeft + @pgaNonInvSignal.setter + def pgaNonInvSignal(self, signal:int) -> None: + self.pgaNonInvSignalLeft = self.pgaNonInvSignalRight = signal + + ## Boost + + pgaLeftBoostEnabled = WMBit(_REG_ADCL_SIGNAL_PATH, 3, False) + pgaRightBoostEnabled = WMBit(_REG_ADCR_SIGNAL_PATH, 3, False) + + @property + def pgaBoostEnabled(self) -> None: + return self.pgaLeftBoostEnabled and self.pgaRightBoostEnabled + @pgaBoostEnabled.setter + def pgaBoostEnabled(self, value:int) -> None: + self.pgaLeftBoostEnabled = self.pgaRightBoostEnabled = value + + pgaBoostGainLeft = WMBits(2, _REG_ADCL_SIGNAL_PATH, 4, 0) + pgaBoostGainRight = WMBits(2, _REG_ADCR_SIGNAL_PATH, 4, 0) + + @property + def pgaBoostGain(self) -> None: + return max(self.pgaBoostGainLeft, self.pgaBoostGainRight) + @pgaBoostGain.setter + def pgaBoostGain(self, value:int) -> None: + self.pgaGainLeft = self.pgaGainRight = value + + ## Volume + + _pgaLeftVolume = WMBits(6, _REG_LEFT_INPUT_VOLUME, 0, 0b010111) + _pgaLeftVolumeSet = WMBit(_REG_LEFT_INPUT_VOLUME, 8, False) + + _pgaRightVolume = WMBits(6, _REG_RIGHT_INPUT_VOLUME, 0, 0b010111) + _pgaRightVolumeSet = WMBit(_REG_RIGHT_INPUT_VOLUME, 8, False) + + @property + def pgaLeftVolume(self) -> int: + return self._pgaLeftVolume + @pgaLeftVolume.setter + def pgaLeftVolume(self, value:int) -> None: + self._pgaLeftVolume = value + self._pgaLeftVolumeSet = True + + @property + def pgaLeftVolumeDb(self) -> float: + return map_range(self.pgaLeftVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) + @pgaLeftVolumeDb.setter + def pgaLeftVolumeDb(self, value:float) -> None: + self.pgaLeftVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) - def configureDAC(self, loopback:bool = False) -> bool: - # Connect from DAC outputs to output mixer - if not self.enableD2O(): return False - # Enable output mixers - if not self.enableOMIX(): return False - # Enable DACs - if not self.enableDac(): return False - # Default is "soft mute" on, so we must disable mute to make channels active - if not self.disableDacMute(): return False - - # Loopback sends ADC data directly into DAC - if loopback: - if not self.enableLoopBack(): return False + @property + def pgaRightVolume(self) -> int: + return self._pgaRightVolume + @pgaRightVolume.setter + def pgaRightVolume(self, value:int) -> None: + self._pgaRightVolume = value + self._pgaRightVolumeSet = True + + @property + def pgaRightVolumeDb(self) -> float: + return map_range(self.pgaRightVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) + @pgaRightVolumeDb.setter + def pgaRightVolumeDb(self, value:float) -> None: + self.pgaRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) + + @property + def pgaVolume(self) -> int: + return self.pgaLeftVolume + @pgaVolume.setter + def pgaVolume(self, value:int) -> None: + self.pgaLeftVolume = self.pgaRightVolume = value + + @property + def pgaVolumeDb(self) -> float: + return self.pgaLeftVolumeDb + @pgaVolumeDb.setter + def pgaVolumeDb(self, value:float) -> None: + self.pgaVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) + + ## Zero Cross + + pgaLeftZeroCross = WMBit(_REG_LEFT_INPUT_VOLUME, 6, False) + pgaRightZeroCross = WMBit(_REG_RIGHT_INPUT_VOLUME, 6, False) + + @property + def pgaZeroCross(self) -> bool: + return self.pgaLeftZeroCross and self.pgaRightZeroCross + @pgaZeroCross.setter + def pgaZeroCross(self, value:bool) -> None: + self.pgaLeftZeroCross = self.pgaRightZeroCross = value + + ## Mute + + _pgaLeftMute = WMBit(_REG_LEFT_INPUT_VOLUME, 7, True) + _pgaRightMute = WMBit(_REG_RIGHT_INPUT_VOLUME, 7, True) + + @property + def pgaLeftMute(self) -> bool: + return self._pgaLeftMute + @pgaLeftMute.setter + def pgaLeftMute(self, value:bool) -> None: + self._pgaLeftMute = value + self._pgaLeftVolumeSet = True + + @property + def pgaRightMute(self) -> bool: + return self._pgaRightMute + @pgaRightMute.setter + def pgaRightMute(self, value:bool) -> None: + self._pgaRightMute = value + self._pgaRightVolumeSet = True + + @property + def pgaMute(self) -> bool: + return self._pgaLeftMute and self._pgaRightMute + @pgaMute.setter + def pgaMute(self, value:bool) -> None: + self._pgaLeftMute = self._pgaRightMute = value + + # Boost Mixer + + input2LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 1, 0) + input2RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 1, 0) + + @property + def input2Boost(self) -> int: + return self.input2LeftBoost + @input2Boost.setter + def input2Boost(self, value:int) -> None: + self.input2LeftBoost = self.input2RightBoost = value + + input3LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 4, 0) + input3RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 4, 0) + + # Mic Bias + + micBias = WMBit(_REG_PWR_MGMT_1, 1, False) + micBiasVoltage = WMBit(_REG_ADDITIONAL_CONTROL_4, 0, False) + + @property + def input3Boost(self) -> int: + return self.input3LeftBoost + @input3Boost.setter + def input3Boost(self, value:int) -> None: + self.input3LeftBoost = self.input3RightBoost = value + + # ADC + + adcLeftEnabled = WMBit(_REG_PWR_MGMT_1, 3, False) + adcRightEnabled = WMBit(_REG_PWR_MGMT_1, 2, False) + + @property + def adcEnabled(self) -> bool: + return self.adcLeftEnabled and self.adcRightEnabled + @adcEnabled.setter + def adcEnabled(self, value:bool) -> None: + self.adcLeftEnabled = self.adcRightEnabled = value + + ## Volume + + _adcLeftVolume = WMBits(8, _REG_LEFT_ADC_VOLUME, 0, 0b11000011) + _adcLeftVolumeSet = WMBit(_REG_LEFT_ADC_VOLUME, 8, False) + + @property + def adcLeftVolume(self) -> int: + return self._adcLeftVolume + @adcLeftVolume.setter + def adcLeftVolume(self, value:int) -> None: + self._adcLeftVolume = value + self._adcLeftVolumeSet = True + + @property + def adcLeftVolumeDb(self) -> float: + return map_range(max(self.adcLeftVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + @adcLeftVolumeDb.setter + def adcLeftVolumeDb(self, value:float) -> None: + self.adcLeftVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + + _adcRightVolume = WMBits(8, _REG_RIGHT_ADC_VOLUME, 0, 0b11000011) + _adcRightVolumeSet = WMBit(_REG_RIGHT_ADC_VOLUME, 8, False) + + @property + def adcRightVolume(self) -> int: + return self._adcRightVolume + @adcRightVolume.setter + def adcRightVolume(self, value:int) -> None: + self._adcRightVolume = value + self._adcRightVolumeSet = True + + @property + def adcRightVolumeDb(self) -> float: + return map_range(max(self.adcRightVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + @adcRightVolumeDb.setter + def adcRightVolumeDb(self, value:float) -> None: + self.adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + + @property + def adcVolume(self) -> int: + return self.adcLeftVolume + @adcVolume.setter + def adcVolume(self, value:float) -> None: + self.adcLeftVolume = self.adcRightVolume = value + + @property + def adcVolumeDb(self) -> int: + return self.adcLeftVolumeDb + @adcVolume.setter + def adcVolume(self, value:float) -> None: + self.adcLeftVolume = self.adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + + # ALC + + alcLeftEnabled = WMBit(_REG_ALC1, 7, False) + alcRightEnabled = WMBit(_REG_ALC1, 8, False) + + @property + def alcEnabled(self) -> bool: + return self.alcLeftEnabled and self.alcRightEnabled + @alcEnabled.setter + def alcEnabled(self, value:bool) -> None: + self.alcLeftEnabled = self.alcRightEnabled = value + + alcTarget = WMBits(4, _REG_ALC1, 0, 0b1011) + + @property + def alcTargetDb(self) -> float: + return map_range(self.alcTarget, 0, 15, _ALC_TARGET_MIN, _ALC_TARGET_MAX) + @alcTargetDb.setter + def alcTargetDb(self, value:float) -> None: + self.alcTarget = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) + + alcMaxGain = WMBits(3, _REG_ALC1, 4, 0) + + @property + def alcMaxGainDb(self) -> float: + return map_range(self.alcMaxGain, 0, 7, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX) + @alcMaxGainDb.setter + def alcMaxGainDb(self, value:float) -> None: + self.alcMaxGain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) + + alcMinGain = WMBits(3, _REG_ALC2, 4, 0) + + @property + def alcMinGainDb(self) -> float: + return map_range(self.alcMinGain, 0, 7, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX) + @alcMinGainDb.setter + def alcMinGainDb(self, value:float) -> None: + self.alcMinGain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) + + _alcAttack = WMBits(4, _REG_ALC3, 0, 0b0011) + + @property + def alcAttack(self) -> int: + return self._alcAttack + @alcAttack.setter + def alcAttack(self, value:int) -> None: + self._alcAttack = min(value, _ALC_ATTACK_MAX) + + @property + def alcAttackTime(self) -> float: + return _ALC_ATTACK_TIME_MIN * pow(2, self.alcAttack) + @alcAttackTime.setter + def alcAttackTime(self, value:float) -> None: + self.alcAttack = round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)) + + _alcDecay = WMBits(4, _REG_ALC3, 4, 0b0011) + + @property + def alcDecay(self) -> int: + return self._alcDecay + @alcDecay.setter + def alcDecay(self, value:int) -> None: + self._alcDecay = min(value, _ALC_DECAY_MAX) + + @property + def alcDecayTime(self) -> float: + return _ALC_DECAY_TIME_MIN * pow(2, self.alcDecay) + @alcDecayTime.setter + def alcDecayTime(self, value:float) -> None: + self.alcDecay = round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)) + + alcHold = WMBits(4, _REG_ALC2, 0, 0) + + @property + def alcHoldTime(self) -> float: + value = self.alcHold + if value == 0: return 0.0 + return _ALC_HOLD_TIME_MIN * pow(2, self.alcHold - 1) + @alcHoldTime.setter + def alcHoldTime(self, value:float) -> None: + if value <= 0.0: + self.alcHold = 0 else: - if not self.disableLoopBack(): return False + self.alcHold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) + + alcLimiter = WMBit(_REG_ALC3, 8, False) + + # Noise Gate - return True + noiseGateEnabled = WMBit(_REG_NOISE_GATE, 0, False) + noiseGateThreshold = WMBits(5, _REG_NOISE_GATE, 3, 0) + + @property + def noiseGateThresholdDb(self) -> float: + return map_range(self.noiseGateThreshold, 0, 31, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX) + @noiseGateThresholdDb.setter + def noiseGateThresholdDb(self, value:float) -> None: + self.noiseGateThreshold = round(map_range(value, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX, 0, 31)) + + # DAC + + dacLeftEnabled = WMBit(_REG_PWR_MGMT_2, 8, False) + dacRightEnabled = WMBit(_REG_PWR_MGMT_2, 7, False) + + @property + def dacEnabled(self) -> bool: + return self.dacLeftEnabled and self.dacRightEnabled + @dacEnabled.setter + def dacEnabled(self, value:bool) -> None: + self.dacLeftEnabled = self.dacRightEnabled = value - def configureHeadphones(self, dB:float = 0.0, capless:bool = True) -> bool: - # Enable headphone output - if not self.enableHeadphones(): return False - # Provides VMID as buffer for headphone ground on OUT3 - if capless: - if not self.enableOUT3MIX(): return False - # Adjust headphone volume - if not self.setHeadphoneVolumeDB(dB): return False - return True + _dacLeftVolume = WMBits(8, _REG_LEFT_DAC_VOLUME, 0, 0b11111111) + _dacLeftVolumeSet = WMBit(_REG_LEFT_DAC_VOLUME, 8, False) + + @property + def dacLeftVolume(self) -> int: + return self._dacLeftVolume + @dacLeftVolume.setter + def dacLeftVolume(self, value:int) -> None: + self._dacLeftVolume = value + self._dacLeftVolumeSet = True + + @property + def dacLeftVolumeDb(self) -> float: + return map_range(max(self.dacLeftVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + @dacLeftVolumeDb.setter + def dacLeftVolumeDb(self, value:float) -> None: + self.dacLeftVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + + _dacRightVolume = WMBits(8, _REG_RIGHT_DAC_VOLUME, 0, 0b11111111) + _dacRightVolumeSet = WMBit(_REG_RIGHT_DAC_VOLUME, 8, False) + + @property + def dacRightVolume(self) -> int: + return self._dacRightVolume + @dacRightVolume.setter + def dacRightVolume(self, value:int) -> None: + self._dacRightVolume = value + self._dacRightVolumeSet = True + + @property + def dacRightVolumeDb(self) -> float: + return map_range(max(self.dacRightVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + @dacRightVolumeDb.setter + def dacRightVolumeDb(self, value:float) -> None: + self.dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + + @property + def dacVolume(self) -> int: + return self.dacLeftVolume + @dacVolume.setter + def dacVolume(self, value:float) -> None: + self.dacLeftVolume = self.dacRightVolume = value - def configureSpeakers(self, dB:float = 0.0) -> bool: - # Enable speaker output - if not self.enableSpeakers(): return False - # Adjust speaker volume - if not self.setSpeakerVolumeDB(dB): return False - return True - - def setSampleRate(self, sample_rate:int) -> bool: - # MCLK = 24 MHz - if not self.enablePLL(): return False # Needed for class-d amp clock - self.setSMD(PLL_MODE_FRACTIONAL) - self.setCLKSEL(CLKSEL_PLL) + @property + def dacVolumeDb(self) -> int: + return self.dacLeftVolumeDb + @dacVolume.setter + def dacVolume(self, value:float) -> None: + self.dacLeftVolume = self.dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - self.setPLLPRESCALE(PLLPRESCALE_DIV_2) - self.setSYSCLKDIV(SYSCLK_DIV_BY_2) - self.setBCLKDIV(4) + dacMute = WMBit(_REG_ADC_DAC_CTRL_1, 3, True) + dacSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 3, False) + dacSlowSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 2, 1) + dacAttenuation = WMBit(_REG_ADC_DAC_CTRL_1, 7, False) - self.setDCLKDIV(DCLKDIV_16) + # 3D Enhance - if sample_rate in [8000, 12000, 16000, 24000, 32000, 48000]: - # SYSCLK = 12.288 MHz - # DCLK = 768.0kHz - self.setPLLN(8) - self.setPLLK(0x3126E8) + enhanceEnabled = WMBit(_REG_3D_CONTROL, 0, 1) + enhanceDepth = WMBits(4, _REG_3D_CONTROL, 1, 0) + enhanceFilterLPF = WMBit(_REG_3D_CONTROL, 6, False) + enhanceFilterHPF = WMBit(_REG_3D_CONTROL, 5, False) - div = self._getDivBit(48000, sample_rate) - self.setADCDIV(div) - self.setDACDIV(div) - elif sample_rate in [11025, 22050, 44100]: - # SYSCLK = 11.2896 MHz - # DCLK = 705.6kHz - self.setPLLN(7) - self.setPLLK(0x86C226) + # Output Mixer - div = self._getDivBit(44100, sample_rate) - self.setADCDIV(div) - self.setDACDIV(div) - else: - raise Exception("Invalid sample rate") - - return True - - ''' - Necessary for all other functions of the CODEC - VREF is a single bit we can flip in Register 25 (19h), _REG_PWR_MGMT_1 - VREF is bit 6, 0 = power down, 1 = power up - Returns 1 if successful, 0 if something failed (I2C error) - ''' - def enableVREF(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 6, 1) - - ''' - Use this to save power - VREF is a single bit we can flip in Register 25 (19h), _REG_PWR_MGMT_1 - VREF is bit 6, 0 = power down, 1 = power up - Returns 1 if successful, 0 if something failed (I2C error) - ''' - def disableVREF(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 6, 0) + leftOutputEnabled = WMBit(_REG_PWR_MGMT_3, 3, False) + rightOutputEnabled = WMBit(_REG_PWR_MGMT_3, 2, False) - # Resets all registers to their default state - def reset(self) -> bool: - # Doesn't matter which bit we flip, writing anything will cause the reset - if not self._writeRegisterBit(_REG_RESET, 7, 1): return False - # Update our local copy of the registers to reflect the reset - for i in range(len(_REGISTER_DEFAULTS)): - self._registerLocalCopy[i] = _REGISTER_DEFAULTS[i] - return True - - def enableAINL(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 5, 1) - def disableAINL(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 5, 0) - - def enableAINR(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 4, 1) - def disableAINR(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 4, 0) - - def enableAIN(self) -> bool: - return self.enableAINL() and self.enableAINR() - def disableAIN(self) -> bool: - return self.disableAINL() and self.disableAINR() - - def enableLMIC(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 1) - def disableLMIC(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 0) - - def enableRMIC(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 1) - def disableRMIC(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 0) - - def enableMIC(self) -> bool: - return self.enableLMIC() and self.enableRMIC() - def disableMIC(self) -> bool: - return self.disableLMIC() and self.disableRMIC() - - def enableLMICBOOST(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 1) - def disableLMICBOOST(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 5, 0) - - def enableRMICBOOST(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 1) - def disableRMICBOOST(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 4, 0) - - def enableMICBOOST(self) -> bool: - return self.enableLMICBOOST() and self.enableRMICBOOST() - def disableMICBOOST(self) -> bool: - return self.disableLMICBOOST() and self.disableRMICBOOST() - - # PGA input signal select - # Each PGA (left and right) has a switch on its non-inverting input. - # On PGA_LEFT: - # You can select between VMID, LINPUT2 or LINPUT3 - # Note, the inverting input of PGA_LEFT is perminantly connected to - # LINPUT1 - # On PGA_RIGHT: - # You can select between VMIN, RINPUT2 or RINPUT3 - # Note, the inverting input of PGA_RIGHT is perminantly connected to - # RINPUT1 - - # 3 options: PGAL_LINPUT2, PGAL_LINPUT3, PGAL_VMID - def pgaLeftNonInvSignalSelect(self, signal:int) -> bool: - ''' - Clear LMP2 and LMP3 - Necessary because the previous setting could have either set, - And we don't want to confuse the codec. - Only 1 input can be selected. - ''' - - # LMP3 - if not self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 7, 0): return False - - # LMP2 - if not self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 6, 0): return False - - if signal == PGAL_LINPUT2: - # LMP2 - return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 6, 1) - elif signal == PGAL_LINPUT3: - # LMP3 - return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 7, 1) - elif signal == PGAL_VMID: - # Don't set any bits. When both LMP2 and LMP3 are cleared, then the signal is set to VMID - return True - else: - raise Exception("Invalid PGA signal select") - - # 3 options: PGAR_RINPUT2, PGAR_RINPUT3, PGAR_VMID - def pgaRightNonInvSignalSelect(self, signal:int) -> bool: - ''' - Clear RMP2 and RMP3 - Necessary because the previous setting could have either set, - And we don't want to confuse the codec. - Only 1 input can be selected. - ''' - - # RMP3 - if not self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 7, 0): return False - - # RMP2 - if not self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 6, 0): return False - - if signal == PGAR_RINPUT2: - # RMP2 - return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 6, 1) - elif signal == PGAR_RINPUT3: - # RMP3 - return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 7, 1) - elif signal == PGAR_VMID: - # Don't set any bits. When both RMP2 and RMP3 are cleared, then the signal is set to VMID - return True - else: - raise Exception("Invalid PGA signal select") + @property + def stereoOutputEnabled(self) -> bool: + return self.leftOutputEnabled and self.rightOutputEnabled + @stereoOutputEnabled.setter + def stereoOutputEnabled(self, value:bool): + self.leftOutputEnabled = self.rightOutputEnabled = value - # 3 options: PGA_INPUT2, PGA_INPUT3, PGA_VMID - def pgaNonInvSignalSelect(self, signal:int) -> bool: - return self.pgaLeftNonInvSignalSelect(signal) and self.pgaRightNonInvSignalSelect(signal) + ## DAC Output - # Connections from each INPUT1 to the inverting input of its PGA + dacLeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 8, False) + dacRightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 8, False) - # Connect LINPUT1 to inverting input of Left Input PGA - def connectLMN1(self) -> bool: - return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 8, 1) + @property + def dacOutputEnabled(self) -> bool: + return self.dacLeftOutputEnabled and self.dacRightOutputEnabled + @dacOutputEnabled.setter + def dacOutputEnabled(self, value:bool) -> None: + self.dacLeftOutputEnabled = self.dacRightOutputEnabled = value - # Disconnect LINPUT1 from inverting input of Left Input PGA - def disconnectLMN1(self) -> bool: - return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 8, 0) + ## Input 3 Output - # Connect RINPUT1 to inverting input of Right Input PGA - def connectRMN1(self) -> bool: - return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 8, 1) + input3LeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 7, False) + input3RightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 7, False) + + @property + def input3OutputEnabled(self) -> bool: + return self.input3LeftOutputEnabled and self.input3RightOutputEnabled + @input3OutputEnabled.setter + def input3OutputEnabled(self, value:bool) -> None: + self.input3LeftOutputEnabled = self.input3RightOutputEnabled = value + + input3LeftOutputVolume = WMBits(3, _REG_LEFT_OUT_MIX, 4, 0b101) + + @property + def input3LeftOutputVolumeDb(self) -> float: + return map_range(self.input3LeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @input3LeftOutputVolumeDb.setter + def input3LeftOutputVolumeDb(self, value:float) -> None: + self.input3LeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + + input3RightOutputVolume = WMBits(3, _REG_RIGHT_OUT_MIX, 4, 0b101) + + @property + def input3RightOutputVolumeDb(self) -> float: + return map_range(self.input3RightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @input3RightOutputVolumeDb.setter + def input3RightOutputVolumeDb(self, value:float) -> None: + self.input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + + @property + def input3OutputVolume(self) -> int: + return self.input3LeftOutputVolume + @input3OutputVolume.setter + def input3OutputVolume(self, value:int) -> None: + self.input3LeftOutputVolume = self.input3RightOutputVolume = value + + @property + def input3OutputVolumeDb(self) -> float: + return self.input3LeftOutputVolumeDb + @input3OutputVolumeDb.setter + def input3OutputVolumeDb(self, value:float) -> None: + self.input3LeftOutputVolume = self.input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + + ## PGA Boost Mixer Output + + pgaBoostLeftOutputEnabled = WMBit(_REG_BYPASS_1, 7, False) + pgaBoostRightOutputEnabled = WMBit(_REG_BYPASS_2, 7, False) + + @property + def pgaBoostOutputEnabled(self) -> bool: + return self.pgaBoostLeftOutputEnabled and self.pgaBoostRightOutputEnabled + + pgaBoostLeftOutputVolume = WMBits(3, _REG_BYPASS_1, 4, 0b101) + + @property + def pgaBoostLeftOutputVolumeDb(self) -> float: + return map_range(self.pgaBoostLeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @pgaBoostLeftOutputVolumeDb.setter + def pgaBoostLeftOutputVolumeDb(self, value:float) -> None: + self.pgaBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + + pgaBoostRightOutputVolume = WMBits(3, _REG_BYPASS_2, 4, 0b101) + + @property + def pgaBoostRightOutputVolumeDb(self) -> float: + return map_range(self.pgaBoostRightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @pgaBoostRightOutputVolumeDb.setter + def pgaBoostRightOutputVolumeDb(self, value:float) -> None: + self.pgaBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + + @property + def pgaBoostOutputVolume(self) -> int: + return self.pgaBoostLeftOutputVolume + @pgaBoostOutputVolume.setter + def pgaBoostOutputVolume(self, value:int) -> None: + self.pgaBoostLeftOutputVolume = self.pgaBoostRightOutputVolume = value + + @property + def pgaBoostOutputVolumeDb(self) -> float: + return self.pgaBoostLeftOutputVolumeDb + @pgaBoostOutputVolumeDb.setter + def pgaBoostOutputVolumeDb(self, value:float) -> None: + self.pgaBoostLeftOutputVolume = self.pgaBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + + ## Mono Output + + monoOutputEnabled = WMBit(_REG_PWR_MGMT_2, 1, False) + + monoLeftMixEnabled = WMBit(_REG_MONO_OUT_MIX_1, 7, False) + monoRightMixEnabled = WMBit(_REG_MONO_OUT_MIX_2, 7, False) + + @property + def monoMixEnabled(self) -> bool: + return self.monoLeftMixEnabled and self.monoRightMixEnabled + @monoMixEnabled.setter + def monoMixEnabled(self, value:bool) -> None: + self.monoLeftMixEnabled = self.monoRightMixEnabled = value + + monoOutputAttenuation = WMBit(_REG_MONO_OUT_VOLUME, 6, True) + + vmid = WMBits(2, _REG_PWR_MGMT_1, 7, 0) + + # Amplifier - # Disconnect RINPUT1 from inverting input of Right Input PGA - def disconnectRMN1(self) -> bool: - return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 8, 0) + ## Headphones - def connectMN1(self) -> bool: - return self.connectLMN1() and self.connectRMN1() - def disconnectMN1(self) -> bool: - return self.disconnectLMN1() and self.disconnectRMN1() + leftHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 5, False) + rightHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 6, False) + + @property + def headphoneEnabled(self) -> bool: + return self.leftHeadphoneEnabled and self.rightHeadphoneEnabled + @headphoneEnabled.setter + def headphoneEnabled(self, value:bool) -> None: + self.leftHeadphoneEnabled = self.rightHeadphoneEnabled = value + + headphoneStandby = WMBit(_REG_ANTI_POP_1, 0, False) + + _leftHeadphoneVolume = WMBits(7, _REG_LOUT1_VOLUME, 0, 0) + _leftHeadphoneVolumeSet = WMBit(_REG_LOUT1_VOLUME, 8, False) + + @property + def leftHeadphoneVolume(self) -> int: + return self._leftHeadphoneVolume + @leftHeadphoneVolume.setter + def leftHeadphoneVolume(self, value:int) -> None: + self._leftHeadphoneVolume = value + self._leftHeadphoneVolumeSet = True + + @property + def leftHeadphoneVolumeDb(self) -> float: + return map_range(max(self.leftHeadphoneVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @leftHeadphoneVolumeDb.setter + def leftHeadphoneVolumeDb(self, value:float) -> None: + self.leftHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + + _rightHeadphoneVolume = WMBits(7, _REG_ROUT1_VOLUME, 0, 0) + _rightHeadphoneVolumeSet = WMBit(_REG_ROUT1_VOLUME, 8, False) + + @property + def rightHeadphoneVolume(self) -> int: + return self._rightHeadphoneVolume + @rightHeadphoneVolume.setter + def rightHeadphoneVolume(self, value:int) -> None: + self._rightHeadphoneVolume = value + self._rightHeadphoneVolumeSet = True + + @property + def rightHeadphoneVolumeDb(self) -> float: + return map_range(max(self.rightHeadphoneVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @rightHeadphoneVolumeDb.setter + def rightHeadphoneVolumeDb(self, value:float) -> None: + self.rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + + @property + def headphoneVolume(self) -> int: + return self.leftHeadphoneVolume + @headphoneVolume.setter + def headphoneVolume(self, value:float) -> None: + self.leftHeadphoneVolume = self.rightHeadphoneVolume = value - # Connection from output of PGAs to downstream "boost mixers" - - # Connect Left Input PGA to Left Input Boost mixer - def connectLMIC2B(self) -> bool: - return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 3, 1) - - # Disconnect Left Input PGA to Left Input Boost mixer - def disconnectLMIC2B(self) -> bool: - return self._writeRegisterBit(_REG_ADCL_SIGNAL_PATH, 3, 0) - - # Connect Right Input PGA to Right Input Boost mixer - def connectRMIC2B(self) -> bool: - return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 3, 1) - - # Disconnect Right Input PGA to Right Input Boost mixer - def disconnectRMIC2B(self) -> bool: - return self._writeRegisterBit(_REG_ADCR_SIGNAL_PATH, 3, 0) - - def connectMIC2B(self) -> bool: - return self.connectLMIC2B() and self.connectRMIC2B() - def disconnectMIC2B(self) -> bool: - return self.disconnectLMIC2B() and self.disconnectRMIC2B() - - # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) - def setLINVOL(self, volume:int) -> bool: - # Limit incoming values max - if volume > 63: - volume = 63 - if not self._writeRegisterMultiBits(_REG_LEFT_INPUT_VOLUME, 5, 0, volume): return False - return self.pgaLeftIPVUSet() - - ''' - Sets the volume of the PGA input buffer amp to a specified dB value - passed in as a float argument. - Valid dB settings are -17.25 up to +30.00 - -17.25 = -17.25dB (MIN) - ... 0.75dB steps ... - 30.00 = +30.00dB (MAX) - ''' - def setLINVOLDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setLINVOL() - volume = self.convertDBtoSetting(dB, PGA_GAIN_OFFSET, PGA_GAIN_STEPSIZE, PGA_GAIN_MIN, PGA_GAIN_MAX) - return self.setLINVOL(volume) - - # 0-63, (0 = -17.25dB) <<-- 0.75dB steps -->> (63 = +30dB) - def setRINVOL(self, volume:int) -> bool: - # Limit incoming values max - if volume > 63: - volume = 63 - if not self._writeRegisterMultiBits(_REG_RIGHT_INPUT_VOLUME, 5, 0, volume): return False - return self.pgaRightIPVUSet() - - ''' - Sets the volume of the PGA input buffer amp to a specified dB value - passed in as a float argument. - Valid dB settings are -17.25 up to +30.00 - -17.25 = -17.25dB (MIN) - ... 0.75dB steps ... - 30.00 = +30.00dB (MAX) - ''' - def setRINVOLDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setLINVOL() - volume = self.convertDBtoSetting(dB, PGA_GAIN_OFFSET, PGA_GAIN_STEPSIZE, PGA_GAIN_MIN, PGA_GAIN_MAX) - return self.setRINVOL(volume) - - def setINVOL(self, volume:int) -> bool: - return self.setLINVOL(volume) and self.setRINVOL(volume) - - def setINVOLDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setLINVOL() - volume = self.convertDBtoSetting(dB, PGA_GAIN_OFFSET, PGA_GAIN_STEPSIZE, PGA_GAIN_MIN, PGA_GAIN_MAX) - return self.setLINVOL(volume) and self.setRINVOL(volume) - - # Zero Cross prevents zipper sounds on volume changes - # Sets both left and right PGAs - def enablePgaZeroCross(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 6, 1) and self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 6, 1) - def disablePgaZeroCross(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 6, 0) and self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 6, 0) - - def enableLINMUTE(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 7, 1) - def disableLINMUTE(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 8, 1) - - def enableRINMUTE(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 7, 1) - def disableRINMUTE(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 7, 0) and self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 8, 1) - - def enableINMUTE(self) -> bool: - return self.enableLINMUTE() and self.enableRINMUTE() - def disableINMUTE(self) -> bool: - return self.disableLINMUTE() and self.disableRINMUTE() - - # Causes left and right input PGA volumes to be updated - # (LINVOL and RINVOL) - def pgaLeftIPVUSet(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_INPUT_VOLUME, 8, 1) - - # Causes left and right input PGA volumes to be updated - # (LINVOL and RINVOL) - def pgaRightIPVUSet(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_INPUT_VOLUME, 8, 1) - - # Boosts - - # MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB - def setLMICBOOST(self, boost_gain:int) -> bool: - # Limit incoming values max - if boost_gain > 3: - boost_gain = 3 - return self._writeRegisterMultiBits(_REG_ADCL_SIGNAL_PATH, 5, 4, boost_gain) - - # MIC_BOOST_GAIN_0DB or _13DB, _20DB, _29DB - def setRMICBOOST(self, boost_gain:int) -> bool: - # Limit incoming values max - if boost_gain > 3: - boost_gain = 3 - return self._writeRegisterMultiBits(_REG_ADCR_SIGNAL_PATH, 5, 4, boost_gain) - - def setMICBOOST(self, boost_gain:int) -> bool: - return self.setLMICBOOST(boost_gain) and self.setRMICBOOST(boost_gain) - - # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... - def setLIN2BOOST(self, boost_gain:int) -> bool: - # Limit incoming values max - if boost_gain > 7: - boost_gain = 7 - return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_1, 3, 1, boost_gain) - - # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... - def setRIN2BOOST(self, boost_gain:int) -> bool: - # Limit incoming values max - if boost_gain > 7: - boost_gain = 7 - return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_2, 3, 1, boost_gain) - - def setIN2BOOST(self, boost_gain:int) -> bool: - return self.setLIN2BOOST(boost_gain) and self.setRIN2BOOST(boost_gain) - - # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... - def setLIN3BOOST(self, boost_gain:int) -> bool: - # Limit incoming values max - if boost_gain > 7: - boost_gain = 7 - return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_1, 6, 4, boost_gain) - - # BOOST_MIXER_GAIN_MUTE, BOOST_MIXER_GAIN_NEG_12DB, ... - def setRIN3BOOST(self, boost_gain:int) -> bool: - # Limit incoming values max - if boost_gain > 7: - boost_gain = 7 - return self._writeRegisterMultiBits(_REG_INPUT_BOOST_MIXER_2, 6, 4, boost_gain) - - def setIN3BOOST(self, boost_gain:int) -> bool: - return self.setLIN3BOOST(boost_gain) and self.setRIN3BOOST(boost_gain) - - # Mic Bias control - def enableMicBias(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 1, 1) - def disableMicBias(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 1, 0) - - # MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) - # or MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) - def setMicBiasVoltage(self, voltage:bool) -> bool: - return self._writeRegisterBit(_REG_ADDITIONAL_CONTROL_4, 0, voltage) - - ## ADC - - def enableAdcLeft(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 3, 1) - def disableAdcLeft(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 3, 0) - - def enableAdcRight(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 2, 1) - def disableAdcRight(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_1, 2, 0) - - def enableAdc(self) -> bool: - return self.enableAdcLeft() and self.enableAdcRight() - def disableAdc(self) -> bool: - return self.disableAdcLeft() and self.disableAdcRight() - - # ADC digital volume - # Note, also needs to handle control of the ADCVU bits (volume update). - # Valid inputs are 0-255 - # 0 = mute - # 1 = -97dB - # ... 0.5dB steps up to - # 195 = 0dB - # 255 = +30dB - def setAdcLeftDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(_REG_LEFT_ADC_VOLUME, 7, 0, volume): return False - return self.adcLeftADCVUSet() - def setAdcRightDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(_REG_RIGHT_ADC_VOLUME, 7, 0, volume): return False - return self.adcRightADCVUSet() - - ''' - ADC digital volume DB - Sets the volume of the ADC to a specified dB value passed in as a float - argument. - Valid dB settings are -97.00 up to +30.0 (0.5dB steps) - -97.50 (or lower) = MUTE - -97.00 = -97.00dB (MIN) - ... 0.5dB steps ... - 30.00 = +30.00dB (MAX) - ''' - def setAdcLeftDigitalVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() - volume = self.convertDBtoSetting(dB, ADC_GAIN_OFFSET, ADC_GAIN_STEPSIZE, ADC_GAIN_MIN, ADC_GAIN_MAX) - return self.setAdcLeftDigitalVolume(volume) - def setAdcRightDigitalVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setAdcRightDigitalVolume() - volume = self.convertDBtoSetting(dB, ADC_GAIN_OFFSET, ADC_GAIN_STEPSIZE, ADC_GAIN_MIN, ADC_GAIN_MAX) - return self.setAdcRightDigitalVolume(volume) - - # Causes left and right input ADC volumes to be updated - def adcLeftADCVUSet(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_ADC_VOLUME, 8, 1) - - # Causes left and right input ADC volumes to be updated - def adcRightADCVUSet(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_ADC_VOLUME, 8, 1) - - # Control ADC volume in a stereo pair - def setAdcDigitalVolume(self, volume:int) -> bool: - return self.setAdcLeftDigitalVolume(volume) and self.setAdcRightDigitalVolume(volume) - - def setAdcDigitalVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setAdcLeftDigitalVolume() - volume = self.convertDBtoSetting(dB, ADC_GAIN_OFFSET, ADC_GAIN_STEPSIZE, ADC_GAIN_MIN, ADC_GAIN_MAX) - return self.setAdcDigitalVolume(volume) - - ## ALC - - # Automatic Level Control - # Note that when the ALC function is enabled, the settings of - # Registers 0 and 1 (LINVOL, IPVU, LIZC, LINMUTE, RINVOL, RIZC and - # RINMUTE) are ignored. - - # Also sets alc sample rate to match global sample rate. - def enableAlc(self, mode:int = ALC_MODE_STEREO) -> bool: - bit8 = mode >> 1 - bit7 = mode & 0x1 - return self._writeRegisterBit(_REG_ALC1, 8, bit8) and self._writeRegisterBit(_REG_ALC1, 7, bit7) - - def disableAlc(self) -> bool: - return self._writeRegisterBit(_REG_ALC1, 8, 0) and self._writeRegisterBit(_REG_ALC1, 7, 0) - - # Valid inputs are 0-15 - # 0 = -22.5dB FS ... 1.5dB steps ... 15 = -1.5dB FS - def setAlcTarget(self, target:int) -> bool: - # Limit incoming values max - if target > 15: - target = 15 - return self._writeRegisterMultiBits(_REG_ALC1,3,0,target) - - # Valid inputs are 0-10, 0 = 24ms, 1 = 48ms ... 10 = 24.58seconds - def setAlcDecay(self, decay:int) -> bool: - # Limit incoming values max - if decay > 10: - decay = 10 - return self._writeRegisterMultiBits(_REG_ALC3, 7, 4, decay) - - # Valid inputs are 0-10, 0 = 6ms, 1 = 12ms, 2 = 24ms ... - # 10 = 6.14seconds - def setAlcAttack(self, attack:int) -> bool: - # Limit incoming values max - if attack > 10: - attack = 10 - return self._writeRegisterMultiBits(_REG_ALC3, 3, 0, attack) - - # Valid inputs are 0-7, 0 = -12dB, ... 7 = +30dB - def setAlcMaxGain(self, maxGain:int) -> bool: - # Limit incoming values max - if maxGain > 7: - maxGain = 7 - return self._writeRegisterMultiBits(_REG_ALC1, 6, 4, maxGain) - - # Valid inputs are 0-7, 0 = -17.25dB, ... 7 = +24.75dB - def setAlcMinGain(self, minGain:int) -> bool: - # Limit incoming values max - if minGain > 7: - minGain = 7 - return self._writeRegisterMultiBits(_REG_ALC2, 6, 4, minGain) - - # Valid inputs are 0-15, 0 = 0ms, ... 15 = 43.691s - def setAlcHold(self, hold:int) -> bool: - # Limit incoming values max - if hold > 15: - hold = 15 - return self._writeRegisterMultiBits(_REG_ALC2, 3, 0, hold) - - # Peak Limiter - def enablePeakLimiter(self) -> bool: - return self._writeRegisterBit(_REG_ALC3, 8, 1) - - def disablePeakLimiter(self) -> bool: - return self._writeRegisterBit(_REG_ALC3, 8, 0) + @property + def headphoneVolumeDb(self) -> int: + return self.leftHeadphoneVolumeDb + @headphoneVolumeDb.setter + def headphoneVolumeDb(self, value:float) -> None: + self.leftHeadphoneVolume = self.rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + + leftHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7, False) + rightHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7, False) + + @property + def headphoneZeroCross(self) -> bool: + return self.leftHeadphoneZeroCross and self.rightHeadphoneZeroCross + @headphoneZeroCross.setter + def headphoneZeroCross(self, value:bool) -> None: + self.leftHeadphoneZeroCross = self.rightHeadphoneZeroCross = value - # Noise Gate - def enableNoiseGate(self) -> bool: - return self._writeRegisterBit(_REG_NOISE_GATE, 0, 1) - def disableNoiseGate(self) -> bool: - return self._writeRegisterBit(_REG_NOISE_GATE, 0, 0) - - # 0-31, 0 = -76.5dBfs, 31 = -30dBfs - def setNoiseGateThreshold(self, threshold:int) -> bool: - # TODO - return False - - ## DAC - - # Enable/disble each channel - def enableDacLeft(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 8, 1) - def disableDacLeft(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 8, 0) - - def enableDacRight(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 7, 1) - def disableDacRight(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 7, 0) - - def enableDac(self) -> bool: - return self.enableDacLeft() and self.enableDacRight() - def disableDac(self) -> bool: - return self.disableDacLeft() and self.disableDacRight() - - # DAC digital volume - # Valid inputs are 0-255 - # 0 = mute - # 1 = -127dB - # ... 0.5dB steps up to - # 255 = 0dB - def setDacLeftDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(_REG_LEFT_DAC_VOLUME, 7, 0, volume): return False - return self.dacLeftDACVUSet() - - def setDacRightDigitalVolume(self, volume:int) -> bool: - if not self._writeRegisterMultiBits(_REG_RIGHT_DAC_VOLUME, 7, 0, volume): return False - return self.dacRightDACVUSet() - - ''' - DAC digital volume DB - Sets the volume of the DAC to a specified dB value passed in as a float argument. - Valid dB settings are -97.00 up to +30.0 (0.5dB steps) - -97.50 (or lower) = MUTE - -97.00 = -97.00dB (MIN) - ... 0.5dB steps ... - 30.00 = +30.00dB (MAX) - ''' - def setDacLeftDigitalVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setDacLeftDigitalVolume() - volume = self.convertDBtoSetting(dB, DAC_GAIN_OFFSET, DAC_GAIN_STEPSIZE, DAC_GAIN_MIN, DAC_GAIN_MAX) - return self.setDacLeftDigitalVolume(volume) - - def setDacRightDigitalVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() - volume = self.convertDBtoSetting(dB, DAC_GAIN_OFFSET, DAC_GAIN_STEPSIZE, DAC_GAIN_MIN, DAC_GAIN_MAX) - return self.setDacRightDigitalVolume(volume) - - # Causes left and right input DAC volumes to be updated - def dacLeftDACVUSet(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_DAC_VOLUME, 8, 1) - - # Causes left and right input DAC volumes to be updated - def dacRightDACVUSet(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_DAC_VOLUME, 8, 1) - - def setDacDigitalVolume(self, volume:int) -> bool: - return self.setDacLeftDigitalVolume(volume) and self.setDacRightDigitalVolume(volume) + ## Speakers + + _leftSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 4, False) + _leftSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 6, False) + + @property + def leftSpeakerEnabled(self) -> bool: + return self._leftSpeakerEnabled and self._leftSpeakerAmpEnabled + @leftSpeakerEnabled.setter + def leftSpeakerEnabled(self, value:bool) -> None: + self._leftSpeakerEnabled = self._leftSpeakerAmpEnabled = value - def setDacDigitalVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setDacRightDigitalVolume() - volume = self.convertDBtoSetting(dB, DAC_GAIN_OFFSET, DAC_GAIN_STEPSIZE, DAC_GAIN_MIN, DAC_GAIN_MAX) - return self.setDacDigitalVolume(volume) - - # DAC mute - def enableDacMute(self) -> bool: - return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 3, 1) - def disableDacMute(self) -> bool: - return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 3, 0) - - # DE-Emphasis - - # 3D Stereo Enhancement - # 3D enable/disable - def enable3d(self) -> bool: - return self._writeRegisterBit(_REG_3D_CONTROL, 0, 1) - def disable3d(self) -> bool: - return self._writeRegisterBit(_REG_3D_CONTROL, 0, 0) - def set3dDepth(self, depth:int): # 0 = 0%, 15 = 100% - # Limit incoming values max - if depth > 15: - depth = 15 - return self._writeRegisterMultiBits(_REG_3D_CONTROL, 4, 1, depth) - - # 3D upper/lower cut-off frequencies. - - # DAC output -6dB attentuation enable/disable - def enableDac6dbAttenuation(self) -> bool: - return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 7, 1) - def disableDac6dbAttentuation(self) -> bool: - return self._writeRegisterBit(_REG_ADC_DAC_CTRL_1, 7, 0) - - ## OUTPUT mixers - - # What's connected to what? Oh so many options... - # LOMIX Left Output Mixer - # ROMIX Right Output Mixer - # OUT3MIX Mono Output Mixer - - # Enable/disable left and right output mixers - def enableLOMIX(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 3, 1) - def disableLOMIX(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 3, 0) - - def enableROMIX(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 2, 1) - def disableROMIX(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_3, 2, 0) - - def enableOUT3MIX(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 1, 1) - def disableOUT3MIX(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 1, 0) - - # Enable/disable audio path connections/vols to/from output mixers - # See datasheet page 35 for a nice image of all the connections. - - def enableLI2LO(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 7, 1) - def disableLI2LO(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 7, 0) - - # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setLI2LOVOL(self, volume:int) -> bool: - return self._writeRegisterMultiBits(_REG_LEFT_OUT_MIX_1, 6, 4, volume) - - def enableLB2LO(self) -> bool: - return self._writeRegisterBit(_REG_BYPASS_1, 7, 1) - def disableLB2LO(self) -> bool: - return self._writeRegisterBit(_REG_BYPASS_1, 7, 0) - - # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setLB2LOVOL(self, volume:int) -> bool: - # Limit incoming values max - if volume > 7: - volume = 7 - return self._writeRegisterMultiBits(_REG_BYPASS_1, 6, 4, volume) - - def enableLD2LO(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 8, 1) - def disableLD2LO(self) -> bool: - return self._writeRegisterBit(_REG_LEFT_OUT_MIX_1, 8, 0) - - def enableRI2RO(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 7, 1) - def disableRI2RO(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 7, 0) - - # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setRI2ROVOL(self, volume:int) -> bool: - # Limit incoming values max - if volume > 7: - volume = 7 - return self._writeRegisterMultiBits(_REG_RIGHT_OUT_MIX_2, 6, 4, volume) - - def enableRB2RO(self) -> bool: - return self._writeRegisterBit(_REG_BYPASS_2, 7, 1) - def disableRB2RO(self) -> bool: - return self._writeRegisterBit(_REG_BYPASS_2, 7, 0) - - # 0-7, 0 = 0dB, ... 3dB steps ... 7 = -21dB - def setRB2ROVOL(self, volume:int) -> bool: - # Limit incoming values max - if volume > 7: - volume = 7 - return self._writeRegisterMultiBits(_REG_BYPASS_2, 6, 4, volume) - - def enableRD2RO(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 8, 1) - def disableRD2RO(self) -> bool: - return self._writeRegisterBit(_REG_RIGHT_OUT_MIX_2, 8, 0) - - # Mono Output mixer. - # Note, for capless HPs, we'll want this to output a buffered VMID. - # To do this, we need to disable both of these connections. - def enableLI2MO(self) -> bool: - return self._writeRegisterBit(_REG_MONO_OUT_MIX_1, 7, 1) - def disableLI2MO(self) -> bool: - return self._writeRegisterBit(_REG_MONO_OUT_MIX_1, 7, 0) - - def enableRI2MO(self) -> bool: - return self._writeRegisterBit(_REG_MONO_OUT_MIX_2, 7, 1) - def disableRI2MO(self) -> bool: - return self._writeRegisterBit(_REG_MONO_OUT_MIX_2, 7, 0) - - # Paired stereo functions to enable/disable output mixers - def enableI2O(self) -> bool: - return self.enableLI2LO() and self.enableRI2RO() - def disableI2O(self) -> bool: - return self.disableLI2LO() and self.disableRI2RO() - def setI2OVOL(self, volume:int) -> bool: - return self.setLI2LOVOL(volume) and self.setRI2ROVOL(volume) - - def enableB2O(self) -> bool: - return self.enableLB2LO() and self.enableRB2RO() - def disableB2O(self) -> bool: - return self.disableLB2LO() and self.disableRB2RO() - def setB2OVOL(self, volume:int) -> bool: - return self.setLB2LOVOL(volume) and self.setRB2ROVOL(volume) - - def enableD2O(self) -> bool: - return self.enableLD2LO() and self.enableRD2RO() - def disableD2O(self) -> bool: - return self.disableLD2LO() and self.disableRD2RO() + _rightSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 3, False) + _rightSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 7, False) + + @property + def rightSpeakerEnabled(self) -> bool: + return self._rightSpeakerEnabled and self._rightSpeakerAmpEnabled + @rightSpeakerEnabled.setter + def rightSpeakerEnabled(self, value:bool) -> None: + self._rightSpeakerEnabled = self._rightSpeakerAmpEnabled = value + + @property + def speakerEnabled(self) -> bool: + return self.leftSpeakerEnabled and self.rightSpeakerEnabled + @speakerEnabled.setter + def speakerEnabled(self, value:bool) -> None: + self.leftSpeakerEnabled = self.rightSpeakerEnabled = value + + _leftSpeakerVolume = WMBits(7, _REG_LOUT2_VOLUME, 0, 0) + _leftSpeakerVolumeSet = WMBit(_REG_LOUT2_VOLUME, 8, False) + + @property + def leftSpeakerVolume(self) -> int: + return self._leftSpeakerVolume + @leftSpeakerVolume.setter + def leftSpeakerVolume(self, value:int) -> None: + self._leftSpeakerVolume = value + self._leftSpeakerVolumeSet = True + + @property + def leftSpeakerVolumeDb(self) -> float: + return map_range(max(self.leftSpeakerVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @leftSpeakerVolumeDb.setter + def leftSpeakerVolumeDb(self, value:float) -> None: + self.leftSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + + _rightSpeakerVolume = WMBits(7, _REG_ROUT2_VOLUME, 0, 0) + _rightSpeakerVolumeSet = WMBit(_REG_ROUT2_VOLUME, 8, False) + + @property + def rightSpeakerVolume(self) -> int: + return self._rightSpeakerVolume + @rightSpeakerVolume.setter + def rightSpeakerVolume(self, value:int) -> None: + self._rightSpeakerVolume = value + self._rightSpeakerVolumeSet = True + + @property + def rightSpeakerVolumeDb(self) -> float: + return map_range(max(self.rightSpeakerVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @rightSpeakerVolumeDb.setter + def rightSpeakerVolumeDb(self, value:float) -> None: + self.rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + + @property + def speakerVolume(self) -> int: + return self.leftSpeakerVolume + @speakerVolume.setter + def speakerVolume(self, value:float) -> None: + self.leftSpeakerVolume = self.rightSpeakerVolume = value - def enableI2MO(self) -> bool: - return self.enableLI2MO() and self.enableRI2MO() - def disableI2MO(self) -> bool: - return self.disableLI2MO() and self.disableRI2MO() - - def enableOMIX(self) -> bool: - return self.enableLOMIX() and self.enableROMIX() - def disableOMIX(self) -> bool: - return self.disableLOMIX() and self.disableROMIX() - - # Sets the VMID signal to one of three possible settings. - # 4 options: - # VMIDSEL_DISABLED - # VMIDSEL_2X50KOHM (playback / record) - # VMIDSEL_2X250KOHM (for low power / standby) - # VMIDSEL_2X5KOHM (for fast start-up) - def setVMID(self, setting:int = VMIDSEL_2X50KOHM) -> bool: - return self._writeRegisterMultiBits(_REG_PWR_MGMT_1, 8, 7, setting) + @property + def speakerVolumeDb(self) -> int: + return self.leftSpeakerVolumeDb + @speakerVolume.setter + def speakerVolume(self, value:float) -> None: + self.leftSpeakerVolume = self.rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + + leftSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7, False) + rightSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7, False) + + @property + def speakerZeroCross(self) -> bool: + return self.leftSpeakerZeroCross and self.rightSpeakerZeroCross + @speakerZeroCross.setter + def speakerZeroCross(self, value:bool) -> None: + self.leftSpeakerZeroCross = self.rightSpeakerZeroCross = value + + _speakerDcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 3, 0) + + @property + def speakerDcGain(self) -> int: + return self._speakerDcGain + @speakerDcGain.setter + def speakerDcGain(self, value:int) -> None: + self._speakerDcGain = min(value, 5) + + _speakerAcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 0, 0) + + @property + def speakerAcGain(self) -> int: + return self._speakerAcGain + @speakerAcGain.setter + def speakerAcGain(self, value:int) -> None: + self._speakerAcGain = min(value, 5) + + # Digital Audio Interface Control + + loopback = WMBit(_REG_AUDIO_INTERFACE_2, 0, False) - # Enables VMID in the _REG_PWR_MGMT_2 register, and set's it to - # playback/record settings of 2*50Kohm. - # Note, this function is only here for backwards compatibility with the - # original releases of this library. It is recommended to use the - # setVMID() function instead. - def enableVMID(self) -> bool: - return self.setVMID(VMIDSEL_2X50KOHM) - def disableVMID(self) -> bool: - return self.setVMID(VMIDSEL_DISABLED) - - # This will disable both connections, thus enable VMID on OUT3. Note, - # to enable VMID, you also need to enable OUT3 in the - # _REG_PWR_MGMT_2 [1] - def enableOUT3asVMID(self) -> bool: - return self.disableLI2MO() and self.disableRI2MO() and self.enableOUT3MIX() and self.enableVMID() + pll = WMBit(_REG_PWR_MGMT_2, 0, False) + pllPrescaleDiv2 = WMBit(_REG_PWR_MGMT_2, 4, False) + pllN = WMBits(4, _REG_PLL_N, 0, 0x8) + + _pllK1 = WMBits(6, _REG_PLL_K_1, 0, 0x31) + _pllK2 = WMBits(9, _REG_PLL_K_2, 0, 0x26) + _pllK3 = WMBits(9, _REG_PLL_K_3, 0, 0xE9) + + @property + def pllK(self) -> int: + return self._pllK1 << 18 + self._pllK2 << 9 + self._pllK3 + @pllK.setter + def pllK(self, value:int) -> None: + self._pllK1 = (value >> 18) & 0b111111 + self._pllK2 = (value >> 9) & 0b111111111 + self._pllK3 = value & 0b111111111 + + clockFractionalMode = WMBit(_REG_PLL_N, 5, False) + + clockFromPLL = WMBit(_REG_CLOCKING_1, 0, False) + + _systemClockDivider = WMBits(2, _REG_CLOCKING_1, 1, 0) - ## Headphones + @property + def systemClockDiv2(self) -> bool: + return self._systemClockDivider == _SYSCLK_DIV_BY_2 + @systemClockDiv2.setter + def systemClockDiv2(self, value:bool) -> None: + self._systemClockDivider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 + + _adcClockDivider = WMBits(3, _REG_CLOCKING_1, 6, 0) + + @property + def adcClockDivider(self) -> int: + return _ADCDACDIV[min(self._adcClockDivider, len(_ADCDACDIV))] + @adcClockDivider.setter + def adcClockDivider(self, value:int) -> None: + value = round(value * 2.0) / 2.0 + if value in _ADCDACDIV: + self._adcClockDivider = _ADCDACDIV.index(value) + + _dacClockDivider = WMBits(3, _REG_CLOCKING_1, 3, 0) + + @property + def dacClockDivider(self) -> int: + return _ADCDACDIV[min(self._dacClockDivider, len(_ADCDACDIV))] + @dacClockDivider.setter + def dacClockDivider(self, value:int) -> None: + value = round(value * 2.0) / 2.0 + if value in _ADCDACDIV: + self._dacClockDivider = _ADCDACDIV.index(value) + + _baseClockDivider = WMBits(4, _REG_CLOCKING_2, 0, 0) + + @property + def baseClockDivider(self) -> float: + return _BCLKDIV[min(self._baseClockDivider, len(_BCLKDIV))] + @baseClockDivider.setter + def baseClockDivider(self, value:float) -> None: + value = round(value * 2.0) / 2.0 + if value in _BCLKDIV: + self._baseClockDivider = _BCLKDIV.index(value) + - # Enable and disable headphones (mute) - def enableHeadphones(self) -> bool: - return self.enableRightHeadphone() and self.enableLeftHeadphone() - def disableHeadphones(self) -> bool: - return self.disableRightHeadphone() and self.disableLeftHeadphone() - - def enableRightHeadphone(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 5, 1) - def disableRightHeadphone(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 5, 0) - def enableLeftHeadphone(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 6, 1) - def disableLeftHeadphone(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 6, 0) - - def enableHeadphoneStandby(self) -> bool: - return self._writeRegisterBit(_REG_ANTI_POP_1, 0, 1) - def disableHeadphoneStandby(self) -> bool: - return self._writeRegisterBit(_REG_ANTI_POP_1, 0, 0) - - # Set headphone volume - # Although you can control each headphone output independently, here - # we are going to assume you want both left and right to do the same - # thing. - - # Valid inputs are 47-127. 0-47 = mute, 48 = -73dB ... 1dB steps ... - # 127 = +6dB - def setHeadphoneVolume(self, volume:int) -> bool: - # Updates both left and right channels - # Handles the OUT1VU (volume update) bit control, so that it happens at the - # same time on both channels. Note, we must also make sure that the outputs - # are enabled in the _REG_PWR_MGMT_2 [6:5] - # Grab local copy of register - # Modify the bits we need to - # Write register in device, including the volume update bit write - # If successful, save locally. - - # Limit inputs - if volume > 127: - volume = 127 - - # LEFT - if not self._writeRegisterMultiBits(_REG_LOUT1_VOLUME, 6, 0, volume): return False - - # RIGHT - if not self._writeRegisterMultiBits(_REG_ROUT1_VOLUME, 6, 0, volume): return False - - # UPDATES - # Updated left channel - if not self._writeRegisterBit(_REG_LOUT1_VOLUME, 8, 1): return False - - # Updated right channel - return self._writeRegisterBit(_REG_ROUT1_VOLUME, 8, 1) - - # Set headphone volume dB - # Sets the volume of the headphone output buffer amp to a speicified - # dB value passed in as a float argument. - # Valid dB settings are -74.0 up to +6.0 - # User input will be rounded to nearest whole integer - # -74 (or lower) = MUTE - # -73 = -73dB (MIN) - # ... 1dB steps ... - # 0 = 0dB - # ... 1dB steps ... - # 6 = +6dB (MAX) - def setHeadphoneVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setHeadphoneVolume() - volume = self.convertDBtoSetting(dB, HP_GAIN_OFFSET, HP_GAIN_STEPSIZE, HP_GAIN_MIN, HP_GAIN_MAX) - return self.setHeadphoneVolume(volume) - - # Zero Cross prevents zipper sounds on volume changes - # Sets both left and right Headphone outputs - def enableHeadphoneZeroCross(self) -> bool: - # Left - if not self._writeRegisterBit(_REG_LOUT1_VOLUME, 7, 1): return False - # Right - return self._writeRegisterBit(_REG_ROUT1_VOLUME, 7, 1) - - def disableHeadphoneZeroCross(self) -> bool: - # Left - if not self._writeRegisterBit(_REG_LOUT1_VOLUME, 7, 0): return False - # Right - return self._writeRegisterBit(_REG_ROUT1_VOLUME, 7, 0) + _ampClockDivider = WMBits(3, _REG_CLOCKING_2, 6, 0b111) - ## Speakers + @property + def ampClockDivider(self) -> float: + return _DCLKDIV[min(self._ampClockDivider, len(_DCLKDIV))] + @ampClockDivider.setter + def ampClockDivider(self, value:float) -> None: + value = round(value * 2.0) / 2.0 + if value in _DCLKDIV: + self._ampClockDivider = _DCLKDIV.index(value) + + ## Mode + + masterMode = WMBit(_REG_AUDIO_INTERFACE_1, 6, False) - # Enable and disable speakers (mute) - def enableSpeakers(self) -> bool: - return self.enableRightSpeaker() and self.enableLeftSpeaker() - def disableSpeakers(self) -> bool: - return self.disableRightSpeaker() and self.disableLeftSpeaker() - - def enableRightSpeaker(self) -> bool: - # SPK_OP_EN - if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 7, 1): return False - # SPKR - return self._writeRegisterBit(_REG_PWR_MGMT_2, 3, 1) - - def disableRightSpeaker(self) -> bool: - # SPK_OP_EN - if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 7, 0): return False - # SPKR - return self._writeRegisterBit(_REG_PWR_MGMT_2, 3, 0) - - def enableLeftSpeaker(self) -> bool: - # SPK_OP_EN - if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 6, 1): return False - # SPKL - return self._writeRegisterBit(_REG_PWR_MGMT_2, 4, 1) - - def disableLeftSpeaker(self) -> bool: - # SPK_OP_EN - if not self._writeRegisterBit(_REG_CLASS_D_CONTROL_1, 6, 0): return False - # SPKL - return self._writeRegisterBit(_REG_PWR_MGMT_2, 4, 0) - - # Set Speaker output volume - # Although you can control each Speaker output independently, here we - # are going to assume you want both left and right to do the same thing. - # Valid inputs are 47-127. 0-47 = mute, 48 = -73dB ... 1dB steps ... - # 127 = +6dB - - def setSpeakerVolume(self, volume:int) -> bool: - # Updates both left and right channels - # Handles the SPKVU (volume update) bit control, so that it happens at the - # same time on both channels. Note, we must also make sure that the outputs - # are enabled in the _REG_PWR_MGMT_2 [4:3], and the class D control - # reg _REG_CLASS_D_CONTROL_1 [7:6] - - # Limit inputs - if volume > 127: - volume = 127 - - # LEFT - if not self._writeRegisterMultiBits(_REG_LOUT2_VOLUME, 6, 0, volume): return False - # RIGHT - if not self._writeRegisterMultiBits(_REG_ROUT2_VOLUME, 6, 0, volume): return False - - # SPKVU - # Updated left channel - if not self._writeRegisterBit(_REG_LOUT2_VOLUME, 8, 1): return False - # Updated right channel - return self._writeRegisterBit(_REG_ROUT2_VOLUME, 8, 1) - - def setSpeakerVolumeDB(self, dB:float) -> bool: - # Create an unsigned integer volume setting variable we can send to setSpeakerVolume() - volume = self.convertDBtoSetting(dB, SPEAKER_GAIN_OFFSET, SPEAKER_GAIN_STEPSIZE, SPEAKER_GAIN_MIN, SPEAKER_GAIN_MAX) - return self.setSpeakerVolume(volume) - - # Zero Cross prevents zipper sounds on volume changes - # Sets both left and right Speaker outputs - def enableSpeakerZeroCross(self) -> bool: - # Left - if not self._writeRegisterBit(_REG_LOUT2_VOLUME, 7, 1): return False - # Right - return self._writeRegisterBit(_REG_ROUT2_VOLUME, 7, 1) - - def disableSpeakerZeroCross(self) -> bool: - # Left - if not self._writeRegisterBit(_REG_LOUT2_VOLUME, 7, 0): return False - # Right - return self._writeRegisterBit(_REG_ROUT2_VOLUME, 7, 0) - - # DC and AC gain - allows signal to be higher than the DACs swing - # (use only if your SPKVDD is high enough to handle a larger signal) - # Valid inputs are 0-5 - # 0 = +0dB (1.0x boost) ... up to ... 5 = +5.1dB (1.8x boost) - def setSpeakerDcGain(self, gain:int) -> bool: - # Limit incoming values max - if gain > 5: - gain = 5 - return self._writeRegisterMultiBits(_REG_CLASS_D_CONTROL_3, 5, 3, gain) - - def setSpeakerAcGain(self, gain:int) -> bool: - # Limit incoming values max - if gain > 5: - gain = 5 - return self._writeRegisterMultiBits(_REG_CLASS_D_CONTROL_3, 2, 0, gain) - - ## Digital audio interface control - - # Defaults to I2S, peripheral-mode, 24-bit word length - - # Loopback - # When enabled, the output data from the ADC audio interface is fed - # directly into the DAC data input. - def enableLoopBack(self) -> bool: - return self._writeRegisterBit(_REG_AUDIO_INTERFACE_2, 0, 1) - def disableLoopBack(self) -> bool: - return self._writeRegisterBit(_REG_AUDIO_INTERFACE_2, 0, 0) - - ## Clock controls - - # Getting the Frequency of SampleRate as we wish - # Our MCLK (an external clock on the SFE breakout board) is 24.0MHz. - # According to table 40 (DS pg 58), we want SYSCLK to be 11.2896 for a - # SR of 44.1KHz. To get that Desired Output (SYSCLK), we need the - # following settings on the PLL stuff: - # As found on table 45 (ds pg 61). - # PRESCALE DIVIDE (PLLPRESCALE): 2 - # POSTSCALE DVIDE (SYSCLKDIV[1:0]): 2 - # FIXED POST-DIVIDE: 4 - # R: 7.5264 - # N: 7h - # K: 86C226h - - # Example at bottom of table 46, shows that we should be in fractional - # mode for a 44.1KHz. - - # In terms of registers, this is what we want for 44.1KHz - # PLLEN=1 (PLL enable) - # PLLPRESCALE=1 (divide by 2) *This get's us from MCLK (24MHz) down - # to 12MHZ for F2 - # PLLN=7h (PLL N value) *this is "int R" - # PLLK=86C226h (PLL K value) *this is int ( 2^24 * (R- intR)) - # SDM=1 (Fractional mode) - # CLKSEL=1 (PLL select) - # MS=0 (Peripheral mode) - # WL=00 (16 bits) - # SYSCLKDIV=2 (Divide by 2) - # ADCDIV=000 (Divide by 1) = 44.1kHz - # DACDIV=000 (Divide by 1) = 44.1kHz - # BCLKDIV=0100 (Divide by 4) = 64fs - # DCLKDIV=111 (Divide by 16) = 705.6kHz - - # And now for the functions that will set these registers... - def enablePLL(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 0, 1) - def disablePLL(self) -> bool: - return self._writeRegisterBit(_REG_PWR_MGMT_2, 0, 0) - - # Valid options are PLLPRESCALE_DIV_1, PLLPRESCALE_DIV_2 - def setPLLPRESCALE(self, div:bool) -> bool: - return self._writeRegisterBit(_REG_PLL_N, 4, div) - - def setPLLN(self, n:int) -> bool: - return self._writeRegisterMultiBits(_REG_PLL_N, 3, 0, n) - - # Send each nibble of 24-bit value for value K - def setPLLK(self, k:int) -> bool: - if not self._writeRegisterMultiBits(_REG_PLL_K_1, 5, 0, (k >> 16) & 0x1F): return False - if not self._writeRegisterMultiBits(_REG_PLL_K_2, 8, 0, (k >> 8) & 0xFF): return False - return self._writeRegisterMultiBits(_REG_PLL_K_3, 8, 0, k & 0xFF) - - # 0=integer, 1=fractional - def setSMD(self, mode:bool) -> bool: - return self._writeRegisterBit(_REG_PLL_N, 5, mode) - - # 0=MCLK, 1=PLL_output - def setCLKSEL(self, sel:bool) -> bool: - return self._writeRegisterBit(_REG_CLOCKING_1, 0, sel) - - # (0=divide by 1), (2=div by 2) *1 and 3 are "reserved" - def setSYSCLKDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(_REG_CLOCKING_1, 2, 1, div) - - # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options - def setADCDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(_REG_CLOCKING_1, 8, 6, div) - - # 000 = SYSCLK / (1.0*256). See ds pg 57 for other options - def setDACDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(_REG_CLOCKING_1, 5, 3, div) - - # 0100 (4) = sufficiently high for 24bit, div by 4 allows for max word - # length of 32bit - def setBCLKDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(_REG_CLOCKING_2, 3, 0, div) - - # Class D amp, 111= SYSCLK/16, so 11.2896MHz/16 = 705.6KHz - def setDCLKDIV(self, div:int) -> bool: - return self._writeRegisterMultiBits(_REG_CLOCKING_2, 8, 6, div) - - # Set LR clock to be the same for ADC & DAC (needed for loopback mode) - def setALRCGPIO(self) -> bool: - return self._writeRegisterBit(_REG_AUDIO_INTERFACE_2, 6, 1) - - def enableMasterMode(self) -> bool: - return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 6, 1) + _wordLength = WMBits(2, _REG_AUDIO_INTERFACE_1, 2, 0b10) + + @property + def wordLength(self) -> int: + value = self._wordLength + if value == 3: + return 32 + else: + return 16 + 4 * value + @wordLength.setter + def wordLength(self, value:int) -> None: + self._wordLength = (min(value, 28) - 16) // 4 + + wordSelectInverted = WMBit(_REG_AUDIO_INTERFACE_1, 4, False) + + adcChannelSwap = WMBit(_REG_AUDIO_INTERFACE_1, 8, False) + + vrefOutputDisabled = WMBit(_REG_ADDITIONAL_CONTROL_3, 6, False) + + _vsel = WMBits(2, _REG_ADDITIONAL_CONTROL_1, 6, 0b11) + + @property + def powerSupply(self) -> float: + return (constrain(self._vsel, 1, 2) - 1) * 0.6 + 2.7 + @powerSupply.setter + def powerSupply(self, value:float) -> None: + self._vsel = (constrain(value, 2.7, 3.3) - 2.7) // 0.6 * 2 + 1 + + ## GPIO + + gpioOutput = WMBit(_REG_AUDIO_INTERFACE_2, 6, False) + gpioOutputMode = WMBits(3, _REG_ADDITIONAL_CONTROL_4, 4, 0) + gpioOutputInverted = WMBit(_REG_ADDITIONAL_CONTROL_4, 7, False) - def enablePeripheralMode(self) -> bool: - return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 6, 0) - - def setWL(self, word_length:int) -> bool: - return self._writeRegisterMultiBits(_REG_AUDIO_INTERFACE_1, 3, 2, word_length) - - def setLRP(self, polarity:bool) -> bool: - return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 4, polarity) - - def setALRSWAP(self, swap:bool) -> bool: - return self._writeRegisterBit(_REG_AUDIO_INTERFACE_1, 8, swap) - - def setVROI(self, setting:bool) -> bool: - return self._writeRegisterBit(_REG_ADDITIONAL_CONTROL_3, 6, setting) - - def setVSEL(self, setting:int) -> bool: - return self._writeRegisterMultiBits(_REG_ADDITIONAL_CONTROL_1, 7, 6, setting) - - # General-purpose register write - def writeRegister(self, reg:int, value:int) -> bool: - self._buf[0] = reg << 1 | value >> 8 - self._buf[1] = value & 0xff - try: - with self.i2c_device as i2c: - i2c.write(self._buf) - except OSError: - return False - self._registerLocalCopy[reg] = value - return True - - # **The WM8960 does not support reading registers!!! - - # Writes a 0 or 1 to the desired bit in the desired register - def _writeRegisterBit(self, registerAddress:int, bitNumber:int, bitValue:bool) -> bool: - regvalue = self._registerLocalCopy[registerAddress] - if bitValue: - regvalue |= 1< int: + return self._gpioClockDivider + @gpioClockDivider.setter + def gpioClockDivider(self, value:int) -> None: + self._gpioClockDivider = min(value, 5) + + @property + def sampleRate(self) -> int: + return self._sampleRate + @sampleRate.setter + def sampleRate(self, value:int) -> None: + # MCLK = 24 MHz + self.pll = True # Needed for class-d amp clock + self.clockFractionalMode = True + self.clockFromPLL = True + + self.pllPrescaleDiv2 = True + self.systemClockDiv2 = True + self.baseClockDivider = 4.0 + self.ampClockDivider = 16.0 + + if value in [8000, 12000, 16000, 24000, 32000, 48000]: + # SYSCLK = 12.288 MHz + # DCLK = 768.0kHz + self.pllN = 8 + self.pllK = 0x3126E8 + self.adcClockDivider = self.dacClockDivider = 48000 / value + + elif value in [11025, 22050, 44100]: + # SYSCLK = 11.2896 MHz + # DCLK = 705.6kHz + self.pllN = 7 + self.pllK = 0x86C226 + self.adcClockDivider = self.dacClockDivider = 44100 / value + else: - regvalue &= ~(1< bool: - regvalue = self._registerLocalCopy[registerAddress] - - # Clear bits we care about - numOfBits = (settingMsbNum - settingLsbNum) + 1 - for i in range(numOfBits): - regvalue &= ~(1 << (settingLsbNum + i)) - - # Shift and set the bits from in incoming desired setting value - regvalue |= setting << settingLsbNum - - # Write modified value to device - return self.writeRegister(registerAddress, regvalue) - - def convertDBtoSetting(self, dB:float, offset:float, stepSize:float, minDB:float, maxDB:float) -> int: - ''' - Limit incoming dB values to acceptable range. Note, the minimum limit we - want to limit this too is actually one step lower than the minDB, because - that is still an acceptable dB level (it is actually "true mute"). - Note, the PGA amp does not have a "true mute" setting available, so we - must check for its unique minDB of -17.25. - ''' - - # Limit max. This is the same for all amps. - if dB > maxDB: - dB = maxDB - - ''' - PGA amp doesn't have mute setting, so minDB should be limited to minDB - Let's check for the PGAs unique minDB (-17.25) to know we are currently - converting a PGA setting. - ''' - if minDB == PGA_GAIN_MIN: - if dB < minDB: - dB = minDB - else: # Not PGA. All other amps have a mute setting below minDb - if dB < minDB - stepSize: - dB = minDB - stepSize - - ''' - Adjust for offset - Offset is the number that gets us from the minimum dB option of an amp - up to the minimum setting value in the register. - ''' - dB = dB + offset - - ''' - Find out how many steps we are above the minimum (at this point, our - minimum is "0". Note, because dB comes in as a float, the result of this - division (volume) can be a partial number. We will round that next. - ''' - volume = dB / stepSize - volume = round(volume) # round to the nearest setting value. - - return int(volume) & 0xff # cast from float to integer + raise Exception("Invalid sample rate") + + self._sampleRate = value + + def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR) -> None: + self.i2c_device = I2CDevice(i2c_bus, address) + self._sampleRate = None + + self._registers = [ + 0x0097, # R0 (0x00) + 0x0097, # R1 (0x01) + 0x0000, # R2 (0x02) + 0x0000, # R3 (0x03) + 0x0000, # R4 (0x04) + 0x0008, # F5 (0x05) + 0x0000, # R6 (0x06) + 0x000A, # R7 (0x07) + 0x01C0, # R8 (0x08) + 0x0000, # R9 (0x09) + 0x00FF, # R10 (0x0a) + 0x00FF, # R11 (0x0b) + 0x0000, # R12 (0x0C) RESERVED + 0x0000, # R13 (0x0D) RESERVED + 0x0000, # R14 (0x0E) RESERVED + 0x0000, # R15 (0x0F) RESERVED + 0x0000, # R16 (0x10) + 0x007B, # R17 (0x11) + 0x0100, # R18 (0x12) + 0x0032, # R19 (0x13) + 0x0000, # R20 (0x14) + 0x00C3, # R21 (0x15) + 0x00C3, # R22 (0x16) + 0x01C0, # R23 (0x17) + 0x0000, # R24 (0x18) + 0x0000, # R25 (0x19) + 0x0000, # R26 (0x1A) + 0x0000, # R27 (0x1B) + 0x0000, # R28 (0x1C) + 0x0000, # R29 (0x1D) + 0x0000, # R30 (0x1E) RESERVED + 0x0000, # R31 (0x1F) RESERVED + 0x0100, # R32 (0x20) + 0x0100, # R33 (0x21) + 0x0050, # R34 (0x22) + 0x0000, # R35 (0x23) RESERVED + 0x0000, # R36 (0x24) RESERVED + 0x0050, # R37 (0x25) + 0x0000, # R38 (0x26) + 0x0000, # R39 (0x27) + 0x0000, # R40 (0x28) + 0x0000, # R41 (0x29) + 0x0040, # R42 (0x2A) + 0x0000, # R43 (0x2B) + 0x0000, # R44 (0x2C) + 0x0050, # R45 (0x2D) + 0x0050, # R46 (0x2E) + 0x0000, # R47 (0x2F) + 0x0002, # R48 (0x30) + 0x0037, # R49 (0x31) + 0x0000, # R50 (0x32) RESERVED + 0x0080, # R51 (0x33) + 0x0008, # R52 (0x34) + 0x0031, # R53 (0x35) + 0x0026, # R54 (0x36) + 0x00e9, # R55 (0x37) + ] + for i in range(len(self._registers)): + self._registers[i] = bytearray(self._registers[i].to_bytes(2, 'big')) + self._registers[i][0] |= i << 1 + + self.reset() + + # General setup + self.vref = True + self.vmid = VMIDSEL_PLAYBACK + + # Resets all registers to their default state + _reset = WMBit(_REG_RESET, 7, False) + def reset(self) -> None: + self._reset = True + for name in dir(self): + if not name.startswith('_') and isinstance(getattr(self, name), (WMBit, WMBits)): + getattr(self, name).reset(self) diff --git a/requirements.txt b/requirements.txt index 12bae3e..beb3efc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,5 @@ # # SPDX-License-Identifier: MIT -Adafruit-Blinka adafruit-circuitpython-busdevice +adafruit-circuitpython-simplemath From a5590782585e51c23ce7d3fa6933ce4e379683f2 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 16 Aug 2024 15:18:33 -0500 Subject: [PATCH 25/56] Basic examples updated to use new API. --- examples/wm8960_eighties_dystopia.py | 25 +++++++++++++++---------- examples/wm8960_simpletest.py | 25 ++++++++++++++++--------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/examples/wm8960_eighties_dystopia.py b/examples/wm8960_eighties_dystopia.py index 9c4815f..dba5f94 100644 --- a/examples/wm8960_eighties_dystopia.py +++ b/examples/wm8960_eighties_dystopia.py @@ -38,19 +38,24 @@ import digitalio import ulab.numpy as np -sample_rate = 44100 - codec = adafruit_wm8960.WM8960(board.I2C()) -# Set up codec as peripheral device with clock configured for desired sample rate (and 16-bit words) -codec.configureI2S(sample_rate) +# Setup Digital Interface +codec.sampleRate = 44100 +codec.wordLength = 16 -# Enable DAC and output mixer -codec.configureDAC() +# Enable DAC +codec.dacEnabled = True +codec.dacOutputEnabled = True +codec.stereoOutputEnabled = True +codec.dacMute = False -# Enable headphone output with OUT3 as capless buffer for headphone ground, adjust volume with dB -codec.configureHeadphones(dB=0.0, capless=True) +# Enable Headphone Amp with OUT3 as capless buffer for headphone ground +codec.headphoneEnabled = True +codec.monoOutputEnabled = True +codec.headphoneVolumeDb = 0.0 +# Configure I2S Output audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) led = digitalio.DigitalInOut(board.LED) @@ -63,8 +68,8 @@ lpf_basef = 500 # filter lowest frequency lpf_resonance = 1.5 # filter q -mixer = audiomixer.Mixer(channel_count=2, sample_rate=44100, buffer_size=8192) -synth = synthio.Synthesizer(channel_count=2, sample_rate=44100) +mixer = audiomixer.Mixer(channel_count=2, sample_rate=codec.sampleRate, buffer_size=8192) +synth = synthio.Synthesizer(channel_count=2, sample_rate=codec.sampleRate) audio.play(mixer) mixer.voice[0].play(synth) mixer.voice[0].level = 0.8 diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index 142b216..ffe1b5c 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -44,21 +44,28 @@ import time import digitalio -sample_rate = 44100 - codec = adafruit_wm8960.WM8960(board.I2C()) -# Set up codec as peripheral device with clock configured for desired sample rate (and 16-bit words) -codec.configureI2S(sample_rate) +# Setup Digital Interface +codec.sampleRate = 44100 +codec.wordLength = 16 -# Enable DAC and output mixer -codec.configureDAC() +# Enable DAC +codec.dacEnabled = True +codec.dacOutputEnabled = True +codec.stereoOutputEnabled = True +codec.dacMute = False -# Enable headphone output with OUT3 as capless buffer for headphone ground, adjust volume with dB -codec.configureHeadphones(dB=0.0, capless=True) +# Enable Headphone Amp with OUT3 as capless buffer for headphone ground +codec.headphoneEnabled = True +codec.monoOutputEnabled = True +codec.headphoneVolumeDb = 0.0 +# Configure I2S Output audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) -synth = synthio.Synthesizer(sample_rate=44100) + +# Setup synthio +synth = synthio.Synthesizer(sample_rate=codec.sampleRate) audio.play(synth) led = digitalio.DigitalInOut(board.LED) From 2fa811e832ce0f5d9a20a2111bac7eefed6e0a69 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 16 Aug 2024 16:07:38 -0500 Subject: [PATCH 26/56] Determine register default value automatically during initial reset. --- adafruit_wm8960.py | 255 +++++++++++++++++++++++---------------------- 1 file changed, 128 insertions(+), 127 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index a0f8f37..b921b64 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -228,12 +228,10 @@ def __init__( self, register_address: int, bit: int, - default: bool, ) -> None: self.register_address = register_address self.bit = bit self.bit_mask = 1 << (bit % 8) # the bitmask *within* the byte! - self.default = default self.byte = 1 - (bit // 8) # the byte number within the buffer def _set(self, obj:Optional[I2CDeviceDriver], value:bool) -> None: @@ -243,6 +241,8 @@ def _set(self, obj:Optional[I2CDeviceDriver], value:bool) -> None: obj._registers[self.register_address][self.byte] &= ~self.bit_mask def reset(self, obj: Optional[I2CDeviceDriver]) -> None: + if not hasattr(self, 'default'): + self.default = self.__get__(obj) self._set(obj, self.default) def __get__( @@ -258,18 +258,17 @@ def __set__(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: i2c.write(obj._registers[self.register_address]) class WMBits: + def __init__( # pylint: disable=too-many-arguments self, num_bits: int, register_address: int, lowest_bit: int, - default: int, ) -> None: self.register_width = 1 self.register_address = register_address self.bit_mask = ((1 << num_bits) - 1) << lowest_bit self.lowest_bit = lowest_bit - self.default = default << self.lowest_bit def _set(self, obj:Optional[I2CDeviceDriver], value:int) -> None: value <<= self.lowest_bit # shift the value over to the right spot @@ -284,6 +283,8 @@ def _set(self, obj:Optional[I2CDeviceDriver], value:int) -> None: # Must call first before using object def reset(self, obj:Optional[I2CDeviceDriver]) -> None: + if not hasattr(self, 'default'): + self.default = self.__get__(obj) self._set(obj, self.default) def __get__( @@ -307,10 +308,10 @@ class WM8960: # Power - vref = WMBit(_REG_PWR_MGMT_1, 6, False) + vref = WMBit(_REG_PWR_MGMT_1, 6) - analogInputLeftEnabled = WMBit(_REG_PWR_MGMT_1, 5, False) - analogInputRightEnabled = WMBit(_REG_PWR_MGMT_1, 4, False) + analogInputLeftEnabled = WMBit(_REG_PWR_MGMT_1, 5) + analogInputRightEnabled = WMBit(_REG_PWR_MGMT_1, 4) @property def analogInputEnabled(self) -> bool: @@ -323,8 +324,8 @@ def analogInputEnabled(self, value:bool) -> None: ## PWR_MGMT - pgaLeftEnabled = WMBit(_REG_PWR_MGMT_3, 5, False) - pgaRightEnabled = WMBit(_REG_PWR_MGMT_3, 4, False) + pgaLeftEnabled = WMBit(_REG_PWR_MGMT_3, 5) + pgaRightEnabled = WMBit(_REG_PWR_MGMT_3, 4) @property def pgaEnabled(self) -> bool: @@ -335,13 +336,13 @@ def pgaEnabled(self, value:bool) -> None: ## SIGNAL_PATH - _pgaInput1Left = WMBit(_REG_ADCL_SIGNAL_PATH, 8, True) - _pgaInput2Left = WMBit(_REG_ADCL_SIGNAL_PATH, 6, False) - _pgaInput3Left = WMBit(_REG_ADCL_SIGNAL_PATH, 7, False) + _pgaInput1Left = WMBit(_REG_ADCL_SIGNAL_PATH, 8) + _pgaInput2Left = WMBit(_REG_ADCL_SIGNAL_PATH, 6) + _pgaInput3Left = WMBit(_REG_ADCL_SIGNAL_PATH, 7) - _pgaInput1Right = WMBit(_REG_ADCR_SIGNAL_PATH, 8, True) - _pgaInput2Right = WMBit(_REG_ADCR_SIGNAL_PATH, 6, False) - _pgaInput3Right = WMBit(_REG_ADCR_SIGNAL_PATH, 7, False) + _pgaInput1Right = WMBit(_REG_ADCR_SIGNAL_PATH, 8) + _pgaInput2Right = WMBit(_REG_ADCR_SIGNAL_PATH, 6) + _pgaInput3Right = WMBit(_REG_ADCR_SIGNAL_PATH, 7) @property def pgaNonInvSignalLeft(self) -> int: @@ -379,8 +380,8 @@ def pgaNonInvSignal(self, signal:int) -> None: ## Boost - pgaLeftBoostEnabled = WMBit(_REG_ADCL_SIGNAL_PATH, 3, False) - pgaRightBoostEnabled = WMBit(_REG_ADCR_SIGNAL_PATH, 3, False) + pgaLeftBoostEnabled = WMBit(_REG_ADCL_SIGNAL_PATH, 3) + pgaRightBoostEnabled = WMBit(_REG_ADCR_SIGNAL_PATH, 3) @property def pgaBoostEnabled(self) -> None: @@ -389,8 +390,8 @@ def pgaBoostEnabled(self) -> None: def pgaBoostEnabled(self, value:int) -> None: self.pgaLeftBoostEnabled = self.pgaRightBoostEnabled = value - pgaBoostGainLeft = WMBits(2, _REG_ADCL_SIGNAL_PATH, 4, 0) - pgaBoostGainRight = WMBits(2, _REG_ADCR_SIGNAL_PATH, 4, 0) + pgaBoostGainLeft = WMBits(2, _REG_ADCL_SIGNAL_PATH, 4) + pgaBoostGainRight = WMBits(2, _REG_ADCR_SIGNAL_PATH, 4) @property def pgaBoostGain(self) -> None: @@ -401,11 +402,11 @@ def pgaBoostGain(self, value:int) -> None: ## Volume - _pgaLeftVolume = WMBits(6, _REG_LEFT_INPUT_VOLUME, 0, 0b010111) - _pgaLeftVolumeSet = WMBit(_REG_LEFT_INPUT_VOLUME, 8, False) + _pgaLeftVolume = WMBits(6, _REG_LEFT_INPUT_VOLUME, 0) + _pgaLeftVolumeSet = WMBit(_REG_LEFT_INPUT_VOLUME, 8) - _pgaRightVolume = WMBits(6, _REG_RIGHT_INPUT_VOLUME, 0, 0b010111) - _pgaRightVolumeSet = WMBit(_REG_RIGHT_INPUT_VOLUME, 8, False) + _pgaRightVolume = WMBits(6, _REG_RIGHT_INPUT_VOLUME, 0) + _pgaRightVolumeSet = WMBit(_REG_RIGHT_INPUT_VOLUME, 8) @property def pgaLeftVolume(self) -> int: @@ -453,8 +454,8 @@ def pgaVolumeDb(self, value:float) -> None: ## Zero Cross - pgaLeftZeroCross = WMBit(_REG_LEFT_INPUT_VOLUME, 6, False) - pgaRightZeroCross = WMBit(_REG_RIGHT_INPUT_VOLUME, 6, False) + pgaLeftZeroCross = WMBit(_REG_LEFT_INPUT_VOLUME, 6) + pgaRightZeroCross = WMBit(_REG_RIGHT_INPUT_VOLUME, 6) @property def pgaZeroCross(self) -> bool: @@ -465,8 +466,8 @@ def pgaZeroCross(self, value:bool) -> None: ## Mute - _pgaLeftMute = WMBit(_REG_LEFT_INPUT_VOLUME, 7, True) - _pgaRightMute = WMBit(_REG_RIGHT_INPUT_VOLUME, 7, True) + _pgaLeftMute = WMBit(_REG_LEFT_INPUT_VOLUME, 7) + _pgaRightMute = WMBit(_REG_RIGHT_INPUT_VOLUME, 7) @property def pgaLeftMute(self) -> bool: @@ -493,8 +494,8 @@ def pgaMute(self, value:bool) -> None: # Boost Mixer - input2LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 1, 0) - input2RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 1, 0) + input2LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 1) + input2RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 1) @property def input2Boost(self) -> int: @@ -503,13 +504,13 @@ def input2Boost(self) -> int: def input2Boost(self, value:int) -> None: self.input2LeftBoost = self.input2RightBoost = value - input3LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 4, 0) - input3RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 4, 0) + input3LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 4) + input3RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 4) # Mic Bias - micBias = WMBit(_REG_PWR_MGMT_1, 1, False) - micBiasVoltage = WMBit(_REG_ADDITIONAL_CONTROL_4, 0, False) + micBias = WMBit(_REG_PWR_MGMT_1, 1) + micBiasVoltage = WMBit(_REG_ADDITIONAL_CONTROL_4, 0) @property def input3Boost(self) -> int: @@ -520,8 +521,8 @@ def input3Boost(self, value:int) -> None: # ADC - adcLeftEnabled = WMBit(_REG_PWR_MGMT_1, 3, False) - adcRightEnabled = WMBit(_REG_PWR_MGMT_1, 2, False) + adcLeftEnabled = WMBit(_REG_PWR_MGMT_1, 3) + adcRightEnabled = WMBit(_REG_PWR_MGMT_1, 2) @property def adcEnabled(self) -> bool: @@ -532,8 +533,8 @@ def adcEnabled(self, value:bool) -> None: ## Volume - _adcLeftVolume = WMBits(8, _REG_LEFT_ADC_VOLUME, 0, 0b11000011) - _adcLeftVolumeSet = WMBit(_REG_LEFT_ADC_VOLUME, 8, False) + _adcLeftVolume = WMBits(8, _REG_LEFT_ADC_VOLUME, 0) + _adcLeftVolumeSet = WMBit(_REG_LEFT_ADC_VOLUME, 8) @property def adcLeftVolume(self) -> int: @@ -550,8 +551,8 @@ def adcLeftVolumeDb(self) -> float: def adcLeftVolumeDb(self, value:float) -> None: self.adcLeftVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - _adcRightVolume = WMBits(8, _REG_RIGHT_ADC_VOLUME, 0, 0b11000011) - _adcRightVolumeSet = WMBit(_REG_RIGHT_ADC_VOLUME, 8, False) + _adcRightVolume = WMBits(8, _REG_RIGHT_ADC_VOLUME, 0) + _adcRightVolumeSet = WMBit(_REG_RIGHT_ADC_VOLUME, 8) @property def adcRightVolume(self) -> int: @@ -584,8 +585,8 @@ def adcVolume(self, value:float) -> None: # ALC - alcLeftEnabled = WMBit(_REG_ALC1, 7, False) - alcRightEnabled = WMBit(_REG_ALC1, 8, False) + alcLeftEnabled = WMBit(_REG_ALC1, 7) + alcRightEnabled = WMBit(_REG_ALC1, 8) @property def alcEnabled(self) -> bool: @@ -594,7 +595,7 @@ def alcEnabled(self) -> bool: def alcEnabled(self, value:bool) -> None: self.alcLeftEnabled = self.alcRightEnabled = value - alcTarget = WMBits(4, _REG_ALC1, 0, 0b1011) + alcTarget = WMBits(4, _REG_ALC1, 0) @property def alcTargetDb(self) -> float: @@ -603,7 +604,7 @@ def alcTargetDb(self) -> float: def alcTargetDb(self, value:float) -> None: self.alcTarget = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) - alcMaxGain = WMBits(3, _REG_ALC1, 4, 0) + alcMaxGain = WMBits(3, _REG_ALC1, 4) @property def alcMaxGainDb(self) -> float: @@ -612,7 +613,7 @@ def alcMaxGainDb(self) -> float: def alcMaxGainDb(self, value:float) -> None: self.alcMaxGain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) - alcMinGain = WMBits(3, _REG_ALC2, 4, 0) + alcMinGain = WMBits(3, _REG_ALC2, 4) @property def alcMinGainDb(self) -> float: @@ -621,7 +622,7 @@ def alcMinGainDb(self) -> float: def alcMinGainDb(self, value:float) -> None: self.alcMinGain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) - _alcAttack = WMBits(4, _REG_ALC3, 0, 0b0011) + _alcAttack = WMBits(4, _REG_ALC3, 0) @property def alcAttack(self) -> int: @@ -637,7 +638,7 @@ def alcAttackTime(self) -> float: def alcAttackTime(self, value:float) -> None: self.alcAttack = round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)) - _alcDecay = WMBits(4, _REG_ALC3, 4, 0b0011) + _alcDecay = WMBits(4, _REG_ALC3, 4) @property def alcDecay(self) -> int: @@ -653,7 +654,7 @@ def alcDecayTime(self) -> float: def alcDecayTime(self, value:float) -> None: self.alcDecay = round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)) - alcHold = WMBits(4, _REG_ALC2, 0, 0) + alcHold = WMBits(4, _REG_ALC2, 0) @property def alcHoldTime(self) -> float: @@ -667,12 +668,12 @@ def alcHoldTime(self, value:float) -> None: else: self.alcHold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) - alcLimiter = WMBit(_REG_ALC3, 8, False) + alcLimiter = WMBit(_REG_ALC3, 8) # Noise Gate - noiseGateEnabled = WMBit(_REG_NOISE_GATE, 0, False) - noiseGateThreshold = WMBits(5, _REG_NOISE_GATE, 3, 0) + noiseGateEnabled = WMBit(_REG_NOISE_GATE, 0) + noiseGateThreshold = WMBits(5, _REG_NOISE_GATE, 3) @property def noiseGateThresholdDb(self) -> float: @@ -683,8 +684,8 @@ def noiseGateThresholdDb(self, value:float) -> None: # DAC - dacLeftEnabled = WMBit(_REG_PWR_MGMT_2, 8, False) - dacRightEnabled = WMBit(_REG_PWR_MGMT_2, 7, False) + dacLeftEnabled = WMBit(_REG_PWR_MGMT_2, 8) + dacRightEnabled = WMBit(_REG_PWR_MGMT_2, 7) @property def dacEnabled(self) -> bool: @@ -693,8 +694,8 @@ def dacEnabled(self) -> bool: def dacEnabled(self, value:bool) -> None: self.dacLeftEnabled = self.dacRightEnabled = value - _dacLeftVolume = WMBits(8, _REG_LEFT_DAC_VOLUME, 0, 0b11111111) - _dacLeftVolumeSet = WMBit(_REG_LEFT_DAC_VOLUME, 8, False) + _dacLeftVolume = WMBits(8, _REG_LEFT_DAC_VOLUME, 0) + _dacLeftVolumeSet = WMBit(_REG_LEFT_DAC_VOLUME, 8) @property def dacLeftVolume(self) -> int: @@ -711,8 +712,8 @@ def dacLeftVolumeDb(self) -> float: def dacLeftVolumeDb(self, value:float) -> None: self.dacLeftVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - _dacRightVolume = WMBits(8, _REG_RIGHT_DAC_VOLUME, 0, 0b11111111) - _dacRightVolumeSet = WMBit(_REG_RIGHT_DAC_VOLUME, 8, False) + _dacRightVolume = WMBits(8, _REG_RIGHT_DAC_VOLUME, 0) + _dacRightVolumeSet = WMBit(_REG_RIGHT_DAC_VOLUME, 8) @property def dacRightVolume(self) -> int: @@ -743,22 +744,22 @@ def dacVolumeDb(self) -> int: def dacVolume(self, value:float) -> None: self.dacLeftVolume = self.dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - dacMute = WMBit(_REG_ADC_DAC_CTRL_1, 3, True) - dacSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 3, False) - dacSlowSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 2, 1) - dacAttenuation = WMBit(_REG_ADC_DAC_CTRL_1, 7, False) + dacMute = WMBit(_REG_ADC_DAC_CTRL_1, 3) + dacSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 3) + dacSlowSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 2) + dacAttenuation = WMBit(_REG_ADC_DAC_CTRL_1, 7) # 3D Enhance - enhanceEnabled = WMBit(_REG_3D_CONTROL, 0, 1) - enhanceDepth = WMBits(4, _REG_3D_CONTROL, 1, 0) - enhanceFilterLPF = WMBit(_REG_3D_CONTROL, 6, False) - enhanceFilterHPF = WMBit(_REG_3D_CONTROL, 5, False) + enhanceEnabled = WMBit(_REG_3D_CONTROL, 0) + enhanceDepth = WMBits(4, _REG_3D_CONTROL, 1) + enhanceFilterLPF = WMBit(_REG_3D_CONTROL, 6) + enhanceFilterHPF = WMBit(_REG_3D_CONTROL, 5) # Output Mixer - leftOutputEnabled = WMBit(_REG_PWR_MGMT_3, 3, False) - rightOutputEnabled = WMBit(_REG_PWR_MGMT_3, 2, False) + leftOutputEnabled = WMBit(_REG_PWR_MGMT_3, 3) + rightOutputEnabled = WMBit(_REG_PWR_MGMT_3, 2) @property def stereoOutputEnabled(self) -> bool: @@ -769,8 +770,8 @@ def stereoOutputEnabled(self, value:bool): ## DAC Output - dacLeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 8, False) - dacRightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 8, False) + dacLeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 8) + dacRightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 8) @property def dacOutputEnabled(self) -> bool: @@ -781,8 +782,8 @@ def dacOutputEnabled(self, value:bool) -> None: ## Input 3 Output - input3LeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 7, False) - input3RightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 7, False) + input3LeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 7) + input3RightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 7) @property def input3OutputEnabled(self) -> bool: @@ -791,7 +792,7 @@ def input3OutputEnabled(self) -> bool: def input3OutputEnabled(self, value:bool) -> None: self.input3LeftOutputEnabled = self.input3RightOutputEnabled = value - input3LeftOutputVolume = WMBits(3, _REG_LEFT_OUT_MIX, 4, 0b101) + input3LeftOutputVolume = WMBits(3, _REG_LEFT_OUT_MIX, 4) @property def input3LeftOutputVolumeDb(self) -> float: @@ -800,7 +801,7 @@ def input3LeftOutputVolumeDb(self) -> float: def input3LeftOutputVolumeDb(self, value:float) -> None: self.input3LeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - input3RightOutputVolume = WMBits(3, _REG_RIGHT_OUT_MIX, 4, 0b101) + input3RightOutputVolume = WMBits(3, _REG_RIGHT_OUT_MIX, 4) @property def input3RightOutputVolumeDb(self) -> float: @@ -825,14 +826,14 @@ def input3OutputVolumeDb(self, value:float) -> None: ## PGA Boost Mixer Output - pgaBoostLeftOutputEnabled = WMBit(_REG_BYPASS_1, 7, False) - pgaBoostRightOutputEnabled = WMBit(_REG_BYPASS_2, 7, False) + pgaBoostLeftOutputEnabled = WMBit(_REG_BYPASS_1, 7) + pgaBoostRightOutputEnabled = WMBit(_REG_BYPASS_2, 7) @property def pgaBoostOutputEnabled(self) -> bool: return self.pgaBoostLeftOutputEnabled and self.pgaBoostRightOutputEnabled - pgaBoostLeftOutputVolume = WMBits(3, _REG_BYPASS_1, 4, 0b101) + pgaBoostLeftOutputVolume = WMBits(3, _REG_BYPASS_1, 4) @property def pgaBoostLeftOutputVolumeDb(self) -> float: @@ -841,7 +842,7 @@ def pgaBoostLeftOutputVolumeDb(self) -> float: def pgaBoostLeftOutputVolumeDb(self, value:float) -> None: self.pgaBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - pgaBoostRightOutputVolume = WMBits(3, _REG_BYPASS_2, 4, 0b101) + pgaBoostRightOutputVolume = WMBits(3, _REG_BYPASS_2, 4) @property def pgaBoostRightOutputVolumeDb(self) -> float: @@ -866,10 +867,10 @@ def pgaBoostOutputVolumeDb(self, value:float) -> None: ## Mono Output - monoOutputEnabled = WMBit(_REG_PWR_MGMT_2, 1, False) + monoOutputEnabled = WMBit(_REG_PWR_MGMT_2, 1) - monoLeftMixEnabled = WMBit(_REG_MONO_OUT_MIX_1, 7, False) - monoRightMixEnabled = WMBit(_REG_MONO_OUT_MIX_2, 7, False) + monoLeftMixEnabled = WMBit(_REG_MONO_OUT_MIX_1, 7) + monoRightMixEnabled = WMBit(_REG_MONO_OUT_MIX_2, 7) @property def monoMixEnabled(self) -> bool: @@ -878,16 +879,16 @@ def monoMixEnabled(self) -> bool: def monoMixEnabled(self, value:bool) -> None: self.monoLeftMixEnabled = self.monoRightMixEnabled = value - monoOutputAttenuation = WMBit(_REG_MONO_OUT_VOLUME, 6, True) + monoOutputAttenuation = WMBit(_REG_MONO_OUT_VOLUME, 6) - vmid = WMBits(2, _REG_PWR_MGMT_1, 7, 0) + vmid = WMBits(2, _REG_PWR_MGMT_1, 7) # Amplifier ## Headphones - leftHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 5, False) - rightHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 6, False) + leftHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 5) + rightHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 6) @property def headphoneEnabled(self) -> bool: @@ -896,10 +897,10 @@ def headphoneEnabled(self) -> bool: def headphoneEnabled(self, value:bool) -> None: self.leftHeadphoneEnabled = self.rightHeadphoneEnabled = value - headphoneStandby = WMBit(_REG_ANTI_POP_1, 0, False) + headphoneStandby = WMBit(_REG_ANTI_POP_1, 0) - _leftHeadphoneVolume = WMBits(7, _REG_LOUT1_VOLUME, 0, 0) - _leftHeadphoneVolumeSet = WMBit(_REG_LOUT1_VOLUME, 8, False) + _leftHeadphoneVolume = WMBits(7, _REG_LOUT1_VOLUME, 0) + _leftHeadphoneVolumeSet = WMBit(_REG_LOUT1_VOLUME, 8) @property def leftHeadphoneVolume(self) -> int: @@ -916,8 +917,8 @@ def leftHeadphoneVolumeDb(self) -> float: def leftHeadphoneVolumeDb(self, value:float) -> None: self.leftHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - _rightHeadphoneVolume = WMBits(7, _REG_ROUT1_VOLUME, 0, 0) - _rightHeadphoneVolumeSet = WMBit(_REG_ROUT1_VOLUME, 8, False) + _rightHeadphoneVolume = WMBits(7, _REG_ROUT1_VOLUME, 0) + _rightHeadphoneVolumeSet = WMBit(_REG_ROUT1_VOLUME, 8) @property def rightHeadphoneVolume(self) -> int: @@ -948,8 +949,8 @@ def headphoneVolumeDb(self) -> int: def headphoneVolumeDb(self, value:float) -> None: self.leftHeadphoneVolume = self.rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) - leftHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7, False) - rightHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7, False) + leftHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7) + rightHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7) @property def headphoneZeroCross(self) -> bool: @@ -960,8 +961,8 @@ def headphoneZeroCross(self, value:bool) -> None: ## Speakers - _leftSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 4, False) - _leftSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 6, False) + _leftSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 4) + _leftSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 6) @property def leftSpeakerEnabled(self) -> bool: @@ -970,8 +971,8 @@ def leftSpeakerEnabled(self) -> bool: def leftSpeakerEnabled(self, value:bool) -> None: self._leftSpeakerEnabled = self._leftSpeakerAmpEnabled = value - _rightSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 3, False) - _rightSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 7, False) + _rightSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 3) + _rightSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 7) @property def rightSpeakerEnabled(self) -> bool: @@ -987,8 +988,8 @@ def speakerEnabled(self) -> bool: def speakerEnabled(self, value:bool) -> None: self.leftSpeakerEnabled = self.rightSpeakerEnabled = value - _leftSpeakerVolume = WMBits(7, _REG_LOUT2_VOLUME, 0, 0) - _leftSpeakerVolumeSet = WMBit(_REG_LOUT2_VOLUME, 8, False) + _leftSpeakerVolume = WMBits(7, _REG_LOUT2_VOLUME, 0) + _leftSpeakerVolumeSet = WMBit(_REG_LOUT2_VOLUME, 8) @property def leftSpeakerVolume(self) -> int: @@ -1005,8 +1006,8 @@ def leftSpeakerVolumeDb(self) -> float: def leftSpeakerVolumeDb(self, value:float) -> None: self.leftSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - _rightSpeakerVolume = WMBits(7, _REG_ROUT2_VOLUME, 0, 0) - _rightSpeakerVolumeSet = WMBit(_REG_ROUT2_VOLUME, 8, False) + _rightSpeakerVolume = WMBits(7, _REG_ROUT2_VOLUME, 0) + _rightSpeakerVolumeSet = WMBit(_REG_ROUT2_VOLUME, 8) @property def rightSpeakerVolume(self) -> int: @@ -1037,8 +1038,8 @@ def speakerVolumeDb(self) -> int: def speakerVolume(self, value:float) -> None: self.leftSpeakerVolume = self.rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) - leftSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7, False) - rightSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7, False) + leftSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7) + rightSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7) @property def speakerZeroCross(self) -> bool: @@ -1047,7 +1048,7 @@ def speakerZeroCross(self) -> bool: def speakerZeroCross(self, value:bool) -> None: self.leftSpeakerZeroCross = self.rightSpeakerZeroCross = value - _speakerDcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 3, 0) + _speakerDcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 3) @property def speakerDcGain(self) -> int: @@ -1056,7 +1057,7 @@ def speakerDcGain(self) -> int: def speakerDcGain(self, value:int) -> None: self._speakerDcGain = min(value, 5) - _speakerAcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 0, 0) + _speakerAcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 0) @property def speakerAcGain(self) -> int: @@ -1067,15 +1068,15 @@ def speakerAcGain(self, value:int) -> None: # Digital Audio Interface Control - loopback = WMBit(_REG_AUDIO_INTERFACE_2, 0, False) + loopback = WMBit(_REG_AUDIO_INTERFACE_2, 0) - pll = WMBit(_REG_PWR_MGMT_2, 0, False) - pllPrescaleDiv2 = WMBit(_REG_PWR_MGMT_2, 4, False) - pllN = WMBits(4, _REG_PLL_N, 0, 0x8) + pll = WMBit(_REG_PWR_MGMT_2, 0) + pllPrescaleDiv2 = WMBit(_REG_PWR_MGMT_2, 4) + pllN = WMBits(4, _REG_PLL_N, 0) - _pllK1 = WMBits(6, _REG_PLL_K_1, 0, 0x31) - _pllK2 = WMBits(9, _REG_PLL_K_2, 0, 0x26) - _pllK3 = WMBits(9, _REG_PLL_K_3, 0, 0xE9) + _pllK1 = WMBits(6, _REG_PLL_K_1, 0) + _pllK2 = WMBits(9, _REG_PLL_K_2, 0) + _pllK3 = WMBits(9, _REG_PLL_K_3, 0) @property def pllK(self) -> int: @@ -1086,11 +1087,11 @@ def pllK(self, value:int) -> None: self._pllK2 = (value >> 9) & 0b111111111 self._pllK3 = value & 0b111111111 - clockFractionalMode = WMBit(_REG_PLL_N, 5, False) + clockFractionalMode = WMBit(_REG_PLL_N, 5) - clockFromPLL = WMBit(_REG_CLOCKING_1, 0, False) + clockFromPLL = WMBit(_REG_CLOCKING_1, 0) - _systemClockDivider = WMBits(2, _REG_CLOCKING_1, 1, 0) + _systemClockDivider = WMBits(2, _REG_CLOCKING_1, 1) @property def systemClockDiv2(self) -> bool: @@ -1099,7 +1100,7 @@ def systemClockDiv2(self) -> bool: def systemClockDiv2(self, value:bool) -> None: self._systemClockDivider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 - _adcClockDivider = WMBits(3, _REG_CLOCKING_1, 6, 0) + _adcClockDivider = WMBits(3, _REG_CLOCKING_1, 6) @property def adcClockDivider(self) -> int: @@ -1110,7 +1111,7 @@ def adcClockDivider(self, value:int) -> None: if value in _ADCDACDIV: self._adcClockDivider = _ADCDACDIV.index(value) - _dacClockDivider = WMBits(3, _REG_CLOCKING_1, 3, 0) + _dacClockDivider = WMBits(3, _REG_CLOCKING_1, 3) @property def dacClockDivider(self) -> int: @@ -1121,7 +1122,7 @@ def dacClockDivider(self, value:int) -> None: if value in _ADCDACDIV: self._dacClockDivider = _ADCDACDIV.index(value) - _baseClockDivider = WMBits(4, _REG_CLOCKING_2, 0, 0) + _baseClockDivider = WMBits(4, _REG_CLOCKING_2, 0) @property def baseClockDivider(self) -> float: @@ -1133,7 +1134,7 @@ def baseClockDivider(self, value:float) -> None: self._baseClockDivider = _BCLKDIV.index(value) - _ampClockDivider = WMBits(3, _REG_CLOCKING_2, 6, 0b111) + _ampClockDivider = WMBits(3, _REG_CLOCKING_2, 6) @property def ampClockDivider(self) -> float: @@ -1146,9 +1147,9 @@ def ampClockDivider(self, value:float) -> None: ## Mode - masterMode = WMBit(_REG_AUDIO_INTERFACE_1, 6, False) + masterMode = WMBit(_REG_AUDIO_INTERFACE_1, 6) - _wordLength = WMBits(2, _REG_AUDIO_INTERFACE_1, 2, 0b10) + _wordLength = WMBits(2, _REG_AUDIO_INTERFACE_1, 2) @property def wordLength(self) -> int: @@ -1161,13 +1162,13 @@ def wordLength(self) -> int: def wordLength(self, value:int) -> None: self._wordLength = (min(value, 28) - 16) // 4 - wordSelectInverted = WMBit(_REG_AUDIO_INTERFACE_1, 4, False) + wordSelectInverted = WMBit(_REG_AUDIO_INTERFACE_1, 4) - adcChannelSwap = WMBit(_REG_AUDIO_INTERFACE_1, 8, False) + adcChannelSwap = WMBit(_REG_AUDIO_INTERFACE_1, 8) - vrefOutputDisabled = WMBit(_REG_ADDITIONAL_CONTROL_3, 6, False) + vrefOutputDisabled = WMBit(_REG_ADDITIONAL_CONTROL_3, 6) - _vsel = WMBits(2, _REG_ADDITIONAL_CONTROL_1, 6, 0b11) + _vsel = WMBits(2, _REG_ADDITIONAL_CONTROL_1, 6) @property def powerSupply(self) -> float: @@ -1178,11 +1179,11 @@ def powerSupply(self, value:float) -> None: ## GPIO - gpioOutput = WMBit(_REG_AUDIO_INTERFACE_2, 6, False) - gpioOutputMode = WMBits(3, _REG_ADDITIONAL_CONTROL_4, 4, 0) - gpioOutputInverted = WMBit(_REG_ADDITIONAL_CONTROL_4, 7, False) + gpioOutput = WMBit(_REG_AUDIO_INTERFACE_2, 6) + gpioOutputMode = WMBits(3, _REG_ADDITIONAL_CONTROL_4, 4) + gpioOutputInverted = WMBit(_REG_ADDITIONAL_CONTROL_4, 7) - _gpioClockDivider = WMBits(3, _REG_CLOCKING_2, 6, 0) + _gpioClockDivider = WMBits(3, _REG_CLOCKING_2, 6) @property def gpioClockDivider(self) -> int: @@ -1298,7 +1299,7 @@ def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR) -> None: self.vmid = VMIDSEL_PLAYBACK # Resets all registers to their default state - _reset = WMBit(_REG_RESET, 7, False) + _reset = WMBit(_REG_RESET, 7) def reset(self) -> None: self._reset = True for name in dir(self): From 5eabc9566902074083602c7ff5957fb00dec3257 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 13:05:11 -0500 Subject: [PATCH 27/56] Rename WMBit(s) to WOBit(s). --- adafruit_wm8960.py | 252 ++++++++++++++++++++++----------------------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index b921b64..64d024e 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -222,7 +222,7 @@ 6.0 ] -class WMBit: +class WOBit: def __init__( self, @@ -257,7 +257,7 @@ def __set__(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: with obj.i2c_device as i2c: i2c.write(obj._registers[self.register_address]) -class WMBits: +class WOBits: def __init__( # pylint: disable=too-many-arguments self, @@ -308,10 +308,10 @@ class WM8960: # Power - vref = WMBit(_REG_PWR_MGMT_1, 6) + vref = WOBit(_REG_PWR_MGMT_1, 6) - analogInputLeftEnabled = WMBit(_REG_PWR_MGMT_1, 5) - analogInputRightEnabled = WMBit(_REG_PWR_MGMT_1, 4) + analogInputLeftEnabled = WOBit(_REG_PWR_MGMT_1, 5) + analogInputRightEnabled = WOBit(_REG_PWR_MGMT_1, 4) @property def analogInputEnabled(self) -> bool: @@ -324,8 +324,8 @@ def analogInputEnabled(self, value:bool) -> None: ## PWR_MGMT - pgaLeftEnabled = WMBit(_REG_PWR_MGMT_3, 5) - pgaRightEnabled = WMBit(_REG_PWR_MGMT_3, 4) + pgaLeftEnabled = WOBit(_REG_PWR_MGMT_3, 5) + pgaRightEnabled = WOBit(_REG_PWR_MGMT_3, 4) @property def pgaEnabled(self) -> bool: @@ -336,13 +336,13 @@ def pgaEnabled(self, value:bool) -> None: ## SIGNAL_PATH - _pgaInput1Left = WMBit(_REG_ADCL_SIGNAL_PATH, 8) - _pgaInput2Left = WMBit(_REG_ADCL_SIGNAL_PATH, 6) - _pgaInput3Left = WMBit(_REG_ADCL_SIGNAL_PATH, 7) + _pgaInput1Left = WOBit(_REG_ADCL_SIGNAL_PATH, 8) + _pgaInput2Left = WOBit(_REG_ADCL_SIGNAL_PATH, 6) + _pgaInput3Left = WOBit(_REG_ADCL_SIGNAL_PATH, 7) - _pgaInput1Right = WMBit(_REG_ADCR_SIGNAL_PATH, 8) - _pgaInput2Right = WMBit(_REG_ADCR_SIGNAL_PATH, 6) - _pgaInput3Right = WMBit(_REG_ADCR_SIGNAL_PATH, 7) + _pgaInput1Right = WOBit(_REG_ADCR_SIGNAL_PATH, 8) + _pgaInput2Right = WOBit(_REG_ADCR_SIGNAL_PATH, 6) + _pgaInput3Right = WOBit(_REG_ADCR_SIGNAL_PATH, 7) @property def pgaNonInvSignalLeft(self) -> int: @@ -380,8 +380,8 @@ def pgaNonInvSignal(self, signal:int) -> None: ## Boost - pgaLeftBoostEnabled = WMBit(_REG_ADCL_SIGNAL_PATH, 3) - pgaRightBoostEnabled = WMBit(_REG_ADCR_SIGNAL_PATH, 3) + pgaLeftBoostEnabled = WOBit(_REG_ADCL_SIGNAL_PATH, 3) + pgaRightBoostEnabled = WOBit(_REG_ADCR_SIGNAL_PATH, 3) @property def pgaBoostEnabled(self) -> None: @@ -390,8 +390,8 @@ def pgaBoostEnabled(self) -> None: def pgaBoostEnabled(self, value:int) -> None: self.pgaLeftBoostEnabled = self.pgaRightBoostEnabled = value - pgaBoostGainLeft = WMBits(2, _REG_ADCL_SIGNAL_PATH, 4) - pgaBoostGainRight = WMBits(2, _REG_ADCR_SIGNAL_PATH, 4) + pgaBoostGainLeft = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) + pgaBoostGainRight = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) @property def pgaBoostGain(self) -> None: @@ -402,11 +402,11 @@ def pgaBoostGain(self, value:int) -> None: ## Volume - _pgaLeftVolume = WMBits(6, _REG_LEFT_INPUT_VOLUME, 0) - _pgaLeftVolumeSet = WMBit(_REG_LEFT_INPUT_VOLUME, 8) + _pgaLeftVolume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) + _pgaLeftVolumeSet = WOBit(_REG_LEFT_INPUT_VOLUME, 8) - _pgaRightVolume = WMBits(6, _REG_RIGHT_INPUT_VOLUME, 0) - _pgaRightVolumeSet = WMBit(_REG_RIGHT_INPUT_VOLUME, 8) + _pgaRightVolume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) + _pgaRightVolumeSet = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) @property def pgaLeftVolume(self) -> int: @@ -454,8 +454,8 @@ def pgaVolumeDb(self, value:float) -> None: ## Zero Cross - pgaLeftZeroCross = WMBit(_REG_LEFT_INPUT_VOLUME, 6) - pgaRightZeroCross = WMBit(_REG_RIGHT_INPUT_VOLUME, 6) + pgaLeftZeroCross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) + pgaRightZeroCross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) @property def pgaZeroCross(self) -> bool: @@ -466,8 +466,8 @@ def pgaZeroCross(self, value:bool) -> None: ## Mute - _pgaLeftMute = WMBit(_REG_LEFT_INPUT_VOLUME, 7) - _pgaRightMute = WMBit(_REG_RIGHT_INPUT_VOLUME, 7) + _pgaLeftMute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) + _pgaRightMute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) @property def pgaLeftMute(self) -> bool: @@ -494,8 +494,8 @@ def pgaMute(self, value:bool) -> None: # Boost Mixer - input2LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 1) - input2RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 1) + input2LeftBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) + input2RightBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) @property def input2Boost(self) -> int: @@ -504,13 +504,13 @@ def input2Boost(self) -> int: def input2Boost(self, value:int) -> None: self.input2LeftBoost = self.input2RightBoost = value - input3LeftBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_1, 4) - input3RightBoost = WMBits(3, _REG_INPUT_BOOST_MIXER_2, 4) + input3LeftBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) + input3RightBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) # Mic Bias - micBias = WMBit(_REG_PWR_MGMT_1, 1) - micBiasVoltage = WMBit(_REG_ADDITIONAL_CONTROL_4, 0) + micBias = WOBit(_REG_PWR_MGMT_1, 1) + micBiasVoltage = WOBit(_REG_ADDITIONAL_CONTROL_4, 0) @property def input3Boost(self) -> int: @@ -521,8 +521,8 @@ def input3Boost(self, value:int) -> None: # ADC - adcLeftEnabled = WMBit(_REG_PWR_MGMT_1, 3) - adcRightEnabled = WMBit(_REG_PWR_MGMT_1, 2) + adcLeftEnabled = WOBit(_REG_PWR_MGMT_1, 3) + adcRightEnabled = WOBit(_REG_PWR_MGMT_1, 2) @property def adcEnabled(self) -> bool: @@ -533,8 +533,8 @@ def adcEnabled(self, value:bool) -> None: ## Volume - _adcLeftVolume = WMBits(8, _REG_LEFT_ADC_VOLUME, 0) - _adcLeftVolumeSet = WMBit(_REG_LEFT_ADC_VOLUME, 8) + _adcLeftVolume = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) + _adcLeftVolumeSet = WOBit(_REG_LEFT_ADC_VOLUME, 8) @property def adcLeftVolume(self) -> int: @@ -551,8 +551,8 @@ def adcLeftVolumeDb(self) -> float: def adcLeftVolumeDb(self, value:float) -> None: self.adcLeftVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - _adcRightVolume = WMBits(8, _REG_RIGHT_ADC_VOLUME, 0) - _adcRightVolumeSet = WMBit(_REG_RIGHT_ADC_VOLUME, 8) + _adcRightVolume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) + _adcRightVolumeSet = WOBit(_REG_RIGHT_ADC_VOLUME, 8) @property def adcRightVolume(self) -> int: @@ -585,8 +585,8 @@ def adcVolume(self, value:float) -> None: # ALC - alcLeftEnabled = WMBit(_REG_ALC1, 7) - alcRightEnabled = WMBit(_REG_ALC1, 8) + alcLeftEnabled = WOBit(_REG_ALC1, 7) + alcRightEnabled = WOBit(_REG_ALC1, 8) @property def alcEnabled(self) -> bool: @@ -595,7 +595,7 @@ def alcEnabled(self) -> bool: def alcEnabled(self, value:bool) -> None: self.alcLeftEnabled = self.alcRightEnabled = value - alcTarget = WMBits(4, _REG_ALC1, 0) + alcTarget = WOBits(4, _REG_ALC1, 0) @property def alcTargetDb(self) -> float: @@ -604,7 +604,7 @@ def alcTargetDb(self) -> float: def alcTargetDb(self, value:float) -> None: self.alcTarget = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) - alcMaxGain = WMBits(3, _REG_ALC1, 4) + alcMaxGain = WOBits(3, _REG_ALC1, 4) @property def alcMaxGainDb(self) -> float: @@ -613,7 +613,7 @@ def alcMaxGainDb(self) -> float: def alcMaxGainDb(self, value:float) -> None: self.alcMaxGain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) - alcMinGain = WMBits(3, _REG_ALC2, 4) + alcMinGain = WOBits(3, _REG_ALC2, 4) @property def alcMinGainDb(self) -> float: @@ -622,7 +622,7 @@ def alcMinGainDb(self) -> float: def alcMinGainDb(self, value:float) -> None: self.alcMinGain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) - _alcAttack = WMBits(4, _REG_ALC3, 0) + _alcAttack = WOBits(4, _REG_ALC3, 0) @property def alcAttack(self) -> int: @@ -638,7 +638,7 @@ def alcAttackTime(self) -> float: def alcAttackTime(self, value:float) -> None: self.alcAttack = round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)) - _alcDecay = WMBits(4, _REG_ALC3, 4) + _alcDecay = WOBits(4, _REG_ALC3, 4) @property def alcDecay(self) -> int: @@ -654,7 +654,7 @@ def alcDecayTime(self) -> float: def alcDecayTime(self, value:float) -> None: self.alcDecay = round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)) - alcHold = WMBits(4, _REG_ALC2, 0) + alcHold = WOBits(4, _REG_ALC2, 0) @property def alcHoldTime(self) -> float: @@ -668,12 +668,12 @@ def alcHoldTime(self, value:float) -> None: else: self.alcHold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) - alcLimiter = WMBit(_REG_ALC3, 8) + alcLimiter = WOBit(_REG_ALC3, 8) # Noise Gate - noiseGateEnabled = WMBit(_REG_NOISE_GATE, 0) - noiseGateThreshold = WMBits(5, _REG_NOISE_GATE, 3) + noiseGateEnabled = WOBit(_REG_NOISE_GATE, 0) + noiseGateThreshold = WOBits(5, _REG_NOISE_GATE, 3) @property def noiseGateThresholdDb(self) -> float: @@ -684,8 +684,8 @@ def noiseGateThresholdDb(self, value:float) -> None: # DAC - dacLeftEnabled = WMBit(_REG_PWR_MGMT_2, 8) - dacRightEnabled = WMBit(_REG_PWR_MGMT_2, 7) + dacLeftEnabled = WOBit(_REG_PWR_MGMT_2, 8) + dacRightEnabled = WOBit(_REG_PWR_MGMT_2, 7) @property def dacEnabled(self) -> bool: @@ -694,8 +694,8 @@ def dacEnabled(self) -> bool: def dacEnabled(self, value:bool) -> None: self.dacLeftEnabled = self.dacRightEnabled = value - _dacLeftVolume = WMBits(8, _REG_LEFT_DAC_VOLUME, 0) - _dacLeftVolumeSet = WMBit(_REG_LEFT_DAC_VOLUME, 8) + _dacLeftVolume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) + _dacLeftVolumeSet = WOBit(_REG_LEFT_DAC_VOLUME, 8) @property def dacLeftVolume(self) -> int: @@ -712,8 +712,8 @@ def dacLeftVolumeDb(self) -> float: def dacLeftVolumeDb(self, value:float) -> None: self.dacLeftVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - _dacRightVolume = WMBits(8, _REG_RIGHT_DAC_VOLUME, 0) - _dacRightVolumeSet = WMBit(_REG_RIGHT_DAC_VOLUME, 8) + _dacRightVolume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) + _dacRightVolumeSet = WOBit(_REG_RIGHT_DAC_VOLUME, 8) @property def dacRightVolume(self) -> int: @@ -744,22 +744,22 @@ def dacVolumeDb(self) -> int: def dacVolume(self, value:float) -> None: self.dacLeftVolume = self.dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - dacMute = WMBit(_REG_ADC_DAC_CTRL_1, 3) - dacSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 3) - dacSlowSoftMute = WMBit(_REG_ADC_DAC_CTRL_2, 2) - dacAttenuation = WMBit(_REG_ADC_DAC_CTRL_1, 7) + dacMute = WOBit(_REG_ADC_DAC_CTRL_1, 3) + dacSoftMute = WOBit(_REG_ADC_DAC_CTRL_2, 3) + dacSlowSoftMute = WOBit(_REG_ADC_DAC_CTRL_2, 2) + dacAttenuation = WOBit(_REG_ADC_DAC_CTRL_1, 7) # 3D Enhance - enhanceEnabled = WMBit(_REG_3D_CONTROL, 0) - enhanceDepth = WMBits(4, _REG_3D_CONTROL, 1) - enhanceFilterLPF = WMBit(_REG_3D_CONTROL, 6) - enhanceFilterHPF = WMBit(_REG_3D_CONTROL, 5) + enhanceEnabled = WOBit(_REG_3D_CONTROL, 0) + enhanceDepth = WOBits(4, _REG_3D_CONTROL, 1) + enhanceFilterLPF = WOBit(_REG_3D_CONTROL, 6) + enhanceFilterHPF = WOBit(_REG_3D_CONTROL, 5) # Output Mixer - leftOutputEnabled = WMBit(_REG_PWR_MGMT_3, 3) - rightOutputEnabled = WMBit(_REG_PWR_MGMT_3, 2) + leftOutputEnabled = WOBit(_REG_PWR_MGMT_3, 3) + rightOutputEnabled = WOBit(_REG_PWR_MGMT_3, 2) @property def stereoOutputEnabled(self) -> bool: @@ -770,8 +770,8 @@ def stereoOutputEnabled(self, value:bool): ## DAC Output - dacLeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 8) - dacRightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 8) + dacLeftOutputEnabled = WOBit(_REG_LEFT_OUT_MIX, 8) + dacRightOutputEnabled = WOBit(_REG_RIGHT_OUT_MIX, 8) @property def dacOutputEnabled(self) -> bool: @@ -782,8 +782,8 @@ def dacOutputEnabled(self, value:bool) -> None: ## Input 3 Output - input3LeftOutputEnabled = WMBit(_REG_LEFT_OUT_MIX, 7) - input3RightOutputEnabled = WMBit(_REG_RIGHT_OUT_MIX, 7) + input3LeftOutputEnabled = WOBit(_REG_LEFT_OUT_MIX, 7) + input3RightOutputEnabled = WOBit(_REG_RIGHT_OUT_MIX, 7) @property def input3OutputEnabled(self) -> bool: @@ -792,7 +792,7 @@ def input3OutputEnabled(self) -> bool: def input3OutputEnabled(self, value:bool) -> None: self.input3LeftOutputEnabled = self.input3RightOutputEnabled = value - input3LeftOutputVolume = WMBits(3, _REG_LEFT_OUT_MIX, 4) + input3LeftOutputVolume = WOBits(3, _REG_LEFT_OUT_MIX, 4) @property def input3LeftOutputVolumeDb(self) -> float: @@ -801,7 +801,7 @@ def input3LeftOutputVolumeDb(self) -> float: def input3LeftOutputVolumeDb(self, value:float) -> None: self.input3LeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - input3RightOutputVolume = WMBits(3, _REG_RIGHT_OUT_MIX, 4) + input3RightOutputVolume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) @property def input3RightOutputVolumeDb(self) -> float: @@ -826,14 +826,14 @@ def input3OutputVolumeDb(self, value:float) -> None: ## PGA Boost Mixer Output - pgaBoostLeftOutputEnabled = WMBit(_REG_BYPASS_1, 7) - pgaBoostRightOutputEnabled = WMBit(_REG_BYPASS_2, 7) + pgaBoostLeftOutputEnabled = WOBit(_REG_BYPASS_1, 7) + pgaBoostRightOutputEnabled = WOBit(_REG_BYPASS_2, 7) @property def pgaBoostOutputEnabled(self) -> bool: return self.pgaBoostLeftOutputEnabled and self.pgaBoostRightOutputEnabled - pgaBoostLeftOutputVolume = WMBits(3, _REG_BYPASS_1, 4) + pgaBoostLeftOutputVolume = WOBits(3, _REG_BYPASS_1, 4) @property def pgaBoostLeftOutputVolumeDb(self) -> float: @@ -842,7 +842,7 @@ def pgaBoostLeftOutputVolumeDb(self) -> float: def pgaBoostLeftOutputVolumeDb(self, value:float) -> None: self.pgaBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - pgaBoostRightOutputVolume = WMBits(3, _REG_BYPASS_2, 4) + pgaBoostRightOutputVolume = WOBits(3, _REG_BYPASS_2, 4) @property def pgaBoostRightOutputVolumeDb(self) -> float: @@ -867,10 +867,10 @@ def pgaBoostOutputVolumeDb(self, value:float) -> None: ## Mono Output - monoOutputEnabled = WMBit(_REG_PWR_MGMT_2, 1) + monoOutputEnabled = WOBit(_REG_PWR_MGMT_2, 1) - monoLeftMixEnabled = WMBit(_REG_MONO_OUT_MIX_1, 7) - monoRightMixEnabled = WMBit(_REG_MONO_OUT_MIX_2, 7) + monoLeftMixEnabled = WOBit(_REG_MONO_OUT_MIX_1, 7) + monoRightMixEnabled = WOBit(_REG_MONO_OUT_MIX_2, 7) @property def monoMixEnabled(self) -> bool: @@ -879,16 +879,16 @@ def monoMixEnabled(self) -> bool: def monoMixEnabled(self, value:bool) -> None: self.monoLeftMixEnabled = self.monoRightMixEnabled = value - monoOutputAttenuation = WMBit(_REG_MONO_OUT_VOLUME, 6) + monoOutputAttenuation = WOBit(_REG_MONO_OUT_VOLUME, 6) - vmid = WMBits(2, _REG_PWR_MGMT_1, 7) + vmid = WOBits(2, _REG_PWR_MGMT_1, 7) # Amplifier ## Headphones - leftHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 5) - rightHeadphoneEnabled = WMBit(_REG_PWR_MGMT_2, 6) + leftHeadphoneEnabled = WOBit(_REG_PWR_MGMT_2, 5) + rightHeadphoneEnabled = WOBit(_REG_PWR_MGMT_2, 6) @property def headphoneEnabled(self) -> bool: @@ -897,10 +897,10 @@ def headphoneEnabled(self) -> bool: def headphoneEnabled(self, value:bool) -> None: self.leftHeadphoneEnabled = self.rightHeadphoneEnabled = value - headphoneStandby = WMBit(_REG_ANTI_POP_1, 0) + headphoneStandby = WOBit(_REG_ANTI_POP_1, 0) - _leftHeadphoneVolume = WMBits(7, _REG_LOUT1_VOLUME, 0) - _leftHeadphoneVolumeSet = WMBit(_REG_LOUT1_VOLUME, 8) + _leftHeadphoneVolume = WOBits(7, _REG_LOUT1_VOLUME, 0) + _leftHeadphoneVolumeSet = WOBit(_REG_LOUT1_VOLUME, 8) @property def leftHeadphoneVolume(self) -> int: @@ -917,8 +917,8 @@ def leftHeadphoneVolumeDb(self) -> float: def leftHeadphoneVolumeDb(self, value:float) -> None: self.leftHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - _rightHeadphoneVolume = WMBits(7, _REG_ROUT1_VOLUME, 0) - _rightHeadphoneVolumeSet = WMBit(_REG_ROUT1_VOLUME, 8) + _rightHeadphoneVolume = WOBits(7, _REG_ROUT1_VOLUME, 0) + _rightHeadphoneVolumeSet = WOBit(_REG_ROUT1_VOLUME, 8) @property def rightHeadphoneVolume(self) -> int: @@ -949,8 +949,8 @@ def headphoneVolumeDb(self) -> int: def headphoneVolumeDb(self, value:float) -> None: self.leftHeadphoneVolume = self.rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) - leftHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7) - rightHeadphoneZeroCross = WMBit(_REG_LOUT1_VOLUME, 7) + leftHeadphoneZeroCross = WOBit(_REG_LOUT1_VOLUME, 7) + rightHeadphoneZeroCross = WOBit(_REG_LOUT1_VOLUME, 7) @property def headphoneZeroCross(self) -> bool: @@ -961,8 +961,8 @@ def headphoneZeroCross(self, value:bool) -> None: ## Speakers - _leftSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 4) - _leftSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 6) + _leftSpeakerEnabled = WOBit(_REG_PWR_MGMT_2, 4) + _leftSpeakerAmpEnabled = WOBit(_REG_CLASS_D_CONTROL_1, 6) @property def leftSpeakerEnabled(self) -> bool: @@ -971,8 +971,8 @@ def leftSpeakerEnabled(self) -> bool: def leftSpeakerEnabled(self, value:bool) -> None: self._leftSpeakerEnabled = self._leftSpeakerAmpEnabled = value - _rightSpeakerEnabled = WMBit(_REG_PWR_MGMT_2, 3) - _rightSpeakerAmpEnabled = WMBit(_REG_CLASS_D_CONTROL_1, 7) + _rightSpeakerEnabled = WOBit(_REG_PWR_MGMT_2, 3) + _rightSpeakerAmpEnabled = WOBit(_REG_CLASS_D_CONTROL_1, 7) @property def rightSpeakerEnabled(self) -> bool: @@ -988,8 +988,8 @@ def speakerEnabled(self) -> bool: def speakerEnabled(self, value:bool) -> None: self.leftSpeakerEnabled = self.rightSpeakerEnabled = value - _leftSpeakerVolume = WMBits(7, _REG_LOUT2_VOLUME, 0) - _leftSpeakerVolumeSet = WMBit(_REG_LOUT2_VOLUME, 8) + _leftSpeakerVolume = WOBits(7, _REG_LOUT2_VOLUME, 0) + _leftSpeakerVolumeSet = WOBit(_REG_LOUT2_VOLUME, 8) @property def leftSpeakerVolume(self) -> int: @@ -1006,8 +1006,8 @@ def leftSpeakerVolumeDb(self) -> float: def leftSpeakerVolumeDb(self, value:float) -> None: self.leftSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - _rightSpeakerVolume = WMBits(7, _REG_ROUT2_VOLUME, 0) - _rightSpeakerVolumeSet = WMBit(_REG_ROUT2_VOLUME, 8) + _rightSpeakerVolume = WOBits(7, _REG_ROUT2_VOLUME, 0) + _rightSpeakerVolumeSet = WOBit(_REG_ROUT2_VOLUME, 8) @property def rightSpeakerVolume(self) -> int: @@ -1038,8 +1038,8 @@ def speakerVolumeDb(self) -> int: def speakerVolume(self, value:float) -> None: self.leftSpeakerVolume = self.rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) - leftSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7) - rightSpeakerZeroCross = WMBit(_REG_LOUT2_VOLUME, 7) + leftSpeakerZeroCross = WOBit(_REG_LOUT2_VOLUME, 7) + rightSpeakerZeroCross = WOBit(_REG_LOUT2_VOLUME, 7) @property def speakerZeroCross(self) -> bool: @@ -1048,7 +1048,7 @@ def speakerZeroCross(self) -> bool: def speakerZeroCross(self, value:bool) -> None: self.leftSpeakerZeroCross = self.rightSpeakerZeroCross = value - _speakerDcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 3) + _speakerDcGain = WOBits(3, _REG_CLASS_D_CONTROL_3, 3) @property def speakerDcGain(self) -> int: @@ -1057,7 +1057,7 @@ def speakerDcGain(self) -> int: def speakerDcGain(self, value:int) -> None: self._speakerDcGain = min(value, 5) - _speakerAcGain = WMBits(3, _REG_CLASS_D_CONTROL_3, 0) + _speakerAcGain = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) @property def speakerAcGain(self) -> int: @@ -1068,15 +1068,15 @@ def speakerAcGain(self, value:int) -> None: # Digital Audio Interface Control - loopback = WMBit(_REG_AUDIO_INTERFACE_2, 0) + loopback = WOBit(_REG_AUDIO_INTERFACE_2, 0) - pll = WMBit(_REG_PWR_MGMT_2, 0) - pllPrescaleDiv2 = WMBit(_REG_PWR_MGMT_2, 4) - pllN = WMBits(4, _REG_PLL_N, 0) + pll = WOBit(_REG_PWR_MGMT_2, 0) + pllPrescaleDiv2 = WOBit(_REG_PWR_MGMT_2, 4) + pllN = WOBits(4, _REG_PLL_N, 0) - _pllK1 = WMBits(6, _REG_PLL_K_1, 0) - _pllK2 = WMBits(9, _REG_PLL_K_2, 0) - _pllK3 = WMBits(9, _REG_PLL_K_3, 0) + _pllK1 = WOBits(6, _REG_PLL_K_1, 0) + _pllK2 = WOBits(9, _REG_PLL_K_2, 0) + _pllK3 = WOBits(9, _REG_PLL_K_3, 0) @property def pllK(self) -> int: @@ -1087,11 +1087,11 @@ def pllK(self, value:int) -> None: self._pllK2 = (value >> 9) & 0b111111111 self._pllK3 = value & 0b111111111 - clockFractionalMode = WMBit(_REG_PLL_N, 5) + clockFractionalMode = WOBit(_REG_PLL_N, 5) - clockFromPLL = WMBit(_REG_CLOCKING_1, 0) + clockFromPLL = WOBit(_REG_CLOCKING_1, 0) - _systemClockDivider = WMBits(2, _REG_CLOCKING_1, 1) + _systemClockDivider = WOBits(2, _REG_CLOCKING_1, 1) @property def systemClockDiv2(self) -> bool: @@ -1100,7 +1100,7 @@ def systemClockDiv2(self) -> bool: def systemClockDiv2(self, value:bool) -> None: self._systemClockDivider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 - _adcClockDivider = WMBits(3, _REG_CLOCKING_1, 6) + _adcClockDivider = WOBits(3, _REG_CLOCKING_1, 6) @property def adcClockDivider(self) -> int: @@ -1111,7 +1111,7 @@ def adcClockDivider(self, value:int) -> None: if value in _ADCDACDIV: self._adcClockDivider = _ADCDACDIV.index(value) - _dacClockDivider = WMBits(3, _REG_CLOCKING_1, 3) + _dacClockDivider = WOBits(3, _REG_CLOCKING_1, 3) @property def dacClockDivider(self) -> int: @@ -1122,7 +1122,7 @@ def dacClockDivider(self, value:int) -> None: if value in _ADCDACDIV: self._dacClockDivider = _ADCDACDIV.index(value) - _baseClockDivider = WMBits(4, _REG_CLOCKING_2, 0) + _baseClockDivider = WOBits(4, _REG_CLOCKING_2, 0) @property def baseClockDivider(self) -> float: @@ -1134,7 +1134,7 @@ def baseClockDivider(self, value:float) -> None: self._baseClockDivider = _BCLKDIV.index(value) - _ampClockDivider = WMBits(3, _REG_CLOCKING_2, 6) + _ampClockDivider = WOBits(3, _REG_CLOCKING_2, 6) @property def ampClockDivider(self) -> float: @@ -1147,9 +1147,9 @@ def ampClockDivider(self, value:float) -> None: ## Mode - masterMode = WMBit(_REG_AUDIO_INTERFACE_1, 6) + masterMode = WOBit(_REG_AUDIO_INTERFACE_1, 6) - _wordLength = WMBits(2, _REG_AUDIO_INTERFACE_1, 2) + _wordLength = WOBits(2, _REG_AUDIO_INTERFACE_1, 2) @property def wordLength(self) -> int: @@ -1162,13 +1162,13 @@ def wordLength(self) -> int: def wordLength(self, value:int) -> None: self._wordLength = (min(value, 28) - 16) // 4 - wordSelectInverted = WMBit(_REG_AUDIO_INTERFACE_1, 4) + wordSelectInverted = WOBit(_REG_AUDIO_INTERFACE_1, 4) - adcChannelSwap = WMBit(_REG_AUDIO_INTERFACE_1, 8) + adcChannelSwap = WOBit(_REG_AUDIO_INTERFACE_1, 8) - vrefOutputDisabled = WMBit(_REG_ADDITIONAL_CONTROL_3, 6) + vrefOutputDisabled = WOBit(_REG_ADDITIONAL_CONTROL_3, 6) - _vsel = WMBits(2, _REG_ADDITIONAL_CONTROL_1, 6) + _vsel = WOBits(2, _REG_ADDITIONAL_CONTROL_1, 6) @property def powerSupply(self) -> float: @@ -1179,11 +1179,11 @@ def powerSupply(self, value:float) -> None: ## GPIO - gpioOutput = WMBit(_REG_AUDIO_INTERFACE_2, 6) - gpioOutputMode = WMBits(3, _REG_ADDITIONAL_CONTROL_4, 4) - gpioOutputInverted = WMBit(_REG_ADDITIONAL_CONTROL_4, 7) + gpioOutput = WOBit(_REG_AUDIO_INTERFACE_2, 6) + gpioOutputMode = WOBits(3, _REG_ADDITIONAL_CONTROL_4, 4) + gpioOutputInverted = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) - _gpioClockDivider = WMBits(3, _REG_CLOCKING_2, 6) + _gpioClockDivider = WOBits(3, _REG_CLOCKING_2, 6) @property def gpioClockDivider(self) -> int: @@ -1299,9 +1299,9 @@ def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR) -> None: self.vmid = VMIDSEL_PLAYBACK # Resets all registers to their default state - _reset = WMBit(_REG_RESET, 7) + _reset = WOBit(_REG_RESET, 7) def reset(self) -> None: self._reset = True for name in dir(self): - if not name.startswith('_') and isinstance(getattr(self, name), (WMBit, WMBits)): + if not name.startswith('_') and isinstance(getattr(self, name), (WOBit, WOBits)): getattr(self, name).reset(self) From eba04c95b3b50c13bec166d6d9fb9cbc2b5a5973 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 13:08:43 -0500 Subject: [PATCH 28/56] Rename "pga" to "mic" --- adafruit_wm8960.py | 268 ++++++++++++++++++++++----------------------- 1 file changed, 134 insertions(+), 134 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 64d024e..a999274 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -324,173 +324,173 @@ def analogInputEnabled(self, value:bool) -> None: ## PWR_MGMT - pgaLeftEnabled = WOBit(_REG_PWR_MGMT_3, 5) - pgaRightEnabled = WOBit(_REG_PWR_MGMT_3, 4) + micLeftEnabled = WOBit(_REG_PWR_MGMT_3, 5) + micRightEnabled = WOBit(_REG_PWR_MGMT_3, 4) @property - def pgaEnabled(self) -> bool: - return self.pgaLeftEnabled and self.pgaRightEnabled - @pgaEnabled.setter - def pgaEnabled(self, value:bool) -> None: - self.pgaLeftEnabled = self.pgaRightEnabled = value + def micEnabled(self) -> bool: + return self.micLeftEnabled and self.micRightEnabled + @micEnabled.setter + def micEnabled(self, value:bool) -> None: + self.micLeftEnabled = self.micRightEnabled = value ## SIGNAL_PATH - _pgaInput1Left = WOBit(_REG_ADCL_SIGNAL_PATH, 8) - _pgaInput2Left = WOBit(_REG_ADCL_SIGNAL_PATH, 6) - _pgaInput3Left = WOBit(_REG_ADCL_SIGNAL_PATH, 7) + _micInput1Left = WOBit(_REG_ADCL_SIGNAL_PATH, 8) + _micInput2Left = WOBit(_REG_ADCL_SIGNAL_PATH, 6) + _micInput3Left = WOBit(_REG_ADCL_SIGNAL_PATH, 7) - _pgaInput1Right = WOBit(_REG_ADCR_SIGNAL_PATH, 8) - _pgaInput2Right = WOBit(_REG_ADCR_SIGNAL_PATH, 6) - _pgaInput3Right = WOBit(_REG_ADCR_SIGNAL_PATH, 7) + _micInput1Right = WOBit(_REG_ADCR_SIGNAL_PATH, 8) + _micInput2Right = WOBit(_REG_ADCR_SIGNAL_PATH, 6) + _micInput3Right = WOBit(_REG_ADCR_SIGNAL_PATH, 7) @property - def pgaNonInvSignalLeft(self) -> int: - if self._pgaInput2Left: + def micNonInvSignalLeft(self) -> int: + if self._micInput2Left: return PGA_INPUT2 - elif self._pgaInput3Left: + elif self._micInput3Left: return PGA_INPUT3 else: return PGA_VMID - @pgaNonInvSignalLeft.setter - def pgaNonInvSignalLeft(self, signal:int) -> None: - self._pgaInput2Left = signal == PGA_INPUT2 - self._pgaInput3Left = signal == PGA_INPUT3 + @micNonInvSignalLeft.setter + def micNonInvSignalLeft(self, signal:int) -> None: + self._micInput2Left = signal == PGA_INPUT2 + self._micInput3Left = signal == PGA_INPUT3 @property - def pgaNonInvSignalRight(self) -> int: - if self._pgaInput2Right: + def micNonInvSignalRight(self) -> int: + if self._micInput2Right: return PGA_INPUT2 - elif self._pgaInput3Right: + elif self._micInput3Right: return PGA_INPUT3 else: return PGA_VMID - @pgaNonInvSignalRight.setter - def pgaNonInvSignalRight(self, signal:int) -> None: - self._pgaInput2Right = signal == PGA_INPUT2 - self._pgaInput3Right = signal == PGA_INPUT3 + @micNonInvSignalRight.setter + def micNonInvSignalRight(self, signal:int) -> None: + self._micInput2Right = signal == PGA_INPUT2 + self._micInput3Right = signal == PGA_INPUT3 @property - def pgaNonInvSignal(self) -> int: + def micNonInvSignal(self) -> int: # NOTE: Not checking right signal - return self.pgaNonInvSignalLeft - @pgaNonInvSignal.setter - def pgaNonInvSignal(self, signal:int) -> None: - self.pgaNonInvSignalLeft = self.pgaNonInvSignalRight = signal + return self.micNonInvSignalLeft + @micNonInvSignal.setter + def micNonInvSignal(self, signal:int) -> None: + self.micNonInvSignalLeft = self.micNonInvSignalRight = signal ## Boost - pgaLeftBoostEnabled = WOBit(_REG_ADCL_SIGNAL_PATH, 3) - pgaRightBoostEnabled = WOBit(_REG_ADCR_SIGNAL_PATH, 3) + micLeftBoostEnabled = WOBit(_REG_ADCL_SIGNAL_PATH, 3) + micRightBoostEnabled = WOBit(_REG_ADCR_SIGNAL_PATH, 3) @property - def pgaBoostEnabled(self) -> None: - return self.pgaLeftBoostEnabled and self.pgaRightBoostEnabled - @pgaBoostEnabled.setter - def pgaBoostEnabled(self, value:int) -> None: - self.pgaLeftBoostEnabled = self.pgaRightBoostEnabled = value + def micBoostEnabled(self) -> None: + return self.micLeftBoostEnabled and self.micRightBoostEnabled + @micBoostEnabled.setter + def micBoostEnabled(self, value:int) -> None: + self.micLeftBoostEnabled = self.micRightBoostEnabled = value - pgaBoostGainLeft = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) - pgaBoostGainRight = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) + micBoostGainLeft = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) + micBoostGainRight = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) @property - def pgaBoostGain(self) -> None: - return max(self.pgaBoostGainLeft, self.pgaBoostGainRight) - @pgaBoostGain.setter - def pgaBoostGain(self, value:int) -> None: - self.pgaGainLeft = self.pgaGainRight = value + def micBoostGain(self) -> None: + return max(self.micBoostGainLeft, self.micBoostGainRight) + @micBoostGain.setter + def micBoostGain(self, value:int) -> None: + self.micGainLeft = self.micGainRight = value ## Volume - _pgaLeftVolume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) - _pgaLeftVolumeSet = WOBit(_REG_LEFT_INPUT_VOLUME, 8) + _micLeftVolume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) + _micLeftVolumeSet = WOBit(_REG_LEFT_INPUT_VOLUME, 8) - _pgaRightVolume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) - _pgaRightVolumeSet = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) + _micRightVolume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) + _micRightVolumeSet = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) @property - def pgaLeftVolume(self) -> int: - return self._pgaLeftVolume - @pgaLeftVolume.setter - def pgaLeftVolume(self, value:int) -> None: - self._pgaLeftVolume = value - self._pgaLeftVolumeSet = True + def micLeftVolume(self) -> int: + return self._micLeftVolume + @micLeftVolume.setter + def micLeftVolume(self, value:int) -> None: + self._micLeftVolume = value + self._micLeftVolumeSet = True @property - def pgaLeftVolumeDb(self) -> float: - return map_range(self.pgaLeftVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) - @pgaLeftVolumeDb.setter - def pgaLeftVolumeDb(self, value:float) -> None: - self.pgaLeftVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) + def micLeftVolumeDb(self) -> float: + return map_range(self.micLeftVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) + @micLeftVolumeDb.setter + def micLeftVolumeDb(self, value:float) -> None: + self.micLeftVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) @property - def pgaRightVolume(self) -> int: - return self._pgaRightVolume - @pgaRightVolume.setter - def pgaRightVolume(self, value:int) -> None: - self._pgaRightVolume = value - self._pgaRightVolumeSet = True + def micRightVolume(self) -> int: + return self._micRightVolume + @micRightVolume.setter + def micRightVolume(self, value:int) -> None: + self._micRightVolume = value + self._micRightVolumeSet = True @property - def pgaRightVolumeDb(self) -> float: - return map_range(self.pgaRightVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) - @pgaRightVolumeDb.setter - def pgaRightVolumeDb(self, value:float) -> None: - self.pgaRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) + def micRightVolumeDb(self) -> float: + return map_range(self.micRightVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) + @micRightVolumeDb.setter + def micRightVolumeDb(self, value:float) -> None: + self.micRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) @property - def pgaVolume(self) -> int: - return self.pgaLeftVolume - @pgaVolume.setter - def pgaVolume(self, value:int) -> None: - self.pgaLeftVolume = self.pgaRightVolume = value + def micVolume(self) -> int: + return self.micLeftVolume + @micVolume.setter + def micVolume(self, value:int) -> None: + self.micLeftVolume = self.micRightVolume = value @property - def pgaVolumeDb(self) -> float: - return self.pgaLeftVolumeDb - @pgaVolumeDb.setter - def pgaVolumeDb(self, value:float) -> None: - self.pgaVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) + def micVolumeDb(self) -> float: + return self.micLeftVolumeDb + @micVolumeDb.setter + def micVolumeDb(self, value:float) -> None: + self.micVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) ## Zero Cross - pgaLeftZeroCross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) - pgaRightZeroCross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) + micLeftZeroCross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) + micRightZeroCross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) @property - def pgaZeroCross(self) -> bool: - return self.pgaLeftZeroCross and self.pgaRightZeroCross - @pgaZeroCross.setter - def pgaZeroCross(self, value:bool) -> None: - self.pgaLeftZeroCross = self.pgaRightZeroCross = value + def micZeroCross(self) -> bool: + return self.micLeftZeroCross and self.micRightZeroCross + @micZeroCross.setter + def micZeroCross(self, value:bool) -> None: + self.micLeftZeroCross = self.micRightZeroCross = value ## Mute - _pgaLeftMute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) - _pgaRightMute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) + _micLeftMute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) + _micRightMute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) @property - def pgaLeftMute(self) -> bool: - return self._pgaLeftMute - @pgaLeftMute.setter - def pgaLeftMute(self, value:bool) -> None: - self._pgaLeftMute = value - self._pgaLeftVolumeSet = True + def micLeftMute(self) -> bool: + return self._micLeftMute + @micLeftMute.setter + def micLeftMute(self, value:bool) -> None: + self._micLeftMute = value + self._micLeftVolumeSet = True @property - def pgaRightMute(self) -> bool: - return self._pgaRightMute - @pgaRightMute.setter - def pgaRightMute(self, value:bool) -> None: - self._pgaRightMute = value - self._pgaRightVolumeSet = True + def micRightMute(self) -> bool: + return self._micRightMute + @micRightMute.setter + def micRightMute(self, value:bool) -> None: + self._micRightMute = value + self._micRightVolumeSet = True @property - def pgaMute(self) -> bool: - return self._pgaLeftMute and self._pgaRightMute - @pgaMute.setter - def pgaMute(self, value:bool) -> None: - self._pgaLeftMute = self._pgaRightMute = value + def micMute(self) -> bool: + return self._micLeftMute and self._micRightMute + @micMute.setter + def micMute(self, value:bool) -> None: + self._micLeftMute = self._micRightMute = value # Boost Mixer @@ -826,44 +826,44 @@ def input3OutputVolumeDb(self, value:float) -> None: ## PGA Boost Mixer Output - pgaBoostLeftOutputEnabled = WOBit(_REG_BYPASS_1, 7) - pgaBoostRightOutputEnabled = WOBit(_REG_BYPASS_2, 7) + micBoostLeftOutputEnabled = WOBit(_REG_BYPASS_1, 7) + micBoostRightOutputEnabled = WOBit(_REG_BYPASS_2, 7) @property - def pgaBoostOutputEnabled(self) -> bool: - return self.pgaBoostLeftOutputEnabled and self.pgaBoostRightOutputEnabled + def micBoostOutputEnabled(self) -> bool: + return self.micBoostLeftOutputEnabled and self.micBoostRightOutputEnabled - pgaBoostLeftOutputVolume = WOBits(3, _REG_BYPASS_1, 4) + micBoostLeftOutputVolume = WOBits(3, _REG_BYPASS_1, 4) @property - def pgaBoostLeftOutputVolumeDb(self) -> float: - return map_range(self.pgaBoostLeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @pgaBoostLeftOutputVolumeDb.setter - def pgaBoostLeftOutputVolumeDb(self, value:float) -> None: - self.pgaBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def micBoostLeftOutputVolumeDb(self) -> float: + return map_range(self.micBoostLeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @micBoostLeftOutputVolumeDb.setter + def micBoostLeftOutputVolumeDb(self, value:float) -> None: + self.micBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - pgaBoostRightOutputVolume = WOBits(3, _REG_BYPASS_2, 4) + micBoostRightOutputVolume = WOBits(3, _REG_BYPASS_2, 4) @property - def pgaBoostRightOutputVolumeDb(self) -> float: - return map_range(self.pgaBoostRightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @pgaBoostRightOutputVolumeDb.setter - def pgaBoostRightOutputVolumeDb(self, value:float) -> None: - self.pgaBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def micBoostRightOutputVolumeDb(self) -> float: + return map_range(self.micBoostRightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @micBoostRightOutputVolumeDb.setter + def micBoostRightOutputVolumeDb(self, value:float) -> None: + self.micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) @property - def pgaBoostOutputVolume(self) -> int: - return self.pgaBoostLeftOutputVolume - @pgaBoostOutputVolume.setter - def pgaBoostOutputVolume(self, value:int) -> None: - self.pgaBoostLeftOutputVolume = self.pgaBoostRightOutputVolume = value + def micBoostOutputVolume(self) -> int: + return self.micBoostLeftOutputVolume + @micBoostOutputVolume.setter + def micBoostOutputVolume(self, value:int) -> None: + self.micBoostLeftOutputVolume = self.micBoostRightOutputVolume = value @property - def pgaBoostOutputVolumeDb(self) -> float: - return self.pgaBoostLeftOutputVolumeDb - @pgaBoostOutputVolumeDb.setter - def pgaBoostOutputVolumeDb(self, value:float) -> None: - self.pgaBoostLeftOutputVolume = self.pgaBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def micBoostOutputVolumeDb(self) -> float: + return self.micBoostLeftOutputVolumeDb + @micBoostOutputVolumeDb.setter + def micBoostOutputVolumeDb(self, value:float) -> None: + self.micBoostLeftOutputVolume = self.micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) ## Mono Output From 8727e62dae30eb744fab25236791e87bd97b6e23 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 13:13:05 -0500 Subject: [PATCH 29/56] Removed property verbs such as "Enabled". --- adafruit_wm8960.py | 219 +++++++++++++++++++++++---------------------- 1 file changed, 113 insertions(+), 106 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index a999274..2b0b22a 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -310,29 +310,29 @@ class WM8960: vref = WOBit(_REG_PWR_MGMT_1, 6) - analogInputLeftEnabled = WOBit(_REG_PWR_MGMT_1, 5) - analogInputRightEnabled = WOBit(_REG_PWR_MGMT_1, 4) + analogInputLeft = WOBit(_REG_PWR_MGMT_1, 5) + analogInputRight = WOBit(_REG_PWR_MGMT_1, 4) @property - def analogInputEnabled(self) -> bool: - return self.analogInputLeftEnabled and self.analogInputRightEnabled - @analogInputEnabled.setter - def analogInputEnabled(self, value:bool) -> None: - self.analogInputLeftEnabled = self.analogInputRightEnabled = value + def analogInput(self) -> bool: + return self.analogInputLeft and self.analogInputRight + @analogInput.setter + def analogInput(self, value:bool) -> None: + self.analogInputLeft = self.analogInputRight = value # PGA ## PWR_MGMT - micLeftEnabled = WOBit(_REG_PWR_MGMT_3, 5) - micRightEnabled = WOBit(_REG_PWR_MGMT_3, 4) + micLeft = WOBit(_REG_PWR_MGMT_3, 5) + micRight = WOBit(_REG_PWR_MGMT_3, 4) @property - def micEnabled(self) -> bool: - return self.micLeftEnabled and self.micRightEnabled - @micEnabled.setter - def micEnabled(self, value:bool) -> None: - self.micLeftEnabled = self.micRightEnabled = value + def mic(self) -> bool: + return self.micLeft and self.micRight + @mic.setter + def mic(self, value:bool) -> None: + self.micLeft = self.micRight = value ## SIGNAL_PATH @@ -380,15 +380,15 @@ def micNonInvSignal(self, signal:int) -> None: ## Boost - micLeftBoostEnabled = WOBit(_REG_ADCL_SIGNAL_PATH, 3) - micRightBoostEnabled = WOBit(_REG_ADCR_SIGNAL_PATH, 3) + micLeftBoost = WOBit(_REG_ADCL_SIGNAL_PATH, 3) + micRightBoost = WOBit(_REG_ADCR_SIGNAL_PATH, 3) @property - def micBoostEnabled(self) -> None: - return self.micLeftBoostEnabled and self.micRightBoostEnabled - @micBoostEnabled.setter - def micBoostEnabled(self, value:int) -> None: - self.micLeftBoostEnabled = self.micRightBoostEnabled = value + def micBoost(self) -> None: + return self.micLeftBoost and self.micRightBoost + @micBoost.setter + def micBoost(self, value:int) -> None: + self.micLeftBoost = self.micRightBoost = value micBoostGainLeft = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) micBoostGainRight = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) @@ -521,15 +521,15 @@ def input3Boost(self, value:int) -> None: # ADC - adcLeftEnabled = WOBit(_REG_PWR_MGMT_1, 3) - adcRightEnabled = WOBit(_REG_PWR_MGMT_1, 2) + adcLeft = WOBit(_REG_PWR_MGMT_1, 3) + adcRight = WOBit(_REG_PWR_MGMT_1, 2) @property - def adcEnabled(self) -> bool: - return self.adcLeftEnabled and self.adcRightEnabled - @adcEnabled.setter - def adcEnabled(self, value:bool) -> None: - self.adcLeftEnabled = self.adcRightEnabled = value + def adc(self) -> bool: + return self.adcLeft and self.adcRight + @adc.setter + def adc(self, value:bool) -> None: + self.adcLeft = self.adcRight = value ## Volume @@ -585,15 +585,15 @@ def adcVolume(self, value:float) -> None: # ALC - alcLeftEnabled = WOBit(_REG_ALC1, 7) - alcRightEnabled = WOBit(_REG_ALC1, 8) + alcLeft = WOBit(_REG_ALC1, 7) + alcRight = WOBit(_REG_ALC1, 8) @property - def alcEnabled(self) -> bool: - return self.alcLeftEnabled and self.alcRightEnabled - @alcEnabled.setter - def alcEnabled(self, value:bool) -> None: - self.alcLeftEnabled = self.alcRightEnabled = value + def alc(self) -> bool: + return self.alcLeft and self.alcRight + @alc.setter + def alc(self, value:bool) -> None: + self.alcLeft = self.alcRight = value alcTarget = WOBits(4, _REG_ALC1, 0) @@ -672,7 +672,7 @@ def alcHoldTime(self, value:float) -> None: # Noise Gate - noiseGateEnabled = WOBit(_REG_NOISE_GATE, 0) + noiseGate = WOBit(_REG_NOISE_GATE, 0) noiseGateThreshold = WOBits(5, _REG_NOISE_GATE, 3) @property @@ -684,15 +684,15 @@ def noiseGateThresholdDb(self, value:float) -> None: # DAC - dacLeftEnabled = WOBit(_REG_PWR_MGMT_2, 8) - dacRightEnabled = WOBit(_REG_PWR_MGMT_2, 7) + dacLeft = WOBit(_REG_PWR_MGMT_2, 8) + dacRight = WOBit(_REG_PWR_MGMT_2, 7) @property - def dacEnabled(self) -> bool: - return self.dacLeftEnabled and self.dacRightEnabled - @dacEnabled.setter - def dacEnabled(self, value:bool) -> None: - self.dacLeftEnabled = self.dacRightEnabled = value + def dac(self) -> bool: + return self.dacLeft and self.dacRight + @dac.setter + def dac(self, value:bool) -> None: + self.dacLeft = self.dacRight = value _dacLeftVolume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) _dacLeftVolumeSet = WOBit(_REG_LEFT_DAC_VOLUME, 8) @@ -751,46 +751,46 @@ def dacVolume(self, value:float) -> None: # 3D Enhance - enhanceEnabled = WOBit(_REG_3D_CONTROL, 0) + enhance = WOBit(_REG_3D_CONTROL, 0) enhanceDepth = WOBits(4, _REG_3D_CONTROL, 1) enhanceFilterLPF = WOBit(_REG_3D_CONTROL, 6) enhanceFilterHPF = WOBit(_REG_3D_CONTROL, 5) # Output Mixer - leftOutputEnabled = WOBit(_REG_PWR_MGMT_3, 3) - rightOutputEnabled = WOBit(_REG_PWR_MGMT_3, 2) + leftOutput = WOBit(_REG_PWR_MGMT_3, 3) + rightOutput = WOBit(_REG_PWR_MGMT_3, 2) @property - def stereoOutputEnabled(self) -> bool: - return self.leftOutputEnabled and self.rightOutputEnabled - @stereoOutputEnabled.setter - def stereoOutputEnabled(self, value:bool): - self.leftOutputEnabled = self.rightOutputEnabled = value + def output(self) -> bool: + return self.leftOutput and self.rightOutput + @output.setter + def output(self, value:bool): + self.leftOutput = self.rightOutput = value ## DAC Output - dacLeftOutputEnabled = WOBit(_REG_LEFT_OUT_MIX, 8) - dacRightOutputEnabled = WOBit(_REG_RIGHT_OUT_MIX, 8) + dacLeftOutput = WOBit(_REG_LEFT_OUT_MIX, 8) + dacRightOutput = WOBit(_REG_RIGHT_OUT_MIX, 8) @property - def dacOutputEnabled(self) -> bool: - return self.dacLeftOutputEnabled and self.dacRightOutputEnabled - @dacOutputEnabled.setter - def dacOutputEnabled(self, value:bool) -> None: - self.dacLeftOutputEnabled = self.dacRightOutputEnabled = value + def dacOutput(self) -> bool: + return self.dacLeftOutput and self.dacRightOutput + @dacOutput.setter + def dacOutput(self, value:bool) -> None: + self.dacLeftOutput = self.dacRightOutput = value ## Input 3 Output - input3LeftOutputEnabled = WOBit(_REG_LEFT_OUT_MIX, 7) - input3RightOutputEnabled = WOBit(_REG_RIGHT_OUT_MIX, 7) + input3LeftOutput = WOBit(_REG_LEFT_OUT_MIX, 7) + input3RightOutput = WOBit(_REG_RIGHT_OUT_MIX, 7) @property - def input3OutputEnabled(self) -> bool: - return self.input3LeftOutputEnabled and self.input3RightOutputEnabled - @input3OutputEnabled.setter - def input3OutputEnabled(self, value:bool) -> None: - self.input3LeftOutputEnabled = self.input3RightOutputEnabled = value + def input3Output(self) -> bool: + return self.input3LeftOutput and self.input3RightOutput + @input3Output.setter + def input3Output(self, value:bool) -> None: + self.input3LeftOutput = self.input3RightOutput = value input3LeftOutputVolume = WOBits(3, _REG_LEFT_OUT_MIX, 4) @@ -826,12 +826,12 @@ def input3OutputVolumeDb(self, value:float) -> None: ## PGA Boost Mixer Output - micBoostLeftOutputEnabled = WOBit(_REG_BYPASS_1, 7) - micBoostRightOutputEnabled = WOBit(_REG_BYPASS_2, 7) + micBoostLeftOutput = WOBit(_REG_BYPASS_1, 7) + micBoostRightOutput = WOBit(_REG_BYPASS_2, 7) @property - def micBoostOutputEnabled(self) -> bool: - return self.micBoostLeftOutputEnabled and self.micBoostRightOutputEnabled + def micBoostOutput(self) -> bool: + return self.micBoostLeftOutput and self.micBoostRightOutput micBoostLeftOutputVolume = WOBits(3, _REG_BYPASS_1, 4) @@ -867,17 +867,17 @@ def micBoostOutputVolumeDb(self, value:float) -> None: ## Mono Output - monoOutputEnabled = WOBit(_REG_PWR_MGMT_2, 1) + monoOutput = WOBit(_REG_PWR_MGMT_2, 1) - monoLeftMixEnabled = WOBit(_REG_MONO_OUT_MIX_1, 7) - monoRightMixEnabled = WOBit(_REG_MONO_OUT_MIX_2, 7) + monoLeftMix = WOBit(_REG_MONO_OUT_MIX_1, 7) + monoRightMix = WOBit(_REG_MONO_OUT_MIX_2, 7) @property - def monoMixEnabled(self) -> bool: - return self.monoLeftMixEnabled and self.monoRightMixEnabled - @monoMixEnabled.setter - def monoMixEnabled(self, value:bool) -> None: - self.monoLeftMixEnabled = self.monoRightMixEnabled = value + def monoMix(self) -> bool: + return self.monoLeftMix and self.monoRightMix + @monoMix.setter + def monoMix(self, value:bool) -> None: + self.monoLeftMix = self.monoRightMix = value monoOutputAttenuation = WOBit(_REG_MONO_OUT_VOLUME, 6) @@ -887,15 +887,15 @@ def monoMixEnabled(self, value:bool) -> None: ## Headphones - leftHeadphoneEnabled = WOBit(_REG_PWR_MGMT_2, 5) - rightHeadphoneEnabled = WOBit(_REG_PWR_MGMT_2, 6) + leftHeadphone = WOBit(_REG_PWR_MGMT_2, 5) + rightHeadphone = WOBit(_REG_PWR_MGMT_2, 6) @property - def headphoneEnabled(self) -> bool: - return self.leftHeadphoneEnabled and self.rightHeadphoneEnabled - @headphoneEnabled.setter - def headphoneEnabled(self, value:bool) -> None: - self.leftHeadphoneEnabled = self.rightHeadphoneEnabled = value + def headphone(self) -> bool: + return self.leftHeadphone and self.rightHeadphone + @headphone.setter + def headphone(self, value:bool) -> None: + self.leftHeadphone = self.rightHeadphone = value headphoneStandby = WOBit(_REG_ANTI_POP_1, 0) @@ -961,32 +961,32 @@ def headphoneZeroCross(self, value:bool) -> None: ## Speakers - _leftSpeakerEnabled = WOBit(_REG_PWR_MGMT_2, 4) - _leftSpeakerAmpEnabled = WOBit(_REG_CLASS_D_CONTROL_1, 6) + _leftSpeaker = WOBit(_REG_PWR_MGMT_2, 4) + _leftSpeakerAmp = WOBit(_REG_CLASS_D_CONTROL_1, 6) @property - def leftSpeakerEnabled(self) -> bool: - return self._leftSpeakerEnabled and self._leftSpeakerAmpEnabled - @leftSpeakerEnabled.setter - def leftSpeakerEnabled(self, value:bool) -> None: - self._leftSpeakerEnabled = self._leftSpeakerAmpEnabled = value + def leftSpeaker(self) -> bool: + return self._leftSpeaker and self._leftSpeakerAmp + @leftSpeaker.setter + def leftSpeaker(self, value:bool) -> None: + self._leftSpeaker = self._leftSpeakerAmp = value - _rightSpeakerEnabled = WOBit(_REG_PWR_MGMT_2, 3) - _rightSpeakerAmpEnabled = WOBit(_REG_CLASS_D_CONTROL_1, 7) + _rightSpeaker = WOBit(_REG_PWR_MGMT_2, 3) + _rightSpeakerAmp = WOBit(_REG_CLASS_D_CONTROL_1, 7) @property - def rightSpeakerEnabled(self) -> bool: - return self._rightSpeakerEnabled and self._rightSpeakerAmpEnabled - @rightSpeakerEnabled.setter - def rightSpeakerEnabled(self, value:bool) -> None: - self._rightSpeakerEnabled = self._rightSpeakerAmpEnabled = value + def rightSpeaker(self) -> bool: + return self._rightSpeaker and self._rightSpeakerAmp + @rightSpeaker.setter + def rightSpeaker(self, value:bool) -> None: + self._rightSpeaker = self._rightSpeakerAmp = value @property - def speakerEnabled(self) -> bool: - return self.leftSpeakerEnabled and self.rightSpeakerEnabled - @speakerEnabled.setter - def speakerEnabled(self, value:bool) -> None: - self.leftSpeakerEnabled = self.rightSpeakerEnabled = value + def speaker(self) -> bool: + return self.leftSpeaker and self.rightSpeaker + @speaker.setter + def speaker(self, value:bool) -> None: + self.leftSpeaker = self.rightSpeaker = value _leftSpeakerVolume = WOBits(7, _REG_LOUT2_VOLUME, 0) _leftSpeakerVolumeSet = WOBit(_REG_LOUT2_VOLUME, 8) @@ -1162,11 +1162,18 @@ def wordLength(self) -> int: def wordLength(self, value:int) -> None: self._wordLength = (min(value, 28) - 16) // 4 - wordSelectInverted = WOBit(_REG_AUDIO_INTERFACE_1, 4) + wordSelectInvert = WOBit(_REG_AUDIO_INTERFACE_1, 4) adcChannelSwap = WOBit(_REG_AUDIO_INTERFACE_1, 8) - vrefOutputDisabled = WOBit(_REG_ADDITIONAL_CONTROL_3, 6) + _vrefOutputDisable = WOBit(_REG_ADDITIONAL_CONTROL_3, 6) + + @property + def vrefOutput(self) -> bool: + return not self._vrefOutputDisable + @vrefOutput.setter + def vrefOutput(self, value:bool) -> None: + self._vrefOutputDisable = not value _vsel = WOBits(2, _REG_ADDITIONAL_CONTROL_1, 6) @@ -1181,7 +1188,7 @@ def powerSupply(self, value:float) -> None: gpioOutput = WOBit(_REG_AUDIO_INTERFACE_2, 6) gpioOutputMode = WOBits(3, _REG_ADDITIONAL_CONTROL_4, 4) - gpioOutputInverted = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) + gpioOutputInvert = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) _gpioClockDivider = WOBits(3, _REG_CLOCKING_2, 6) From bade89759d02432ee4f32fdc1135f11e2fd27a1f Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 13:35:43 -0500 Subject: [PATCH 30/56] Default settings to decibel and time values and obfuscate direct register attributes. --- adafruit_wm8960.py | 374 +++++++++++++++------------------------------ 1 file changed, 124 insertions(+), 250 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 2b0b22a..cfbd0d3 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -409,48 +409,28 @@ def micBoostGain(self, value:int) -> None: _micRightVolumeSet = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) @property - def micLeftVolume(self) -> int: - return self._micLeftVolume + def micLeftVolume(self) -> float: + return map_range(self._micLeftVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) @micLeftVolume.setter - def micLeftVolume(self, value:int) -> None: - self._micLeftVolume = value + def micLeftVolume(self, value:float) -> None: + self._micLeftVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) self._micLeftVolumeSet = True - - @property - def micLeftVolumeDb(self) -> float: - return map_range(self.micLeftVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) - @micLeftVolumeDb.setter - def micLeftVolumeDb(self, value:float) -> None: - self.micLeftVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) @property - def micRightVolume(self) -> int: - return self._micRightVolume + def micRightVolume(self) -> float: + return map_range(self._micRightVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) @micRightVolume.setter - def micRightVolume(self, value:int) -> None: - self._micRightVolume = value + def micRightVolume(self, value:float) -> None: + self._micRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) self._micRightVolumeSet = True - - @property - def micRightVolumeDb(self) -> float: - return map_range(self.micRightVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) - @micRightVolumeDb.setter - def micRightVolumeDb(self, value:float) -> None: - self.micRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) @property - def micVolume(self) -> int: - return self.micLeftVolume + def micVolume(self) -> float: + return max(self.micLeftVolume, self.micRightVolume) @micVolume.setter - def micVolume(self, value:int) -> None: - self.micLeftVolume = self.micRightVolume = value - - @property - def micVolumeDb(self) -> float: - return self.micLeftVolumeDb - @micVolumeDb.setter - def micVolumeDb(self, value:float) -> None: - self.micVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) + def micVolume(self, value:float) -> None: + self._micLeftVolume = self._micRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) + self._micLeftVolumeSet = self._micRightVolumeSet = True ## Zero Cross @@ -537,51 +517,31 @@ def adc(self, value:bool) -> None: _adcLeftVolumeSet = WOBit(_REG_LEFT_ADC_VOLUME, 8) @property - def adcLeftVolume(self) -> int: - return self._adcLeftVolume + def adcLeftVolume(self) -> float: + return map_range(max(self._adcLeftVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) @adcLeftVolume.setter - def adcLeftVolume(self, value:int) -> None: - self._adcLeftVolume = value + def adcLeftVolume(self, value:float) -> None: + self._adcLeftVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) self._adcLeftVolumeSet = True - @property - def adcLeftVolumeDb(self) -> float: - return map_range(max(self.adcLeftVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) - @adcLeftVolumeDb.setter - def adcLeftVolumeDb(self, value:float) -> None: - self.adcLeftVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - _adcRightVolume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) _adcRightVolumeSet = WOBit(_REG_RIGHT_ADC_VOLUME, 8) @property - def adcRightVolume(self) -> int: - return self._adcRightVolume + def adcRightVolume(self) -> float: + return map_range(max(self._adcRightVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) @adcRightVolume.setter - def adcRightVolume(self, value:int) -> None: - self._adcRightVolume = value + def adcRightVolume(self, value:float) -> None: + self._adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) self._adcRightVolumeSet = True - @property - def adcRightVolumeDb(self) -> float: - return map_range(max(self.adcRightVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) - @adcRightVolumeDb.setter - def adcRightVolumeDb(self, value:float) -> None: - self.adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - @property def adcVolume(self) -> int: - return self.adcLeftVolume + return max(self.adcLeftVolume, self.adcRightVolume) @adcVolume.setter def adcVolume(self, value:float) -> None: - self.adcLeftVolume = self.adcRightVolume = value - - @property - def adcVolumeDb(self) -> int: - return self.adcLeftVolumeDb - @adcVolume.setter - def adcVolume(self, value:float) -> None: - self.adcLeftVolume = self.adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._adcLeftVolume = self._adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._adcLeftVolumeSet = self._adcRightVolumeSet = True # ALC @@ -595,92 +555,79 @@ def alc(self) -> bool: def alc(self, value:bool) -> None: self.alcLeft = self.alcRight = value - alcTarget = WOBits(4, _REG_ALC1, 0) + _alcTarget = WOBits(4, _REG_ALC1, 0) @property - def alcTargetDb(self) -> float: - return map_range(self.alcTarget, 0, 15, _ALC_TARGET_MIN, _ALC_TARGET_MAX) - @alcTargetDb.setter - def alcTargetDb(self, value:float) -> None: - self.alcTarget = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) + def alcTarget(self) -> float: + return map_range(self._alcTarget, 0, 15, _ALC_TARGET_MIN, _ALC_TARGET_MAX) + @alcTarget.setter + def alcTarget(self, value:float) -> None: + self._alcTarget = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) - alcMaxGain = WOBits(3, _REG_ALC1, 4) + _alcMaxGain = WOBits(3, _REG_ALC1, 4) @property - def alcMaxGainDb(self) -> float: - return map_range(self.alcMaxGain, 0, 7, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX) - @alcMaxGainDb.setter - def alcMaxGainDb(self, value:float) -> None: - self.alcMaxGain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) + def alcMaxGain(self) -> float: + return map_range(self._alcMaxGain, 0, 7, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX) + @alcMaxGain.setter + def alcMaxGain(self, value:float) -> None: + self._alcMaxGain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) - alcMinGain = WOBits(3, _REG_ALC2, 4) + _alcMinGain = WOBits(3, _REG_ALC2, 4) @property - def alcMinGainDb(self) -> float: - return map_range(self.alcMinGain, 0, 7, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX) - @alcMinGainDb.setter - def alcMinGainDb(self, value:float) -> None: - self.alcMinGain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) + def alcMinGain(self) -> float: + return map_range(self._alcMinGain, 0, 7, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX) + @alcMinGain.setter + def alcMinGain(self, value:float) -> None: + self._alcMinGain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) _alcAttack = WOBits(4, _REG_ALC3, 0) - @property - def alcAttack(self) -> int: - return self._alcAttack - @alcAttack.setter - def alcAttack(self, value:int) -> None: - self._alcAttack = min(value, _ALC_ATTACK_MAX) - @property def alcAttackTime(self) -> float: - return _ALC_ATTACK_TIME_MIN * pow(2, self.alcAttack) + return _ALC_ATTACK_TIME_MIN * pow(2, self._alcAttack) @alcAttackTime.setter def alcAttackTime(self, value:float) -> None: - self.alcAttack = round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)) + self._alcAttack = min(round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)), _ALC_ATTACK_MAX) _alcDecay = WOBits(4, _REG_ALC3, 4) - @property - def alcDecay(self) -> int: - return self._alcDecay - @alcDecay.setter - def alcDecay(self, value:int) -> None: - self._alcDecay = min(value, _ALC_DECAY_MAX) - @property def alcDecayTime(self) -> float: - return _ALC_DECAY_TIME_MIN * pow(2, self.alcDecay) + return _ALC_DECAY_TIME_MIN * pow(2, self._alcDecay) @alcDecayTime.setter def alcDecayTime(self, value:float) -> None: - self.alcDecay = round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)) + self._alcDecay = min(round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)), _ALC_DECAY_MAX) - alcHold = WOBits(4, _REG_ALC2, 0) + _alcHold = WOBits(4, _REG_ALC2, 0) @property def alcHoldTime(self) -> float: - value = self.alcHold + value = self._alcHold if value == 0: return 0.0 - return _ALC_HOLD_TIME_MIN * pow(2, self.alcHold - 1) + return _ALC_HOLD_TIME_MIN * pow(2, self._alcHold - 1) @alcHoldTime.setter def alcHoldTime(self, value:float) -> None: if value <= 0.0: - self.alcHold = 0 + self._alcHold = 0 else: - self.alcHold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) + self._alcHold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) alcLimiter = WOBit(_REG_ALC3, 8) # Noise Gate noiseGate = WOBit(_REG_NOISE_GATE, 0) - noiseGateThreshold = WOBits(5, _REG_NOISE_GATE, 3) + + _noiseGateThreshold = WOBits(5, _REG_NOISE_GATE, 3) @property - def noiseGateThresholdDb(self) -> float: - return map_range(self.noiseGateThreshold, 0, 31, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX) - @noiseGateThresholdDb.setter - def noiseGateThresholdDb(self, value:float) -> None: - self.noiseGateThreshold = round(map_range(value, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX, 0, 31)) + def noiseGateThreshold(self) -> float: + return map_range(self._noiseGateThreshold, 0, 31, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX) + @noiseGateThreshold.setter + def noiseGateThreshold(self, value:float) -> None: + self._noiseGateThreshold = round(map_range(value, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX, 0, 31)) # DAC @@ -698,51 +645,31 @@ def dac(self, value:bool) -> None: _dacLeftVolumeSet = WOBit(_REG_LEFT_DAC_VOLUME, 8) @property - def dacLeftVolume(self) -> int: - return self._dacLeftVolume + def dacLeftVolume(self) -> float: + return map_range(max(self._dacLeftVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) @dacLeftVolume.setter - def dacLeftVolume(self, value:int) -> None: - self._dacLeftVolume = value + def dacLeftVolume(self, value:float) -> None: + self._dacLeftVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) self._dacLeftVolumeSet = True - @property - def dacLeftVolumeDb(self) -> float: - return map_range(max(self.dacLeftVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) - @dacLeftVolumeDb.setter - def dacLeftVolumeDb(self, value:float) -> None: - self.dacLeftVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - _dacRightVolume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) _dacRightVolumeSet = WOBit(_REG_RIGHT_DAC_VOLUME, 8) @property - def dacRightVolume(self) -> int: - return self._dacRightVolume + def dacRightVolume(self) -> float: + return map_range(max(self._dacRightVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) @dacRightVolume.setter - def dacRightVolume(self, value:int) -> None: - self._dacRightVolume = value + def dacRightVolume(self, value:float) -> None: + self._dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) self._dacRightVolumeSet = True - @property - def dacRightVolumeDb(self) -> float: - return map_range(max(self.dacRightVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) - @dacRightVolumeDb.setter - def dacRightVolumeDb(self, value:float) -> None: - self.dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - @property def dacVolume(self) -> int: - return self.dacLeftVolume + return max(self.dacLeftVolume, self.dacRightVolume) @dacVolume.setter def dacVolume(self, value:float) -> None: - self.dacLeftVolume = self.dacRightVolume = value - - @property - def dacVolumeDb(self) -> int: - return self.dacLeftVolumeDb - @dacVolume.setter - def dacVolume(self, value:float) -> None: - self.dacLeftVolume = self.dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._dacLeftVolume = self._dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._dacLeftVolumeSet = self._dacRightVolumeSet = True dacMute = WOBit(_REG_ADC_DAC_CTRL_1, 3) dacSoftMute = WOBit(_REG_ADC_DAC_CTRL_2, 3) @@ -792,37 +719,30 @@ def input3Output(self) -> bool: def input3Output(self, value:bool) -> None: self.input3LeftOutput = self.input3RightOutput = value - input3LeftOutputVolume = WOBits(3, _REG_LEFT_OUT_MIX, 4) + _input3LeftOutputVolume = WOBits(3, _REG_LEFT_OUT_MIX, 4) @property - def input3LeftOutputVolumeDb(self) -> float: - return map_range(self.input3LeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @input3LeftOutputVolumeDb.setter - def input3LeftOutputVolumeDb(self, value:float) -> None: - self.input3LeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def input3LeftOutputVolume(self) -> float: + return map_range(self._input3LeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @input3LeftOutputVolume.setter + def input3LeftOutputVolume(self, value:float) -> None: + self._input3LeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - input3RightOutputVolume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) + _input3RightOutputVolume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) @property - def input3RightOutputVolumeDb(self) -> float: - return map_range(self.input3RightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @input3RightOutputVolumeDb.setter - def input3RightOutputVolumeDb(self, value:float) -> None: - self.input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def input3RightOutputVolume(self) -> float: + return map_range(self._input3RightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @input3RightOutputVolume.setter + def input3RightOutputVolume(self, value:float) -> None: + self._input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) @property - def input3OutputVolume(self) -> int: - return self.input3LeftOutputVolume + def input3OutputVolume(self) -> float: + return max(self.input3LeftOutputVolume, self.input3RightOutputVolume) @input3OutputVolume.setter - def input3OutputVolume(self, value:int) -> None: - self.input3LeftOutputVolume = self.input3RightOutputVolume = value - - @property - def input3OutputVolumeDb(self) -> float: - return self.input3LeftOutputVolumeDb - @input3OutputVolumeDb.setter - def input3OutputVolumeDb(self, value:float) -> None: - self.input3LeftOutputVolume = self.input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def input3OutputVolume(self, value:float) -> None: + self._input3LeftOutputVolume = self._input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) ## PGA Boost Mixer Output @@ -833,37 +753,30 @@ def input3OutputVolumeDb(self, value:float) -> None: def micBoostOutput(self) -> bool: return self.micBoostLeftOutput and self.micBoostRightOutput - micBoostLeftOutputVolume = WOBits(3, _REG_BYPASS_1, 4) + _micBoostLeftOutputVolume = WOBits(3, _REG_BYPASS_1, 4) @property - def micBoostLeftOutputVolumeDb(self) -> float: - return map_range(self.micBoostLeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @micBoostLeftOutputVolumeDb.setter - def micBoostLeftOutputVolumeDb(self, value:float) -> None: - self.micBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def micBoostLeftOutputVolume(self) -> float: + return map_range(self._micBoostLeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @micBoostLeftOutputVolume.setter + def micBoostLeftOutputVolume(self, value:float) -> None: + self._micBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - micBoostRightOutputVolume = WOBits(3, _REG_BYPASS_2, 4) + _micBoostRightOutputVolume = WOBits(3, _REG_BYPASS_2, 4) @property - def micBoostRightOutputVolumeDb(self) -> float: - return map_range(self.micBoostRightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @micBoostRightOutputVolumeDb.setter - def micBoostRightOutputVolumeDb(self, value:float) -> None: - self.micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def micBoostRightOutputVolume(self) -> float: + return map_range(self._micBoostRightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @micBoostRightOutputVolume.setter + def micBoostRightOutputVolume(self, value:float) -> None: + self._micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) @property - def micBoostOutputVolume(self) -> int: - return self.micBoostLeftOutputVolume + def micBoostOutputVolume(self) -> float: + return max(self.micBoostLeftOutputVolume, self.micBoostRightOutputVolume) @micBoostOutputVolume.setter - def micBoostOutputVolume(self, value:int) -> None: - self.micBoostLeftOutputVolume = self.micBoostRightOutputVolume = value - - @property - def micBoostOutputVolumeDb(self) -> float: - return self.micBoostLeftOutputVolumeDb - @micBoostOutputVolumeDb.setter - def micBoostOutputVolumeDb(self, value:float) -> None: - self.micBoostLeftOutputVolume = self.micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def micBoostOutputVolume(self, value:float) -> None: + self._micBoostLeftOutputVolume = self._micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) ## Mono Output @@ -903,51 +816,31 @@ def headphone(self, value:bool) -> None: _leftHeadphoneVolumeSet = WOBit(_REG_LOUT1_VOLUME, 8) @property - def leftHeadphoneVolume(self) -> int: - return self._leftHeadphoneVolume + def leftHeadphoneVolume(self) -> float: + return map_range(max(self._leftHeadphoneVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) @leftHeadphoneVolume.setter - def leftHeadphoneVolume(self, value:int) -> None: - self._leftHeadphoneVolume = value + def leftHeadphoneVolume(self, value:float) -> None: + self._leftHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) self._leftHeadphoneVolumeSet = True - @property - def leftHeadphoneVolumeDb(self) -> float: - return map_range(max(self.leftHeadphoneVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @leftHeadphoneVolumeDb.setter - def leftHeadphoneVolumeDb(self, value:float) -> None: - self.leftHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - _rightHeadphoneVolume = WOBits(7, _REG_ROUT1_VOLUME, 0) _rightHeadphoneVolumeSet = WOBit(_REG_ROUT1_VOLUME, 8) @property - def rightHeadphoneVolume(self) -> int: - return self._rightHeadphoneVolume + def rightHeadphoneVolume(self) -> float: + return map_range(max(self._rightHeadphoneVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) @rightHeadphoneVolume.setter - def rightHeadphoneVolume(self, value:int) -> None: - self._rightHeadphoneVolume = value + def rightHeadphoneVolume(self, value:float) -> None: + self._rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) self._rightHeadphoneVolumeSet = True - @property - def rightHeadphoneVolumeDb(self) -> float: - return map_range(max(self.rightHeadphoneVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @rightHeadphoneVolumeDb.setter - def rightHeadphoneVolumeDb(self, value:float) -> None: - self.rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - @property def headphoneVolume(self) -> int: - return self.leftHeadphoneVolume + return max(self.leftHeadphoneVolume, self.rightHeadphoneVolume) @headphoneVolume.setter def headphoneVolume(self, value:float) -> None: - self.leftHeadphoneVolume = self.rightHeadphoneVolume = value - - @property - def headphoneVolumeDb(self) -> int: - return self.leftHeadphoneVolumeDb - @headphoneVolumeDb.setter - def headphoneVolumeDb(self, value:float) -> None: - self.leftHeadphoneVolume = self.rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + self._leftHeadphoneVolume = self._rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + self._leftHeadphoneVolumeSet = self._rightHeadphoneVolumeSet = True leftHeadphoneZeroCross = WOBit(_REG_LOUT1_VOLUME, 7) rightHeadphoneZeroCross = WOBit(_REG_LOUT1_VOLUME, 7) @@ -992,51 +885,32 @@ def speaker(self, value:bool) -> None: _leftSpeakerVolumeSet = WOBit(_REG_LOUT2_VOLUME, 8) @property - def leftSpeakerVolume(self) -> int: - return self._leftSpeakerVolume + def leftSpeakerVolume(self) -> float: + return map_range(max(self._leftSpeakerVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) @leftSpeakerVolume.setter - def leftSpeakerVolume(self, value:int) -> None: - self._leftSpeakerVolume = value - self._leftSpeakerVolumeSet = True - - @property - def leftSpeakerVolumeDb(self) -> float: - return map_range(max(self.leftSpeakerVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @leftSpeakerVolumeDb.setter - def leftSpeakerVolumeDb(self, value:float) -> None: - self.leftSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + def leftSpeakerVolume(self, value:float) -> None: + self._leftSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._leftSpeakerSet = True _rightSpeakerVolume = WOBits(7, _REG_ROUT2_VOLUME, 0) _rightSpeakerVolumeSet = WOBit(_REG_ROUT2_VOLUME, 8) @property - def rightSpeakerVolume(self) -> int: - return self._rightSpeakerVolume + def rightSpeakerVolume(self) -> float: + return map_range(max(self.rightSpeakerVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) @rightSpeakerVolume.setter - def rightSpeakerVolume(self, value:int) -> None: - self._rightSpeakerVolume = value + def rightSpeakerVolume(self, value:float) -> None: + self._rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) self._rightSpeakerVolumeSet = True - @property - def rightSpeakerVolumeDb(self) -> float: - return map_range(max(self.rightSpeakerVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @rightSpeakerVolumeDb.setter - def rightSpeakerVolumeDb(self, value:float) -> None: - self.rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) @property def speakerVolume(self) -> int: - return self.leftSpeakerVolume - @speakerVolume.setter - def speakerVolume(self, value:float) -> None: - self.leftSpeakerVolume = self.rightSpeakerVolume = value - - @property - def speakerVolumeDb(self) -> int: - return self.leftSpeakerVolumeDb + return max(self.leftSpeakerVolume, self.rightSpeakerVolume) @speakerVolume.setter def speakerVolume(self, value:float) -> None: - self.leftSpeakerVolume = self.rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + self._leftSpeakerVolume = self._rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + self._leftSpeakerVolumeSet = self._rightSpeakerVolumeSet = True leftSpeakerZeroCross = WOBit(_REG_LOUT2_VOLUME, 7) rightSpeakerZeroCross = WOBit(_REG_LOUT2_VOLUME, 7) From ceee3b7887b1876fdeb7f38a60a9f19635361628 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 13:59:40 -0500 Subject: [PATCH 31/56] Converted attribute names from camel case to snake case. --- adafruit_wm8960.py | 973 ++++++++++++++++++++++----------------------- 1 file changed, 486 insertions(+), 487 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index cfbd0d3..060647d 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -93,12 +93,12 @@ _REG_PLL_K_2 = const(0x36) _REG_PLL_K_3 = const(0x37) -# PGA input selections -PGA_INPUT2 = 0 -PGA_INPUT3 = 1 -PGA_VMID = 2 +# Microphone input selections +MIC_INPUT2 = 0 +MIC_INPUT3 = 1 +MIC_VMID = 2 -# Mic (aka PGA) BOOST gain options +# Microphone Boost gain options MIC_BOOST_GAIN_0DB = 0 MIC_BOOST_GAIN_13DB = 1 MIC_BOOST_GAIN_20DB = 2 @@ -130,8 +130,8 @@ _SYSCLK_DIV_BY_2 = const(2) # Gain/Level mins, maxes, offsets and step-sizes -_PGA_GAIN_MIN = -17.25 -_PGA_GAIN_MAX = 30.00 +_MIC_GAIN_MIN = -17.25 +_MIC_GAIN_MAX = 30.00 _ADC_VOLUME_MIN = -97.00 _ADC_VOLUME_MAX = 30.00 @@ -310,489 +310,489 @@ class WM8960: vref = WOBit(_REG_PWR_MGMT_1, 6) - analogInputLeft = WOBit(_REG_PWR_MGMT_1, 5) - analogInputRight = WOBit(_REG_PWR_MGMT_1, 4) + analog_input_left = WOBit(_REG_PWR_MGMT_1, 5) + analog_input_right = WOBit(_REG_PWR_MGMT_1, 4) @property - def analogInput(self) -> bool: - return self.analogInputLeft and self.analogInputRight - @analogInput.setter - def analogInput(self, value:bool) -> None: - self.analogInputLeft = self.analogInputRight = value + def analog_input(self) -> bool: + return self.analog_input_left and self.analog_input_right + @analog_input.setter + def analog_input(self, value:bool) -> None: + self.analog_input_left = self.analog_input_right = value - # PGA + # MIC ## PWR_MGMT - micLeft = WOBit(_REG_PWR_MGMT_3, 5) - micRight = WOBit(_REG_PWR_MGMT_3, 4) + mic_left = WOBit(_REG_PWR_MGMT_3, 5) + mic_right = WOBit(_REG_PWR_MGMT_3, 4) @property def mic(self) -> bool: - return self.micLeft and self.micRight + return self.mic_left and self.mic_right @mic.setter def mic(self, value:bool) -> None: - self.micLeft = self.micRight = value + self.mic_left = self.mic_right = value ## SIGNAL_PATH - _micInput1Left = WOBit(_REG_ADCL_SIGNAL_PATH, 8) - _micInput2Left = WOBit(_REG_ADCL_SIGNAL_PATH, 6) - _micInput3Left = WOBit(_REG_ADCL_SIGNAL_PATH, 7) + _mic_input1_left = WOBit(_REG_ADCL_SIGNAL_PATH, 8) + _mic_input2_left = WOBit(_REG_ADCL_SIGNAL_PATH, 6) + _mic_input3_left = WOBit(_REG_ADCL_SIGNAL_PATH, 7) - _micInput1Right = WOBit(_REG_ADCR_SIGNAL_PATH, 8) - _micInput2Right = WOBit(_REG_ADCR_SIGNAL_PATH, 6) - _micInput3Right = WOBit(_REG_ADCR_SIGNAL_PATH, 7) + _mic_input1_right = WOBit(_REG_ADCR_SIGNAL_PATH, 8) + _mic_input2_right = WOBit(_REG_ADCR_SIGNAL_PATH, 6) + _mic_input3_right = WOBit(_REG_ADCR_SIGNAL_PATH, 7) @property - def micNonInvSignalLeft(self) -> int: - if self._micInput2Left: - return PGA_INPUT2 - elif self._micInput3Left: - return PGA_INPUT3 + def mic_signal_left(self) -> int: + if self._mic_input2_left: + return MIC_INPUT2 + elif self._mic_input3_left: + return MIC_INPUT3 else: - return PGA_VMID - @micNonInvSignalLeft.setter - def micNonInvSignalLeft(self, signal:int) -> None: - self._micInput2Left = signal == PGA_INPUT2 - self._micInput3Left = signal == PGA_INPUT3 + return MIC_VMID + @mic_signal_left.setter + def mic_signal_left(self, signal:int) -> None: + self._mic_input2_left = signal == MIC_INPUT2 + self._mic_input3_left = signal == MIC_INPUT3 @property - def micNonInvSignalRight(self) -> int: - if self._micInput2Right: - return PGA_INPUT2 - elif self._micInput3Right: - return PGA_INPUT3 + def mic_signal_right(self) -> int: + if self._mic_input2_right: + return MIC_INPUT2 + elif self._mic_input3_right: + return MIC_INPUT3 else: - return PGA_VMID - @micNonInvSignalRight.setter - def micNonInvSignalRight(self, signal:int) -> None: - self._micInput2Right = signal == PGA_INPUT2 - self._micInput3Right = signal == PGA_INPUT3 + return MIC_VMID + @mic_signal_right.setter + def mic_signal_right(self, signal:int) -> None: + self._mic_input2_right = signal == MIC_INPUT2 + self._mic_input3_right = signal == MIC_INPUT3 @property - def micNonInvSignal(self) -> int: + def mic_signal(self) -> int: # NOTE: Not checking right signal - return self.micNonInvSignalLeft - @micNonInvSignal.setter - def micNonInvSignal(self, signal:int) -> None: - self.micNonInvSignalLeft = self.micNonInvSignalRight = signal + return self.mic_signal_left + @mic_signal.setter + def mic_signal(self, signal:int) -> None: + self.mic_signal_left = self.mic_signal_right = signal ## Boost - micLeftBoost = WOBit(_REG_ADCL_SIGNAL_PATH, 3) - micRightBoost = WOBit(_REG_ADCR_SIGNAL_PATH, 3) + mic_left_boost = WOBit(_REG_ADCL_SIGNAL_PATH, 3) + mic_right_boost = WOBit(_REG_ADCR_SIGNAL_PATH, 3) @property - def micBoost(self) -> None: - return self.micLeftBoost and self.micRightBoost - @micBoost.setter - def micBoost(self, value:int) -> None: - self.micLeftBoost = self.micRightBoost = value + def mic_boost(self) -> None: + return self.mic_left_boost and self.mic_right_boost + @mic_boost.setter + def mic_boost(self, value:int) -> None: + self.mic_left_boost = self.mic_right_boost = value - micBoostGainLeft = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) - micBoostGainRight = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) + mic_boost_gain_left = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) + mic_boost_gain_right = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) @property - def micBoostGain(self) -> None: - return max(self.micBoostGainLeft, self.micBoostGainRight) - @micBoostGain.setter - def micBoostGain(self, value:int) -> None: - self.micGainLeft = self.micGainRight = value + def mic_boost_gain(self) -> None: + return max(self.mic_boost_gain_left, self.mic_boost_gain_right) + @mic_boost_gain.setter + def mic_boost_gain(self, value:int) -> None: + self.mic_gain_left = self.mic_gain_right = value ## Volume - _micLeftVolume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) - _micLeftVolumeSet = WOBit(_REG_LEFT_INPUT_VOLUME, 8) + _mic_left_volume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) + _mic_left_volume_set = WOBit(_REG_LEFT_INPUT_VOLUME, 8) - _micRightVolume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) - _micRightVolumeSet = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) + _mic_right_volume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) + _mic_right_volume_set = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) @property - def micLeftVolume(self) -> float: - return map_range(self._micLeftVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) - @micLeftVolume.setter - def micLeftVolume(self, value:float) -> None: - self._micLeftVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) - self._micLeftVolumeSet = True + def mic_left_volume(self) -> float: + return map_range(self._mic_left_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) + @mic_left_volume.setter + def mic_left_volume(self, value:float) -> None: + self._mic_left_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._mic_left_volume_set = True @property - def micRightVolume(self) -> float: - return map_range(self._micRightVolume, 0, 63, _PGA_GAIN_MIN, _PGA_GAIN_MAX) - @micRightVolume.setter - def micRightVolume(self, value:float) -> None: - self._micRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) - self._micRightVolumeSet = True + def mic_right_volume(self) -> float: + return map_range(self._mic_right_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) + @mic_right_volume.setter + def mic_right_volume(self, value:float) -> None: + self._mic_right_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._mic_right_volume_set = True @property - def micVolume(self) -> float: - return max(self.micLeftVolume, self.micRightVolume) - @micVolume.setter - def micVolume(self, value:float) -> None: - self._micLeftVolume = self._micRightVolume = round(map_range(value, _PGA_GAIN_MIN, _PGA_GAIN_MAX, 0, 63)) - self._micLeftVolumeSet = self._micRightVolumeSet = True + def mic_volume(self) -> float: + return max(self.mic_left_volume, self.mic_right_volume) + @mic_volume.setter + def mic_volume(self, value:float) -> None: + self._mic_left_volume = self._mic_right_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._mic_left_volume_set = self._mic_right_volume_set = True ## Zero Cross - micLeftZeroCross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) - micRightZeroCross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) + mic_left_zero_cross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) + mic_right_zero_cross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) @property - def micZeroCross(self) -> bool: - return self.micLeftZeroCross and self.micRightZeroCross - @micZeroCross.setter - def micZeroCross(self, value:bool) -> None: - self.micLeftZeroCross = self.micRightZeroCross = value + def mic_zero_cross(self) -> bool: + return self.mic_left_zero_cross and self.mic_right_zero_cross + @mic_zero_cross.setter + def mic_zero_cross(self, value:bool) -> None: + self.mic_left_zero_cross = self.mic_right_zero_cross = value ## Mute - _micLeftMute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) - _micRightMute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) + _mic_left_mute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) + _mic_right_mute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) @property - def micLeftMute(self) -> bool: - return self._micLeftMute - @micLeftMute.setter - def micLeftMute(self, value:bool) -> None: - self._micLeftMute = value - self._micLeftVolumeSet = True + def mic_left_mute(self) -> bool: + return self._mic_left_mute + @mic_left_mute.setter + def mic_left_mute(self, value:bool) -> None: + self._mic_left_mute = value + self._mic_left_volume_set = True @property - def micRightMute(self) -> bool: - return self._micRightMute - @micRightMute.setter - def micRightMute(self, value:bool) -> None: - self._micRightMute = value - self._micRightVolumeSet = True + def mic_right_mute(self) -> bool: + return self._mic_right_mute + @mic_right_mute.setter + def mic_right_mute(self, value:bool) -> None: + self._mic_right_mute = value + self._mic_right_volume_set = True @property - def micMute(self) -> bool: - return self._micLeftMute and self._micRightMute - @micMute.setter - def micMute(self, value:bool) -> None: - self._micLeftMute = self._micRightMute = value + def mic_mute(self) -> bool: + return self._mic_left_mute and self._mic_right_mute + @mic_mute.setter + def mic_mute(self, value:bool) -> None: + self._mic_left_mute = self._mic_right_mute = value # Boost Mixer - input2LeftBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) - input2RightBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) + input2_left_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) + input2_right_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) @property - def input2Boost(self) -> int: - return self.input2LeftBoost - @input2Boost.setter - def input2Boost(self, value:int) -> None: - self.input2LeftBoost = self.input2RightBoost = value + def input2_boost(self) -> int: + return self.input2_left_boost + @input2_boost.setter + def input2_boost(self, value:int) -> None: + self.input2_left_boost = self.input2_right_boost = value - input3LeftBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) - input3RightBoost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) + input3_left_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) + input3_right_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) # Mic Bias - micBias = WOBit(_REG_PWR_MGMT_1, 1) - micBiasVoltage = WOBit(_REG_ADDITIONAL_CONTROL_4, 0) + mic_bias = WOBit(_REG_PWR_MGMT_1, 1) + mic_bias_voltage = WOBit(_REG_ADDITIONAL_CONTROL_4, 0) @property - def input3Boost(self) -> int: - return self.input3LeftBoost - @input3Boost.setter - def input3Boost(self, value:int) -> None: - self.input3LeftBoost = self.input3RightBoost = value + def input3_boost(self) -> int: + return self.input3_left_boost + @input3_boost.setter + def input3_boost(self, value:int) -> None: + self.input3_left_boost = self.input3_right_boost = value # ADC - adcLeft = WOBit(_REG_PWR_MGMT_1, 3) - adcRight = WOBit(_REG_PWR_MGMT_1, 2) + adc_left = WOBit(_REG_PWR_MGMT_1, 3) + adc_right = WOBit(_REG_PWR_MGMT_1, 2) @property def adc(self) -> bool: - return self.adcLeft and self.adcRight + return self.adc_left and self.adc_right @adc.setter def adc(self, value:bool) -> None: - self.adcLeft = self.adcRight = value + self.adc_left = self.adc_right = value ## Volume - _adcLeftVolume = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) - _adcLeftVolumeSet = WOBit(_REG_LEFT_ADC_VOLUME, 8) + _adc_left_volume = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) + _adc_left_volume_set = WOBit(_REG_LEFT_ADC_VOLUME, 8) @property - def adcLeftVolume(self) -> float: - return map_range(max(self._adcLeftVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) - @adcLeftVolume.setter - def adcLeftVolume(self, value:float) -> None: - self._adcLeftVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - self._adcLeftVolumeSet = True + def adc_left_volume(self) -> float: + return map_range(max(self._adc_left_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + @adc_left_volume.setter + def adc_left_volume(self, value:float) -> None: + self._adc_left_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._adc_left_volume_set = True - _adcRightVolume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) - _adcRightVolumeSet = WOBit(_REG_RIGHT_ADC_VOLUME, 8) + _adc_right_volume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) + _adc_right_volume_set = WOBit(_REG_RIGHT_ADC_VOLUME, 8) @property - def adcRightVolume(self) -> float: - return map_range(max(self._adcRightVolume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) - @adcRightVolume.setter - def adcRightVolume(self, value:float) -> None: - self._adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - self._adcRightVolumeSet = True + def adc_right_volume(self) -> float: + return map_range(max(self._adc_right_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + @adc_right_volume.setter + def adc_right_volume(self, value:float) -> None: + self._adc_right_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._adc_right_volume_set = True @property - def adcVolume(self) -> int: - return max(self.adcLeftVolume, self.adcRightVolume) - @adcVolume.setter - def adcVolume(self, value:float) -> None: - self._adcLeftVolume = self._adcRightVolume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - self._adcLeftVolumeSet = self._adcRightVolumeSet = True + def adc_volume(self) -> int: + return max(self.adc_left_volume, self.adc_right_volume) + @adc_volume.setter + def adc_volume(self, value:float) -> None: + self._adc_left_volume = self._adc_right_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._adc_left_volume_set = self._adc_right_volume_set = True # ALC - alcLeft = WOBit(_REG_ALC1, 7) - alcRight = WOBit(_REG_ALC1, 8) + alc_left = WOBit(_REG_ALC1, 7) + alc_right = WOBit(_REG_ALC1, 8) @property def alc(self) -> bool: - return self.alcLeft and self.alcRight + return self.alc_left and self.alc_right @alc.setter def alc(self, value:bool) -> None: - self.alcLeft = self.alcRight = value + self.alc_left = self.alc_right = value - _alcTarget = WOBits(4, _REG_ALC1, 0) + _alc_target = WOBits(4, _REG_ALC1, 0) @property - def alcTarget(self) -> float: - return map_range(self._alcTarget, 0, 15, _ALC_TARGET_MIN, _ALC_TARGET_MAX) - @alcTarget.setter - def alcTarget(self, value:float) -> None: - self._alcTarget = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) + def alc_target(self) -> float: + return map_range(self._alc_target, 0, 15, _ALC_TARGET_MIN, _ALC_TARGET_MAX) + @alc_target.setter + def alc_target(self, value:float) -> None: + self._alc_target = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) - _alcMaxGain = WOBits(3, _REG_ALC1, 4) + _alc_max_gain = WOBits(3, _REG_ALC1, 4) @property - def alcMaxGain(self) -> float: - return map_range(self._alcMaxGain, 0, 7, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX) - @alcMaxGain.setter - def alcMaxGain(self, value:float) -> None: - self._alcMaxGain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) + def alc_max_gain(self) -> float: + return map_range(self._alc_max_gain, 0, 7, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX) + @alc_max_gain.setter + def alc_max_gain(self, value:float) -> None: + self._alc_max_gain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) - _alcMinGain = WOBits(3, _REG_ALC2, 4) + _alc_min_gain = WOBits(3, _REG_ALC2, 4) @property - def alcMinGain(self) -> float: - return map_range(self._alcMinGain, 0, 7, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX) - @alcMinGain.setter - def alcMinGain(self, value:float) -> None: - self._alcMinGain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) + def alc_min_gain(self) -> float: + return map_range(self._alc_min_gain, 0, 7, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX) + @alc_min_gain.setter + def alc_min_gain(self, value:float) -> None: + self._alc_min_gain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) - _alcAttack = WOBits(4, _REG_ALC3, 0) + _alc_attack = WOBits(4, _REG_ALC3, 0) @property - def alcAttackTime(self) -> float: - return _ALC_ATTACK_TIME_MIN * pow(2, self._alcAttack) - @alcAttackTime.setter - def alcAttackTime(self, value:float) -> None: - self._alcAttack = min(round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)), _ALC_ATTACK_MAX) + def alc_attack_time(self) -> float: + return _ALC_ATTACK_TIME_MIN * pow(2, self._alc_attack) + @alc_attack_time.setter + def alc_attack_time(self, value:float) -> None: + self._alc_attack = min(round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)), _ALC_ATTACK_MAX) - _alcDecay = WOBits(4, _REG_ALC3, 4) + _alc_decay = WOBits(4, _REG_ALC3, 4) @property - def alcDecayTime(self) -> float: - return _ALC_DECAY_TIME_MIN * pow(2, self._alcDecay) - @alcDecayTime.setter - def alcDecayTime(self, value:float) -> None: - self._alcDecay = min(round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)), _ALC_DECAY_MAX) + def alc_decay_time(self) -> float: + return _ALC_DECAY_TIME_MIN * pow(2, self._alc_decay) + @alc_decay_time.setter + def alc_decay_time(self, value:float) -> None: + self._alc_decay = min(round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)), _ALC_DECAY_MAX) - _alcHold = WOBits(4, _REG_ALC2, 0) + _alc_hold = WOBits(4, _REG_ALC2, 0) @property - def alcHoldTime(self) -> float: - value = self._alcHold + def alc_hold_time(self) -> float: + value = self._alc_hold if value == 0: return 0.0 - return _ALC_HOLD_TIME_MIN * pow(2, self._alcHold - 1) - @alcHoldTime.setter - def alcHoldTime(self, value:float) -> None: + return _ALC_HOLD_TIME_MIN * pow(2, self._alc_hold - 1) + @alc_hold_time.setter + def alc_hold_time(self, value:float) -> None: if value <= 0.0: - self._alcHold = 0 + self._alc_hold = 0 else: - self._alcHold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) + self._alc_hold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) - alcLimiter = WOBit(_REG_ALC3, 8) + alc_limiter = WOBit(_REG_ALC3, 8) # Noise Gate - noiseGate = WOBit(_REG_NOISE_GATE, 0) + noise_gate = WOBit(_REG_NOISE_GATE, 0) - _noiseGateThreshold = WOBits(5, _REG_NOISE_GATE, 3) + _noise_gate_threshold = WOBits(5, _REG_NOISE_GATE, 3) @property - def noiseGateThreshold(self) -> float: - return map_range(self._noiseGateThreshold, 0, 31, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX) - @noiseGateThreshold.setter - def noiseGateThreshold(self, value:float) -> None: - self._noiseGateThreshold = round(map_range(value, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX, 0, 31)) + def noise_gate_threshold(self) -> float: + return map_range(self._noise_gate_threshold, 0, 31, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX) + @noise_gate_threshold.setter + def noise_gate_threshold(self, value:float) -> None: + self._noise_gate_threshold = round(map_range(value, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX, 0, 31)) # DAC - dacLeft = WOBit(_REG_PWR_MGMT_2, 8) - dacRight = WOBit(_REG_PWR_MGMT_2, 7) + dac_left = WOBit(_REG_PWR_MGMT_2, 8) + dac_right = WOBit(_REG_PWR_MGMT_2, 7) @property def dac(self) -> bool: - return self.dacLeft and self.dacRight + return self.dac_left and self.dac_right @dac.setter def dac(self, value:bool) -> None: - self.dacLeft = self.dacRight = value + self.dac_left = self.dac_right = value - _dacLeftVolume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) - _dacLeftVolumeSet = WOBit(_REG_LEFT_DAC_VOLUME, 8) + _dac_left_volume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) + _dac_left_volume_set = WOBit(_REG_LEFT_DAC_VOLUME, 8) @property - def dacLeftVolume(self) -> float: - return map_range(max(self._dacLeftVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) - @dacLeftVolume.setter - def dacLeftVolume(self, value:float) -> None: - self._dacLeftVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - self._dacLeftVolumeSet = True + def dac_left_volume(self) -> float: + return map_range(max(self._dac_left_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + @dac_left_volume.setter + def dac_left_volume(self, value:float) -> None: + self._dac_left_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._dac_left_volume_set = True - _dacRightVolume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) - _dacRightVolumeSet = WOBit(_REG_RIGHT_DAC_VOLUME, 8) + _dac_right_volume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) + _dac_right_volume_set = WOBit(_REG_RIGHT_DAC_VOLUME, 8) @property - def dacRightVolume(self) -> float: - return map_range(max(self._dacRightVolume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) - @dacRightVolume.setter - def dacRightVolume(self, value:float) -> None: - self._dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - self._dacRightVolumeSet = True + def dac_right_volume(self) -> float: + return map_range(max(self._dac_right_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + @dac_right_volume.setter + def dac_right_volume(self, value:float) -> None: + self._dac_right_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._dac_right_volume_set = True @property - def dacVolume(self) -> int: - return max(self.dacLeftVolume, self.dacRightVolume) - @dacVolume.setter - def dacVolume(self, value:float) -> None: - self._dacLeftVolume = self._dacRightVolume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - self._dacLeftVolumeSet = self._dacRightVolumeSet = True + def dac_volume(self) -> int: + return max(self.dac_left_volume, self.dac_right_volume) + @dac_volume.setter + def dac_volume(self, value:float) -> None: + self._dac_left_volume = self._dac_right_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._dac_left_volume_set = self._dac_right_volume_set = True - dacMute = WOBit(_REG_ADC_DAC_CTRL_1, 3) - dacSoftMute = WOBit(_REG_ADC_DAC_CTRL_2, 3) - dacSlowSoftMute = WOBit(_REG_ADC_DAC_CTRL_2, 2) - dacAttenuation = WOBit(_REG_ADC_DAC_CTRL_1, 7) + dac_mute = WOBit(_REG_ADC_DAC_CTRL_1, 3) + dac_soft_mute = WOBit(_REG_ADC_DAC_CTRL_2, 3) + dac_slow_soft_mute = WOBit(_REG_ADC_DAC_CTRL_2, 2) + dac_attenuation = WOBit(_REG_ADC_DAC_CTRL_1, 7) - # 3D Enhance + # 3_d Enhance enhance = WOBit(_REG_3D_CONTROL, 0) - enhanceDepth = WOBits(4, _REG_3D_CONTROL, 1) - enhanceFilterLPF = WOBit(_REG_3D_CONTROL, 6) - enhanceFilterHPF = WOBit(_REG_3D_CONTROL, 5) + enhance_depth = WOBits(4, _REG_3D_CONTROL, 1) + enhance_filter_lPF = WOBit(_REG_3D_CONTROL, 6) + enhance_filter_hPF = WOBit(_REG_3D_CONTROL, 5) # Output Mixer - leftOutput = WOBit(_REG_PWR_MGMT_3, 3) - rightOutput = WOBit(_REG_PWR_MGMT_3, 2) + left_output = WOBit(_REG_PWR_MGMT_3, 3) + right_output = WOBit(_REG_PWR_MGMT_3, 2) @property def output(self) -> bool: - return self.leftOutput and self.rightOutput + return self.left_output and self.right_output @output.setter def output(self, value:bool): - self.leftOutput = self.rightOutput = value + self.left_output = self.right_output = value ## DAC Output - dacLeftOutput = WOBit(_REG_LEFT_OUT_MIX, 8) - dacRightOutput = WOBit(_REG_RIGHT_OUT_MIX, 8) + dac_left_output = WOBit(_REG_LEFT_OUT_MIX, 8) + dac_right_output = WOBit(_REG_RIGHT_OUT_MIX, 8) @property - def dacOutput(self) -> bool: - return self.dacLeftOutput and self.dacRightOutput - @dacOutput.setter - def dacOutput(self, value:bool) -> None: - self.dacLeftOutput = self.dacRightOutput = value + def dac_output(self) -> bool: + return self.dac_left_output and self.dac_right_output + @dac_output.setter + def dac_output(self, value:bool) -> None: + self.dac_left_output = self.dac_right_output = value ## Input 3 Output - input3LeftOutput = WOBit(_REG_LEFT_OUT_MIX, 7) - input3RightOutput = WOBit(_REG_RIGHT_OUT_MIX, 7) + input3_left_output = WOBit(_REG_LEFT_OUT_MIX, 7) + input3_right_output = WOBit(_REG_RIGHT_OUT_MIX, 7) @property - def input3Output(self) -> bool: - return self.input3LeftOutput and self.input3RightOutput - @input3Output.setter - def input3Output(self, value:bool) -> None: - self.input3LeftOutput = self.input3RightOutput = value + def input3_output(self) -> bool: + return self.input3_left_output and self.input3_right_output + @input3_output.setter + def input3_output(self, value:bool) -> None: + self.input3_left_output = self.input3_right_output = value - _input3LeftOutputVolume = WOBits(3, _REG_LEFT_OUT_MIX, 4) + _input3_left_output_volume = WOBits(3, _REG_LEFT_OUT_MIX, 4) @property - def input3LeftOutputVolume(self) -> float: - return map_range(self._input3LeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @input3LeftOutputVolume.setter - def input3LeftOutputVolume(self, value:float) -> None: - self._input3LeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def input3_left_output_volume(self) -> float: + return map_range(self._input3_left_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @input3_left_output_volume.setter + def input3_left_output_volume(self, value:float) -> None: + self._input3_left_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - _input3RightOutputVolume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) + _input3_right_output_volume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) @property - def input3RightOutputVolume(self) -> float: - return map_range(self._input3RightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @input3RightOutputVolume.setter - def input3RightOutputVolume(self, value:float) -> None: - self._input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def input3_right_output_volume(self) -> float: + return map_range(self._input3_right_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @input3_right_output_volume.setter + def input3_right_output_volume(self, value:float) -> None: + self._input3_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) @property - def input3OutputVolume(self) -> float: - return max(self.input3LeftOutputVolume, self.input3RightOutputVolume) - @input3OutputVolume.setter - def input3OutputVolume(self, value:float) -> None: - self._input3LeftOutputVolume = self._input3RightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def input3_output_volume(self) -> float: + return max(self.input3_left_output_volume, self.input3_right_output_volume) + @input3_output_volume.setter + def input3_output_volume(self, value:float) -> None: + self._input3_left_output_volume = self._input3_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - ## PGA Boost Mixer Output + ## MIC Boost Mixer Output - micBoostLeftOutput = WOBit(_REG_BYPASS_1, 7) - micBoostRightOutput = WOBit(_REG_BYPASS_2, 7) + mic_boost_left_output = WOBit(_REG_BYPASS_1, 7) + mic_boost_right_output = WOBit(_REG_BYPASS_2, 7) @property - def micBoostOutput(self) -> bool: - return self.micBoostLeftOutput and self.micBoostRightOutput + def mic_boost_output(self) -> bool: + return self.mic_boost_left_output and self.mic_boost_right_output - _micBoostLeftOutputVolume = WOBits(3, _REG_BYPASS_1, 4) + _mic_boost_left_output_volume = WOBits(3, _REG_BYPASS_1, 4) @property - def micBoostLeftOutputVolume(self) -> float: - return map_range(self._micBoostLeftOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @micBoostLeftOutputVolume.setter - def micBoostLeftOutputVolume(self, value:float) -> None: - self._micBoostLeftOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def mic_boost_left_output_volume(self) -> float: + return map_range(self._mic_boost_left_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @mic_boost_left_output_volume.setter + def mic_boost_left_output_volume(self, value:float) -> None: + self._mic_boost_left_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - _micBoostRightOutputVolume = WOBits(3, _REG_BYPASS_2, 4) + _mic_boost_right_output_volume = WOBits(3, _REG_BYPASS_2, 4) @property - def micBoostRightOutputVolume(self) -> float: - return map_range(self._micBoostRightOutputVolume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @micBoostRightOutputVolume.setter - def micBoostRightOutputVolume(self, value:float) -> None: - self._micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def mic_boost_right_output_volume(self) -> float: + return map_range(self._mic_boost_right_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @mic_boost_right_output_volume.setter + def mic_boost_right_output_volume(self, value:float) -> None: + self._mic_boost_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) @property - def micBoostOutputVolume(self) -> float: - return max(self.micBoostLeftOutputVolume, self.micBoostRightOutputVolume) - @micBoostOutputVolume.setter - def micBoostOutputVolume(self, value:float) -> None: - self._micBoostLeftOutputVolume = self._micBoostRightOutputVolume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def mic_boost_output_volume(self) -> float: + return max(self.mic_boost_left_output_volume, self.mic_boost_right_output_volume) + @mic_boost_output_volume.setter + def mic_boost_output_volume(self, value:float) -> None: + self._mic_boost_left_output_volume = self._mic_boost_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) ## Mono Output - monoOutput = WOBit(_REG_PWR_MGMT_2, 1) + mono_output = WOBit(_REG_PWR_MGMT_2, 1) - monoLeftMix = WOBit(_REG_MONO_OUT_MIX_1, 7) - monoRightMix = WOBit(_REG_MONO_OUT_MIX_2, 7) + mono_left_mix = WOBit(_REG_MONO_OUT_MIX_1, 7) + mono_right_mix = WOBit(_REG_MONO_OUT_MIX_2, 7) @property - def monoMix(self) -> bool: - return self.monoLeftMix and self.monoRightMix - @monoMix.setter - def monoMix(self, value:bool) -> None: - self.monoLeftMix = self.monoRightMix = value + def mono_mix(self) -> bool: + return self.mono_left_mix and self.mono_right_mix + @mono_mix.setter + def mono_mix(self, value:bool) -> None: + self.mono_left_mix = self.mono_right_mix = value - monoOutputAttenuation = WOBit(_REG_MONO_OUT_VOLUME, 6) + mono_output_attenuation = WOBit(_REG_MONO_OUT_VOLUME, 6) vmid = WOBits(2, _REG_PWR_MGMT_1, 7) @@ -800,316 +800,315 @@ def monoMix(self, value:bool) -> None: ## Headphones - leftHeadphone = WOBit(_REG_PWR_MGMT_2, 5) - rightHeadphone = WOBit(_REG_PWR_MGMT_2, 6) + left_headphone = WOBit(_REG_PWR_MGMT_2, 5) + right_headphone = WOBit(_REG_PWR_MGMT_2, 6) @property def headphone(self) -> bool: - return self.leftHeadphone and self.rightHeadphone + return self.left_headphone and self.right_headphone @headphone.setter def headphone(self, value:bool) -> None: - self.leftHeadphone = self.rightHeadphone = value + self.left_headphone = self.right_headphone = value - headphoneStandby = WOBit(_REG_ANTI_POP_1, 0) + headphone_standby = WOBit(_REG_ANTI_POP_1, 0) - _leftHeadphoneVolume = WOBits(7, _REG_LOUT1_VOLUME, 0) - _leftHeadphoneVolumeSet = WOBit(_REG_LOUT1_VOLUME, 8) + _left_headphone_volume = WOBits(7, _REG_LOUT1_VOLUME, 0) + _left_headphone_volume_set = WOBit(_REG_LOUT1_VOLUME, 8) @property - def leftHeadphoneVolume(self) -> float: - return map_range(max(self._leftHeadphoneVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @leftHeadphoneVolume.setter - def leftHeadphoneVolume(self, value:float) -> None: - self._leftHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - self._leftHeadphoneVolumeSet = True + def left_headphone_volume(self) -> float: + return map_range(max(self._left_headphone_volume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @left_headphone_volume.setter + def left_headphone_volume(self, value:float) -> None: + self._left_headphone_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._left_headphone_volume_set = True - _rightHeadphoneVolume = WOBits(7, _REG_ROUT1_VOLUME, 0) - _rightHeadphoneVolumeSet = WOBit(_REG_ROUT1_VOLUME, 8) + _right_headphone_volume = WOBits(7, _REG_ROUT1_VOLUME, 0) + _right_headphone_volume_set = WOBit(_REG_ROUT1_VOLUME, 8) @property - def rightHeadphoneVolume(self) -> float: - return map_range(max(self._rightHeadphoneVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @rightHeadphoneVolume.setter - def rightHeadphoneVolume(self, value:float) -> None: - self._rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - self._rightHeadphoneVolumeSet = True + def right_headphone_volume(self) -> float: + return map_range(max(self._right_headphone_volume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @right_headphone_volume.setter + def right_headphone_volume(self, value:float) -> None: + self._right_headphone_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._right_headphone_volume_set = True @property - def headphoneVolume(self) -> int: - return max(self.leftHeadphoneVolume, self.rightHeadphoneVolume) - @headphoneVolume.setter - def headphoneVolume(self, value:float) -> None: - self._leftHeadphoneVolume = self._rightHeadphoneVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) - self._leftHeadphoneVolumeSet = self._rightHeadphoneVolumeSet = True + def headphone_volume(self) -> int: + return max(self.left_headphone_volume, self.right_headphone_volume) + @headphone_volume.setter + def headphone_volume(self, value:float) -> None: + self._left_headphone_volume = self._right_headphone_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + self._left_headphone_volume_set = self._right_headphone_volume_set = True - leftHeadphoneZeroCross = WOBit(_REG_LOUT1_VOLUME, 7) - rightHeadphoneZeroCross = WOBit(_REG_LOUT1_VOLUME, 7) + left_headphone_zero_cross = WOBit(_REG_LOUT1_VOLUME, 7) + right_headphone_zero_cross = WOBit(_REG_LOUT1_VOLUME, 7) @property - def headphoneZeroCross(self) -> bool: - return self.leftHeadphoneZeroCross and self.rightHeadphoneZeroCross - @headphoneZeroCross.setter - def headphoneZeroCross(self, value:bool) -> None: - self.leftHeadphoneZeroCross = self.rightHeadphoneZeroCross = value + def headphone_zero_cross(self) -> bool: + return self.left_headphone_zero_cross and self.right_headphone_zero_cross + @headphone_zero_cross.setter + def headphone_zero_cross(self, value:bool) -> None: + self.left_headphone_zero_cross = self.right_headphone_zero_cross = value ## Speakers - _leftSpeaker = WOBit(_REG_PWR_MGMT_2, 4) - _leftSpeakerAmp = WOBit(_REG_CLASS_D_CONTROL_1, 6) + _left_speaker = WOBit(_REG_PWR_MGMT_2, 4) + _left_speaker_amp = WOBit(_REG_CLASS_D_CONTROL_1, 6) @property - def leftSpeaker(self) -> bool: - return self._leftSpeaker and self._leftSpeakerAmp - @leftSpeaker.setter - def leftSpeaker(self, value:bool) -> None: - self._leftSpeaker = self._leftSpeakerAmp = value + def left_speaker(self) -> bool: + return self._left_speaker and self._left_speaker_amp + @left_speaker.setter + def left_speaker(self, value:bool) -> None: + self._left_speaker = self._left_speaker_amp = value - _rightSpeaker = WOBit(_REG_PWR_MGMT_2, 3) - _rightSpeakerAmp = WOBit(_REG_CLASS_D_CONTROL_1, 7) + _right_speaker = WOBit(_REG_PWR_MGMT_2, 3) + _right_speaker_amp = WOBit(_REG_CLASS_D_CONTROL_1, 7) @property - def rightSpeaker(self) -> bool: - return self._rightSpeaker and self._rightSpeakerAmp - @rightSpeaker.setter - def rightSpeaker(self, value:bool) -> None: - self._rightSpeaker = self._rightSpeakerAmp = value + def right_speaker(self) -> bool: + return self._right_speaker and self._right_speaker_amp + @right_speaker.setter + def right_speaker(self, value:bool) -> None: + self._right_speaker = self._right_speaker_amp = value @property def speaker(self) -> bool: - return self.leftSpeaker and self.rightSpeaker + return self.left_speaker and self.right_speaker @speaker.setter def speaker(self, value:bool) -> None: - self.leftSpeaker = self.rightSpeaker = value + self.left_speaker = self.right_speaker = value - _leftSpeakerVolume = WOBits(7, _REG_LOUT2_VOLUME, 0) - _leftSpeakerVolumeSet = WOBit(_REG_LOUT2_VOLUME, 8) + _left_speaker_volume = WOBits(7, _REG_LOUT2_VOLUME, 0) + _left_speaker_volume_set = WOBit(_REG_LOUT2_VOLUME, 8) @property - def leftSpeakerVolume(self) -> float: - return map_range(max(self._leftSpeakerVolume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @leftSpeakerVolume.setter - def leftSpeakerVolume(self, value:float) -> None: - self._leftSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - self._leftSpeakerSet = True + def left_speaker_volume(self) -> float: + return map_range(max(self._left_speaker_volume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @left_speaker_volume.setter + def left_speaker_volume(self, value:float) -> None: + self._left_speaker_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._left_speaker_set = True - _rightSpeakerVolume = WOBits(7, _REG_ROUT2_VOLUME, 0) - _rightSpeakerVolumeSet = WOBit(_REG_ROUT2_VOLUME, 8) + _right_speaker_volume = WOBits(7, _REG_ROUT2_VOLUME, 0) + _right_speaker_volume_set = WOBit(_REG_ROUT2_VOLUME, 8) @property - def rightSpeakerVolume(self) -> float: - return map_range(max(self.rightSpeakerVolume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) - @rightSpeakerVolume.setter - def rightSpeakerVolume(self, value:float) -> None: - self._rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) - self._rightSpeakerVolumeSet = True - + def right_speaker_volume(self) -> float: + return map_range(max(self._right_speaker_volume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + @right_speaker_volume.setter + def right_speaker_volume(self, value:float) -> None: + self._right_speaker_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._right_speaker_volume_set = True @property - def speakerVolume(self) -> int: - return max(self.leftSpeakerVolume, self.rightSpeakerVolume) - @speakerVolume.setter - def speakerVolume(self, value:float) -> None: - self._leftSpeakerVolume = self._rightSpeakerVolume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) - self._leftSpeakerVolumeSet = self._rightSpeakerVolumeSet = True + def speaker_volume(self) -> int: + return max(self.left_speaker_volume, self.right_speaker_volume) + @speaker_volume.setter + def speaker_volume(self, value:float) -> None: + self._left_speaker_volume = self._right_speaker_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._left_speaker_volume_set = self._right_speaker_volume_set = True - leftSpeakerZeroCross = WOBit(_REG_LOUT2_VOLUME, 7) - rightSpeakerZeroCross = WOBit(_REG_LOUT2_VOLUME, 7) + left_speaker_zero_cross = WOBit(_REG_LOUT2_VOLUME, 7) + right_speaker_zero_cross = WOBit(_REG_LOUT2_VOLUME, 7) @property - def speakerZeroCross(self) -> bool: - return self.leftSpeakerZeroCross and self.rightSpeakerZeroCross - @speakerZeroCross.setter - def speakerZeroCross(self, value:bool) -> None: - self.leftSpeakerZeroCross = self.rightSpeakerZeroCross = value + def speaker_zero_cross(self) -> bool: + return self.left_speaker_zero_cross and self.right_speaker_zero_cross + @speaker_zero_cross.setter + def speaker_zero_cross(self, value:bool) -> None: + self.left_speaker_zero_cross = self.right_speaker_zero_cross = value - _speakerDcGain = WOBits(3, _REG_CLASS_D_CONTROL_3, 3) + _speaker_dc_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 3) @property - def speakerDcGain(self) -> int: - return self._speakerDcGain - @speakerDcGain.setter - def speakerDcGain(self, value:int) -> None: - self._speakerDcGain = min(value, 5) + def speaker_dc_gain(self) -> int: + return self._speaker_dc_gain + @speaker_dc_gain.setter + def speaker_dc_gain(self, value:int) -> None: + self._speaker_dc_gain = min(value, 5) - _speakerAcGain = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) + _speaker_ac_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) @property - def speakerAcGain(self) -> int: - return self._speakerAcGain - @speakerAcGain.setter - def speakerAcGain(self, value:int) -> None: - self._speakerAcGain = min(value, 5) + def speaker_ac_gain(self) -> int: + return self._speaker_ac_gain + @speaker_ac_gain.setter + def speaker_ac_gain(self, value:int) -> None: + self._speaker_ac_gain = min(value, 5) # Digital Audio Interface Control loopback = WOBit(_REG_AUDIO_INTERFACE_2, 0) pll = WOBit(_REG_PWR_MGMT_2, 0) - pllPrescaleDiv2 = WOBit(_REG_PWR_MGMT_2, 4) - pllN = WOBits(4, _REG_PLL_N, 0) + pll_prescale_div2 = WOBit(_REG_PWR_MGMT_2, 4) + pll_n = WOBits(4, _REG_PLL_N, 0) - _pllK1 = WOBits(6, _REG_PLL_K_1, 0) - _pllK2 = WOBits(9, _REG_PLL_K_2, 0) - _pllK3 = WOBits(9, _REG_PLL_K_3, 0) + _pll_k1 = WOBits(6, _REG_PLL_K_1, 0) + _pll_k2 = WOBits(9, _REG_PLL_K_2, 0) + _pll_k3 = WOBits(9, _REG_PLL_K_3, 0) @property - def pllK(self) -> int: - return self._pllK1 << 18 + self._pllK2 << 9 + self._pllK3 - @pllK.setter - def pllK(self, value:int) -> None: - self._pllK1 = (value >> 18) & 0b111111 - self._pllK2 = (value >> 9) & 0b111111111 - self._pllK3 = value & 0b111111111 + def pll_k(self) -> int: + return self._pll_k1 << 18 + self._pll_k2 << 9 + self._pll_k3 + @pll_k.setter + def pll_k(self, value:int) -> None: + self._pll_k1 = (value >> 18) & 0b111111 + self._pll_k2 = (value >> 9) & 0b111111111 + self._pll_k3 = value & 0b111111111 - clockFractionalMode = WOBit(_REG_PLL_N, 5) + clock_fractional_mode = WOBit(_REG_PLL_N, 5) - clockFromPLL = WOBit(_REG_CLOCKING_1, 0) + clock_from_pLL = WOBit(_REG_CLOCKING_1, 0) - _systemClockDivider = WOBits(2, _REG_CLOCKING_1, 1) + _system_clock_divider = WOBits(2, _REG_CLOCKING_1, 1) @property - def systemClockDiv2(self) -> bool: - return self._systemClockDivider == _SYSCLK_DIV_BY_2 - @systemClockDiv2.setter - def systemClockDiv2(self, value:bool) -> None: - self._systemClockDivider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 + def system_clock_div2(self) -> bool: + return self._system_clock_divider == _SYSCLK_DIV_BY_2 + @system_clock_div2.setter + def system_clock_div2(self, value:bool) -> None: + self._system_clock_divider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 - _adcClockDivider = WOBits(3, _REG_CLOCKING_1, 6) + _adc_clock_divider = WOBits(3, _REG_CLOCKING_1, 6) @property - def adcClockDivider(self) -> int: - return _ADCDACDIV[min(self._adcClockDivider, len(_ADCDACDIV))] - @adcClockDivider.setter - def adcClockDivider(self, value:int) -> None: + def adc_clock_divider(self) -> int: + return _ADCDACDIV[min(self._adc_clock_divider, len(_ADCDACDIV))] + @adc_clock_divider.setter + def adc_clock_divider(self, value:int) -> None: value = round(value * 2.0) / 2.0 if value in _ADCDACDIV: - self._adcClockDivider = _ADCDACDIV.index(value) + self._adc_clock_divider = _ADCDACDIV.index(value) - _dacClockDivider = WOBits(3, _REG_CLOCKING_1, 3) + _dac_clock_divider = WOBits(3, _REG_CLOCKING_1, 3) @property - def dacClockDivider(self) -> int: - return _ADCDACDIV[min(self._dacClockDivider, len(_ADCDACDIV))] - @dacClockDivider.setter - def dacClockDivider(self, value:int) -> None: + def dac_clock_divider(self) -> int: + return _ADCDACDIV[min(self._dac_clock_divider, len(_ADCDACDIV))] + @dac_clock_divider.setter + def dac_clock_divider(self, value:int) -> None: value = round(value * 2.0) / 2.0 if value in _ADCDACDIV: - self._dacClockDivider = _ADCDACDIV.index(value) + self._dac_clock_divider = _ADCDACDIV.index(value) - _baseClockDivider = WOBits(4, _REG_CLOCKING_2, 0) + _base_clock_divider = WOBits(4, _REG_CLOCKING_2, 0) @property - def baseClockDivider(self) -> float: - return _BCLKDIV[min(self._baseClockDivider, len(_BCLKDIV))] - @baseClockDivider.setter - def baseClockDivider(self, value:float) -> None: + def base_clock_divider(self) -> float: + return _BCLKDIV[min(self._base_clock_divider, len(_BCLKDIV))] + @base_clock_divider.setter + def base_clock_divider(self, value:float) -> None: value = round(value * 2.0) / 2.0 if value in _BCLKDIV: - self._baseClockDivider = _BCLKDIV.index(value) + self._base_clock_divider = _BCLKDIV.index(value) - _ampClockDivider = WOBits(3, _REG_CLOCKING_2, 6) + _amp_clock_divider = WOBits(3, _REG_CLOCKING_2, 6) @property - def ampClockDivider(self) -> float: - return _DCLKDIV[min(self._ampClockDivider, len(_DCLKDIV))] - @ampClockDivider.setter - def ampClockDivider(self, value:float) -> None: + def amp_clock_divider(self) -> float: + return _DCLKDIV[min(self._amp_clock_divider, len(_DCLKDIV))] + @amp_clock_divider.setter + def amp_clock_divider(self, value:float) -> None: value = round(value * 2.0) / 2.0 if value in _DCLKDIV: - self._ampClockDivider = _DCLKDIV.index(value) + self._amp_clock_divider = _DCLKDIV.index(value) ## Mode - masterMode = WOBit(_REG_AUDIO_INTERFACE_1, 6) + master_mode = WOBit(_REG_AUDIO_INTERFACE_1, 6) - _wordLength = WOBits(2, _REG_AUDIO_INTERFACE_1, 2) + _word_length = WOBits(2, _REG_AUDIO_INTERFACE_1, 2) @property - def wordLength(self) -> int: - value = self._wordLength + def word_length(self) -> int: + value = self._word_length if value == 3: return 32 else: return 16 + 4 * value - @wordLength.setter - def wordLength(self, value:int) -> None: - self._wordLength = (min(value, 28) - 16) // 4 + @word_length.setter + def word_length(self, value:int) -> None: + self._word_length = (min(value, 28) - 16) // 4 - wordSelectInvert = WOBit(_REG_AUDIO_INTERFACE_1, 4) + word_select_invert = WOBit(_REG_AUDIO_INTERFACE_1, 4) - adcChannelSwap = WOBit(_REG_AUDIO_INTERFACE_1, 8) + adc_channel_swap = WOBit(_REG_AUDIO_INTERFACE_1, 8) - _vrefOutputDisable = WOBit(_REG_ADDITIONAL_CONTROL_3, 6) + _vref_output_disable = WOBit(_REG_ADDITIONAL_CONTROL_3, 6) @property - def vrefOutput(self) -> bool: - return not self._vrefOutputDisable - @vrefOutput.setter - def vrefOutput(self, value:bool) -> None: - self._vrefOutputDisable = not value + def vref_output(self) -> bool: + return not self._vref_output_disable + @vref_output.setter + def vref_output(self, value:bool) -> None: + self._vref_output_disable = not value _vsel = WOBits(2, _REG_ADDITIONAL_CONTROL_1, 6) @property - def powerSupply(self) -> float: + def power_supply(self) -> float: return (constrain(self._vsel, 1, 2) - 1) * 0.6 + 2.7 - @powerSupply.setter - def powerSupply(self, value:float) -> None: + @power_supply.setter + def power_supply(self, value:float) -> None: self._vsel = (constrain(value, 2.7, 3.3) - 2.7) // 0.6 * 2 + 1 ## GPIO - gpioOutput = WOBit(_REG_AUDIO_INTERFACE_2, 6) - gpioOutputMode = WOBits(3, _REG_ADDITIONAL_CONTROL_4, 4) - gpioOutputInvert = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) + gpio_output = WOBit(_REG_AUDIO_INTERFACE_2, 6) + gpio_output_mode = WOBits(3, _REG_ADDITIONAL_CONTROL_4, 4) + gpio_output_invert = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) - _gpioClockDivider = WOBits(3, _REG_CLOCKING_2, 6) + _gpio_clock_divider = WOBits(3, _REG_CLOCKING_2, 6) @property - def gpioClockDivider(self) -> int: - return self._gpioClockDivider - @gpioClockDivider.setter - def gpioClockDivider(self, value:int) -> None: - self._gpioClockDivider = min(value, 5) + def gpio_clock_divider(self) -> int: + return self._gpio_clock_divider + @gpio_clock_divider.setter + def gpio_clock_divider(self, value:int) -> None: + self._gpio_clock_divider = min(value, 5) @property - def sampleRate(self) -> int: - return self._sampleRate - @sampleRate.setter - def sampleRate(self, value:int) -> None: + def sample_rate(self) -> int: + return self._sample_rate + @sample_rate.setter + def sample_rate(self, value:int) -> None: # MCLK = 24 MHz self.pll = True # Needed for class-d amp clock - self.clockFractionalMode = True - self.clockFromPLL = True + self.clock_fractional_mode = True + self.clock_from_pLL = True - self.pllPrescaleDiv2 = True - self.systemClockDiv2 = True - self.baseClockDivider = 4.0 - self.ampClockDivider = 16.0 + self.pll_prescale_div2 = True + self.system_clock_div2 = True + self.base_clock_divider = 4.0 + self.amp_clock_divider = 16.0 if value in [8000, 12000, 16000, 24000, 32000, 48000]: # SYSCLK = 12.288 MHz - # DCLK = 768.0kHz - self.pllN = 8 - self.pllK = 0x3126E8 - self.adcClockDivider = self.dacClockDivider = 48000 / value + # DCLK = 768.0k_hz + self.pll_n = 8 + self.pll_k = 0x3126_e8 + self.adc_clock_divider = self.dac_clock_divider = 48000 / value elif value in [11025, 22050, 44100]: # SYSCLK = 11.2896 MHz - # DCLK = 705.6kHz - self.pllN = 7 - self.pllK = 0x86C226 - self.adcClockDivider = self.dacClockDivider = 44100 / value + # DCLK = 705.6k_hz + self.pll_n = 7 + self.pll_k = 0x86_c226 + self.adc_clock_divider = self.dac_clock_divider = 44100 / value else: raise Exception("Invalid sample rate") - self._sampleRate = value + self._sample_rate = value def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR) -> None: self.i2c_device = I2CDevice(i2c_bus, address) - self._sampleRate = None + self._sample_rate = None self._registers = [ 0x0097, # R0 (0x00) From 3874f23a6cfc65c6e05af261c43427479a65cd8d Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 14:14:55 -0500 Subject: [PATCH 32/56] Move left/right to prepend attribute name. --- adafruit_wm8960.py | 361 +++++++++++++++++++++++---------------------- 1 file changed, 181 insertions(+), 180 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 060647d..367979b 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -310,182 +310,183 @@ class WM8960: vref = WOBit(_REG_PWR_MGMT_1, 6) - analog_input_left = WOBit(_REG_PWR_MGMT_1, 5) - analog_input_right = WOBit(_REG_PWR_MGMT_1, 4) + left_input = WOBit(_REG_PWR_MGMT_1, 5) + right_input = WOBit(_REG_PWR_MGMT_1, 4) @property - def analog_input(self) -> bool: - return self.analog_input_left and self.analog_input_right - @analog_input.setter - def analog_input(self, value:bool) -> None: - self.analog_input_left = self.analog_input_right = value + def input(self) -> bool: + return self.left_input and self.right_input + @input.setter + def input(self, value:bool) -> None: + self.left_input = self.right_input = value # MIC ## PWR_MGMT - mic_left = WOBit(_REG_PWR_MGMT_3, 5) - mic_right = WOBit(_REG_PWR_MGMT_3, 4) + left_mic = WOBit(_REG_PWR_MGMT_3, 5) + right_mic = WOBit(_REG_PWR_MGMT_3, 4) @property def mic(self) -> bool: - return self.mic_left and self.mic_right + return self.left_mic and self.right_mic @mic.setter def mic(self, value:bool) -> None: - self.mic_left = self.mic_right = value + self.left_mic = self.right_mic = value ## SIGNAL_PATH - _mic_input1_left = WOBit(_REG_ADCL_SIGNAL_PATH, 8) - _mic_input2_left = WOBit(_REG_ADCL_SIGNAL_PATH, 6) - _mic_input3_left = WOBit(_REG_ADCL_SIGNAL_PATH, 7) + left_mic_inverting_input = WOBit(_REG_ADCL_SIGNAL_PATH, 8) + right_mic_inverting_input = WOBit(_REG_ADCR_SIGNAL_PATH, 8) - _mic_input1_right = WOBit(_REG_ADCR_SIGNAL_PATH, 8) - _mic_input2_right = WOBit(_REG_ADCR_SIGNAL_PATH, 6) - _mic_input3_right = WOBit(_REG_ADCR_SIGNAL_PATH, 7) + _left_mic_input2 = WOBit(_REG_ADCL_SIGNAL_PATH, 6) + _left_mic_input3 = WOBit(_REG_ADCL_SIGNAL_PATH, 7) @property - def mic_signal_left(self) -> int: - if self._mic_input2_left: + def left_mic_input(self) -> int: + if self._left_mic_input2: return MIC_INPUT2 - elif self._mic_input3_left: + elif self._left_mic_input3: return MIC_INPUT3 else: return MIC_VMID - @mic_signal_left.setter - def mic_signal_left(self, signal:int) -> None: - self._mic_input2_left = signal == MIC_INPUT2 - self._mic_input3_left = signal == MIC_INPUT3 + @left_mic_input.setter + def left_mic_input(self, input:int) -> None: + self._left_mic_input2 = input == MIC_INPUT2 + self._left_mic_input3 = input == MIC_INPUT3 + + _right_mic_input2 = WOBit(_REG_ADCR_SIGNAL_PATH, 6) + _right_mic_input3 = WOBit(_REG_ADCR_SIGNAL_PATH, 7) @property - def mic_signal_right(self) -> int: - if self._mic_input2_right: + def right_mic_input(self) -> int: + if self._right_mic_input2: return MIC_INPUT2 - elif self._mic_input3_right: + elif self._right_mic_input3: return MIC_INPUT3 else: return MIC_VMID - @mic_signal_right.setter - def mic_signal_right(self, signal:int) -> None: - self._mic_input2_right = signal == MIC_INPUT2 - self._mic_input3_right = signal == MIC_INPUT3 + @right_mic_input.setter + def right_mic_input(self, input:int) -> None: + self._right_mic_input2 = input == MIC_INPUT2 + self._right_mic_input3 = input == MIC_INPUT3 @property - def mic_signal(self) -> int: + def mic_input(self) -> int: # NOTE: Not checking right signal - return self.mic_signal_left - @mic_signal.setter - def mic_signal(self, signal:int) -> None: - self.mic_signal_left = self.mic_signal_right = signal + return self.left_mic_input + @mic_input.setter + def mic_input(self, input:int) -> None: + self.left_mic_input = self.right_mic_input = input ## Boost - mic_left_boost = WOBit(_REG_ADCL_SIGNAL_PATH, 3) - mic_right_boost = WOBit(_REG_ADCR_SIGNAL_PATH, 3) + left_mic_boost = WOBit(_REG_ADCL_SIGNAL_PATH, 3) + right_mic_boost = WOBit(_REG_ADCR_SIGNAL_PATH, 3) @property def mic_boost(self) -> None: - return self.mic_left_boost and self.mic_right_boost + return self.left_mic_boost and self.right_mic_boost @mic_boost.setter def mic_boost(self, value:int) -> None: - self.mic_left_boost = self.mic_right_boost = value + self.left_mic_boost = self.right_mic_boost = value - mic_boost_gain_left = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) - mic_boost_gain_right = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) + left_mic_boost_gain = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) + right_mic_boost_gain = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) @property def mic_boost_gain(self) -> None: - return max(self.mic_boost_gain_left, self.mic_boost_gain_right) + return max(self.left_mic_boost_gain, self.right_mic_boost_gain) @mic_boost_gain.setter def mic_boost_gain(self, value:int) -> None: - self.mic_gain_left = self.mic_gain_right = value + self.left_mic_boost_gain = self.right_mic_boost_gain = value ## Volume - _mic_left_volume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) - _mic_left_volume_set = WOBit(_REG_LEFT_INPUT_VOLUME, 8) + _left_mic_volume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) + _left_mic_volume_set = WOBit(_REG_LEFT_INPUT_VOLUME, 8) - _mic_right_volume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) - _mic_right_volume_set = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) + _right_mic_volume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) + _right_mic_volume_set = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) @property - def mic_left_volume(self) -> float: - return map_range(self._mic_left_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) - @mic_left_volume.setter - def mic_left_volume(self, value:float) -> None: - self._mic_left_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) - self._mic_left_volume_set = True + def left_mic_volume(self) -> float: + return map_range(self._left_mic_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) + @left_mic_volume.setter + def left_mic_volume(self, value:float) -> None: + self._left_mic_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._left_mic_volume_set = True @property - def mic_right_volume(self) -> float: - return map_range(self._mic_right_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) - @mic_right_volume.setter - def mic_right_volume(self, value:float) -> None: - self._mic_right_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) - self._mic_right_volume_set = True + def right_mic_volume(self) -> float: + return map_range(self._right_mic_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) + @right_mic_volume.setter + def right_mic_volume(self, value:float) -> None: + self._right_mic_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._right_mic_volume_set = True @property def mic_volume(self) -> float: - return max(self.mic_left_volume, self.mic_right_volume) + return max(self.left_mic_volume, self.right_mic_volume) @mic_volume.setter def mic_volume(self, value:float) -> None: - self._mic_left_volume = self._mic_right_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) - self._mic_left_volume_set = self._mic_right_volume_set = True + self._left_mic_volume = self._right_mic_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._left_mic_volume_set = self._right_mic_volume_set = True ## Zero Cross - mic_left_zero_cross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) - mic_right_zero_cross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) + left_mic_zero_cross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) + right_mic_zero_cross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) @property def mic_zero_cross(self) -> bool: - return self.mic_left_zero_cross and self.mic_right_zero_cross + return self.left_mic_zero_cross and self.right_mic_zero_cross @mic_zero_cross.setter def mic_zero_cross(self, value:bool) -> None: - self.mic_left_zero_cross = self.mic_right_zero_cross = value + self.left_mic_zero_cross = self.right_mic_zero_cross = value ## Mute - _mic_left_mute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) - _mic_right_mute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) + _left_mic_mute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) + _right_mic_mute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) @property - def mic_left_mute(self) -> bool: - return self._mic_left_mute - @mic_left_mute.setter - def mic_left_mute(self, value:bool) -> None: - self._mic_left_mute = value - self._mic_left_volume_set = True + def left_mic_mute(self) -> bool: + return self._left_mic_mute + @left_mic_mute.setter + def left_mic_mute(self, value:bool) -> None: + self._left_mic_mute = value + self._left_mic_volume_set = True @property - def mic_right_mute(self) -> bool: - return self._mic_right_mute - @mic_right_mute.setter - def mic_right_mute(self, value:bool) -> None: - self._mic_right_mute = value - self._mic_right_volume_set = True + def right_mic_mute(self) -> bool: + return self._right_mic_mute + @right_mic_mute.setter + def right_mic_mute(self, value:bool) -> None: + self._right_mic_mute = value + self._right_mic_volume_set = True @property def mic_mute(self) -> bool: - return self._mic_left_mute and self._mic_right_mute + return self._left_mic_mute and self._right_mic_mute @mic_mute.setter def mic_mute(self, value:bool) -> None: - self._mic_left_mute = self._mic_right_mute = value + self._left_mic_mute = self._right_mic_mute = value # Boost Mixer - input2_left_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) - input2_right_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) + left_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) + right_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) @property def input2_boost(self) -> int: - return self.input2_left_boost + return self.left_input2_boost @input2_boost.setter def input2_boost(self, value:int) -> None: - self.input2_left_boost = self.input2_right_boost = value + self.left_input2_boost = self.right_input2_boost = value - input3_left_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) - input3_right_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) + left_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) + right_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) # Mic Bias @@ -494,66 +495,66 @@ def input2_boost(self, value:int) -> None: @property def input3_boost(self) -> int: - return self.input3_left_boost + return self.left_input3_boost @input3_boost.setter def input3_boost(self, value:int) -> None: - self.input3_left_boost = self.input3_right_boost = value + self.left_input3_boost = self.right_input3_boost = value # ADC - adc_left = WOBit(_REG_PWR_MGMT_1, 3) - adc_right = WOBit(_REG_PWR_MGMT_1, 2) + left_adc = WOBit(_REG_PWR_MGMT_1, 3) + right_adc = WOBit(_REG_PWR_MGMT_1, 2) @property def adc(self) -> bool: - return self.adc_left and self.adc_right + return self.left_adc and self.right_adc @adc.setter def adc(self, value:bool) -> None: - self.adc_left = self.adc_right = value + self.left_adc = self.right_adc = value ## Volume - _adc_left_volume = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) - _adc_left_volume_set = WOBit(_REG_LEFT_ADC_VOLUME, 8) + _left_adc_volume = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) + _left_adc_volume_set = WOBit(_REG_LEFT_ADC_VOLUME, 8) @property - def adc_left_volume(self) -> float: - return map_range(max(self._adc_left_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) - @adc_left_volume.setter - def adc_left_volume(self, value:float) -> None: - self._adc_left_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - self._adc_left_volume_set = True + def left_adc_volume(self) -> float: + return map_range(max(self._left_adc_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + @left_adc_volume.setter + def left_adc_volume(self, value:float) -> None: + self._left_adc_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._left_adc_volume_set = True - _adc_right_volume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) - _adc_right_volume_set = WOBit(_REG_RIGHT_ADC_VOLUME, 8) + _right_adc_volume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) + _right_adc_volume_set = WOBit(_REG_RIGHT_ADC_VOLUME, 8) @property - def adc_right_volume(self) -> float: - return map_range(max(self._adc_right_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) - @adc_right_volume.setter - def adc_right_volume(self, value:float) -> None: - self._adc_right_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - self._adc_right_volume_set = True + def right_adc_volume(self) -> float: + return map_range(max(self._right_adc_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + @right_adc_volume.setter + def right_adc_volume(self, value:float) -> None: + self._right_adc_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._right_adc_volume_set = True @property def adc_volume(self) -> int: - return max(self.adc_left_volume, self.adc_right_volume) + return max(self.left_adc_volume, self.right_adc_volume) @adc_volume.setter def adc_volume(self, value:float) -> None: - self._adc_left_volume = self._adc_right_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) - self._adc_left_volume_set = self._adc_right_volume_set = True + self._left_adc_volume = self._right_adc_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._left_adc_volume_set = self._right_adc_volume_set = True # ALC - alc_left = WOBit(_REG_ALC1, 7) - alc_right = WOBit(_REG_ALC1, 8) + left_alc = WOBit(_REG_ALC1, 7) + right_alc = WOBit(_REG_ALC1, 8) @property def alc(self) -> bool: - return self.alc_left and self.alc_right + return self.left_alc and self.right_alc @alc.setter def alc(self, value:bool) -> None: - self.alc_left = self.alc_right = value + self.left_alc = self.right_alc = value _alc_target = WOBits(4, _REG_ALC1, 0) @@ -631,45 +632,45 @@ def noise_gate_threshold(self, value:float) -> None: # DAC - dac_left = WOBit(_REG_PWR_MGMT_2, 8) - dac_right = WOBit(_REG_PWR_MGMT_2, 7) + left_dac = WOBit(_REG_PWR_MGMT_2, 8) + right_dac = WOBit(_REG_PWR_MGMT_2, 7) @property def dac(self) -> bool: - return self.dac_left and self.dac_right + return self.left_dac and self.right_dac @dac.setter def dac(self, value:bool) -> None: - self.dac_left = self.dac_right = value + self.left_dac = self.right_dac = value - _dac_left_volume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) - _dac_left_volume_set = WOBit(_REG_LEFT_DAC_VOLUME, 8) + _left_dac_volume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) + _left_dac_volume_set = WOBit(_REG_LEFT_DAC_VOLUME, 8) @property - def dac_left_volume(self) -> float: - return map_range(max(self._dac_left_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) - @dac_left_volume.setter - def dac_left_volume(self, value:float) -> None: - self._dac_left_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - self._dac_left_volume_set = True + def left_dac_volume(self) -> float: + return map_range(max(self._left_dac_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + @left_dac_volume.setter + def left_dac_volume(self, value:float) -> None: + self._left_dac_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._left_dac_volume_set = True - _dac_right_volume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) - _dac_right_volume_set = WOBit(_REG_RIGHT_DAC_VOLUME, 8) + _right_dac_volume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) + _right_dac_volume_set = WOBit(_REG_RIGHT_DAC_VOLUME, 8) @property - def dac_right_volume(self) -> float: - return map_range(max(self._dac_right_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) - @dac_right_volume.setter - def dac_right_volume(self, value:float) -> None: - self._dac_right_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - self._dac_right_volume_set = True + def right_dac_volume(self) -> float: + return map_range(max(self._right_dac_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + @right_dac_volume.setter + def right_dac_volume(self, value:float) -> None: + self._right_dac_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._right_dac_volume_set = True @property def dac_volume(self) -> int: - return max(self.dac_left_volume, self.dac_right_volume) + return max(self.left_dac_volume, self.right_dac_volume) @dac_volume.setter def dac_volume(self, value:float) -> None: - self._dac_left_volume = self._dac_right_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) - self._dac_left_volume_set = self._dac_right_volume_set = True + self._left_dac_volume = self._right_dac_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._left_dac_volume_set = self._right_dac_volume_set = True dac_mute = WOBit(_REG_ADC_DAC_CTRL_1, 3) dac_soft_mute = WOBit(_REG_ADC_DAC_CTRL_2, 3) @@ -697,86 +698,86 @@ def output(self, value:bool): ## DAC Output - dac_left_output = WOBit(_REG_LEFT_OUT_MIX, 8) - dac_right_output = WOBit(_REG_RIGHT_OUT_MIX, 8) + left_dac_output = WOBit(_REG_LEFT_OUT_MIX, 8) + right_dac_output = WOBit(_REG_RIGHT_OUT_MIX, 8) @property def dac_output(self) -> bool: - return self.dac_left_output and self.dac_right_output + return self.left_dac_output and self.right_dac_output @dac_output.setter def dac_output(self, value:bool) -> None: - self.dac_left_output = self.dac_right_output = value + self.left_dac_output = self.right_dac_output = value ## Input 3 Output - input3_left_output = WOBit(_REG_LEFT_OUT_MIX, 7) - input3_right_output = WOBit(_REG_RIGHT_OUT_MIX, 7) + left_input3_output = WOBit(_REG_LEFT_OUT_MIX, 7) + right_input3_output = WOBit(_REG_RIGHT_OUT_MIX, 7) @property def input3_output(self) -> bool: - return self.input3_left_output and self.input3_right_output + return self.left_input3_output and self.right_input3_output @input3_output.setter def input3_output(self, value:bool) -> None: - self.input3_left_output = self.input3_right_output = value + self.left_input3_output = self.right_input3_output = value - _input3_left_output_volume = WOBits(3, _REG_LEFT_OUT_MIX, 4) + _left_input3_output_volume = WOBits(3, _REG_LEFT_OUT_MIX, 4) @property - def input3_left_output_volume(self) -> float: - return map_range(self._input3_left_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @input3_left_output_volume.setter - def input3_left_output_volume(self, value:float) -> None: - self._input3_left_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def left_input3_output_volume(self) -> float: + return map_range(self._left_input3_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @left_input3_output_volume.setter + def left_input3_output_volume(self, value:float) -> None: + self._left_input3_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - _input3_right_output_volume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) + _right_input3_output_volume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) @property - def input3_right_output_volume(self) -> float: - return map_range(self._input3_right_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @input3_right_output_volume.setter - def input3_right_output_volume(self, value:float) -> None: - self._input3_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def right_input3_output_volume(self) -> float: + return map_range(self._right_input3_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @right_input3_output_volume.setter + def right_input3_output_volume(self, value:float) -> None: + self._right_input3_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) @property def input3_output_volume(self) -> float: - return max(self.input3_left_output_volume, self.input3_right_output_volume) + return max(self.left_input3_output_volume, self.right_input3_output_volume) @input3_output_volume.setter def input3_output_volume(self, value:float) -> None: - self._input3_left_output_volume = self._input3_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._left_input3_output_volume = self._right_input3_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) ## MIC Boost Mixer Output - mic_boost_left_output = WOBit(_REG_BYPASS_1, 7) - mic_boost_right_output = WOBit(_REG_BYPASS_2, 7) + left_mic_boost_output = WOBit(_REG_BYPASS_1, 7) + right_mic_boost_output = WOBit(_REG_BYPASS_2, 7) @property def mic_boost_output(self) -> bool: - return self.mic_boost_left_output and self.mic_boost_right_output + return self.left_mic_boost_output and self.right_mic_boost_output - _mic_boost_left_output_volume = WOBits(3, _REG_BYPASS_1, 4) + _left_mic_boost_output_volume = WOBits(3, _REG_BYPASS_1, 4) @property - def mic_boost_left_output_volume(self) -> float: - return map_range(self._mic_boost_left_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @mic_boost_left_output_volume.setter - def mic_boost_left_output_volume(self, value:float) -> None: - self._mic_boost_left_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def left_mic_boost_output_volume(self) -> float: + return map_range(self._left_mic_boost_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @left_mic_boost_output_volume.setter + def left_mic_boost_output_volume(self, value:float) -> None: + self._left_mic_boost_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) - _mic_boost_right_output_volume = WOBits(3, _REG_BYPASS_2, 4) + _right_mic_boost_output_volume = WOBits(3, _REG_BYPASS_2, 4) @property - def mic_boost_right_output_volume(self) -> float: - return map_range(self._mic_boost_right_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) - @mic_boost_right_output_volume.setter - def mic_boost_right_output_volume(self, value:float) -> None: - self._mic_boost_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + def right_mic_boost_output_volume(self) -> float: + return map_range(self._right_mic_boost_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + @right_mic_boost_output_volume.setter + def right_mic_boost_output_volume(self, value:float) -> None: + self._right_mic_boost_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) @property def mic_boost_output_volume(self) -> float: - return max(self.mic_boost_left_output_volume, self.mic_boost_right_output_volume) + return max(self.left_mic_boost_output_volume, self.right_mic_boost_output_volume) @mic_boost_output_volume.setter def mic_boost_output_volume(self, value:float) -> None: - self._mic_boost_left_output_volume = self._mic_boost_right_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._left_mic_boost_output_volume = self._right_mic_boost_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) ## Mono Output From 933e7f78224dc7489a89dd8155ab4eda8e6a8f3f Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 14:20:46 -0500 Subject: [PATCH 33/56] Make min/max constants that are relevant to attribute values public. --- adafruit_wm8960.py | 146 ++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 367979b..1f363b4 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -130,44 +130,44 @@ _SYSCLK_DIV_BY_2 = const(2) # Gain/Level mins, maxes, offsets and step-sizes -_MIC_GAIN_MIN = -17.25 -_MIC_GAIN_MAX = 30.00 +MIC_GAIN_MIN = -17.25 +MIC_GAIN_MAX = 30.00 -_ADC_VOLUME_MIN = -97.00 -_ADC_VOLUME_MAX = 30.00 +ADC_VOLUME_MIN = -97.00 +ADC_VOLUME_MAX = 30.00 -_DAC_VOLUME_MIN = -127.00 -_DAC_VOLUME_MAX = 0.00 +DAC_VOLUME_MIN = -127.00 +DAC_VOLUME_MAX = 0.00 -_ALC_TARGET_MIN = -22.50 -_ALC_TARGET_MAX = -1.50 +ALC_TARGET_MIN = -22.50 +ALC_TARGET_MAX = -1.50 -_ALC_MAX_GAIN_MIN = -12.00 -_ALC_MAX_GAIN_MAX = 30.00 +ALC_MAX_GAIN_MIN = -12.00 +ALC_MAX_GAIN_MAX = 30.00 -_ALC_MIN_GAIN_MIN = -17.25 -_ALC_MIN_GAIN_MAX = 24.75 +ALC_MIN_GAIN_MIN = -17.25 +ALC_MIN_GAIN_MAX = 24.75 -_GATE_THRESHOLD_MIN = -76.50 -_GATE_THRESHOLD_MAX = -30.00 +GATE_THRESHOLD_MIN = -76.50 +GATE_THRESHOLD_MAX = -30.00 -_OUTPUT_VOLUME_MIN = -21.00 -_OUTPUT_VOLUME_MAX = 0.00 +OUTPUT_VOLUME_MIN = -21.00 +OUTPUT_VOLUME_MAX = 0.00 -_AMP_VOLUME_MIN = -73.00 -_AMP_VOLUME_MAX = 6.00 +AMP_VOLUME_MIN = -73.00 +AMP_VOLUME_MAX = 6.00 # ALC Time mapping _ALC_ATTACK_MAX = const(10) -_ALC_ATTACK_TIME_MIN = 0.006 -_ALC_ATTACK_TIME_MAX = 6.140 +ALC_ATTACK_TIME_MIN = 0.006 +ALC_ATTACK_TIME_MAX = 6.140 _ALC_DECAY_MAX = const(10) -_ALC_DECAY_TIME_MIN = 0.024 -_ALC_DECAY_TIME_MAX = 24.580 +ALC_DECAY_TIME_MIN = 0.024 +ALC_DECAY_TIME_MAX = 24.580 -_ALC_HOLD_TIME_MIN = 0.00267 -_ALC_HOLD_TIME_MAX = 43.691 +ALC_HOLD_TIME_MIN = 0.00267 +ALC_HOLD_TIME_MAX = 43.691 # Speaker Boost Gains (DC and AC) SPEAKER_BOOST_GAIN_0DB = 0 @@ -411,18 +411,18 @@ def mic_boost_gain(self, value:int) -> None: @property def left_mic_volume(self) -> float: - return map_range(self._left_mic_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) + return map_range(self._left_mic_volume, 0, 63, MIC_GAIN_MIN, MIC_GAIN_MAX) @left_mic_volume.setter def left_mic_volume(self, value:float) -> None: - self._left_mic_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._left_mic_volume = round(map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63)) self._left_mic_volume_set = True @property def right_mic_volume(self) -> float: - return map_range(self._right_mic_volume, 0, 63, _MIC_GAIN_MIN, _MIC_GAIN_MAX) + return map_range(self._right_mic_volume, 0, 63, MIC_GAIN_MIN, MIC_GAIN_MAX) @right_mic_volume.setter def right_mic_volume(self, value:float) -> None: - self._right_mic_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._right_mic_volume = round(map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63)) self._right_mic_volume_set = True @property @@ -430,7 +430,7 @@ def mic_volume(self) -> float: return max(self.left_mic_volume, self.right_mic_volume) @mic_volume.setter def mic_volume(self, value:float) -> None: - self._left_mic_volume = self._right_mic_volume = round(map_range(value, _MIC_GAIN_MIN, _MIC_GAIN_MAX, 0, 63)) + self._left_mic_volume = self._right_mic_volume = round(map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63)) self._left_mic_volume_set = self._right_mic_volume_set = True ## Zero Cross @@ -519,10 +519,10 @@ def adc(self, value:bool) -> None: @property def left_adc_volume(self) -> float: - return map_range(max(self._left_adc_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + return map_range(max(self._left_adc_volume, 1), 1, 255, ADC_VOLUME_MIN, ADC_VOLUME_MAX) @left_adc_volume.setter def left_adc_volume(self, value:float) -> None: - self._left_adc_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._left_adc_volume = round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 0, 254) + 1.0) self._left_adc_volume_set = True _right_adc_volume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) @@ -530,10 +530,10 @@ def left_adc_volume(self, value:float) -> None: @property def right_adc_volume(self) -> float: - return map_range(max(self._right_adc_volume, 1), 1, 255, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX) + return map_range(max(self._right_adc_volume, 1), 1, 255, ADC_VOLUME_MIN, ADC_VOLUME_MAX) @right_adc_volume.setter def right_adc_volume(self, value:float) -> None: - self._right_adc_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._right_adc_volume = round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 0, 254) + 1.0) self._right_adc_volume_set = True @property @@ -541,7 +541,7 @@ def adc_volume(self) -> int: return max(self.left_adc_volume, self.right_adc_volume) @adc_volume.setter def adc_volume(self, value:float) -> None: - self._left_adc_volume = self._right_adc_volume = round(map_range(value, _ADC_VOLUME_MIN, _ADC_VOLUME_MAX, 0, 254) + 1.0) + self._left_adc_volume = self._right_adc_volume = round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 0, 254) + 1.0) self._left_adc_volume_set = self._right_adc_volume_set = True # ALC @@ -560,46 +560,46 @@ def alc(self, value:bool) -> None: @property def alc_target(self) -> float: - return map_range(self._alc_target, 0, 15, _ALC_TARGET_MIN, _ALC_TARGET_MAX) + return map_range(self._alc_target, 0, 15, ALC_TARGET_MIN, ALC_TARGET_MAX) @alc_target.setter def alc_target(self, value:float) -> None: - self._alc_target = round(map_range(value, _ALC_TARGET_MIN, _ALC_TARGET_MAX, 0, 15)) + self._alc_target = round(map_range(value, ALC_TARGET_MIN, ALC_TARGET_MAX, 0, 15)) _alc_max_gain = WOBits(3, _REG_ALC1, 4) @property def alc_max_gain(self) -> float: - return map_range(self._alc_max_gain, 0, 7, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX) + return map_range(self._alc_max_gain, 0, 7, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX) @alc_max_gain.setter def alc_max_gain(self, value:float) -> None: - self._alc_max_gain = round(map_range(value, _ALC_MAX_GAIN_MIN, _ALC_MAX_GAIN_MAX, 0, 7)) + self._alc_max_gain = round(map_range(value, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX, 0, 7)) _alc_min_gain = WOBits(3, _REG_ALC2, 4) @property def alc_min_gain(self) -> float: - return map_range(self._alc_min_gain, 0, 7, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX) + return map_range(self._alc_min_gain, 0, 7, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX) @alc_min_gain.setter def alc_min_gain(self, value:float) -> None: - self._alc_min_gain = round(map_range(value, _ALC_MIN_GAIN_MIN, _ALC_MIN_GAIN_MAX, 0, 7)) + self._alc_min_gain = round(map_range(value, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX, 0, 7)) _alc_attack = WOBits(4, _REG_ALC3, 0) @property def alc_attack_time(self) -> float: - return _ALC_ATTACK_TIME_MIN * pow(2, self._alc_attack) + return ALC_ATTACK_TIME_MIN * pow(2, self._alc_attack) @alc_attack_time.setter def alc_attack_time(self, value:float) -> None: - self._alc_attack = min(round(math.log2((constrain(value, _ALC_ATTACK_TIME_MIN, _ALC_ATTACK_TIME_MAX) - _ALC_ATTACK_TIME_MIN) / _ALC_ATTACK_TIME_MIN)), _ALC_ATTACK_MAX) + self._alc_attack = min(round(math.log2((constrain(value, ALC_ATTACK_TIME_MIN, ALC_ATTACK_TIME_MAX) - ALC_ATTACK_TIME_MIN) / ALC_ATTACK_TIME_MIN)), _ALC_ATTACK_MAX) _alc_decay = WOBits(4, _REG_ALC3, 4) @property def alc_decay_time(self) -> float: - return _ALC_DECAY_TIME_MIN * pow(2, self._alc_decay) + return ALC_DECAY_TIME_MIN * pow(2, self._alc_decay) @alc_decay_time.setter def alc_decay_time(self, value:float) -> None: - self._alc_decay = min(round(math.log2((constrain(value, _ALC_DECAY_TIME_MIN, _ALC_DECAY_TIME_MAX) - _ALC_DECAY_TIME_MIN) / _ALC_DECAY_TIME_MIN)), _ALC_DECAY_MAX) + self._alc_decay = min(round(math.log2((constrain(value, ALC_DECAY_TIME_MIN, ALC_DECAY_TIME_MAX) - ALC_DECAY_TIME_MIN) / ALC_DECAY_TIME_MIN)), _ALC_DECAY_MAX) _alc_hold = WOBits(4, _REG_ALC2, 0) @@ -607,13 +607,13 @@ def alc_decay_time(self, value:float) -> None: def alc_hold_time(self) -> float: value = self._alc_hold if value == 0: return 0.0 - return _ALC_HOLD_TIME_MIN * pow(2, self._alc_hold - 1) + return ALC_HOLD_TIME_MIN * pow(2, self._alc_hold - 1) @alc_hold_time.setter def alc_hold_time(self, value:float) -> None: if value <= 0.0: self._alc_hold = 0 else: - self._alc_hold = round(math.log2((constrain(value, _ALC_HOLD_TIME_MIN, _ALC_HOLD_TIME_MAX) - _ALC_HOLD_TIME_MIN) / _ALC_HOLD_TIME_MIN) + 1.0) + self._alc_hold = round(math.log2((constrain(value, ALC_HOLD_TIME_MIN, ALC_HOLD_TIME_MAX) - ALC_HOLD_TIME_MIN) / ALC_HOLD_TIME_MIN) + 1.0) alc_limiter = WOBit(_REG_ALC3, 8) @@ -625,10 +625,10 @@ def alc_hold_time(self, value:float) -> None: @property def noise_gate_threshold(self) -> float: - return map_range(self._noise_gate_threshold, 0, 31, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX) + return map_range(self._noise_gate_threshold, 0, 31, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX) @noise_gate_threshold.setter def noise_gate_threshold(self, value:float) -> None: - self._noise_gate_threshold = round(map_range(value, _GATE_THRESHOLD_MIN, _GATE_THRESHOLD_MAX, 0, 31)) + self._noise_gate_threshold = round(map_range(value, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX, 0, 31)) # DAC @@ -647,10 +647,10 @@ def dac(self, value:bool) -> None: @property def left_dac_volume(self) -> float: - return map_range(max(self._left_dac_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + return map_range(max(self._left_dac_volume, 1), 1, 255, DAC_VOLUME_MIN, DAC_VOLUME_MAX) @left_dac_volume.setter def left_dac_volume(self, value:float) -> None: - self._left_dac_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._left_dac_volume = round(map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 0, 254) + 1.0) self._left_dac_volume_set = True _right_dac_volume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) @@ -658,10 +658,10 @@ def left_dac_volume(self, value:float) -> None: @property def right_dac_volume(self) -> float: - return map_range(max(self._right_dac_volume, 1), 1, 255, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX) + return map_range(max(self._right_dac_volume, 1), 1, 255, DAC_VOLUME_MIN, DAC_VOLUME_MAX) @right_dac_volume.setter def right_dac_volume(self, value:float) -> None: - self._right_dac_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._right_dac_volume = round(map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 0, 254) + 1.0) self._right_dac_volume_set = True @property @@ -669,7 +669,7 @@ def dac_volume(self) -> int: return max(self.left_dac_volume, self.right_dac_volume) @dac_volume.setter def dac_volume(self, value:float) -> None: - self._left_dac_volume = self._right_dac_volume = round(map_range(value, _DAC_VOLUME_MIN, _DAC_VOLUME_MAX, 0, 254) + 1.0) + self._left_dac_volume = self._right_dac_volume = round(map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 0, 254) + 1.0) self._left_dac_volume_set = self._right_dac_volume_set = True dac_mute = WOBit(_REG_ADC_DAC_CTRL_1, 3) @@ -724,26 +724,26 @@ def input3_output(self, value:bool) -> None: @property def left_input3_output_volume(self) -> float: - return map_range(self._left_input3_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + return map_range(self._left_input3_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) @left_input3_output_volume.setter def left_input3_output_volume(self, value:float) -> None: - self._left_input3_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._left_input3_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) _right_input3_output_volume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) @property def right_input3_output_volume(self) -> float: - return map_range(self._right_input3_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + return map_range(self._right_input3_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) @right_input3_output_volume.setter def right_input3_output_volume(self, value:float) -> None: - self._right_input3_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._right_input3_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) @property def input3_output_volume(self) -> float: return max(self.left_input3_output_volume, self.right_input3_output_volume) @input3_output_volume.setter def input3_output_volume(self, value:float) -> None: - self._left_input3_output_volume = self._right_input3_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._left_input3_output_volume = self._right_input3_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) ## MIC Boost Mixer Output @@ -758,26 +758,26 @@ def mic_boost_output(self) -> bool: @property def left_mic_boost_output_volume(self) -> float: - return map_range(self._left_mic_boost_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + return map_range(self._left_mic_boost_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) @left_mic_boost_output_volume.setter def left_mic_boost_output_volume(self, value:float) -> None: - self._left_mic_boost_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._left_mic_boost_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) _right_mic_boost_output_volume = WOBits(3, _REG_BYPASS_2, 4) @property def right_mic_boost_output_volume(self) -> float: - return map_range(self._right_mic_boost_output_volume, 0, 7, _OUTPUT_VOLUME_MAX, _OUTPUT_VOLUME_MIN) + return map_range(self._right_mic_boost_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) @right_mic_boost_output_volume.setter def right_mic_boost_output_volume(self, value:float) -> None: - self._right_mic_boost_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._right_mic_boost_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) @property def mic_boost_output_volume(self) -> float: return max(self.left_mic_boost_output_volume, self.right_mic_boost_output_volume) @mic_boost_output_volume.setter def mic_boost_output_volume(self, value:float) -> None: - self._left_mic_boost_output_volume = self._right_mic_boost_output_volume = round(map_range(value, _OUTPUT_VOLUME_MIN, _OUTPUT_VOLUME_MAX, 7, 0)) + self._left_mic_boost_output_volume = self._right_mic_boost_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) ## Mono Output @@ -818,10 +818,10 @@ def headphone(self, value:bool) -> None: @property def left_headphone_volume(self) -> float: - return map_range(max(self._left_headphone_volume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + return map_range(max(self._left_headphone_volume, 48), 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) @left_headphone_volume.setter def left_headphone_volume(self, value:float) -> None: - self._left_headphone_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._left_headphone_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) self._left_headphone_volume_set = True _right_headphone_volume = WOBits(7, _REG_ROUT1_VOLUME, 0) @@ -829,10 +829,10 @@ def left_headphone_volume(self, value:float) -> None: @property def right_headphone_volume(self) -> float: - return map_range(max(self._right_headphone_volume, 48), 48, 255, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + return map_range(max(self._right_headphone_volume, 48), 48, 255, AMP_VOLUME_MIN, AMP_VOLUME_MAX) @right_headphone_volume.setter def right_headphone_volume(self, value:float) -> None: - self._right_headphone_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._right_headphone_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) self._right_headphone_volume_set = True @property @@ -840,7 +840,7 @@ def headphone_volume(self) -> int: return max(self.left_headphone_volume, self.right_headphone_volume) @headphone_volume.setter def headphone_volume(self, value:float) -> None: - self._left_headphone_volume = self._right_headphone_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127) + 1.0) + self._left_headphone_volume = self._right_headphone_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127) + 1.0) self._left_headphone_volume_set = self._right_headphone_volume_set = True left_headphone_zero_cross = WOBit(_REG_LOUT1_VOLUME, 7) @@ -887,10 +887,10 @@ def speaker(self, value:bool) -> None: @property def left_speaker_volume(self) -> float: - return map_range(max(self._left_speaker_volume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + return map_range(max(self._left_speaker_volume, 48), 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) @left_speaker_volume.setter def left_speaker_volume(self, value:float) -> None: - self._left_speaker_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._left_speaker_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) self._left_speaker_set = True _right_speaker_volume = WOBits(7, _REG_ROUT2_VOLUME, 0) @@ -898,10 +898,10 @@ def left_speaker_volume(self, value:float) -> None: @property def right_speaker_volume(self) -> float: - return map_range(max(self._right_speaker_volume, 48), 48, 127, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX) + return map_range(max(self._right_speaker_volume, 48), 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) @right_speaker_volume.setter def right_speaker_volume(self, value:float) -> None: - self._right_speaker_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._right_speaker_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) self._right_speaker_volume_set = True @property @@ -909,7 +909,7 @@ def speaker_volume(self) -> int: return max(self.left_speaker_volume, self.right_speaker_volume) @speaker_volume.setter def speaker_volume(self, value:float) -> None: - self._left_speaker_volume = self._right_speaker_volume = round(map_range(value, _AMP_VOLUME_MIN, _AMP_VOLUME_MAX, 48, 127)) + self._left_speaker_volume = self._right_speaker_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) self._left_speaker_volume_set = self._right_speaker_volume_set = True left_speaker_zero_cross = WOBit(_REG_LOUT2_VOLUME, 7) From 703a2673bb6bc8731e34604adcfb01af939cfb36 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 14:23:18 -0500 Subject: [PATCH 34/56] Rename `word_length` to `bit_depth` --- adafruit_wm8960.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 1f363b4..548b30f 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -1023,18 +1023,18 @@ def amp_clock_divider(self, value:float) -> None: master_mode = WOBit(_REG_AUDIO_INTERFACE_1, 6) - _word_length = WOBits(2, _REG_AUDIO_INTERFACE_1, 2) + _bit_depth = WOBits(2, _REG_AUDIO_INTERFACE_1, 2) @property - def word_length(self) -> int: - value = self._word_length + def bit_depth(self) -> int: + value = self._bit_depth if value == 3: return 32 else: return 16 + 4 * value - @word_length.setter - def word_length(self, value:int) -> None: - self._word_length = (min(value, 28) - 16) // 4 + @bit_depth.setter + def bit_depth(self, value:int) -> None: + self._bit_depth = (min(value, 28) - 16) // 4 word_select_invert = WOBit(_REG_AUDIO_INTERFACE_1, 4) From 2de958091024c76f436e8ebd96c718b8b919f02e Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Sat, 17 Aug 2024 14:24:44 -0500 Subject: [PATCH 35/56] Updated simpletest to utilize new attribute naming scheme. --- examples/wm8960_simpletest.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index ffe1b5c..74ae6d8 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -47,25 +47,25 @@ codec = adafruit_wm8960.WM8960(board.I2C()) # Setup Digital Interface -codec.sampleRate = 44100 -codec.wordLength = 16 +codec.sample_rate = 44100 +codec.bit_depth = 16 # Enable DAC -codec.dacEnabled = True -codec.dacOutputEnabled = True -codec.stereoOutputEnabled = True -codec.dacMute = False +codec.dac = True +codec.dac_output = True +codec.output = True +codec.dac_mute = False # Enable Headphone Amp with OUT3 as capless buffer for headphone ground -codec.headphoneEnabled = True -codec.monoOutputEnabled = True -codec.headphoneVolumeDb = 0.0 +codec.headphone = True +codec.mono_output = True +codec.headphone_volume = 0.0 # Configure I2S Output audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) # Setup synthio -synth = synthio.Synthesizer(sample_rate=codec.sampleRate) +synth = synthio.Synthesizer(sample_rate=codec.sample_rate) audio.play(synth) led = digitalio.DigitalInOut(board.LED) From 79c3596eedb566c2a467cddf04e88d30fb532c85 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 22 Aug 2024 09:39:57 -0500 Subject: [PATCH 36/56] Use decibel values instead of gain step constants for mic and speaker boost properties. --- adafruit_wm8960.py | 171 ++++++++++++++++++++++++++++++--------------- 1 file changed, 116 insertions(+), 55 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 548b30f..44389af 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -99,27 +99,12 @@ MIC_VMID = 2 # Microphone Boost gain options -MIC_BOOST_GAIN_0DB = 0 -MIC_BOOST_GAIN_13DB = 1 -MIC_BOOST_GAIN_20DB = 2 -MIC_BOOST_GAIN_29DB = 3 - -''' -Boost Mixer gain options -These are used to control the gain (aka volume) at the following settings: -LIN2BOOST -LIN3BOOST -RIN2BOOST -RIN3BOOST -''' -BOOST_MIXER_GAIN_MUTE = 0 -BOOST_MIXER_GAIN_NEG_12DB = 1 -BOOST_MIXER_GAIN_NEG_9DB = 2 -BOOST_MIXER_GAIN_NEG_6DB = 3 -BOOST_MIXER_GAIN_NEG_3DB = 4 -BOOST_MIXER_GAIN_0DB = 5 -BOOST_MIXER_GAIN_3DB = 6 -BOOST_MIXER_GAIN_6DB = 7 +_MIC_BOOST_GAIN = [ # in dB + 0.0, # 0 + 13.0, # 1 + 20.0, # 2 + 29.0 # 3 +] # Mic Bias voltage options MIC_BIAS_VOLTAGE_0_9_AVDD = 0 @@ -129,7 +114,10 @@ _SYSCLK_DIV_BY_1 = const(0) _SYSCLK_DIV_BY_2 = const(2) -# Gain/Level mins, maxes, offsets and step-sizes +# Gain/Level min/max +BOOST_GAIN_MIN = -12.00 +BOOST_GAIN_MAX = 6.0 + MIC_GAIN_MIN = -17.25 MIC_GAIN_MAX = 30.00 @@ -170,12 +158,14 @@ ALC_HOLD_TIME_MAX = 43.691 # Speaker Boost Gains (DC and AC) -SPEAKER_BOOST_GAIN_0DB = 0 -SPEAKER_BOOST_GAIN_2_1DB = 1 -SPEAKER_BOOST_GAIN_2_9DB = 2 -SPEAKER_BOOST_GAIN_3_6DB = 3 -SPEAKER_BOOST_GAIN_4_5DB = 4 -SPEAKER_BOOST_GAIN_5_1DB = 5 +_SPEAKER_BOOST_GAIN = [ # in dB + 0.0, # 0 + 2.1, # 1 + 2.9, # 2 + 3.6, # 3 + 4.5, # 4 + 5.1 # 5 +] # VMIDSEL settings VMIDSEL_DISABLED = 0 @@ -391,15 +381,36 @@ def mic_boost(self) -> None: def mic_boost(self, value:int) -> None: self.left_mic_boost = self.right_mic_boost = value - left_mic_boost_gain = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) - right_mic_boost_gain = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) + def _get_mic_boost_gain(self, value:float) -> int: + for i in reversed(range(len(_MIC_BOOST_GAIN))): + if value >= _MIC_BOOST_GAIN[i]: + return i + return 0 + + _left_mic_boost_gain = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) + + @property + def left_mic_boost_gain(self) -> float: + return _MIC_BOOST_GAIN[self._left_mic_boost_gain] + @left_mic_boost_gain.setter + def left_mic_boost_gain(self, value:float) -> None: + self._left_mic_boost_gain = self._get_mic_boost_gain(value) + + _right_mic_boost_gain = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) + + @property + def right_mic_boost_gain(self) -> float: + return _MIC_BOOST_GAIN[self._right_mic_boost_gain] + @right_mic_boost_gain.setter + def right_mic_boost_gain(self, value:float) -> None: + self._right_mic_boost_gain = self._get_mic_boost_gain(value) @property def mic_boost_gain(self) -> None: return max(self.left_mic_boost_gain, self.right_mic_boost_gain) @mic_boost_gain.setter - def mic_boost_gain(self, value:int) -> None: - self.left_mic_boost_gain = self.right_mic_boost_gain = value + def mic_boost_gain(self, value:float) -> None: + self._left_mic_boost_gain = self._right_mic_boost_gain = self._get_mic_boost_gain(value) ## Volume @@ -475,30 +486,74 @@ def mic_mute(self, value:bool) -> None: # Boost Mixer - left_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) - right_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) + _left_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) + + @property + def left_input2_boost(self) -> float: + value = self._left_input2_boost + if value == 0: + return None + return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + @left_input2_boost.setter + def left_input2_boost(self, value:float) -> None: + self._left_input2_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + + _right_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) + + @property + def right_input2_boost(self) -> float: + value = self._right_input2_boost + if value == 0: + return None + return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + @right_input2_boost.setter + def right_input2_boost(self, value:float) -> None: + self._right_input2_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) @property - def input2_boost(self) -> int: - return self.left_input2_boost + def input2_boost(self) -> float: + value = max(self._left_input2_boost, self._right_input2_boost) + return None if value is 0 else map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) @input2_boost.setter - def input2_boost(self, value:int) -> None: - self.left_input2_boost = self.right_input2_boost = value + def input2_boost(self, value:float) -> None: + self._left_input2_boost = self._right_input2_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) - left_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) - right_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) + _left_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) - # Mic Bias + @property + def left_input3_boost(self) -> float: + value = self._left_input3_boost + if value == 0: + return None + return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + @left_input3_boost.setter + def left_input3_boost(self, value:float) -> None: + self._left_input3_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) - mic_bias = WOBit(_REG_PWR_MGMT_1, 1) - mic_bias_voltage = WOBit(_REG_ADDITIONAL_CONTROL_4, 0) + _right_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) + + @property + def right_input3_boost(self) -> float: + value = self._right_input3_boost + if value == 0: + return None + return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + @right_input3_boost.setter + def right_input3_boost(self, value:float) -> None: + self._right_input3_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) @property - def input3_boost(self) -> int: - return self.left_input3_boost + def input3_boost(self) -> float: + value = max(self._left_input3_boost, self._right_input3_boost) + return None if value is 0 else map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) @input3_boost.setter - def input3_boost(self, value:int) -> None: - self.left_input3_boost = self.right_input3_boost = value + def input3_boost(self, value:float) -> None: + self._left_input3_boost = self._right_input3_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + + # Mic Bias + + mic_bias = WOBit(_REG_PWR_MGMT_1, 1) + mic_bias_voltage = WOBit(_REG_ADDITIONAL_CONTROL_4, 0) # ADC @@ -922,23 +977,29 @@ def speaker_zero_cross(self) -> bool: def speaker_zero_cross(self, value:bool) -> None: self.left_speaker_zero_cross = self.right_speaker_zero_cross = value + def _get_speaker_boost_gain(self, value:float) -> int: + for i in reversed(range(len(_SPEAKER_BOOST_GAIN))): + if value >= _SPEAKER_BOOST_GAIN[i]: + return i + return 0 + _speaker_dc_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 3) @property - def speaker_dc_gain(self) -> int: - return self._speaker_dc_gain + def speaker_dc_gain(self) -> float: + return _SPEAKER_BOOST_GAIN[self._speaker_dc_gain] @speaker_dc_gain.setter - def speaker_dc_gain(self, value:int) -> None: - self._speaker_dc_gain = min(value, 5) + def speaker_dc_gain(self, value:float) -> None: + self._speaker_dc_gain = self._get_speaker_boost_gain(value) _speaker_ac_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) @property - def speaker_ac_gain(self) -> int: - return self._speaker_ac_gain + def speaker_ac_gain(self) -> float: + return _SPEAKER_BOOST_GAIN[self._speaker_ac_gain] @speaker_ac_gain.setter - def speaker_ac_gain(self, value:int) -> None: - self._speaker_ac_gain = min(value, 5) + def speaker_ac_gain(self, value:float) -> None: + self._speaker_ac_gain = self._get_speaker_boost_gain(value) # Digital Audio Interface Control From ed2b134590006c1115fa3e3f2ef98ecdf9c63af3 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 22 Aug 2024 09:40:32 -0500 Subject: [PATCH 37/56] Add stereo function to set mic inverting input. --- adafruit_wm8960.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 44389af..d62a324 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -329,6 +329,13 @@ def mic(self, value:bool) -> None: left_mic_inverting_input = WOBit(_REG_ADCL_SIGNAL_PATH, 8) right_mic_inverting_input = WOBit(_REG_ADCR_SIGNAL_PATH, 8) + @property + def mic_inverting_input(self) -> bool: + return self.left_mic_inverting_input and self.right_mic_inverting_input + @mic_inverting_input.setter + def mic_inverting_input(self, value:bool) -> None: + self.left_mic_inverting_input = self.right_mic_inverting_input = value + _left_mic_input2 = WOBit(_REG_ADCL_SIGNAL_PATH, 6) _left_mic_input3 = WOBit(_REG_ADCL_SIGNAL_PATH, 7) @@ -748,7 +755,7 @@ def dac_volume(self, value:float) -> None: def output(self) -> bool: return self.left_output and self.right_output @output.setter - def output(self, value:bool): + def output(self, value:bool) -> None: self.left_output = self.right_output = value ## DAC Output From 8480a43b5f610367f7a2184e22cc6ce2b06a2ac0 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 22 Aug 2024 09:41:01 -0500 Subject: [PATCH 38/56] Remove "boost" from mic output attributes. --- adafruit_wm8960.py | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index d62a324..ba3c66b 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -809,37 +809,40 @@ def input3_output_volume(self, value:float) -> None: ## MIC Boost Mixer Output - left_mic_boost_output = WOBit(_REG_BYPASS_1, 7) - right_mic_boost_output = WOBit(_REG_BYPASS_2, 7) + left_mic_output = WOBit(_REG_BYPASS_1, 7) + right_mic_output = WOBit(_REG_BYPASS_2, 7) @property - def mic_boost_output(self) -> bool: - return self.left_mic_boost_output and self.right_mic_boost_output + def mic_output(self) -> bool: + return self.left_mic_output and self.right_mic_output + @mic_output.setter + def mic_output(self, value:bool) -> None: + self.left_mic_output = self.right_mic_output = value - _left_mic_boost_output_volume = WOBits(3, _REG_BYPASS_1, 4) + _left_mic_output_volume = WOBits(3, _REG_BYPASS_1, 4) @property - def left_mic_boost_output_volume(self) -> float: - return map_range(self._left_mic_boost_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) - @left_mic_boost_output_volume.setter - def left_mic_boost_output_volume(self, value:float) -> None: - self._left_mic_boost_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def left_mic_output_volume(self) -> float: + return map_range(self._left_mic_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) + @left_mic_output_volume.setter + def left_mic_output_volume(self, value:float) -> None: + self._left_mic_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) - _right_mic_boost_output_volume = WOBits(3, _REG_BYPASS_2, 4) + _right_mic_output_volume = WOBits(3, _REG_BYPASS_2, 4) @property - def right_mic_boost_output_volume(self) -> float: - return map_range(self._right_mic_boost_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) - @right_mic_boost_output_volume.setter - def right_mic_boost_output_volume(self, value:float) -> None: - self._right_mic_boost_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def right_mic_output_volume(self) -> float: + return map_range(self._right_mic_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) + @right_mic_output_volume.setter + def right_mic_output_volume(self, value:float) -> None: + self._right_mic_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) @property - def mic_boost_output_volume(self) -> float: - return max(self.left_mic_boost_output_volume, self.right_mic_boost_output_volume) - @mic_boost_output_volume.setter - def mic_boost_output_volume(self, value:float) -> None: - self._left_mic_boost_output_volume = self._right_mic_boost_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def mic_output_volume(self) -> float: + return max(self.left_mic_output_volume, self.right_mic_output_volume) + @mic_output_volume.setter + def mic_output_volume(self, value:float) -> None: + self._left_mic_output_volume = self._right_mic_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) ## Mono Output From 3b085c88d69a59a0dc9d1bdf7e6f8b241f0a9305 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 22 Aug 2024 10:27:00 -0500 Subject: [PATCH 39/56] Use monotonic value for 3D enhance depth. --- adafruit_wm8960.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index ba3c66b..68509f6 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -739,13 +739,21 @@ def dac_volume(self, value:float) -> None: dac_slow_soft_mute = WOBit(_REG_ADC_DAC_CTRL_2, 2) dac_attenuation = WOBit(_REG_ADC_DAC_CTRL_1, 7) - # 3_d Enhance + # 3D Enhance enhance = WOBit(_REG_3D_CONTROL, 0) - enhance_depth = WOBits(4, _REG_3D_CONTROL, 1) enhance_filter_lPF = WOBit(_REG_3D_CONTROL, 6) enhance_filter_hPF = WOBit(_REG_3D_CONTROL, 5) + _enhance_depth = WOBits(4, _REG_3D_CONTROL, 1) + + @property + def enhance_depth(self) -> float: + return self._enhance_depth / 15.0 + @enhance_depth.setter + def enhance_depth(self, value:float) -> None: + self._enhance_depth = round(map_range(value, 0.0, 1.0, 0, 15)) + # Output Mixer left_output = WOBit(_REG_PWR_MGMT_3, 3) From 722dec45ec9af35b4b4f4178134cb5b9623098a3 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 22 Aug 2024 10:27:19 -0500 Subject: [PATCH 40/56] Fix minor typing issues. --- adafruit_wm8960.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 68509f6..f3a9cb4 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -382,10 +382,10 @@ def mic_input(self, input:int) -> None: right_mic_boost = WOBit(_REG_ADCR_SIGNAL_PATH, 3) @property - def mic_boost(self) -> None: + def mic_boost(self) -> bool: return self.left_mic_boost and self.right_mic_boost @mic_boost.setter - def mic_boost(self, value:int) -> None: + def mic_boost(self, value:bool) -> None: self.left_mic_boost = self.right_mic_boost = value def _get_mic_boost_gain(self, value:float) -> int: @@ -413,7 +413,7 @@ def right_mic_boost_gain(self, value:float) -> None: self._right_mic_boost_gain = self._get_mic_boost_gain(value) @property - def mic_boost_gain(self) -> None: + def mic_boost_gain(self) -> float: return max(self.left_mic_boost_gain, self.right_mic_boost_gain) @mic_boost_gain.setter def mic_boost_gain(self, value:float) -> None: From b4d42b515a118f568733bfe5a1c78ceb6a7a0699 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Thu, 22 Aug 2024 10:28:14 -0500 Subject: [PATCH 41/56] Remove unnecessary examples and reorganize into meaningful examples.. --- docs/examples.rst | 125 ++---------- examples/wm8960_01_Volume.py | 86 --------- examples/wm8960_03_INPUT1.py | 85 --------- examples/wm8960_04_Speaker.py | 94 --------- examples/wm8960_06_3D_Enhance.py | 124 ------------ examples/wm8960_07_MicBias.py | 57 ------ examples/wm8960_08_I2S_Passthrough.py | 133 ------------- examples/wm8960_09_I2S_Bluetooth.py | 92 --------- examples/wm8960_10_AdcGain.py | 148 --------------- examples/wm8960_11_VolumePlotter.py | 132 ------------- examples/wm8960_13_DacGain.py | 148 --------------- examples/wm8960_14_ElectretMics.py | 115 ----------- ..._15_VolumePlotter_MEMS_Mic_Differential.py | 144 -------------- ...60_05_Loopback.py => wm8960_3d_enhance.py} | 52 ++--- examples/wm8960_I2SInOut.py | 178 ------------------ ...l.py => wm8960_automatic_level_control.py} | 83 ++++---- examples/wm8960_eighties_dystopia.py | 124 ------------ .../{wm8960_02_INPUT2.py => wm8960_input.py} | 37 ++-- examples/wm8960_simpletest.py | 4 + 19 files changed, 100 insertions(+), 1861 deletions(-) delete mode 100644 examples/wm8960_01_Volume.py delete mode 100644 examples/wm8960_03_INPUT1.py delete mode 100644 examples/wm8960_04_Speaker.py delete mode 100644 examples/wm8960_06_3D_Enhance.py delete mode 100644 examples/wm8960_07_MicBias.py delete mode 100644 examples/wm8960_08_I2S_Passthrough.py delete mode 100644 examples/wm8960_09_I2S_Bluetooth.py delete mode 100644 examples/wm8960_10_AdcGain.py delete mode 100644 examples/wm8960_11_VolumePlotter.py delete mode 100644 examples/wm8960_13_DacGain.py delete mode 100644 examples/wm8960_14_ElectretMics.py delete mode 100644 examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py rename examples/{wm8960_05_Loopback.py => wm8960_3d_enhance.py} (77%) delete mode 100644 examples/wm8960_I2SInOut.py rename examples/{wm8960_12_AutomaticLevelControl.py => wm8960_automatic_level_control.py} (62%) delete mode 100644 examples/wm8960_eighties_dystopia.py rename examples/{wm8960_02_INPUT2.py => wm8960_input.py} (72%) diff --git a/docs/examples.rst b/docs/examples.rst index 63be490..690125f 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,136 +1,35 @@ Simple test ----------- -Ensure your device works with this simple test. +Ensure your device works with this simple test that uses synthio to generate basic tones, outputs this signal to the codec via I2S, and plays back the DAC output with both headphones and speakers. .. literalinclude:: ../examples/wm8960_simpletest.py :caption: examples/wm8960_simpletest.py :linenos: -Eighties Dystopia ------------------ - -Use synthio to generate a more complex audio output. - -.. literalinclude:: ../examples/wm8960_eighties_dystopia.py - :caption: examples/wm8960_eighties_dystopia.py - :linenos: - -01: Volume ----------- - -Demonstrates analog audio input, volume control, and headphone output on the WM8960 Codec. - -.. literalinclude:: ../examples/wm8960_01_Volume.py - :caption: examples/wm8960_01_Volume.py - :linenos: - -02: INPUT2 ----------- - -Demonstrates analog audio input (on INPUT2s), sets volume control, and headphone output on the WM8960 Codec. - -.. literalinclude:: ../examples/wm8960_02_INPUT2.py - :caption: examples/wm8960_02_INPUT2.py - :linenos: - -03: INPUT1 +Input ---------- -Demonstrates analog audio input (on INPUT1s), sets volume control, and headphone output on the WM8960 Codec. - -.. literalinclude:: ../examples/wm8960_03_INPUT1.py - :caption: examples/wm8960_03_INPUT1.py - :linenos: - -04: Speaker ------------ - -Demonstrates analog audio input (on INPUT1s), sets volume control, and Speaker output on the WM8960 Codec. - -.. literalinclude:: ../examples/wm8960_04_Speaker.py - :caption: examples/wm8960_03_Speaker.py - :linenos: - -05: Loopback ------------- - -Demonstrates analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone output on the WM8960 Codec. +Demonstrates analog stereo audio input bypass on INPUT1, INPUT2, and INPUT3 simultaneously. -.. literalinclude:: ../examples/wm8960_05_Loopback.py - :caption: examples/wm8960_05_Loopback.py +.. literalinclude:: ../examples/wm8960_input.py + :caption: examples/wm8960_input.py :linenos: -06: 3D Enhance +3D Enhance -------------- -Demonstrates 3D Enhance feature of WM8960 Codec. +Demonstrates 3D Enhance and ADC/DAC loopback feature of WM8960 Codec. -.. literalinclude:: ../examples/wm8960_06_3D_Enhance.py - :caption: examples/wm8960_06_3D_Enhance.py +.. literalinclude:: ../examples/wm8960_3d_enhance.py + :caption: examples/wm8960_3d_enhance.py :linenos: -07: Mic Bias ------------- - -This example demonstrates control of the mic bias feature of WM8960 Codec. - -.. literalinclude:: ../examples/wm8960_07_MicBias.py - :caption: examples/wm8960_07_MicBias.py - :linenos: - -08: I2S Passthrough -------------------- - -.. note:: This example is not complete at this moment. - -09: I2S Bluetooth ------------------ - -.. note:: This example is not complete at this moment. - -10: ADC Gain ------------- - -Demonstrates how to control the volume using the codec's ADC digital volume control. - -.. literalinclude:: ../examples/wm8960_10_AdcGain.py - :caption: examples/wm8960_10_AdcGain.py - :linenos: - -11: Volume Plotter ------------------- - -.. note:: This example is not complete at this moment. - -12: Automatic Level Control +Automatic Level Control --------------------------- Demonstrates how to use the automatic level control feature of the WM8960 Codec. -.. literalinclude:: ../examples/wm8960_12_AutomaticLevelControl.py - :caption: examples/wm8960_12_AutomaticLevelControl.py - :linenos: - -13: DAC Gain ------------- - -Demonstrates how to control the volume using the codec's DAC digital volume control. - -.. literalinclude:: ../examples/wm8960_13_DacGain.py - :caption: examples/wm8960_13_DacGain.py +.. literalinclude:: ../examples/wm8960_automatic_level_control.py + :caption: examples/wm8960_automatic_level_control.py :linenos: - -14: Electret Mics ------------------ - -Demonstrates electret microphone analog audio input (on INPUT1/INPUT2 as "pseudo-differential MIC configuration"). Sets the PGA gain, sets the non-inverting pga input to INPUT2s, sets volume control, and headphone output on the WM8960 Codec. - -.. literalinclude:: ../examples/wm8960_14_ElectretMics.py - :caption: examples/wm8960_14_ElectretMics.py - :linenos: - -14: Volume Plotter MEMS Mic Differential ----------------------------------------- - -.. note:: This example is not complete at this moment. diff --git a/examples/wm8960_01_Volume.py b/examples/wm8960_01_Volume.py deleted file mode 100644 index 47169df..0000000 --- a/examples/wm8960_01_Volume.py +++ /dev/null @@ -1,86 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates analog audio input, volume control, and headphone output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT3" inputs, they are labeled "RIN3" and "LIN3" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. - -It will output the sound on the headphone outputs. -It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3"vand this provides a buffered VMID. - -You can now control the volume of the codecs built in headphone buffers using this function: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT3 ----- TRS INPUT TIP *left audio -RINPUT3 ----- TRS INPUT RING1 *right audio - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board, time -import adafruit_wm8960 - -print("Example 1 - Volume") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow through the analog audio bypass connections - -# Enable output mixer -codec.enableOMIX() - -# Enable bypass connection from Left/Right INPUT3 to Left/Right output mixer, note, the default gain on these inputs (LI2LOVOL/RI2ROVOL) is -15dB -codec.enableI2O() - -# Sets volume control between "left/right input" to "left/right output mixer" -codec.setI2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) - -codec.configureHeadphones(capless=True) # Capless provides VMID as buffer for headphone ground - -print("Volume set to +6.00dB (max)") -codec.setHeadphoneVolumeDB(6.00) -time.sleep(5.0) - -print("Volume set to +0.00dB") -codec.setHeadphoneVolumeDB(0.00) -time.sleep(5.0) - -print("Volume set to -12.00dB") -codec.setHeadphoneVolumeDB(-12.00) -time.sleep(5.0) - -print("Volume set to -74.00dB, aka MUTE") -codec.setHeadphoneVolumeDB(-74.00) -time.sleep(5.0) - -print("Example complete") diff --git a/examples/wm8960_03_INPUT1.py b/examples/wm8960_03_INPUT1.py deleted file mode 100644 index e004ab2..0000000 --- a/examples/wm8960_03_INPUT1.py +++ /dev/null @@ -1,85 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates analog audio input (on INPUT1s), sets volume control, and headphone output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. - -It will output the sound on the headphone outputs. It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. - -You can now control the volume of the codecs built in headphone buffers using this function: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board -import adafruit_wm8960 - -print("Example 3 - INPUT1") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow through the analog audio bypass connections -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) - -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Connect LB2LO (booster to output mixer (analog bypass) -codec.enableB2O() - -# Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) - -# Enable output mixers -codec.enableOMIX() - -print("Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground - -print("Example complete. Listen to INPUT1 on headphone outputs.") diff --git a/examples/wm8960_04_Speaker.py b/examples/wm8960_04_Speaker.py deleted file mode 100644 index 3915c3e..0000000 --- a/examples/wm8960_04_Speaker.py +++ /dev/null @@ -1,94 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates analog audio input (on INPUT1s), sets volume control, and Speaker output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. - -It will output the sound on the Speaker outputs. - -You can now control the volume of the codecs built in class-d amp using this function: -codec.setSpeakerVolumeDB(6.00) -Valid inputs are -73.00 to 6.00 (1.00 dB steps) - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC -------- AUDIO OUT -********************** -SL+ --------- Left Speaker + -SL- --------- Left Speaker - -SR+ --------- Right Speaker + -SR- --------- Right Speaker - - -*Note, with a class-d speaker amp like this, you need to connections like above. -Each speaker must be connected to its correct + and -. -You cannot connect the "-" side of the speaker to GND. -You cannot share the "-" side of two speakers (like with common "TRS-wired" -headphones). - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board -import adafruit_wm8960 - -print("Example 4 - Speaker") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow through the analog audio bypass connections -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INPUT1) -codec.disableINMUTE() - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Connect booster to output mixer (analog bypass) -codec.enableB2O() - -# Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) - -# Enable output mixers -codec.enableOMIX() - -# Set up clock for 44.1KHz -codec.configureI2S(44100) - -# Enable speakers at default volume of 0dB -codec.configureSpeakers() - -print("Example complete. Listen to left/right INPUT1 on Speaker outputs.") diff --git a/examples/wm8960_06_3D_Enhance.py b/examples/wm8960_06_3D_Enhance.py deleted file mode 100644 index 7bc272a..0000000 --- a/examples/wm8960_06_3D_Enhance.py +++ /dev/null @@ -1,124 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates 3D Enhance feature of WM8960 Codec. - -Toggles on/off 3D Enhance every 5 seconds, so you can hear the difference. - -Note, it also sets the 3D enhance Depth setting to 15 (max). -You can change this to your desired depth from 0-15. - -Note, this is very similar to the Loopback example, but also demos the 3D Enhance feature. - -Sets up analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). -Then send the output of the DAC to the headphone outs. - -You can now control the volume of the codecs built in headphone amp using this function: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board, time -import adafruit_wm8960 - -print("Example 6 - 3D Enhance") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Disconnect LB2LO (booster to output mixer (analog bypass) -# For this example, we are going to pass audio throught the ADC and DAC -codec.disableB2O() - -# Connect from DAC outputs to output mixer -codec.enableD2O() - -# Set gainstage between booster mixer and output mixer -# For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) - -# Enable output mixers -codec.enableOMIX() - -# Setup sample rate -codec.setSampleRate(44100) -codec.enableMasterMode() -codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. - -# Enable ADCs and DACs -codec.enableAdc() -codec.enableDac() - -# Loopback sends ADC data directly into DAC -codec.enableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() - -print("Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground - -print("3D Enhance Depth set to 15 (max)") -codec.set3dDepth(15) - -print("Listen to left/right INPUT1 on Headphone outputs with toggling 3D enhance!") - -while True: - codec.enable3d() - print("3D Enhance enabled") - time.sleep(5.0) - - codec.disable3d() - print("3D Enhance disabled") - time.sleep(5.0) diff --git a/examples/wm8960_07_MicBias.py b/examples/wm8960_07_MicBias.py deleted file mode 100644 index 11f33ba..0000000 --- a/examples/wm8960_07_MicBias.py +++ /dev/null @@ -1,57 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -This example demonstrates control of the mic bias feature of WM8960 Codec. - -Electret Mics are powered with a mic bias voltage applied to their signal line - usually with a 2.2K resistor in series. This Codec can provide a clean mic bias. - -This example turns on the mic bias, set's it to each available output voltage, and then turns it off to demonstrate disable. - -Measure the voltage with a multimeter to verfy you are getting the correct voltages you desire on mic bias. - -You can later use this mic bias voltage to power an electret mic in a more advanced example (example 14). - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board, time -import adafruit_wm8960 - -print("Example 7 - MicBias Control") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -codec.enableMicBias() - -# MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or -# MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) -codec.setMicBiasVoltage(adafruit_wm8960.MIC_BIAS_VOLTAGE_0_9_AVDD) -print("Mic Bias enabled (0.9*AVDD)") -time.sleep(3.0) - -# MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or -# MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) -codec.setMicBiasVoltage(adafruit_wm8960.MIC_BIAS_VOLTAGE_0_65_AVDD) -print("Mic Bias enabled (0.65*AVDD)") -time.sleep(3.0) - -codec.disableMicBias() -print("Mic Bias disabled") - -print("Example Complete. Hit reset to begin again.") diff --git a/examples/wm8960_08_I2S_Passthrough.py b/examples/wm8960_08_I2S_Passthrough.py deleted file mode 100644 index 18a796b..0000000 --- a/examples/wm8960_08_I2S_Passthrough.py +++ /dev/null @@ -1,133 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates reading I2S audio from the ADC, and passing that back to the DAC. - -This example sets up analog audio input (on INPUT1s), ADC/DAC enabled as I2S peripheral, sets volume control, and Headphone output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Read the audio from the ADC via I2S. Then send audio immediately back to the DAC via I2S. Then send the output of the DAC to the headphone outs. - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) -AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC -AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. -AUDIO_RXD --- ADAT *aka ADC_DATA/I2S_SD/"serial data in", this carries the I2S audio data from codec's ADC to MCU I2S bus. -AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. -AUDIO_SYNC -- ALR *for this example WS is shared for both the ADC WS (ALR) and the DAC WS (DLRC) - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -You can now control the volume of the codecs built in headphone amp using this fuction: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board -import adafruit_wm8960 -import audiobusio - -print("Example 8 - I2S Passthough") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set pga volumes -codec.setINVOLDB(0.0) # Valid options are -17.25dB to +30dB (0.75dB steps) - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) - -# Connect from MIC inputs (aka pga output) to boost mixers -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Disconnect LB2LO (booster to output mixer (analog bypass) -# For this example, we are going to pass audio throught the ADC and DAC -codec.disableB2O() - -# Connect from DAC outputs to output mixer -codec.enableD2O() - -# Set gainstage between booster mixer and output mixer -# For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) - -# Enable output mixers -codec.enableOMIX() - -# Set sample rate, word length, and mode -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) - -# Enable ADCs and DACs -codec.enableAdc() -codec.enableDac() - -# Loopback sends ADC data directly into DAC -codec.disableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() - -print("Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground - -print("Codec Setup complete. Listen to left/right INPUT1 on Headphone outputs.") - -# Set up I2S -audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) - -while True: - # Get I2S data and place in data buffer - # TODO: Read from I2SIn and loopback into I2SOut - pass - - # DelayMicroseconds(300) # Only hear to demonstrate how much time you have - # to do things. - # Do not do much in this main loop, or the audio won't pass through correctly. - # With default settings (64 samples in buffer), you can spend up to 300 - # microseconds doing something in between passing each buffer of data - # You can tweak the buffer length to get more time if you need it. - # When bufferlength is 64, then you get ~300 microseconds - # When bufferlength is 128, then you get ~600 microseconds - # Note, as you increase bufferlength, then you are increasing latency between - # ADC input to DAC output. - # Latency may or may not be desired, depending on the project. diff --git a/examples/wm8960_09_I2S_Bluetooth.py b/examples/wm8960_09_I2S_Bluetooth.py deleted file mode 100644 index 804132f..0000000 --- a/examples/wm8960_09_I2S_Bluetooth.py +++ /dev/null @@ -1,92 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates how to receive audio via Bluetooth and play it back via I2S to the codec DAC. - -This example sets up the MCU as a bluetooth sink device, with its output set -to I2S audio. - -This example sets up the WM8960 codec as an I2S peripheral, sets volume -control, and Headphone output. - -A bluetooth device, such as your phone or laptop, can connect to the MCU and -then begin playing an audio file. - -The MCU will send the I2S audio to the DAC of the codec. The DAC is -connected to the HP outputs. - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) -AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC -AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. -AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -Note, once connected and playing a sound file, your bluetooth source device -(i.e. your phone) can control volume with its own volume control. - -You can also control the volume of the codecs built in headphone amp using this fuction: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board -import audiobusio -import adafruit_wm8960 - -print("Example 9 - Bluetooth Audio") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Connect from DAC outputs to output mixer -codec.enableD2O() - -# Set gainstage between booster mixer and output mixer -# For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) - -# Enable output mixers -codec.enableOMIX() - -# Setup sample rate, word length, and I2S mode -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) - -# Enable DACs -codec.enableDac() - -# Loopback sends ADC data directly into DAC -codec.disableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() - -print("Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground - -print("Codec Setup complete. Connect via Bluetooth, play music, and listen on Headphone outputs.") - -# Set up I2S -audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) - -# TODO: Bluetooth setup diff --git a/examples/wm8960_10_AdcGain.py b/examples/wm8960_10_AdcGain.py deleted file mode 100644 index 73d4a96..0000000 --- a/examples/wm8960_10_AdcGain.py +++ /dev/null @@ -1,148 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates how to control the volume using the codec's ADC digital volume control. - -Attach a potentiomenter to GND/A0/3V3 to actively adjust the setting. - -This example sets up the codec for analog audio input (on INPUT1s), ADC/DAC Loopback, sets hp volume, and Headphone output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). -Then send the output of the DAC to the headphone outs. - -We will use the gain stage at the ADC to control the volume of the signal. -This is capable of more precision, with 255 available settings. - -** ADC digital volume -** Valid dB settings are -97.00 up to +30.0 (0.5dB steps) -** -97.50 (or lower) = MUTE -** -97.00 = -97.00dB (MIN) -** ... 0.5dB steps ... -** 30.00 = +30.00dB (MAX) - -You can also control the volume of the codecs built in headphone amp using this function: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -********************** -MCU --------- POTENTIOMTER (aka blue little trimpot) -********************** -GND --------- "right-side pin" -A0 ---------- center pin *aka center tap connection -3V3 --------- "left-side pin" - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board, time -from analogio import AnalogIn -from adafruit_simplemath import map_range -import adafruit_wm8960 - -# Used to store incoming potentiometer settings to set ADC digital volume setting -userInputA0 = 0 -analog_in = AnalogIn(board.A0) - -print("Example 10 - ADC Digital Volume Control") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Disconnect LB2LO (booster to output mixer (analog bypass) -# For this example, we are going to pass audio throught the ADC and DAC -codec.disableB2O() - -# Connect from DAC outputs to output mixer -codec.enableD2O() - -# Set gainstage between booster mixer and output mixer -# For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) - -# Enable output mixers -codec.enableOMIX() - -# Setup clock and mode -codec.setSampleRate(44100) -codec.enableMasterMode() -codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. - -# Enable ADCs and DACs -codec.enableAdc() -codec.enableDac() - -# Loopback sends ADC data directly into DAC -codec.enableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() - -print("Headphone Amp Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground - -print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") - -while True: - # Take a bunch of readings and average them, to smooth out the value - for i in range(250): - userInputA0 += analog_in.value - time.sleep(0.001) - - # After taking a bunch of samples, divide down to the average single reading - userInputA0 /= 250.0 - - # Map it from 0-4096, to a dB value that is acceptable in the ADC digital volume control (-97.50 [MUTE] to +30dB) - adcVolumeDB = map_range(userInputA0, 0.0, 65536.0, 30.0, -97.5) - - print("adcVolumeDB: ", adcVolumeDB) - - codec.setAdcDigitalVolumeDB(adcVolumeDB) # -97.50 to +30.00dB - - time.sleep(0.05) diff --git a/examples/wm8960_11_VolumePlotter.py b/examples/wm8960_11_VolumePlotter.py deleted file mode 100644 index fcaa46b..0000000 --- a/examples/wm8960_11_VolumePlotter.py +++ /dev/null @@ -1,132 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates reading I2S audio from the ADC, and plotting the audio samples on the Arduino Serial Plotter. - -This example sets up analog audio input (on INPUT1s), ADC enabled as I2S peripheral, sets volume control, and Headphone output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Read the audio from the ADC via I2S. - -The analog bypass paths is also setup, so your audio will pass through the codec and playback on HP outs. - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) -AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. -AUDIO_RXD --- ADAT *aka ADC_DATA/I2S_SD/"serial data in", this carries the I2S audio data from codec's ADC to MCU I2S bus. -AUDIO_SYNC -- ALR *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -You can now control the volume of the codecs built in headphone amp using this fuction: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board, time -import audiobusio -import adafruit_wm8960 - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set pga volumes -codec.setINVOLDB(0.00) # Valid options are -17.25dB to +30dB (0.75dB steps) - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) - -# Connect from MIC inputs (aka pga output) to boost mixers -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Connect LB2LO (booster to output mixer (analog bypass) -codec.enableB2O() - -# Disconnect from DAC outputs to output mixer -codec.disableD2O() - -# Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) - -# Enable output mixers -codec.enableOMIX() - -# Configure I2S -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) - -# Enable ADCs, and disable DACs -codec.enableAdc() -codec.disableDac() -codec.disableDacMute() - -# Loopback sends ADC data directly into DAC -codec.disableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.enableDacMute() - -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground - -# Set up I2S -audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) - -sBuffer = [0 for i in range(64)] - -while True: - # False print statements to "lock range" on serial plotter display - # Change rangelimit value to adjust "sensitivity" - rangelimit = 3000 - print(rangelimit * -1, " ", rangelimit, " ", end=None) - - # Get I2S data and place in data buffer - # TODO: Read from I2SIn - - # Read I2S data buffer - mean = 0.0 - for i in range(64): - mean += sBuffer[i] - - # Average the data reading - mean /= 64.0 - - # Print to serial plotter - print(mean) diff --git a/examples/wm8960_13_DacGain.py b/examples/wm8960_13_DacGain.py deleted file mode 100644 index afb827c..0000000 --- a/examples/wm8960_13_DacGain.py +++ /dev/null @@ -1,148 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates how to control the volume using the codec's DAC digital volume control. - -Attach a potentiomenter to GND/A0/3V3 to actively adjust the setting. - -This example sets up the codec for analog audio input (on INPUT1s), ADC/DAC Loopback, sets hp volume, and Headphone output on the WM8960 Codec. - -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. - -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). -Then send the output of the DAC to the headphone outs. - -We will use the gain stage at the DAC to control the volume of the signal. -This is capable of more precision, with 255 available settings. - -** DAC digital volume -** Valid dB settings are -97.00 up to +30.0 (0.5dB steps) -** -97.50 (or lower) = MUTE -** -97.00 = -97.00dB (MIN) -** ... 0.5dB steps ... -** 30.00 = +30.00dB (MAX) - -You can also control the volume of the codecs built in headphone amp using this function: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -********************** -MCU --------- POTENTIOMTER (aka blue little trimpot) -********************** -GND --------- "right-side pin" -A0 ---------- center pin *aka center tap connection -3V3 --------- "left-side pin" - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC ------- AUDIO OUT -********************** -OUT3 -------- TRS OUTPUT SLEEVE -HPL --------- TRS OUTPUT TIP *left HP output -HPR --------- TRS OUTPUT RING1 *right HP output - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board, time -from analogio import AnalogIn -from adafruit_simplemath import map_range -import adafruit_wm8960 - -print("Example 13 - DAC Digital Volume Control") - -# Used to store incoming potentiometer settings to set ADC digital volume setting -userInputA0 = 0 -analog_in = AnalogIn(board.A0) - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Disconnect LB2LO (booster to output mixer (analog bypass) -# For this example, we are going to pass audio throught the ADC and DAC -codec.disableB2O() - -# Connect from DAC outputs to output mixer -codec.enableD2O() - -# Set gainstage between booster mixer and output mixer -# For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) - -# Enable output mixers -codec.enableOMIX() - -# Set sample rate and mode -codec.setSampleRate(44100) -codec.enableMasterMode() -codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. - -# Enable ADCs and DACs -codec.enableAdc() -codec.enableDac() - -# Loopback sends ADC data directly into DAC -codec.enableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() - -print("Headphopne Amp Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground - -print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") - -while True: - # Take a bunch of readings and average them, to smooth out the value - for i in range(250): - userInputA0 += analog_in.value - time.sleep(0.001) - - # After taking a bunch of samples, divide down to the average single reading - userInputA0 /= 250.0 - - # Map it from 0-4096, to a dB value that is acceptable in the DAC digital volume control (-97.50 [MUTE] to +30dB) - dacVolumeDB = map_range(userInputA0, 0.0, 65536.0, 30.0, -97.5) - - print("dacVolumeDB: ", dacVolumeDB) - - codec.setDacDigitalVolumeDB(dacVolumeDB) # -97.50 to +30.00dB - - time.sleep(0.05) diff --git a/examples/wm8960_14_ElectretMics.py b/examples/wm8960_14_ElectretMics.py deleted file mode 100644 index 734f1f7..0000000 --- a/examples/wm8960_14_ElectretMics.py +++ /dev/null @@ -1,115 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates electret microphone analog audio input (on INPUT1/INPUT2 as "pseudo-differential MIC configuration"). Sets the PGA gain, sets the non-inverting pga input to INPUT2s, sets volume control, and headphone output on the WM8960 Codec. - -Note, most of the examples in this library set all gain stages at 0dB, but for this electret mic example, we need a bit more gain on the initial input stage (the PGA), so we set it to +24dB (aka "57" as the argument to the function). -We are also bumping up the headphone output gainstage to max, +6dB (aka "127"). - -Electret mics should be connected to left and right inputs. - -MicBias should be provided to the "+" side of each mic via an in-series 2.2K resistor. - -The Mic "-" should be connected to each INPUT2. Note, this is also GND in this example, but enables a "psuedo-differential setup". - -This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. - -It will output the sound on the headphone outputs. It is setup to do a capless headphone setup, so connet your headphones ground to "OUT3" and this provides a buffered VMID. - -You can now control the volume of the codecs built in headphone buffers using this function: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -HARDWARE CONNECTIONS -See Datasheet page 25 for electret mic hardware hookup example diagrams. -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) - -********************** -CODEC ------- AUDIO IN (ELECTRET MICS) -********************** -GND --------- LEFT MIC - -LINPUT1 ----- LEFT MIC - -LINPUT2 ----- LEFT MIC + -MICBIAS ----- 2.2K RESISTOR ----- LEFT MIC+ -GND --------- RIGHT MIC - -RINPUT1 ----- RIGHT MIC - -RINPUT2 ----- RIGHT MIC + -MICBIAS ----- 2.2K RESISTOR ----- RIGHT MIC+ - -********************** -CODEC -------- AUDIO OUT -********************** -OUT3 --------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL ---------- TRS OUTPUT TIP *left HP output -HPR ---------- TRS OUTPUT RING1 *right HP output - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board -import adafruit_wm8960 - -print("Example 14 - Electret Mics") - -codec = adafruit_wm8960.WM8960(board.I2C()) - -codec.enableMicBias() - -# MIC_BIAS_VOLTAGE_0_9_AVDD (0.9*AVDD) or -# MIC_BIAS_VOLTAGE_0_65_AVDD (0.65*AVDD) -codec.setMicBiasVoltage(adafruit_wm8960.MIC_BIAS_VOLTAGE_0_9_AVDD) -print("Mic Bias enabled (0.9*AVDD)") - -# Setup signal flow through the analog audio bypass connections -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set pga volumes -codec.setINVOLDB(24.00) # Valid options are -17.25dB to +30.00dB -print("PGA gain set to +24dB") - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -print("Mic boost stage set to 0dB") - -# For MIC+ signal of differential mic signal -codec.pgaNonInvSignalSelect(adafruit_wm8960.PGA_INPUT2) -print("Pga non-inverting inputs set to INPUT2s") - -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Connect LB2LO (booster to output mixer (analog bypass) -codec.enableB2O() - -# Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) - -# Enable output mixers -codec.enableOMIX() - -print("Headphone output buffer volume set to +6dB (max)") -codec.configureHeadphones(dB=6.0, capless=True) # Capless provides VMID as buffer for headphone ground - -print("Example complete. Listen to Electret mics on headphone outputs.") diff --git a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py b/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py deleted file mode 100644 index 9a27a2b..0000000 --- a/examples/wm8960_15_VolumePlotter_MEMS_Mic_Differential.py +++ /dev/null @@ -1,144 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -This example is very similar to example 11, however it uses a MEMS mic with differential signal as the source of the sound. Here we are using just one of VM2020 Breakouts plugged into the left channel. - -Note, with this VM2020 differential mic signal, we are going to add much more gain than in previous examples. We are going to use a MICBOOST of 29dB (max), and set the PGA to +8.25dB. - -Demonstrates reading I2S audio from the ADC, and plotting the audio samples on the Arduino Serial Plotter. - -This example sets up differential analog audio input (on LINPUT1/LINPUT2), ADC enabled as I2S peripheral, sets volume control, and Headphone output on the WM8960 Codec. - -A MEMS mic should be connected to the left channel INPUT1 and INPUT2, they are labeled "LIN1" and "LIN2" on the board. These will provide the input connections for the positive and negative signals from the MEMS mic. - -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Read the audio from the ADC via I2S. - -The analog bypass paths is also setup, so your audio will pass through the codec and playback on HP outs. - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) -AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. -AUDIO_RXD --- ADAT *aka ADC_DATA/I2S_SD/"serial data in", this carries the I2S audio data from codec's ADC to MCU I2S bus. -AUDIO_SYNC -- ALR *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. - -********************** -CODEC ------- MIC IN -********************** -GND --------- GND *Ground -AVDD -------- VCC *3.3V (default on the Codec breakout) -LINPUT1 ----- OUT- *Mic signal "-" -LINPUT2 ----- OUT+ *Mic signal "+" - -********************** -CODEC ------- AUDIO OUT -********************** -OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL --------- TRS OUTPUT TIP *left HP output -HPR --------- TRS OUTPUT RING1 *right HP output - -You can now control the volume of the codecs built in headphone amp using this fuction: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_WM8960_Arduino_Library - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board -import audiobusio -import adafruit_wm8960 - -sBuffer = [0 for i in range(64)] - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.enableMIC() - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() - -# Set pga volumes -codec.setINVOLDB(8.25) # Valid options are -17.25dB to +30dB (0.75dB steps) - -# Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_29DB) - -# For MIC+ signal of differential mic signal -codec.pgaNonInvSignalSelect(adafruit_wm8960.PGA_INPUT2) - -# Connect from MIC inputs (aka pga output) to boost mixers -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() - -# Connect LB2LO (booster to output mixer (analog bypass) -codec.enableB2O() - -# Disconnect from DAC outputs to output mixer -codec.disableD2O() - -# Set gainstage between booster mixer and output mixer -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) - -# Enable output mixers -codec.enableOMIX() - -# Set sample rate, word length, and mode -codec.configureI2S(sample_rate=44100, word_length=adafruit_wm8960.WL_16BIT, master=False) - -# Enable ADCs, and disable DACs -codec.enableAdc() -codec.disableDac() -codec.disableDacMute() - -# Loopback sends ADC data directly into DAC -codec.disableLoopBack() - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.enableDacMute() - -codec.configureHeadphones(dB=6.0, capless=True) # Capless provides VMID as buffer for headphone ground - -# Set up I2S -audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) - -while True: - # False print statements to "lock range" on serial plotter display - # Change rangelimit value to adjust "sensitivity" - rangelimit = 3000 - print(rangelimit * -1, " ", rangelimit, " ", end=None) - - # Get I2S data and place in data buffer - # TODO: Read from I2SIn - - # Read I2S data buffer - mean = 0.0 - # Only looking at left signal samples in the buffer (e.g. 0,2,4,6,8...) - # Notice in our for loop here, we are incrementing the index by 2. - for i in range(64): - mean += sBuffer[i] - - # Average the data reading - # We're only concerned with left input for this example. So we must - # divide by "half of samples read" (because it is stereo I2S audio data) - mean /= 64.0 / 2.0 - - # Print to serial plotter - print(mean) diff --git a/examples/wm8960_05_Loopback.py b/examples/wm8960_3d_enhance.py similarity index 77% rename from examples/wm8960_05_Loopback.py rename to examples/wm8960_3d_enhance.py index dd38f80..6e95b5e 100644 --- a/examples/wm8960_05_Loopback.py +++ b/examples/wm8960_3d_enhance.py @@ -45,59 +45,61 @@ https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf ''' -import board +import board, time import adafruit_wm8960 -print("Example 5 - Loopback") - codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC -codec.enableMIC() +codec.mic = True # Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() +codec.mic_inverting_input = True # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() +codec.mic_mute = False # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() +codec.mic_boost_gain = 0.0 +codec.mic_boost = True # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableB2O() +codec.mic_output = False # Connect from DAC outputs to output mixer -codec.enableD2O() +codec.dac_output = True # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) +codec.mic_output_volume = adafruit_wm8960.OUTPUT_VOLUME_MIN # Enable output mixers -codec.enableOMIX() +codec.output = True # Setup sample rate -codec.setSampleRate(44100) -codec.enableMasterMode() -codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. +codec.sample_rate = 44100 +codec.master_mode = True +codec.gpio_output = True # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdc() -codec.enableDac() +codec.adc = codec.dac = True # Loopback sends ADC data directly into DAC -codec.enableLoopBack() +codec.loopback = True # Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() +codec.dac_mute = False + +# Enable Headphone Amp with OUT3 as capless buffer for headphone ground +codec.headphone = True +codec.mono_output = True +codec.headphone_volume = 0.0 -print("Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground +# Set 3D enhance depth to max (0.0 - 1.0) +codec.enhance_depth = 1.0 -print("Example complete. Listen to left/right INPUT1 on Headphone outputs.") +# Toggle 3D enhance on and off +while True: + codec.enhance = not codec.enhance + time.sleep(2.0) diff --git a/examples/wm8960_I2SInOut.py b/examples/wm8960_I2SInOut.py deleted file mode 100644 index 508f868..0000000 --- a/examples/wm8960_I2SInOut.py +++ /dev/null @@ -1,178 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: Unlicense - -''' -Demonstrates I2C Input and Output on WM8960 Codec. - -It will output the sound on the headphone outputs. -It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) -AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC -AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. -AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. - -********************** -CODEC ------- AUDIO IN -********************** -GND --------- TRS INPUT SLEEVE *ground for line level input -LINPUT1 ----- TRS INPUT TIP *left audio -RINPUT1 ----- TRS INPUT RING1 *right audio - -********************** -CODEC ------- AUDIO OUT -********************** -OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL --------- TRS OUTPUT TIP *left HP output -HPR --------- TRS OUTPUT RING1 *right HP output - -You can now control the volume of the codecs built in headphone buffers using this function: -codec.setHeadphoneVolumeDB(6.00) -Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). - -For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: -https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' - -import board -import adafruit_wm8960 -import array -import rp2pio -import adafruit_pioasm - -CHANNELS = 2 -BITS = 16 -SAMPLE_RATE = 48000 -BUFFER_SIZE = 2048 -BUFFER_TYPE = "L" -BUFFER_WIDTH = 32 - -import busio -codec = adafruit_wm8960.WM8960(busio.I2C(board.GP17, board.GP16)) - -# Setup INPUT1 -codec.enableMIC() -codec.connectMN1() -codec.disableINMUTE() -codec.setINVOLDB(0.0) -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -codec.connectMIC2B() -codec.enableAIN() -codec.disableB2O() -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) - -# Setup DAC -codec.enableD2O() -codec.enableOMIX() -codec.disableLoopBack() - -# Set up codec as peripheral device with clock configured for desired sample rate (and 16-bit words) -codec.configureI2S(SAMPLE_RATE) - -# Enable ADCs and DACs -codec.enableAdc() -codec.enableDac() -codec.disableDacMute() - -# Enable headphone output with OUT3 as capless buffer for headphone ground, adjust volume with dB -codec.configureHeadphones(dB=0.0, capless=True) - -def i2s_codec( - channels=2, - sample_rate=48000, - bits=16, - bclk_pin=None, - out_pin=None, - in_pin=None, -): - i2s_clock = sample_rate * channels * bits - pio_clock = 4 * i2s_clock - pio_code = """ - .program i2s_codec - .side_set 2 - ; at program start we initialize the bit count top - ; (which may be >32) with data - ; pulled from the input fifo - pull noblock ; first empty the input fifo - pull noblock - pull noblock - pull noblock - out null, 32 ; then clear OSR so we can get a new value - pull block ; then get the bit count top value from the fifo - ; /--- LRCLK - ; |/-- BCLK - ; || - mov x, osr; side 0b01 [1] ; save it in x - out null, 32 side 0b00 [1] - mov y, x side 0b01 [1] ; start of main loop (wrap target=8) - bitloop1: - out pins 1 side 0b00 - in pins 1 side 0b00 - jmp y-- bitloop1 side 0b01 [1] - out pins 1 side 0b10 - in pins 1 side 0b10 - mov y, x side 0b11 [1] - bitloop0: - out pins 1 side 0b10 - in pins 1 side 0b10 - jmp y-- bitloop0 side 0b11 [1] - out pins 1 side 0b00 - in pins 1 side 0b00 - """ - pio_params = { - "frequency": pio_clock, - "first_out_pin": out_pin, - "first_in_pin": in_pin, - "first_sideset_pin": bclk_pin, - "sideset_pin_count": 2, - "auto_pull": True, - "auto_push": True, - "out_shift_right": False, - "in_shift_right": False, - "pull_threshold": bits, - "push_threshold": bits, - "wait_for_txstall": False, - "wrap_target": 8, - } - pio_instructions = adafruit_pioasm.assemble(pio_code) - i2s_clock = sample_rate * channels * bits - pio_clock = 4 * i2s_clock - pio = rp2pio.StateMachine(pio_instructions, **pio_params) - return pio - -def spaced_samples(length, bits): - max_int = (1 << bits) - 1 - if length == 1: - return [0] - step = max_int / (length - 1) - result = [round(i * step) for i in range(length)] - result[0] = 0 - result[-1] = max_int - return result - -# initialize pio bit count top value by sending it at the start of output data -bit_count_top = BITS * (CHANNELS // 2) - 2 - -PIO = i2s_codec( - channels=CHANNELS, - bits=BITS, - sample_rate=SAMPLE_RATE, - out_pin=board.GP20, - in_pin=board.GP21, - bclk_pin=board.GP18, # L/R signal will be one pin higher, i.e. GP19 -) - -buffer_in = array.array(BUFFER_TYPE, [0] * BUFFER_SIZE) -while True: - buffer_out = array.array(BUFFER_TYPE, [bit_count_top] + [d << (BUFFER_WIDTH - BITS) for d in buffer_in]) - PIO.write_readinto(buffer_out, buffer_in) - PIO.clear_rxfifo() - del buffer_out diff --git a/examples/wm8960_12_AutomaticLevelControl.py b/examples/wm8960_automatic_level_control.py similarity index 62% rename from examples/wm8960_12_AutomaticLevelControl.py rename to examples/wm8960_automatic_level_control.py index 1dca92e..6797898 100644 --- a/examples/wm8960_12_AutomaticLevelControl.py +++ b/examples/wm8960_automatic_level_control.py @@ -59,90 +59,71 @@ from adafruit_simplemath import map_range import adafruit_wm8960 -print("Example 12 - Automatic Level Control") - -# Used to store incoming potentiometer settings to set ADC digital volume setting -userInputA0 = 0 analog_in = AnalogIn(board.A0) codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow to the ADC -codec.enableMIC() +codec.mic = True -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.connectMN1() +# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs +codec.mic_inverting_input = True # Disable mutes on PGA inputs (aka INTPUT1) -codec.disableINMUTE() +codec.mic_mute = False # Set input boosts to get inputs 1 to the boost mixers -codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) -codec.connectMIC2B() - -# Enable boost mixers -codec.enableAIN() +codec.mic_boost_gain = adafruit_wm8960.MIC_BOOST_GAIN_0DB +codec.mic_boost = True +codec.input = True # Enable boost mixers # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC -codec.disableB2O() +codec.mic_output = False # Connect from DAC outputs to output mixer -codec.enableD2O() +codec.dac_output = True # Set gainstage between booster mixer and output mixer # For this loopback example, we are going to keep these as low as they go -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_NEG_21DB) +codec.mic_output_volume = adafruit_wm8960.OUTPUT_VOLUME_MIN # Enable output mixers -codec.enableOMIX() +codec.output = True # Setup clock and mode -codec.setSampleRate(44100) -codec.enableMasterMode() -codec.setALRCGPIO() # Note, should not be changed while ADC is enabled. +codec.sample_rate = 44100 +codec.master_mode = True +codec.gpio_output = True # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs -codec.enableAdc() -codec.enableDac() +codec.adc = codec.dac = True # Loopback sends ADC data directly into DAC -codec.enableLoopBack() +codec.loopback = True # Default is "soft mute" on, so we must disable mute to make channels active -codec.disableDacMute() +codec.dac_mute = False -print("Headphopne Amp Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground +# Enable headphone amp output +codec.headphone = True +codec.headphone_volume = 0.0 +codec.mono_output = True # Enables capless mode using the VMID as buffer for headphone ground on OUT3 -# Automatic Level control stuff +# Automatic Level control configuration -# Only allows pga gain stages at a "zero crossover" point in audio stream. +# Only allows mic gain stages at a "zero crossover" point in audio stream. # Minimizes "zipper" noise when chaning gains. -codec.enablePgaZeroCross() +codec.mic_zero_cross = True -codec.enableAlc(adafruit_wm8960.ALC_MODE_STEREO) -codec.setAlcTarget(adafruit_wm8960.ALC_TARGET_LEVEL_NEG_6DB) -codec.setAlcDecay(adafruit_wm8960.ALC_DECAY_TIME_192MS) -codec.setAlcAttack(adafruit_wm8960.ALC_ATTACK_TIME_24MS) -codec.setAlcMaxGain(adafruit_wm8960.ALC_MAX_GAIN_LEVEL_30DB) -codec.setAlcMinGain(adafruit_wm8960.ALC_MIN_GAIN_LEVEL_NEG_17_25DB) -codec.setAlcHold(adafruit_wm8960.ALC_HOLD_TIME_0MS) - -print("Codec setup complete. Listen to left/right INPUT1 on Headphone outputs.") +codec.alc = True +codec.alc_target = -6.0 +codec.alc_attack_time = 0.024 +codec.alc_hold_time = 0.0 +codec.alc_decay_time = 0.192 +codec.alc_max_gain = adafruit_wm8960.ALC_MAX_GAIN_MAX +codec.alc_min_gain = adafruit_wm8960.ALC_MIN_GAIN_MIN while True: - # Take a bunch of readings and average them, to smooth out the value - for i in range(250): - userInputA0 += analog_in.value - time.sleep(0.001) - userInputA0 /= 250.0 - - # Map it from 0-4096, to a value that is acceptable for the setting - alcTarget = map_range(userInputA0, 0.0, 65536.0, 15.0, 0.0) - - print("alcTarget: ", alcTarget) - - codec.setAlcTarget(alcTarget) # Valid inputs are 0-15, 0 = -22.5dB FS, ... 1.5dB steps ... , 15 = -1.5dB FS - + codec.alc_target = map_range(analog_in.value, 0, 65536, adafruit_wm8960.ALC_TARGET_MIN, adafruit_wm8960.ALC_TARGET_MAX) time.sleep(1.0) diff --git a/examples/wm8960_eighties_dystopia.py b/examples/wm8960_eighties_dystopia.py deleted file mode 100644 index dba5f94..0000000 --- a/examples/wm8960_eighties_dystopia.py +++ /dev/null @@ -1,124 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Tod Kurt (@todbot) -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT - -''' -Demonstrates I2C Output on WM8960 Codec by generating "a swirling ominous wub that evolves over time" using synthio. - -Modified from original code by @todbot / Tod Kurt: https://github.com/todbot/circuitpython-synthio-tricks/blob/main/examples/eighties_dystopia/code.py - -It will output the sound on the headphone outputs. -It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. - -HARDWARE CONNECTIONS - -********************** -MCU --------- CODEC -********************** -QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL -GND --------- GND *optional, but not a bad idea -5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) -AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC -AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. -AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. - -********************** -CODEC ------- AUDIO OUT -********************** -OUT3 -------- TRS OUTPUT SLEEVE *buffered "vmid" (aka "HP GND") -HPL --------- TRS OUTPUT TIP *left HP output -HPR --------- TRS OUTPUT RING1 *right HP output -''' - -import board, audiobusio -import audiomixer, synthio -import adafruit_wm8960 -import time, random -import digitalio -import ulab.numpy as np - -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup Digital Interface -codec.sampleRate = 44100 -codec.wordLength = 16 - -# Enable DAC -codec.dacEnabled = True -codec.dacOutputEnabled = True -codec.stereoOutputEnabled = True -codec.dacMute = False - -# Enable Headphone Amp with OUT3 as capless buffer for headphone ground -codec.headphoneEnabled = True -codec.monoOutputEnabled = True -codec.headphoneVolumeDb = 0.0 - -# Configure I2S Output -audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) - -led = digitalio.DigitalInOut(board.LED) -led.switch_to_output() -led.value = True - -notes = (33, 34, 31) # possible notes to play MIDI A1, A1#, G1 -note_duration = 15 # how long each note plays for -num_voices = 5 # how many voices for each note -lpf_basef = 500 # filter lowest frequency -lpf_resonance = 1.5 # filter q - -mixer = audiomixer.Mixer(channel_count=2, sample_rate=codec.sampleRate, buffer_size=8192) -synth = synthio.Synthesizer(channel_count=2, sample_rate=codec.sampleRate) -audio.play(mixer) -mixer.voice[0].play(synth) -mixer.voice[0].level = 0.8 - -# our oscillator waveform, a 512 sample downward saw wave going from +/-30k -wave_saw = np.linspace(30000, -30000, num=512, dtype=np.int16) # max is +/-32k but gives us headroom -amp_env = synthio.Envelope(attack_level=1, sustain_level=1) - -# set up the voices (aka "Notes" in synthio-speak) w/ initial values -voices = [] -for i in range(num_voices): - voices.append( synthio.Note( frequency=0, envelope=amp_env, waveform=wave_saw ) ) - -# set all the voices to the "same" frequency (with random detuning) -# zeroth voice is sub-oscillator, one-octave down -def set_notes(n): - for voice in voices: - #f = synthio.midi_to_hz( n ) + random.uniform(0,1.0) # what orig sketch does - f = synthio.midi_to_hz( n + random.uniform(0,0.4) ) # more valid if we move up the scale - voice.frequency = f - voices[0].frequency = voices[0].frequency/2 # bass note one octave down - -# the LFO that modulates the filter cutoff -lfo_filtermod = synthio.LFO(rate=0.05, scale=2000, offset=2000) -# we can't attach this directly to a filter input, so stash it in the blocks runner -synth.blocks.append(lfo_filtermod) - -note = notes[0] -last_note_time = time.monotonic() -last_filtermod_time = time.monotonic() - -# start the voices playing -set_notes(note) -synth.press(voices) - -while True: - # continuosly update filter, no global filter, so update each voice's filter - for v in voices: - v.filter = synth.low_pass_filter( lpf_basef + lfo_filtermod.value, lpf_resonance ) - - if time.monotonic() - last_filtermod_time > 1: - last_filtermod_time = time.monotonic() - # randomly modulate the filter frequency ('rate' in synthio) to make more dynamic - lfo_filtermod.rate = 0.01 + random.random() / 8 - print("filtermod",lfo_filtermod.rate) - - if time.monotonic() - last_note_time > note_duration: - last_note_time = time.monotonic() - # pick new note, but not one we're currently playing - note = random.choice([n for n in notes if n != note]) - set_notes(note) - print("note", note, ["%3.2f" % v.frequency for v in voices] ) diff --git a/examples/wm8960_02_INPUT2.py b/examples/wm8960_input.py similarity index 72% rename from examples/wm8960_02_INPUT2.py rename to examples/wm8960_input.py index c442595..efa239e 100644 --- a/examples/wm8960_02_INPUT2.py +++ b/examples/wm8960_input.py @@ -49,28 +49,41 @@ import board import adafruit_wm8960 -print("Example 2 - INPUT2") - codec = adafruit_wm8960.WM8960(board.I2C()) # Setup signal flow through the analog audio bypass connections -# Set input boosts to get INPUT2 (both left and right) to the boost mixers -codec.setIN2BOOST(adafruit_wm8960.BOOST_MIXER_GAIN_0DB) +# INPUT1 must pass through the Mic Boost +codec.mic = True +codec.mic_inverting_input = True +codec.mic_input = adafruit_wm8960.MIC_VMID # Non-inverting input +codec.mic_mute = False +codec.mic_boost_gain = 0.0 +codec.mic_boost = True +codec.mic_volume = 0.0 + +# In order to use INPUT2 or INPUT3 with the Mic Boost (PGA) +#codec.mic_input = adafruit_wm8960.MIC_INPUT2 +#codec.mic_input = adafruit_wm8960.MIC_INPUT3 +# If codec.mic_inverting_input is enabled, mic boost uses input as balanced input between INPUT1 & INPUT2/3 + +# Set input boosts to get INPUT2 or INPUT3 (both left and right) to the boost mixers and bypass the Mic Boost (PGA) +codec.input2_boost = 0.0 +codec.input3_boost = 0.0 # Enable input boost mixers -codec.enableAIN() +codec.input = True # Connect LB2LO (booster to output mixer [aka analog bypass]) -codec.enableB2O() +codec.mic_output = True # Set gainstage between boost mixer and output mixers (analog bypass) -codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) +codec.mic_output_volume = 0.0 # Enable output mixers -codec.enableOMIX() - -print("Volume set to +0dB") -codec.configureHeadphones(dB=0.0, capless=True) # Capless provides VMID as buffer for headphone ground +codec.output = True -print("Example complete. Listen to inputs 2 on headphone outputs.") +# Enable Headphone Amp with OUT3 as capless buffer for headphone ground +codec.headphone = True +codec.mono_output = True +codec.headphone_volume = 0.0 diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index 74ae6d8..91dd6f0 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -61,6 +61,10 @@ codec.mono_output = True codec.headphone_volume = 0.0 +# Enable Speaker Amp +codec.speaker = True +codec.speaker_volume = 0.0 + # Configure I2S Output audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) From dcd09f2f6598294efc1e7a90e9e2cac87721bdd4 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 23 Aug 2024 08:36:17 -0500 Subject: [PATCH 42/56] Added related product links. --- docs/index.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 4ad2b82..4e58eb7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,14 +24,11 @@ Table of Contents .. toctree:: :caption: Tutorials -.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave - the toctree above for use later. - .. toctree:: :caption: Related Products -.. todo:: Add any product links here. If there are none, then simply delete this todo and leave - the toctree above for use later. + Adafruit Voice Bonnet for Raspberry Pi + SparkFun Audio Codec Breakout .. toctree:: :caption: Other Links From 062655c1f0ca86fbc8d632c2559b1872fd48ec1f Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 23 Aug 2024 08:36:31 -0500 Subject: [PATCH 43/56] Updated usage example. --- README.rst | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 2cf86c6..2af6f81 100644 --- a/README.rst +++ b/README.rst @@ -87,20 +87,16 @@ Usage Example .. code-block:: python - # Monitor Stereo MIC Input: - # MIC (INPUT1) => PGA => Boost Mixer => Output Mixer => Headphones + # Monitor Stereo Input: INPUT3 => Output Mixer => Headphones import board, adafruit_wm8960 codec = adafruit_wm8960.WM8960(board.I2C()) - codec.enableMIC() - codec.enableMN1() - codec.enableINMUTE() - codec.setMICBOOST(adafruit_wm8960.MIC_BOOST_GAIN_0DB) - codec.connectMIC2B() - codec.enableAIN() - codec.enableB2O() - codec.setB2OVOL(adafruit_wm8960.OUTPUT_MIXER_GAIN_0DB) - codec.enableOMIX() - codec.configureHeadphones() + codec.input = True + codec.input3_output = True + codec.input3_output_volume = 0.0 + codec.output = True + codec.headphone = True + codec.mono_output = True + codec.headphone_volume = 0.0 Documentation ============= From 8811ae14d23bcf96b80497c9af7b36823bfeaf39 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 23 Aug 2024 09:44:48 -0500 Subject: [PATCH 44/56] Updated formatting using black. --- adafruit_wm8960.py | 938 ++++++++++++++------- docs/conf.py | 4 +- examples/wm8960_3d_enhance.py | 6 +- examples/wm8960_automatic_level_control.py | 20 +- examples/wm8960_input.py | 10 +- examples/wm8960_simpletest.py | 8 +- 6 files changed, 684 insertions(+), 302 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index f3a9cb4..7eef3df 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -99,12 +99,7 @@ MIC_VMID = 2 # Microphone Boost gain options -_MIC_BOOST_GAIN = [ # in dB - 0.0, # 0 - 13.0, # 1 - 20.0, # 2 - 29.0 # 3 -] +_MIC_BOOST_GAIN = [0.0, 13.0, 20.0, 29.0] # in dB # Mic Bias voltage options MIC_BIAS_VOLTAGE_0_9_AVDD = 0 @@ -158,59 +153,19 @@ ALC_HOLD_TIME_MAX = 43.691 # Speaker Boost Gains (DC and AC) -_SPEAKER_BOOST_GAIN = [ # in dB - 0.0, # 0 - 2.1, # 1 - 2.9, # 2 - 3.6, # 3 - 4.5, # 4 - 5.1 # 5 -] +_SPEAKER_BOOST_GAIN = [0.0, 2.1, 2.9, 3.6, 4.5, 5.1] # in dB # VMIDSEL settings VMIDSEL_DISABLED = 0 -VMIDSEL_PLAYBACK = 1 # 2X50KOHM -VMIDSEL_LOWPOWER = 2 # 2X250KOHM -VMIDSEL_FASTSTART = 3 # 2X5KOHM +VMIDSEL_PLAYBACK = 1 # 2X50KOHM +VMIDSEL_LOWPOWER = 2 # 2X250KOHM +VMIDSEL_FASTSTART = 3 # 2X5KOHM # Clock Divider Tables -_BCLKDIV = [ - 1.0, - 1.5, - 2.0, - 3.0, - 4.0, - 5.5, - 6.0, - 8.0, - 11.0, - 12.0, - 16.0, - 22.0, - 24.0, - 32.0 -] - -_DCLKDIV = [ - 1.5, - 2.0, - 3.0, - 4.0, - 6.0, - 8.0, - 12.0, - 16.0 -] - -_ADCDACDIV = [ - 1.0, - 1.5, - 2.0, - 3.0, - 4.0, - 5.5, - 6.0 -] +_BCLKDIV = [1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0, 8.0, 11.0, 12.0, 16.0, 22.0, 24.0, 32.0] +_DCLKDIV = [1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0] +_ADCDACDIV = [1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0] + class WOBit: @@ -221,17 +176,17 @@ def __init__( ) -> None: self.register_address = register_address self.bit = bit - self.bit_mask = 1 << (bit % 8) # the bitmask *within* the byte! - self.byte = 1 - (bit // 8) # the byte number within the buffer + self.bit_mask = 1 << (bit % 8) # the bitmask *within* the byte! + self.byte = 1 - (bit // 8) # the byte number within the buffer - def _set(self, obj:Optional[I2CDeviceDriver], value:bool) -> None: + def _set(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: if value: obj._registers[self.register_address][self.byte] |= self.bit_mask else: obj._registers[self.register_address][self.byte] &= ~self.bit_mask - + def reset(self, obj: Optional[I2CDeviceDriver]) -> None: - if not hasattr(self, 'default'): + if not hasattr(self, "default"): self.default = self.__get__(obj) self._set(obj, self.default) @@ -247,6 +202,7 @@ def __set__(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: with obj.i2c_device as i2c: i2c.write(obj._registers[self.register_address]) + class WOBits: def __init__( # pylint: disable=too-many-arguments @@ -260,7 +216,7 @@ def __init__( # pylint: disable=too-many-arguments self.bit_mask = ((1 << num_bits) - 1) << lowest_bit self.lowest_bit = lowest_bit - def _set(self, obj:Optional[I2CDeviceDriver], value:int) -> None: + def _set(self, obj: Optional[I2CDeviceDriver], value: int) -> None: value <<= self.lowest_bit # shift the value over to the right spot reg = 0 for i in range(2): @@ -270,10 +226,10 @@ def _set(self, obj:Optional[I2CDeviceDriver], value:int) -> None: for i in range(1, -1, -1): obj._registers[self.register_address][i] = reg & 0xFF reg >>= 8 - + # Must call first before using object - def reset(self, obj:Optional[I2CDeviceDriver]) -> None: - if not hasattr(self, 'default'): + def reset(self, obj: Optional[I2CDeviceDriver]) -> None: + if not hasattr(self, "default"): self.default = self.__get__(obj) self._set(obj, self.default) @@ -294,6 +250,7 @@ def __set__(self, obj: Optional[I2CDeviceDriver], value: int) -> None: with obj.i2c_device as i2c: i2c.write(obj._registers[self.register_address]) + class WM8960: # Power @@ -306,12 +263,13 @@ class WM8960: @property def input(self) -> bool: return self.left_input and self.right_input + @input.setter - def input(self, value:bool) -> None: + def input(self, value: bool) -> None: self.left_input = self.right_input = value # MIC - + ## PWR_MGMT left_mic = WOBit(_REG_PWR_MGMT_3, 5) @@ -320,8 +278,9 @@ def input(self, value:bool) -> None: @property def mic(self) -> bool: return self.left_mic and self.right_mic + @mic.setter - def mic(self, value:bool) -> None: + def mic(self, value: bool) -> None: self.left_mic = self.right_mic = value ## SIGNAL_PATH @@ -332,8 +291,9 @@ def mic(self, value:bool) -> None: @property def mic_inverting_input(self) -> bool: return self.left_mic_inverting_input and self.right_mic_inverting_input + @mic_inverting_input.setter - def mic_inverting_input(self, value:bool) -> None: + def mic_inverting_input(self, value: bool) -> None: self.left_mic_inverting_input = self.right_mic_inverting_input = value _left_mic_input2 = WOBit(_REG_ADCL_SIGNAL_PATH, 6) @@ -347,14 +307,15 @@ def left_mic_input(self) -> int: return MIC_INPUT3 else: return MIC_VMID + @left_mic_input.setter - def left_mic_input(self, input:int) -> None: + def left_mic_input(self, input: int) -> None: self._left_mic_input2 = input == MIC_INPUT2 self._left_mic_input3 = input == MIC_INPUT3 _right_mic_input2 = WOBit(_REG_ADCR_SIGNAL_PATH, 6) _right_mic_input3 = WOBit(_REG_ADCR_SIGNAL_PATH, 7) - + @property def right_mic_input(self) -> int: if self._right_mic_input2: @@ -363,8 +324,9 @@ def right_mic_input(self) -> int: return MIC_INPUT3 else: return MIC_VMID + @right_mic_input.setter - def right_mic_input(self, input:int) -> None: + def right_mic_input(self, input: int) -> None: self._right_mic_input2 = input == MIC_INPUT2 self._right_mic_input3 = input == MIC_INPUT3 @@ -372,8 +334,9 @@ def right_mic_input(self, input:int) -> None: def mic_input(self) -> int: # NOTE: Not checking right signal return self.left_mic_input + @mic_input.setter - def mic_input(self, input:int) -> None: + def mic_input(self, input: int) -> None: self.left_mic_input = self.right_mic_input = input ## Boost @@ -384,23 +347,25 @@ def mic_input(self, input:int) -> None: @property def mic_boost(self) -> bool: return self.left_mic_boost and self.right_mic_boost + @mic_boost.setter - def mic_boost(self, value:bool) -> None: + def mic_boost(self, value: bool) -> None: self.left_mic_boost = self.right_mic_boost = value - def _get_mic_boost_gain(self, value:float) -> int: + def _get_mic_boost_gain(self, value: float) -> int: for i in reversed(range(len(_MIC_BOOST_GAIN))): if value >= _MIC_BOOST_GAIN[i]: return i return 0 - + _left_mic_boost_gain = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) @property def left_mic_boost_gain(self) -> float: return _MIC_BOOST_GAIN[self._left_mic_boost_gain] + @left_mic_boost_gain.setter - def left_mic_boost_gain(self, value:float) -> None: + def left_mic_boost_gain(self, value: float) -> None: self._left_mic_boost_gain = self._get_mic_boost_gain(value) _right_mic_boost_gain = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) @@ -408,16 +373,20 @@ def left_mic_boost_gain(self, value:float) -> None: @property def right_mic_boost_gain(self) -> float: return _MIC_BOOST_GAIN[self._right_mic_boost_gain] + @right_mic_boost_gain.setter - def right_mic_boost_gain(self, value:float) -> None: + def right_mic_boost_gain(self, value: float) -> None: self._right_mic_boost_gain = self._get_mic_boost_gain(value) @property def mic_boost_gain(self) -> float: return max(self.left_mic_boost_gain, self.right_mic_boost_gain) + @mic_boost_gain.setter - def mic_boost_gain(self, value:float) -> None: - self._left_mic_boost_gain = self._right_mic_boost_gain = self._get_mic_boost_gain(value) + def mic_boost_gain(self, value: float) -> None: + self._left_mic_boost_gain = self._right_mic_boost_gain = ( + self._get_mic_boost_gain(value) + ) ## Volume @@ -429,26 +398,54 @@ def mic_boost_gain(self, value:float) -> None: @property def left_mic_volume(self) -> float: - return map_range(self._left_mic_volume, 0, 63, MIC_GAIN_MIN, MIC_GAIN_MAX) + # fmt: off + return map_range( + self._left_mic_volume, 0, 63, + MIC_GAIN_MIN, MIC_GAIN_MAX + ) + # fmt: on + @left_mic_volume.setter - def left_mic_volume(self, value:float) -> None: - self._left_mic_volume = round(map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63)) + def left_mic_volume(self, value: float) -> None: + # fmt: off + self._left_mic_volume = round(map_range( + value, MIC_GAIN_MIN, MIC_GAIN_MAX, + 0, 63 + )) + # fmt: on self._left_mic_volume_set = True - + @property def right_mic_volume(self) -> float: - return map_range(self._right_mic_volume, 0, 63, MIC_GAIN_MIN, MIC_GAIN_MAX) + # fmt: off + return map_range( + self._right_mic_volume, 0, 63, + MIC_GAIN_MIN, MIC_GAIN_MAX + ) + # fmt: on + @right_mic_volume.setter - def right_mic_volume(self, value:float) -> None: - self._right_mic_volume = round(map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63)) + def right_mic_volume(self, value: float) -> None: + # fmt: off + self._right_mic_volume = round(map_range( + value, MIC_GAIN_MIN, MIC_GAIN_MAX, + 0, 63 + )) + # fmt: on self._right_mic_volume_set = True - + @property def mic_volume(self) -> float: return max(self.left_mic_volume, self.right_mic_volume) + @mic_volume.setter - def mic_volume(self, value:float) -> None: - self._left_mic_volume = self._right_mic_volume = round(map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63)) + def mic_volume(self, value: float) -> None: + # fmt: off + self._left_mic_volume = self._right_mic_volume = round(map_range( + value, MIC_GAIN_MIN, MIC_GAIN_MAX, + 0, 63 + )) + # fmt: on self._left_mic_volume_set = self._right_mic_volume_set = True ## Zero Cross @@ -459,8 +456,9 @@ def mic_volume(self, value:float) -> None: @property def mic_zero_cross(self) -> bool: return self.left_mic_zero_cross and self.right_mic_zero_cross + @mic_zero_cross.setter - def mic_zero_cross(self, value:bool) -> None: + def mic_zero_cross(self, value: bool) -> None: self.left_mic_zero_cross = self.right_mic_zero_cross = value ## Mute @@ -471,24 +469,27 @@ def mic_zero_cross(self, value:bool) -> None: @property def left_mic_mute(self) -> bool: return self._left_mic_mute + @left_mic_mute.setter - def left_mic_mute(self, value:bool) -> None: + def left_mic_mute(self, value: bool) -> None: self._left_mic_mute = value self._left_mic_volume_set = True @property def right_mic_mute(self) -> bool: return self._right_mic_mute + @right_mic_mute.setter - def right_mic_mute(self, value:bool) -> None: + def right_mic_mute(self, value: bool) -> None: self._right_mic_mute = value self._right_mic_volume_set = True @property def mic_mute(self) -> bool: return self._left_mic_mute and self._right_mic_mute + @mic_mute.setter - def mic_mute(self, value:bool) -> None: + def mic_mute(self, value: bool) -> None: self._left_mic_mute = self._right_mic_mute = value # Boost Mixer @@ -500,10 +501,25 @@ def left_input2_boost(self) -> float: value = self._left_input2_boost if value == 0: return None - return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + # fmt: off + return map_range( + value, 1, 7, + BOOST_GAIN_MIN, BOOST_GAIN_MAX + ) + # fmt: on + @left_input2_boost.setter - def left_input2_boost(self, value:float) -> None: - self._left_input2_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + def left_input2_boost(self, value: float) -> None: + # fmt: off + self._left_input2_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range( + value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, + 1, 7 + )) + ) + # fmt: on _right_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) @@ -512,18 +528,52 @@ def right_input2_boost(self) -> float: value = self._right_input2_boost if value == 0: return None - return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + # fmt: off + return map_range( + value, 1, 7, + BOOST_GAIN_MIN, BOOST_GAIN_MAX + ) + # fmt: on + @right_input2_boost.setter - def right_input2_boost(self, value:float) -> None: - self._right_input2_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + def right_input2_boost(self, value: float) -> None: + # fmt: off + self._right_input2_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range( + value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, + 1, 7 + )) + ) + # fmt: on @property def input2_boost(self) -> float: value = max(self._left_input2_boost, self._right_input2_boost) - return None if value is 0 else map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + # fmt: off + return ( + None + if value is 0 + else map_range( + value, 1, 7, + BOOST_GAIN_MIN, BOOST_GAIN_MAX + ) + ) + # fmt: on + @input2_boost.setter - def input2_boost(self, value:float) -> None: - self._left_input2_boost = self._right_input2_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + def input2_boost(self, value: float) -> None: + # fmt: off + self._left_input2_boost = self._right_input2_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range( + value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, + 1, 7 + )) + ) + # fmt: on _left_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) @@ -532,10 +582,25 @@ def left_input3_boost(self) -> float: value = self._left_input3_boost if value == 0: return None - return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + # fmt: off + return map_range( + value, 1, 7, + BOOST_GAIN_MIN, BOOST_GAIN_MAX + ) + # fmt: on + @left_input3_boost.setter - def left_input3_boost(self, value:float) -> None: - self._left_input3_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + def left_input3_boost(self, value: float) -> None: + # fmt: off + self._left_input3_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range( + value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, + 1, 7 + )) + ) + # fmt: on _right_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) @@ -544,18 +609,52 @@ def right_input3_boost(self) -> float: value = self._right_input3_boost if value == 0: return None - return map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + # fmt: off + return map_range( + value, 1, 7, + BOOST_GAIN_MIN, BOOST_GAIN_MAX + ) + # fmt: on + @right_input3_boost.setter - def right_input3_boost(self, value:float) -> None: - self._right_input3_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + def right_input3_boost(self, value: float) -> None: + # fmt: off + self._right_input3_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range( + value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, + 1, 7 + )) + ) + # fmt: on @property def input3_boost(self) -> float: value = max(self._left_input3_boost, self._right_input3_boost) - return None if value is 0 else map_range(max(value, 1), 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + # fmt: off + return ( + None + if value is 0 + else map_range( + value, 1, 7, + BOOST_GAIN_MIN, BOOST_GAIN_MAX + ) + ) + # fmt: on + @input3_boost.setter - def input3_boost(self, value:float) -> None: - self._left_input3_boost = self._right_input3_boost = 0 if value < BOOST_GAIN_MIN else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + def input3_boost(self, value: float) -> None: + # fmt: off + self._left_input3_boost = self._right_input3_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range( + value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, + 1, 7 + )) + ) + # fmt: on # Mic Bias @@ -570,10 +669,11 @@ def input3_boost(self, value:float) -> None: @property def adc(self) -> bool: return self.left_adc and self.right_adc + @adc.setter - def adc(self, value:bool) -> None: + def adc(self, value: bool) -> None: self.left_adc = self.right_adc = value - + ## Volume _left_adc_volume = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) @@ -581,10 +681,21 @@ def adc(self, value:bool) -> None: @property def left_adc_volume(self) -> float: - return map_range(max(self._left_adc_volume, 1), 1, 255, ADC_VOLUME_MIN, ADC_VOLUME_MAX) + # fmt: off + return map_range( + self._left_adc_volume, 1, 255, + ADC_VOLUME_MIN, ADC_VOLUME_MAX + ) + # fmt: on + @left_adc_volume.setter - def left_adc_volume(self, value:float) -> None: - self._left_adc_volume = round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 0, 254) + 1.0) + def left_adc_volume(self, value: float) -> None: + # fmt: off + self._left_adc_volume = round(map_range( + value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, + 1, 255 + )) + # fmt: on self._left_adc_volume_set = True _right_adc_volume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) @@ -592,18 +703,35 @@ def left_adc_volume(self, value:float) -> None: @property def right_adc_volume(self) -> float: - return map_range(max(self._right_adc_volume, 1), 1, 255, ADC_VOLUME_MIN, ADC_VOLUME_MAX) + # fmt: off + return map_range( + self._right_adc_volume, 1, 255, + ADC_VOLUME_MIN, ADC_VOLUME_MAX + ) + # fmt: on + @right_adc_volume.setter - def right_adc_volume(self, value:float) -> None: - self._right_adc_volume = round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 0, 254) + 1.0) + def right_adc_volume(self, value: float) -> None: + # fmt: off + self._right_adc_volume = round(map_range( + value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, + 1, 255 + )) + # fmt: on self._right_adc_volume_set = True @property def adc_volume(self) -> int: return max(self.left_adc_volume, self.right_adc_volume) + @adc_volume.setter - def adc_volume(self, value:float) -> None: - self._left_adc_volume = self._right_adc_volume = round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 0, 254) + 1.0) + def adc_volume(self, value: float) -> None: + # fmt: off + self._left_adc_volume = self._right_adc_volume = round(map_range( + value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, + 1, 255 + )) + # fmt: on self._left_adc_volume_set = self._right_adc_volume_set = True # ALC @@ -614,68 +742,130 @@ def adc_volume(self, value:float) -> None: @property def alc(self) -> bool: return self.left_alc and self.right_alc + @alc.setter - def alc(self, value:bool) -> None: + def alc(self, value: bool) -> None: self.left_alc = self.right_alc = value _alc_target = WOBits(4, _REG_ALC1, 0) @property def alc_target(self) -> float: - return map_range(self._alc_target, 0, 15, ALC_TARGET_MIN, ALC_TARGET_MAX) + # fmt: off + return map_range( + self._alc_target, 0, 15, + ALC_TARGET_MIN, ALC_TARGET_MAX + ) + # fmt: on + @alc_target.setter - def alc_target(self, value:float) -> None: - self._alc_target = round(map_range(value, ALC_TARGET_MIN, ALC_TARGET_MAX, 0, 15)) + def alc_target(self, value: float) -> None: + # fmt: off + self._alc_target = round(map_range( + value, ALC_TARGET_MIN, ALC_TARGET_MAX, + 0, 15 + )) + # fmt: on _alc_max_gain = WOBits(3, _REG_ALC1, 4) @property def alc_max_gain(self) -> float: - return map_range(self._alc_max_gain, 0, 7, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX) + # fmt: off + return map_range( + self._alc_max_gain, 0, 7, + ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX + ) + # fmt: on + @alc_max_gain.setter - def alc_max_gain(self, value:float) -> None: - self._alc_max_gain = round(map_range(value, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX, 0, 7)) + def alc_max_gain(self, value: float) -> None: + # fmt: off + self._alc_max_gain = round(map_range( + value, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX, + 0, 7 + )) + # fmt: on _alc_min_gain = WOBits(3, _REG_ALC2, 4) @property def alc_min_gain(self) -> float: - return map_range(self._alc_min_gain, 0, 7, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX) + # fmt: off + return map_range( + self._alc_min_gain, 0, 7, + ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX + ) + # fmt: on + @alc_min_gain.setter - def alc_min_gain(self, value:float) -> None: - self._alc_min_gain = round(map_range(value, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX, 0, 7)) + def alc_min_gain(self, value: float) -> None: + # fmt: off + self._alc_min_gain = round(map_range( + value, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX, + 0, 7 + )) + # fmt: on _alc_attack = WOBits(4, _REG_ALC3, 0) @property def alc_attack_time(self) -> float: return ALC_ATTACK_TIME_MIN * pow(2, self._alc_attack) + @alc_attack_time.setter - def alc_attack_time(self, value:float) -> None: - self._alc_attack = min(round(math.log2((constrain(value, ALC_ATTACK_TIME_MIN, ALC_ATTACK_TIME_MAX) - ALC_ATTACK_TIME_MIN) / ALC_ATTACK_TIME_MIN)), _ALC_ATTACK_MAX) + def alc_attack_time(self, value: float) -> None: + # fmt: off + self._alc_attack = min( + round(math.log2( + (constrain(value, ALC_ATTACK_TIME_MIN, ALC_ATTACK_TIME_MAX) - ALC_ATTACK_TIME_MIN) + / ALC_ATTACK_TIME_MIN + )), + _ALC_ATTACK_MAX + ) + # fmt: on _alc_decay = WOBits(4, _REG_ALC3, 4) @property def alc_decay_time(self) -> float: return ALC_DECAY_TIME_MIN * pow(2, self._alc_decay) + @alc_decay_time.setter - def alc_decay_time(self, value:float) -> None: - self._alc_decay = min(round(math.log2((constrain(value, ALC_DECAY_TIME_MIN, ALC_DECAY_TIME_MAX) - ALC_DECAY_TIME_MIN) / ALC_DECAY_TIME_MIN)), _ALC_DECAY_MAX) + def alc_decay_time(self, value: float) -> None: + # fmt: off + self._alc_decay = min( + round(math.log2( + (constrain(value, ALC_DECAY_TIME_MIN, ALC_DECAY_TIME_MAX) - ALC_DECAY_TIME_MIN) + / ALC_DECAY_TIME_MIN + )), + _ALC_DECAY_MAX + ) + # fmt: on _alc_hold = WOBits(4, _REG_ALC2, 0) @property def alc_hold_time(self) -> float: value = self._alc_hold - if value == 0: return 0.0 + if value == 0: + return 0.0 return ALC_HOLD_TIME_MIN * pow(2, self._alc_hold - 1) + @alc_hold_time.setter - def alc_hold_time(self, value:float) -> None: + def alc_hold_time(self, value: float) -> None: if value <= 0.0: self._alc_hold = 0 else: - self._alc_hold = round(math.log2((constrain(value, ALC_HOLD_TIME_MIN, ALC_HOLD_TIME_MAX) - ALC_HOLD_TIME_MIN) / ALC_HOLD_TIME_MIN) + 1.0) + # fmt: off + self._alc_hold = round( + math.log2( + (constrain(value, ALC_HOLD_TIME_MIN, ALC_HOLD_TIME_MAX) - ALC_HOLD_TIME_MIN) + / ALC_HOLD_TIME_MIN + ) + + 1.0 + ) + # fmt: on alc_limiter = WOBit(_REG_ALC3, 8) @@ -687,10 +877,21 @@ def alc_hold_time(self, value:float) -> None: @property def noise_gate_threshold(self) -> float: - return map_range(self._noise_gate_threshold, 0, 31, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX) + # fmt: off + return map_range( + self._noise_gate_threshold, 0, 31, + GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX + ) + # fmt: on + @noise_gate_threshold.setter - def noise_gate_threshold(self, value:float) -> None: - self._noise_gate_threshold = round(map_range(value, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX, 0, 31)) + def noise_gate_threshold(self, value: float) -> None: + # fmt: off + self._noise_gate_threshold = round(map_range( + value, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX, + 0, 31 + )) + # fmt: on # DAC @@ -700,19 +901,31 @@ def noise_gate_threshold(self, value:float) -> None: @property def dac(self) -> bool: return self.left_dac and self.right_dac + @dac.setter - def dac(self, value:bool) -> None: + def dac(self, value: bool) -> None: self.left_dac = self.right_dac = value - + _left_dac_volume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) _left_dac_volume_set = WOBit(_REG_LEFT_DAC_VOLUME, 8) @property def left_dac_volume(self) -> float: - return map_range(max(self._left_dac_volume, 1), 1, 255, DAC_VOLUME_MIN, DAC_VOLUME_MAX) + # fmt: off + return map_range( + self._left_dac_volume, 1, 255, + DAC_VOLUME_MIN, DAC_VOLUME_MAX + ) + # fmt: on + @left_dac_volume.setter - def left_dac_volume(self, value:float) -> None: - self._left_dac_volume = round(map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 0, 254) + 1.0) + def left_dac_volume(self, value: float) -> None: + # fmt: off + self._left_dac_volume = round(map_range( + value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, + 1, 255 + )) + # fmt: on self._left_dac_volume_set = True _right_dac_volume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) @@ -720,18 +933,35 @@ def left_dac_volume(self, value:float) -> None: @property def right_dac_volume(self) -> float: - return map_range(max(self._right_dac_volume, 1), 1, 255, DAC_VOLUME_MIN, DAC_VOLUME_MAX) + # fmt: off + return map_range( + self._right_dac_volume, 1, 255, + DAC_VOLUME_MIN, DAC_VOLUME_MAX + ) + # fmt: on + @right_dac_volume.setter - def right_dac_volume(self, value:float) -> None: - self._right_dac_volume = round(map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 0, 254) + 1.0) + def right_dac_volume(self, value: float) -> None: + # fmt: off + self._right_dac_volume = round(map_range( + value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, + 1, 255 + )) + # fmt: on self._right_dac_volume_set = True @property def dac_volume(self) -> int: return max(self.left_dac_volume, self.right_dac_volume) + @dac_volume.setter - def dac_volume(self, value:float) -> None: - self._left_dac_volume = self._right_dac_volume = round(map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 0, 254) + 1.0) + def dac_volume(self, value: float) -> None: + # fmt: off + self._left_dac_volume = self._right_dac_volume = round(map_range( + value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, + 1, 255 + )) + # fmt: on self._left_dac_volume_set = self._right_dac_volume_set = True dac_mute = WOBit(_REG_ADC_DAC_CTRL_1, 3) @@ -750,9 +980,15 @@ def dac_volume(self, value:float) -> None: @property def enhance_depth(self) -> float: return self._enhance_depth / 15.0 + @enhance_depth.setter - def enhance_depth(self, value:float) -> None: - self._enhance_depth = round(map_range(value, 0.0, 1.0, 0, 15)) + def enhance_depth(self, value: float) -> None: + # fmt: off + self._enhance_depth = round(map_range( + value, 0.0, 1.0, + 0, 15 + )) + # fmt: on # Output Mixer @@ -762,8 +998,9 @@ def enhance_depth(self, value:float) -> None: @property def output(self) -> bool: return self.left_output and self.right_output + @output.setter - def output(self, value:bool) -> None: + def output(self, value: bool) -> None: self.left_output = self.right_output = value ## DAC Output @@ -774,46 +1011,76 @@ def output(self, value:bool) -> None: @property def dac_output(self) -> bool: return self.left_dac_output and self.right_dac_output + @dac_output.setter - def dac_output(self, value:bool) -> None: + def dac_output(self, value: bool) -> None: self.left_dac_output = self.right_dac_output = value ## Input 3 Output left_input3_output = WOBit(_REG_LEFT_OUT_MIX, 7) right_input3_output = WOBit(_REG_RIGHT_OUT_MIX, 7) - + @property def input3_output(self) -> bool: return self.left_input3_output and self.right_input3_output + @input3_output.setter - def input3_output(self, value:bool) -> None: + def input3_output(self, value: bool) -> None: self.left_input3_output = self.right_input3_output = value _left_input3_output_volume = WOBits(3, _REG_LEFT_OUT_MIX, 4) @property def left_input3_output_volume(self) -> float: - return map_range(self._left_input3_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) + # fmt: off + return map_range( + self._left_input3_output_volume, 0, 7, + OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + # fmt: on + @left_input3_output_volume.setter - def left_input3_output_volume(self, value:float) -> None: - self._left_input3_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def left_input3_output_volume(self, value: float) -> None: + # fmt: off + self._left_input3_output_volume = round(map_range( + value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, + 7, 0 + )) + # fmt: on _right_input3_output_volume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) @property def right_input3_output_volume(self) -> float: - return map_range(self._right_input3_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) + # fmt: off + return map_range( + self._right_input3_output_volume, 0, 7, + OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + # fmt: on + @right_input3_output_volume.setter - def right_input3_output_volume(self, value:float) -> None: - self._right_input3_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def right_input3_output_volume(self, value: float) -> None: + # fmt: off + self._right_input3_output_volume = round(map_range( + value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, + 7, 0 + )) + # fmt: on @property def input3_output_volume(self) -> float: return max(self.left_input3_output_volume, self.right_input3_output_volume) + @input3_output_volume.setter - def input3_output_volume(self, value:float) -> None: - self._left_input3_output_volume = self._right_input3_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def input3_output_volume(self, value: float) -> None: + # fmt: off + self._left_input3_output_volume = self._right_input3_output_volume = round(map_range( + value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, + 7, 0 + )) + # fmt: on ## MIC Boost Mixer Output @@ -823,34 +1090,63 @@ def input3_output_volume(self, value:float) -> None: @property def mic_output(self) -> bool: return self.left_mic_output and self.right_mic_output + @mic_output.setter - def mic_output(self, value:bool) -> None: + def mic_output(self, value: bool) -> None: self.left_mic_output = self.right_mic_output = value _left_mic_output_volume = WOBits(3, _REG_BYPASS_1, 4) @property def left_mic_output_volume(self) -> float: - return map_range(self._left_mic_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) + # fmt: off + return map_range( + self._left_mic_output_volume, 0, 7, + OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + # fmt: on + @left_mic_output_volume.setter - def left_mic_output_volume(self, value:float) -> None: - self._left_mic_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def left_mic_output_volume(self, value: float) -> None: + # fmt: off + self._left_mic_output_volume = round(map_range( + value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, + 7, 0 + )) + # fmt: on _right_mic_output_volume = WOBits(3, _REG_BYPASS_2, 4) @property def right_mic_output_volume(self) -> float: - return map_range(self._right_mic_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN) + # fmt: off + return map_range( + self._right_mic_output_volume, 0, 7, + OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + # fmt: on + @right_mic_output_volume.setter - def right_mic_output_volume(self, value:float) -> None: - self._right_mic_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def right_mic_output_volume(self, value: float) -> None: + # fmt: off + self._right_mic_output_volume = round(map_range( + value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, + 7, 0 + )) + # fmt: on @property def mic_output_volume(self) -> float: return max(self.left_mic_output_volume, self.right_mic_output_volume) + @mic_output_volume.setter - def mic_output_volume(self, value:float) -> None: - self._left_mic_output_volume = self._right_mic_output_volume = round(map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0)) + def mic_output_volume(self, value: float) -> None: + # fmt: off + self._left_mic_output_volume = self._right_mic_output_volume = round(map_range( + value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, + 7, 0 + )) + # fmt: on ## Mono Output @@ -862,8 +1158,9 @@ def mic_output_volume(self, value:float) -> None: @property def mono_mix(self) -> bool: return self.mono_left_mix and self.mono_right_mix + @mono_mix.setter - def mono_mix(self, value:bool) -> None: + def mono_mix(self, value: bool) -> None: self.mono_left_mix = self.mono_right_mix = value mono_output_attenuation = WOBit(_REG_MONO_OUT_VOLUME, 6) @@ -880,8 +1177,9 @@ def mono_mix(self, value:bool) -> None: @property def headphone(self) -> bool: return self.left_headphone and self.right_headphone + @headphone.setter - def headphone(self, value:bool) -> None: + def headphone(self, value: bool) -> None: self.left_headphone = self.right_headphone = value headphone_standby = WOBit(_REG_ANTI_POP_1, 0) @@ -891,10 +1189,21 @@ def headphone(self, value:bool) -> None: @property def left_headphone_volume(self) -> float: - return map_range(max(self._left_headphone_volume, 48), 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + # fmt: off + return map_range( + self._left_headphone_volume, 48, 127, + AMP_VOLUME_MIN, AMP_VOLUME_MAX + ) + # fmt: on + @left_headphone_volume.setter - def left_headphone_volume(self, value:float) -> None: - self._left_headphone_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + def left_headphone_volume(self, value: float) -> None: + # fmt: off + self._left_headphone_volume = round(map_range( + value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, + 48, 127 + )) + # fmt: on self._left_headphone_volume_set = True _right_headphone_volume = WOBits(7, _REG_ROUT1_VOLUME, 0) @@ -902,18 +1211,35 @@ def left_headphone_volume(self, value:float) -> None: @property def right_headphone_volume(self) -> float: - return map_range(max(self._right_headphone_volume, 48), 48, 255, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + # fmt: off + return map_range( + self._right_headphone_volume, 48, 127, + AMP_VOLUME_MIN, AMP_VOLUME_MAX + ) + # fmt: on + @right_headphone_volume.setter - def right_headphone_volume(self, value:float) -> None: - self._right_headphone_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + def right_headphone_volume(self, value: float) -> None: + # fmt: off + self._right_headphone_volume = round(map_range( + value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, + 48, 127 + )) + # fmt: on self._right_headphone_volume_set = True @property def headphone_volume(self) -> int: return max(self.left_headphone_volume, self.right_headphone_volume) + @headphone_volume.setter - def headphone_volume(self, value:float) -> None: - self._left_headphone_volume = self._right_headphone_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127) + 1.0) + def headphone_volume(self, value: float) -> None: + # fmt: off + self._left_headphone_volume = self._right_headphone_volume = round(map_range( + value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, + 48, 127 + )) + # fmt: on self._left_headphone_volume_set = self._right_headphone_volume_set = True left_headphone_zero_cross = WOBit(_REG_LOUT1_VOLUME, 7) @@ -922,8 +1248,9 @@ def headphone_volume(self, value:float) -> None: @property def headphone_zero_cross(self) -> bool: return self.left_headphone_zero_cross and self.right_headphone_zero_cross + @headphone_zero_cross.setter - def headphone_zero_cross(self, value:bool) -> None: + def headphone_zero_cross(self, value: bool) -> None: self.left_headphone_zero_cross = self.right_headphone_zero_cross = value ## Speakers @@ -934,25 +1261,28 @@ def headphone_zero_cross(self, value:bool) -> None: @property def left_speaker(self) -> bool: return self._left_speaker and self._left_speaker_amp + @left_speaker.setter - def left_speaker(self, value:bool) -> None: + def left_speaker(self, value: bool) -> None: self._left_speaker = self._left_speaker_amp = value - + _right_speaker = WOBit(_REG_PWR_MGMT_2, 3) _right_speaker_amp = WOBit(_REG_CLASS_D_CONTROL_1, 7) @property def right_speaker(self) -> bool: return self._right_speaker and self._right_speaker_amp + @right_speaker.setter - def right_speaker(self, value:bool) -> None: + def right_speaker(self, value: bool) -> None: self._right_speaker = self._right_speaker_amp = value @property def speaker(self) -> bool: return self.left_speaker and self.right_speaker + @speaker.setter - def speaker(self, value:bool) -> None: + def speaker(self, value: bool) -> None: self.left_speaker = self.right_speaker = value _left_speaker_volume = WOBits(7, _REG_LOUT2_VOLUME, 0) @@ -960,10 +1290,21 @@ def speaker(self, value:bool) -> None: @property def left_speaker_volume(self) -> float: - return map_range(max(self._left_speaker_volume, 48), 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + # fmt: off + return map_range( + self._left_speaker_volume, 48, 127, + AMP_VOLUME_MIN, AMP_VOLUME_MAX + ) + # fmt: on + @left_speaker_volume.setter - def left_speaker_volume(self, value:float) -> None: - self._left_speaker_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + def left_speaker_volume(self, value: float) -> None: + # fmt: off + self._left_speaker_volume = round(map_range( + value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, + 48, 127 + )) + # fmt: on self._left_speaker_set = True _right_speaker_volume = WOBits(7, _REG_ROUT2_VOLUME, 0) @@ -971,18 +1312,35 @@ def left_speaker_volume(self, value:float) -> None: @property def right_speaker_volume(self) -> float: - return map_range(max(self._right_speaker_volume, 48), 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + # fmt: off + return map_range( + self._right_speaker_volume, 48, 127, + AMP_VOLUME_MIN, AMP_VOLUME_MAX + ) + # fmt: on + @right_speaker_volume.setter - def right_speaker_volume(self, value:float) -> None: - self._right_speaker_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + def right_speaker_volume(self, value: float) -> None: + # fmt: off + self._right_speaker_volume = round(map_range( + value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, + 48, 127 + )) + # fmt: on self._right_speaker_volume_set = True @property def speaker_volume(self) -> int: return max(self.left_speaker_volume, self.right_speaker_volume) + @speaker_volume.setter - def speaker_volume(self, value:float) -> None: - self._left_speaker_volume = self._right_speaker_volume = round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + def speaker_volume(self, value: float) -> None: + # fmt: off + self._left_speaker_volume = self._right_speaker_volume = round(map_range( + value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, + 48, 127 + )) + # fmt: on self._left_speaker_volume_set = self._right_speaker_volume_set = True left_speaker_zero_cross = WOBit(_REG_LOUT2_VOLUME, 7) @@ -991,11 +1349,12 @@ def speaker_volume(self, value:float) -> None: @property def speaker_zero_cross(self) -> bool: return self.left_speaker_zero_cross and self.right_speaker_zero_cross + @speaker_zero_cross.setter - def speaker_zero_cross(self, value:bool) -> None: + def speaker_zero_cross(self, value: bool) -> None: self.left_speaker_zero_cross = self.right_speaker_zero_cross = value - def _get_speaker_boost_gain(self, value:float) -> int: + def _get_speaker_boost_gain(self, value: float) -> int: for i in reversed(range(len(_SPEAKER_BOOST_GAIN))): if value >= _SPEAKER_BOOST_GAIN[i]: return i @@ -1006,8 +1365,9 @@ def _get_speaker_boost_gain(self, value:float) -> int: @property def speaker_dc_gain(self) -> float: return _SPEAKER_BOOST_GAIN[self._speaker_dc_gain] + @speaker_dc_gain.setter - def speaker_dc_gain(self, value:float) -> None: + def speaker_dc_gain(self, value: float) -> None: self._speaker_dc_gain = self._get_speaker_boost_gain(value) _speaker_ac_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) @@ -1015,14 +1375,15 @@ def speaker_dc_gain(self, value:float) -> None: @property def speaker_ac_gain(self) -> float: return _SPEAKER_BOOST_GAIN[self._speaker_ac_gain] + @speaker_ac_gain.setter - def speaker_ac_gain(self, value:float) -> None: + def speaker_ac_gain(self, value: float) -> None: self._speaker_ac_gain = self._get_speaker_boost_gain(value) # Digital Audio Interface Control loopback = WOBit(_REG_AUDIO_INTERFACE_2, 0) - + pll = WOBit(_REG_PWR_MGMT_2, 0) pll_prescale_div2 = WOBit(_REG_PWR_MGMT_2, 4) pll_n = WOBits(4, _REG_PLL_N, 0) @@ -1034,8 +1395,9 @@ def speaker_ac_gain(self, value:float) -> None: @property def pll_k(self) -> int: return self._pll_k1 << 18 + self._pll_k2 << 9 + self._pll_k3 + @pll_k.setter - def pll_k(self, value:int) -> None: + def pll_k(self, value: int) -> None: self._pll_k1 = (value >> 18) & 0b111111 self._pll_k2 = (value >> 9) & 0b111111111 self._pll_k3 = value & 0b111111111 @@ -1045,21 +1407,23 @@ def pll_k(self, value:int) -> None: clock_from_pLL = WOBit(_REG_CLOCKING_1, 0) _system_clock_divider = WOBits(2, _REG_CLOCKING_1, 1) - + @property def system_clock_div2(self) -> bool: return self._system_clock_divider == _SYSCLK_DIV_BY_2 + @system_clock_div2.setter - def system_clock_div2(self, value:bool) -> None: + def system_clock_div2(self, value: bool) -> None: self._system_clock_divider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 - + _adc_clock_divider = WOBits(3, _REG_CLOCKING_1, 6) @property def adc_clock_divider(self) -> int: return _ADCDACDIV[min(self._adc_clock_divider, len(_ADCDACDIV))] + @adc_clock_divider.setter - def adc_clock_divider(self, value:int) -> None: + def adc_clock_divider(self, value: int) -> None: value = round(value * 2.0) / 2.0 if value in _ADCDACDIV: self._adc_clock_divider = _ADCDACDIV.index(value) @@ -1069,8 +1433,9 @@ def adc_clock_divider(self, value:int) -> None: @property def dac_clock_divider(self) -> int: return _ADCDACDIV[min(self._dac_clock_divider, len(_ADCDACDIV))] + @dac_clock_divider.setter - def dac_clock_divider(self, value:int) -> None: + def dac_clock_divider(self, value: int) -> None: value = round(value * 2.0) / 2.0 if value in _ADCDACDIV: self._dac_clock_divider = _ADCDACDIV.index(value) @@ -1080,20 +1445,21 @@ def dac_clock_divider(self, value:int) -> None: @property def base_clock_divider(self) -> float: return _BCLKDIV[min(self._base_clock_divider, len(_BCLKDIV))] + @base_clock_divider.setter - def base_clock_divider(self, value:float) -> None: + def base_clock_divider(self, value: float) -> None: value = round(value * 2.0) / 2.0 if value in _BCLKDIV: self._base_clock_divider = _BCLKDIV.index(value) - _amp_clock_divider = WOBits(3, _REG_CLOCKING_2, 6) @property def amp_clock_divider(self) -> float: return _DCLKDIV[min(self._amp_clock_divider, len(_DCLKDIV))] + @amp_clock_divider.setter - def amp_clock_divider(self, value:float) -> None: + def amp_clock_divider(self, value: float) -> None: value = round(value * 2.0) / 2.0 if value in _DCLKDIV: self._amp_clock_divider = _DCLKDIV.index(value) @@ -1111,8 +1477,9 @@ def bit_depth(self) -> int: return 32 else: return 16 + 4 * value + @bit_depth.setter - def bit_depth(self, value:int) -> None: + def bit_depth(self, value: int) -> None: self._bit_depth = (min(value, 28) - 16) // 4 word_select_invert = WOBit(_REG_AUDIO_INTERFACE_1, 4) @@ -1124,8 +1491,9 @@ def bit_depth(self, value:int) -> None: @property def vref_output(self) -> bool: return not self._vref_output_disable + @vref_output.setter - def vref_output(self, value:bool) -> None: + def vref_output(self, value: bool) -> None: self._vref_output_disable = not value _vsel = WOBits(2, _REG_ADDITIONAL_CONTROL_1, 6) @@ -1133,8 +1501,9 @@ def vref_output(self, value:bool) -> None: @property def power_supply(self) -> float: return (constrain(self._vsel, 1, 2) - 1) * 0.6 + 2.7 + @power_supply.setter - def power_supply(self, value:float) -> None: + def power_supply(self, value: float) -> None: self._vsel = (constrain(value, 2.7, 3.3) - 2.7) // 0.6 * 2 + 1 ## GPIO @@ -1142,23 +1511,25 @@ def power_supply(self, value:float) -> None: gpio_output = WOBit(_REG_AUDIO_INTERFACE_2, 6) gpio_output_mode = WOBits(3, _REG_ADDITIONAL_CONTROL_4, 4) gpio_output_invert = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) - + _gpio_clock_divider = WOBits(3, _REG_CLOCKING_2, 6) @property def gpio_clock_divider(self) -> int: return self._gpio_clock_divider + @gpio_clock_divider.setter - def gpio_clock_divider(self, value:int) -> None: + def gpio_clock_divider(self, value: int) -> None: self._gpio_clock_divider = min(value, 5) - + @property def sample_rate(self) -> int: return self._sample_rate + @sample_rate.setter - def sample_rate(self, value:int) -> None: + def sample_rate(self, value: int) -> None: # MCLK = 24 MHz - self.pll = True # Needed for class-d amp clock + self.pll = True # Needed for class-d amp clock self.clock_fractional_mode = True self.clock_from_pLL = True @@ -1171,85 +1542,85 @@ def sample_rate(self, value:int) -> None: # SYSCLK = 12.288 MHz # DCLK = 768.0k_hz self.pll_n = 8 - self.pll_k = 0x3126_e8 + self.pll_k = 0x3126_E8 self.adc_clock_divider = self.dac_clock_divider = 48000 / value - + elif value in [11025, 22050, 44100]: # SYSCLK = 11.2896 MHz # DCLK = 705.6k_hz self.pll_n = 7 - self.pll_k = 0x86_c226 + self.pll_k = 0x86_C226 self.adc_clock_divider = self.dac_clock_divider = 44100 / value - + else: raise Exception("Invalid sample rate") self._sample_rate = value - - def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR) -> None: + + def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_I2C_ADDR) -> None: self.i2c_device = I2CDevice(i2c_bus, address) self._sample_rate = None self._registers = [ - 0x0097, # R0 (0x00) - 0x0097, # R1 (0x01) - 0x0000, # R2 (0x02) - 0x0000, # R3 (0x03) - 0x0000, # R4 (0x04) - 0x0008, # F5 (0x05) - 0x0000, # R6 (0x06) - 0x000A, # R7 (0x07) - 0x01C0, # R8 (0x08) - 0x0000, # R9 (0x09) - 0x00FF, # R10 (0x0a) - 0x00FF, # R11 (0x0b) - 0x0000, # R12 (0x0C) RESERVED - 0x0000, # R13 (0x0D) RESERVED - 0x0000, # R14 (0x0E) RESERVED - 0x0000, # R15 (0x0F) RESERVED - 0x0000, # R16 (0x10) - 0x007B, # R17 (0x11) - 0x0100, # R18 (0x12) - 0x0032, # R19 (0x13) - 0x0000, # R20 (0x14) - 0x00C3, # R21 (0x15) - 0x00C3, # R22 (0x16) - 0x01C0, # R23 (0x17) - 0x0000, # R24 (0x18) - 0x0000, # R25 (0x19) - 0x0000, # R26 (0x1A) - 0x0000, # R27 (0x1B) - 0x0000, # R28 (0x1C) - 0x0000, # R29 (0x1D) - 0x0000, # R30 (0x1E) RESERVED - 0x0000, # R31 (0x1F) RESERVED - 0x0100, # R32 (0x20) - 0x0100, # R33 (0x21) - 0x0050, # R34 (0x22) - 0x0000, # R35 (0x23) RESERVED - 0x0000, # R36 (0x24) RESERVED - 0x0050, # R37 (0x25) - 0x0000, # R38 (0x26) - 0x0000, # R39 (0x27) - 0x0000, # R40 (0x28) - 0x0000, # R41 (0x29) - 0x0040, # R42 (0x2A) - 0x0000, # R43 (0x2B) - 0x0000, # R44 (0x2C) - 0x0050, # R45 (0x2D) - 0x0050, # R46 (0x2E) - 0x0000, # R47 (0x2F) - 0x0002, # R48 (0x30) - 0x0037, # R49 (0x31) - 0x0000, # R50 (0x32) RESERVED - 0x0080, # R51 (0x33) - 0x0008, # R52 (0x34) - 0x0031, # R53 (0x35) - 0x0026, # R54 (0x36) - 0x00e9, # R55 (0x37) + 0x0097, # R0 (0x00) + 0x0097, # R1 (0x01) + 0x0000, # R2 (0x02) + 0x0000, # R3 (0x03) + 0x0000, # R4 (0x04) + 0x0008, # F5 (0x05) + 0x0000, # R6 (0x06) + 0x000A, # R7 (0x07) + 0x01C0, # R8 (0x08) + 0x0000, # R9 (0x09) + 0x00FF, # R10 (0x0a) + 0x00FF, # R11 (0x0b) + 0x0000, # R12 (0x0C) RESERVED + 0x0000, # R13 (0x0D) RESERVED + 0x0000, # R14 (0x0E) RESERVED + 0x0000, # R15 (0x0F) RESERVED + 0x0000, # R16 (0x10) + 0x007B, # R17 (0x11) + 0x0100, # R18 (0x12) + 0x0032, # R19 (0x13) + 0x0000, # R20 (0x14) + 0x00C3, # R21 (0x15) + 0x00C3, # R22 (0x16) + 0x01C0, # R23 (0x17) + 0x0000, # R24 (0x18) + 0x0000, # R25 (0x19) + 0x0000, # R26 (0x1A) + 0x0000, # R27 (0x1B) + 0x0000, # R28 (0x1C) + 0x0000, # R29 (0x1D) + 0x0000, # R30 (0x1E) RESERVED + 0x0000, # R31 (0x1F) RESERVED + 0x0100, # R32 (0x20) + 0x0100, # R33 (0x21) + 0x0050, # R34 (0x22) + 0x0000, # R35 (0x23) RESERVED + 0x0000, # R36 (0x24) RESERVED + 0x0050, # R37 (0x25) + 0x0000, # R38 (0x26) + 0x0000, # R39 (0x27) + 0x0000, # R40 (0x28) + 0x0000, # R41 (0x29) + 0x0040, # R42 (0x2A) + 0x0000, # R43 (0x2B) + 0x0000, # R44 (0x2C) + 0x0050, # R45 (0x2D) + 0x0050, # R46 (0x2E) + 0x0000, # R47 (0x2F) + 0x0002, # R48 (0x30) + 0x0037, # R49 (0x31) + 0x0000, # R50 (0x32) RESERVED + 0x0080, # R51 (0x33) + 0x0008, # R52 (0x34) + 0x0031, # R53 (0x35) + 0x0026, # R54 (0x36) + 0x00E9, # R55 (0x37) ] for i in range(len(self._registers)): - self._registers[i] = bytearray(self._registers[i].to_bytes(2, 'big')) + self._registers[i] = bytearray(self._registers[i].to_bytes(2, "big")) self._registers[i][0] |= i << 1 self.reset() @@ -1260,8 +1631,11 @@ def __init__(self, i2c_bus:I2C, address:int = _DEFAULT_I2C_ADDR) -> None: # Resets all registers to their default state _reset = WOBit(_REG_RESET, 7) + def reset(self) -> None: self._reset = True for name in dir(self): - if not name.startswith('_') and isinstance(getattr(self, name), (WOBit, WOBits)): + if not name.startswith("_") and isinstance( + getattr(self, name), (WOBit, WOBits) + ): getattr(self, name).reset(self) diff --git a/docs/conf.py b/docs/conf.py index dd30270..2cfe3a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,8 +33,8 @@ intersphinx_mapping = { - "python": ("https://docs.python.org/3", None),"BusDevice": ("https://docs.circuitpython.org/projects/busdevice/en/latest/", None), - + "python": ("https://docs.python.org/3", None), + "BusDevice": ("https://docs.circuitpython.org/projects/busdevice/en/latest/", None), "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None), } diff --git a/examples/wm8960_3d_enhance.py b/examples/wm8960_3d_enhance.py index 6e95b5e..4ac4c5b 100644 --- a/examples/wm8960_3d_enhance.py +++ b/examples/wm8960_3d_enhance.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: MIT -''' +""" Demonstrates analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone output on the WM8960 Codec. Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. @@ -43,7 +43,7 @@ For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' +""" import board, time import adafruit_wm8960 @@ -80,7 +80,7 @@ # Setup sample rate codec.sample_rate = 44100 codec.master_mode = True -codec.gpio_output = True # Note, should not be changed while ADC is enabled. +codec.gpio_output = True # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs codec.adc = codec.dac = True diff --git a/examples/wm8960_automatic_level_control.py b/examples/wm8960_automatic_level_control.py index 6797898..e593131 100644 --- a/examples/wm8960_automatic_level_control.py +++ b/examples/wm8960_automatic_level_control.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: MIT -''' +""" Demonstrates how to use the automatic level control feature of the WM8960 Codec. Attach a potentiomenter to GND/A0/3V3 to actively adjust the ALC target setting. @@ -52,7 +52,7 @@ For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' +""" import board, time from analogio import AnalogIn @@ -75,7 +75,7 @@ # Set input boosts to get inputs 1 to the boost mixers codec.mic_boost_gain = adafruit_wm8960.MIC_BOOST_GAIN_0DB codec.mic_boost = True -codec.input = True # Enable boost mixers +codec.input = True # Enable boost mixers # Disconnect LB2LO (booster to output mixer (analog bypass) # For this example, we are going to pass audio throught the ADC and DAC @@ -94,7 +94,7 @@ # Setup clock and mode codec.sample_rate = 44100 codec.master_mode = True -codec.gpio_output = True # Note, should not be changed while ADC is enabled. +codec.gpio_output = True # Note, should not be changed while ADC is enabled. # Enable ADCs and DACs codec.adc = codec.dac = True @@ -108,7 +108,9 @@ # Enable headphone amp output codec.headphone = True codec.headphone_volume = 0.0 -codec.mono_output = True # Enables capless mode using the VMID as buffer for headphone ground on OUT3 + +# Enables capless mode using the VMID as buffer for headphone ground on OUT3 +codec.mono_output = True # Automatic Level control configuration @@ -125,5 +127,11 @@ codec.alc_min_gain = adafruit_wm8960.ALC_MIN_GAIN_MIN while True: - codec.alc_target = map_range(analog_in.value, 0, 65536, adafruit_wm8960.ALC_TARGET_MIN, adafruit_wm8960.ALC_TARGET_MAX) + codec.alc_target = map_range( + analog_in.value, + 0, + 65536, + adafruit_wm8960.ALC_TARGET_MIN, + adafruit_wm8960.ALC_TARGET_MAX, + ) time.sleep(1.0) diff --git a/examples/wm8960_input.py b/examples/wm8960_input.py index efa239e..6b99fed 100644 --- a/examples/wm8960_input.py +++ b/examples/wm8960_input.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: MIT -''' +""" Demonstrates analog audio input (on INPUT2s), sets volume control, and headphone output on the WM8960 Codec. Audio should be connected to both the left and right "INPUT2" inputs, they are labeled "RIN2" and "LIN2" on the board. @@ -44,7 +44,7 @@ For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' +""" import board import adafruit_wm8960 @@ -56,15 +56,15 @@ # INPUT1 must pass through the Mic Boost codec.mic = True codec.mic_inverting_input = True -codec.mic_input = adafruit_wm8960.MIC_VMID # Non-inverting input +codec.mic_input = adafruit_wm8960.MIC_VMID # Non-inverting input codec.mic_mute = False codec.mic_boost_gain = 0.0 codec.mic_boost = True codec.mic_volume = 0.0 # In order to use INPUT2 or INPUT3 with the Mic Boost (PGA) -#codec.mic_input = adafruit_wm8960.MIC_INPUT2 -#codec.mic_input = adafruit_wm8960.MIC_INPUT3 +# codec.mic_input = adafruit_wm8960.MIC_INPUT2 +# codec.mic_input = adafruit_wm8960.MIC_INPUT3 # If codec.mic_inverting_input is enabled, mic boost uses input as balanced input between INPUT1 & INPUT2/3 # Set input boosts to get INPUT2 or INPUT3 (both left and right) to the boost mixers and bypass the Mic Boost (PGA) diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index 91dd6f0..be57403 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -4,7 +4,7 @@ # # SPDX-License-Identifier: Unlicense -''' +""" Demonstrates I2C Output on WM8960 Codec by generating a simple tone using synthio. Sounds like an alarm clock. It will output the sound on the headphone outputs. @@ -35,7 +35,7 @@ For information on the data sent to and received from the CODEC, refer to the WM8960 datasheet at: https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf -''' +""" import audiobusio import board @@ -77,10 +77,10 @@ while True: print("note on") - synth.press(65) # midi note 65 = F4 + synth.press(65) # midi note 65 = F4 led.value = True time.sleep(0.5) - synth.release(65) # release the note we pressed + synth.release(65) # release the note we pressed led.value = False print("note off") time.sleep(0.5) From 776e3bc521ef46a79d1d1d196a84163425d09ca0 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 23 Aug 2024 10:36:22 -0500 Subject: [PATCH 45/56] Local register list reset bug fix. --- adafruit_wm8960.py | 144 +++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 77 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 7eef3df..9172880 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -166,6 +166,66 @@ _DCLKDIV = [1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0] _ADCDACDIV = [1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0] +# Default Register Map +_REG_DEFAULTS = [ + const(0x0097), # R0 (0x00) + const(0x0097), # R1 (0x01) + const(0x0000), # R2 (0x02) + const(0x0000), # R3 (0x03) + const(0x0000), # R4 (0x04) + const(0x0008), # F5 (0x05) + const(0x0000), # R6 (0x06) + const(0x000A), # R7 (0x07) + const(0x01C0), # R8 (0x08) + const(0x0000), # R9 (0x09) + const(0x00FF), # R10 (0x0a) + const(0x00FF), # R11 (0x0b) + const(0x0000), # R12 (0x0C) RESERVED + const(0x0000), # R13 (0x0D) RESERVED + const(0x0000), # R14 (0x0E) RESERVED + const(0x0000), # R15 (0x0F) RESERVED + const(0x0000), # R16 (0x10) + const(0x007B), # R17 (0x11) + const(0x0100), # R18 (0x12) + const(0x0032), # R19 (0x13) + const(0x0000), # R20 (0x14) + const(0x00C3), # R21 (0x15) + const(0x00C3), # R22 (0x16) + const(0x01C0), # R23 (0x17) + const(0x0000), # R24 (0x18) + const(0x0000), # R25 (0x19) + const(0x0000), # R26 (0x1A) + const(0x0000), # R27 (0x1B) + const(0x0000), # R28 (0x1C) + const(0x0000), # R29 (0x1D) + const(0x0000), # R30 (0x1E) RESERVED + const(0x0000), # R31 (0x1F) RESERVED + const(0x0100), # R32 (0x20) + const(0x0100), # R33 (0x21) + const(0x0050), # R34 (0x22) + const(0x0000), # R35 (0x23) RESERVED + const(0x0000), # R36 (0x24) RESERVED + const(0x0050), # R37 (0x25) + const(0x0000), # R38 (0x26) + const(0x0000), # R39 (0x27) + const(0x0000), # R40 (0x28) + const(0x0000), # R41 (0x29) + const(0x0040), # R42 (0x2A) + const(0x0000), # R43 (0x2B) + const(0x0000), # R44 (0x2C) + const(0x0050), # R45 (0x2D) + const(0x0050), # R46 (0x2E) + const(0x0000), # R47 (0x2F) + const(0x0002), # R48 (0x30) + const(0x0037), # R49 (0x31) + const(0x0000), # R50 (0x32) RESERVED + const(0x0080), # R51 (0x33) + const(0x0008), # R52 (0x34) + const(0x0031), # R53 (0x35) + const(0x0026), # R54 (0x36) + const(0x00E9), # R55 (0x37) +] + class WOBit: @@ -185,11 +245,6 @@ def _set(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: else: obj._registers[self.register_address][self.byte] &= ~self.bit_mask - def reset(self, obj: Optional[I2CDeviceDriver]) -> None: - if not hasattr(self, "default"): - self.default = self.__get__(obj) - self._set(obj, self.default) - def __get__( self, obj: Optional[I2CDeviceDriver], @@ -227,12 +282,6 @@ def _set(self, obj: Optional[I2CDeviceDriver], value: int) -> None: obj._registers[self.register_address][i] = reg & 0xFF reg >>= 8 - # Must call first before using object - def reset(self, obj: Optional[I2CDeviceDriver]) -> None: - if not hasattr(self, "default"): - self.default = self.__get__(obj) - self._set(obj, self.default) - def __get__( self, obj: Optional[I2CDeviceDriver], @@ -1561,67 +1610,11 @@ def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_I2C_ADDR) -> None: self.i2c_device = I2CDevice(i2c_bus, address) self._sample_rate = None - self._registers = [ - 0x0097, # R0 (0x00) - 0x0097, # R1 (0x01) - 0x0000, # R2 (0x02) - 0x0000, # R3 (0x03) - 0x0000, # R4 (0x04) - 0x0008, # F5 (0x05) - 0x0000, # R6 (0x06) - 0x000A, # R7 (0x07) - 0x01C0, # R8 (0x08) - 0x0000, # R9 (0x09) - 0x00FF, # R10 (0x0a) - 0x00FF, # R11 (0x0b) - 0x0000, # R12 (0x0C) RESERVED - 0x0000, # R13 (0x0D) RESERVED - 0x0000, # R14 (0x0E) RESERVED - 0x0000, # R15 (0x0F) RESERVED - 0x0000, # R16 (0x10) - 0x007B, # R17 (0x11) - 0x0100, # R18 (0x12) - 0x0032, # R19 (0x13) - 0x0000, # R20 (0x14) - 0x00C3, # R21 (0x15) - 0x00C3, # R22 (0x16) - 0x01C0, # R23 (0x17) - 0x0000, # R24 (0x18) - 0x0000, # R25 (0x19) - 0x0000, # R26 (0x1A) - 0x0000, # R27 (0x1B) - 0x0000, # R28 (0x1C) - 0x0000, # R29 (0x1D) - 0x0000, # R30 (0x1E) RESERVED - 0x0000, # R31 (0x1F) RESERVED - 0x0100, # R32 (0x20) - 0x0100, # R33 (0x21) - 0x0050, # R34 (0x22) - 0x0000, # R35 (0x23) RESERVED - 0x0000, # R36 (0x24) RESERVED - 0x0050, # R37 (0x25) - 0x0000, # R38 (0x26) - 0x0000, # R39 (0x27) - 0x0000, # R40 (0x28) - 0x0000, # R41 (0x29) - 0x0040, # R42 (0x2A) - 0x0000, # R43 (0x2B) - 0x0000, # R44 (0x2C) - 0x0050, # R45 (0x2D) - 0x0050, # R46 (0x2E) - 0x0000, # R47 (0x2F) - 0x0002, # R48 (0x30) - 0x0037, # R49 (0x31) - 0x0000, # R50 (0x32) RESERVED - 0x0080, # R51 (0x33) - 0x0008, # R52 (0x34) - 0x0031, # R53 (0x35) - 0x0026, # R54 (0x36) - 0x00E9, # R55 (0x37) - ] - for i in range(len(self._registers)): - self._registers[i] = bytearray(self._registers[i].to_bytes(2, "big")) - self._registers[i][0] |= i << 1 + self._registers = _REG_DEFAULTS[:] + for i, reg in enumerate(self._registers): + reg = bytearray(reg.to_bytes(2, "big")) + reg[0] |= i << 1 + self._registers[i] = reg self.reset() @@ -1634,8 +1627,5 @@ def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_I2C_ADDR) -> None: def reset(self) -> None: self._reset = True - for name in dir(self): - if not name.startswith("_") and isinstance( - getattr(self, name), (WOBit, WOBits) - ): - getattr(self, name).reset(self) + for i, reg in enumerate(self._registers): + reg[:] = _REG_DEFAULTS[i].to_bytes(2, "big") From bc58f1aef9441f52fc26e140d79060ae7982a35e Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 23 Aug 2024 11:03:42 -0500 Subject: [PATCH 46/56] Ensure that registers contain address index in bytearray before and after reset. --- adafruit_wm8960.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index 9172880..ce0a30c 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -1610,11 +1610,12 @@ def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_I2C_ADDR) -> None: self.i2c_device = I2CDevice(i2c_bus, address) self._sample_rate = None - self._registers = _REG_DEFAULTS[:] + self._registers = [0] * len(_REG_DEFAULTS) for i, reg in enumerate(self._registers): - reg = bytearray(reg.to_bytes(2, "big")) - reg[0] |= i << 1 - self._registers[i] = reg + self._registers[i] = bytearray(reg.to_bytes(2, "big")) + + # Must be called before `reset` to ensure that _REG_RESET is addressed properly + self._reset_registers() self.reset() @@ -1625,7 +1626,11 @@ def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_I2C_ADDR) -> None: # Resets all registers to their default state _reset = WOBit(_REG_RESET, 7) - def reset(self) -> None: - self._reset = True + def _reset_registers(self) -> None: for i, reg in enumerate(self._registers): reg[:] = _REG_DEFAULTS[i].to_bytes(2, "big") + reg[0] |= i << 1 + + def reset(self) -> None: + self._reset = True + self._reset_registers() From 5da5380937aa32ae72b779114926e77a199c1d80 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 23 Aug 2024 11:04:20 -0500 Subject: [PATCH 47/56] Conform to pylint standards (excluding `missing-function-docstring` and `missing-class-docstring`). --- adafruit_wm8960.py | 70 ++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py index ce0a30c..7ec113e 100644 --- a/adafruit_wm8960.py +++ b/adafruit_wm8960.py @@ -24,18 +24,19 @@ # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ +# pylint: disable=too-many-lines __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960.git" +import math from busio import I2C from adafruit_bus_device.i2c_device import I2CDevice from adafruit_simplemath import constrain, map_range -import math from micropython import const try: - from typing import Optional, Type, Protocol + from typing import Optional, Type from circuitpython_typing.device_drivers import I2CDeviceDriver except ImportError: pass @@ -227,6 +228,9 @@ ] +# pylint: disable=protected-access + + class WOBit: def __init__( @@ -300,7 +304,10 @@ def __set__(self, obj: Optional[I2CDeviceDriver], value: int) -> None: i2c.write(obj._registers[self.register_address]) -class WM8960: +# pylint: enable=protected-access + + +class WM8960: # pylint: disable=too-many-instance-attributes,too-many-public-methods # Power @@ -352,15 +359,14 @@ def mic_inverting_input(self, value: bool) -> None: def left_mic_input(self) -> int: if self._left_mic_input2: return MIC_INPUT2 - elif self._left_mic_input3: + if self._left_mic_input3: return MIC_INPUT3 - else: - return MIC_VMID + return MIC_VMID @left_mic_input.setter - def left_mic_input(self, input: int) -> None: - self._left_mic_input2 = input == MIC_INPUT2 - self._left_mic_input3 = input == MIC_INPUT3 + def left_mic_input(self, value: int) -> None: + self._left_mic_input2 = value == MIC_INPUT2 + self._left_mic_input3 = value == MIC_INPUT3 _right_mic_input2 = WOBit(_REG_ADCR_SIGNAL_PATH, 6) _right_mic_input3 = WOBit(_REG_ADCR_SIGNAL_PATH, 7) @@ -369,15 +375,14 @@ def left_mic_input(self, input: int) -> None: def right_mic_input(self) -> int: if self._right_mic_input2: return MIC_INPUT2 - elif self._right_mic_input3: + if self._right_mic_input3: return MIC_INPUT3 - else: - return MIC_VMID + return MIC_VMID @right_mic_input.setter - def right_mic_input(self, input: int) -> None: - self._right_mic_input2 = input == MIC_INPUT2 - self._right_mic_input3 = input == MIC_INPUT3 + def right_mic_input(self, value: int) -> None: + self._right_mic_input2 = value == MIC_INPUT2 + self._right_mic_input3 = value == MIC_INPUT3 @property def mic_input(self) -> int: @@ -385,8 +390,8 @@ def mic_input(self) -> int: return self.left_mic_input @mic_input.setter - def mic_input(self, input: int) -> None: - self.left_mic_input = self.right_mic_input = input + def mic_input(self, value: int) -> None: + self.left_mic_input = self.right_mic_input = value ## Boost @@ -401,7 +406,8 @@ def mic_boost(self) -> bool: def mic_boost(self, value: bool) -> None: self.left_mic_boost = self.right_mic_boost = value - def _get_mic_boost_gain(self, value: float) -> int: + @staticmethod + def _get_mic_boost_gain(value: float) -> int: for i in reversed(range(len(_MIC_BOOST_GAIN))): if value >= _MIC_BOOST_GAIN[i]: return i @@ -415,7 +421,7 @@ def left_mic_boost_gain(self) -> float: @left_mic_boost_gain.setter def left_mic_boost_gain(self, value: float) -> None: - self._left_mic_boost_gain = self._get_mic_boost_gain(value) + self._left_mic_boost_gain = WM8960._get_mic_boost_gain(value) _right_mic_boost_gain = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) @@ -425,7 +431,7 @@ def right_mic_boost_gain(self) -> float: @right_mic_boost_gain.setter def right_mic_boost_gain(self, value: float) -> None: - self._right_mic_boost_gain = self._get_mic_boost_gain(value) + self._right_mic_boost_gain = WM8960._get_mic_boost_gain(value) @property def mic_boost_gain(self) -> float: @@ -434,7 +440,7 @@ def mic_boost_gain(self) -> float: @mic_boost_gain.setter def mic_boost_gain(self, value: float) -> None: self._left_mic_boost_gain = self._right_mic_boost_gain = ( - self._get_mic_boost_gain(value) + WM8960._get_mic_boost_gain(value) ) ## Volume @@ -603,7 +609,7 @@ def input2_boost(self) -> float: # fmt: off return ( None - if value is 0 + if value == 0 else map_range( value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX @@ -684,7 +690,7 @@ def input3_boost(self) -> float: # fmt: off return ( None - if value is 0 + if value == 0 else map_range( value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX @@ -1403,7 +1409,8 @@ def speaker_zero_cross(self) -> bool: def speaker_zero_cross(self, value: bool) -> None: self.left_speaker_zero_cross = self.right_speaker_zero_cross = value - def _get_speaker_boost_gain(self, value: float) -> int: + @staticmethod + def _get_speaker_boost_gain(value: float) -> int: for i in reversed(range(len(_SPEAKER_BOOST_GAIN))): if value >= _SPEAKER_BOOST_GAIN[i]: return i @@ -1417,7 +1424,7 @@ def speaker_dc_gain(self) -> float: @speaker_dc_gain.setter def speaker_dc_gain(self, value: float) -> None: - self._speaker_dc_gain = self._get_speaker_boost_gain(value) + self._speaker_dc_gain = WM8960._get_speaker_boost_gain(value) _speaker_ac_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) @@ -1427,7 +1434,7 @@ def speaker_ac_gain(self) -> float: @speaker_ac_gain.setter def speaker_ac_gain(self, value: float) -> None: - self._speaker_ac_gain = self._get_speaker_boost_gain(value) + self._speaker_ac_gain = WM8960._get_speaker_boost_gain(value) # Digital Audio Interface Control @@ -1453,7 +1460,7 @@ def pll_k(self, value: int) -> None: clock_fractional_mode = WOBit(_REG_PLL_N, 5) - clock_from_pLL = WOBit(_REG_CLOCKING_1, 0) + clock_from_pll = WOBit(_REG_CLOCKING_1, 0) _system_clock_divider = WOBits(2, _REG_CLOCKING_1, 1) @@ -1522,10 +1529,7 @@ def amp_clock_divider(self, value: float) -> None: @property def bit_depth(self) -> int: value = self._bit_depth - if value == 3: - return 32 - else: - return 16 + 4 * value + return 32 if value == 3 else 16 + 4 * value @bit_depth.setter def bit_depth(self, value: int) -> None: @@ -1580,7 +1584,7 @@ def sample_rate(self, value: int) -> None: # MCLK = 24 MHz self.pll = True # Needed for class-d amp clock self.clock_fractional_mode = True - self.clock_from_pLL = True + self.clock_from_pll = True self.pll_prescale_div2 = True self.system_clock_div2 = True @@ -1602,7 +1606,7 @@ def sample_rate(self, value: int) -> None: self.adc_clock_divider = self.dac_clock_divider = 44100 / value else: - raise Exception("Invalid sample rate") + raise ValueError("Invalid sample rate") self._sample_rate = value From 6299b2b475f0e933c948958b39fa223c6ef053e8 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Mon, 2 Sep 2024 08:53:23 -0500 Subject: [PATCH 48/56] Rearranged library structure to introduce basic driver class, `adafruit_wm8960.WM8960`, and advanced driver class, `adafruit_wm8960.advanced.WM8960_Advanced`. Documentation completed. --- adafruit_wm8960.py | 1640 ----------------------- adafruit_wm8960/__init__.py | 415 ++++++ adafruit_wm8960/advanced.py | 2384 +++++++++++++++++++++++++++++++++ docs/api-advanced.rst | 3 + docs/api-advanced.rst.license | 5 + docs/index.rst | 6 + 6 files changed, 2813 insertions(+), 1640 deletions(-) delete mode 100644 adafruit_wm8960.py create mode 100644 adafruit_wm8960/__init__.py create mode 100644 adafruit_wm8960/advanced.py create mode 100644 docs/api-advanced.rst create mode 100644 docs/api-advanced.rst.license diff --git a/adafruit_wm8960.py b/adafruit_wm8960.py deleted file mode 100644 index 7ec113e..0000000 --- a/adafruit_wm8960.py +++ /dev/null @@ -1,1640 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics -# SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries -# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple -# -# SPDX-License-Identifier: MIT -""" -`adafruit_wm8960` -================================================================================ - -CircuitPython driver for WM8960 Stereo CODEC - -* Author(s): Scott Shawcroft, Cooper Dalrymple - -Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 -https://github.com/sparkfun/SparkFun_Arduino_Library - -Implementation Notes --------------------- - -**Software and Dependencies:** - -* Adafruit CircuitPython firmware for the supported boards: - https://circuitpython.org/downloads - -# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice -""" -# pylint: disable=too-many-lines - -__version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960.git" - -import math -from busio import I2C -from adafruit_bus_device.i2c_device import I2CDevice -from adafruit_simplemath import constrain, map_range -from micropython import const - -try: - from typing import Optional, Type - from circuitpython_typing.device_drivers import I2CDeviceDriver -except ImportError: - pass - -# I2C address -_DEFAULT_I2C_ADDR = const(0x1A) - -# WM8960 register addresses -_REG_LEFT_INPUT_VOLUME = const(0x00) -_REG_RIGHT_INPUT_VOLUME = const(0x01) -_REG_LOUT1_VOLUME = const(0x02) -_REG_ROUT1_VOLUME = const(0x03) -_REG_CLOCKING_1 = const(0x04) -_REG_ADC_DAC_CTRL_1 = const(0x05) -_REG_ADC_DAC_CTRL_2 = const(0x06) -_REG_AUDIO_INTERFACE_1 = const(0x07) -_REG_CLOCKING_2 = const(0x08) -_REG_AUDIO_INTERFACE_2 = const(0x09) -_REG_LEFT_DAC_VOLUME = const(0x0A) -_REG_RIGHT_DAC_VOLUME = const(0x0B) -_REG_RESET = const(0x0F) -_REG_3D_CONTROL = const(0x10) -_REG_ALC1 = const(0x11) -_REG_ALC2 = const(0x12) -_REG_ALC3 = const(0x13) -_REG_NOISE_GATE = const(0x14) -_REG_LEFT_ADC_VOLUME = const(0x15) -_REG_RIGHT_ADC_VOLUME = const(0x16) -_REG_ADDITIONAL_CONTROL_1 = const(0x17) -_REG_ADDITIONAL_CONTROL_2 = const(0x18) -_REG_PWR_MGMT_1 = const(0x19) -_REG_PWR_MGMT_2 = const(0x1A) -_REG_ADDITIONAL_CONTROL_3 = const(0x1B) -_REG_ANTI_POP_1 = const(0x1C) -_REG_ANTI_POP_2 = const(0x1D) -_REG_ADCL_SIGNAL_PATH = const(0x20) -_REG_ADCR_SIGNAL_PATH = const(0x21) -_REG_LEFT_OUT_MIX = const(0x22) -_REG_RIGHT_OUT_MIX = const(0x25) -_REG_MONO_OUT_MIX_1 = const(0x26) -_REG_MONO_OUT_MIX_2 = const(0x27) -_REG_LOUT2_VOLUME = const(0x28) -_REG_ROUT2_VOLUME = const(0x29) -_REG_MONO_OUT_VOLUME = const(0x2A) -_REG_INPUT_BOOST_MIXER_1 = const(0x2B) -_REG_INPUT_BOOST_MIXER_2 = const(0x2C) -_REG_BYPASS_1 = const(0x2D) -_REG_BYPASS_2 = const(0x2E) -_REG_PWR_MGMT_3 = const(0x2F) -_REG_ADDITIONAL_CONTROL_4 = const(0x30) -_REG_CLASS_D_CONTROL_1 = const(0x31) -_REG_CLASS_D_CONTROL_3 = const(0x33) -_REG_PLL_N = const(0x34) -_REG_PLL_K_1 = const(0x35) -_REG_PLL_K_2 = const(0x36) -_REG_PLL_K_3 = const(0x37) - -# Microphone input selections -MIC_INPUT2 = 0 -MIC_INPUT3 = 1 -MIC_VMID = 2 - -# Microphone Boost gain options -_MIC_BOOST_GAIN = [0.0, 13.0, 20.0, 29.0] # in dB - -# Mic Bias voltage options -MIC_BIAS_VOLTAGE_0_9_AVDD = 0 -MIC_BIAS_VOLTAGE_0_65_AVDD = 1 - -# SYSCLK divide -_SYSCLK_DIV_BY_1 = const(0) -_SYSCLK_DIV_BY_2 = const(2) - -# Gain/Level min/max -BOOST_GAIN_MIN = -12.00 -BOOST_GAIN_MAX = 6.0 - -MIC_GAIN_MIN = -17.25 -MIC_GAIN_MAX = 30.00 - -ADC_VOLUME_MIN = -97.00 -ADC_VOLUME_MAX = 30.00 - -DAC_VOLUME_MIN = -127.00 -DAC_VOLUME_MAX = 0.00 - -ALC_TARGET_MIN = -22.50 -ALC_TARGET_MAX = -1.50 - -ALC_MAX_GAIN_MIN = -12.00 -ALC_MAX_GAIN_MAX = 30.00 - -ALC_MIN_GAIN_MIN = -17.25 -ALC_MIN_GAIN_MAX = 24.75 - -GATE_THRESHOLD_MIN = -76.50 -GATE_THRESHOLD_MAX = -30.00 - -OUTPUT_VOLUME_MIN = -21.00 -OUTPUT_VOLUME_MAX = 0.00 - -AMP_VOLUME_MIN = -73.00 -AMP_VOLUME_MAX = 6.00 - -# ALC Time mapping -_ALC_ATTACK_MAX = const(10) -ALC_ATTACK_TIME_MIN = 0.006 -ALC_ATTACK_TIME_MAX = 6.140 - -_ALC_DECAY_MAX = const(10) -ALC_DECAY_TIME_MIN = 0.024 -ALC_DECAY_TIME_MAX = 24.580 - -ALC_HOLD_TIME_MIN = 0.00267 -ALC_HOLD_TIME_MAX = 43.691 - -# Speaker Boost Gains (DC and AC) -_SPEAKER_BOOST_GAIN = [0.0, 2.1, 2.9, 3.6, 4.5, 5.1] # in dB - -# VMIDSEL settings -VMIDSEL_DISABLED = 0 -VMIDSEL_PLAYBACK = 1 # 2X50KOHM -VMIDSEL_LOWPOWER = 2 # 2X250KOHM -VMIDSEL_FASTSTART = 3 # 2X5KOHM - -# Clock Divider Tables -_BCLKDIV = [1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0, 8.0, 11.0, 12.0, 16.0, 22.0, 24.0, 32.0] -_DCLKDIV = [1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0] -_ADCDACDIV = [1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0] - -# Default Register Map -_REG_DEFAULTS = [ - const(0x0097), # R0 (0x00) - const(0x0097), # R1 (0x01) - const(0x0000), # R2 (0x02) - const(0x0000), # R3 (0x03) - const(0x0000), # R4 (0x04) - const(0x0008), # F5 (0x05) - const(0x0000), # R6 (0x06) - const(0x000A), # R7 (0x07) - const(0x01C0), # R8 (0x08) - const(0x0000), # R9 (0x09) - const(0x00FF), # R10 (0x0a) - const(0x00FF), # R11 (0x0b) - const(0x0000), # R12 (0x0C) RESERVED - const(0x0000), # R13 (0x0D) RESERVED - const(0x0000), # R14 (0x0E) RESERVED - const(0x0000), # R15 (0x0F) RESERVED - const(0x0000), # R16 (0x10) - const(0x007B), # R17 (0x11) - const(0x0100), # R18 (0x12) - const(0x0032), # R19 (0x13) - const(0x0000), # R20 (0x14) - const(0x00C3), # R21 (0x15) - const(0x00C3), # R22 (0x16) - const(0x01C0), # R23 (0x17) - const(0x0000), # R24 (0x18) - const(0x0000), # R25 (0x19) - const(0x0000), # R26 (0x1A) - const(0x0000), # R27 (0x1B) - const(0x0000), # R28 (0x1C) - const(0x0000), # R29 (0x1D) - const(0x0000), # R30 (0x1E) RESERVED - const(0x0000), # R31 (0x1F) RESERVED - const(0x0100), # R32 (0x20) - const(0x0100), # R33 (0x21) - const(0x0050), # R34 (0x22) - const(0x0000), # R35 (0x23) RESERVED - const(0x0000), # R36 (0x24) RESERVED - const(0x0050), # R37 (0x25) - const(0x0000), # R38 (0x26) - const(0x0000), # R39 (0x27) - const(0x0000), # R40 (0x28) - const(0x0000), # R41 (0x29) - const(0x0040), # R42 (0x2A) - const(0x0000), # R43 (0x2B) - const(0x0000), # R44 (0x2C) - const(0x0050), # R45 (0x2D) - const(0x0050), # R46 (0x2E) - const(0x0000), # R47 (0x2F) - const(0x0002), # R48 (0x30) - const(0x0037), # R49 (0x31) - const(0x0000), # R50 (0x32) RESERVED - const(0x0080), # R51 (0x33) - const(0x0008), # R52 (0x34) - const(0x0031), # R53 (0x35) - const(0x0026), # R54 (0x36) - const(0x00E9), # R55 (0x37) -] - - -# pylint: disable=protected-access - - -class WOBit: - - def __init__( - self, - register_address: int, - bit: int, - ) -> None: - self.register_address = register_address - self.bit = bit - self.bit_mask = 1 << (bit % 8) # the bitmask *within* the byte! - self.byte = 1 - (bit // 8) # the byte number within the buffer - - def _set(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: - if value: - obj._registers[self.register_address][self.byte] |= self.bit_mask - else: - obj._registers[self.register_address][self.byte] &= ~self.bit_mask - - def __get__( - self, - obj: Optional[I2CDeviceDriver], - objtype: Optional[Type[I2CDeviceDriver]] = None, - ) -> bool: - return bool(obj._registers[self.register_address][self.byte] & self.bit_mask) - - def __set__(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: - self._set(obj, value) - with obj.i2c_device as i2c: - i2c.write(obj._registers[self.register_address]) - - -class WOBits: - - def __init__( # pylint: disable=too-many-arguments - self, - num_bits: int, - register_address: int, - lowest_bit: int, - ) -> None: - self.register_width = 1 - self.register_address = register_address - self.bit_mask = ((1 << num_bits) - 1) << lowest_bit - self.lowest_bit = lowest_bit - - def _set(self, obj: Optional[I2CDeviceDriver], value: int) -> None: - value <<= self.lowest_bit # shift the value over to the right spot - reg = 0 - for i in range(2): - reg = (reg << 8) | obj._registers[self.register_address][i] - reg &= ~self.bit_mask # mask off the bits we're about to change - reg |= value # then or in our new value - for i in range(1, -1, -1): - obj._registers[self.register_address][i] = reg & 0xFF - reg >>= 8 - - def __get__( - self, - obj: Optional[I2CDeviceDriver], - objtype: Optional[Type[I2CDeviceDriver]] = None, - ) -> int: - # read the number of bytes into a single variable - reg = 0 - for i in range(2): - reg = (reg << 8) | obj._registers[self.register_address][i] - reg = (reg & self.bit_mask) >> self.lowest_bit - return reg - - def __set__(self, obj: Optional[I2CDeviceDriver], value: int) -> None: - self._set(obj, value) - with obj.i2c_device as i2c: - i2c.write(obj._registers[self.register_address]) - - -# pylint: enable=protected-access - - -class WM8960: # pylint: disable=too-many-instance-attributes,too-many-public-methods - - # Power - - vref = WOBit(_REG_PWR_MGMT_1, 6) - - left_input = WOBit(_REG_PWR_MGMT_1, 5) - right_input = WOBit(_REG_PWR_MGMT_1, 4) - - @property - def input(self) -> bool: - return self.left_input and self.right_input - - @input.setter - def input(self, value: bool) -> None: - self.left_input = self.right_input = value - - # MIC - - ## PWR_MGMT - - left_mic = WOBit(_REG_PWR_MGMT_3, 5) - right_mic = WOBit(_REG_PWR_MGMT_3, 4) - - @property - def mic(self) -> bool: - return self.left_mic and self.right_mic - - @mic.setter - def mic(self, value: bool) -> None: - self.left_mic = self.right_mic = value - - ## SIGNAL_PATH - - left_mic_inverting_input = WOBit(_REG_ADCL_SIGNAL_PATH, 8) - right_mic_inverting_input = WOBit(_REG_ADCR_SIGNAL_PATH, 8) - - @property - def mic_inverting_input(self) -> bool: - return self.left_mic_inverting_input and self.right_mic_inverting_input - - @mic_inverting_input.setter - def mic_inverting_input(self, value: bool) -> None: - self.left_mic_inverting_input = self.right_mic_inverting_input = value - - _left_mic_input2 = WOBit(_REG_ADCL_SIGNAL_PATH, 6) - _left_mic_input3 = WOBit(_REG_ADCL_SIGNAL_PATH, 7) - - @property - def left_mic_input(self) -> int: - if self._left_mic_input2: - return MIC_INPUT2 - if self._left_mic_input3: - return MIC_INPUT3 - return MIC_VMID - - @left_mic_input.setter - def left_mic_input(self, value: int) -> None: - self._left_mic_input2 = value == MIC_INPUT2 - self._left_mic_input3 = value == MIC_INPUT3 - - _right_mic_input2 = WOBit(_REG_ADCR_SIGNAL_PATH, 6) - _right_mic_input3 = WOBit(_REG_ADCR_SIGNAL_PATH, 7) - - @property - def right_mic_input(self) -> int: - if self._right_mic_input2: - return MIC_INPUT2 - if self._right_mic_input3: - return MIC_INPUT3 - return MIC_VMID - - @right_mic_input.setter - def right_mic_input(self, value: int) -> None: - self._right_mic_input2 = value == MIC_INPUT2 - self._right_mic_input3 = value == MIC_INPUT3 - - @property - def mic_input(self) -> int: - # NOTE: Not checking right signal - return self.left_mic_input - - @mic_input.setter - def mic_input(self, value: int) -> None: - self.left_mic_input = self.right_mic_input = value - - ## Boost - - left_mic_boost = WOBit(_REG_ADCL_SIGNAL_PATH, 3) - right_mic_boost = WOBit(_REG_ADCR_SIGNAL_PATH, 3) - - @property - def mic_boost(self) -> bool: - return self.left_mic_boost and self.right_mic_boost - - @mic_boost.setter - def mic_boost(self, value: bool) -> None: - self.left_mic_boost = self.right_mic_boost = value - - @staticmethod - def _get_mic_boost_gain(value: float) -> int: - for i in reversed(range(len(_MIC_BOOST_GAIN))): - if value >= _MIC_BOOST_GAIN[i]: - return i - return 0 - - _left_mic_boost_gain = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) - - @property - def left_mic_boost_gain(self) -> float: - return _MIC_BOOST_GAIN[self._left_mic_boost_gain] - - @left_mic_boost_gain.setter - def left_mic_boost_gain(self, value: float) -> None: - self._left_mic_boost_gain = WM8960._get_mic_boost_gain(value) - - _right_mic_boost_gain = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) - - @property - def right_mic_boost_gain(self) -> float: - return _MIC_BOOST_GAIN[self._right_mic_boost_gain] - - @right_mic_boost_gain.setter - def right_mic_boost_gain(self, value: float) -> None: - self._right_mic_boost_gain = WM8960._get_mic_boost_gain(value) - - @property - def mic_boost_gain(self) -> float: - return max(self.left_mic_boost_gain, self.right_mic_boost_gain) - - @mic_boost_gain.setter - def mic_boost_gain(self, value: float) -> None: - self._left_mic_boost_gain = self._right_mic_boost_gain = ( - WM8960._get_mic_boost_gain(value) - ) - - ## Volume - - _left_mic_volume = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) - _left_mic_volume_set = WOBit(_REG_LEFT_INPUT_VOLUME, 8) - - _right_mic_volume = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) - _right_mic_volume_set = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) - - @property - def left_mic_volume(self) -> float: - # fmt: off - return map_range( - self._left_mic_volume, 0, 63, - MIC_GAIN_MIN, MIC_GAIN_MAX - ) - # fmt: on - - @left_mic_volume.setter - def left_mic_volume(self, value: float) -> None: - # fmt: off - self._left_mic_volume = round(map_range( - value, MIC_GAIN_MIN, MIC_GAIN_MAX, - 0, 63 - )) - # fmt: on - self._left_mic_volume_set = True - - @property - def right_mic_volume(self) -> float: - # fmt: off - return map_range( - self._right_mic_volume, 0, 63, - MIC_GAIN_MIN, MIC_GAIN_MAX - ) - # fmt: on - - @right_mic_volume.setter - def right_mic_volume(self, value: float) -> None: - # fmt: off - self._right_mic_volume = round(map_range( - value, MIC_GAIN_MIN, MIC_GAIN_MAX, - 0, 63 - )) - # fmt: on - self._right_mic_volume_set = True - - @property - def mic_volume(self) -> float: - return max(self.left_mic_volume, self.right_mic_volume) - - @mic_volume.setter - def mic_volume(self, value: float) -> None: - # fmt: off - self._left_mic_volume = self._right_mic_volume = round(map_range( - value, MIC_GAIN_MIN, MIC_GAIN_MAX, - 0, 63 - )) - # fmt: on - self._left_mic_volume_set = self._right_mic_volume_set = True - - ## Zero Cross - - left_mic_zero_cross = WOBit(_REG_LEFT_INPUT_VOLUME, 6) - right_mic_zero_cross = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) - - @property - def mic_zero_cross(self) -> bool: - return self.left_mic_zero_cross and self.right_mic_zero_cross - - @mic_zero_cross.setter - def mic_zero_cross(self, value: bool) -> None: - self.left_mic_zero_cross = self.right_mic_zero_cross = value - - ## Mute - - _left_mic_mute = WOBit(_REG_LEFT_INPUT_VOLUME, 7) - _right_mic_mute = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) - - @property - def left_mic_mute(self) -> bool: - return self._left_mic_mute - - @left_mic_mute.setter - def left_mic_mute(self, value: bool) -> None: - self._left_mic_mute = value - self._left_mic_volume_set = True - - @property - def right_mic_mute(self) -> bool: - return self._right_mic_mute - - @right_mic_mute.setter - def right_mic_mute(self, value: bool) -> None: - self._right_mic_mute = value - self._right_mic_volume_set = True - - @property - def mic_mute(self) -> bool: - return self._left_mic_mute and self._right_mic_mute - - @mic_mute.setter - def mic_mute(self, value: bool) -> None: - self._left_mic_mute = self._right_mic_mute = value - - # Boost Mixer - - _left_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) - - @property - def left_input2_boost(self) -> float: - value = self._left_input2_boost - if value == 0: - return None - # fmt: off - return map_range( - value, 1, 7, - BOOST_GAIN_MIN, BOOST_GAIN_MAX - ) - # fmt: on - - @left_input2_boost.setter - def left_input2_boost(self, value: float) -> None: - # fmt: off - self._left_input2_boost = ( - 0 - if value < BOOST_GAIN_MIN - else round(map_range( - value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, - 1, 7 - )) - ) - # fmt: on - - _right_input2_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) - - @property - def right_input2_boost(self) -> float: - value = self._right_input2_boost - if value == 0: - return None - # fmt: off - return map_range( - value, 1, 7, - BOOST_GAIN_MIN, BOOST_GAIN_MAX - ) - # fmt: on - - @right_input2_boost.setter - def right_input2_boost(self, value: float) -> None: - # fmt: off - self._right_input2_boost = ( - 0 - if value < BOOST_GAIN_MIN - else round(map_range( - value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, - 1, 7 - )) - ) - # fmt: on - - @property - def input2_boost(self) -> float: - value = max(self._left_input2_boost, self._right_input2_boost) - # fmt: off - return ( - None - if value == 0 - else map_range( - value, 1, 7, - BOOST_GAIN_MIN, BOOST_GAIN_MAX - ) - ) - # fmt: on - - @input2_boost.setter - def input2_boost(self, value: float) -> None: - # fmt: off - self._left_input2_boost = self._right_input2_boost = ( - 0 - if value < BOOST_GAIN_MIN - else round(map_range( - value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, - 1, 7 - )) - ) - # fmt: on - - _left_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) - - @property - def left_input3_boost(self) -> float: - value = self._left_input3_boost - if value == 0: - return None - # fmt: off - return map_range( - value, 1, 7, - BOOST_GAIN_MIN, BOOST_GAIN_MAX - ) - # fmt: on - - @left_input3_boost.setter - def left_input3_boost(self, value: float) -> None: - # fmt: off - self._left_input3_boost = ( - 0 - if value < BOOST_GAIN_MIN - else round(map_range( - value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, - 1, 7 - )) - ) - # fmt: on - - _right_input3_boost = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) - - @property - def right_input3_boost(self) -> float: - value = self._right_input3_boost - if value == 0: - return None - # fmt: off - return map_range( - value, 1, 7, - BOOST_GAIN_MIN, BOOST_GAIN_MAX - ) - # fmt: on - - @right_input3_boost.setter - def right_input3_boost(self, value: float) -> None: - # fmt: off - self._right_input3_boost = ( - 0 - if value < BOOST_GAIN_MIN - else round(map_range( - value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, - 1, 7 - )) - ) - # fmt: on - - @property - def input3_boost(self) -> float: - value = max(self._left_input3_boost, self._right_input3_boost) - # fmt: off - return ( - None - if value == 0 - else map_range( - value, 1, 7, - BOOST_GAIN_MIN, BOOST_GAIN_MAX - ) - ) - # fmt: on - - @input3_boost.setter - def input3_boost(self, value: float) -> None: - # fmt: off - self._left_input3_boost = self._right_input3_boost = ( - 0 - if value < BOOST_GAIN_MIN - else round(map_range( - value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, - 1, 7 - )) - ) - # fmt: on - - # Mic Bias - - mic_bias = WOBit(_REG_PWR_MGMT_1, 1) - mic_bias_voltage = WOBit(_REG_ADDITIONAL_CONTROL_4, 0) - - # ADC - - left_adc = WOBit(_REG_PWR_MGMT_1, 3) - right_adc = WOBit(_REG_PWR_MGMT_1, 2) - - @property - def adc(self) -> bool: - return self.left_adc and self.right_adc - - @adc.setter - def adc(self, value: bool) -> None: - self.left_adc = self.right_adc = value - - ## Volume - - _left_adc_volume = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) - _left_adc_volume_set = WOBit(_REG_LEFT_ADC_VOLUME, 8) - - @property - def left_adc_volume(self) -> float: - # fmt: off - return map_range( - self._left_adc_volume, 1, 255, - ADC_VOLUME_MIN, ADC_VOLUME_MAX - ) - # fmt: on - - @left_adc_volume.setter - def left_adc_volume(self, value: float) -> None: - # fmt: off - self._left_adc_volume = round(map_range( - value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, - 1, 255 - )) - # fmt: on - self._left_adc_volume_set = True - - _right_adc_volume = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) - _right_adc_volume_set = WOBit(_REG_RIGHT_ADC_VOLUME, 8) - - @property - def right_adc_volume(self) -> float: - # fmt: off - return map_range( - self._right_adc_volume, 1, 255, - ADC_VOLUME_MIN, ADC_VOLUME_MAX - ) - # fmt: on - - @right_adc_volume.setter - def right_adc_volume(self, value: float) -> None: - # fmt: off - self._right_adc_volume = round(map_range( - value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, - 1, 255 - )) - # fmt: on - self._right_adc_volume_set = True - - @property - def adc_volume(self) -> int: - return max(self.left_adc_volume, self.right_adc_volume) - - @adc_volume.setter - def adc_volume(self, value: float) -> None: - # fmt: off - self._left_adc_volume = self._right_adc_volume = round(map_range( - value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, - 1, 255 - )) - # fmt: on - self._left_adc_volume_set = self._right_adc_volume_set = True - - # ALC - - left_alc = WOBit(_REG_ALC1, 7) - right_alc = WOBit(_REG_ALC1, 8) - - @property - def alc(self) -> bool: - return self.left_alc and self.right_alc - - @alc.setter - def alc(self, value: bool) -> None: - self.left_alc = self.right_alc = value - - _alc_target = WOBits(4, _REG_ALC1, 0) - - @property - def alc_target(self) -> float: - # fmt: off - return map_range( - self._alc_target, 0, 15, - ALC_TARGET_MIN, ALC_TARGET_MAX - ) - # fmt: on - - @alc_target.setter - def alc_target(self, value: float) -> None: - # fmt: off - self._alc_target = round(map_range( - value, ALC_TARGET_MIN, ALC_TARGET_MAX, - 0, 15 - )) - # fmt: on - - _alc_max_gain = WOBits(3, _REG_ALC1, 4) - - @property - def alc_max_gain(self) -> float: - # fmt: off - return map_range( - self._alc_max_gain, 0, 7, - ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX - ) - # fmt: on - - @alc_max_gain.setter - def alc_max_gain(self, value: float) -> None: - # fmt: off - self._alc_max_gain = round(map_range( - value, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX, - 0, 7 - )) - # fmt: on - - _alc_min_gain = WOBits(3, _REG_ALC2, 4) - - @property - def alc_min_gain(self) -> float: - # fmt: off - return map_range( - self._alc_min_gain, 0, 7, - ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX - ) - # fmt: on - - @alc_min_gain.setter - def alc_min_gain(self, value: float) -> None: - # fmt: off - self._alc_min_gain = round(map_range( - value, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX, - 0, 7 - )) - # fmt: on - - _alc_attack = WOBits(4, _REG_ALC3, 0) - - @property - def alc_attack_time(self) -> float: - return ALC_ATTACK_TIME_MIN * pow(2, self._alc_attack) - - @alc_attack_time.setter - def alc_attack_time(self, value: float) -> None: - # fmt: off - self._alc_attack = min( - round(math.log2( - (constrain(value, ALC_ATTACK_TIME_MIN, ALC_ATTACK_TIME_MAX) - ALC_ATTACK_TIME_MIN) - / ALC_ATTACK_TIME_MIN - )), - _ALC_ATTACK_MAX - ) - # fmt: on - - _alc_decay = WOBits(4, _REG_ALC3, 4) - - @property - def alc_decay_time(self) -> float: - return ALC_DECAY_TIME_MIN * pow(2, self._alc_decay) - - @alc_decay_time.setter - def alc_decay_time(self, value: float) -> None: - # fmt: off - self._alc_decay = min( - round(math.log2( - (constrain(value, ALC_DECAY_TIME_MIN, ALC_DECAY_TIME_MAX) - ALC_DECAY_TIME_MIN) - / ALC_DECAY_TIME_MIN - )), - _ALC_DECAY_MAX - ) - # fmt: on - - _alc_hold = WOBits(4, _REG_ALC2, 0) - - @property - def alc_hold_time(self) -> float: - value = self._alc_hold - if value == 0: - return 0.0 - return ALC_HOLD_TIME_MIN * pow(2, self._alc_hold - 1) - - @alc_hold_time.setter - def alc_hold_time(self, value: float) -> None: - if value <= 0.0: - self._alc_hold = 0 - else: - # fmt: off - self._alc_hold = round( - math.log2( - (constrain(value, ALC_HOLD_TIME_MIN, ALC_HOLD_TIME_MAX) - ALC_HOLD_TIME_MIN) - / ALC_HOLD_TIME_MIN - ) - + 1.0 - ) - # fmt: on - - alc_limiter = WOBit(_REG_ALC3, 8) - - # Noise Gate - - noise_gate = WOBit(_REG_NOISE_GATE, 0) - - _noise_gate_threshold = WOBits(5, _REG_NOISE_GATE, 3) - - @property - def noise_gate_threshold(self) -> float: - # fmt: off - return map_range( - self._noise_gate_threshold, 0, 31, - GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX - ) - # fmt: on - - @noise_gate_threshold.setter - def noise_gate_threshold(self, value: float) -> None: - # fmt: off - self._noise_gate_threshold = round(map_range( - value, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX, - 0, 31 - )) - # fmt: on - - # DAC - - left_dac = WOBit(_REG_PWR_MGMT_2, 8) - right_dac = WOBit(_REG_PWR_MGMT_2, 7) - - @property - def dac(self) -> bool: - return self.left_dac and self.right_dac - - @dac.setter - def dac(self, value: bool) -> None: - self.left_dac = self.right_dac = value - - _left_dac_volume = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) - _left_dac_volume_set = WOBit(_REG_LEFT_DAC_VOLUME, 8) - - @property - def left_dac_volume(self) -> float: - # fmt: off - return map_range( - self._left_dac_volume, 1, 255, - DAC_VOLUME_MIN, DAC_VOLUME_MAX - ) - # fmt: on - - @left_dac_volume.setter - def left_dac_volume(self, value: float) -> None: - # fmt: off - self._left_dac_volume = round(map_range( - value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, - 1, 255 - )) - # fmt: on - self._left_dac_volume_set = True - - _right_dac_volume = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) - _right_dac_volume_set = WOBit(_REG_RIGHT_DAC_VOLUME, 8) - - @property - def right_dac_volume(self) -> float: - # fmt: off - return map_range( - self._right_dac_volume, 1, 255, - DAC_VOLUME_MIN, DAC_VOLUME_MAX - ) - # fmt: on - - @right_dac_volume.setter - def right_dac_volume(self, value: float) -> None: - # fmt: off - self._right_dac_volume = round(map_range( - value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, - 1, 255 - )) - # fmt: on - self._right_dac_volume_set = True - - @property - def dac_volume(self) -> int: - return max(self.left_dac_volume, self.right_dac_volume) - - @dac_volume.setter - def dac_volume(self, value: float) -> None: - # fmt: off - self._left_dac_volume = self._right_dac_volume = round(map_range( - value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, - 1, 255 - )) - # fmt: on - self._left_dac_volume_set = self._right_dac_volume_set = True - - dac_mute = WOBit(_REG_ADC_DAC_CTRL_1, 3) - dac_soft_mute = WOBit(_REG_ADC_DAC_CTRL_2, 3) - dac_slow_soft_mute = WOBit(_REG_ADC_DAC_CTRL_2, 2) - dac_attenuation = WOBit(_REG_ADC_DAC_CTRL_1, 7) - - # 3D Enhance - - enhance = WOBit(_REG_3D_CONTROL, 0) - enhance_filter_lPF = WOBit(_REG_3D_CONTROL, 6) - enhance_filter_hPF = WOBit(_REG_3D_CONTROL, 5) - - _enhance_depth = WOBits(4, _REG_3D_CONTROL, 1) - - @property - def enhance_depth(self) -> float: - return self._enhance_depth / 15.0 - - @enhance_depth.setter - def enhance_depth(self, value: float) -> None: - # fmt: off - self._enhance_depth = round(map_range( - value, 0.0, 1.0, - 0, 15 - )) - # fmt: on - - # Output Mixer - - left_output = WOBit(_REG_PWR_MGMT_3, 3) - right_output = WOBit(_REG_PWR_MGMT_3, 2) - - @property - def output(self) -> bool: - return self.left_output and self.right_output - - @output.setter - def output(self, value: bool) -> None: - self.left_output = self.right_output = value - - ## DAC Output - - left_dac_output = WOBit(_REG_LEFT_OUT_MIX, 8) - right_dac_output = WOBit(_REG_RIGHT_OUT_MIX, 8) - - @property - def dac_output(self) -> bool: - return self.left_dac_output and self.right_dac_output - - @dac_output.setter - def dac_output(self, value: bool) -> None: - self.left_dac_output = self.right_dac_output = value - - ## Input 3 Output - - left_input3_output = WOBit(_REG_LEFT_OUT_MIX, 7) - right_input3_output = WOBit(_REG_RIGHT_OUT_MIX, 7) - - @property - def input3_output(self) -> bool: - return self.left_input3_output and self.right_input3_output - - @input3_output.setter - def input3_output(self, value: bool) -> None: - self.left_input3_output = self.right_input3_output = value - - _left_input3_output_volume = WOBits(3, _REG_LEFT_OUT_MIX, 4) - - @property - def left_input3_output_volume(self) -> float: - # fmt: off - return map_range( - self._left_input3_output_volume, 0, 7, - OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN - ) - # fmt: on - - @left_input3_output_volume.setter - def left_input3_output_volume(self, value: float) -> None: - # fmt: off - self._left_input3_output_volume = round(map_range( - value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, - 7, 0 - )) - # fmt: on - - _right_input3_output_volume = WOBits(3, _REG_RIGHT_OUT_MIX, 4) - - @property - def right_input3_output_volume(self) -> float: - # fmt: off - return map_range( - self._right_input3_output_volume, 0, 7, - OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN - ) - # fmt: on - - @right_input3_output_volume.setter - def right_input3_output_volume(self, value: float) -> None: - # fmt: off - self._right_input3_output_volume = round(map_range( - value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, - 7, 0 - )) - # fmt: on - - @property - def input3_output_volume(self) -> float: - return max(self.left_input3_output_volume, self.right_input3_output_volume) - - @input3_output_volume.setter - def input3_output_volume(self, value: float) -> None: - # fmt: off - self._left_input3_output_volume = self._right_input3_output_volume = round(map_range( - value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, - 7, 0 - )) - # fmt: on - - ## MIC Boost Mixer Output - - left_mic_output = WOBit(_REG_BYPASS_1, 7) - right_mic_output = WOBit(_REG_BYPASS_2, 7) - - @property - def mic_output(self) -> bool: - return self.left_mic_output and self.right_mic_output - - @mic_output.setter - def mic_output(self, value: bool) -> None: - self.left_mic_output = self.right_mic_output = value - - _left_mic_output_volume = WOBits(3, _REG_BYPASS_1, 4) - - @property - def left_mic_output_volume(self) -> float: - # fmt: off - return map_range( - self._left_mic_output_volume, 0, 7, - OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN - ) - # fmt: on - - @left_mic_output_volume.setter - def left_mic_output_volume(self, value: float) -> None: - # fmt: off - self._left_mic_output_volume = round(map_range( - value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, - 7, 0 - )) - # fmt: on - - _right_mic_output_volume = WOBits(3, _REG_BYPASS_2, 4) - - @property - def right_mic_output_volume(self) -> float: - # fmt: off - return map_range( - self._right_mic_output_volume, 0, 7, - OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN - ) - # fmt: on - - @right_mic_output_volume.setter - def right_mic_output_volume(self, value: float) -> None: - # fmt: off - self._right_mic_output_volume = round(map_range( - value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, - 7, 0 - )) - # fmt: on - - @property - def mic_output_volume(self) -> float: - return max(self.left_mic_output_volume, self.right_mic_output_volume) - - @mic_output_volume.setter - def mic_output_volume(self, value: float) -> None: - # fmt: off - self._left_mic_output_volume = self._right_mic_output_volume = round(map_range( - value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, - 7, 0 - )) - # fmt: on - - ## Mono Output - - mono_output = WOBit(_REG_PWR_MGMT_2, 1) - - mono_left_mix = WOBit(_REG_MONO_OUT_MIX_1, 7) - mono_right_mix = WOBit(_REG_MONO_OUT_MIX_2, 7) - - @property - def mono_mix(self) -> bool: - return self.mono_left_mix and self.mono_right_mix - - @mono_mix.setter - def mono_mix(self, value: bool) -> None: - self.mono_left_mix = self.mono_right_mix = value - - mono_output_attenuation = WOBit(_REG_MONO_OUT_VOLUME, 6) - - vmid = WOBits(2, _REG_PWR_MGMT_1, 7) - - # Amplifier - - ## Headphones - - left_headphone = WOBit(_REG_PWR_MGMT_2, 5) - right_headphone = WOBit(_REG_PWR_MGMT_2, 6) - - @property - def headphone(self) -> bool: - return self.left_headphone and self.right_headphone - - @headphone.setter - def headphone(self, value: bool) -> None: - self.left_headphone = self.right_headphone = value - - headphone_standby = WOBit(_REG_ANTI_POP_1, 0) - - _left_headphone_volume = WOBits(7, _REG_LOUT1_VOLUME, 0) - _left_headphone_volume_set = WOBit(_REG_LOUT1_VOLUME, 8) - - @property - def left_headphone_volume(self) -> float: - # fmt: off - return map_range( - self._left_headphone_volume, 48, 127, - AMP_VOLUME_MIN, AMP_VOLUME_MAX - ) - # fmt: on - - @left_headphone_volume.setter - def left_headphone_volume(self, value: float) -> None: - # fmt: off - self._left_headphone_volume = round(map_range( - value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, - 48, 127 - )) - # fmt: on - self._left_headphone_volume_set = True - - _right_headphone_volume = WOBits(7, _REG_ROUT1_VOLUME, 0) - _right_headphone_volume_set = WOBit(_REG_ROUT1_VOLUME, 8) - - @property - def right_headphone_volume(self) -> float: - # fmt: off - return map_range( - self._right_headphone_volume, 48, 127, - AMP_VOLUME_MIN, AMP_VOLUME_MAX - ) - # fmt: on - - @right_headphone_volume.setter - def right_headphone_volume(self, value: float) -> None: - # fmt: off - self._right_headphone_volume = round(map_range( - value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, - 48, 127 - )) - # fmt: on - self._right_headphone_volume_set = True - - @property - def headphone_volume(self) -> int: - return max(self.left_headphone_volume, self.right_headphone_volume) - - @headphone_volume.setter - def headphone_volume(self, value: float) -> None: - # fmt: off - self._left_headphone_volume = self._right_headphone_volume = round(map_range( - value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, - 48, 127 - )) - # fmt: on - self._left_headphone_volume_set = self._right_headphone_volume_set = True - - left_headphone_zero_cross = WOBit(_REG_LOUT1_VOLUME, 7) - right_headphone_zero_cross = WOBit(_REG_LOUT1_VOLUME, 7) - - @property - def headphone_zero_cross(self) -> bool: - return self.left_headphone_zero_cross and self.right_headphone_zero_cross - - @headphone_zero_cross.setter - def headphone_zero_cross(self, value: bool) -> None: - self.left_headphone_zero_cross = self.right_headphone_zero_cross = value - - ## Speakers - - _left_speaker = WOBit(_REG_PWR_MGMT_2, 4) - _left_speaker_amp = WOBit(_REG_CLASS_D_CONTROL_1, 6) - - @property - def left_speaker(self) -> bool: - return self._left_speaker and self._left_speaker_amp - - @left_speaker.setter - def left_speaker(self, value: bool) -> None: - self._left_speaker = self._left_speaker_amp = value - - _right_speaker = WOBit(_REG_PWR_MGMT_2, 3) - _right_speaker_amp = WOBit(_REG_CLASS_D_CONTROL_1, 7) - - @property - def right_speaker(self) -> bool: - return self._right_speaker and self._right_speaker_amp - - @right_speaker.setter - def right_speaker(self, value: bool) -> None: - self._right_speaker = self._right_speaker_amp = value - - @property - def speaker(self) -> bool: - return self.left_speaker and self.right_speaker - - @speaker.setter - def speaker(self, value: bool) -> None: - self.left_speaker = self.right_speaker = value - - _left_speaker_volume = WOBits(7, _REG_LOUT2_VOLUME, 0) - _left_speaker_volume_set = WOBit(_REG_LOUT2_VOLUME, 8) - - @property - def left_speaker_volume(self) -> float: - # fmt: off - return map_range( - self._left_speaker_volume, 48, 127, - AMP_VOLUME_MIN, AMP_VOLUME_MAX - ) - # fmt: on - - @left_speaker_volume.setter - def left_speaker_volume(self, value: float) -> None: - # fmt: off - self._left_speaker_volume = round(map_range( - value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, - 48, 127 - )) - # fmt: on - self._left_speaker_set = True - - _right_speaker_volume = WOBits(7, _REG_ROUT2_VOLUME, 0) - _right_speaker_volume_set = WOBit(_REG_ROUT2_VOLUME, 8) - - @property - def right_speaker_volume(self) -> float: - # fmt: off - return map_range( - self._right_speaker_volume, 48, 127, - AMP_VOLUME_MIN, AMP_VOLUME_MAX - ) - # fmt: on - - @right_speaker_volume.setter - def right_speaker_volume(self, value: float) -> None: - # fmt: off - self._right_speaker_volume = round(map_range( - value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, - 48, 127 - )) - # fmt: on - self._right_speaker_volume_set = True - - @property - def speaker_volume(self) -> int: - return max(self.left_speaker_volume, self.right_speaker_volume) - - @speaker_volume.setter - def speaker_volume(self, value: float) -> None: - # fmt: off - self._left_speaker_volume = self._right_speaker_volume = round(map_range( - value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, - 48, 127 - )) - # fmt: on - self._left_speaker_volume_set = self._right_speaker_volume_set = True - - left_speaker_zero_cross = WOBit(_REG_LOUT2_VOLUME, 7) - right_speaker_zero_cross = WOBit(_REG_LOUT2_VOLUME, 7) - - @property - def speaker_zero_cross(self) -> bool: - return self.left_speaker_zero_cross and self.right_speaker_zero_cross - - @speaker_zero_cross.setter - def speaker_zero_cross(self, value: bool) -> None: - self.left_speaker_zero_cross = self.right_speaker_zero_cross = value - - @staticmethod - def _get_speaker_boost_gain(value: float) -> int: - for i in reversed(range(len(_SPEAKER_BOOST_GAIN))): - if value >= _SPEAKER_BOOST_GAIN[i]: - return i - return 0 - - _speaker_dc_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 3) - - @property - def speaker_dc_gain(self) -> float: - return _SPEAKER_BOOST_GAIN[self._speaker_dc_gain] - - @speaker_dc_gain.setter - def speaker_dc_gain(self, value: float) -> None: - self._speaker_dc_gain = WM8960._get_speaker_boost_gain(value) - - _speaker_ac_gain = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) - - @property - def speaker_ac_gain(self) -> float: - return _SPEAKER_BOOST_GAIN[self._speaker_ac_gain] - - @speaker_ac_gain.setter - def speaker_ac_gain(self, value: float) -> None: - self._speaker_ac_gain = WM8960._get_speaker_boost_gain(value) - - # Digital Audio Interface Control - - loopback = WOBit(_REG_AUDIO_INTERFACE_2, 0) - - pll = WOBit(_REG_PWR_MGMT_2, 0) - pll_prescale_div2 = WOBit(_REG_PWR_MGMT_2, 4) - pll_n = WOBits(4, _REG_PLL_N, 0) - - _pll_k1 = WOBits(6, _REG_PLL_K_1, 0) - _pll_k2 = WOBits(9, _REG_PLL_K_2, 0) - _pll_k3 = WOBits(9, _REG_PLL_K_3, 0) - - @property - def pll_k(self) -> int: - return self._pll_k1 << 18 + self._pll_k2 << 9 + self._pll_k3 - - @pll_k.setter - def pll_k(self, value: int) -> None: - self._pll_k1 = (value >> 18) & 0b111111 - self._pll_k2 = (value >> 9) & 0b111111111 - self._pll_k3 = value & 0b111111111 - - clock_fractional_mode = WOBit(_REG_PLL_N, 5) - - clock_from_pll = WOBit(_REG_CLOCKING_1, 0) - - _system_clock_divider = WOBits(2, _REG_CLOCKING_1, 1) - - @property - def system_clock_div2(self) -> bool: - return self._system_clock_divider == _SYSCLK_DIV_BY_2 - - @system_clock_div2.setter - def system_clock_div2(self, value: bool) -> None: - self._system_clock_divider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 - - _adc_clock_divider = WOBits(3, _REG_CLOCKING_1, 6) - - @property - def adc_clock_divider(self) -> int: - return _ADCDACDIV[min(self._adc_clock_divider, len(_ADCDACDIV))] - - @adc_clock_divider.setter - def adc_clock_divider(self, value: int) -> None: - value = round(value * 2.0) / 2.0 - if value in _ADCDACDIV: - self._adc_clock_divider = _ADCDACDIV.index(value) - - _dac_clock_divider = WOBits(3, _REG_CLOCKING_1, 3) - - @property - def dac_clock_divider(self) -> int: - return _ADCDACDIV[min(self._dac_clock_divider, len(_ADCDACDIV))] - - @dac_clock_divider.setter - def dac_clock_divider(self, value: int) -> None: - value = round(value * 2.0) / 2.0 - if value in _ADCDACDIV: - self._dac_clock_divider = _ADCDACDIV.index(value) - - _base_clock_divider = WOBits(4, _REG_CLOCKING_2, 0) - - @property - def base_clock_divider(self) -> float: - return _BCLKDIV[min(self._base_clock_divider, len(_BCLKDIV))] - - @base_clock_divider.setter - def base_clock_divider(self, value: float) -> None: - value = round(value * 2.0) / 2.0 - if value in _BCLKDIV: - self._base_clock_divider = _BCLKDIV.index(value) - - _amp_clock_divider = WOBits(3, _REG_CLOCKING_2, 6) - - @property - def amp_clock_divider(self) -> float: - return _DCLKDIV[min(self._amp_clock_divider, len(_DCLKDIV))] - - @amp_clock_divider.setter - def amp_clock_divider(self, value: float) -> None: - value = round(value * 2.0) / 2.0 - if value in _DCLKDIV: - self._amp_clock_divider = _DCLKDIV.index(value) - - ## Mode - - master_mode = WOBit(_REG_AUDIO_INTERFACE_1, 6) - - _bit_depth = WOBits(2, _REG_AUDIO_INTERFACE_1, 2) - - @property - def bit_depth(self) -> int: - value = self._bit_depth - return 32 if value == 3 else 16 + 4 * value - - @bit_depth.setter - def bit_depth(self, value: int) -> None: - self._bit_depth = (min(value, 28) - 16) // 4 - - word_select_invert = WOBit(_REG_AUDIO_INTERFACE_1, 4) - - adc_channel_swap = WOBit(_REG_AUDIO_INTERFACE_1, 8) - - _vref_output_disable = WOBit(_REG_ADDITIONAL_CONTROL_3, 6) - - @property - def vref_output(self) -> bool: - return not self._vref_output_disable - - @vref_output.setter - def vref_output(self, value: bool) -> None: - self._vref_output_disable = not value - - _vsel = WOBits(2, _REG_ADDITIONAL_CONTROL_1, 6) - - @property - def power_supply(self) -> float: - return (constrain(self._vsel, 1, 2) - 1) * 0.6 + 2.7 - - @power_supply.setter - def power_supply(self, value: float) -> None: - self._vsel = (constrain(value, 2.7, 3.3) - 2.7) // 0.6 * 2 + 1 - - ## GPIO - - gpio_output = WOBit(_REG_AUDIO_INTERFACE_2, 6) - gpio_output_mode = WOBits(3, _REG_ADDITIONAL_CONTROL_4, 4) - gpio_output_invert = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) - - _gpio_clock_divider = WOBits(3, _REG_CLOCKING_2, 6) - - @property - def gpio_clock_divider(self) -> int: - return self._gpio_clock_divider - - @gpio_clock_divider.setter - def gpio_clock_divider(self, value: int) -> None: - self._gpio_clock_divider = min(value, 5) - - @property - def sample_rate(self) -> int: - return self._sample_rate - - @sample_rate.setter - def sample_rate(self, value: int) -> None: - # MCLK = 24 MHz - self.pll = True # Needed for class-d amp clock - self.clock_fractional_mode = True - self.clock_from_pll = True - - self.pll_prescale_div2 = True - self.system_clock_div2 = True - self.base_clock_divider = 4.0 - self.amp_clock_divider = 16.0 - - if value in [8000, 12000, 16000, 24000, 32000, 48000]: - # SYSCLK = 12.288 MHz - # DCLK = 768.0k_hz - self.pll_n = 8 - self.pll_k = 0x3126_E8 - self.adc_clock_divider = self.dac_clock_divider = 48000 / value - - elif value in [11025, 22050, 44100]: - # SYSCLK = 11.2896 MHz - # DCLK = 705.6k_hz - self.pll_n = 7 - self.pll_k = 0x86_C226 - self.adc_clock_divider = self.dac_clock_divider = 44100 / value - - else: - raise ValueError("Invalid sample rate") - - self._sample_rate = value - - def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_I2C_ADDR) -> None: - self.i2c_device = I2CDevice(i2c_bus, address) - self._sample_rate = None - - self._registers = [0] * len(_REG_DEFAULTS) - for i, reg in enumerate(self._registers): - self._registers[i] = bytearray(reg.to_bytes(2, "big")) - - # Must be called before `reset` to ensure that _REG_RESET is addressed properly - self._reset_registers() - - self.reset() - - # General setup - self.vref = True - self.vmid = VMIDSEL_PLAYBACK - - # Resets all registers to their default state - _reset = WOBit(_REG_RESET, 7) - - def _reset_registers(self) -> None: - for i, reg in enumerate(self._registers): - reg[:] = _REG_DEFAULTS[i].to_bytes(2, "big") - reg[0] |= i << 1 - - def reset(self) -> None: - self._reset = True - self._reset_registers() diff --git a/adafruit_wm8960/__init__.py b/adafruit_wm8960/__init__.py new file mode 100644 index 0000000..f7b7a08 --- /dev/null +++ b/adafruit_wm8960/__init__.py @@ -0,0 +1,415 @@ +# SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT +""" +`adafruit_wm8960` +================================================================================ + +CircuitPython driver for WM8960 Stereo CODEC + +* Author(s): Scott Shawcroft, Cooper Dalrymple + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads + +# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +""" + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_WM8960.git" + +from busio import I2C +from adafruit_simplemath import constrain, map_range + +from .advanced import * + + +# pylint: disable=too-few-public-methods + + +class Input: + """An enum-like class representing the input options of the WM8960. Used for + :attr:`WM8960.input`. + """ + + DISABLED = 0b000 + """Disconnect all inputs.""" + + MIC1 = 0b001 + """Connect input 1 to the microphone amplifier in single-ended mode.""" + + MIC2 = 0b011 + """Connect input 1 and 2 to the microphone amplifier in differential mode.""" + + MIC3 = 0b101 + """Connect input 1 and 3 to the microphone amplifier in differential mode.""" + + LINE2 = 0b010 + """Connect input 2 as a line input.""" + + LINE3 = 0b100 + """Connect input 3 as a line input.""" + + +# pylint: enable=too-few-public-methods + + +class WM8960: + """Driver for controlling the WM8960 audio codec over an I2C connection.Used for receiving line + and microphone input through the ADC over I2S, monitoring analog input to the output mixer, and + sending digital audio to the headphone and speaker amplifiers via I2S. + + NOTE: This driver assumes that the master clock of the WM8960 is 24 MHz in order determine + appropriate clock settings. + + :param i2c: The I2C bus. + :type i2c: :class:`I2C` + :param sample_rate: The sample rate of each audio frame used by the digital interface of the + WM8960. The sample rates 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, and 48000 + are supported. Defaults to 44100. + :type sample_rate: `int`, optional + :param bit_depth: The number of bits per sample. The values 16, 20, 24, and 32 are supported. + Defaults to 16. + :type bit_depth: `int`, optional + """ + + def __init__( + self, + i2c_bus: I2C, + sample_rate: int = 44100, + bit_depth: int = 16, + ) -> None: + self._input = Input.DISABLED + self._gain = 0.0 + + self._codec = WM8960_Advanced(i2c_bus) + + # Digital Interface + self._codec.sample_rate = sample_rate + self._codec.bit_depth = bit_depth + + # ADC + self._codec.adc = True + self._codec.input = True + self._codec.mic_boost_gain = 0.0 + self._codec.mic_zero_cross = True + + # DAC + self._codec.dac = True + self._codec.dac_output = True + + # Output + self._codec.output = True + self._codec.mono_output = True + self._codec.headphone_zero_cross = True + self._codec.speaker_zero_cross = True + + # Digital Interface + + @property + def sample_rate(self) -> int: + """The rate of the I2S bus in samples per second. This property is read-only.""" + return self._codec.sample_rate + + @property + def bit_depth(self) -> int: + """The number of bits per sample. This property is read-only.""" + return self._codec.bit_depth + + # Input + + @property + def input(self) -> int: + """The signal used as the input to the ADC. See :class:`Input` for available selections. + + :default: :const:`Input.DISABLED` + """ + return self._input + + @input.setter + def input(self, value: int) -> None: + mic = bool(value & 0b001) + + # Configure microphone amplifier + self._codec.mic = mic + self._codec.mic_inverting_input = mic + self._codec.mic_input = (value & 0b110) >> 1 if mic else Mic_Input.VMID + self._codec.mic_mute = mic + self._codec.mic_boost = mic + + # Reset gain values + self.gain = self._gain + + self._input = value + + @property + def gain(self) -> float: + """The amount of analog gain on the selected input before the ADC. + + :default: 0.0 + """ + return self._gain + + @gain.setter + def gain(self, value: float) -> None: + mic = bool(self._input & 0b001) + + self._codec.mic_volume = ( + map_range(value, 0.0, 1.0, MIC_GAIN_MIN, MIC_GAIN_MAX) + if mic + else MIC_GAIN_MIN + ) + + self._codec.input2_boost = ( + map_range(value, 0.0, 1.0, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + if not mic and self._input & 0b010 + else BOOST_GAIN_MIN + ) + + self._codec.input3_boost = ( + map_range(value, 0.0, 1.0, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + if not mic and self._input & 0b100 + else BOOST_GAIN_MIN + ) + + self._gain = constrain(value, 0.0, 1.0) + + @property + def monitor(self) -> float: + """The volume of the analog input bypassed to the output mixer. If set to 0.0, this bypass + will be muted. + + :default: 0.0 + """ + if not self._codec.mic_output: + return 0.0 + return map_range( + self._codec.mic_output_volume, + OUTPUT_VOLUME_MIN, + OUTPUT_VOLUME_MAX, + 0.0, + 1.0, + ) + + @monitor.setter + def monitor(self, value: float) -> None: + if value <= 0.0: + self._codec.mic_output = False + elif not self._codec.mic_output: + self._codec.mic_output = True + self._codec.mic_output_volume = map_range( + value, 0.0, 1.0, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX + ) + + @property + def loopback(self) -> bool: + """Whether or not the data from the ADC is routed directly into the DAC internally. If set + to `True`, the I2S interface will be disabled. This property is useful if you would like to + apply :attr:`enhance` to the input signal. + + :default: `False` + """ + return ( + self._codec.master_mode and self._codec.gpio_output and self._codec.loopback + ) + + @loopback.setter + def loopback(self, value: bool) -> None: + self._codec.master_mode = value + self._codec.gpio_output = value + self._codec.loopback = value + + # Output + + @property + def volume(self) -> float: + """The level of the output mixer before the :attr:`headphone` and :attr:`speaker` + amplifiers. + + :default: 0.0 + """ + if self._codec.dac_mute: + return 0.0 + return map_range( + self._codec.dac_volume, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 0.0, 1.0 + ) + + @volume.setter + def volume(self, value: float) -> None: + if value <= 0.0: + self._codec.dac_mute = True + elif self._codec.dac_mute: + self._codec.dac_mute = False + self._codec.dac_output = map_range( + value, 0.0, 1.0, DAC_VOLUME_MIN, DAC_VOLUME_MAX + ) + + @property + def headphone(self) -> float: + """The volume of the headphone amplifier output from 0.0 to 1.0. Setting this property to + 0.0 will mute and power off the headphone amplifier. + + :default: 0.0 + """ + if not self._codec.headphone: + return 0.0 + value = self._codec.headphone_volume + if value is None: + return 0.0 + return map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 0.0, 1.0) + + @headphone.setter + def headphone(self, value: float) -> None: + if value <= 0.0: + self._codec.headphone = False + elif not self._codec.headphone: + self._codec.headphone = True + self._codec.headphone_volume = ( + map_range(value, 0.0, 1.0, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + if value > 0.0 + else AMP_VOLUME_MIN - 1.0 # Mute + ) + + @property + def speaker(self) -> float: + """The volume of the speaker amplifier output from 0.0 to 1.0. Setting this property to 0.0 + will power off the speaker amplifier. + + :default: 0.0 + """ + if not self._codec.speaker: + return 0.0 + return map_range( + self._codec.speaker_volume, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 0.0, 1.0 + ) + + @speaker.setter + def speaker(self, value: float) -> None: + if value <= 0.0: + self._codec.speaker = False + elif not self._codec.speaker: + self._codec.speaker = True + self._codec.speaker_volume = map_range( + value, 0.0, 1.0, AMP_VOLUME_MIN, AMP_VOLUME_MAX + ) + + # 3D Enhance + + @property + def enhance(self) -> float: + """The amount of digital 3D stereo enhancement that artificially increases the separation + between the left and right channels within the DAC. Set to 0.0 to disable. + + :default: 0.0 + """ + if not self._codec.enhance: + return 0.0 + return self._codec.enhance_depth + + @enhance.setter + def enhance(self, value: float) -> None: + if value <= 0.0: + self._codec.enhance = False + elif not self._codec.enhance: + self._codec.enhance = True + self._codec.enhance_depth = value + + # ALC & Noise Gate + + @property + def alc(self) -> bool: + """Whether or not the Automatic Level Control (ALC) is enabled. The ALC uses the digital + signal within the ADC to control the gain of the microphone amplifier. This feature does not + apply if one of the line-level inputs is used for :attr:`input` (ie: :const:`Input.LINE2` or + :const:`Input.LINE3`). + + :default: `False` + """ + return self._codec.alc + + @alc.setter + def alc(self, value: bool) -> None: + self._codec.alc = value + + @property + def alc_gain(self) -> tuple: + """The gain properties of the Automatic Level Control (ALC). Provided as a tuple in the + format of target, max gain, min gain, and noise gate threshold all as monotonic `float` + values. + + :default: (0.733333, 1.0, 0.0, 0.0) + """ + return ( + map_range(self._codec.alc_target, ALC_TARGET_MIN, ALC_TARGET_MAX, 0.0, 1.0), + map_range( + self._codec.alc_max_gain, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX, 0.0, 1.0 + ), + map_range( + self._codec.alc_min_gain, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX, 0.0, 1.0 + ), + ( + map_range( + self._codec.noise_gate_threshold, + GATE_THRESHOLD_MIN, + GATE_THRESHOLD_MAX, + 0.0, + 1.0, + ) + if self._codec.noise_gate + else 0.0 + ), + ) + + @alc_gain.setter + def alc_gain(self, value: tuple) -> None: + if len(value) < 4: + raise ValueError("Invalid tuple object") + + self._codec.alc_target = map_range( + value[0], 0.0, 1.0, ALC_TARGET_MIN, ALC_TARGET_MAX + ) + + self._codec.alc_max_gain = map_range( + value[1], 0.0, 1.0, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX + ) + + self._codec.alc_min_gain = map_range( + value[2], 0.0, 1.0, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX + ) + + if value[3] <= 0.0: + self._codec.noise_gate = False + elif not self._codec.noise_gate: + self._codec.noise_gate = True + self._codec.noise_gate_threshold = map_range( + value[3], 0.0, 1.0, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX + ) + + @property + def alc_time(self) -> tuple: + """The time properties of the Automatic Level Control (ALC). Provided as a tuple in the + format of attack time (from 0.006s to 6.14s), decay time (from 0.024s to 24.58s), and hold + time (from 0.00267s to 43.691s) all as seconds within `float` values. + + :default: (0.024, 0.192, 0.0) + """ + return ( + self._codec.alc_attack_time, + self._codec.alc_decay_time, + self._codec.alc_hold_time, + ) + + @alc_time.setter + def alc_time(self, value: tuple) -> None: + if len(value) < 3: + raise ValueError("Invalid tuple object") + + self._codec.alc_attack_time = value[0] + self._codec.alc_decay_time = value[1] + self._codec.alc_hold_time = value[2] diff --git a/adafruit_wm8960/advanced.py b/adafruit_wm8960/advanced.py new file mode 100644 index 0000000..3b9e792 --- /dev/null +++ b/adafruit_wm8960/advanced.py @@ -0,0 +1,2384 @@ +# SPDX-FileCopyrightText: Copyright (c) 2022 Pete Lewis for SparkFun Electronics +# SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple +# +# SPDX-License-Identifier: MIT +""" +`adafruit_wm8960.advanced` +================================================================================ + +CircuitPython driver for WM8960 Stereo CODEC with advanced control + +* Author(s): Scott Shawcroft, Cooper Dalrymple + +Originally authored by Pete Lewis @ SparkFun Electronics, October 14th, 2022 +https://github.com/sparkfun/SparkFun_Arduino_Library + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads + +# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +""" + +# pylint: disable=too-many-lines + +import math +from busio import I2C +from adafruit_bus_device.i2c_device import I2CDevice +from adafruit_simplemath import constrain, map_range +from micropython import const + +try: + from typing import Optional, Type + from circuitpython_typing.device_drivers import I2CDeviceDriver +except ImportError: + pass + +# I2C address +_DEFAULT_I2C_ADDR = const(0x1A) + +# WM8960 register addresses +_REG_LEFT_INPUT_VOLUME = const(0x00) +_REG_RIGHT_INPUT_VOLUME = const(0x01) +_REG_LOUT1_VOLUME = const(0x02) +_REG_ROUT1_VOLUME = const(0x03) +_REG_CLOCKING_1 = const(0x04) +_REG_ADC_DAC_CTRL_1 = const(0x05) +_REG_ADC_DAC_CTRL_2 = const(0x06) +_REG_AUDIO_INTERFACE_1 = const(0x07) +_REG_CLOCKING_2 = const(0x08) +_REG_AUDIO_INTERFACE_2 = const(0x09) +_REG_LEFT_DAC_VOLUME = const(0x0A) +_REG_RIGHT_DAC_VOLUME = const(0x0B) +_REG_RESET = const(0x0F) +_REG_3D_CONTROL = const(0x10) +_REG_ALC1 = const(0x11) +_REG_ALC2 = const(0x12) +_REG_ALC3 = const(0x13) +_REG_NOISE_GATE = const(0x14) +_REG_LEFT_ADC_VOLUME = const(0x15) +_REG_RIGHT_ADC_VOLUME = const(0x16) +_REG_ADDITIONAL_CONTROL_1 = const(0x17) +_REG_ADDITIONAL_CONTROL_2 = const(0x18) +_REG_PWR_MGMT_1 = const(0x19) +_REG_PWR_MGMT_2 = const(0x1A) +_REG_ADDITIONAL_CONTROL_3 = const(0x1B) +_REG_ANTI_POP_1 = const(0x1C) +_REG_ANTI_POP_2 = const(0x1D) +_REG_ADCL_SIGNAL_PATH = const(0x20) +_REG_ADCR_SIGNAL_PATH = const(0x21) +_REG_LEFT_OUT_MIX = const(0x22) +_REG_RIGHT_OUT_MIX = const(0x25) +_REG_MONO_OUT_MIX_1 = const(0x26) +_REG_MONO_OUT_MIX_2 = const(0x27) +_REG_LOUT2_VOLUME = const(0x28) +_REG_ROUT2_VOLUME = const(0x29) +_REG_MONO_OUT_VOLUME = const(0x2A) +_REG_INPUT_BOOST_MIXER_1 = const(0x2B) +_REG_INPUT_BOOST_MIXER_2 = const(0x2C) +_REG_BYPASS_1 = const(0x2D) +_REG_BYPASS_2 = const(0x2E) +_REG_PWR_MGMT_3 = const(0x2F) +_REG_ADDITIONAL_CONTROL_4 = const(0x30) +_REG_CLASS_D_CONTROL_1 = const(0x31) +_REG_CLASS_D_CONTROL_3 = const(0x33) +_REG_PLL_N = const(0x34) +_REG_PLL_K_1 = const(0x35) +_REG_PLL_K_2 = const(0x36) +_REG_PLL_K_3 = const(0x37) + +# ALC Time mapping + +ALC_ATTACK_TIME_MIN = 0.006 +"""The minimum amount of time allowed by the attack (Gain Ramp-Down) time of the automatic level +control (ALC) of the WM8960 in seconds. Used by the attribute +:attr:`WM8960_Advanced.alc_attack_time`. +""" + +ALC_ATTACK_TIME_MAX = 6.140 +"""The maximum amount of time allowed by the attack (Gain Ramp-Down) time of the automatic level +control (ALC) of the WM8960 in seconds. Used by the attribute +:attr:`WM8960_Advanced.alc_attack_time`. +""" + +ALC_DECAY_TIME_MIN = 0.024 +"""The minimum amount of time allowed by the decay (Gain Ramp-Up) time of the automatic level +control (ALC) of the WM8960 in seconds. Used by the attribute +:attr:`WM8960_Advanced.alc_decay_time`. +""" + +ALC_DECAY_TIME_MAX = 24.580 +"""The maximum amount of time allowed by the decay (Gain Ramp-Up) time of the automatic level +control (ALC) of the WM8960 in seconds. Used by the attribute +:attr:`WM8960_Advanced.alc_decay_time`. +""" + +ALC_HOLD_TIME_MIN = 0.00267 +"""The minimum amount of time allowed by the hold time of the automatic level control (ALC) of the +WM8960 in seconds. Used by the attribute :attr:`WM8960_Advanced.alc_hold_time`. +""" + +ALC_HOLD_TIME_MAX = 43.691 +"""The maximum amount of time allowed by the hold time of the automatic level control (ALC) of the +WM8960 in seconds. Used by the attribute :attr:`WM8960_Advanced.alc_hold_time`. +""" + +# Gain/Level min/max + +BOOST_GAIN_MIN = -12.00 +"""The minimum gain allowed by the WM8960 input boost mixers in decibels. Used by the attributes +:attr:`WM8960_Advanced.left_input2_boost`, :attr:`WM8960_Advanced.right_input2_boost`, +:attr:`WM8960_Advanced.input2_boost`, :attr:`WM8960_Advanced.left_input3_boost`, +:attr:`WM8960_Advanced.right_input3_boost`, and :attr:`WM8960_Advanced.input3_boost`. +""" + +BOOST_GAIN_MAX = 6.0 +"""The maximum gain allowed by the WM8960 input boost mixers in decibels. Used by the attributes +:attr:`WM8960_Advanced.left_input2_boost`, :attr:`WM8960_Advanced.right_input2_boost`, +:attr:`WM8960_Advanced.input2_boost`, :attr:`WM8960_Advanced.left_input3_boost`, +:attr:`WM8960_Advanced.right_input3_boost`, and :attr:`WM8960_Advanced.input3_boost`. +""" + +MIC_GAIN_MIN = -17.25 +"""The minimum gain allowed by the WM8960 PGA (microphone) amplifier in decibels. Used by the +attributes :attr:`WM8960_Advanced.left_mic_volume`, :attr:`WM8960_Advanced.right_mic_volume`, and +:attr:`WM8960_Advanced.mic_volume`. +""" + +MIC_GAIN_MAX = 30.00 +"""The maximum gain allowed by the WM8960 PGA (microphone) amplifier in decibels. Used by the +attributes :attr:`WM8960_Advanced.left_mic_volume`, :attr:`WM8960_Advanced.right_mic_volume`, and +:attr:`WM8960_Advanced.mic_volume`. +""" + +ADC_VOLUME_MIN = -97.00 +"""The minimum digital volume allowed by the WM8960 ADC in decibels. Used by the attributes +:attr:`WM8960_Advanced.left_adc_volume`, :attr:`WM8960_Advanced.right_adc_volume`, and +:attr:`WM8960_Advanced.adc_volume`. +""" + +ADC_VOLUME_MAX = 30.00 +"""The maximum digital volume allowed by the WM8960 ADC in decibels. Used by the attributes +:attr:`WM8960_Advanced.left_adc_volume`, :attr:`WM8960_Advanced.right_adc_volume`, and +:attr:`WM8960_Advanced.adc_volume`. +""" + +DAC_VOLUME_MIN = -127.00 +"""The minimum digital volume allowed by the WM8960 DAC in decibels. Used by the attributes +:attr:`WM8960_Advanced.left_dac_volume`, :attr:`WM8960_Advanced.right_dac_volume`, and +:attr:`WM8960_Advanced.dac_volume`. +""" + +DAC_VOLUME_MAX = 0.00 +"""The maximum digital volume allowed by the WM8960 DAC in decibels. Used by the attributes +:attr:`WM8960_Advanced.left_dac_volume`, :attr:`WM8960_Advanced.right_dac_volume`, and +:attr:`WM8960_Advanced.dac_volume`. +""" + +ALC_TARGET_MIN = -22.50 +"""The minimum target level of the WM8960 automatic level control (ALC) in decibels. Used by the +attribute :attr:`WM8960_Advanced.alc_target`. +""" + +ALC_TARGET_MAX = -1.50 +"""The maximum target level of the WM8960 automatic level control (ALC) in decibels. Used by the +attribute :attr:`WM8960_Advanced.alc_target`. +""" + +ALC_MAX_GAIN_MIN = -12.00 +"""The minimum gain of the maximum allowed gain of the WM8960 automatic level control (ALC) in +decibels. Used by the attribute :attr:`WM8960_Advanced.alc_max_gain`. +""" + +ALC_MAX_GAIN_MAX = 30.00 +"""The maximum gain of the maximum allowed gain of the WM8960 automatic level control (ALC) in +decibels. Used by the attribute :attr:`WM8960_Advanced.alc_max_gain`. +""" + +ALC_MIN_GAIN_MIN = -17.25 +"""The minimum gain of the minimum allowed gain of the WM8960 automatic level control (ALC) in +decibels. Used by the attribute :attr:`WM8960_Advanced.alc_min_gain`. +""" + +ALC_MIN_GAIN_MAX = 24.75 +"""The maximum gain of the minimum allowed gain of the WM8960 automatic level control (ALC) in +decibels. Used by the attribute :attr:`WM8960_Advanced.alc_min_gain`. +""" + +GATE_THRESHOLD_MIN = -76.50 +"""The minimum level to trigger the noise gate of the WM8960 in decibels. Used by the attribute +:attr:`WM8960_Advanced.noise_gate_threshold`. +""" + +GATE_THRESHOLD_MAX = -30.00 +"""The maximum level to trigger the noise gate of the WM8960 in decibels. Used by the attribute +:attr:`WM8960_Advanced.noise_gate_threshold`. +""" + +OUTPUT_VOLUME_MIN = -21.00 +"""The minimum volume level used by the analog bypass output mixers of the WM8960 in decibels. Used +by the attributes :attr:`WM8960_Advanced.left_input3_output_volume`, +:attr:`WM8960_Advanced.right_input3_output_volume`, :attr:`WM8960_Advanced.input3_output_volume`, +:attr:`WM8960_Advanced.left_mic_output_volume`, :attr:`WM8960_Advanced.right_mic_output_volume`, +and :attr:`WM8960_Advanced.mic_output_volume`. +""" + +OUTPUT_VOLUME_MAX = 0.00 +"""The maximum volume level used by the analog bypass output mixers of the WM8960 in decibels. Used +by the attributes :attr:`WM8960_Advanced.left_input3_output_volume`, +:attr:`WM8960_Advanced.right_input3_output_volume`, :attr:`WM8960_Advanced.input3_output_volume`, +:attr:`WM8960_Advanced.left_mic_output_volume`, :attr:`WM8960_Advanced.right_mic_output_volume`, and +:attr:`WM8960_Advanced.mic_output_volume`. +""" + +AMP_VOLUME_MIN = -73.00 +"""The minimum volume level used by the headphone and speaker amplifiers of the WM8960 in decibels. +Used by the attributes :attr:`WM8960_Advanced.left_headphone_volume`, +:attr:`WM8960_Advanced.right_headphone_volume`, :attr:`WM8960_Advanced.headphone_volume`, +:attr:`WM8960_Advanced.left_speaker_volume`, :attr:`WM8960_Advanced.right_speaker_volume`, and +:attr:`WM8960_Advanced.speaker_volume`. +""" + +AMP_VOLUME_MAX = 6.00 +"""The maximum volume level used by the headphone and speaker amplifiers of the WM8960 in decibels. +Used by the attributes :attr:`WM8960_Advanced.left_headphone_volume`, +:attr:`WM8960_Advanced.right_headphone_volume`, :attr:`WM8960_Advanced.headphone_volume`, +:attr:`WM8960_Advanced.left_speaker_volume`, :attr:`WM8960_Advanced.right_speaker_volume`, and +:attr:`WM8960_Advanced.speaker_volume`. +""" + + +# pylint: disable=too-few-public-methods + + +class Mic_Input: + """An enum-like class representing the microphone amplifier (PGA) input modes. Used for the + attributes :attr:`WM8960_Advanced.left_mic_input`, :attr:`WM8960_Advanced.right_mic_input`, and + :attr:`WM8960_Advanced.mic_input`. + """ + + VMID = 0 + """Disconnect both input 2 and input 3 from the non-inverting input of the amplifier and instead + connect it to the internally buffered VMID refernce (see :attr:`WM8960_Advanced.vmid`). + """ + + INPUT2 = 1 + """Connect the signal of input 2 to the non-inverting input of the amplifier.""" + + INPUT3 = 2 + """Connect the signal of input 3 to the non-inverting input of the amplifier.""" + + +# pylint: enable=too-few-public-methods + + +# Microphone Boost gain options +_MIC_BOOST_GAIN = [0.0, 13.0, 20.0, 29.0] # in dB + +# SYSCLK divide +_SYSCLK_DIV_BY_1 = const(0) +_SYSCLK_DIV_BY_2 = const(2) + +# ALC Time mapping +_ALC_DECAY_MAX = const(10) +_ALC_ATTACK_MAX = const(10) + +# Speaker Boost Gains (DC and AC) +_SPEAKER_BOOST_GAIN = [0.0, 2.1, 2.9, 3.6, 4.5, 5.1] # in dB + + +# pylint: disable=too-few-public-methods + + +class Vmid_Mode: + """An enum-like class representing possible Vmid reference voltage modes used in conjunction + with the attribute :attr:`WM8960_Advanced.vmid`. + """ + + DISABLED = 0 + """Disable the vmid reference voltage used by both the microphone amplifier (PGA) and OUT3 when + mono output is disabled as a buffered headphone ground. + """ + + PLAYBACK = 1 # 2X50KOHM + """Set the internal voltage reference to a 2*50kOhm potential divider. This setting is typical + for normal playback and power consumption and is used for the microphone amplifier and as a + buffered headphone ground. + """ + + LOWPOWER = 2 # 2X250KOHM + """Set the internal voltage reference to a 2*250kOhm potential divider. Use this setting for low + power maintenance when all other operations of the WM8960 are disabled. + """ + + FASTSTART = 3 # 2X5KOHM + """Set the internal voltage reference to a 2*5kOhm potential divider. Use this setting if a fast + start-up of playback operation is desired. This setting will consume more power. + """ + + +# pylint: enable=too-few-public-methods + + +# Clock Divider Tables +_BCLKDIV = [1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0, 8.0, 11.0, 12.0, 16.0, 22.0, 24.0, 32.0] +_DCLKDIV = [1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0] +_ADCDACDIV = [1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0] +_OPCLKDIV = [1.0, 2.0, 3.0, 4.0, 5.5, 6.0] + +# Default Register Map +_REG_DEFAULTS = [ + const(0x0097), # R0 (0x00) + const(0x0097), # R1 (0x01) + const(0x0000), # R2 (0x02) + const(0x0000), # R3 (0x03) + const(0x0000), # R4 (0x04) + const(0x0008), # F5 (0x05) + const(0x0000), # R6 (0x06) + const(0x000A), # R7 (0x07) + const(0x01C0), # R8 (0x08) + const(0x0000), # R9 (0x09) + const(0x00FF), # R10 (0x0a) + const(0x00FF), # R11 (0x0b) + const(0x0000), # R12 (0x0C) RESERVED + const(0x0000), # R13 (0x0D) RESERVED + const(0x0000), # R14 (0x0E) RESERVED + const(0x0000), # R15 (0x0F) RESERVED + const(0x0000), # R16 (0x10) + const(0x007B), # R17 (0x11) + const(0x0100), # R18 (0x12) + const(0x0032), # R19 (0x13) + const(0x0000), # R20 (0x14) + const(0x00C3), # R21 (0x15) + const(0x00C3), # R22 (0x16) + const(0x01C0), # R23 (0x17) + const(0x0000), # R24 (0x18) + const(0x0000), # R25 (0x19) + const(0x0000), # R26 (0x1A) + const(0x0000), # R27 (0x1B) + const(0x0000), # R28 (0x1C) + const(0x0000), # R29 (0x1D) + const(0x0000), # R30 (0x1E) RESERVED + const(0x0000), # R31 (0x1F) RESERVED + const(0x0100), # R32 (0x20) + const(0x0100), # R33 (0x21) + const(0x0050), # R34 (0x22) + const(0x0000), # R35 (0x23) RESERVED + const(0x0000), # R36 (0x24) RESERVED + const(0x0050), # R37 (0x25) + const(0x0000), # R38 (0x26) + const(0x0000), # R39 (0x27) + const(0x0000), # R40 (0x28) + const(0x0000), # R41 (0x29) + const(0x0040), # R42 (0x2A) + const(0x0000), # R43 (0x2B) + const(0x0000), # R44 (0x2C) + const(0x0050), # R45 (0x2D) + const(0x0050), # R46 (0x2E) + const(0x0000), # R47 (0x2F) + const(0x0002), # R48 (0x30) + const(0x0037), # R49 (0x31) + const(0x0000), # R50 (0x32) RESERVED + const(0x0080), # R51 (0x33) + const(0x0008), # R52 (0x34) + const(0x0031), # R53 (0x35) + const(0x0026), # R54 (0x36) + const(0x00E9), # R55 (0x37) +] + + +# pylint: disable=protected-access,missing-class-docstring,missing-function-docstring + + +class WOBit: + + def __init__( + self, + register_address: int, + bit: int, + ) -> None: + self.register_address = register_address + self.bit = bit + self.bit_mask = 1 << (bit % 8) # the bitmask *within* the byte! + self.byte = 1 - (bit // 8) # the byte number within the buffer + + def _set(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: + if value: + obj._registers[self.register_address][self.byte] |= self.bit_mask + else: + obj._registers[self.register_address][self.byte] &= ~self.bit_mask + + def __get__( + self, + obj: Optional[I2CDeviceDriver], + objtype: Optional[Type[I2CDeviceDriver]] = None, + ) -> bool: + return bool(obj._registers[self.register_address][self.byte] & self.bit_mask) + + def __set__(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: + self._set(obj, value) + with obj.i2c_device as i2c: + i2c.write(obj._registers[self.register_address]) + + +class WOBits: + + def __init__( # pylint: disable=too-many-arguments + self, + num_bits: int, + register_address: int, + lowest_bit: int, + ) -> None: + self.register_width = 1 + self.register_address = register_address + self.bit_mask = ((1 << num_bits) - 1) << lowest_bit + self.lowest_bit = lowest_bit + + def _set(self, obj: Optional[I2CDeviceDriver], value: int) -> None: + value <<= self.lowest_bit # shift the value over to the right spot + reg = 0 + for i in range(2): + reg = (reg << 8) | obj._registers[self.register_address][i] + reg &= ~self.bit_mask # mask off the bits we're about to change + reg |= value # then or in our new value + for i in range(1, -1, -1): + obj._registers[self.register_address][i] = reg & 0xFF + reg >>= 8 + + def __get__( + self, + obj: Optional[I2CDeviceDriver], + objtype: Optional[Type[I2CDeviceDriver]] = None, + ) -> int: + # read the number of bytes into a single variable + reg = 0 + for i in range(2): + reg = (reg << 8) | obj._registers[self.register_address][i] + reg = (reg & self.bit_mask) >> self.lowest_bit + return reg + + def __set__(self, obj: Optional[I2CDeviceDriver], value: int) -> None: + self._set(obj, value) + with obj.i2c_device as i2c: + i2c.write(obj._registers[self.register_address]) + + +# pylint: enable=protected-access,missing-class-docstring,missing-function-docstring + + +class WM8960_Advanced: # pylint: disable=too-many-instance-attributes,too-many-public-methods + """Driver for interacting directly with a WM8960 audio codec to control analog and digital audio + pathways over an I2C connection.""" + + # Power + + power: bool = WOBit(_REG_PWR_MGMT_1, 6) + """Control the power state of all functions of the WM8960. + + :default: `True` + """ + + vmid: int = WOBits(2, _REG_PWR_MGMT_1, 7) + """The buffered reference voltage mode. Use constants from class :class:`Vmid_Mode`. + + :default: :const:`Vmid_Mode.PLAYBACK` + """ + + left_input: bool = WOBit(_REG_PWR_MGMT_1, 5) + """The power state of the left channel boost stage and microphone amplifier (if :attr:`left_mic` + is set to `True`). + + :default: `False` + """ + + right_input: bool = WOBit(_REG_PWR_MGMT_1, 4) + """The power state of the right channel boost stage and microphone amplifier (if + :attr:`right_mic` is set to `True`). + + :default: `False` + """ + + @property + def input(self) -> bool: + """The power state of the stereo boost stage and microphone amplifier (if :attr:`mic` is set + to `True`). + + :default: `False` + """ + return self.left_input and self.right_input + + @input.setter + def input(self, value: bool) -> None: + self.left_input = self.right_input = value + + # MIC + + ## PWR_MGMT + + left_mic: bool = WOBit(_REG_PWR_MGMT_3, 5) + """The power state of the left channel microphone amplifier (if :attr:`left_input` is set to + `True`). + + :default: `False` + """ + + right_mic: bool = WOBit(_REG_PWR_MGMT_3, 4) + """The power state of the right channel microphone amplifier (if :attr:`right_input` is set to + `True`). + + :default: `False` + """ + + @property + def mic(self) -> bool: + """The power state of the stereo microphone amplifier (if :attr:`input` is set to `True`). + + :default: `False` + """ + return self.left_mic and self.right_mic + + @mic.setter + def mic(self, value: bool) -> None: + self.left_mic = self.right_mic = value + + ## SIGNAL_PATH + + left_mic_inverting_input: bool = WOBit(_REG_ADCL_SIGNAL_PATH, 8) + """The connection of the left inverting input of the microphone amplifier (PGA) to the left + input 1. + + :default: `False` + """ + + right_mic_inverting_input: bool = WOBit(_REG_ADCR_SIGNAL_PATH, 8) + """The connection the right inverting input of the microphone amplifier (PGA) to the right input + 1. + + :default: `False` + """ + + @property + def mic_inverting_input(self) -> bool: + """The connection of the inverting input of the microphone amplifier (PGA) to input 1. + + :default: `False` + """ + return self.left_mic_inverting_input and self.right_mic_inverting_input + + @mic_inverting_input.setter + def mic_inverting_input(self, value: bool) -> None: + self.left_mic_inverting_input = self.right_mic_inverting_input = value + + _left_mic_input2: bool = WOBit(_REG_ADCL_SIGNAL_PATH, 6) + _left_mic_input3: bool = WOBit(_REG_ADCL_SIGNAL_PATH, 7) + + @property + def left_mic_input(self) -> int: + """The connection to the left non-inverting input of the microphone amplifier. Use constants + from class :class:`Mic_Input`. + + :default: :const:`Mic_Input.VMID` + """ + if self._left_mic_input2: + return Mic_Input.INPUT2 + if self._left_mic_input3: + return Mic_Input.INPUT3 + return Mic_Input.VMID + + @left_mic_input.setter + def left_mic_input(self, value: int) -> None: + self._left_mic_input2 = value == Mic_Input.INPUT2 + self._left_mic_input3 = value == Mic_Input.INPUT3 + + _right_mic_input2: bool = WOBit(_REG_ADCR_SIGNAL_PATH, 6) + _right_mic_input3: bool = WOBit(_REG_ADCR_SIGNAL_PATH, 7) + + @property + def right_mic_input(self) -> int: + """The connection to the right non-inverting input of the microphone amplifier. Use + constants from class :class:`Mic_Input`. + + :default: :const:`Mic_Input.VMID` + """ + if self._right_mic_input2: + return Mic_Input.INPUT2 + if self._right_mic_input3: + return Mic_Input.INPUT3 + return Mic_Input.VMID + + @right_mic_input.setter + def right_mic_input(self, value: int) -> None: + self._right_mic_input2 = value == Mic_Input.INPUT2 + self._right_mic_input3 = value == Mic_Input.INPUT3 + + @property + def mic_input(self) -> int: + """The connection to the non-inverting input of the microphone amplifier. Use constants from + class :class:`Mic_Input`. + + :default: :const:`Mic_Input.VMID` + """ + # NOTE: Not checking right signal + return self.left_mic_input + + @mic_input.setter + def mic_input(self, value: int) -> None: + self.left_mic_input = self.right_mic_input = value + + ## Boost + + left_mic_boost: bool = WOBit(_REG_ADCL_SIGNAL_PATH, 3) + """The connection to the left input boost of the microphone amplifier. + + :default: `False` + """ + + right_mic_boost: bool = WOBit(_REG_ADCR_SIGNAL_PATH, 3) + """The connection to the right input boost of the microphone amplifier. + + :default: `False` + """ + + @property + def mic_boost(self) -> bool: + """The connection to the input boost of the microphone amplifier. + + :default: `False` + """ + return self.left_mic_boost and self.right_mic_boost + + @mic_boost.setter + def mic_boost(self, value: bool) -> None: + self.left_mic_boost = self.right_mic_boost = value + + @staticmethod + def _get_mic_boost_gain(value: float) -> int: + for i in reversed(range(len(_MIC_BOOST_GAIN))): + if value >= _MIC_BOOST_GAIN[i]: + return i + return 0 + + _left_mic_boost_gain: int = WOBits(2, _REG_ADCL_SIGNAL_PATH, 4) + + @property + def left_mic_boost_gain(self) -> float: + """The amount of gain of the left input boost of the microphone amplifier in decibels. + Allowed values are 0.0, 13.0, 20.0, and 29.0. :attr:`WM8960_Advanced.left_mic_boost` must be + set to `True`. + + :default: 0.0 + """ + return _MIC_BOOST_GAIN[self._left_mic_boost_gain] + + @left_mic_boost_gain.setter + def left_mic_boost_gain(self, value: float) -> None: + self._left_mic_boost_gain = WM8960_Advanced._get_mic_boost_gain(value) + + _right_mic_boost_gain: int = WOBits(2, _REG_ADCR_SIGNAL_PATH, 4) + + @property + def right_mic_boost_gain(self) -> float: + """The amount of gain of the right input boost of the microphone amplifier in decibels. + Allowed values are 0.0, 13.0, 20.0, and 29.0. :attr:`WM8960_Advanced.right_mic_boost` must + be set to `True`. + + :default: 0.0 + """ + return _MIC_BOOST_GAIN[self._right_mic_boost_gain] + + @right_mic_boost_gain.setter + def right_mic_boost_gain(self, value: float) -> None: + self._right_mic_boost_gain = WM8960_Advanced._get_mic_boost_gain(value) + + @property + def mic_boost_gain(self) -> float: + """The amount of gain of the input boost of the microphone amplifier in decibels. Allowed + values are 0.0, 13.0, 20.0, and 29.0. :attr:`WM8960_Advanced.mic_boost` must be set to + `True`. + + :default: 0.0 + """ + return max(self.left_mic_boost_gain, self.right_mic_boost_gain) + + @mic_boost_gain.setter + def mic_boost_gain(self, value: float) -> None: + self._left_mic_boost_gain = self._right_mic_boost_gain = ( + WM8960_Advanced._get_mic_boost_gain(value) + ) + + ## Volume + + _left_mic_volume: int = WOBits(6, _REG_LEFT_INPUT_VOLUME, 0) + _left_mic_volume_set: bool = WOBit(_REG_LEFT_INPUT_VOLUME, 8) + + _right_mic_volume: int = WOBits(6, _REG_RIGHT_INPUT_VOLUME, 0) + _right_mic_volume_set: bool = WOBit(_REG_RIGHT_INPUT_VOLUME, 8) + + @property + def left_mic_volume(self) -> float: + """The level of the left microphone amplifier in decibels. Value ranges from a minimum of + -17.25dB (:const:`MIC_GAIN_MIN`) to a maximum of +30.0dB (:const:`MIC_GAIN_MAX`). + :attr:`WM8960_Advanced.left_mic` must be set to `True`. + + :default: 0.0 + """ + return map_range(self._left_mic_volume, 0, 63, MIC_GAIN_MIN, MIC_GAIN_MAX) + + @left_mic_volume.setter + def left_mic_volume(self, value: float) -> None: + self._left_mic_volume = round( + map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63) + ) + self._left_mic_volume_set = True + + @property + def right_mic_volume(self) -> float: + """The level of the right microphone amplifier in decibels. Value ranges from a minimum of + -17.25dB (:const:`MIC_GAIN_MIN`) to a maximum of +30.0dB (:const:`MIC_GAIN_MAX`). + :attr:`WM8960_Advanced.right_mic` must be set to `True`. + + :default: 0.0 + """ + return map_range(self._right_mic_volume, 0, 63, MIC_GAIN_MIN, MIC_GAIN_MAX) + + @right_mic_volume.setter + def right_mic_volume(self, value: float) -> None: + self._right_mic_volume = round( + map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63) + ) + self._right_mic_volume_set = True + + @property + def mic_volume(self) -> float: + """The level of the microphone amplifier in decibels. Value ranges from a minimum of + -17.25dB (:const:`MIC_GAIN_MIN`) to a maximum of +30.0dB (:const:`MIC_GAIN_MAX`). + :attr:`WM8960_Advanced.mic` must be set to `True`. + + :default: 0.0 + """ + return max(self.left_mic_volume, self.right_mic_volume) + + @mic_volume.setter + def mic_volume(self, value: float) -> None: + self._left_mic_volume = self._right_mic_volume = round( + map_range(value, MIC_GAIN_MIN, MIC_GAIN_MAX, 0, 63) + ) + self._left_mic_volume_set = self._right_mic_volume_set = True + + ## Zero Cross + + left_mic_zero_cross: bool = WOBit(_REG_LEFT_INPUT_VOLUME, 6) + """Whether or not the volume of the left microphone amplifier channel will be adjusted when a + "zero" input level is detected in order to avoid harsh jumps in the waveform. + + :default: `False` + """ + + right_mic_zero_cross: bool = WOBit(_REG_RIGHT_INPUT_VOLUME, 6) + """Whether or not the volume of the right microphone amplifier channel will be adjusted when a + "zero" input level is detected in order to avoid harsh jumps in the waveform. + + :default: `False` + """ + + @property + def mic_zero_cross(self) -> bool: + """Whether or not the volume of the microphone amplifier will be adjusted when a "zero" + input level is detected in order to avoid harsh jumps in the waveform. + + :default: `False` + """ + return self.left_mic_zero_cross and self.right_mic_zero_cross + + @mic_zero_cross.setter + def mic_zero_cross(self, value: bool) -> None: + self.left_mic_zero_cross = self.right_mic_zero_cross = value + + ## Mute + + _left_mic_mute: bool = WOBit(_REG_LEFT_INPUT_VOLUME, 7) + _right_mic_mute: bool = WOBit(_REG_RIGHT_INPUT_VOLUME, 7) + + @property + def left_mic_mute(self) -> bool: + """The muted state of the left channel of the microphone amplifier. + + :default: `False` + """ + return self._left_mic_mute + + @left_mic_mute.setter + def left_mic_mute(self, value: bool) -> None: + self._left_mic_mute = value + self._left_mic_volume_set = True + + @property + def right_mic_mute(self) -> bool: + """The muted state of the right channel of the microphone amplifier. + + :default: `False` + """ + return self._right_mic_mute + + @right_mic_mute.setter + def right_mic_mute(self, value: bool) -> None: + self._right_mic_mute = value + self._right_mic_volume_set = True + + @property + def mic_mute(self) -> bool: + """The muted state of the microphone amplifier. + + :default: `False` + """ + return self._left_mic_mute and self._right_mic_mute + + @mic_mute.setter + def mic_mute(self, value: bool) -> None: + self._left_mic_mute = self._right_mic_mute = value + + # Boost Mixer + + _left_input2_boost: int = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 1) + + @property + def left_input2_boost(self) -> float: + """The gain of the left channel of input 2 boost amp into the boost mixer before the ADC in + decibels. Accepts a minimum value of -12.0dB (:const:`BOOST_GAIN_MIN`) and a maximum of + +6.0dB (:const:`BOOST_GAIN_MAX`). In order to mute line-level input 2 into the ADC, set this + value below -12.0dB. If muted, a value of `None` will be returned. + + :default: `None` + """ + value = self._left_input2_boost + if value == 0: + return None + return map_range(value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + + @left_input2_boost.setter + def left_input2_boost(self, value: float) -> None: + self._left_input2_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + ) + + _right_input2_boost: int = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 1) + + @property + def right_input2_boost(self) -> float: + """The gain of the right channel of input 2 boost amp into the boost mixer before the ADC in + decibels. Accepts a minimum value of -12.0dB (:const:`BOOST_GAIN_MIN`) and a maximum of + +6.0dB (:const:`BOOST_GAIN_MAX`). In order to mute line-level input 2 into the ADC, set this + value below -12.0dB. If muted, a value of `None` will be returned. + + :default: `None` + """ + value = self._right_input2_boost + if value == 0: + return None + return map_range(value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + + @right_input2_boost.setter + def right_input2_boost(self, value: float) -> None: + self._right_input2_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + ) + + @property + def input2_boost(self) -> float: + """The gain of both channels of input 2 boost amp into the boost mixer before the ADC in + decibels. Accepts a minimum value of -12.0dB (:const:`BOOST_GAIN_MIN`) and a maximum of + +6.0dB (:const:`BOOST_GAIN_MAX`). In order to mute line-level input 2 into the ADC, set this + value below -12.0dB. If muted, a value of `None` will be returned. + + :default: `None` + """ + value = max(self._left_input2_boost, self._right_input2_boost) + return ( + None + if value == 0 + else map_range(value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + ) + + @input2_boost.setter + def input2_boost(self, value: float) -> None: + self._left_input2_boost = self._right_input2_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + ) + + _left_input3_boost: int = WOBits(3, _REG_INPUT_BOOST_MIXER_1, 4) + + @property + def left_input3_boost(self) -> float: + """The gain of the left channel of input 3 boost amp into the boost mixer before the ADC in + decibels. Accepts a minimum value of -12.0dB (:const:`BOOST_GAIN_MIN`) and a maximum of + +6.0dB (:const:`BOOST_GAIN_MAX`). In order to mute line-level input 3 into the ADC, set this + value below -12.0dB. If muted, a value of `None` will be returned. + + :default: `None` + """ + value = self._left_input3_boost + if value == 0: + return None + return map_range(value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + + @left_input3_boost.setter + def left_input3_boost(self, value: float) -> None: + self._left_input3_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + ) + + _right_input3_boost: int = WOBits(3, _REG_INPUT_BOOST_MIXER_2, 4) + + @property + def right_input3_boost(self) -> float: + """The gain of the right channel of input 3 boost amp into the boost mixer before the ADC in + decibels. Accepts a minimum value of -12.0dB (:const:`BOOST_GAIN_MIN`) and a maximum of + +6.0dB (:const:`BOOST_GAIN_MAX`). In order to mute line-level input 3 into the ADC, set this + value below -12.0dB. If muted, a value of `None` will be returned. + + :default: `None` + """ + value = self._right_input3_boost + if value == 0: + return None + return map_range(value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + + @right_input3_boost.setter + def right_input3_boost(self, value: float) -> None: + self._right_input3_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + ) + + @property + def input3_boost(self) -> float: + """The gain of both channels of input 3 boost amp into the boost mixer before the ADC in + decibels. Accepts a minimum value of -12.0dB (:const:`BOOST_GAIN_MIN`) and a maximum of + +6.0dB (:const:`BOOST_GAIN_MAX`). In order to mute line-level input 3 into the ADC, set this + value below -12.0dB. If muted, a value of `None` will be returned. + + :default: `None` + """ + value = max(self._left_input3_boost, self._right_input3_boost) + return ( + None + if value == 0 + else map_range(value, 1, 7, BOOST_GAIN_MIN, BOOST_GAIN_MAX) + ) + + @input3_boost.setter + def input3_boost(self, value: float) -> None: + self._left_input3_boost = self._right_input3_boost = ( + 0 + if value < BOOST_GAIN_MIN + else round(map_range(value, BOOST_GAIN_MIN, BOOST_GAIN_MAX, 1, 7)) + ) + + # Mic Bias + + mic_bias: bool = WOBit(_REG_PWR_MGMT_1, 1) + """Whether or not the WM8960 is outputting a reference voltage on the MICBIAS pin for biasing + electret type microphones. + + :default: `False` + """ + + mic_bias_voltage: bool = WOBit(_REG_ADDITIONAL_CONTROL_4, 0) + """The electret type microphone bias mode. :attr:`WM8960.mic_bias` must be set to `True` to + utilize this setting. + `False` = the reference voltage is 0.9 * AVDD or 1.8 * VMID + `True` = the reference voltage is 0.65 * AVDD or 1.3 * VMID + + :default: `False` + """ + + # ADC + + left_adc: bool = WOBit(_REG_PWR_MGMT_1, 3) + """Whether or not the left channel of the ADC is enabled. + + :default: `False` + """ + + right_adc: bool = WOBit(_REG_PWR_MGMT_1, 2) + """Whether or not the right channel of the ADC is enabled. + + :default: `False` + """ + + @property + def adc(self) -> bool: + """Whether or not the both channels of the ADC are enabled. + + :default: `False` + """ + return self.left_adc and self.right_adc + + @adc.setter + def adc(self, value: bool) -> None: + self.left_adc = self.right_adc = value + + ## Volume + + _left_adc_volume: int = WOBits(8, _REG_LEFT_ADC_VOLUME, 0) + _left_adc_volume_set: bool = WOBit(_REG_LEFT_ADC_VOLUME, 8) + + @property + def left_adc_volume(self) -> float: + """The digital volume of the left channel ADC in decibels. Accepts a minimum value of + -97.0dB (:const:`ADC_VOLUME_MIN`) and a maximum of +30.0dB (:const:`ADC_VOLUME_MAX`). In + order to digitally mute the left channel of the ADC, set this value below -97.0dB. If muted, + a value of `None` will be returned. + + :default: `None` + """ + value = self._left_adc_volume + if value == 0: + return None + return map_range(value, 1, 255, ADC_VOLUME_MIN, ADC_VOLUME_MAX) + + @left_adc_volume.setter + def left_adc_volume(self, value: float) -> None: + self._left_adc_volume = ( + 0 + if value < ADC_VOLUME_MIN + else round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 1, 255)) + ) + self._left_adc_volume_set = True + + _right_adc_volume: int = WOBits(8, _REG_RIGHT_ADC_VOLUME, 0) + _right_adc_volume_set: bool = WOBit(_REG_RIGHT_ADC_VOLUME, 8) + + @property + def right_adc_volume(self) -> float: + """The digital volume of the right channel ADC in decibels. Accepts a minimum value of + -97.0dB (:const:`ADC_VOLUME_MIN`) and a maximum of +30.0dB (:const:`ADC_VOLUME_MAX`). In + order to digitally mute the right channel of the ADC, set this value below -97.0dB. If + muted, a value of `None` will be returned. + + :default: `None` + """ + value = self._right_adc_volume + if value == 0: + return None + return map_range(value, 1, 255, ADC_VOLUME_MIN, ADC_VOLUME_MAX) + + @right_adc_volume.setter + def right_adc_volume(self, value: float) -> None: + self._right_adc_volume = ( + 0 + if value < ADC_VOLUME_MIN + else round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 1, 255)) + ) + self._right_adc_volume_set = True + + @property + def adc_volume(self) -> float: + """The digital volume of both channels of the ADC in decibels. Accepts a minimum value of + -97.0dB (:const:`ADC_VOLUME_MIN`) and a maximum of +30.0dB (:const:`ADC_VOLUME_MAX`). In + order to digitally mute the ADC, set this value below -97.0dB. If muted, a value of `None` + will be returned. + + :default: `None` + """ + return max(self.left_adc_volume, self.right_adc_volume) + + @adc_volume.setter + def adc_volume(self, value: float) -> None: + self._left_adc_volume = self._right_adc_volume = ( + 0 + if value < ADC_VOLUME_MIN + else round(map_range(value, ADC_VOLUME_MIN, ADC_VOLUME_MAX, 1, 255)) + ) + self._left_adc_volume_set = self._right_adc_volume_set = True + + # ALC + + left_alc: bool = WOBit(_REG_ALC1, 7) + """Whether or not the left channel of the automatic level control (ALC) is enabled. + + :default: `False` + """ + + right_alc: bool = WOBit(_REG_ALC1, 8) + """Whether or not the right channel of the automatic level control (ALC) is enabled. + + :default: `False` + """ + + @property + def alc(self) -> bool: + """Whether or not both channels of the automatic level control (ALC) are enabled. The ALC + will automatically adjust the gain of incoming signal into the microphone amplifier. + + :default: `False` + """ + return self.left_alc and self.right_alc + + @alc.setter + def alc(self, value: bool) -> None: + self.left_alc = self.right_alc = value + + _alc_target: int = WOBits(4, _REG_ALC1, 0) + + @property + def alc_target(self) -> float: + """The target level of the input signal out of the microphone amplifier as controlled by the + ALC in decibels. Accepts a value between -22.5dB (:const:`ALC_TARGET_MIN`) and +6.0dB + (:const:`ALC_TARGET_MAX`). + + :default: `False` + """ + return map_range(self._alc_target, 0, 15, ALC_TARGET_MIN, ALC_TARGET_MAX) + + @alc_target.setter + def alc_target(self, value: float) -> None: + self._alc_target = round( + map_range(value, ALC_TARGET_MIN, ALC_TARGET_MAX, 0, 15) + ) + + _alc_max_gain: int = WOBits(3, _REG_ALC1, 4) + + @property + def alc_max_gain(self) -> float: + """The maximum potential gain of the ALC to be applied to the microphone amplifier in + decibels. Accepts a value between -12.0dB (:const:`ALC_MAX_GAIN_MIN`) and +30.0dB + (:const:`ALC_MAX_GAIN_MAX`). + + :default: 30.0 + """ + return map_range(self._alc_max_gain, 0, 7, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX) + + @alc_max_gain.setter + def alc_max_gain(self, value: float) -> None: + self._alc_max_gain = round( + map_range(value, ALC_MAX_GAIN_MIN, ALC_MAX_GAIN_MAX, 0, 7) + ) + + _alc_min_gain: int = WOBits(3, _REG_ALC2, 4) + + @property + def alc_min_gain(self) -> float: + """The minimum allowed gain of the ALC to be applied to the microphone amplifier in + decibels. Accepts a value between -17.25dB (:const:`ALC_MIN_GAIN_MIN`) and +24.75dB + (:const:`ALC_MIN_GAIN_MAX`). + + :default: -17.25 + """ + return map_range(self._alc_min_gain, 0, 7, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX) + + @alc_min_gain.setter + def alc_min_gain(self, value: float) -> None: + self._alc_min_gain = round( + map_range(value, ALC_MIN_GAIN_MIN, ALC_MIN_GAIN_MAX, 0, 7) + ) + + _alc_hold: int = WOBits(4, _REG_ALC2, 0) + + @property + def alc_hold_time(self) -> float: + """The time delay between the peak level detected being below target and the microphone + amplifier gain beginning to ramp up. However, there is no delay before ramping the gain down + when the signal level is above target. Accepts a minimum value of 0.00267s + (:const:`ALC_HOLD_TIME_MIN`) increasing exponentially to a maximum of 43.691s + (:const:`ALC_HOLD_TIME_MAX`). This value can also be set to 0. + + :default: 30.0 + """ + value = self._alc_hold + if value == 0: + return 0.0 + return ALC_HOLD_TIME_MIN * pow(2, value - 1) + + @alc_hold_time.setter + def alc_hold_time(self, value: float) -> None: + self._alc_hold = ( + round( + math.log2( + ( + constrain(value, ALC_HOLD_TIME_MIN, ALC_HOLD_TIME_MAX) + - ALC_HOLD_TIME_MIN + ) + / ALC_HOLD_TIME_MIN + ) + + 1.0 + ) + if value > 0.0 + else 0 + ) + + _alc_decay: int = WOBits(4, _REG_ALC3, 4) + + @property + def alc_decay_time(self) -> float: + """The time that it takes for the microphone amplifier gain to ramp up and return to its + target value, :attr:`alc_target`. This time is relative depending on the gain adjustment + required. Accepts a minimum value of 0.024s (:const:`ALC_HOLD_TIME_MIN`) increasing + exponentially to a maximum of 43.691s (:const:`ALC_HOLD_TIME_MAX`). + + :default: 0.024 + """ + return ALC_DECAY_TIME_MIN * pow(2, self._alc_decay) + + @alc_decay_time.setter + def alc_decay_time(self, value: float) -> None: + self._alc_decay = min( + round( + math.log2( + ( + constrain(value, ALC_DECAY_TIME_MIN, ALC_DECAY_TIME_MAX) + - ALC_DECAY_TIME_MIN + ) + / ALC_DECAY_TIME_MIN + ) + ), + _ALC_DECAY_MAX, + ) + + _alc_attack: int = WOBits(4, _REG_ALC3, 0) + + @property + def alc_attack_time(self) -> float: + """The time that it takes for the microphone amplifier gain to ramp down and adjust the + incoming signal to its target level, :attr:`alc_target`. This time is relative depending on + the gain adjustment required. Accepts a minimum value of 0.006s (:const:`ALC_HOLD_TIME_MIN`) + increasing exponentially to a maximum of 6.14s (:const:`ALC_HOLD_TIME_MAX`). + + :default: 0.024 + """ + return ALC_ATTACK_TIME_MIN * pow(2, self._alc_attack) + + @alc_attack_time.setter + def alc_attack_time(self, value: float) -> None: + self._alc_attack = min( + round( + math.log2( + ( + constrain(value, ALC_ATTACK_TIME_MIN, ALC_ATTACK_TIME_MAX) + - ALC_ATTACK_TIME_MIN + ) + / ALC_ATTACK_TIME_MIN + ) + ), + _ALC_ATTACK_MAX, + ) + + alc_limiter: bool = WOBit(_REG_ALC3, 8) + """Whether or not the ALC limiter is enabled to prevent clipping. This function is automatically + enabled whenever :attr:`alc` is set to `True`. + + :default: `False` + """ + + # Noise Gate + + noise_gate: bool = WOBit(_REG_NOISE_GATE, 0) + """Whether or not the noise gate is enabled. The ALC, :attr:`alc`, must be set to `True` for + this functionality to work. This feature will help prevent "noise pumping" during periods of + quiet input signal. Only applicable to signal into the microphone amplifier. + + :default: `False` + """ + + _noise_gate_threshold: int = WOBits(5, _REG_NOISE_GATE, 3) + + @property + def noise_gate_threshold(self) -> float: + """The input signal level below at which to engage the noise gate in decibels. + + :default: -76.5 + """ + return map_range( + self._noise_gate_threshold, 0, 31, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX + ) + + @noise_gate_threshold.setter + def noise_gate_threshold(self, value: float) -> None: + self._noise_gate_threshold = round( + map_range(value, GATE_THRESHOLD_MIN, GATE_THRESHOLD_MAX, 0, 31) + ) + + # DAC + + left_dac: bool = WOBit(_REG_PWR_MGMT_2, 8) + """Whether or not the left channel of the DAC is enabled. + + :default: `False` + """ + + right_dac: bool = WOBit(_REG_PWR_MGMT_2, 7) + """Whether or not the right channel of the DAC is enabled. + + :default: `False` + """ + + @property + def dac(self) -> bool: + """Whether or not the boths channels of the DAC are enabled. + + :default: `False` + """ + return self.left_dac and self.right_dac + + @dac.setter + def dac(self, value: bool) -> None: + self.left_dac = self.right_dac = value + + _left_dac_volume: int = WOBits(8, _REG_LEFT_DAC_VOLUME, 0) + _left_dac_volume_set: bool = WOBit(_REG_LEFT_DAC_VOLUME, 8) + + @property + def left_dac_volume(self) -> float: + """The digital volume of the left channel DAC in decibels. Accepts a minimum value of + -127.0dB (:const:`DAC_VOLUME_MIN`) and a maximum of +0.0dB (:const:`DAC_VOLUME_MAX`). + + :default: 0.0 + """ + return map_range(self._left_dac_volume, 1, 255, DAC_VOLUME_MIN, DAC_VOLUME_MAX) + + @left_dac_volume.setter + def left_dac_volume(self, value: float) -> None: + self._left_dac_volume = round( + map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 1, 255) + ) + self._left_dac_volume_set = True + + _right_dac_volume: int = WOBits(8, _REG_RIGHT_DAC_VOLUME, 0) + _right_dac_volume_set: bool = WOBit(_REG_RIGHT_DAC_VOLUME, 8) + + @property + def right_dac_volume(self) -> float: + """The digital volume of the right channel DAC in decibels. Accepts a minimum value of + -127.0dB (:const:`DAC_VOLUME_MIN`) and a maximum of +0.0dB (:const:`DAC_VOLUME_MAX`). + + :default: 0.0 + """ + return map_range(self._right_dac_volume, 1, 255, DAC_VOLUME_MIN, DAC_VOLUME_MAX) + + @right_dac_volume.setter + def right_dac_volume(self, value: float) -> None: + self._right_dac_volume = round( + map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 1, 255) + ) + self._right_dac_volume_set = True + + @property + def dac_volume(self) -> float: + """The digital volume of both channels of the DAC in decibels. Accepts a minimum value of + -127.0dB (:const:`DAC_VOLUME_MIN`) and a maximum of +0.0dB (:const:`DAC_VOLUME_MAX`). + + :default: 0.0 + """ + return max(self.left_dac_volume, self.right_dac_volume) + + @dac_volume.setter + def dac_volume(self, value: float) -> None: + self._left_dac_volume = self._right_dac_volume = round( + map_range(value, DAC_VOLUME_MIN, DAC_VOLUME_MAX, 1, 255) + ) + self._left_dac_volume_set = self._right_dac_volume_set = True + + dac_mute: bool = WOBit(_REG_ADC_DAC_CTRL_1, 3) + """Whether or not the DAC is soft-muted. By default, this feature is enabled. To play back an + audio signal from the DAC, this must first set to `False`. + + :default: `True` + """ + + dac_soft_mute: bool = WOBit(_REG_ADC_DAC_CTRL_2, 3) + """Prevents a sudden volume increase when disabling :attr:`dac_mute` during playback to avoid + creating a popping noise. + + :default: `False` + """ + + dac_slow_soft_mute: bool = WOBit(_REG_ADC_DAC_CTRL_2, 2) + """The rate at which the DAC soft mute, :attr:`dac_mute`, ramps up or down. When set to `False`, + it takes roughly 10.7ms to mute or unmute (when :attr:`sample_rate` is set to 48000). When set + to `True`, it takes roughly 171ms. + + :default: `False` + """ + + dac_attenuation: bool = WOBit(_REG_ADC_DAC_CTRL_1, 7) + """When set to `True`, the signal of the DAC will be attenuated by -6dB. This is commonly used + when :attr:`enhance` is set to `True` to avoid limiting. + + :default: `False` + """ + + # 3D Enhance + + enhance: bool = WOBit(_REG_3D_CONTROL, 0) + """Whether or not the digital 3D stereo enhancement feature on the DAC is enabled. This option + will artificially increase the separation between the left and right channels. + + :default: `False` + """ + + enhance_filter_lpf: bool = WOBit(_REG_3D_CONTROL, 6) + """Whether or not a low-pass filter is applied before 3D enhancement processing. Recommended + when :attr:`sample_rate` is less than 32000. + + :default: `False` + """ + + enhance_filter_hpf: bool = WOBit(_REG_3D_CONTROL, 5) + """Whether or not a high-pass filter is applied before 3D enhancement processing. Recommended + when :attr:`sample_rate` is less than 32000. + + :default: `False` + """ + + _enhance_depth: int = WOBits(4, _REG_3D_CONTROL, 1) + + @property + def enhance_depth(self) -> float: + """The depth of the 3D stereo enhancement effect. Accepts a monotonic value of 0.0 to 1.0. + + :default: 0.0 + """ + return self._enhance_depth / 15.0 + + @enhance_depth.setter + def enhance_depth(self, value: float) -> None: + self._enhance_depth = round(map_range(value, 0.0, 1.0, 0, 15)) + + # Output Mixer + + left_output: bool = WOBit(_REG_PWR_MGMT_3, 3) + """Whether or not the left channel of the output mixer is enabled. + + :default: `False` + """ + + right_output: bool = WOBit(_REG_PWR_MGMT_3, 2) + """Whether or not the right channel of the output mixer is enabled. + + :default: `False` + """ + + @property + def output(self) -> bool: + """Whether or not both channels of the output mixer are enabled. + + :default: `False` + """ + return self.left_output and self.right_output + + @output.setter + def output(self, value: bool) -> None: + self.left_output = self.right_output = value + + ## DAC Output + + left_dac_output: bool = WOBit(_REG_LEFT_OUT_MIX, 8) + """Whether or not the left channel of the DAC is connected to the left channel of the output + mixer. + + :default: `False` + """ + + right_dac_output: bool = WOBit(_REG_RIGHT_OUT_MIX, 8) + """Whether or not the right channel of the DAC is connected to the right channel of the output + mixer. + + :default: `False` + """ + + @property + def dac_output(self) -> bool: + """Whether or not both channels of the DAC are connected to the output mixer. Required for + playback of the DAC through the headphone or speaker amplifier. + + :default: `False` + """ + return self.left_dac_output and self.right_dac_output + + @dac_output.setter + def dac_output(self, value: bool) -> None: + self.left_dac_output = self.right_dac_output = value + + ## Input 3 Output + + left_input3_output: bool = WOBit(_REG_LEFT_OUT_MIX, 7) + """The connection of the left channel of input 3 directly to the output mixer, bypassing the + ADC. + + :default: `False` + """ + + right_input3_output: bool = WOBit(_REG_RIGHT_OUT_MIX, 7) + """The connection of the right channel of input 3 directly to the output mixer, bypassing the + ADC. + + :default: `False` + """ + + @property + def input3_output(self) -> bool: + """The connection of both channels of input 3 directly to the output mixer, bypassing the + ADC. + + :default: `False` + """ + return self.left_input3_output and self.right_input3_output + + @input3_output.setter + def input3_output(self, value: bool) -> None: + self.left_input3_output = self.right_input3_output = value + + _left_input3_output_volume: int = WOBits(3, _REG_LEFT_OUT_MIX, 4) + + @property + def left_input3_output_volume(self) -> float: + """The level of the left channel of input 3 into the output mixer in decibels. Accepts a + value from -21.0dB (:const:`OUTPUT_VOLUME_MIN`) to +0.0dB (:const:`OUTPUT_VOLUME_MAX`). + + :default: -21.0 + """ + return map_range( + self._left_input3_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + + @left_input3_output_volume.setter + def left_input3_output_volume(self, value: float) -> None: + self._left_input3_output_volume = round( + map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0) + ) + + _right_input3_output_volume: int = WOBits(3, _REG_RIGHT_OUT_MIX, 4) + + @property + def right_input3_output_volume(self) -> float: + """The level of the right channel of input 3 into the output mixer in decibels. Accepts a + value from -21.0dB (:const:`OUTPUT_VOLUME_MIN`) to +0.0dB (:const:`OUTPUT_VOLUME_MAX`). + + :default: -21.0 + """ + return map_range( + self._right_input3_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + + @right_input3_output_volume.setter + def right_input3_output_volume(self, value: float) -> None: + self._right_input3_output_volume = round( + map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0) + ) + + @property + def input3_output_volume(self) -> float: + """The level of both channels of input 3 into the output mixer in decibels. Accepts a value + from -21.0dB (:const:`OUTPUT_VOLUME_MIN`) to +0.0dB (:const:`OUTPUT_VOLUME_MAX`). + + :default: -21.0 + """ + return max(self.left_input3_output_volume, self.right_input3_output_volume) + + @input3_output_volume.setter + def input3_output_volume(self, value: float) -> None: + self._left_input3_output_volume = self._right_input3_output_volume = round( + map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0) + ) + + ## MIC Boost Mixer Output + + left_mic_output: bool = WOBit(_REG_BYPASS_1, 7) + """The connection of the left channel of the microphone amplifier directly to the output mixer, + bypassing the ADC. + + :default: `False` + """ + + right_mic_output: bool = WOBit(_REG_BYPASS_2, 7) + """The connection of the right channel of the microphone amplifier directly to the output mixer, + bypassing the ADC. + + :default: `False` + """ + + @property + def mic_output(self) -> bool: + """The connection of both channels of the microphone amplifier directly to the output mixer, + bypassing the ADC. + + :default: `False` + """ + return self.left_mic_output and self.right_mic_output + + @mic_output.setter + def mic_output(self, value: bool) -> None: + self.left_mic_output = self.right_mic_output = value + + _left_mic_output_volume: int = WOBits(3, _REG_BYPASS_1, 4) + + @property + def left_mic_output_volume(self) -> float: + """The level of the left channel of the microphone amplifier into the output mixer in + decibels. Accepts a value from -21.0dB (:const:`OUTPUT_VOLUME_MIN`) to +0.0dB + (:const:`OUTPUT_VOLUME_MAX`). + + :default: -21.0 + """ + return map_range( + self._left_mic_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + + @left_mic_output_volume.setter + def left_mic_output_volume(self, value: float) -> None: + self._left_mic_output_volume = round( + map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0) + ) + + _right_mic_output_volume: int = WOBits(3, _REG_BYPASS_2, 4) + + @property + def right_mic_output_volume(self) -> float: + """The level of the right channel of the microphone amplifier into the output mixer in + decibels. Accepts a value from -21.0dB (:const:`OUTPUT_VOLUME_MIN`) to +0.0dB + (:const:`OUTPUT_VOLUME_MAX`). + + :default: -21.0 + """ + return map_range( + self._right_mic_output_volume, 0, 7, OUTPUT_VOLUME_MAX, OUTPUT_VOLUME_MIN + ) + + @right_mic_output_volume.setter + def right_mic_output_volume(self, value: float) -> None: + self._right_mic_output_volume = round( + map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0) + ) + + @property + def mic_output_volume(self) -> float: + """The level of both channels of the microphone amplifier into the output mixer in decibels. + Accepts a value from -21.0dB (:const:`OUTPUT_VOLUME_MIN`) to +0.0dB + (:const:`OUTPUT_VOLUME_MAX`). + + :default: -21.0 + """ + return max(self.left_mic_output_volume, self.right_mic_output_volume) + + @mic_output_volume.setter + def mic_output_volume(self, value: float) -> None: + self._left_mic_output_volume = self._right_mic_output_volume = round( + map_range(value, OUTPUT_VOLUME_MIN, OUTPUT_VOLUME_MAX, 7, 0) + ) + + ## Mono Output + + mono_output: bool = WOBit(_REG_PWR_MGMT_2, 1) + """Whether or not the mono output mixer is enabled. This can be used to provide a mono mix of + the left and right channels of the output mixer (if :attr:`mono_left_mix` or + :attr:`mono_right_mix` is set to `True`) or as a buffer for the headphone amplifier to allow + capless headphone output. + + :default: `False` + """ + + mono_left_mix: bool = WOBit(_REG_MONO_OUT_MIX_1, 7) + """Whether or not the left channel of the output mixer is connected to the mono output. + + :default: `False` + """ + + mono_right_mix: bool = WOBit(_REG_MONO_OUT_MIX_2, 7) + """Whether or not the right channel of the output mixer is connected to the mono output. + + :default: `False` + """ + + @property + def mono_mix(self) -> bool: + """Whether or not both channels of the output mixer are connected to the mono output. + + :default: `False` + """ + return self.mono_left_mix and self.mono_right_mix + + @mono_mix.setter + def mono_mix(self, value: bool) -> None: + self.mono_left_mix = self.mono_right_mix = value + + mono_output_attenuation: bool = WOBit(_REG_MONO_OUT_VOLUME, 6) + """When set to `True`, the signal of the mono output will be attenuated by -6dB. This can help + prevent clipping. This property should not be modified when :attr:`mono_output` is set to `True` + as this may cause an audible click noise. + + :default: `False` + """ + + # Amplifier + + ## Headphones + + left_headphone: bool = WOBit(_REG_PWR_MGMT_2, 5) + """Whether or not the left channel of the headphone amplifier is powered on. + + :default: `False` + """ + + right_headphone: bool = WOBit(_REG_PWR_MGMT_2, 6) + """Whether or not the right channel of the headphone amplifier is powered on. + + :default: `False` + """ + + @property + def headphone(self) -> bool: + """Whether or not the headphone amplifier is powered on. + + :default: `False` + """ + return self.left_headphone and self.right_headphone + + @headphone.setter + def headphone(self, value: bool) -> None: + self.left_headphone = self.right_headphone = value + + headphone_standby: bool = WOBit(_REG_ANTI_POP_1, 0) + """Headphone amplifier standby mode. + + :default: `False` + """ + + _left_headphone_volume: int = WOBits(7, _REG_LOUT1_VOLUME, 0) + _left_headphone_volume_set: bool = WOBit(_REG_LOUT1_VOLUME, 8) + + @property + def left_headphone_volume(self) -> float: + """The volume level of the left channel of the headphone amplifier in decibels. Accepts a + minimum value of -73.0dB (:const:`AMP_VOLUME_MIN`) and a maximum of +6.0dB + (:const:`AMP_VOLUME_MAX`). If set to a value less than the minimum (-73.0dB), the left + channel will be muted and this property will return a value of `None`. + + :default: `None` + """ + value = self._left_headphone_volume + if value < 48: + return None + return map_range(value, 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + + @left_headphone_volume.setter + def left_headphone_volume(self, value: float) -> None: + self._left_headphone_volume = ( + round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + if value >= AMP_VOLUME_MIN + else 0 + ) + self._left_headphone_volume_set = True + + _right_headphone_volume: int = WOBits(7, _REG_ROUT1_VOLUME, 0) + _right_headphone_volume_set: bool = WOBit(_REG_ROUT1_VOLUME, 8) + + @property + def right_headphone_volume(self) -> float: + """The volume level of the right channel of the headphone amplifier in decibels. Accepts a + minimum value of -73.0dB (:const:`AMP_VOLUME_MIN`) and a maximum of +6.0dB + (:const:`AMP_VOLUME_MAX`). If set to a value less than the minimum (-73.0dB), the right + channel will be muted and this property will return a value of `None`. + + :default: `None` + """ + value = self._right_headphone_volume + if value < 48: + return None + return map_range(value, 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + + @right_headphone_volume.setter + def right_headphone_volume(self, value: float) -> None: + self._right_headphone_volume = ( + round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + if value >= AMP_VOLUME_MIN + else 0 + ) + self._right_headphone_volume_set = True + + @property + def headphone_volume(self) -> float: + """The volume level of both channels of the headphone amplifier in decibels. Accepts a + minimum value of -73.0dB (:const:`AMP_VOLUME_MIN`) and a maximum of +6.0dB + (:const:`AMP_VOLUME_MAX`). If set to a value less than the minimum (-73.0dB), the amplifier + will be muted and this property will return a value of `None`. + + :default: `None` + """ + left = self.left_headphone_volume + right = self.right_headphone_volume + if left is None or right is None: + if left is None and right is None: + return None + return right if left is None else left + return max(left, right) + + @headphone_volume.setter + def headphone_volume(self, value: float) -> None: + self._left_headphone_volume = self._right_headphone_volume = ( + round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + if value >= AMP_VOLUME_MIN + else 0 + ) + self._left_headphone_volume_set = self._right_headphone_volume_set = True + + left_headphone_zero_cross: bool = WOBit(_REG_LOUT1_VOLUME, 7) + """Whether or not the volume of the left headphone amplifier channel will be adjusted when a + "zero" input level is detected in order to avoid harsh jumps in the output. + + :default: `False` + """ + + right_headphone_zero_cross: bool = WOBit(_REG_LOUT1_VOLUME, 7) + """Whether or not the volume of the right headphone amplifier channel will be adjusted when a + "zero" input level is detected in order to avoid harsh jumps in the output. + + :default: `False` + """ + + @property + def headphone_zero_cross(self) -> bool: + """Whether or not the volume of the headphone amplifier channel will be adjusted when a + "zero" input level is detected in order to avoid harsh jumps in the output. + + :default: `False` + """ + return self.left_headphone_zero_cross and self.right_headphone_zero_cross + + @headphone_zero_cross.setter + def headphone_zero_cross(self, value: bool) -> None: + self.left_headphone_zero_cross = self.right_headphone_zero_cross = value + + ## Speakers + + _left_speaker: bool = WOBit(_REG_PWR_MGMT_2, 4) + _left_speaker_amp: bool = WOBit(_REG_CLASS_D_CONTROL_1, 6) + + @property + def left_speaker(self) -> bool: + """Whether or not the left channel of the speaker amplifier is powered on. + + :default: `False` + """ + return self._left_speaker and self._left_speaker_amp + + @left_speaker.setter + def left_speaker(self, value: bool) -> None: + self._left_speaker = self._left_speaker_amp = value + + _right_speaker: bool = WOBit(_REG_PWR_MGMT_2, 3) + _right_speaker_amp: bool = WOBit(_REG_CLASS_D_CONTROL_1, 7) + + @property + def right_speaker(self) -> bool: + """Whether or not the right channel of the speaker amplifier is powered on. + + :default: `False` + """ + return self._right_speaker and self._right_speaker_amp + + @right_speaker.setter + def right_speaker(self, value: bool) -> None: + self._right_speaker = self._right_speaker_amp = value + + @property + def speaker(self) -> bool: + """Whether or not the speaker amplifier is powered on. + + :default: `False` + """ + return self.left_speaker and self.right_speaker + + @speaker.setter + def speaker(self, value: bool) -> None: + self.left_speaker = self.right_speaker = value + + _left_speaker_volume: int = WOBits(7, _REG_LOUT2_VOLUME, 0) + _left_speaker_volume_set: bool = WOBit(_REG_LOUT2_VOLUME, 8) + + @property + def left_speaker_volume(self) -> float: + """The volume level of the left channel of the speaker amplifier in decibels. Accepts a + minimum value of -73.0dB (:const:`AMP_VOLUME_MIN`) and a maximum of +6.0dB + (:const:`AMP_VOLUME_MAX`). If set to a value less than the minimum (-73.0dB), the left + channel will be muted and this property will return a value of `None`. + + :default: `None` + """ + value = self._left_speaker_volume + if value < 48: + return None + return map_range(value, 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + + @left_speaker_volume.setter + def left_speaker_volume(self, value: float) -> None: + self._left_speaker_volume = ( + round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + if value >= AMP_VOLUME_MIN + else 0 + ) + self._left_speaker_set = True + + _right_speaker_volume: int = WOBits(7, _REG_ROUT2_VOLUME, 0) + _right_speaker_volume_set: bool = WOBit(_REG_ROUT2_VOLUME, 8) + + @property + def right_speaker_volume(self) -> float: + """The volume level of the right channel of the speaker amplifier in decibels. Accepts a + minimum value of -73.0dB (:const:`AMP_VOLUME_MIN`) and a maximum of +6.0dB + (:const:`AMP_VOLUME_MAX`). If set to a value less than the minimum (-73.0dB), the right + channel will be muted and this property will return a value of `None`. + + :default: `None` + """ + value = self._right_speaker_volume + if value < 48: + return None + return map_range(value, 48, 127, AMP_VOLUME_MIN, AMP_VOLUME_MAX) + + @right_speaker_volume.setter + def right_speaker_volume(self, value: float) -> None: + self._right_speaker_volume = ( + round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + if value >= AMP_VOLUME_MIN + else 0 + ) + self._right_speaker_volume_set = True + + @property + def speaker_volume(self) -> float: + """The volume level of the speaker amplifier in decibels. Accepts a minimum value of -73.0dB + (:const:`AMP_VOLUME_MIN`) and a maximum of +6.0dB (:const:`AMP_VOLUME_MAX`). If set to a + value less than the minimum (-73.0dB), the amplifier will be muted and this property will + return a value of `None`. + + :default: `None` + """ + left = self.left_speaker_volume + right = self.right_speaker_volume + if left is None or right is None: + if left is None and right is None: + return None + return right if left is None else left + return max(left, right) + + @speaker_volume.setter + def speaker_volume(self, value: float) -> None: + self._left_speaker_volume = self._right_speaker_volume = ( + round(map_range(value, AMP_VOLUME_MIN, AMP_VOLUME_MAX, 48, 127)) + if value >= AMP_VOLUME_MIN + else 0 + ) + self._left_speaker_volume_set = self._right_speaker_volume_set = True + + left_speaker_zero_cross: bool = WOBit(_REG_LOUT2_VOLUME, 7) + """Whether or not the volume of the left speaker amplifier channel will be adjusted when a + "zero" input level is detected in order to avoid harsh jumps in the output. + + :default: `False` + """ + + right_speaker_zero_cross: bool = WOBit(_REG_LOUT2_VOLUME, 7) + """Whether or not the volume of the right speaker amplifier channel will be adjusted when a + "zero" input level is detected in order to avoid harsh jumps in the output. + + :default: `False` + """ + + @property + def speaker_zero_cross(self) -> bool: + """Whether or not the volume of the speaker amplifier will be adjusted when a "zero" input + level is detected in order to avoid harsh jumps in the output. + + :default: `False` + """ + return self.left_speaker_zero_cross and self.right_speaker_zero_cross + + @speaker_zero_cross.setter + def speaker_zero_cross(self, value: bool) -> None: + self.left_speaker_zero_cross = self.right_speaker_zero_cross = value + + @staticmethod + def _get_speaker_boost_gain(value: float) -> int: + for i in reversed(range(len(_SPEAKER_BOOST_GAIN))): + if value >= _SPEAKER_BOOST_GAIN[i]: + return i + return 0 + + _speaker_dc_gain: int = WOBits(3, _REG_CLASS_D_CONTROL_3, 3) + + @property + def speaker_dc_gain(self) -> float: + """Speaker DC output level boost on both left and right channels in decibels. Accepts values + of +0.0dB, +2.1dB, +2.9dB, +3.6dB, +4.5dB, and +5.1dB. + + :default: 0.0 + """ + return _SPEAKER_BOOST_GAIN[self._speaker_dc_gain] + + @speaker_dc_gain.setter + def speaker_dc_gain(self, value: float) -> None: + self._speaker_dc_gain = WM8960_Advanced._get_speaker_boost_gain(value) + + _speaker_ac_gain: int = WOBits(3, _REG_CLASS_D_CONTROL_3, 0) + + @property + def speaker_ac_gain(self) -> float: + """Speaker AC output level boost on both left and right channels in decibels. Accepts values + of +0.0dB, +2.1dB, +2.9dB, +3.6dB, +4.5dB, and +5.1dB. + + :default: 0.0 + """ + return _SPEAKER_BOOST_GAIN[self._speaker_ac_gain] + + @speaker_ac_gain.setter + def speaker_ac_gain(self, value: float) -> None: + self._speaker_ac_gain = WM8960_Advanced._get_speaker_boost_gain(value) + + # Digital Audio Interface Control + + loopback: bool = WOBit(_REG_AUDIO_INTERFACE_2, 0) + """Whether or not digital loopback between the ADC and DAC is enabled. + + :default: `False` + """ + + pll: bool = WOBit(_REG_PWR_MGMT_2, 0) + """Whether or not the phase-locked loop (PLL) clock is powered on. + + :default: `False` + """ + + pll_prescale_div2: bool = WOBit(_REG_PWR_MGMT_2, 4) + """Divide MCLK by 2 before input to PLL. + + :default: `False` + """ + + pll_n: int = WOBits(4, _REG_PLL_N, 0) + """The intenger (N) part of PLL input/output ratio. Accepts a value greater than 5 and less than + 13. + + :default: 8 + """ + + _pll_k1: int = WOBits(6, _REG_PLL_K_1, 0) + _pll_k2: int = WOBits(9, _REG_PLL_K_2, 0) + _pll_k3: int = WOBits(9, _REG_PLL_K_3, 0) + + @property + def pll_k(self) -> int: + """The fractional (K) part of PLL input/output ratio. Accepts a 24-bit unsigned integer. + + :default: 0x3126E9 + """ + return self._pll_k1 << 18 + self._pll_k2 << 9 + self._pll_k3 + + @pll_k.setter + def pll_k(self, value: int) -> None: + self._pll_k1 = (value >> 18) & 0b111111 + self._pll_k2 = (value >> 9) & 0b111111111 + self._pll_k3 = value & 0b111111111 + + clock_fractional_mode: bool = WOBit(_REG_PLL_N, 5) + """Whether the integer mode (`False`) or fractional mode (`True`) of the PLL is used to + calculate the output clock. + + :default: `False` + """ + + clock_from_pll: bool = WOBit(_REG_CLOCKING_1, 0) + """Whether the SYSCLK is derived from MCLK (`False`) or the output of the PLL (`True`). + + :default: `False` + """ + + _system_clock_divider: int = WOBits(2, _REG_CLOCKING_1, 1) + + @property + def system_clock_div2(self) -> bool: + """Whether the clock source of the SYSCLK (see :attr:`clock_from_pll`) is divided by 1 + (`False`) or 2 (`True`). + + :default: `False` + """ + return self._system_clock_divider == _SYSCLK_DIV_BY_2 + + @system_clock_div2.setter + def system_clock_div2(self, value: bool) -> None: + self._system_clock_divider = _SYSCLK_DIV_BY_2 if value else _SYSCLK_DIV_BY_1 + + _adc_clock_divider: int = WOBits(3, _REG_CLOCKING_1, 6) + + @property + def adc_clock_divider(self) -> float: + """The sample rate divisor of the ADC as SYSCLK / (value * 256). Accepts a value of 1.0, + 1.5, 2.0, 3.0, 4.0, 5.5, or 6.0. + + :default: 1.0 + """ + return _ADCDACDIV[min(self._adc_clock_divider, len(_ADCDACDIV))] + + @adc_clock_divider.setter + def adc_clock_divider(self, value: float) -> None: + value = round(value * 2.0) / 2.0 + if value in _ADCDACDIV: + self._adc_clock_divider = _ADCDACDIV.index(value) + + _dac_clock_divider: int = WOBits(3, _REG_CLOCKING_1, 3) + + @property + def dac_clock_divider(self) -> float: + """The sample rate divisor of the DAC as SYSCLK / (value * 256). Accepts a value of 1.0, + 1.5, 2.0, 3.0, 4.0, 5.5, or 6.0. + + :default: 1.0 + """ + return _ADCDACDIV[min(self._dac_clock_divider, len(_ADCDACDIV))] + + @dac_clock_divider.setter + def dac_clock_divider(self, value: float) -> None: + value = round(value * 2.0) / 2.0 + if value in _ADCDACDIV: + self._dac_clock_divider = _ADCDACDIV.index(value) + + _base_clock_divider: int = WOBits(4, _REG_CLOCKING_2, 0) + + @property + def base_clock_divider(self) -> float: + """The divisor of the SYSCLK (when :attr:`master_mode` is set to `True`) when determining + BCLK frequency as SYSCLK / value. Accepts a value of 1.0, 1.5, 2.0, 3.0, 4.0, 5.5, 6.0, 8.0, + 11.0, 12.0, 16.0, 22.0, 24.0, or 32.0. + + :default: 1.0 + """ + return _BCLKDIV[min(self._base_clock_divider, len(_BCLKDIV))] + + @base_clock_divider.setter + def base_clock_divider(self, value: float) -> None: + value = round(value * 2.0) / 2.0 + if value in _BCLKDIV: + self._base_clock_divider = _BCLKDIV.index(value) + + _amp_clock_divider: int = WOBits(3, _REG_CLOCKING_2, 6) + + @property + def amp_clock_divider(self) -> float: + """The divisor of the SYSCLK used to operating the Class D amplifier switching clock as + SYSCLK / value. Accepts a value of 1.5 (not recommended), 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, and + 16.0. + + :default: 16.0 + """ + return _DCLKDIV[min(self._amp_clock_divider, len(_DCLKDIV))] + + @amp_clock_divider.setter + def amp_clock_divider(self, value: float) -> None: + value = round(value * 2.0) / 2.0 + if value in _DCLKDIV: + self._amp_clock_divider = _DCLKDIV.index(value) + + ## Mode + + master_mode: bool = WOBit(_REG_AUDIO_INTERFACE_1, 6) + """Whether the operation of the digital interface is controlled externally in slave mode + (`False`) or operated internally in master mode (`True`). + + :default: `False` + """ + + _bit_depth: int = WOBits(2, _REG_AUDIO_INTERFACE_1, 2) + + @property + def bit_depth(self) -> int: + """The number of bits per sample. The values 16, 20, 24, and 32 are supported. + + :default: 32 + """ + value = self._bit_depth + return 32 if value == 3 else 16 + 4 * value + + @bit_depth.setter + def bit_depth(self, value: int) -> None: + self._bit_depth = (min(value, 28) - 16) // 4 + + word_select_invert: bool = WOBit(_REG_AUDIO_INTERFACE_1, 4) + """The polarity of the LRCLK, either left-first (`False`) or right-first (`True`). + + :default: `False` + """ + + adc_channel_swap: bool = WOBit(_REG_AUDIO_INTERFACE_1, 8) + """Whether or not to swap left and right ADC data. + + :default: `False` + """ + + _vref_output_disable: bool = WOBit(_REG_ADDITIONAL_CONTROL_3, 6) + + @property + def vref_output(self) -> bool: + """Whether or not VMID is sent to the output circuitry. If set to `False`, it will + essentially disable the output of the device. + + :default `True` + """ + return not self._vref_output_disable + + @vref_output.setter + def vref_output(self, value: bool) -> None: + self._vref_output_disable = not value + + _vsel: int = WOBits(2, _REG_ADDITIONAL_CONTROL_1, 6) + + @property + def power_supply(self) -> float: + """The incoming voltage of the AVDD power supply in volts. Setting this value appropriately + will optimize bias current. Accepts a value of 2.7v or 3.3v. + + :default: 3.3 + """ + return (constrain(self._vsel, 1, 2) - 1) * 0.6 + 2.7 + + @power_supply.setter + def power_supply(self, value: float) -> None: + self._vsel = (constrain(value, 2.7, 3.3) - 2.7) // 0.6 * 2 + 1 + + ## GPIO + + gpio_output: bool = WOBit(_REG_AUDIO_INTERFACE_2, 6) + """Whether or not to enable special GPIO operation modes on the ADCLRC/GPIO1 pin. + + :default: `False` + """ + + gpio_output_mode: int = WOBits(3, _REG_ADDITIONAL_CONTROL_4, 4) + """The GPIO operation mode of ADCLRC/GPIO1 when :attr:`gpio_output` is set to `True`. Accepts a + value between 0 and 7. See WM8960 datasheet for the operation of each function. + + :default: 0 + """ + + gpio_output_invert: bool = WOBit(_REG_ADDITIONAL_CONTROL_4, 7) + """Whether or not to invert the polarity of the output of ADCLRC/GPIO1 when :attr:`gpio_output` + is set to `True`. + + :default: `False` + """ + + _gpio_clock_divider: int = WOBits(3, _REG_CLOCKING_2, 6) + + @property + def gpio_clock_divider(self) -> float: + """The divisor of the GPIO clock (when :attr:`gpio_output_mode` is set to 4) as SYSCLK / + value. Accepts a value of 1.0, 2.0, 3.0, 4.0, 5.5, or 6.0. + + :default: 1.0 + """ + return _OPCLKDIV[min(self._gpio_clock_divider, len(_ADCDACDIV))] + + @gpio_clock_divider.setter + def gpio_clock_divider(self, value: float) -> None: + value = round(value * 2.0) / 2.0 + if value in _OPCLKDIV: + self._gpio_clock_divider = _OPCLKDIV.index(value) + + @property + def sample_rate(self) -> int: + """The rate of the ADC/DAC processing of the device in samples per second used for I2S + communication and internal digital processing. If this property has not been set to a valid + value before being accessed, it will return `None`. The sample rates 8000, 11025, 12000, + 16000, 22050, 24000, 32000, 44100, and 48000 are supported. + + NOTE: This assumes that the master clock of the WM8960 is 24 MHz in order determine + appropriate clock settings. + + :default: `None` + """ + return self._sample_rate + + @sample_rate.setter + def sample_rate(self, value: int) -> None: + # MCLK = 24 MHz + self.pll = True # Needed for class-d amp clock + self.clock_fractional_mode = True + self.clock_from_pll = True + + self.pll_prescale_div2 = True + self.system_clock_div2 = True + self.base_clock_divider = 4.0 + self.amp_clock_divider = 16.0 + + if value in [8000, 12000, 16000, 24000, 32000, 48000]: + # SYSCLK = 12.288 MHz + # DCLK = 768.0k_hz + self.pll_n = 8 + self.pll_k = 0x3126E8 + self.adc_clock_divider = self.dac_clock_divider = 48000 / value + + elif value in [11025, 22050, 44100]: + # SYSCLK = 11.2896 MHz + # DCLK = 705.6k_hz + self.pll_n = 7 + self.pll_k = 0x86C226 + self.adc_clock_divider = self.dac_clock_divider = 44100 / value + + else: + raise ValueError("Invalid sample rate") + + self._sample_rate = value + + def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_I2C_ADDR) -> None: + """ + Initialize the WM8960 device. + + This function initialized the I2C device, performs a reset, turns on power, and sets vmid to + :const:`Vmid_Mode.PLAYBACK`. + + :param i2c: The I2C bus. + :param address: The I2C address of the device. Defaults to 0x1A. + """ + self.i2c_device = I2CDevice(i2c_bus, address) + self._sample_rate = None + + self._registers = [0] * len(_REG_DEFAULTS) + for i, reg in enumerate(self._registers): + self._registers[i] = bytearray(reg.to_bytes(2, "big")) + + # Must be called before `reset` to ensure that _REG_RESET is addressed properly + self._reset_registers() + + self.reset() + + # General setup + self.power = True + self.vmid = Vmid_Mode.PLAYBACK + + # Resets all registers to their default state + _reset: bool = WOBit(_REG_RESET, 7) + + def _reset_registers(self) -> None: + for i, reg in enumerate(self._registers): + reg[:] = _REG_DEFAULTS[i].to_bytes(2, "big") + reg[0] |= i << 1 + + def reset(self) -> None: + """Resets all parameters of the WM8960. All audio and digital functionality will be disabled + after calling this function. In order to resume normal operation, :attr:`power` must be set + to `True` and :attr:`vmid` should be set to :const:`Vmid_Mode.PLAYBACK`. + """ + self._reset = True + self._reset_registers() diff --git a/docs/api-advanced.rst b/docs/api-advanced.rst new file mode 100644 index 0000000..82a4c0b --- /dev/null +++ b/docs/api-advanced.rst @@ -0,0 +1,3 @@ + +.. automodule:: adafruit_wm8960.advanced + :members: diff --git a/docs/api-advanced.rst.license b/docs/api-advanced.rst.license new file mode 100644 index 0000000..f87f82a --- /dev/null +++ b/docs/api-advanced.rst.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries +SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple + +SPDX-License-Identifier: MIT diff --git a/docs/index.rst b/docs/index.rst index 4e58eb7..1c4c113 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,6 +21,12 @@ Table of Contents api +.. toctree:: + :caption: Advanced API Reference + :maxdepth: 3 + + api-advanced + .. toctree:: :caption: Tutorials From 557d8e70657c72b7545137015f7601ca3ab6a88a Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Mon, 2 Sep 2024 08:53:49 -0500 Subject: [PATCH 49/56] Updated example code to utilize basic driver. --- README.rst | 16 ++-- examples/wm8960_3d_enhance.py | 57 ++------------ examples/wm8960_automatic_level_control.py | 91 ++++++---------------- examples/wm8960_input.py | 45 +++-------- examples/wm8960_simpletest.py | 26 ++----- 5 files changed, 52 insertions(+), 183 deletions(-) diff --git a/README.rst b/README.rst index 2af6f81..ebe2bcc 100644 --- a/README.rst +++ b/README.rst @@ -88,15 +88,13 @@ Usage Example .. code-block:: python # Monitor Stereo Input: INPUT3 => Output Mixer => Headphones - import board, adafruit_wm8960 - codec = adafruit_wm8960.WM8960(board.I2C()) - codec.input = True - codec.input3_output = True - codec.input3_output_volume = 0.0 - codec.output = True - codec.headphone = True - codec.mono_output = True - codec.headphone_volume = 0.0 + import board + from adafruit_wm8960 import Input, WM8960 + codec = WM8960(board.I2C()) + codec.input = Input.LINE3 + codec.gain = 0.5 + codec.monitor = 1.0 + codec.headphone = 0.5 Documentation ============= diff --git a/examples/wm8960_3d_enhance.py b/examples/wm8960_3d_enhance.py index 4ac4c5b..c2a7504 100644 --- a/examples/wm8960_3d_enhance.py +++ b/examples/wm8960_3d_enhance.py @@ -46,60 +46,17 @@ """ import board, time -import adafruit_wm8960 +from adafruit_wm8960 import Input, WM8960 -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.mic = True - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs. -codec.mic_inverting_input = True - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.mic_mute = False - -# Set input boosts to get inputs 1 to the boost mixers -codec.mic_boost_gain = 0.0 -codec.mic_boost = True - -# Disconnect LB2LO (booster to output mixer (analog bypass) -# For this example, we are going to pass audio throught the ADC and DAC -codec.mic_output = False - -# Connect from DAC outputs to output mixer -codec.dac_output = True - -# Set gainstage between booster mixer and output mixer -# For this loopback example, we are going to keep these as low as they go -codec.mic_output_volume = adafruit_wm8960.OUTPUT_VOLUME_MIN - -# Enable output mixers -codec.output = True - -# Setup sample rate -codec.sample_rate = 44100 -codec.master_mode = True -codec.gpio_output = True # Note, should not be changed while ADC is enabled. - -# Enable ADCs and DACs -codec.adc = codec.dac = True - -# Loopback sends ADC data directly into DAC +codec = WM8960(board.I2C()) codec.loopback = True +codec.input = Input.MIC1 +codec.gain = 0.5 +codec.volume = 1.0 -# Default is "soft mute" on, so we must disable mute to make channels active -codec.dac_mute = False - -# Enable Headphone Amp with OUT3 as capless buffer for headphone ground -codec.headphone = True -codec.mono_output = True -codec.headphone_volume = 0.0 - -# Set 3D enhance depth to max (0.0 - 1.0) -codec.enhance_depth = 1.0 +codec.headphone = 0.5 # Toggle 3D enhance on and off while True: - codec.enhance = not codec.enhance + codec.enhance = 0.0 if codec.enhance else 1.0 time.sleep(2.0) diff --git a/examples/wm8960_automatic_level_control.py b/examples/wm8960_automatic_level_control.py index e593131..a7c5d53 100644 --- a/examples/wm8960_automatic_level_control.py +++ b/examples/wm8960_automatic_level_control.py @@ -57,81 +57,36 @@ import board, time from analogio import AnalogIn from adafruit_simplemath import map_range -import adafruit_wm8960 +from adafruit_wm8960 import Input, WM8960 analog_in = AnalogIn(board.A0) -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup signal flow to the ADC -codec.mic = True - -# Connect from INPUT1 to "n" (aka inverting) inputs of PGAs -codec.mic_inverting_input = True - -# Disable mutes on PGA inputs (aka INTPUT1) -codec.mic_mute = False - -# Set input boosts to get inputs 1 to the boost mixers -codec.mic_boost_gain = adafruit_wm8960.MIC_BOOST_GAIN_0DB -codec.mic_boost = True -codec.input = True # Enable boost mixers - -# Disconnect LB2LO (booster to output mixer (analog bypass) -# For this example, we are going to pass audio throught the ADC and DAC -codec.mic_output = False - -# Connect from DAC outputs to output mixer -codec.dac_output = True - -# Set gainstage between booster mixer and output mixer -# For this loopback example, we are going to keep these as low as they go -codec.mic_output_volume = adafruit_wm8960.OUTPUT_VOLUME_MIN - -# Enable output mixers -codec.output = True - -# Setup clock and mode -codec.sample_rate = 44100 -codec.master_mode = True -codec.gpio_output = True # Note, should not be changed while ADC is enabled. - -# Enable ADCs and DACs -codec.adc = codec.dac = True - -# Loopback sends ADC data directly into DAC +codec = WM8960(board.I2C()) codec.loopback = True - -# Default is "soft mute" on, so we must disable mute to make channels active -codec.dac_mute = False - -# Enable headphone amp output -codec.headphone = True -codec.headphone_volume = 0.0 - -# Enables capless mode using the VMID as buffer for headphone ground on OUT3 -codec.mono_output = True - -# Automatic Level control configuration - -# Only allows mic gain stages at a "zero crossover" point in audio stream. -# Minimizes "zipper" noise when chaning gains. -codec.mic_zero_cross = True +codec.input = Input.MIC1 +codec.gain = 0.5 +codec.volume = 1.0 +codec.headphone = 0.5 codec.alc = True -codec.alc_target = -6.0 -codec.alc_attack_time = 0.024 -codec.alc_hold_time = 0.0 -codec.alc_decay_time = 0.192 -codec.alc_max_gain = adafruit_wm8960.ALC_MAX_GAIN_MAX -codec.alc_min_gain = adafruit_wm8960.ALC_MIN_GAIN_MIN - +codec.alc_gain = ( + 0.75, # target + 1.0, # max gain + 0.0, # min gain + 0.0, # noise gate +) +codec.alc_time = ( + 0.024, # attack + 0.192, # decay + 0.0, # hold +) + +gain = codec.alc_gain while True: - codec.alc_target = map_range( + gain[0] = map_range( analog_in.value, - 0, - 65536, - adafruit_wm8960.ALC_TARGET_MIN, - adafruit_wm8960.ALC_TARGET_MAX, + 0, 65536, + 0.0, 1.0 ) + codec.alc_gain = gain time.sleep(1.0) diff --git a/examples/wm8960_input.py b/examples/wm8960_input.py index 6b99fed..33a1acf 100644 --- a/examples/wm8960_input.py +++ b/examples/wm8960_input.py @@ -47,43 +47,18 @@ """ import board -import adafruit_wm8960 +from adafruit_wm8960 import Input, WM8960 -codec = adafruit_wm8960.WM8960(board.I2C()) +codec = WM8960(board.I2C()) -# Setup signal flow through the analog audio bypass connections +# Select the desired input. Available options are MIC1 (single-ended), MIC2 (differential), MIC3 (differential), LINE2, or LINE3. +codec.input = Input.MIC1 -# INPUT1 must pass through the Mic Boost -codec.mic = True -codec.mic_inverting_input = True -codec.mic_input = adafruit_wm8960.MIC_VMID # Non-inverting input -codec.mic_mute = False -codec.mic_boost_gain = 0.0 -codec.mic_boost = True -codec.mic_volume = 0.0 +# Configure the microphone boost gain +codec.gain = 0.5 -# In order to use INPUT2 or INPUT3 with the Mic Boost (PGA) -# codec.mic_input = adafruit_wm8960.MIC_INPUT2 -# codec.mic_input = adafruit_wm8960.MIC_INPUT3 -# If codec.mic_inverting_input is enabled, mic boost uses input as balanced input between INPUT1 & INPUT2/3 +# Bypass analog signal to analog output +codec.monitor = 1.0 -# Set input boosts to get INPUT2 or INPUT3 (both left and right) to the boost mixers and bypass the Mic Boost (PGA) -codec.input2_boost = 0.0 -codec.input3_boost = 0.0 - -# Enable input boost mixers -codec.input = True - -# Connect LB2LO (booster to output mixer [aka analog bypass]) -codec.mic_output = True - -# Set gainstage between boost mixer and output mixers (analog bypass) -codec.mic_output_volume = 0.0 - -# Enable output mixers -codec.output = True - -# Enable Headphone Amp with OUT3 as capless buffer for headphone ground -codec.headphone = True -codec.mono_output = True -codec.headphone_volume = 0.0 +# Enable the amplifier and set the output volume +codec.headphone = 0.5 diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index be57403..8abd952 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -40,30 +40,14 @@ import audiobusio import board import synthio -import adafruit_wm8960 +from adafruit_wm8960 import WM8960 import time import digitalio -codec = adafruit_wm8960.WM8960(board.I2C()) - -# Setup Digital Interface -codec.sample_rate = 44100 -codec.bit_depth = 16 - -# Enable DAC -codec.dac = True -codec.dac_output = True -codec.output = True -codec.dac_mute = False - -# Enable Headphone Amp with OUT3 as capless buffer for headphone ground -codec.headphone = True -codec.mono_output = True -codec.headphone_volume = 0.0 - -# Enable Speaker Amp -codec.speaker = True -codec.speaker_volume = 0.0 +codec = WM8960(board.I2C(), 44100, 16) +codec.volume = 1.0 +codec.headphone = 0.5 +codec.speaker = 0.5 # Configure I2S Output audio = audiobusio.I2SOut(board.AUDIO_BCLK, board.AUDIO_SYNC, board.AUDIO_TXD) From 1f968b4c79be8e087794930881eeca96981895ef Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Mon, 2 Sep 2024 09:07:24 -0500 Subject: [PATCH 50/56] Fix logarithmic functions. --- adafruit_wm8960/advanced.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/adafruit_wm8960/advanced.py b/adafruit_wm8960/advanced.py index 3b9e792..8d83097 100644 --- a/adafruit_wm8960/advanced.py +++ b/adafruit_wm8960/advanced.py @@ -1208,12 +1208,13 @@ def alc_hold_time(self) -> float: def alc_hold_time(self, value: float) -> None: self._alc_hold = ( round( - math.log2( + math.log( ( constrain(value, ALC_HOLD_TIME_MIN, ALC_HOLD_TIME_MAX) - ALC_HOLD_TIME_MIN ) - / ALC_HOLD_TIME_MIN + / ALC_HOLD_TIME_MIN, + 2.0, ) + 1.0 ) @@ -1238,12 +1239,13 @@ def alc_decay_time(self) -> float: def alc_decay_time(self, value: float) -> None: self._alc_decay = min( round( - math.log2( + math.log( ( constrain(value, ALC_DECAY_TIME_MIN, ALC_DECAY_TIME_MAX) - ALC_DECAY_TIME_MIN ) - / ALC_DECAY_TIME_MIN + / ALC_DECAY_TIME_MIN, + 2.0, ) ), _ALC_DECAY_MAX, @@ -1266,12 +1268,13 @@ def alc_attack_time(self) -> float: def alc_attack_time(self, value: float) -> None: self._alc_attack = min( round( - math.log2( + math.log( ( constrain(value, ALC_ATTACK_TIME_MIN, ALC_ATTACK_TIME_MAX) - ALC_ATTACK_TIME_MIN ) - / ALC_ATTACK_TIME_MIN + / ALC_ATTACK_TIME_MIN, + 2.0, ) ), _ALC_ATTACK_MAX, From ec79c6544c595fbd3868a2ae97683d496cac15be Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Mon, 2 Sep 2024 09:07:43 -0500 Subject: [PATCH 51/56] Fix tuple manipulation error. --- examples/wm8960_automatic_level_control.py | 25 ++++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/examples/wm8960_automatic_level_control.py b/examples/wm8960_automatic_level_control.py index a7c5d53..5005f52 100644 --- a/examples/wm8960_automatic_level_control.py +++ b/examples/wm8960_automatic_level_control.py @@ -62,7 +62,6 @@ analog_in = AnalogIn(board.A0) codec = WM8960(board.I2C()) -codec.loopback = True codec.input = Input.MIC1 codec.gain = 0.5 codec.volume = 1.0 @@ -70,23 +69,21 @@ codec.alc = True codec.alc_gain = ( - 0.75, # target - 1.0, # max gain - 0.0, # min gain - 0.0, # noise gate + 0.75, # target + 1.0, # max gain + 0.0, # min gain + 0.0, # noise gate ) codec.alc_time = ( - 0.024, # attack - 0.192, # decay - 0.0, # hold + 0.024, # attack + 0.192, # decay + 0.0, # hold ) -gain = codec.alc_gain +codec.loopback = True + +gain = list(codec.alc_gain) while True: - gain[0] = map_range( - analog_in.value, - 0, 65536, - 0.0, 1.0 - ) + gain[0] = map_range(analog_in.value, 0, 65536, 0.0, 1.0) codec.alc_gain = gain time.sleep(1.0) From cf4f99531d7563e3eb2c213a3ef83a7537d92311 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Mon, 2 Sep 2024 09:12:03 -0500 Subject: [PATCH 52/56] Updated examples to pass pylint compatibility and black formatting. --- examples/wm8960_3d_enhance.py | 12 ++++++++---- examples/wm8960_automatic_level_control.py | 15 ++++++++++----- examples/wm8960_input.py | 15 ++++++++++----- examples/wm8960_simpletest.py | 19 ++++++++++++------- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/examples/wm8960_3d_enhance.py b/examples/wm8960_3d_enhance.py index c2a7504..0bd2303 100644 --- a/examples/wm8960_3d_enhance.py +++ b/examples/wm8960_3d_enhance.py @@ -4,11 +4,14 @@ # SPDX-License-Identifier: MIT """ -Demonstrates analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone output on the WM8960 Codec. +Demonstrates analog audio input (on INPUT1s), ADC/DAC Loopback, sets volume control, and Headphone +output on the WM8960 Codec. -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and +"LIN1" on the board. -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). +This example will pass your audio source through the mixers and gain stages of the codec into the +ADC. Turn on Loopback (so ADC is feed directly to DAC). Then send the output of the DAC to the headphone outs. You can now control the volume of the codecs built in headphone amp using this function: @@ -45,7 +48,8 @@ https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf """ -import board, time +import time +import board from adafruit_wm8960 import Input, WM8960 codec = WM8960(board.I2C()) diff --git a/examples/wm8960_automatic_level_control.py b/examples/wm8960_automatic_level_control.py index 5005f52..02ea63d 100644 --- a/examples/wm8960_automatic_level_control.py +++ b/examples/wm8960_automatic_level_control.py @@ -8,14 +8,18 @@ Attach a potentiomenter to GND/A0/3V3 to actively adjust the ALC target setting. -This example sets up the codec for analog audio input (on INPUT1s), ADC/DAC Loopback, sets hp volume, and Headphone output on the WM8960 Codec. +This example sets up the codec for analog audio input (on INPUT1s), ADC/DAC Loopback, sets headphone +volume, and Headphone output on the WM8960 Codec. -Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and "LIN1" on the board. +Audio should be connected to both the left and right "INPUT1" inputs, they are labeled "RIN1" and +"LIN1" on the board. -This example will pass your audio source through the mixers and gain stages of the codec into the ADC. Turn on Loopback (so ADC is feed directly to DAC). +This example will pass your audio source through the mixers and gain stages of the codec into the +ADC. Turn on Loopback (so ADC is feed directly to DAC). Then send the output of the DAC to the headphone outs. -We will use the user input via potentiometer on A0 to set the ALC target value. The ALC will adjust the gain of the pga input buffer to try and keep the signal level at the target. +We will use the user input via potentiometer on A0 to set the ALC target value. The ALC will adjust +the gain of the pga input buffer to try and keep the signal level at the target. HARDWARE CONNECTIONS @@ -54,7 +58,8 @@ https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf """ -import board, time +import time +import board from analogio import AnalogIn from adafruit_simplemath import map_range from adafruit_wm8960 import Input, WM8960 diff --git a/examples/wm8960_input.py b/examples/wm8960_input.py index 33a1acf..8aeb2d6 100644 --- a/examples/wm8960_input.py +++ b/examples/wm8960_input.py @@ -4,14 +4,18 @@ # SPDX-License-Identifier: MIT """ -Demonstrates analog audio input (on INPUT2s), sets volume control, and headphone output on the WM8960 Codec. +Demonstrates analog audio input (on INPUT2s), sets volume control, and headphone output on the +WM8960 Codec. -Audio should be connected to both the left and right "INPUT2" inputs, they are labeled "RIN2" and "LIN2" on the board. +Audio should be connected to both the left and right "INPUT2" inputs, they are labeled "RIN2" and +"LIN2" on the board. -This example will pass your audio source through the mixers and gain stages of the codec using all of the analog bypass paths. +This example will pass your audio source through the mixers and gain stages of the codec using all +of the analog bypass paths. It will output the sound on the headphone outputs. -It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. +It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this +provides a buffered VMID. You can now control the volume of the codecs built in headphone buffers using this function: codec.setHeadphoneVolumeDB(6.00); Valid inputs are -74.00 (MUTE) up to +6.00, (1.00dB steps). @@ -51,7 +55,8 @@ codec = WM8960(board.I2C()) -# Select the desired input. Available options are MIC1 (single-ended), MIC2 (differential), MIC3 (differential), LINE2, or LINE3. +# Select the desired input. Available options are MIC1 (single-ended), MIC2 (differential), +# MIC3 (differential), LINE2, or LINE3. codec.input = Input.MIC1 # Configure the microphone boost gain diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index 8abd952..2f3a706 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -5,10 +5,12 @@ # SPDX-License-Identifier: Unlicense """ -Demonstrates I2C Output on WM8960 Codec by generating a simple tone using synthio. Sounds like an alarm clock. +Demonstrates I2C Output on WM8960 Codec by generating a simple tone using synthio. Sounds like an +alarm clock. It will output the sound on the headphone outputs. -It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this provides a buffered VMID. +It is setup to do a capless headphone setup, so connect your headphones ground to "OUT3" and this +provides a buffered VMID. HARDWARE CONNECTIONS @@ -18,9 +20,12 @@ QWIIC ------- QWIIC *Note this connects GND/3.3V/SDA/SCL GND --------- GND *optional, but not a bad idea 5V ---------- VIN *needed to power codec's onboard AVDD (3.3V vreg) -AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data from MCU to codec DAC -AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be controlled via controller or peripheral. -AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left or right channel data. +AUDIO_TXD --- DDT *aka DAC_DATA/I2S_SDO/"serial data out", this carries the I2S audio data + from MCU to codec DAC +AUDIO_BCLK -- BCK *aka BCLK/I2S_SCK/"bit clock", this is the clock for I2S audio, can be + controlled via controller or peripheral. +AUDIO_SYNC -- DLRC *aka I2S_WS/LRC/"word select"/"left-right-channel", this toggles for left + or right channel data. ********************** CODEC ------- AUDIO OUT @@ -37,11 +42,11 @@ https://github.com/sparkfun/SparkFun_Audio_Codec_Breakout_WM8960/blob/main/Documents/WM8960_datasheet_v4.2.pdf """ -import audiobusio +import time import board +import audiobusio import synthio from adafruit_wm8960 import WM8960 -import time import digitalio codec = WM8960(board.I2C(), 44100, 16) From 7442bf8a2fff7b420416d623ae7bff4942f961c8 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Wed, 4 Sep 2024 14:15:59 -0500 Subject: [PATCH 53/56] Fix whitespace in class description. Co-authored-by: Scott Shawcroft --- adafruit_wm8960/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_wm8960/__init__.py b/adafruit_wm8960/__init__.py index f7b7a08..6a72853 100644 --- a/adafruit_wm8960/__init__.py +++ b/adafruit_wm8960/__init__.py @@ -61,7 +61,7 @@ class Input: class WM8960: - """Driver for controlling the WM8960 audio codec over an I2C connection.Used for receiving line + """Driver for controlling the WM8960 audio codec over an I2C connection. Used for receiving line and microphone input through the ADC over I2S, monitoring analog input to the output mixer, and sending digital audio to the headphone and speaker amplifiers via I2S. From 86cbd8b638dfe4f64de4b9dfbc32dafc81716ccd Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 6 Sep 2024 12:10:41 -0500 Subject: [PATCH 54/56] Fix import order in simpletest example. --- examples/wm8960_simpletest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/wm8960_simpletest.py b/examples/wm8960_simpletest.py index 2f3a706..b5196c8 100644 --- a/examples/wm8960_simpletest.py +++ b/examples/wm8960_simpletest.py @@ -44,10 +44,10 @@ import time import board +import digitalio import audiobusio import synthio from adafruit_wm8960 import WM8960 -import digitalio codec = WM8960(board.I2C(), 44100, 16) codec.volume = 1.0 From 962a1a8b3ed6507bf3ad538c2eb744ef2be24459 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 6 Sep 2024 12:14:36 -0500 Subject: [PATCH 55/56] Fix trailing whitespace and minor formatting. --- adafruit_wm8960/advanced.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/adafruit_wm8960/advanced.py b/adafruit_wm8960/advanced.py index 8d83097..390e6c9 100644 --- a/adafruit_wm8960/advanced.py +++ b/adafruit_wm8960/advanced.py @@ -396,7 +396,6 @@ class Vmid_Mode: class WOBit: - def __init__( self, register_address: int, @@ -427,7 +426,6 @@ def __set__(self, obj: Optional[I2CDeviceDriver], value: bool) -> None: class WOBits: - def __init__( # pylint: disable=too-many-arguments self, num_bits: int, @@ -707,9 +705,9 @@ def mic_boost_gain(self) -> float: @mic_boost_gain.setter def mic_boost_gain(self, value: float) -> None: - self._left_mic_boost_gain = self._right_mic_boost_gain = ( - WM8960_Advanced._get_mic_boost_gain(value) - ) + self._left_mic_boost_gain = ( + self._right_mic_boost_gain + ) = WM8960_Advanced._get_mic_boost_gain(value) ## Volume @@ -1293,7 +1291,7 @@ def alc_attack_time(self, value: float) -> None: """Whether or not the noise gate is enabled. The ALC, :attr:`alc`, must be set to `True` for this functionality to work. This feature will help prevent "noise pumping" during periods of quiet input signal. Only applicable to signal into the microphone amplifier. - + :default: `False` """ From 5ae8bc517179bb04e00797a371f25b2c2ef7f475 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 6 Sep 2024 16:28:25 -0500 Subject: [PATCH 56/56] Updated pyproject.toml to use package folder. --- pyproject.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9c8458b..2708ef1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,9 +43,7 @@ classifiers = [ dynamic = ["dependencies", "optional-dependencies"] [tool.setuptools] -# TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, -# CHANGE `py_modules = ['...']` TO `packages = ['...']` -py-modules = ["adafruit_wm8960"] +packages = ["adafruit_wm8960"] [tool.setuptools.dynamic] dependencies = {file = ["requirements.txt"]}