12static constexpr uint32_t MEDIA_CONTROLS_QUEUE_LENGTH = 20;
48 this->
pipelines_[pipeline].sources.emplace_back(std::make_unique<SourceBinding>(
this, media_source, pipeline));
54 "Speaker Source Media Player:\n"
55 " Volume Increment: %.2f\n"
69 if (this->
pref_.
load(&volume_restore_state)) {
78 for (
size_t i = 0; i < this->
pipelines_.size(); i++) {
93 if (active_source ==
nullptr) {
102 source_frames = std::min(frames, current);
103 }
while (source_frames > 0 &&
104 !ps.
pending_frames.compare_exchange_weak(current, current - source_frames, std::memory_order_relaxed));
106 if (source_frames > 0) {
130 call.set_media_url(uri);
196 vTaskDelay(pdMS_TO_TICKS(timeout_ms));
202 ps.
pending_frames.fetch_add(reserved_frames, std::memory_order_relaxed);
203 size_t bytes_written = ps.
speaker->
play(data,
length, pdMS_TO_TICKS(timeout_ms));
205 if (unused_frames > 0) {
209 while (!ps.
pending_frames.compare_exchange_weak(pending, pending - std::min(pending, unused_frames),
210 std::memory_order_relaxed)) {
213 return bytes_written;
217 vTaskDelay(pdMS_TO_TICKS(timeout_ms));
224 if (source !=
nullptr) {
231 ESP_LOGE(TAG,
"Media source error");
264 if (announcement_source !=
nullptr) {
268 switch (announcement_state) {
274 ESP_LOGE(TAG,
"Announcement source error");
292 if (this->
state != old_state) {
301 for (
auto &binding : ps.
sources) {
302 if (binding->source->can_handle(uri)) {
305 return binding->source;
307 if (first_match ==
nullptr) {
308 first_match = binding->source;
318 if (target_source ==
nullptr) {
319 ESP_LOGW(TAG,
"No source for URI");
320 ESP_LOGV(TAG,
"URI: %s", uri.c_str());
329 if (active_source !=
nullptr) {
334 ESP_LOGV(TAG,
"Pipeline %u: stopping active source", pipeline);
348 ESP_LOGV(TAG,
"Pipeline %u: target source busy, stopping", pipeline);
368 if (!target_source->
play_uri(uri)) {
369 ESP_LOGE(TAG,
"Pipeline %u: Failed to play URI: %s", pipeline, uri.c_str());
384 cmd.pipeline = pipeline;
386 ESP_LOGE(TAG,
"Queue full, command dropped");
393 this->
pipelines_[pipeline].playlist_delay_ms = delay_ms;
401 this->
set_timeout(PIPELINE_TIMEOUT_IDS[pipeline], delay_ms,
417 bool command_executed =
false;
418 uint8_t pipeline = control_command.
pipeline;
424 switch (control_command.type) {
431 ps.
playlist.push_back(*control_command.data.uri);
435 command_executed =
true;
441 ps.
playlist.push_back(*control_command.data.uri);
449 bool nothing_playing =
455 command_executed =
true;
475 command_executed =
true;
485 command_executed =
true;
492 command_executed =
true;
498 if (command_executed) {
504 delete control_command.data.uri;
518 if (active_source !=
nullptr) {
519 target_source = active_source;
524 switch (player_command) {
528 if (target_source !=
nullptr) {
531 }
else if (!has_internal_playlist && active_source ==
nullptr && !ps.
playlist.empty()) {
533 if (last_has_internal_playlist) {
542 if (target_source !=
nullptr) {
550 if (!has_internal_playlist && active_source ==
nullptr && !ps.
playlist.empty()) {
552 if (last_has_internal_playlist) {
560 }
else if (target_source !=
nullptr) {
567 if (target_source !=
nullptr) {
574 if (!has_internal_playlist) {
580 if (target_source !=
nullptr) {
587 if (!has_internal_playlist) {
596 }
else if (target_source !=
nullptr) {
603 if (!has_internal_playlist) {
612 }
else if (target_source !=
nullptr) {
619 if (!has_internal_playlist) {
621 }
else if (target_source !=
nullptr) {
627 if (!has_internal_playlist) {
629 }
else if (target_source !=
nullptr) {
635 if (!has_internal_playlist) {
637 }
else if (target_source !=
nullptr) {
643 if (!has_internal_playlist) {
655 }
else if (target_source !=
nullptr) {
662 if (!has_internal_playlist) {
664 }
else if (target_source !=
nullptr) {
670 if (!has_internal_playlist) {
672 }
else if (target_source !=
nullptr) {
695 auto announcement =
call.get_announcement();
696 if (announcement.has_value() && announcement.value()) {
710 const auto &media_url =
call.get_media_url();
711 if (media_url.has_value()) {
712 auto command =
call.get_command();
723 control_command.data.uri =
new std::string(media_url.value());
725 delete control_command.data.uri;
726 ESP_LOGE(TAG,
"Queue full, URI dropped");
738 auto cmd =
call.get_command();
739 if (cmd.has_value()) {
740 switch (cmd.value()) {
756 control_command.data.command = cmd.value();
758 ESP_LOGE(TAG,
"Queue full, command dropped");
771 traits.set_supports_pause(
true);
774 if (ps.format.has_value()) {
775 traits.get_supported_formats().push_back(ps.format.value());
795 if (ps.is_configured()) {
796 ps.speaker->set_mute_state(mute_state);
807 for (
auto &ps : this->pipelines_) {
808 for (
auto &binding : ps.sources) {
809 binding->source->notify_mute_changed(mute_state);
823 float bounded_volume = (
volume < speaker::SILENT_VOLUME_THRESHOLD)
828 if (ps.is_configured()) {
829 ps.speaker->set_volume(bounded_volume);
838 for (
auto &ps : this->pipelines_) {
839 for (
auto &binding : ps.sources) {
840 binding->source->notify_volume_changed(
volume);
873 for (
size_t i = 0; i < ps.
playlist.size(); i++) {
bool cancel_timeout(const char *name)
Cancel a timeout function.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
void defer(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call with a const char* name.
ESPPreferenceObject make_entity_preference(uint32_t version=0)
Create a preference object for storing this entity's state/settings.
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
uint32_t bytes_to_frames(size_t bytes) const
Convert bytes to frames.
virtual size_t play(const uint8_t *data, size_t length)=0
Plays the provided audio data.
void set_audio_stream_info(const audio::AudioStreamInfo &audio_stream_info)
audio::AudioStreamInfo & get_audio_stream_info()
ESPHOME_LOG_TAG(TAG, "speaker_source_media_player")
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
T remap(U value, U min, U max, T min_out, T max_out)
Remap value from the range (min, max) to (min_out, max_out).
speaker::Speaker * speaker
std::vector< size_t > shuffle_indices
uint32_t playlist_delay_ms
media_source::MediaSource * last_source
media_source::MediaSource * pending_source
std::atomic< uint32_t > pending_frames
std::vector< std::unique_ptr< SourceBinding > > sources
std::vector< std::string > playlist
std::atomic< media_source::MediaSource * > active_source
media_source::MediaSource * stopping_source
void request_play_uri(const std::string &uri) override
void request_mute(bool is_muted) override
SpeakerSourceMediaPlayer * player
void request_volume(float volume) override
void report_state(media_source::MediaSourceState state) override
size_t write_audio(const uint8_t *data, size_t length, uint32_t timeout_ms, const audio::AudioStreamInfo &stream_info) override
media_source::MediaSource * source