Skip to content

Commit

Permalink
[nabu_microphone] allow amplification shifts by chosen power (#99)
Browse files Browse the repository at this point in the history
* allow amplification shifts by chosen power

* use channel_0 and channel_1 to match XMOS firmware

* make no amplification the default

* use default shift levels for building

* reference this branch to successfully build the firmware

* yamllint fix
  • Loading branch information
kahrendt committed Sep 17, 2024
1 parent 785d96d commit 8fc0c5b
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 63 deletions.
45 changes: 22 additions & 23 deletions esphome/components/nabu_microphone/microphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
CONF_PDM = "pdm"
CONF_SAMPLE_RATE = "sample_rate"
CONF_USE_APLL = "use_apll"
CONF_CHANNEL_0 = "channel_0"
CONF_CHANNEL_1 = "channel_1"
CONF_CHANNEL_2 = "channel_2"
CONF_AMPLIFY = "amplify"
CONF_AMPLIFY_SHIFT = "amplify_shift"

nabu_microphone_ns = cg.esphome_ns.namespace("nabu_microphone")

Expand Down Expand Up @@ -62,6 +62,15 @@ def validate_esp32_variant(config):
raise NotImplementedError


MICROPHONE_CHANNEL_SCHEMA = microphone.MICROPHONE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(NabuMicrophoneChannel),
cv.Optional(CONF_AMPLIFY_SHIFT, default=0): cv.All(
cv.uint8_t, cv.Range(min=0, max=8)
),
}
)

BASE_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(NabuMicrophone),
Expand All @@ -74,18 +83,8 @@ def validate_esp32_variant(config):
I2S_MODE_OPTIONS, lower=True
),
cv.Optional(CONF_USE_APLL, default=False): cv.boolean,
cv.Optional(CONF_CHANNEL_1): microphone.MICROPHONE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(NabuMicrophoneChannel),
cv.Optional(CONF_AMPLIFY, default=True): cv.boolean,
}
),
cv.Optional(CONF_CHANNEL_2): microphone.MICROPHONE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(NabuMicrophoneChannel),
cv.Optional(CONF_AMPLIFY, default=True): cv.boolean,
}
),
cv.Optional(CONF_CHANNEL_0): MICROPHONE_CHANNEL_SCHEMA,
cv.Optional(CONF_CHANNEL_1): MICROPHONE_CHANNEL_SCHEMA,
}
).extend(cv.COMPONENT_SCHEMA)

Expand Down Expand Up @@ -116,21 +115,21 @@ async def to_code(config):

await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])

if channel_0_config := config.get(CONF_CHANNEL_0):
channel_0 = cg.new_Pvariable(channel_0_config[CONF_ID])
await cg.register_component(channel_0, channel_0_config)
await cg.register_parented(channel_0, config[CONF_ID])
await microphone.register_microphone(channel_0, channel_0_config)
cg.add(var.set_channel_0(channel_0))
cg.add(channel_0.set_amplify_shift(channel_0_config[CONF_AMPLIFY_SHIFT]))

if channel_1_config := config.get(CONF_CHANNEL_1):
channel_1 = cg.new_Pvariable(channel_1_config[CONF_ID])
await cg.register_component(channel_1, channel_1_config)
await cg.register_parented(channel_1, config[CONF_ID])
await microphone.register_microphone(channel_1, channel_1_config)
cg.add(var.set_channel_1(channel_1))
cg.add(channel_1.set_amplify(channel_1_config[CONF_AMPLIFY]))

if channel_2_config := config.get(CONF_CHANNEL_2):
channel_2 = cg.new_Pvariable(channel_2_config[CONF_ID])
await cg.register_component(channel_2, channel_2_config)
await cg.register_parented(channel_2, config[CONF_ID])
await microphone.register_microphone(channel_2, channel_2_config)
cg.add(var.set_channel_2(channel_2))
cg.add(channel_2.set_amplify(channel_2_config[CONF_AMPLIFY]))
cg.add(channel_1.set_amplify_shift(channel_1_config[CONF_AMPLIFY_SHIFT]))

if config[CONF_ADC_TYPE] == "internal":
variant = esp32.get_esp32_variant()
Expand Down
60 changes: 30 additions & 30 deletions esphome/components/nabu_microphone/nabu_microphone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,21 @@ void NabuMicrophone::setup() {
}

void NabuMicrophone::mute() {
if (this->channel_0_ != nullptr) {
this->channel_0_->set_mute_state(true);
}
if (this->channel_1_ != nullptr) {
this->channel_1_->set_mute_state(true);
}
if (this->channel_2_ != nullptr) {
this->channel_2_->set_mute_state(true);
}
}

void NabuMicrophone::unmute() {
if (this->channel_0_ != nullptr) {
this->channel_0_->set_mute_state(false);
}
if (this->channel_1_ != nullptr) {
this->channel_1_->set_mute_state(false);
}
if (this->channel_2_ != nullptr) {
this->channel_2_->set_mute_state(false);
}
}

esp_err_t NabuMicrophone::start_i2s_driver_() {
Expand Down Expand Up @@ -203,14 +203,14 @@ void NabuMicrophone::read_task_(void *params) {
event.type = TaskEventType::STARTING;
xQueueSend(this_microphone->event_queue_, &event, portMAX_DELAY);

if ((this_microphone->channel_1_ != nullptr) && this_microphone->channel_1_->is_failed()) {
if ((this_microphone->channel_0_ != nullptr) && this_microphone->channel_0_->is_failed()) {
event.type = TaskEventType::WARNING;
event.err = ESP_ERR_INVALID_STATE;
xQueueSend(this_microphone->event_queue_, &event, portMAX_DELAY);
continue;
}

if ((this_microphone->channel_2_ != nullptr) && this_microphone->channel_2_->is_failed()) {
if ((this_microphone->channel_1_ != nullptr) && this_microphone->channel_1_->is_failed()) {
event.type = TaskEventType::WARNING;
event.err = ESP_ERR_INVALID_STATE;
xQueueSend(this_microphone->event_queue_, &event, portMAX_DELAY);
Expand All @@ -221,17 +221,17 @@ void NabuMicrophone::read_task_(void *params) {
ExternalRAMAllocator<int32_t> allocator(ExternalRAMAllocator<int32_t>::ALLOW_FAILURE);
int32_t *buffer = allocator.allocate(SAMPLES_IN_ALL_DMA_BUFFERS);

std::vector<int16_t, ExternalRAMAllocator<int16_t>> channel_0_samples;
std::vector<int16_t, ExternalRAMAllocator<int16_t>> channel_1_samples;
std::vector<int16_t, ExternalRAMAllocator<int16_t>> channel_2_samples;

if (this_microphone->channel_0_ != nullptr)
channel_0_samples.reserve(FRAMES_IN_ALL_DMA_BUFFERS);

if (this_microphone->channel_1_ != nullptr)
channel_1_samples.reserve(FRAMES_IN_ALL_DMA_BUFFERS);

if (this_microphone->channel_2_ != nullptr)
channel_2_samples.reserve(FRAMES_IN_ALL_DMA_BUFFERS);

if ((buffer == nullptr) || (channel_1_samples.capacity() < FRAMES_IN_ALL_DMA_BUFFERS) ||
(channel_2_samples.capacity() < FRAMES_IN_ALL_DMA_BUFFERS)) {
if ((buffer == nullptr) || (channel_0_samples.capacity() < FRAMES_IN_ALL_DMA_BUFFERS) ||
(channel_1_samples.capacity() < FRAMES_IN_ALL_DMA_BUFFERS)) {
event.type = TaskEventType::WARNING;
event.err = ESP_ERR_NO_MEM;
xQueueSend(this_microphone->event_queue_, &event, portMAX_DELAY);
Expand All @@ -243,8 +243,8 @@ void NabuMicrophone::read_task_(void *params) {
xQueueSend(this_microphone->event_queue_, &event, portMAX_DELAY);
} else {
// TODO: Is this the ideal spot to reset the ring buffers?
this_microphone->channel_0_->get_ring_buffer()->reset();
this_microphone->channel_1_->get_ring_buffer()->reset();
this_microphone->channel_2_->get_ring_buffer()->reset();

event.type = TaskEventType::STARTED;
xQueueSend(this_microphone->event_queue_, &event, portMAX_DELAY);
Expand Down Expand Up @@ -272,31 +272,31 @@ void NabuMicrophone::read_task_(void *params) {
const size_t frames_read =
samples_read / NUMBER_OF_CHANNELS; // Left and right channel samples combine into 1 frame

const uint8_t channel_1_shift = 16 - 2 * this_microphone->channel_1_->get_amplify();
const uint8_t channel_2_shift = 16 - 2 * this_microphone->channel_2_->get_amplify();
const uint8_t channel_0_shift = 16 - this_microphone->channel_0_->get_amplify_shift();
const uint8_t channel_1_shift = 16 - this_microphone->channel_1_->get_amplify_shift();

for (size_t i = 0; i < frames_read; i++) {
int32_t channel_0_sample = 0;
if ((this_microphone->channel_0_ != nullptr) && (!this_microphone->channel_0_->get_mute_state())) {
channel_0_sample = buffer[NUMBER_OF_CHANNELS * i] >> channel_0_shift;
channel_0_samples[i] = (int16_t)clamp<int32_t>(channel_0_sample, INT16_MIN, INT16_MAX);
}

int32_t channel_1_sample = 0;
if ((this_microphone->channel_1_ != nullptr) && (!this_microphone->channel_1_->get_mute_state())) {
channel_1_sample = buffer[NUMBER_OF_CHANNELS * i] >> channel_1_shift;
channel_1_sample = buffer[NUMBER_OF_CHANNELS * i + 1] >> channel_1_shift;
channel_1_samples[i] = (int16_t)clamp<int32_t>(channel_1_sample, INT16_MIN, INT16_MAX);
}

int32_t channel_2_sample = 0;
if ((this_microphone->channel_2_ != nullptr) && (!this_microphone->channel_2_->get_mute_state())) {
channel_2_sample = buffer[NUMBER_OF_CHANNELS * i + 1] >> channel_2_shift;
channel_2_samples[i] = (int16_t)clamp<int32_t>(channel_2_sample, INT16_MIN, INT16_MAX);
}
}

size_t bytes_to_write = frames_read * sizeof(int16_t);

if (this_microphone->channel_1_ != nullptr) {
this_microphone->channel_1_->get_ring_buffer()->write((void *) channel_1_samples.data(),
if (this_microphone->channel_0_ != nullptr) {
this_microphone->channel_0_->get_ring_buffer()->write((void *) channel_0_samples.data(),
bytes_to_write);
}
if (this_microphone->channel_2_ != nullptr) {
this_microphone->channel_2_->get_ring_buffer()->write((void *) channel_2_samples.data(),
if (this_microphone->channel_1_ != nullptr) {
this_microphone->channel_1_->get_ring_buffer()->write((void *) channel_1_samples.data(),
bytes_to_write);
}
}
Expand Down Expand Up @@ -347,8 +347,8 @@ void NabuMicrophone::stop() {
}

void NabuMicrophone::loop() {
if ((this->channel_1_ != nullptr) && (this->channel_1_->get_requested_stop()) && (this->channel_2_ != nullptr) &&
(this->channel_2_->get_requested_stop())) {
if ((this->channel_0_ != nullptr) && (this->channel_0_->get_requested_stop()) && (this->channel_1_ != nullptr) &&
(this->channel_1_->get_requested_stop())) {
// Both microphone channels have requested a stop
this->stop();
}
Expand Down
13 changes: 8 additions & 5 deletions esphome/components/nabu_microphone/nabu_microphone.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ class NabuMicrophone : public i2s_audio::I2SAudioIn, public Component {
void mute();
void unmute();

void set_channel_0(NabuMicrophoneChannel *microphone) { this->channel_0_ = microphone; }
void set_channel_1(NabuMicrophoneChannel *microphone) { this->channel_1_ = microphone; }
void set_channel_2(NabuMicrophoneChannel *microphone) { this->channel_2_ = microphone; }

NabuMicrophoneChannel *get_channel_0() { return this->channel_0_; }
NabuMicrophoneChannel *get_channel_1() { return this->channel_0_; }

#if SOC_I2S_SUPPORTS_ADC
void set_adc_channel(adc1_channel_t channel) {
Expand Down Expand Up @@ -73,8 +76,8 @@ class NabuMicrophone : public i2s_audio::I2SAudioIn, public Component {
TaskHandle_t read_task_handle_{nullptr};
QueueHandle_t event_queue_;

NabuMicrophoneChannel *channel_0_{nullptr};
NabuMicrophoneChannel *channel_1_{nullptr};
NabuMicrophoneChannel *channel_2_{nullptr};

bool use_apll_;
bool pdm_{false};
Expand Down Expand Up @@ -124,14 +127,14 @@ class NabuMicrophoneChannel : public microphone::Microphone, public Component {

RingBuffer *get_ring_buffer() { return this->ring_buffer_.get(); }

void set_amplify(bool amplify) { this->amplify_ = amplify; }
bool get_amplify() { return this->amplify_; }
void set_amplify_shift(uint8_t amplify_shift) { this->amplify_shift_ = amplify_shift; }
uint8_t get_amplify_shift() { return this->amplify_shift_; }

protected:
NabuMicrophone *parent_;
std::unique_ptr<RingBuffer> ring_buffer_;

bool amplify_;
uint8_t amplify_shift_;
bool is_muted_;
bool requested_stop_;
};
Expand Down
10 changes: 5 additions & 5 deletions home-assistant-voice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1236,12 +1236,12 @@ microphone:
bits_per_sample: 32bit
i2s_mode: secondary
i2s_audio_id: i2s_input
channel_1:
channel_0:
id: asr_mic
amplify: false
channel_2:
amplify_shift: 0
channel_1:
id: comm_mic
amplify: false # May be useful to enable, but may also result in worse wake word detection if speaking close to the device
amplify_shift: 0 # May be useful to enable, but may also result in worse wake word detection if speaking close to the device

media_player:
- platform: nabu
Expand Down Expand Up @@ -1321,7 +1321,7 @@ external_components:
- source:
type: git
url: https://github.com/esphome/voice-kit
ref: dev
ref: kahrendt-20240911-mic-amp-levels
components:
- aic3204
- audio_dac
Expand Down

0 comments on commit 8fc0c5b

Please sign in to comment.