summaryrefslogtreecommitdiff
path: root/src/tangara/audio/bt_audio_output.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tangara/audio/bt_audio_output.cpp')
-rw-r--r--src/tangara/audio/bt_audio_output.cpp124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/tangara/audio/bt_audio_output.cpp b/src/tangara/audio/bt_audio_output.cpp
new file mode 100644
index 00000000..229a38bb
--- /dev/null
+++ b/src/tangara/audio/bt_audio_output.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "bt_audio_output.hpp"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <variant>
+
+#include "esp_err.h"
+#include "esp_heap_caps.h"
+#include "freertos/portmacro.h"
+#include "freertos/projdefs.h"
+
+#include "gpios.hpp"
+#include "i2c.hpp"
+#include "i2s_dac.hpp"
+#include "result.hpp"
+#include "tasks.hpp"
+#include "wm8523.hpp"
+
+[[maybe_unused]] static const char* kTag = "BTOUT";
+
+namespace audio {
+
+static constexpr uint16_t kVolumeRange = 60;
+
+BluetoothAudioOutput::BluetoothAudioOutput(StreamBufferHandle_t s,
+ drivers::Bluetooth& bt,
+ tasks::WorkerPool& p)
+ : IAudioOutput(s), bluetooth_(bt), bg_worker_(p), volume_() {}
+
+BluetoothAudioOutput::~BluetoothAudioOutput() {}
+
+auto BluetoothAudioOutput::changeMode(Modes mode) -> void {
+ if (mode == Modes::kOnPlaying) {
+ bluetooth_.SetSource(stream());
+ } else {
+ bluetooth_.SetSource(nullptr);
+ }
+}
+
+auto BluetoothAudioOutput::SetVolumeImbalance(int_fast8_t balance) -> void {
+ // FIXME: Support two separate scaling factors in the bluetooth driver.
+}
+
+auto BluetoothAudioOutput::SetVolume(uint16_t v) -> void {
+ volume_ = std::clamp<uint16_t>(v, 0, 100);
+ bg_worker_.Dispatch<void>([&]() {
+ float factor =
+ pow(10, static_cast<double>(kVolumeRange) * (volume_ - 100) / 100 / 20);
+ bluetooth_.SetVolumeFactor(factor);
+ });
+}
+
+auto BluetoothAudioOutput::GetVolume() -> uint16_t {
+ return volume_;
+}
+
+auto BluetoothAudioOutput::GetVolumePct() -> uint_fast8_t {
+ return static_cast<uint_fast8_t>(round(static_cast<int>(volume_)));
+}
+
+auto BluetoothAudioOutput::SetVolumePct(uint_fast8_t val) -> bool {
+ if (val > 100) {
+ return false;
+ }
+ SetVolume(val);
+ return true;
+}
+
+auto BluetoothAudioOutput::GetVolumeDb() -> int_fast16_t {
+ double pct = GetVolumePct() / 100.0;
+ if (pct <= 0) {
+ pct = 0.01;
+ }
+ int_fast16_t db = log(pct) * 20;
+ return db;
+}
+
+auto BluetoothAudioOutput::SetVolumeDb(int_fast16_t val) -> bool {
+ double pct = exp(val / 20.0) * 100;
+ return SetVolumePct(pct);
+}
+
+auto BluetoothAudioOutput::AdjustVolumeUp() -> bool {
+ if (volume_ == 100 || !bluetooth_.IsConnected()) {
+ return false;
+ }
+ volume_++;
+ SetVolume(volume_);
+ return true;
+}
+
+auto BluetoothAudioOutput::AdjustVolumeDown() -> bool {
+ if (volume_ == 0 || !bluetooth_.IsConnected()) {
+ return false;
+ }
+ volume_--;
+ SetVolume(volume_);
+ return true;
+}
+
+auto BluetoothAudioOutput::PrepareFormat(const Format& orig) -> Format {
+ // ESP-IDF's current Bluetooth implementation currently handles SBC encoding,
+ // but requires a fixed input format.
+ return Format{
+ .sample_rate = 48000,
+ .num_channels = 2,
+ .bits_per_sample = 16,
+ };
+}
+
+auto BluetoothAudioOutput::Configure(const Format& fmt) -> void {
+ // No configuration necessary; the output format is fixed.
+}
+
+} // namespace audio