Audio#

The espp::Audio component drives one I2S port with 16-bit PCM: a TX channel for playback, an RX channel for recording, or both, full duplex, in standard (Philips) mode. An instance that only records can instead receive TDM (e.g. an ES7210 with four microphones) or a PDM microphone, and HardwareConfig::recording_slots picks the slots its callback receives. It owns the machinery the espp BSPs used to copy into each board:

  • the I2S channels and their DMA rings, sized so the TX ring outlasts the playback task’s bounded wait,

  • the playback stream buffer and task, which always writes whole blocks so the TX ring never runs dry (no pops between sounds),

  • the recording task, which drains the RX ring continuously and hands the samples to a callback while recording,

  • volume, mute and microphone gain (in the codec, or volume and mute in software when there is no codec),

  • sample rate changes, and

  • a complete unwind when initialization fails, deinitialize() and the destructor.

Chip-specific setup goes through the espp::AudioCodec interface. This component provides adapters for the ES8311 (espp::Es8311Codec, playback and recording), the ES8388 (espp::Es8388Codec, playback) and the ES7210 (espp::Es7210Codec, recording), on the drivers in the codec component. A chip that handles both directions, like the ES8311, is passed as both the output and the input codec.

espp::Audio::Config has two parts: an espp::Audio::AppConfig (what an application chooses: the sample rate, which directions run, the playback buffer and the tasks) and an espp::Audio::HardwareConfig (how the board is wired: the I2S port and pins, channels, the recording format and slots, the MCLK multiple and the codecs). The BSPs take only the AppConfig, as their AudioConfig, and fill in the HardwareConfig for their board.

Board-specific parts - amplifier enable pins, IO expanders, buttons - stay in the BSP. Config::on_playback_block lets a BSP follow playback activity (the M5Stack Tab5 pauses its battery charger with it, since the charger pops the speaker). A board whose microphones are on a separate I2S port uses two instances: one for playback and one for recording, which such a BSP’s microphone_audio() returns.

BSP audio API#

The BSPs that use this component expose the same audio methods, which forward to it: initialize_audio() / initialize_audio(const AudioConfig &) (AudioConfig is espp::Audio::AppConfig; default_audio_config() returns the board’s defaults to start one from), deinitialize_audio(), is_audio_initialized(), audio(), enable_audio(bool) (the amplifier), play_audio(std::span<const uint8_t>) / play_audio(const uint8_t *, size_t), clear_audio(), volume(), mute() / is_muted(), audio_sample_rate(), audio_buffer_size(), start_audio_recording(callback) / stop_audio_recording() and microphone_volume(). Older names are kept as [[deprecated]] methods that forward to these.

API Reference#

Header File#

Classes#

class Audio : public espp::BaseComponent#

I2S audio playback and recording, shared by the espp BSPs.

One instance drives one I2S port in standard (Philips) mode with 16-bit PCM: a TX channel for playback, an RX channel for recording, or both, full duplex on the same clocks. It owns all of the machinery that used to be copied into each BSP:

  • the I2S channels and their DMA rings, sized so the ring outlasts the playback task’s bounded wait (i2s_tx_feed.hpp),

  • the playback stream buffer and task, which always writes whole blocks so the TX ring never runs dry,

  • the recording task, which drains the RX ring continuously and hands the samples to a callback while recording,

  • volume / mute (in the codec, or in software when it has none) and microphone gain,

  • sample rate changes, and

  • a complete unwind on failure, deinitialize() and the destructor.

Chip-specific configuration goes through espp::AudioCodec (see the ES8388 / ES7210 adapters in this component). Board-specific parts - amplifier enable pins, IO expanders, buttons - stay in the BSP; Config::on_playback_block lets a BSP follow playback activity (the M5Stack Tab5 pauses its charger with it).

A board whose microphones are on a separate I2S port uses two instances: one for playback and one for recording.

Example#

  // Playback only, to an I2S amplifier with no codec (e.g. a MAX98357A):
  // volume and mute are applied in software.
  espp::Audio audio({
      .app = {.sample_rate = 44100, .playback_enabled = true, .recording_enabled = false},
      .hardware = {.pins = {.bclk = static_cast<gpio_num_t>(CONFIG_EXAMPLE_I2S_BCLK_GPIO),
                            .ws = static_cast<gpio_num_t>(CONFIG_EXAMPLE_I2S_WS_GPIO),
                            .dout = static_cast<gpio_num_t>(CONFIG_EXAMPLE_I2S_DOUT_GPIO)}},
      .volume = 30.0f,
      .log_level = espp::Logger::Verbosity::INFO,
  });
  if (!audio.initialize()) {
    logger.error("Could not initialize the audio");
    return;
  }

  // One second of a 440 Hz tone, 16-bit interleaved stereo
  const uint32_t rate = audio.sample_rate();
  std::vector<int16_t> tone(rate * 2);
  for (size_t i = 0; i < rate; i++) {
    const float t = static_cast<float>(i) / rate;
    const auto sample =
        static_cast<int16_t>(std::sin(2.0f * std::numbers::pi_v<float> * 440.0f * t) * 8000.0f);
    tone[2 * i] = sample;
    tone[2 * i + 1] = sample;
  }
  std::span<const uint8_t> bytes(reinterpret_cast<const uint8_t *>(tone.data()),
                                 tone.size() * sizeof(int16_t));

  // play() queues what fits and returns how much that was: stream the tone in
  // from the returned offset
  auto play_all = [&](std::span<const uint8_t> data) {
    while (!data.empty()) {
      const size_t queued = audio.play(data);
      data = data.subspan(queued);
      if (queued == 0) {
        std::this_thread::sleep_for(5ms);
      }
    }
  };

  for (float volume : {30.0f, 60.0f, 90.0f}) {
    logger.info("Playing the tone at {:.0f} % volume", volume);
    audio.volume(volume);
    play_all(bytes);
    std::this_thread::sleep_for(500ms);
  }
  logger.info("Playing the tone muted");
  audio.mute(true);
  play_all(bytes);
  // play_all() returns once the tone is queued (the stream and the DMA ring
  // still hold about 130 ms): let that play out muted before unmuting
  std::this_thread::sleep_for(250ms);
  audio.mute(false);

  // the audio is torn down when `audio` goes out of scope; deinitialize() does
  // it explicitly
  std::this_thread::sleep_for(1s);
  audio.deinitialize();

Public Types

enum class RecordingFormat#

How the recording (RX) channel receives.

Values:

enumerator STANDARD#

Standard (Philips) I2S: two slots per frame. In full duplex it runs on the playback clocks.

enumerator TDM#

TDM: HardwareConfig::recording_tdm_slots slots per frame (e.g. an ES7210 with four microphones). Recording only; needs SOC_I2S_SUPPORTS_TDM.

enumerator PDM#

A PDM microphone: Pins::ws is its clock and Pins::din its data; mono. Recording only; needs SOC_I2S_SUPPORTS_PDM_RX.

using recording_callback_t = std::function<void(const uint8_t *data, size_t length)>#

Function called with recorded audio: 16-bit signed PCM, interleaved when HardwareConfig::recording_channels is 2. Runs on the recording task; it may call any method except start_recording() (which fails from it: it waits for the callback to return), initialize() and deinitialize().

using playback_block_callback_t = std::function<void(bool active)>#

Function called by the playback task once per block with whether the block carried audio (false: silence). Keep it short and non-blocking; it must not call initialize() or deinitialize().

Public Functions

explicit Audio(const Config &config)#

Construct (does not touch the hardware; call initialize()).

Parameters:

config – The configuration.

~Audio()#

Destroy - deinitializes if still initialized.

bool initialize(std::error_code &ec)#

Set up the I2S channels, codecs and tasks. Idempotent.

Parameters:

ec – Set to the error on failure; cleared on success.

Returns:

true on success. A failure part way through undoes what was set up before it, so the call can be retried.

bool initialize()#

Convenience overload that logs on failure.

Returns:

true on success.

void deinitialize()#

Stop the tasks and codecs and free the I2S channels and buffers. Idempotent.

inline bool is_initialized() const#
Returns:

true once initialize() has succeeded (until deinitialize()).

inline bool is_playback_enabled() const#
Returns:

Whether playback is set up (AppConfig::playback_enabled).

inline bool is_recording_enabled() const#
Returns:

Whether recording is set up (AppConfig::recording_enabled).

size_t play(std::span<const uint8_t> data)#

Queue audio for playback.

Note

Non-blocking; call it from a task, not an ISR. Returns 0 when playback is not initialized.

Parameters:

data – 16-bit signed PCM, interleaved when HardwareConfig::playback_channels is 2.

Returns:

The number of bytes queued: whole frames only, and less than the data when the stream buffer is full. To stream more than the buffer holds, call it again from the returned offset.

size_t play(const uint8_t *data, size_t num_bytes)#

Queue audio for playback.

Parameters:
Returns:

The number of bytes queued (see play(std::span)).

void clear()#

Drop the audio queued for playback.

void volume(float volume)#

Set the output volume.

Note

Before initialize() (or without playback) the value is stored and applied when playback starts.

Parameters:

volume – The volume, 0 - 100 (clamped).

inline float volume() const#
Returns:

The output volume, 0 - 100.

void mute(bool mute)#

Mute or unmute the output.

Note

Before initialize() (or without playback) the value is stored and applied when playback starts.

Parameters:

mute – true to mute.

inline bool is_muted() const#
Returns:

Whether the output is muted.

bool sample_rate(uint32_t sample_rate)#

Change the sample rate: the I2S clocks, the codecs and, since the queued audio was for the old rate, the playback stream (it is cleared).

Note

The block size (buffer_size()) stays the one for the rate passed to initialize().

Parameters:

sample_rate – The new sample rate, in Hz (at least UPDATE_FREQUENCY).

Returns:

true on success (false when not initialized, a codec does not support the rate - the previous rate is then restored - the rate is below UPDATE_FREQUENCY, or the playback DMA ring, sized for the rate passed to initialize(), is too short for it: initialize() at the highest rate you will use).

inline uint32_t sample_rate() const#
Returns:

The sample rate, in Hz.

inline size_t buffer_size() const#
Returns:

The size of one playback block, in bytes (0 before initialize()): sample_rate / UPDATE_FREQUENCY frames, rounded down (at most 1 / UPDATE_FREQUENCY seconds).

bool start_recording(const recording_callback_t &callback)#

Start delivering recorded audio to callback.

Note

Not from the recording callback (it would replace the function that is running).

Parameters:

callback – Called on the recording task with each buffer of samples (one block’s worth).

Returns:

false when recording is not initialized, the callback is empty or a recording is already in progress (stop it first).

void stop_recording()#

Stop delivering recorded audio. The recording task keeps draining the RX ring (which in full duplex keeps playback clean).

inline bool is_recording() const#
Returns:

Whether a recording is in progress.

void microphone_volume(float volume)#

Set the microphone gain.

Note

Before initialize() (or without recording) the value is stored and applied when recording starts.

Parameters:

volume – The gain, 0 - 100 (clamped), mapped onto the input codec’s range.

inline float microphone_volume() const#
Returns:

The microphone gain, 0 - 100.

inline i2s_chan_handle_t tx_handle() const#
Returns:

The I2S TX channel (nullptr without playback).

inline i2s_chan_handle_t rx_handle() const#
Returns:

The I2S RX channel (nullptr without recording).

inline const std::string &get_name() const#

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

template<typename T = void>
inline void set_log_tag(const std::string_view&)#

Not available: the tag is fixed at construction (pass it to the constructor). Enable CONFIG_ESPP_LOGGER_MUTABLE_TAG (menuconfig: ESPP Logger -> Mutable tag) to change it afterwards.

inline espp::Logger::Verbosity get_log_level() const#

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)#

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)#

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const#

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)#

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

Public Static Attributes

static constexpr uint32_t UPDATE_FREQUENCY = 60#

How many blocks per second the playback task writes: a block is sample_rate / UPDATE_FREQUENCY frames, rounded down to whole frames (so at most 1 / UPDATE_FREQUENCY seconds of audio).

struct AppConfig#

What an application chooses: the rate, which directions run, buffering and the tasks. The BSPs take this (as their AudioConfig) and fill in the HardwareConfig for their board.

Public Members

uint32_t sample_rate = {48000}#

Sample rate, in Hz (at least UPDATE_FREQUENCY).

uint32_t recording_sample_rate = {0}#

The recording rate, in Hz, for an instance that only records (0: sample_rate). An instance that plays and records runs both on one clock, so it must be 0 or sample_rate there; a playback-only instance ignores it. Lets a board whose microphones have their own I2S port (e.g. the T-Deck) record at a rate other than it plays.

bool playback_enabled = {true}#

Set up playback (the TX channel, the output codec and the playback task).

bool recording_enabled = {false}#

Set up recording (the RX channel, the input codec and the recording task).

Note

Each I2S channel has a DMA ring in internal DMA-capable RAM (about 11.5 KB of stereo frames up to 48 kHz; half that for an instance that only records, times the slots / 2 for TDM) and each task a stack in internal RAM, so a direction that is not needed is better left off.

size_t playback_buffer_size = {0}#

Size of the playback stream that play() queues into, in bytes (0: four blocks, about 67 ms). Larger lets one play() queue a whole UI sound; at least two blocks.

uint32_t playback_buffer_caps = {MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT}#

Heap capabilities for the playback stream (e.g. MALLOC_CAP_SPIRAM for a large one).

espp::Task::BaseConfig playback_task_config  {.name = "audio out", .stack_size_bytes = 4096, .priority = 20}

The playback task’s configuration (unpinned by default; a BSP can set core_id for its board).

espp::Task::BaseConfig recording_task_config  {.name = "audio in", .stack_size_bytes = 4096, .priority = 20}

The recording task’s configuration (its stack also runs the recording callback; unpinned by default).

struct Config#

Configuration.

Public Members

AppConfig app = {}#

What the application chooses.

HardwareConfig hardware = {}#

How the board is wired.

float volume = {50.0f}#

Initial output volume, 0 - 100.

bool muted = {false}#

Initial mute state.

float microphone_volume = {70.0f}#

Initial microphone gain, 0 - 100.

playback_block_callback_t on_playback_block = {}#

Called by the playback task once per block (optional).

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}#

Log verbosity.

struct HardwareConfig#

How the board is wired: the I2S port and pins, the slot layout and the codecs. A BSP fills this in for its board.

Public Members

int port = {I2S_NUM_0}#

I2S port (I2S_NUM_0, I2S_NUM_1, …).

Pins pins = {}#

I2S pins.

uint8_t playback_channels = {2}#

Playback channels: 2 (interleaved stereo) or 1 (mono).

uint8_t recording_channels = {2}#

Recording channels: 2 (interleaved stereo) or 1 (mono); other values are rejected. They are taken from recording_slots (by default mono is the left slot).

Note

Mono is received as stereo slots and the callback gets the left slot only: an RX channel in mono slot mode still delivers both slots (measured on the ESP32-P4, recording only and full duplex: twice the samples, in a DMA ring sized for one). So mono costs a stereo RX DMA ring.

RecordingFormat recording_format = {RecordingFormat::STANDARD}#

How the RX channel receives (STANDARD, TDM or PDM). TDM and PDM are for an instance that only records (playback_enabled = false), e.g. a board whose microphones have their own I2S port.

uint8_t recording_tdm_slots = {4}#

TDM: slots per frame, 2 - 16.

std::array<uint8_t, 2> recording_slots = {0, 1}#

The slots the callback receives, in order: the first recording_channels (1 or 2) of them (STANDARD: of 2, TDM: of recording_tdm_slots; PDM has one). E.g. {0, 2} keeps a four-slot ES7210’s MIC1 and MIC3 as stereo, and {1} with one channel the right slot as mono.

i2s_mclk_multiple_t mclk_multiple = {I2S_MCLK_MULTIPLE_256}#

MCLK as a multiple of the sample rate.

std::shared_ptr<AudioCodec> output_codec = {}#

The codec for playback (nullptr: none, e.g. an I2S amplifier; volume and mute are then applied in software).

std::shared_ptr<AudioCodec> input_codec = {}#

The codec for recording (nullptr: none, e.g. an I2S microphone). May be the same object as output_codec.

struct Pins#

I2S pins. Unused pins are GPIO_NUM_NC.

Public Members

gpio_num_t mclk = {GPIO_NUM_NC}#

Master clock (NC for codecs that derive it from BCLK).

gpio_num_t bclk = {GPIO_NUM_NC}#

Bit clock.

gpio_num_t ws = {GPIO_NUM_NC}#

Word select (LRCK).

gpio_num_t dout = {GPIO_NUM_NC}#

Data out (to the DAC / amplifier), used for playback.

gpio_num_t din = {GPIO_NUM_NC}#

Data in (from the ADC / microphones), used for recording.

Header File#

Classes#

class AudioCodec#

Interface between espp::Audio and an audio codec chip.

espp::Audio owns the I2S channels, the playback and recording tasks and the stream buffer; a codec only has to configure its chip. espp::Audio calls these methods from initialize(), deinitialize() and its setters - never per sample or per block - so the virtual dispatch costs nothing measurable.

A chip that handles both directions (e.g. an ES8311) is passed as both the output and the input codec of espp::Audio::Config; it is then started once, with Direction::BOTH.

The I2S clocks (MCLK, BCLK, WS) are running when start() is called, since most codecs need MCLK to accept their register writes.

Subclassed by espp::Es7210Codec, espp::Es8311Codec, espp::Es8388Codec

Public Types

enum class Direction : uint8_t#

Which direction(s) a codec is started for.

Values:

enumerator PLAYBACK#

DAC: I2S TX to the speaker / headphones.

enumerator RECORDING#

ADC: microphones to I2S RX.

enumerator BOTH#

DAC and ADC.

Public Functions

virtual const char *name() const = 0#
Returns:

A short name for logs (e.g. “ES8388”).

virtual bool start(Direction direction, uint32_t sample_rate) = 0#

Configure the chip and start it.

Parameters:
  • direction – The direction(s) to start.

  • sample_rate – The sample rate the I2S clocks run at, in Hz.

Returns:

true on success. On failure espp::Audio still calls stop(), since a chip may be partly configured.

inline virtual uint32_t mclk_multiple() const#
Returns:

The MCLK / sample rate ratio the chip’s configuration assumes (espp::Audio::HardwareConfig::mclk_multiple must match), or 0 if it follows any.

virtual void stop() = 0#

Stop the chip (and power down what start() powered up).

inline virtual bool set_sample_rate(uint32_t sample_rate)#

Follow a change of the I2S sample rate (the clocks are already running at the new rate).

Parameters:

sample_rate – The new sample rate, in Hz.

Returns:

true on success.

inline virtual bool has_volume() const#
Returns:

true if the chip has a hardware output volume and mute; if not, espp::Audio scales the samples in software instead.

inline virtual bool set_volume(float percent)#

Set the output volume.

Parameters:

percent – The volume, 0 - 100.

Returns:

true on success.

inline virtual bool set_mute(bool mute)#

Mute or unmute the output.

Parameters:

mute – true to mute.

Returns:

true on success.

inline virtual bool has_gain() const#
Returns:

true if the chip has a hardware input (microphone) gain.

inline virtual bool set_gain(float percent)#

Set the input (microphone) gain.

Parameters:

percent – The gain, 0 - 100, mapped onto the chip’s gain range.

Returns:

true on success.

Header File#

Classes#

class Es8311Codec : public espp::AudioCodec#

espp::AudioCodec for the ES8311 mono codec (DAC and ADC), on the codec component’s ES8311 driver.

Configures the ES8311 as an I2S slave for 16-bit Philips data at the I2S sample rate (its clock coefficients follow set_sample_rate() too), drives its hardware volume and mute and maps a 0 - 100 % microphone gain onto its analog gain steps (0 dB - 42 dB). It handles both directions, so pass the same instance as espp::Audio::HardwareConfig::output_codec and input_codec; its ADC drives the left slot (use recording_channels = 1).

Note

The codec component’s ES8311 driver keeps its register access functions in globals, so there can be only one ES8311 per firmware. This class sets them when it is constructed.

Note

It logs through ESP_LOGx (tag “Es8311Codec”) rather than an espp::Logger, whose mutex would cost internal RAM for the few messages an init failure prints.

Public Types

enum class Direction : uint8_t#

Which direction(s) a codec is started for.

Values:

enumerator PLAYBACK#

DAC: I2S TX to the speaker / headphones.

enumerator RECORDING#

ADC: microphones to I2S RX.

enumerator BOTH#

DAC and ADC.

Public Functions

explicit Es8311Codec(const Config &config)#

Construct and wire up the driver’s register access (no I2C traffic yet).

Parameters:

config – The configuration.

~Es8311Codec() override#

Clear the driver’s register access functions, which hold the I2C device (e.g. a shared_ptr captured by espp::make_i2c_addressed_write()), unless a newer instance has set them since.

inline virtual const char *name() const override#
Returns:

A short name for logs (e.g. “ES8388”).

inline virtual uint32_t mclk_multiple() const override#
Returns:

256: the driver selects its clock coefficients for MCLK = 256 x the rate.

virtual bool start(Direction direction, uint32_t sample_rate) override#

Configure the chip and start it.

Parameters:
  • direction – The direction(s) to start.

  • sample_rate – The sample rate the I2S clocks run at, in Hz.

Returns:

true on success. On failure espp::Audio still calls stop(), since a chip may be partly configured.

virtual void stop() override#

Stop the chip (and power down what start() powered up).

virtual bool set_sample_rate(uint32_t sample_rate) override#

Follow a change of the I2S sample rate (the clocks are already running at the new rate).

Parameters:

sample_rate – The new sample rate, in Hz.

Returns:

true on success.

inline virtual bool has_volume() const override#
Returns:

true if the chip has a hardware output volume and mute; if not, espp::Audio scales the samples in software instead.

virtual bool set_volume(float percent) override#

Set the output volume.

Parameters:

percent – The volume, 0 - 100.

Returns:

true on success.

virtual bool set_mute(bool mute) override#

Mute or unmute the output.

Parameters:

mute – true to mute.

Returns:

true on success.

inline virtual bool has_gain() const override#
Returns:

true if the chip has a hardware input (microphone) gain.

virtual bool set_gain(float percent) override#

Set the input (microphone) gain.

Parameters:

percent – The gain, 0 - 100, mapped onto the chip’s gain range.

Returns:

true on success.

struct Config#

Configuration.

Public Members

write_fn write#

Register write, e.g. espp::make_i2c_addressed_write(device).

read_register_fn read#

Register read, e.g. espp::make_i2c_addressed_read_register(device).

Header File#

Classes#

class Es8388Codec : public espp::AudioCodec#

espp::AudioCodec for the ES8388’s DAC (playback), on the codec component’s ES8388 driver.

Configures the ES8388 as an I2S slave for 16-bit Philips data, with both DAC outputs enabled, and drives its hardware volume and mute. Its ADC is not used (boards with an ES8388 record through an ES7210, see espp::Es7210Codec), so start() supports Direction::PLAYBACK only.

Note

The codec component’s ES8388 driver keeps its register access functions in globals, so there can be only one ES8388 per firmware. This class sets them when it is constructed.

Note

It logs through ESP_LOGx (tag “Es8388Codec”) rather than an espp::Logger, whose mutex would cost internal RAM for the few messages an init failure prints.

Public Types

enum class Direction : uint8_t#

Which direction(s) a codec is started for.

Values:

enumerator PLAYBACK#

DAC: I2S TX to the speaker / headphones.

enumerator RECORDING#

ADC: microphones to I2S RX.

enumerator BOTH#

DAC and ADC.

Public Functions

explicit Es8388Codec(const Config &config)#

Construct and wire up the driver’s register access (no I2C traffic yet).

Parameters:

config – The configuration.

~Es8388Codec() override#

Clear the driver’s register access functions, which hold the I2C device (e.g. a shared_ptr captured by espp::make_i2c_addressed_write()), unless a newer instance has set them since.

inline virtual const char *name() const override#
Returns:

A short name for logs (e.g. “ES8388”).

inline virtual uint32_t mclk_multiple() const override#
Returns:

256: the driver sets its DAC to MCLK = 256 x the rate.

virtual bool start(Direction direction, uint32_t sample_rate) override#

Configure the chip and start it.

Parameters:
  • direction – The direction(s) to start.

  • sample_rate – The sample rate the I2S clocks run at, in Hz.

Returns:

true on success. On failure espp::Audio still calls stop(), since a chip may be partly configured.

virtual void stop() override#

Stop the chip (and power down what start() powered up).

inline virtual bool has_volume() const override#
Returns:

true if the chip has a hardware output volume and mute; if not, espp::Audio scales the samples in software instead.

virtual bool set_volume(float percent) override#

Set the output volume.

Parameters:

percent – The volume, 0 - 100.

Returns:

true on success.

virtual bool set_mute(bool mute) override#

Mute or unmute the output.

Parameters:

mute – true to mute.

Returns:

true on success.

inline virtual bool set_sample_rate(uint32_t sample_rate)#

Follow a change of the I2S sample rate (the clocks are already running at the new rate).

Parameters:

sample_rate – The new sample rate, in Hz.

Returns:

true on success.

inline virtual bool has_gain() const#
Returns:

true if the chip has a hardware input (microphone) gain.

inline virtual bool set_gain(float percent)#

Set the input (microphone) gain.

Parameters:

percent – The gain, 0 - 100, mapped onto the chip’s gain range.

Returns:

true on success.

struct Config#

Configuration.

Public Members

write_fn write#

Register write, e.g. espp::make_i2c_addressed_write(device).

read_register_fn read#

Register read, e.g. espp::make_i2c_addressed_read_register(device).

Header File#

Classes#

class Es7210Codec : public espp::AudioCodec#

espp::AudioCodec for the ES7210 four-channel microphone ADC (recording), on the codec component’s ES7210 driver.

Configures the ES7210 as an I2S slave for 16-bit Philips data at the I2S sample rate (its coefficient table follows set_sample_rate() too), enables its digital high-pass filter and maps a 0 - 100 % gain onto its analog gain steps (0 dB - 37.5 dB). start() supports Direction::RECORDING only.

Note

The codec component’s ES7210 driver keeps its register access functions in globals, so there can be only one ES7210 per firmware. This class sets them when it is constructed.

Note

It logs through ESP_LOGx (tag “Es7210Codec”) rather than an espp::Logger, whose mutex would cost internal RAM for the few messages an init failure prints.

Public Types

enum class Direction : uint8_t#

Which direction(s) a codec is started for.

Values:

enumerator PLAYBACK#

DAC: I2S TX to the speaker / headphones.

enumerator RECORDING#

ADC: microphones to I2S RX.

enumerator BOTH#

DAC and ADC.

Public Functions

explicit Es7210Codec(const Config &config)#

Construct and wire up the driver’s register access (no I2C traffic yet).

Parameters:

config – The configuration.

~Es7210Codec() override#

Clear the driver’s register access functions, which hold the I2C device (e.g. a shared_ptr captured by espp::make_i2c_addressed_write()), unless a newer instance has set them since.

inline virtual const char *name() const override#
Returns:

A short name for logs (e.g. “ES8388”).

inline virtual uint32_t mclk_multiple() const override#
Returns:

256: its coefficient table is selected for MCLK = 256 x the rate.

virtual bool start(Direction direction, uint32_t sample_rate) override#

Configure the chip and start it.

Parameters:
  • direction – The direction(s) to start.

  • sample_rate – The sample rate the I2S clocks run at, in Hz.

Returns:

true on success. On failure espp::Audio still calls stop(), since a chip may be partly configured.

virtual void stop() override#

Stop the chip (and power down what start() powered up).

virtual bool set_sample_rate(uint32_t sample_rate) override#

Follow a change of the I2S sample rate (the clocks are already running at the new rate).

Parameters:

sample_rate – The new sample rate, in Hz.

Returns:

true on success.

inline virtual bool has_gain() const override#
Returns:

true if the chip has a hardware input (microphone) gain.

virtual bool set_gain(float percent) override#

Set the input (microphone) gain.

Parameters:

percent – The gain, 0 - 100, mapped onto the chip’s gain range.

Returns:

true on success.

inline virtual bool has_volume() const#
Returns:

true if the chip has a hardware output volume and mute; if not, espp::Audio scales the samples in software instead.

inline virtual bool set_volume(float percent)#

Set the output volume.

Parameters:

percent – The volume, 0 - 100.

Returns:

true on success.

inline virtual bool set_mute(bool mute)#

Mute or unmute the output.

Parameters:

mute – true to mute.

Returns:

true on success.

struct Config#

Configuration.

Public Members

write_fn write#

Register write, e.g. espp::make_i2c_addressed_write(device).

read_register_fn read#

Register read, e.g. espp::make_i2c_addressed_read_register(device).

uint8_t address = {0x40}#

The ES7210’s 7-bit I2C address (0x40 - 0x43, set by its AD1 / AD0 pins), passed to `write` for the adapter’s own register writes.

Note

The codec component’s driver passes 0x40 for its writes, so a write function that addresses by its argument (rather than a device, like espp::make_i2c_addressed_write()) only reaches an ES7210 at 0x40.

std::optional<es7210_input_mics_t> microphones = {}#

The microphones to enable (es7210_mic_select()); unset keeps the driver’s selection.

bool high_pass_filter = {true}#

Enable the digital high-pass filter. The microphones’ DC offset is otherwise amplified by the analog gain and rails the ADC: recordings come out as a near-constant level.