mirror of
https://github.com/esphome/esphome.git
synced 2026-09-23 21:14:03 +00:00
[audio, speaker] Add support for decoding Ogg Opus files (#13967)
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import add_idf_component, include_builtin_idf_component
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_NUM_CHANNELS, CONF_SAMPLE_RATE
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
|
||||
CODEOWNERS = ["@kahrendt"]
|
||||
DOMAIN = "audio"
|
||||
audio_ns = cg.esphome_ns.namespace("audio")
|
||||
|
||||
AudioFile = audio_ns.struct("AudioFile")
|
||||
@@ -14,9 +18,38 @@ AUDIO_FILE_TYPE_ENUM = {
|
||||
"WAV": AudioFileType.WAV,
|
||||
"MP3": AudioFileType.MP3,
|
||||
"FLAC": AudioFileType.FLAC,
|
||||
"OPUS": AudioFileType.OPUS,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class AudioData:
|
||||
flac_support: bool = False
|
||||
mp3_support: bool = False
|
||||
opus_support: bool = False
|
||||
|
||||
|
||||
def _get_data() -> AudioData:
|
||||
if DOMAIN not in CORE.data:
|
||||
CORE.data[DOMAIN] = AudioData()
|
||||
return CORE.data[DOMAIN]
|
||||
|
||||
|
||||
def request_flac_support() -> None:
|
||||
"""Request FLAC codec support for audio decoding."""
|
||||
_get_data().flac_support = True
|
||||
|
||||
|
||||
def request_mp3_support() -> None:
|
||||
"""Request MP3 codec support for audio decoding."""
|
||||
_get_data().mp3_support = True
|
||||
|
||||
|
||||
def request_opus_support() -> None:
|
||||
"""Request Opus codec support for audio decoding."""
|
||||
_get_data().opus_support = True
|
||||
|
||||
|
||||
CONF_MIN_BITS_PER_SAMPLE = "min_bits_per_sample"
|
||||
CONF_MAX_BITS_PER_SAMPLE = "max_bits_per_sample"
|
||||
CONF_MIN_CHANNELS = "min_channels"
|
||||
@@ -173,3 +206,12 @@ async def to_code(config):
|
||||
name="esphome/esp-audio-libs",
|
||||
ref="2.0.3",
|
||||
)
|
||||
|
||||
data = _get_data()
|
||||
if data.flac_support:
|
||||
cg.add_define("USE_AUDIO_FLAC_SUPPORT")
|
||||
if data.mp3_support:
|
||||
cg.add_define("USE_AUDIO_MP3_SUPPORT")
|
||||
if data.opus_support:
|
||||
cg.add_define("USE_AUDIO_OPUS_SUPPORT")
|
||||
add_idf_component(name="esphome/micro-opus", ref="0.3.3")
|
||||
|
||||
@@ -46,6 +46,10 @@ const char *audio_file_type_to_string(AudioFileType file_type) {
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
case AudioFileType::MP3:
|
||||
return "MP3";
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
case AudioFileType::OPUS:
|
||||
return "OPUS";
|
||||
#endif
|
||||
case AudioFileType::WAV:
|
||||
return "WAV";
|
||||
|
||||
@@ -112,6 +112,9 @@ enum class AudioFileType : uint8_t {
|
||||
#endif
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
MP3,
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
OPUS,
|
||||
#endif
|
||||
WAV,
|
||||
};
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace audio {
|
||||
|
||||
static const char *const TAG = "audio.decoder";
|
||||
|
||||
static const uint32_t DECODING_TIMEOUT_MS = 50; // The decode function will yield after this duration
|
||||
static const uint32_t READ_WRITE_TIMEOUT_MS = 20; // Timeout for transferring audio data
|
||||
|
||||
@@ -79,6 +82,14 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
|
||||
// Always reallocate the output transfer buffer to the smallest necessary size
|
||||
this->output_transfer_buffer_->reallocate(this->free_buffer_required_);
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
case AudioFileType::OPUS:
|
||||
this->opus_decoder_ = make_unique<micro_opus::OggOpusDecoder>();
|
||||
this->free_buffer_required_ =
|
||||
this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
|
||||
this->decoder_buffers_internally_ = true;
|
||||
break;
|
||||
#endif
|
||||
case AudioFileType::WAV:
|
||||
this->wav_decoder_ = make_unique<esp_audio_libs::wav_decoder::WAVDecoder>();
|
||||
@@ -158,8 +169,9 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
// Decode more audio
|
||||
|
||||
// Only shift data on the first loop iteration to avoid unnecessary, slow moves
|
||||
size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS),
|
||||
first_loop_iteration);
|
||||
// If the decoder buffers internally, then never shift
|
||||
size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(
|
||||
pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), first_loop_iteration && !this->decoder_buffers_internally_);
|
||||
|
||||
if (!first_loop_iteration && (this->input_transfer_buffer_->available() < bytes_processed)) {
|
||||
// Less data is available than what was processed in last iteration, so don't attempt to decode.
|
||||
@@ -195,6 +207,11 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
case AudioFileType::MP3:
|
||||
state = this->decode_mp3_();
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
case AudioFileType::OPUS:
|
||||
state = this->decode_opus_();
|
||||
break;
|
||||
#endif
|
||||
case AudioFileType::WAV:
|
||||
state = this->decode_wav_();
|
||||
@@ -339,6 +356,45 @@ FileDecoderState AudioDecoder::decode_mp3_() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
FileDecoderState AudioDecoder::decode_opus_() {
|
||||
bool processed_header = this->opus_decoder_->is_initialized();
|
||||
|
||||
size_t bytes_consumed, samples_decoded;
|
||||
|
||||
micro_opus::OggOpusResult result = this->opus_decoder_->decode(
|
||||
this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available(),
|
||||
this->output_transfer_buffer_->get_buffer_end(), this->output_transfer_buffer_->free(), bytes_consumed,
|
||||
samples_decoded);
|
||||
|
||||
if (result == micro_opus::OGG_OPUS_OK) {
|
||||
if (!processed_header && this->opus_decoder_->is_initialized()) {
|
||||
// Header processed and stream info is available
|
||||
this->audio_stream_info_ =
|
||||
audio::AudioStreamInfo(this->opus_decoder_->get_bit_depth(), this->opus_decoder_->get_channels(),
|
||||
this->opus_decoder_->get_sample_rate());
|
||||
}
|
||||
if (samples_decoded > 0 && this->audio_stream_info_.has_value()) {
|
||||
// Some audio was processed
|
||||
this->output_transfer_buffer_->increase_buffer_length(
|
||||
this->audio_stream_info_.value().frames_to_bytes(samples_decoded));
|
||||
}
|
||||
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
|
||||
} else if (result == micro_opus::OGG_OPUS_OUTPUT_BUFFER_TOO_SMALL) {
|
||||
// Reallocate to decode the packet on the next call
|
||||
this->free_buffer_required_ = this->opus_decoder_->get_required_output_buffer_size();
|
||||
if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) {
|
||||
// Couldn't reallocate output buffer
|
||||
return FileDecoderState::FAILED;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Opus decoder failed: %" PRId8, result);
|
||||
return FileDecoderState::POTENTIALLY_FAILED;
|
||||
}
|
||||
return FileDecoderState::MORE_TO_PROCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
FileDecoderState AudioDecoder::decode_wav_() {
|
||||
if (!this->audio_stream_info_.has_value()) {
|
||||
// Header hasn't been processed
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
#endif
|
||||
#include <wav_decoder.h>
|
||||
|
||||
// micro-opus
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
#include <micro_opus/ogg_opus_decoder.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace audio {
|
||||
|
||||
@@ -47,7 +52,7 @@ class AudioDecoder {
|
||||
* @brief Class that facilitates decoding an audio file.
|
||||
* The audio file is read from a ring buffer source, decoded, and sent to an audio sink (ring buffer or speaker
|
||||
* component).
|
||||
* Supports wav, flac, and mp3 formats.
|
||||
* Supports wav, flac, mp3, and ogg opus formats.
|
||||
*/
|
||||
public:
|
||||
/// @brief Allocates the input and output transfer buffers
|
||||
@@ -55,7 +60,7 @@ class AudioDecoder {
|
||||
/// @param output_buffer_size Size of the output transfer buffer in bytes.
|
||||
AudioDecoder(size_t input_buffer_size, size_t output_buffer_size);
|
||||
|
||||
/// @brief Deallocates the MP3 decoder (the flac and wav decoders are deallocated automatically)
|
||||
/// @brief Deallocates the MP3 decoder (the flac, opus, and wav decoders are deallocated automatically)
|
||||
~AudioDecoder();
|
||||
|
||||
/// @brief Adds a source ring buffer for raw file data. Takes ownership of the ring buffer in a shared_ptr.
|
||||
@@ -108,6 +113,10 @@ class AudioDecoder {
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
FileDecoderState decode_mp3_();
|
||||
esp_audio_libs::helix_decoder::HMP3Decoder mp3_decoder_;
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
FileDecoderState decode_opus_();
|
||||
std::unique_ptr<micro_opus::OggOpusDecoder> opus_decoder_;
|
||||
#endif
|
||||
FileDecoderState decode_wav_();
|
||||
|
||||
@@ -124,6 +133,8 @@ class AudioDecoder {
|
||||
bool end_of_file_{false};
|
||||
bool wav_has_known_end_{false};
|
||||
|
||||
bool decoder_buffers_internally_{false};
|
||||
|
||||
bool pause_output_{false};
|
||||
|
||||
uint32_t accumulated_frames_written_{0};
|
||||
|
||||
@@ -197,6 +197,11 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
|
||||
else if (str_endswith_ignore_case(url, ".flac")) {
|
||||
file_type = AudioFileType::FLAC;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
else if (str_endswith_ignore_case(url, ".opus")) {
|
||||
file_type = AudioFileType::OPUS;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
file_type = AudioFileType::NONE;
|
||||
@@ -241,6 +246,14 @@ AudioFileType AudioReader::get_audio_type(const char *content_type) {
|
||||
if (strcasecmp(content_type, "audio/flac") == 0 || strcasecmp(content_type, "audio/x-flac") == 0) {
|
||||
return AudioFileType::FLAC;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
// Match "audio/ogg" with a codecs parameter containing "opus"
|
||||
// Valid forms: audio/ogg;codecs=opus, audio/ogg; codecs="opus", etc.
|
||||
// Plain "audio/ogg" without a codecs parameter is not matched, as those are almost always Ogg Vorbis streams
|
||||
if (strncasecmp(content_type, "audio/ogg", 9) == 0 && strcasestr(content_type + 9, "opus") != nullptr) {
|
||||
return AudioFileType::OPUS;
|
||||
}
|
||||
#endif
|
||||
return AudioFileType::NONE;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ from esphome.const import (
|
||||
from esphome.core import CORE, HexInt
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
from esphome.external_files import download_content
|
||||
from esphome.final_validate import full_config
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -37,6 +36,10 @@ DEPENDENCIES = ["network"]
|
||||
CODEOWNERS = ["@kahrendt", "@synesthesiam"]
|
||||
DOMAIN = "media_player"
|
||||
|
||||
CODEC_SUPPORT_ALL = "all"
|
||||
CODEC_SUPPORT_NEEDED = "needed"
|
||||
CODEC_SUPPORT_NONE = "none"
|
||||
|
||||
TYPE_LOCAL = "local"
|
||||
TYPE_WEB = "web"
|
||||
|
||||
@@ -110,6 +113,8 @@ def _get_supported_format_struct(pipeline, type):
|
||||
args.append(("format", "flac"))
|
||||
elif pipeline[CONF_FORMAT] == "MP3":
|
||||
args.append(("format", "mp3"))
|
||||
elif pipeline[CONF_FORMAT] == "OPUS":
|
||||
args.append(("format", "opus"))
|
||||
elif pipeline[CONF_FORMAT] == "WAV":
|
||||
args.append(("format", "wav"))
|
||||
|
||||
@@ -173,6 +178,13 @@ def _read_audio_file_and_type(file_config):
|
||||
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["MP3"]
|
||||
elif file_type in ("flac"):
|
||||
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["FLAC"]
|
||||
elif (
|
||||
file_type in ("ogg")
|
||||
and len(data) >= 36
|
||||
and data.startswith(b"OggS")
|
||||
and data[28:36] == b"OpusHead"
|
||||
):
|
||||
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["OPUS"]
|
||||
|
||||
return data, media_file_type
|
||||
|
||||
@@ -199,6 +211,10 @@ def _validate_pipeline(config):
|
||||
inherit_property_from(CONF_NUM_CHANNELS, CONF_SPEAKER)(config)
|
||||
inherit_property_from(CONF_SAMPLE_RATE, CONF_SPEAKER)(config)
|
||||
|
||||
# Opus only supports 48 kHz
|
||||
if config.get(CONF_FORMAT) == "OPUS" and config.get(CONF_SAMPLE_RATE) != 48000:
|
||||
raise cv.Invalid("Opus only supports a sample rate of 48000 Hz")
|
||||
|
||||
# Validate the transcoder settings is compatible with the speaker
|
||||
audio.final_validate_audio_schema(
|
||||
"speaker media_player",
|
||||
@@ -225,12 +241,27 @@ def _validate_repeated_speaker(config):
|
||||
|
||||
|
||||
def _final_validate(config):
|
||||
# Default to using codec if psram is enabled
|
||||
if (use_codec := config.get(CONF_CODEC_SUPPORT_ENABLED)) is None:
|
||||
use_codec = psram.DOMAIN in full_config.get()
|
||||
conf_id = config[CONF_ID].id
|
||||
core_data = CORE.data.setdefault(DOMAIN, {conf_id: {}})
|
||||
core_data[conf_id][CONF_CODEC_SUPPORT_ENABLED] = use_codec
|
||||
# Normalize boolean values to string equivalents
|
||||
codec_mode = config[CONF_CODEC_SUPPORT_ENABLED]
|
||||
if codec_mode is True:
|
||||
codec_mode = CODEC_SUPPORT_ALL
|
||||
elif codec_mode is False:
|
||||
codec_mode = CODEC_SUPPORT_NONE
|
||||
|
||||
use_codec = codec_mode != CODEC_SUPPORT_NONE
|
||||
|
||||
# In "needed" mode, collect formats from pipelines and files
|
||||
needed_formats = set()
|
||||
need_all = False
|
||||
if codec_mode == CODEC_SUPPORT_NEEDED:
|
||||
for pipeline_key in (CONF_ANNOUNCEMENT_PIPELINE, CONF_MEDIA_PIPELINE):
|
||||
if pipeline := config.get(pipeline_key):
|
||||
fmt = pipeline[CONF_FORMAT]
|
||||
if fmt == "NONE":
|
||||
# No preferred format means any format could arrive
|
||||
need_all = True
|
||||
else:
|
||||
needed_formats.add(fmt)
|
||||
|
||||
for file_config in config.get(CONF_FILES, []):
|
||||
_, media_file_type = _read_audio_file_and_type(file_config)
|
||||
@@ -243,6 +274,26 @@ def _final_validate(config):
|
||||
raise cv.Invalid(
|
||||
f"Unsupported local media file type, set {CONF_CODEC_SUPPORT_ENABLED} to true or convert the media file to wav"
|
||||
)
|
||||
# In "needed" mode, add file format to needed codecs
|
||||
if codec_mode == CODEC_SUPPORT_NEEDED:
|
||||
for fmt_name, fmt_enum in audio.AUDIO_FILE_TYPE_ENUM.items():
|
||||
if str(media_file_type) == str(fmt_enum):
|
||||
if fmt_name not in ("WAV", "NONE"):
|
||||
needed_formats.add(fmt_name)
|
||||
break
|
||||
|
||||
# Request codec support
|
||||
if codec_mode == CODEC_SUPPORT_ALL or need_all:
|
||||
audio.request_flac_support()
|
||||
audio.request_mp3_support()
|
||||
audio.request_opus_support()
|
||||
elif codec_mode == CODEC_SUPPORT_NEEDED:
|
||||
if "FLAC" in needed_formats:
|
||||
audio.request_flac_support()
|
||||
if "MP3" in needed_formats:
|
||||
audio.request_mp3_support()
|
||||
if "OPUS" in needed_formats:
|
||||
audio.request_opus_support()
|
||||
|
||||
return config
|
||||
|
||||
@@ -307,7 +358,17 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
||||
min=4000, max=4000000
|
||||
),
|
||||
cv.Optional(CONF_CODEC_SUPPORT_ENABLED): cv.boolean,
|
||||
cv.Optional(
|
||||
CONF_CODEC_SUPPORT_ENABLED, default=CODEC_SUPPORT_NEEDED
|
||||
): cv.Any(
|
||||
cv.boolean,
|
||||
cv.one_of(
|
||||
CODEC_SUPPORT_ALL,
|
||||
CODEC_SUPPORT_NEEDED,
|
||||
CODEC_SUPPORT_NONE,
|
||||
lower=True,
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_FILES): cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA),
|
||||
cv.Optional(CONF_TASK_STACK_IN_PSRAM): cv.All(
|
||||
cv.boolean, cv.requires_component(psram.DOMAIN)
|
||||
@@ -340,11 +401,6 @@ FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CORE.data[DOMAIN][config[CONF_ID].id][CONF_CODEC_SUPPORT_ENABLED]:
|
||||
# Compile all supported audio codecs
|
||||
cg.add_define("USE_AUDIO_FLAC_SUPPORT", True)
|
||||
cg.add_define("USE_AUDIO_MP3_SUPPORT", True)
|
||||
|
||||
var = await media_player.new_media_player(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
|
||||
@@ -13,7 +13,12 @@ namespace speaker {
|
||||
static const uint32_t INITIAL_BUFFER_MS = 1000; // Start playback after buffering this duration of the file
|
||||
|
||||
static const uint32_t READ_TASK_STACK_SIZE = 5 * 1024;
|
||||
// Opus decoding uses more stack than other codecs
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
static const uint32_t DECODE_TASK_STACK_SIZE = 5 * 1024;
|
||||
#else
|
||||
static const uint32_t DECODE_TASK_STACK_SIZE = 3 * 1024;
|
||||
#endif
|
||||
|
||||
static const uint32_t INFO_ERROR_QUEUE_COUNT = 5;
|
||||
|
||||
@@ -552,6 +557,11 @@ void AudioPipeline::decode_task(void *params) {
|
||||
case audio::AudioFileType::FLAC:
|
||||
initial_bytes_to_buffer /= 2; // Estimate the FLAC compression factor is 2
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
case audio::AudioFileType::OPUS:
|
||||
initial_bytes_to_buffer /= 8; // Estimate the Opus compression factor is 8
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
#define USE_AUDIO_DAC
|
||||
#define USE_AUDIO_FLAC_SUPPORT
|
||||
#define USE_AUDIO_MP3_SUPPORT
|
||||
#define USE_AUDIO_OPUS_SUPPORT
|
||||
#define USE_API
|
||||
#define USE_API_CLIENT_CONNECTED_TRIGGER
|
||||
#define USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||
|
||||
@@ -3,6 +3,8 @@ dependencies:
|
||||
version: "7.4.2"
|
||||
esphome/esp-audio-libs:
|
||||
version: 2.0.3
|
||||
esphome/micro-opus:
|
||||
version: 0.3.3
|
||||
espressif/esp-tflite-micro:
|
||||
version: 1.3.3~1
|
||||
espressif/esp32-camera:
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
substitutions:
|
||||
i2s_bclk_pin: GPIO27
|
||||
i2s_lrclk_pin: GPIO26
|
||||
i2s_mclk_pin: GPIO25
|
||||
i2s_dout_pin: GPIO23
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common-audio_dac.yaml
|
||||
@@ -1,5 +1,11 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
wifi:
|
||||
ap:
|
||||
|
||||
psram:
|
||||
mode: quad
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
id: speaker_media_player_id
|
||||
@@ -10,3 +16,4 @@ media_player:
|
||||
volume_max: 0.95
|
||||
volume_min: 0.0
|
||||
task_stack_in_psram: true
|
||||
codec_support_enabled: all
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO2
|
||||
sda_pin: GPIO3
|
||||
i2s_bclk_pin: GPIO4
|
||||
i2s_lrclk_pin: GPIO5
|
||||
i2s_mclk_pin: GPIO6
|
||||
i2s_dout_pin: GPIO7
|
||||
|
||||
<<: !include common-media_player.yaml
|
||||
@@ -1,10 +0,0 @@
|
||||
substitutions:
|
||||
i2s_bclk_pin: GPIO27
|
||||
i2s_lrclk_pin: GPIO26
|
||||
i2s_mclk_pin: GPIO25
|
||||
i2s_dout_pin: GPIO4
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user