summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-07-28 13:01:18 +1000
committerjacqueline <me@jacqueline.id.au>2023-07-28 13:01:18 +1000
commit3670859d1620ca0fe3492cffb591bf29e5af849c (patch)
tree51efdf3f8c5fa9ff86dabbdddb8c64416b8703bb /src
parent72fe82ebc43b1e7bf10ebe72efec1723b3792afd (diff)
downloadtangara-fw-3670859d1620ca0fe3492cffb591bf29e5af849c.tar.gz
Volume control! Reasonable default volume! Hooray!
Diffstat (limited to 'src')
-rw-r--r--src/audio/i2s_audio_output.cpp87
-rw-r--r--src/audio/include/i2s_audio_output.hpp6
-rw-r--r--src/drivers/CMakeLists.txt2
-rw-r--r--src/drivers/i2s_dac.cpp64
-rw-r--r--src/drivers/include/wm8523.hpp31
-rw-r--r--src/drivers/wm8523.cpp51
6 files changed, 156 insertions, 85 deletions
diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp
index 8ce43336..5873d80f 100644
--- a/src/audio/i2s_audio_output.cpp
+++ b/src/audio/i2s_audio_output.cpp
@@ -10,6 +10,7 @@
#include <algorithm>
#include <cstddef>
+#include <cstdint>
#include <memory>
#include <variant>
@@ -23,19 +24,32 @@
#include "i2s_dac.hpp"
#include "result.hpp"
#include "stream_info.hpp"
+#include "wm8523.hpp"
static const char* kTag = "I2SOUT";
namespace audio {
+// Consumer line level = 0.316 VRMS = -10db = 61
+// Professional line level = 1.228 VRMS = +4dB = 111
+// Cliipping level = 2.44 VRMS = 133?
+// all into 650 ohms
+
+static constexpr uint16_t kMaxVolume = 0x1ff;
+static constexpr uint16_t kMinVolume = 0b0;
+static constexpr uint16_t kMaxVolumeBeforeClipping = 0x185;
+static constexpr uint16_t kLineLevelVolume = 0x13d;
+static constexpr uint16_t kDefaultVolume = 0x128;
+
I2SAudioOutput::I2SAudioOutput(drivers::IGpios* expander,
std::weak_ptr<drivers::I2SDac> dac)
: expander_(expander),
dac_(dac.lock()),
current_config_(),
left_difference_(0),
- attenuation_() {
- SetVolume(25); // For testing
+ current_volume_(kDefaultVolume),
+ max_volume_(kLineLevelVolume){
+ SetVolume(GetVolume());
dac_->SetSource(stream());
}
@@ -53,63 +67,50 @@ auto I2SAudioOutput::SetInUse(bool in_use) -> void {
}
auto I2SAudioOutput::SetVolumeImbalance(int_fast8_t balance) -> void {
- // TODO.
+ left_difference_ = balance;
+ SetVolume(GetVolume());
}
auto I2SAudioOutput::SetVolume(uint_fast8_t percent) -> void {
- // TODO.
-}
+ percent = std::min<uint_fast8_t>(percent, 100);
+ float new_value = static_cast<float>(max_volume_) / 100 * percent;
+ current_volume_ = std::max<float>(new_value, kMinVolume);
+ ESP_LOGI(kTag, "set volume to %u%% = %u", percent, current_volume_);
-auto I2SAudioOutput::GetVolume() -> uint_fast8_t {
- // TODO.
- return 100;
-}
+ int32_t left_unclamped = current_volume_ + left_difference_;
+ uint16_t left = std::clamp<int32_t>(left_unclamped, kMinVolume, max_volume_);
-auto I2SAudioOutput::GetAdjustedMaxAttenuation() -> int_fast8_t {
- // TODO
- return 0;
+ using drivers::wm8523::Register;
+ drivers::wm8523::WriteRegister(Register::kDacGainLeft, left);
+ drivers::wm8523::WriteRegister(Register::kDacGainRight, current_volume_ | 0x200);
}
-static uint8_t vol = 0xFF;
+auto I2SAudioOutput::GetVolume() -> uint_fast8_t {
+ return
+ static_cast<uint_fast8_t>(
+ static_cast<float>(current_volume_) / max_volume_ * 100.0f);
+}
auto I2SAudioOutput::AdjustVolumeUp() -> bool {
- vol += 0xF;
- {
- drivers::I2CTransaction transaction;
- transaction.start()
- .write_addr(0b0011010, I2C_MASTER_WRITE)
- .write_ack(6, 0b01, vol)
- .stop();
- transaction.Execute();
+ if (GetVolume() >= 100) {
+ return false;
}
- {
- drivers::I2CTransaction transaction;
- transaction.start()
- .write_addr(0b0011010, I2C_MASTER_WRITE)
- .write_ack(7, 0b11, vol)
- .stop();
- transaction.Execute();
+ if (GetVolume() >= 95) {
+ SetVolume(100);
+ } else {
+ SetVolume(GetVolume() + 5);
}
return true;
}
auto I2SAudioOutput::AdjustVolumeDown() -> bool {
- vol -= 0xF;
- {
- drivers::I2CTransaction transaction;
- transaction.start()
- .write_addr(0b0011010, I2C_MASTER_WRITE)
- .write_ack(6, 0b01, vol)
- .stop();
- transaction.Execute();
+ if (GetVolume() == 0) {
+ return false;
}
- {
- drivers::I2CTransaction transaction;
- transaction.start()
- .write_addr(0b0011010, I2C_MASTER_WRITE)
- .write_ack(7, 0b11, vol)
- .stop();
- transaction.Execute();
+ if (GetVolume() <= 5) {
+ SetVolume(0);
+ } else {
+ SetVolume(GetVolume() - 5);
}
return true;
}
diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp
index d42efc42..43155711 100644
--- a/src/audio/include/i2s_audio_output.hpp
+++ b/src/audio/include/i2s_audio_output.hpp
@@ -6,6 +6,7 @@
#pragma once
+#include <sys/_stdint.h>
#include <cstdint>
#include <memory>
#include <vector>
@@ -41,14 +42,13 @@ class I2SAudioOutput : public IAudioSink {
I2SAudioOutput& operator=(const I2SAudioOutput&) = delete;
private:
- auto GetAdjustedMaxAttenuation() -> int_fast8_t;
-
drivers::IGpios* expander_;
std::shared_ptr<drivers::I2SDac> dac_;
std::optional<StreamInfo::Pcm> current_config_;
int_fast8_t left_difference_;
- uint_fast8_t attenuation_;
+ uint16_t current_volume_;
+ uint16_t max_volume_;
};
} // namespace audio
diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt
index 151a3afc..40cd0c4f 100644
--- a/src/drivers/CMakeLists.txt
+++ b/src/drivers/CMakeLists.txt
@@ -4,7 +4,7 @@
idf_component_register(
SRCS "touchwheel.cpp" "i2s_dac.cpp" "gpios.cpp" "battery.cpp" "storage.cpp" "i2c.cpp"
- "spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" "relative_wheel.cpp"
+ "spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" "relative_wheel.cpp" "wm8523.cpp"
INCLUDE_DIRS "include"
REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/drivers/i2s_dac.cpp b/src/drivers/i2s_dac.cpp
index dd454db3..e4e782c2 100644
--- a/src/drivers/i2s_dac.cpp
+++ b/src/drivers/i2s_dac.cpp
@@ -28,6 +28,7 @@
#include "hal/i2c_types.h"
#include "gpios.hpp"
+#include "wm8523.hpp"
#include "hal/i2s_types.h"
#include "i2c.hpp"
#include "soc/clk_tree_defs.h"
@@ -36,28 +37,6 @@ namespace drivers {
static const char* kTag = "i2s_dac";
static const i2s_port_t kI2SPort = I2S_NUM_0;
-static const uint8_t kWm8523Address = 0b0011010;
-
-enum Register {
- kReset = 0,
- kRevision = 1,
- kPsCtrl = 2,
- kAifCtrl1 = 3,
- kAifCtrl2 = 4,
- kDacCtrl = 5,
- kDacGainLeft = 6,
- kDacGainRight = 7,
- kZeroDetect = 8,
-};
-
-auto write_register(Register reg, uint8_t msb, uint8_t lsb) -> esp_err_t {
- I2CTransaction transaction;
- transaction.start()
- .write_addr(kWm8523Address, I2C_MASTER_WRITE)
- .write_ack(reg, msb, lsb)
- .stop();
- return transaction.Execute();
-}
auto I2SDac::create(IGpios* expander) -> std::optional<I2SDac*> {
i2s_chan_handle_t i2s_handle;
@@ -110,17 +89,11 @@ I2SDac::I2SDac(IGpios* gpio, i2s_chan_handle_t i2s_handle)
gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, false);
// Reset all registers back to their default values.
- write_register(kReset, 0, 1);
+ wm8523::WriteRegister(wm8523::Register::kReset, 1);
vTaskDelay(pdMS_TO_TICKS(10));
// Power up the charge pump.
- write_register(kPsCtrl, 0, 0b01);
-
- // TODO: testing
- // write_register(kDacGainLeft, 0b01, 0x50);
- // write_register(kDacGainRight, 0b11, 0x50);
- write_register(kDacGainLeft, 0b01, 0x0);
- write_register(kDacGainRight, 0b11, 0x0);
+ wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b01);
}
I2SDac::~I2SDac() {
@@ -130,15 +103,23 @@ I2SDac::~I2SDac() {
auto I2SDac::Start() -> void {
gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, true);
+ vTaskDelay(pdMS_TO_TICKS(1));
+ wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b10);
- i2s_channel_enable(i2s_handle_);
- write_register(kPsCtrl, 0, 0b11);
+ uint8_t zeroes[256] {0};
+ size_t bytes_loaded = 0;
+ esp_err_t res = ESP_OK;
+ do {
+ res = i2s_channel_preload_data(i2s_handle_, zeroes, 256, &bytes_loaded);
+ } while (bytes_loaded > 0 && res == ESP_OK);
+ wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b11);
+ i2s_channel_enable(i2s_handle_);
i2s_active_ = true;
}
auto I2SDac::Stop() -> void {
- write_register(kPsCtrl, 0, 0b01);
+ wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b01);
i2s_channel_disable(i2s_handle_);
gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, false);
@@ -148,8 +129,15 @@ auto I2SDac::Stop() -> void {
auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate)
-> void {
- write_register(kPsCtrl, 0, 0b01);
- i2s_channel_disable(i2s_handle_);
+ if (i2s_active_) {
+ // Ramp down into mute instead of just outright stopping to minimise any
+ // clicks and pops.
+ wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b10);
+ vTaskDelay(pdMS_TO_TICKS(1));
+
+ wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b01);
+ i2s_channel_disable(i2s_handle_);
+ }
switch (ch) {
case CHANNELS_MONO:
@@ -190,13 +178,13 @@ auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate)
ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_));
// Set the correct word size, and set the input format to I2S-justified.
- write_register(kAifCtrl1, 0, (word_length << 3) | 0b10);
+ wm8523::WriteRegister(wm8523::Register::kAifCtrl1, (word_length << 3) | 0b10);
// Tell the DAC the clock ratio instead of waiting for it to auto detect.
- // write_register(kAifCtrl2, 0, bps == BPS_24 ? 0b100 : 0b011);
+ wm8523::WriteRegister(wm8523::Register::kAifCtrl2, bps == BPS_24 ? 0b100 : 0b011);
if (i2s_active_) {
i2s_channel_enable(i2s_handle_);
- write_register(kPsCtrl, 0, 0b11);
+ wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b11);
}
}
diff --git a/src/drivers/include/wm8523.hpp b/src/drivers/include/wm8523.hpp
new file mode 100644
index 00000000..8b20eda0
--- /dev/null
+++ b/src/drivers/include/wm8523.hpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+#pragma once
+
+#include <cstdint>
+#include <optional>
+
+namespace drivers {
+namespace wm8523 {
+
+enum class Register : uint8_t {
+ kReset = 0,
+ kRevision = 1,
+ kPsCtrl = 2,
+ kAifCtrl1 = 3,
+ kAifCtrl2 = 4,
+ kDacCtrl = 5,
+ kDacGainLeft = 6,
+ kDacGainRight = 7,
+ kZeroDetect = 8,
+};
+
+auto ReadRegister(Register reg) -> std::optional<uint16_t>;
+auto WriteRegister(Register reg, uint16_t data) -> bool;
+auto WriteRegister(Register reg, uint8_t msb, uint8_t lsb) -> bool;
+
+}
+}
diff --git a/src/drivers/wm8523.cpp b/src/drivers/wm8523.cpp
new file mode 100644
index 00000000..dbd4a88e
--- /dev/null
+++ b/src/drivers/wm8523.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+#include "wm8523.hpp"
+
+#include <cstdint>
+
+#include "esp_err.h"
+
+#include "hal/i2c_types.h"
+#include "i2c.hpp"
+
+namespace drivers {
+namespace wm8523 {
+
+static const uint8_t kAddress = 0b0011010;
+
+auto ReadRegister(Register reg) -> std::optional<uint16_t> {
+ uint8_t msb, lsb;
+ I2CTransaction transaction;
+ transaction.start()
+ .write_addr(kAddress, I2C_MASTER_WRITE)
+ .write_ack(static_cast<uint8_t>(reg))
+ .start()
+ .write_addr(kAddress, I2C_MASTER_READ)
+ .read(&msb, I2C_MASTER_ACK)
+ .read(&lsb, I2C_MASTER_LAST_NACK)
+ .stop();
+ if (transaction.Execute() != ESP_OK) {
+ return {};
+ }
+ return (msb << 8) & lsb;
+}
+
+auto WriteRegister(Register reg, uint16_t data) -> bool {
+ return WriteRegister(reg, (data >> 8) & 0xFF, data & 0xFF);
+}
+
+auto WriteRegister(Register reg, uint8_t msb, uint8_t lsb) -> bool {
+ I2CTransaction transaction;
+ transaction.start()
+ .write_addr(kAddress, I2C_MASTER_WRITE)
+ .write_ack(static_cast<uint8_t>(reg), msb, lsb)
+ .stop();
+ return transaction.Execute() == ESP_OK;
+}
+
+}
+}