diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-08-15 13:53:30 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-08-15 13:53:30 +1000 |
| commit | d6b83fcf4a1a3039c06e0b1d1a1f7e2af2351efb (patch) | |
| tree | 03c6a534931736a2755aacef86e271ecc5b8e87c /src/audio | |
| parent | 205e3053506191fab69d01e7523e733dccc09d77 (diff) | |
| download | tangara-fw-d6b83fcf4a1a3039c06e0b1d1a1f7e2af2351efb.tar.gz | |
Flesh out basic bluetooth support
No ui yet, and performance isn't great. It kinda works though!!
Diffstat (limited to 'src/audio')
| -rw-r--r-- | src/audio/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/audio/audio_fsm.cpp | 8 | ||||
| -rw-r--r-- | src/audio/bt_audio_output.cpp | 79 | ||||
| -rw-r--r-- | src/audio/i2s_audio_output.cpp | 7 | ||||
| -rw-r--r-- | src/audio/include/audio_fsm.hpp | 3 | ||||
| -rw-r--r-- | src/audio/include/audio_sink.hpp | 8 | ||||
| -rw-r--r-- | src/audio/include/bt_audio_output.hpp | 49 |
7 files changed, 148 insertions, 8 deletions
diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 2d332a1e..a7dda8fd 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -6,7 +6,7 @@ idf_component_register( SRCS "audio_task.cpp" "chunk.cpp" "fatfs_audio_input.cpp" "stream_message.cpp" "i2s_audio_output.cpp" "stream_buffer.cpp" "track_queue.cpp" "stream_event.cpp" "stream_info.cpp" "audio_fsm.cpp" "sink_mixer.cpp" "resample.cpp" - "fatfs_source.cpp" + "fatfs_source.cpp" "bt_audio_output.cpp" INCLUDE_DIRS "include" REQUIRES "codecs" "drivers" "cbor" "result" "tasks" "span" "memory" "tinyfsm" "database" "system_fsm" "playlist" "speexdsp") diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index 617272b3..8791b9c4 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -11,6 +11,8 @@ #include "audio_decoder.hpp" #include "audio_events.hpp" #include "audio_task.hpp" +#include "bluetooth.hpp" +#include "bt_audio_output.hpp" #include "esp_log.h" #include "event_queue.hpp" #include "fatfs_audio_input.hpp" @@ -35,6 +37,7 @@ std::weak_ptr<database::Database> AudioState::sDatabase; std::unique_ptr<AudioTask> AudioState::sTask; std::unique_ptr<FatfsAudioInput> AudioState::sFileSource; std::unique_ptr<I2SAudioOutput> AudioState::sI2SOutput; +std::unique_ptr<BluetoothAudioOutput> AudioState::sBtOutput; TrackQueue* AudioState::sTrackQueue; std::optional<database::TrackId> AudioState::sCurrentTrack; @@ -42,6 +45,7 @@ std::optional<database::TrackId> AudioState::sCurrentTrack; auto AudioState::Init(drivers::IGpios* gpio_expander, std::weak_ptr<database::Database> database, std::shared_ptr<database::ITagParser> tag_parser, + drivers::Bluetooth* bluetooth, TrackQueue* queue) -> bool { sIGpios = gpio_expander; sTrackQueue = queue; @@ -55,8 +59,10 @@ auto AudioState::Init(drivers::IGpios* gpio_expander, sFileSource.reset(new FatfsAudioInput(tag_parser)); sI2SOutput.reset(new I2SAudioOutput(sIGpios, sDac)); + // sBtOutput.reset(new BluetoothAudioOutput(bluetooth)); AudioTask::Start(sFileSource.get(), sI2SOutput.get()); + // AudioTask::Start(sFileSource.get(), sBtOutput.get()); return true; } @@ -125,6 +131,7 @@ void Standby::react(const QueueUpdate& ev) { void Playback::entry() { ESP_LOGI(kTag, "beginning playback"); sI2SOutput->SetInUse(true); + // sBtOutput->SetInUse(true); } void Playback::exit() { @@ -133,6 +140,7 @@ void Playback::exit() { // to drain. vTaskDelay(pdMS_TO_TICKS(250)); sI2SOutput->SetInUse(false); + // sBtOutput->SetInUse(false); } void Playback::react(const QueueUpdate& ev) { diff --git a/src/audio/bt_audio_output.cpp b/src/audio/bt_audio_output.cpp new file mode 100644 index 00000000..71e40d02 --- /dev/null +++ b/src/audio/bt_audio_output.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "bt_audio_output.hpp" +#include <stdint.h> +#include <sys/_stdint.h> + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <variant> + +#include "esp_err.h" +#include "esp_heap_caps.h" +#include "freertos/portmacro.h" + +#include "audio_element.hpp" +#include "freertos/projdefs.h" +#include "gpios.hpp" +#include "i2c.hpp" +#include "i2s_dac.hpp" +#include "result.hpp" +#include "stream_info.hpp" +#include "wm8523.hpp" + +static const char* kTag = "BTOUT"; + +namespace audio { + +static constexpr size_t kDrainBufferSize = 48 * 1024; + +BluetoothAudioOutput::BluetoothAudioOutput(drivers::Bluetooth* bt) + : IAudioSink(kDrainBufferSize, MALLOC_CAP_SPIRAM), bluetooth_(bt) {} + +BluetoothAudioOutput::~BluetoothAudioOutput() {} + +auto BluetoothAudioOutput::SetInUse(bool in_use) -> void { + if (in_use) { + bluetooth_->SetSource(stream()); + } else { + bluetooth_->SetSource(nullptr); + } +} + +auto BluetoothAudioOutput::SetVolumeImbalance(int_fast8_t balance) -> void {} + +auto BluetoothAudioOutput::SetVolume(uint_fast8_t percent) -> void {} + +auto BluetoothAudioOutput::GetVolume() -> uint_fast8_t { + return 50; +} + +auto BluetoothAudioOutput::AdjustVolumeUp() -> bool { + return false; +} + +auto BluetoothAudioOutput::AdjustVolumeDown() -> bool { + return false; +} + +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 = 44100, + .num_channels = 2, + .bits_per_sample = 16, + }; +} + +auto BluetoothAudioOutput::Configure(const Format& fmt) -> void { + // No configuration necessary; the output format is fixed. +} + +} // namespace audio diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp index b7fcf104..8b7d130f 100644 --- a/src/audio/i2s_audio_output.cpp +++ b/src/audio/i2s_audio_output.cpp @@ -14,7 +14,9 @@ #include <memory> #include <variant> +#include "audio_sink.hpp" #include "esp_err.h" +#include "esp_heap_caps.h" #include "freertos/portmacro.h" #include "audio_element.hpp" @@ -41,9 +43,12 @@ static constexpr uint16_t kMaxVolumeBeforeClipping = 0x185; static constexpr uint16_t kLineLevelVolume = 0x13d; static constexpr uint16_t kDefaultVolume = 0x128; +static constexpr size_t kDrainBufferSize = 8 * 1024; + I2SAudioOutput::I2SAudioOutput(drivers::IGpios* expander, std::weak_ptr<drivers::I2SDac> dac) - : expander_(expander), + : IAudioSink(kDrainBufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), + expander_(expander), dac_(dac.lock()), current_config_(), left_difference_(0), diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index d10f31e1..5d44fcda 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -12,6 +12,7 @@ #include "audio_events.hpp" #include "audio_task.hpp" +#include "bt_audio_output.hpp" #include "database.hpp" #include "display.hpp" #include "fatfs_audio_input.hpp" @@ -33,6 +34,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> { static auto Init(drivers::IGpios* gpio_expander, std::weak_ptr<database::Database>, std::shared_ptr<database::ITagParser>, + drivers::Bluetooth* bluetooth, TrackQueue* queue) -> bool; virtual ~AudioState() {} @@ -68,6 +70,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> { static std::unique_ptr<AudioTask> sTask; static std::unique_ptr<FatfsAudioInput> sFileSource; static std::unique_ptr<I2SAudioOutput> sI2SOutput; + static std::unique_ptr<BluetoothAudioOutput> sBtOutput; static TrackQueue* sTrackQueue; static std::optional<database::TrackId> sCurrentTrack; diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index 2fb4bf63..b5d6ef57 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -18,15 +18,11 @@ namespace audio { class IAudioSink { private: - static const std::size_t kDrainBufferSize = 24 * 1024; StreamBufferHandle_t stream_; public: - IAudioSink() - : stream_(xStreamBufferCreateWithCaps( - kDrainBufferSize, - 1, - MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)) {} + IAudioSink(size_t buffer_size, uint32_t caps) + : stream_(xStreamBufferCreateWithCaps(buffer_size, 1, caps)) {} virtual ~IAudioSink() { vStreamBufferDeleteWithCaps(stream_); } diff --git a/src/audio/include/bt_audio_output.hpp b/src/audio/include/bt_audio_output.hpp new file mode 100644 index 00000000..e11a5d44 --- /dev/null +++ b/src/audio/include/bt_audio_output.hpp @@ -0,0 +1,49 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <sys/_stdint.h> +#include <cstdint> +#include <memory> +#include <vector> + +#include "audio_element.hpp" +#include "audio_sink.hpp" +#include "bluetooth.hpp" +#include "chunk.hpp" +#include "result.hpp" + +#include "gpios.hpp" +#include "i2s_dac.hpp" +#include "stream_info.hpp" + +namespace audio { + +class BluetoothAudioOutput : public IAudioSink { + public: + BluetoothAudioOutput(drivers::Bluetooth* bt); + ~BluetoothAudioOutput(); + + auto SetInUse(bool) -> void override; + + auto SetVolumeImbalance(int_fast8_t balance) -> void override; + auto SetVolume(uint_fast8_t percent) -> void override; + auto GetVolume() -> uint_fast8_t override; + auto AdjustVolumeUp() -> bool override; + auto AdjustVolumeDown() -> bool override; + + auto PrepareFormat(const Format&) -> Format override; + auto Configure(const Format& format) -> void override; + + BluetoothAudioOutput(const BluetoothAudioOutput&) = delete; + BluetoothAudioOutput& operator=(const BluetoothAudioOutput&) = delete; + + private: + drivers::Bluetooth* bluetooth_; +}; + +} // namespace audio |
