Skip to content

Commit 9c4ff3c

Browse files
committed
feat(esp-box): Update implementation
* Break up implementation into different source files * Update audio interface to allow configuration of audio task
1 parent f2b334c commit 9c4ff3c

File tree

5 files changed

+655
-658
lines changed

5 files changed

+655
-658
lines changed

components/esp-box/include/esp-box.hpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,16 @@ class EspBox : public BaseComponent {
7070
/// Get the type of the box
7171
/// \return The type of the box that was detected
7272
/// \see BoxType
73-
BoxType box_type() const;
73+
BoxType box_type() const { return box_type_; }
7474

7575
/// Get a reference to the internal I2C bus
7676
/// \return A reference to the internal I2C bus
7777
/// \note The internal I2C bus is used for the touchscreen and audio codec
78-
I2c &internal_i2c();
78+
I2c &internal_i2c() { return internal_i2c_; }
7979

8080
/// Get a reference to the interrupts
8181
/// \return A reference to the interrupts
82-
espp::Interrupt &interrupts();
82+
espp::Interrupt &interrupts() { return interrupts_; }
8383

8484
/////////////////////////////////////////////////////////////////////////////
8585
// Touchpad
@@ -98,11 +98,11 @@ class EspBox : public BaseComponent {
9898

9999
/// Get the touchpad input
100100
/// \return A shared pointer to the touchpad input
101-
std::shared_ptr<TouchpadInput> touchpad_input() const;
101+
std::shared_ptr<TouchpadInput> touchpad_input() const { return touchpad_input_; }
102102

103103
/// Get the most recent touchpad data
104104
/// \return The touchpad data
105-
TouchpadData touchpad_data() const;
105+
TouchpadData touchpad_data() const { return touchpad_data_; }
106106

107107
/// Get the most recent touchpad data
108108
/// \param num_touch_points The number of touch points
@@ -157,7 +157,7 @@ class EspBox : public BaseComponent {
157157

158158
/// Get a shared pointer to the display
159159
/// \return A shared pointer to the display
160-
std::shared_ptr<Display<Pixel>> display() const;
160+
std::shared_ptr<Display<Pixel>> display() const { return display_; }
161161

162162
/// Set the brightness of the backlight
163163
/// \param brightness The brightness of the backlight as a percentage (0 - 100)
@@ -234,9 +234,16 @@ class EspBox : public BaseComponent {
234234

235235
/// Initialize the sound subsystem
236236
/// \param default_audio_rate The default audio rate
237+
/// \param task_config The task configuration for the audio task
237238
/// \return true if the sound subsystem was successfully initialized, false
238239
/// otherwise
239-
bool initialize_sound(uint32_t default_audio_rate = 48000);
240+
bool initialize_sound(uint32_t default_audio_rate = 48000,
241+
const espp::Task::BaseConfig &task_config = {
242+
.name = "audio",
243+
.stack_size_bytes = 4096,
244+
.priority = 19,
245+
.core_id = 1,
246+
});
240247

241248
/// Enable or disable sound
242249
/// \note This method sets the power pin to the appropriate value
@@ -393,6 +400,7 @@ class EspBox : public BaseComponent {
393400
}
394401
},
395402
.active_level = touch_interrupt_level,
403+
.filter_type = espp::Interrupt::FilterType::PIN_GLITCH_FILTER,
396404
};
397405

398406
// we'll only add each interrupt pin if the initialize method is called

components/esp-box/src/audio.cpp

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#include "esp-box.hpp"
2+
3+
using namespace espp;
4+
5+
////////////////////////
6+
// Audio Functions //
7+
////////////////////////
8+
9+
static TaskHandle_t play_audio_task_handle_ = NULL;
10+
11+
static bool audio_tx_sent_callback(i2s_chan_handle_t handle, i2s_event_data_t *event,
12+
void *user_ctx) {
13+
// notify the main task that we're done
14+
vTaskNotifyGiveFromISR(play_audio_task_handle_, NULL);
15+
return true;
16+
}
17+
18+
bool EspBox::initialize_codec() {
19+
logger_.info("initializing codec");
20+
21+
set_es8311_write(std::bind(&espp::I2c::write, &internal_i2c_, std::placeholders::_1,
22+
std::placeholders::_2, std::placeholders::_3));
23+
set_es8311_read(std::bind(&espp::I2c::read_at_register, &internal_i2c_, std::placeholders::_1,
24+
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
25+
26+
esp_err_t ret_val = ESP_OK;
27+
audio_hal_codec_config_t cfg;
28+
memset(&cfg, 0, sizeof(cfg));
29+
cfg.codec_mode = AUDIO_HAL_CODEC_MODE_DECODE;
30+
cfg.dac_output = AUDIO_HAL_DAC_OUTPUT_LINE1;
31+
cfg.i2s_iface.bits = AUDIO_HAL_BIT_LENGTH_16BITS;
32+
cfg.i2s_iface.fmt = AUDIO_HAL_I2S_NORMAL;
33+
cfg.i2s_iface.mode = AUDIO_HAL_MODE_SLAVE;
34+
cfg.i2s_iface.samples = AUDIO_HAL_16K_SAMPLES;
35+
36+
ret_val |= es8311_codec_init(&cfg);
37+
ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits);
38+
ret_val |= es8311_config_fmt((es_i2s_fmt_t)cfg.i2s_iface.fmt);
39+
ret_val |= es8311_codec_set_voice_volume(volume_);
40+
ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START);
41+
42+
if (ESP_OK != ret_val) {
43+
logger_.error("Codec initialization failed");
44+
return false;
45+
} else {
46+
logger_.info("Codec initialized");
47+
return true;
48+
}
49+
}
50+
51+
bool EspBox::initialize_i2s(uint32_t default_audio_rate) {
52+
logger_.info("initializing i2s driver");
53+
logger_.debug("Using newer I2S standard");
54+
i2s_chan_config_t chan_cfg = {
55+
.id = i2s_port,
56+
.role = I2S_ROLE_MASTER,
57+
.dma_desc_num = 16, // TODO: calculate form audio rate
58+
.dma_frame_num = 48, // TODO: calculate from audio rate
59+
.auto_clear = true,
60+
.intr_priority = 0,
61+
};
62+
63+
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &audio_tx_handle, nullptr));
64+
65+
audio_std_cfg = {
66+
.clk_cfg =
67+
{
68+
.sample_rate_hz = default_audio_rate,
69+
.clk_src = I2S_CLK_SRC_DEFAULT,
70+
.ext_clk_freq_hz = 0,
71+
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
72+
},
73+
.slot_cfg =
74+
I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
75+
.gpio_cfg =
76+
{
77+
.mclk = i2s_mck_io,
78+
.bclk = i2s_bck_io,
79+
.ws = i2s_ws_io,
80+
.dout = i2s_do_io,
81+
.din = i2s_di_io,
82+
.invert_flags =
83+
{
84+
.mclk_inv = false,
85+
.bclk_inv = false,
86+
.ws_inv = false,
87+
},
88+
},
89+
};
90+
audio_std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_256;
91+
92+
ESP_ERROR_CHECK(i2s_channel_init_std_mode(audio_tx_handle, &audio_std_cfg));
93+
94+
auto buffer_size = calc_audio_buffer_size(default_audio_rate);
95+
audio_tx_buffer.resize(buffer_size);
96+
97+
audio_tx_stream = xStreamBufferCreate(buffer_size * 4, 0);
98+
99+
play_audio_task_handle_ = xTaskGetCurrentTaskHandle();
100+
101+
memset(&audio_tx_callbacks_, 0, sizeof(audio_tx_callbacks_));
102+
audio_tx_callbacks_.on_sent = audio_tx_sent_callback;
103+
i2s_channel_register_event_callback(audio_tx_handle, &audio_tx_callbacks_, NULL);
104+
105+
xStreamBufferReset(audio_tx_stream);
106+
107+
ESP_ERROR_CHECK(i2s_channel_enable(audio_tx_handle));
108+
109+
return true;
110+
}
111+
112+
bool EspBox::initialize_sound(uint32_t default_audio_rate,
113+
const espp::Task::BaseConfig &task_config) {
114+
115+
if (!initialize_i2s(default_audio_rate)) {
116+
logger_.error("Could not initialize I2S driver");
117+
return false;
118+
}
119+
if (!initialize_codec()) {
120+
logger_.error("Could not initialize codec");
121+
return false;
122+
}
123+
124+
// Config power control IO
125+
gpio_set_direction(sound_power_pin, GPIO_MODE_OUTPUT);
126+
enable_sound(true);
127+
128+
using namespace std::placeholders;
129+
audio_task_ = espp::Task::make_unique({
130+
.callback = std::bind(&EspBox::audio_task_callback, this, _1, _2, _3),
131+
.task_config = task_config,
132+
});
133+
134+
return audio_task_->start();
135+
}
136+
137+
void EspBox::enable_sound(bool enable) { gpio_set_level(sound_power_pin, enable); }
138+
139+
bool EspBox::audio_task_callback(std::mutex &m, std::condition_variable &cv, bool &task_notified) {
140+
// Queue the next I2S out frame to write
141+
uint16_t available = xStreamBufferBytesAvailable(audio_tx_stream);
142+
int buffer_size = audio_tx_buffer.size();
143+
available = std::min<uint16_t>(available, buffer_size);
144+
uint8_t *buffer = &audio_tx_buffer[0];
145+
memset(buffer, 0, buffer_size);
146+
147+
if (available == 0) {
148+
i2s_channel_write(audio_tx_handle, buffer, buffer_size, NULL, portMAX_DELAY);
149+
} else {
150+
xStreamBufferReceive(audio_tx_stream, buffer, available, 0);
151+
i2s_channel_write(audio_tx_handle, buffer, available, NULL, portMAX_DELAY);
152+
}
153+
return false; // don't stop the task
154+
}
155+
156+
void EspBox::update_volume_output() {
157+
if (mute_) {
158+
es8311_codec_set_voice_volume(0);
159+
} else {
160+
es8311_codec_set_voice_volume(volume_);
161+
}
162+
}
163+
164+
void EspBox::mute(bool mute) {
165+
mute_ = mute;
166+
update_volume_output();
167+
}
168+
169+
bool EspBox::is_muted() const { return mute_; }
170+
171+
void EspBox::volume(float volume) {
172+
volume_ = volume;
173+
update_volume_output();
174+
}
175+
176+
float EspBox::volume() const { return volume_; }
177+
178+
uint32_t EspBox::audio_sample_rate() const { return audio_std_cfg.clk_cfg.sample_rate_hz; }
179+
180+
size_t EspBox::audio_buffer_size() const { return audio_tx_buffer.size(); }
181+
182+
void EspBox::audio_sample_rate(uint32_t sample_rate) {
183+
logger_.info("Setting audio sample rate to {} Hz", sample_rate);
184+
// stop the channel
185+
i2s_channel_disable(audio_tx_handle);
186+
// update the sample rate
187+
audio_std_cfg.clk_cfg.sample_rate_hz = sample_rate;
188+
i2s_channel_reconfig_std_clock(audio_tx_handle, &audio_std_cfg.clk_cfg);
189+
// clear the buffer
190+
xStreamBufferReset(audio_tx_stream);
191+
// restart the channel
192+
i2s_channel_enable(audio_tx_handle);
193+
}
194+
195+
void EspBox::play_audio(const std::vector<uint8_t> &data) { play_audio(data.data(), data.size()); }
196+
197+
void EspBox::play_audio(const uint8_t *data, uint32_t num_bytes) {
198+
play_audio_task_handle_ = xTaskGetCurrentTaskHandle();
199+
if (has_sound) {
200+
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
201+
}
202+
// don't block here
203+
xStreamBufferSendFromISR(audio_tx_stream, data, num_bytes, NULL);
204+
has_sound = true;
205+
}

0 commit comments

Comments
 (0)