mirror of
https://github.com/esphome/esphome.git
synced 2024-11-14 02:58:11 +01:00
Allow i2s microphone bits per sample to be configured (#4884)
This commit is contained in:
parent
9cd173ef83
commit
79abd773a2
7 changed files with 54 additions and 14 deletions
|
@ -20,6 +20,7 @@ DEPENDENCIES = ["i2s_audio"]
|
||||||
CONF_ADC_PIN = "adc_pin"
|
CONF_ADC_PIN = "adc_pin"
|
||||||
CONF_ADC_TYPE = "adc_type"
|
CONF_ADC_TYPE = "adc_type"
|
||||||
CONF_PDM = "pdm"
|
CONF_PDM = "pdm"
|
||||||
|
CONF_BITS_PER_SAMPLE = "bits_per_sample"
|
||||||
|
|
||||||
I2SAudioMicrophone = i2s_audio_ns.class_(
|
I2SAudioMicrophone = i2s_audio_ns.class_(
|
||||||
"I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component
|
"I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component
|
||||||
|
@ -30,10 +31,17 @@ CHANNELS = {
|
||||||
"left": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT,
|
"left": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||||
"right": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT,
|
"right": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT,
|
||||||
}
|
}
|
||||||
|
i2s_bits_per_sample_t = cg.global_ns.enum("i2s_bits_per_sample_t")
|
||||||
|
BITS_PER_SAMPLE = {
|
||||||
|
16: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_16BIT,
|
||||||
|
32: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_32BIT,
|
||||||
|
}
|
||||||
|
|
||||||
INTERNAL_ADC_VARIANTS = [esp32.const.VARIANT_ESP32]
|
INTERNAL_ADC_VARIANTS = [esp32.const.VARIANT_ESP32]
|
||||||
PDM_VARIANTS = [esp32.const.VARIANT_ESP32, esp32.const.VARIANT_ESP32S3]
|
PDM_VARIANTS = [esp32.const.VARIANT_ESP32, esp32.const.VARIANT_ESP32S3]
|
||||||
|
|
||||||
|
_validate_bits = cv.float_with_unit("bits", "bit")
|
||||||
|
|
||||||
|
|
||||||
def validate_esp32_variant(config):
|
def validate_esp32_variant(config):
|
||||||
variant = esp32.get_esp32_variant()
|
variant = esp32.get_esp32_variant()
|
||||||
|
@ -54,6 +62,9 @@ BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend(
|
||||||
cv.GenerateID(): cv.declare_id(I2SAudioMicrophone),
|
cv.GenerateID(): cv.declare_id(I2SAudioMicrophone),
|
||||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||||
cv.Optional(CONF_CHANNEL, default="right"): cv.enum(CHANNELS),
|
cv.Optional(CONF_CHANNEL, default="right"): cv.enum(CHANNELS),
|
||||||
|
cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All(
|
||||||
|
_validate_bits, cv.enum(BITS_PER_SAMPLE)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
@ -93,6 +104,7 @@ async def to_code(config):
|
||||||
cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN]))
|
cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN]))
|
||||||
cg.add(var.set_pdm(config[CONF_PDM]))
|
cg.add(var.set_pdm(config[CONF_PDM]))
|
||||||
|
|
||||||
cg.add(var.set_channel(CHANNELS[config[CONF_CHANNEL]]))
|
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||||
|
cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE]))
|
||||||
|
|
||||||
await microphone.register_microphone(var, config)
|
await microphone.register_microphone(var, config)
|
||||||
|
|
|
@ -16,7 +16,13 @@ static const char *const TAG = "i2s_audio.microphone";
|
||||||
|
|
||||||
void I2SAudioMicrophone::setup() {
|
void I2SAudioMicrophone::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
|
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
|
||||||
this->buffer_.resize(BUFFER_SIZE);
|
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||||
|
this->buffer_ = allocator.allocate(BUFFER_SIZE);
|
||||||
|
if (this->buffer_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate buffer!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if SOC_I2S_SUPPORTS_ADC
|
#if SOC_I2S_SUPPORTS_ADC
|
||||||
if (this->adc_) {
|
if (this->adc_) {
|
||||||
|
@ -48,7 +54,7 @@ void I2SAudioMicrophone::start_() {
|
||||||
i2s_driver_config_t config = {
|
i2s_driver_config_t config = {
|
||||||
.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX),
|
.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX),
|
||||||
.sample_rate = 16000,
|
.sample_rate = 16000,
|
||||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
.bits_per_sample = this->bits_per_sample_,
|
||||||
.channel_format = this->channel_,
|
.channel_format = this->channel_,
|
||||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||||
|
@ -107,16 +113,35 @@ void I2SAudioMicrophone::stop_() {
|
||||||
void I2SAudioMicrophone::read_() {
|
void I2SAudioMicrophone::read_() {
|
||||||
size_t bytes_read = 0;
|
size_t bytes_read = 0;
|
||||||
esp_err_t err =
|
esp_err_t err =
|
||||||
i2s_read(this->parent_->get_port(), this->buffer_.data(), BUFFER_SIZE, &bytes_read, (100 / portTICK_PERIOD_MS));
|
i2s_read(this->parent_->get_port(), this->buffer_, BUFFER_SIZE, &bytes_read, (100 / portTICK_PERIOD_MS));
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
|
|
||||||
this->data_callbacks_.call(this->buffer_);
|
std::vector<int16_t> samples;
|
||||||
|
size_t samples_read = 0;
|
||||||
|
if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) {
|
||||||
|
samples_read = bytes_read / sizeof(int16_t);
|
||||||
|
} else if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_32BIT) {
|
||||||
|
samples_read = bytes_read / sizeof(int32_t);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
samples.resize(samples_read);
|
||||||
|
if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) {
|
||||||
|
memcpy(samples.data(), this->buffer_, bytes_read);
|
||||||
|
} else if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_32BIT) {
|
||||||
|
for (size_t i = 0; i < samples_read; i++) {
|
||||||
|
int32_t temp = reinterpret_cast<int32_t *>(this->buffer_)[i] >> 14;
|
||||||
|
samples[i] = clamp<int16_t>(temp, INT16_MIN, INT16_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->data_callbacks_.call(samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2SAudioMicrophone::loop() {
|
void I2SAudioMicrophone::loop() {
|
||||||
|
|
|
@ -29,6 +29,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; }
|
void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; }
|
||||||
|
void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void start_();
|
void start_();
|
||||||
|
@ -41,8 +42,9 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
|
||||||
bool adc_{false};
|
bool adc_{false};
|
||||||
#endif
|
#endif
|
||||||
bool pdm_{false};
|
bool pdm_{false};
|
||||||
std::vector<uint8_t> buffer_;
|
uint8_t *buffer_;
|
||||||
i2s_channel_fmt_t channel_;
|
i2s_channel_fmt_t channel_;
|
||||||
|
i2s_bits_per_sample_t bits_per_sample_;
|
||||||
|
|
||||||
HighFrequencyLoopRequester high_freq_;
|
HighFrequencyLoopRequester high_freq_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,7 +41,7 @@ async def setup_microphone_core_(var, config):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
trigger,
|
trigger,
|
||||||
[(cg.std_vector.template(cg.uint8).operator("ref").operator("const"), "x")],
|
[(cg.std_vector.template(cg.int16).operator("ref").operator("const"), "x")],
|
||||||
conf,
|
conf,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ template<typename... Ts> class StopCaptureAction : public Action<Ts...>, public
|
||||||
void play(Ts... x) override { this->parent_->stop(); }
|
void play(Ts... x) override { this->parent_->stop(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataTrigger : public Trigger<const std::vector<uint8_t> &> {
|
class DataTrigger : public Trigger<const std::vector<int16_t> &> {
|
||||||
public:
|
public:
|
||||||
explicit DataTrigger(Microphone *mic) {
|
explicit DataTrigger(Microphone *mic) {
|
||||||
mic->add_data_callback([this](const std::vector<uint8_t> &data) { this->trigger(data); });
|
mic->add_data_callback([this](const std::vector<int16_t> &data) { this->trigger(data); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ class Microphone {
|
||||||
public:
|
public:
|
||||||
virtual void start() = 0;
|
virtual void start() = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
void add_data_callback(std::function<void(const std::vector<uint8_t> &)> &&data_callback) {
|
void add_data_callback(std::function<void(const std::vector<int16_t> &)> &&data_callback) {
|
||||||
this->data_callbacks_.add(std::move(data_callback));
|
this->data_callbacks_.add(std::move(data_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class Microphone {
|
||||||
protected:
|
protected:
|
||||||
State state_{STATE_STOPPED};
|
State state_{STATE_STOPPED};
|
||||||
|
|
||||||
CallbackManager<void(const std::vector<uint8_t> &)> data_callbacks_{};
|
CallbackManager<void(const std::vector<int16_t> &)> data_callbacks_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace microphone
|
} // namespace microphone
|
||||||
|
|
|
@ -58,11 +58,12 @@ void VoiceAssistant::setup() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->mic_->add_data_callback([this](const std::vector<uint8_t> &data) {
|
this->mic_->add_data_callback([this](const std::vector<int16_t> &data) {
|
||||||
if (!this->running_) {
|
if (!this->running_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->socket_->sendto(data.data(), data.size(), 0, (struct sockaddr *) &this->dest_addr_, sizeof(this->dest_addr_));
|
this->socket_->sendto(data.data(), data.size() * sizeof(int16_t), 0, (struct sockaddr *) &this->dest_addr_,
|
||||||
|
sizeof(this->dest_addr_));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue