1use std::fmt;
4use std::sync::Arc;
5use std::time::Duration;
6
7use dioxus::prelude::*;
8
9use crate::AudioData;
10use crate::AudioError;
11use crate::AudioErrorKind;
12use crate::analysis::AudioAnalyser;
13use crate::analysis::WaveformSelection;
14
15#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
16mod web;
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20#[non_exhaustive]
21pub enum PlaybackLoadingPolicy {
22 #[default]
24 Eager,
25 OnPlay,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31#[non_exhaustive]
32pub enum PlaybackSourceCrossOrigin {
33 Anonymous,
35 UseCredentials,
37}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct PlaybackSourceAlternative {
42 url: String,
43 media_type: Option<String>,
44 cross_origin: Option<PlaybackSourceCrossOrigin>,
45}
46
47impl PlaybackSourceAlternative {
48 pub fn new(url: impl Into<String>) -> Result<Self, AudioError> {
53 let url = url.into();
54 if url.trim().is_empty() || url.chars().any(char::is_control) {
55 return Err(AudioError::new(
56 AudioErrorKind::InvalidConfiguration,
57 "Playback Source URL must be non-empty and contain no control characters",
58 ));
59 }
60
61 Ok(Self {
62 url,
63 media_type: None,
64 cross_origin: None,
65 })
66 }
67
68 pub fn with_media_type(mut self, media_type: impl Into<String>) -> Result<Self, AudioError> {
70 let media_type = media_type.into();
71 if media_type.trim().is_empty() || media_type.chars().any(char::is_control) {
72 return Err(AudioError::new(
73 AudioErrorKind::InvalidConfiguration,
74 "Playback Source media type must be non-empty and contain no control characters",
75 ));
76 }
77 self.media_type = Some(media_type);
78 Ok(self)
79 }
80
81 pub fn url(&self) -> &str {
82 &self.url
83 }
84
85 pub fn media_type(&self) -> Option<&str> {
86 self.media_type.as_deref()
87 }
88
89 pub fn with_cross_origin(mut self, cross_origin: PlaybackSourceCrossOrigin) -> Self {
94 self.cross_origin = Some(cross_origin);
95 self
96 }
97
98 pub fn cross_origin(&self) -> Option<PlaybackSourceCrossOrigin> {
99 self.cross_origin
100 }
101
102 pub fn is_graph_eligible(&self) -> bool {
105 self.cross_origin == Some(PlaybackSourceCrossOrigin::Anonymous)
106 }
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
110enum PlaybackSourceInput {
111 AudioData(Arc<AudioData>),
112 Url(Arc<[PlaybackSourceAlternative]>),
113}
114
115#[derive(Clone, Debug, PartialEq, Eq)]
117pub struct PlaybackSource {
118 input: PlaybackSourceInput,
119 loading_policy: PlaybackLoadingPolicy,
120}
121
122impl PlaybackSource {
123 pub fn audio_data(audio: AudioData) -> Self {
124 Self {
125 input: PlaybackSourceInput::AudioData(Arc::new(audio)),
126 loading_policy: PlaybackLoadingPolicy::Eager,
127 }
128 }
129
130 pub fn url(alternative: PlaybackSourceAlternative) -> Self {
131 Self {
132 input: PlaybackSourceInput::Url(Arc::from([alternative])),
133 loading_policy: PlaybackLoadingPolicy::Eager,
134 }
135 }
136
137 pub fn url_alternatives(
139 alternatives: impl IntoIterator<Item = PlaybackSourceAlternative>,
140 ) -> Result<Self, AudioError> {
141 let alternatives: Arc<[PlaybackSourceAlternative]> = alternatives.into_iter().collect();
142 if alternatives.is_empty() {
143 return Err(AudioError::new(
144 AudioErrorKind::InvalidConfiguration,
145 "a URL Playback Source must contain at least one alternative",
146 ));
147 }
148
149 Ok(Self {
150 input: PlaybackSourceInput::Url(alternatives),
151 loading_policy: PlaybackLoadingPolicy::Eager,
152 })
153 }
154
155 pub fn alternatives(&self) -> Option<&[PlaybackSourceAlternative]> {
157 match &self.input {
158 PlaybackSourceInput::AudioData(_) => None,
159 PlaybackSourceInput::Url(alternatives) => Some(alternatives),
160 }
161 }
162
163 pub fn with_loading_policy(mut self, loading_policy: PlaybackLoadingPolicy) -> Self {
164 self.loading_policy = loading_policy;
165 self
166 }
167
168 pub fn loading_policy(&self) -> PlaybackLoadingPolicy {
169 self.loading_policy
170 }
171}
172
173impl From<AudioData> for PlaybackSource {
174 fn from(audio: AudioData) -> Self {
175 Self::audio_data(audio)
176 }
177}
178
179#[derive(Clone, Debug, PartialEq, Eq)]
180#[non_exhaustive]
181pub enum PlaybackStatus {
182 Empty,
183 Loading,
184 Ready,
185 Playing,
186 Paused,
187 Ended,
188 Failed(AudioError),
189}
190
191#[derive(Clone, Debug, PartialEq, Eq)]
193#[non_exhaustive]
194pub enum PlaybackSourceLifecycle {
195 Empty,
196 Dormant,
197 Loading,
198 Playable,
199 Failed,
200}
201
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
204#[non_exhaustive]
205pub enum PlaybackTransport {
206 Idle,
207 PlayPending,
208 Playing,
209 Paused,
210 Ended,
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq)]
215#[non_exhaustive]
216pub enum PlaybackReadiness {
217 Unavailable,
218 LoadingMetadata,
219 Metadata,
220 Playable,
221 Waiting,
222}
223
224#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
226#[non_exhaustive]
227pub enum PlaybackNetworkActivity {
228 #[default]
230 Inactive,
231 Unknown,
233 Loading,
235 Idle,
237 Stalled,
239}
240
241#[derive(Clone, Copy, Debug, PartialEq, Eq)]
246pub struct PlaybackTimeRange {
247 start: Duration,
248 end: Duration,
249}
250
251impl PlaybackTimeRange {
252 pub fn new(start: Duration, end: Duration) -> Result<Self, PlaybackCommandError> {
253 if end <= start {
254 return Err(PlaybackCommandError(
255 "Playback time range end must be after its start",
256 ));
257 }
258
259 Ok(Self { start, end })
260 }
261
262 pub fn start(self) -> Duration {
263 self.start
264 }
265
266 pub fn end(self) -> Duration {
267 self.end
268 }
269}
270
271#[derive(Clone, Debug, PartialEq, Eq)]
273#[non_exhaustive]
274pub enum PlaybackSourceFailure {
275 GraphIneligible(AudioError),
277 Unsupported(AudioError),
278 Network(AudioError),
279 Decode(AudioError),
280 Unknown(AudioError),
281}
282
283impl PlaybackSourceFailure {
284 pub fn error(&self) -> &AudioError {
285 match self {
286 Self::GraphIneligible(error)
287 | Self::Unsupported(error)
288 | Self::Network(error)
289 | Self::Decode(error)
290 | Self::Unknown(error) => error,
291 }
292 }
293
294 pub fn kind(&self) -> PlaybackSourceFailureKind {
295 match self {
296 Self::GraphIneligible(_) => PlaybackSourceFailureKind::GraphIneligible,
297 Self::Unsupported(_) => PlaybackSourceFailureKind::Unsupported,
298 Self::Network(_) => PlaybackSourceFailureKind::Network,
299 Self::Decode(_) => PlaybackSourceFailureKind::Decode,
300 Self::Unknown(_) => PlaybackSourceFailureKind::Unknown,
301 }
302 }
303}
304
305#[derive(Clone, Copy, Debug, PartialEq, Eq)]
307#[non_exhaustive]
308pub enum PlaybackSourceFailureKind {
309 GraphIneligible,
310 Unsupported,
311 Network,
312 Decode,
313 Unknown,
314}
315
316#[derive(Clone, Debug, PartialEq, Eq)]
318pub struct PlaybackAlternativeFailure {
319 alternative: PlaybackSourceAlternative,
320 kind: PlaybackSourceFailureKind,
321}
322
323impl PlaybackAlternativeFailure {
324 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
325 fn new(alternative: PlaybackSourceAlternative, failure: &PlaybackSourceFailure) -> Self {
326 Self {
327 alternative,
328 kind: failure.kind(),
329 }
330 }
331
332 pub fn alternative(&self) -> &PlaybackSourceAlternative {
333 &self.alternative
334 }
335
336 pub fn kind(&self) -> PlaybackSourceFailureKind {
337 self.kind
338 }
339}
340
341#[derive(Clone, Debug, PartialEq, Eq)]
343#[non_exhaustive]
344pub enum PlaybackPlayFailure {
345 InteractionRequired(AudioError),
346 Unknown(AudioError),
347}
348
349#[derive(Clone, Copy, Debug, PartialEq, Eq)]
351#[non_exhaustive]
352pub enum BoundedPlaybackMode {
353 Once,
355 Loop,
357}
358
359#[derive(Clone, Copy, Debug, PartialEq, Eq)]
361#[non_exhaustive]
362pub enum BoundedPlaybackFailure {
363 SeekTimedOut,
365 ActivationRejected,
367 PauseRejected,
369}
370
371#[derive(Clone, Copy, Debug, PartialEq, Eq)]
373#[non_exhaustive]
374pub enum BoundedPlaybackPhase {
375 Seeking,
377 Activating,
379 Active,
381 Paused,
383 Retargeting,
385 Wrapping,
387 Completed,
389 Cancelled,
391 Failed(BoundedPlaybackFailure),
393}
394
395#[derive(Clone, Copy, Debug, PartialEq, Eq)]
397#[non_exhaustive]
398pub enum BoundedPlaybackEvent {
399 Started(BoundedPlaybackMode),
400 Completed,
401 Cancelled,
402 Wrapping,
403 Failed,
404}
405
406#[derive(Clone, Copy, Debug, PartialEq, Eq)]
408#[non_exhaustive]
409pub enum BoundedPlaybackRetarget {
410 PreservePosition,
411 SeekToStart,
412}
413
414impl BoundedPlaybackPhase {
415 pub fn is_enforcing(self) -> bool {
417 matches!(
418 self,
419 Self::Seeking
420 | Self::Activating
421 | Self::Active
422 | Self::Paused
423 | Self::Retargeting
424 | Self::Wrapping
425 )
426 }
427}
428
429#[derive(Clone, Copy, Debug, PartialEq, Eq)]
431pub struct BoundedPlaybackSnapshot {
432 pub range: WaveformSelection,
433 pub mode: BoundedPlaybackMode,
434 pub phase: BoundedPlaybackPhase,
435}
436
437impl PlaybackPlayFailure {
438 pub fn error(&self) -> &AudioError {
439 match self {
440 Self::InteractionRequired(error) | Self::Unknown(error) => error,
441 }
442 }
443}
444
445#[derive(Clone, Copy, Debug, PartialEq, Eq)]
447#[non_exhaustive]
448pub enum PlaybackAudibilityCapability {
449 EffectiveGraphGain,
451 BestEffortMediaElement,
453 Unavailable,
455}
456
457#[derive(Clone, Copy, Debug, PartialEq, Eq)]
459#[non_exhaustive]
460pub enum PlaybackGraphState {
461 NotRequested,
463 AwaitingSource,
465 Preparing,
467 Suspended,
469 Running,
471 InteractionRequired,
473 Unavailable,
475}
476
477#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
479pub struct PlaybackOptions {
480 graph_backed: bool,
481}
482
483impl PlaybackOptions {
484 pub const fn graph_backed() -> Self {
486 Self { graph_backed: true }
487 }
488}
489
490#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
492pub struct PlaybackAudibilityLevel(f64);
493
494impl PlaybackAudibilityLevel {
495 pub const SILENT: Self = Self(0.0);
496 pub const FULL: Self = Self(1.0);
497
498 pub fn new(value: f64) -> Result<Self, PlaybackCommandError> {
500 if !value.is_finite() || !(0.0..=1.0).contains(&value) {
501 return Err(PlaybackCommandError(
502 "audibility level must be finite and between 0 and 1",
503 ));
504 }
505
506 Ok(Self(value))
507 }
508
509 pub fn value(self) -> f64 {
510 self.0
511 }
512}
513
514impl Default for PlaybackAudibilityLevel {
515 fn default() -> Self {
516 Self::FULL
517 }
518}
519
520impl Eq for PlaybackAudibilityLevel {}
522
523#[derive(Clone, Debug, PartialEq, Eq)]
525#[non_exhaustive]
526pub struct PlaybackSnapshot {
527 pub source: PlaybackSourceLifecycle,
528 pub transport: PlaybackTransport,
529 pub readiness: PlaybackReadiness,
530 pub network: PlaybackNetworkActivity,
532 pub buffered: Arc<[PlaybackTimeRange]>,
534 pub seekable: Arc<[PlaybackTimeRange]>,
536 pub selected_alternative: Option<PlaybackSourceAlternative>,
538 pub source_failure: Option<PlaybackSourceFailure>,
540 pub alternative_failures: Arc<[PlaybackAlternativeFailure]>,
542 pub play_failure: Option<PlaybackPlayFailure>,
543 pub bounded: Option<BoundedPlaybackSnapshot>,
545 pub bounded_event: Option<BoundedPlaybackEvent>,
547 pub repeat: bool,
549 pub muted: bool,
551 pub audibility_level: PlaybackAudibilityLevel,
553 pub audibility_capability: PlaybackAudibilityCapability,
555 pub graph: PlaybackGraphState,
557}
558
559impl Default for PlaybackSnapshot {
560 fn default() -> Self {
561 Self {
562 source: PlaybackSourceLifecycle::Empty,
563 transport: PlaybackTransport::Idle,
564 readiness: PlaybackReadiness::Unavailable,
565 network: PlaybackNetworkActivity::Inactive,
566 buffered: Arc::from([]),
567 seekable: Arc::from([]),
568 selected_alternative: None,
569 source_failure: None,
570 alternative_failures: Arc::from([]),
571 play_failure: None,
572 bounded: None,
573 bounded_event: None,
574 repeat: false,
575 muted: false,
576 audibility_level: PlaybackAudibilityLevel::FULL,
577 audibility_capability: PlaybackAudibilityCapability::BestEffortMediaElement,
578 graph: PlaybackGraphState::NotRequested,
579 }
580 }
581}
582
583#[derive(Clone, Debug, PartialEq, Eq)]
584pub struct PlaybackCommandError(&'static str);
585
586impl fmt::Display for PlaybackCommandError {
587 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
588 formatter.write_str(self.0)
589 }
590}
591
592impl std::error::Error for PlaybackCommandError {}
593
594#[derive(Debug)]
595pub struct PlaybackLifecycle {
596 status: PlaybackStatus,
597 snapshot: PlaybackSnapshot,
598 graph_backed: bool,
599}
600
601impl Default for PlaybackLifecycle {
602 fn default() -> Self {
603 Self::new(PlaybackOptions::default())
604 }
605}
606
607impl PlaybackLifecycle {
608 pub fn new(options: PlaybackOptions) -> Self {
609 let mut snapshot = PlaybackSnapshot::default();
610 if options.graph_backed {
611 snapshot.graph = PlaybackGraphState::AwaitingSource;
612 snapshot.audibility_capability = PlaybackAudibilityCapability::EffectiveGraphGain;
613 }
614 Self {
615 status: PlaybackStatus::Empty,
616 snapshot,
617 graph_backed: options.graph_backed,
618 }
619 }
620
621 pub fn status(&self) -> &PlaybackStatus {
622 &self.status
623 }
624
625 pub fn snapshot(&self) -> &PlaybackSnapshot {
626 &self.snapshot
627 }
628
629 pub fn source(&self) -> &PlaybackSourceLifecycle {
630 &self.snapshot.source
631 }
632
633 pub fn transport(&self) -> PlaybackTransport {
634 self.snapshot.transport
635 }
636
637 pub fn readiness(&self) -> PlaybackReadiness {
638 self.snapshot.readiness
639 }
640
641 pub fn network_activity(&self) -> PlaybackNetworkActivity {
642 self.snapshot.network
643 }
644
645 pub fn play_failure(&self) -> Option<&PlaybackPlayFailure> {
646 self.snapshot.play_failure.as_ref()
647 }
648
649 pub fn bounded(&self) -> Option<&BoundedPlaybackSnapshot> {
650 self.snapshot.bounded.as_ref()
651 }
652
653 pub fn start_bounded_once(
655 &mut self,
656 range: WaveformSelection,
657 duration_secs: f64,
658 ) -> Result<(), PlaybackCommandError> {
659 self.start_bounded(range, duration_secs, BoundedPlaybackMode::Once)
660 }
661
662 pub fn start_bounded_loop(
664 &mut self,
665 range: WaveformSelection,
666 duration_secs: f64,
667 ) -> Result<(), PlaybackCommandError> {
668 self.start_bounded(range, duration_secs, BoundedPlaybackMode::Loop)
669 }
670
671 fn start_bounded(
672 &mut self,
673 range: WaveformSelection,
674 duration_secs: f64,
675 mode: BoundedPlaybackMode,
676 ) -> Result<(), PlaybackCommandError> {
677 self.validate_bounded_range(range, duration_secs)?;
678
679 self.snapshot.bounded = Some(BoundedPlaybackSnapshot {
680 range,
681 mode,
682 phase: BoundedPlaybackPhase::Seeking,
683 });
684 self.snapshot.bounded_event = Some(BoundedPlaybackEvent::Started(mode));
685 Ok(())
686 }
687
688 fn validate_bounded_range(
689 &self,
690 range: WaveformSelection,
691 duration_secs: f64,
692 ) -> Result<(), PlaybackCommandError> {
693 if self.snapshot.source != PlaybackSourceLifecycle::Playable {
694 return Err(PlaybackCommandError("audio is not loaded"));
695 }
696 if !duration_secs.is_finite() || duration_secs <= 0.0 {
697 return Err(PlaybackCommandError(
698 "authoritative audio duration is unavailable",
699 ));
700 }
701 if range.is_collapsed() {
702 return Err(PlaybackCommandError("Waveform Selection is collapsed"));
703 }
704 if !range.is_playable_within(duration_secs) {
705 return Err(PlaybackCommandError(
706 "Waveform Selection is outside the Playback duration",
707 ));
708 }
709 Ok(())
710 }
711
712 pub fn retarget_bounded(
718 &mut self,
719 range: WaveformSelection,
720 duration_secs: f64,
721 current_position: f64,
722 ) -> Result<BoundedPlaybackRetarget, PlaybackCommandError> {
723 let previous = self
724 .snapshot
725 .bounded
726 .filter(|bounded| bounded.phase.is_enforcing())
727 .ok_or(PlaybackCommandError("Bounded Playback is not active"))?;
728 self.validate_bounded_range(range, duration_secs)?;
729
730 let preserved = current_position.is_finite()
731 && current_position >= range.start()
732 && current_position < range.end();
733 let phase = if preserved {
734 match self.snapshot.transport {
735 PlaybackTransport::Playing => BoundedPlaybackPhase::Active,
736 PlaybackTransport::PlayPending => BoundedPlaybackPhase::Activating,
737 _ => BoundedPlaybackPhase::Paused,
738 }
739 } else {
740 BoundedPlaybackPhase::Retargeting
741 };
742 self.snapshot.bounded = Some(BoundedPlaybackSnapshot {
743 range,
744 mode: previous.mode,
745 phase,
746 });
747 self.snapshot.bounded_event = None;
748 Ok(if preserved {
749 BoundedPlaybackRetarget::PreservePosition
750 } else {
751 BoundedPlaybackRetarget::SeekToStart
752 })
753 }
754
755 pub fn bounded_seeking(&mut self) {
756 if let Some(bounded) = self.snapshot.bounded.as_mut()
757 && bounded.phase.is_enforcing()
758 {
759 bounded.phase = BoundedPlaybackPhase::Seeking;
760 }
761 }
762
763 pub fn bounded_activating(&mut self) {
764 if let Some(bounded) = self.snapshot.bounded.as_mut()
765 && bounded.phase.is_enforcing()
766 {
767 bounded.phase = BoundedPlaybackPhase::Activating;
768 self.snapshot.bounded_event = None;
769 }
770 }
771
772 pub fn bounded_active(&mut self) {
773 if let Some(bounded) = self.snapshot.bounded.as_mut()
774 && bounded.phase == BoundedPlaybackPhase::Activating
775 {
776 bounded.phase = BoundedPlaybackPhase::Active;
777 self.snapshot.bounded_event = None;
778 }
779 }
780
781 pub fn bounded_paused(&mut self) {
782 if let Some(bounded) = self.snapshot.bounded.as_mut()
783 && bounded.phase.is_enforcing()
784 {
785 bounded.phase = BoundedPlaybackPhase::Paused;
786 self.snapshot.bounded_event = None;
787 }
788 }
789
790 pub fn bounded_retargeting(&mut self) {
791 if let Some(bounded) = self.snapshot.bounded.as_mut()
792 && bounded.phase.is_enforcing()
793 {
794 bounded.phase = BoundedPlaybackPhase::Retargeting;
795 self.snapshot.bounded_event = None;
796 }
797 }
798
799 pub fn bounded_wrapping(&mut self) {
800 if let Some(bounded) = self.snapshot.bounded.as_mut()
801 && bounded.mode == BoundedPlaybackMode::Loop
802 && bounded.phase.is_enforcing()
803 {
804 bounded.phase = BoundedPlaybackPhase::Wrapping;
805 self.snapshot.bounded_event = Some(BoundedPlaybackEvent::Wrapping);
806 }
807 }
808
809 pub fn bounded_completed(&mut self) {
810 if let Some(bounded) = self.snapshot.bounded.as_mut()
811 && bounded.mode == BoundedPlaybackMode::Once
812 && bounded.phase.is_enforcing()
813 {
814 bounded.phase = BoundedPlaybackPhase::Completed;
815 self.status = PlaybackStatus::Paused;
816 self.snapshot.transport = PlaybackTransport::Paused;
817 self.snapshot.play_failure = None;
818 self.snapshot.bounded_event = Some(BoundedPlaybackEvent::Completed);
819 }
820 }
821
822 pub fn bounded_failed(&mut self, failure: BoundedPlaybackFailure) {
823 if let Some(bounded) = self.snapshot.bounded.as_mut()
824 && bounded.phase.is_enforcing()
825 {
826 bounded.phase = BoundedPlaybackPhase::Failed(failure);
827 if self.snapshot.transport != PlaybackTransport::Playing {
828 self.snapshot.transport = PlaybackTransport::Paused;
829 self.status = PlaybackStatus::Paused;
830 }
831 self.snapshot.play_failure = None;
832 self.snapshot.bounded_event = Some(BoundedPlaybackEvent::Failed);
833 }
834 }
835
836 pub fn cancel_bounded(&mut self) {
837 let cancelled = self
838 .snapshot
839 .bounded
840 .is_some_and(|bounded| bounded.phase.is_enforcing());
841 self.snapshot.bounded = None;
842 self.snapshot.bounded_event = cancelled.then_some(BoundedPlaybackEvent::Cancelled);
843 }
844
845 pub fn bounded_cancelled(&mut self) {
846 if let Some(bounded) = self.snapshot.bounded.as_mut()
847 && bounded.phase.is_enforcing()
848 {
849 bounded.phase = BoundedPlaybackPhase::Cancelled;
850 self.snapshot.bounded_event = Some(BoundedPlaybackEvent::Cancelled);
851 }
852 }
853
854 pub fn selected_alternative(&self) -> Option<&PlaybackSourceAlternative> {
855 self.snapshot.selected_alternative.as_ref()
856 }
857
858 pub fn source_failure(&self) -> Option<&PlaybackSourceFailure> {
859 self.snapshot.source_failure.as_ref()
860 }
861
862 pub fn repeat(&self) -> bool {
863 self.snapshot.repeat
864 }
865
866 pub fn set_repeat(&mut self, repeat: bool) {
867 self.snapshot.repeat = repeat;
868 }
869
870 pub fn toggle_repeat(&mut self) {
871 self.snapshot.repeat = !self.snapshot.repeat;
872 }
873
874 pub fn muted(&self) -> bool {
875 self.snapshot.muted
876 }
877
878 pub fn set_muted(&mut self, muted: bool) {
879 self.snapshot.muted = muted;
880 }
881
882 pub fn toggle_muted(&mut self) {
883 self.snapshot.muted = !self.snapshot.muted;
884 }
885
886 pub fn audibility_level(&self) -> PlaybackAudibilityLevel {
887 self.snapshot.audibility_level
888 }
889
890 pub fn set_audibility_level(&mut self, level: f64) -> Result<(), PlaybackCommandError> {
891 self.set_validated_audibility_level(PlaybackAudibilityLevel::new(level)?);
892 Ok(())
893 }
894
895 fn set_validated_audibility_level(&mut self, level: PlaybackAudibilityLevel) {
896 self.snapshot.audibility_level = level;
897 }
898
899 pub fn audibility_capability(&self) -> PlaybackAudibilityCapability {
900 self.snapshot.audibility_capability
901 }
902
903 pub fn graph_state(&self) -> PlaybackGraphState {
904 self.snapshot.graph
905 }
906
907 pub fn graph_preparing(&mut self) {
908 if self.graph_backed && self.snapshot.graph != PlaybackGraphState::Unavailable {
909 self.snapshot.graph = PlaybackGraphState::Preparing;
910 self.snapshot.audibility_capability = PlaybackAudibilityCapability::EffectiveGraphGain;
911 }
912 }
913
914 pub fn graph_awaiting_source(&mut self) {
915 if self.graph_backed && self.snapshot.graph != PlaybackGraphState::Unavailable {
916 self.snapshot.graph = PlaybackGraphState::AwaitingSource;
917 self.snapshot.audibility_capability = PlaybackAudibilityCapability::EffectiveGraphGain;
918 }
919 }
920
921 pub fn direct_audibility(&mut self) {
922 if self.snapshot.graph != PlaybackGraphState::Unavailable {
923 self.snapshot.audibility_capability =
924 PlaybackAudibilityCapability::BestEffortMediaElement;
925 }
926 }
927
928 pub fn graph_suspended(&mut self) {
929 if self.graph_backed
930 && !matches!(
931 self.snapshot.graph,
932 PlaybackGraphState::InteractionRequired | PlaybackGraphState::Unavailable
933 )
934 {
935 self.snapshot.graph = PlaybackGraphState::Suspended;
936 }
937 }
938
939 pub fn graph_running(&mut self) {
940 if self.graph_backed
941 && !matches!(
942 self.snapshot.graph,
943 PlaybackGraphState::InteractionRequired | PlaybackGraphState::Unavailable
944 )
945 {
946 self.snapshot.graph = PlaybackGraphState::Running;
947 }
948 }
949
950 pub fn graph_interaction_required(&mut self, error: AudioError) {
951 if !self.graph_backed
952 || self.snapshot.graph == PlaybackGraphState::Unavailable
953 || !matches!(
954 self.snapshot.source,
955 PlaybackSourceLifecycle::Loading | PlaybackSourceLifecycle::Playable
956 )
957 || !matches!(
958 self.snapshot.transport,
959 PlaybackTransport::PlayPending | PlaybackTransport::Playing
960 )
961 {
962 return;
963 }
964
965 self.status = PlaybackStatus::Failed(error.clone());
966 self.snapshot.transport = if self.snapshot.source == PlaybackSourceLifecycle::Loading {
967 PlaybackTransport::Idle
968 } else {
969 PlaybackTransport::Paused
970 };
971 if self.snapshot.readiness == PlaybackReadiness::Waiting {
972 self.snapshot.readiness = PlaybackReadiness::Metadata;
973 }
974 self.snapshot.play_failure = Some(PlaybackPlayFailure::InteractionRequired(error));
975 self.snapshot.graph = PlaybackGraphState::InteractionRequired;
976 }
977
978 pub fn graph_unavailable(&mut self) {
979 if self.graph_backed {
980 self.snapshot.graph = PlaybackGraphState::Unavailable;
981 self.snapshot.audibility_capability =
982 PlaybackAudibilityCapability::BestEffortMediaElement;
983 }
984 }
985
986 fn clear_source_failures(&mut self) {
987 self.snapshot.source_failure = None;
988 self.snapshot.alternative_failures = Arc::from([]);
989 }
990
991 fn clear_source_observations(&mut self) {
992 self.snapshot.selected_alternative = None;
993 self.clear_source_failures();
994 self.snapshot.play_failure = None;
995 self.clear_attempt_observations();
996 }
997
998 fn clear_attempt_observations(&mut self) {
999 self.snapshot.network = PlaybackNetworkActivity::Inactive;
1000 self.snapshot.buffered = Arc::from([]);
1001 self.snapshot.seekable = Arc::from([]);
1002 }
1003
1004 fn clear_bounded_for_lifecycle_change(&mut self) {
1005 if let Some(bounded) = self.snapshot.bounded.take() {
1006 self.snapshot.bounded_event = bounded
1007 .phase
1008 .is_enforcing()
1009 .then_some(BoundedPlaybackEvent::Cancelled);
1010 }
1011 }
1012
1013 pub fn loading(&mut self) {
1014 self.status = PlaybackStatus::Loading;
1015 self.snapshot.source = PlaybackSourceLifecycle::Loading;
1016 self.snapshot.transport = PlaybackTransport::Idle;
1017 self.snapshot.readiness = PlaybackReadiness::LoadingMetadata;
1018 self.clear_bounded_for_lifecycle_change();
1019 self.clear_source_observations();
1020 }
1021
1022 pub fn dormant(&mut self) {
1023 self.status = PlaybackStatus::Ready;
1024 self.snapshot.source = PlaybackSourceLifecycle::Dormant;
1025 self.snapshot.transport = PlaybackTransport::Idle;
1026 self.snapshot.readiness = PlaybackReadiness::Unavailable;
1027 self.clear_bounded_for_lifecycle_change();
1028 self.clear_source_observations();
1029 }
1030
1031 pub fn loaded(&mut self) {
1032 self.status = PlaybackStatus::Ready;
1033 self.snapshot.source = PlaybackSourceLifecycle::Playable;
1034 self.snapshot.readiness = PlaybackReadiness::Metadata;
1035 self.snapshot.bounded_event = None;
1036 self.clear_source_failures();
1037 }
1038
1039 pub fn metadata_loaded(&mut self) {
1040 if self.snapshot.source == PlaybackSourceLifecycle::Loading {
1041 self.snapshot.readiness = PlaybackReadiness::Metadata;
1042 }
1043 }
1044
1045 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1046 fn loading_alternative(&mut self) {
1047 if self.snapshot.source == PlaybackSourceLifecycle::Loading {
1048 self.status = PlaybackStatus::Loading;
1049 self.snapshot.readiness = PlaybackReadiness::LoadingMetadata;
1050 self.clear_source_failures();
1051 self.clear_attempt_observations();
1052 }
1053 }
1054
1055 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1056 fn source_attempt_started(&mut self, url_addressable: bool) {
1057 if self.snapshot.source == PlaybackSourceLifecycle::Loading {
1058 self.snapshot.network = if url_addressable {
1059 PlaybackNetworkActivity::Unknown
1060 } else {
1061 PlaybackNetworkActivity::Inactive
1062 };
1063 self.snapshot.buffered = Arc::from([]);
1064 self.snapshot.seekable = Arc::from([]);
1065 }
1066 }
1067
1068 pub fn url_playable(&mut self, alternative: PlaybackSourceAlternative) {
1069 if self.snapshot.source == PlaybackSourceLifecycle::Loading {
1070 self.status = PlaybackStatus::Ready;
1071 self.snapshot.source = PlaybackSourceLifecycle::Playable;
1072 self.snapshot.readiness = PlaybackReadiness::Playable;
1073 self.snapshot.selected_alternative = Some(alternative);
1074 self.snapshot.bounded_event = None;
1075 self.clear_source_failures();
1076 }
1077 }
1078
1079 pub fn request_play(&mut self) -> Result<(), PlaybackCommandError> {
1080 if !matches!(
1081 self.snapshot.transport,
1082 PlaybackTransport::Idle | PlaybackTransport::Paused | PlaybackTransport::Ended
1083 ) {
1084 return Err(PlaybackCommandError("audio is not ready to play"));
1085 }
1086
1087 match self.snapshot.source {
1088 PlaybackSourceLifecycle::Dormant => {
1089 self.status = PlaybackStatus::Loading;
1090 self.snapshot.source = PlaybackSourceLifecycle::Loading;
1091 self.snapshot.readiness = PlaybackReadiness::LoadingMetadata;
1092 }
1093 PlaybackSourceLifecycle::Loading => {
1094 self.status = PlaybackStatus::Loading;
1095 }
1096 PlaybackSourceLifecycle::Playable => {
1097 if self.snapshot.play_failure.is_some() {
1098 self.status = PlaybackStatus::Ready;
1099 }
1100 }
1101 PlaybackSourceLifecycle::Empty | PlaybackSourceLifecycle::Failed => {
1102 return Err(PlaybackCommandError("audio is not ready to play"));
1103 }
1104 }
1105 self.snapshot.play_failure = None;
1106 self.snapshot.bounded_event = None;
1107 if self.snapshot.graph == PlaybackGraphState::InteractionRequired {
1108 self.snapshot.graph = PlaybackGraphState::Suspended;
1109 }
1110 self.snapshot.transport = PlaybackTransport::PlayPending;
1111 Ok(())
1112 }
1113
1114 pub fn playing(&mut self) {
1115 if !matches!(self.snapshot.source, PlaybackSourceLifecycle::Playable)
1116 || !matches!(
1117 self.snapshot.transport,
1118 PlaybackTransport::PlayPending | PlaybackTransport::Playing
1119 )
1120 {
1121 return;
1122 }
1123 self.status = PlaybackStatus::Playing;
1124 self.snapshot.transport = PlaybackTransport::Playing;
1125 self.snapshot.readiness = PlaybackReadiness::Playable;
1126 self.snapshot.play_failure = None;
1127 self.snapshot.bounded_event = None;
1128 }
1129
1130 pub fn waiting(&mut self) {
1131 if matches!(self.snapshot.source, PlaybackSourceLifecycle::Playable)
1132 && matches!(
1133 self.snapshot.transport,
1134 PlaybackTransport::PlayPending | PlaybackTransport::Playing
1135 )
1136 {
1137 self.snapshot.readiness = PlaybackReadiness::Waiting;
1138 }
1139 }
1140
1141 pub fn playable(&mut self) {
1142 if matches!(self.snapshot.source, PlaybackSourceLifecycle::Playable) {
1143 self.snapshot.readiness = PlaybackReadiness::Playable;
1144 }
1145 }
1146
1147 pub fn network_observed(&mut self, activity: PlaybackNetworkActivity) {
1148 if matches!(
1149 self.snapshot.source,
1150 PlaybackSourceLifecycle::Loading | PlaybackSourceLifecycle::Playable
1151 ) {
1152 self.snapshot.network = activity;
1153 }
1154 }
1155
1156 pub fn ranges_changed(
1161 &mut self,
1162 buffered: impl IntoIterator<Item = PlaybackTimeRange>,
1163 seekable: impl IntoIterator<Item = PlaybackTimeRange>,
1164 ) {
1165 if matches!(
1166 self.snapshot.source,
1167 PlaybackSourceLifecycle::Loading | PlaybackSourceLifecycle::Playable
1168 ) {
1169 self.snapshot.buffered = normalize_time_ranges(buffered);
1170 self.snapshot.seekable = normalize_time_ranges(seekable);
1171 }
1172 }
1173
1174 pub fn paused(&mut self) {
1175 if self.snapshot.bounded.is_none() {
1176 self.snapshot.bounded_event = None;
1177 }
1178 if matches!(
1179 self.snapshot.transport,
1180 PlaybackTransport::PlayPending | PlaybackTransport::Playing
1181 ) {
1182 if self.snapshot.source == PlaybackSourceLifecycle::Loading {
1183 self.snapshot.transport = PlaybackTransport::Idle;
1184 self.status = PlaybackStatus::Loading;
1185 } else {
1186 self.snapshot.transport = PlaybackTransport::Paused;
1187 self.status = PlaybackStatus::Paused;
1188 }
1189 }
1190 }
1191
1192 pub fn stop(&mut self) -> Result<(), PlaybackCommandError> {
1194 if !matches!(self.snapshot.source, PlaybackSourceLifecycle::Playable) {
1195 return Err(PlaybackCommandError("audio is not loaded"));
1196 }
1197
1198 self.status = PlaybackStatus::Ready;
1199 self.snapshot.transport = PlaybackTransport::Idle;
1200 self.snapshot.play_failure = None;
1201 self.clear_bounded_for_lifecycle_change();
1202 Ok(())
1203 }
1204
1205 pub fn ended(&mut self) {
1206 if self.snapshot.transport != PlaybackTransport::Playing {
1207 return;
1208 }
1209 self.status = PlaybackStatus::Ended;
1210 self.snapshot.transport = PlaybackTransport::Ended;
1211 self.snapshot.readiness = PlaybackReadiness::Playable;
1212 }
1213
1214 pub fn play_rejected(&mut self, failure: PlaybackPlayFailure) {
1215 if !matches!(
1216 self.snapshot.source,
1217 PlaybackSourceLifecycle::Loading | PlaybackSourceLifecycle::Playable
1218 ) || !matches!(self.snapshot.transport, PlaybackTransport::PlayPending)
1219 {
1220 return;
1221 }
1222
1223 self.status = PlaybackStatus::Failed(failure.error().clone());
1224 self.snapshot.transport = if self.snapshot.source == PlaybackSourceLifecycle::Loading {
1225 PlaybackTransport::Idle
1226 } else {
1227 PlaybackTransport::Paused
1228 };
1229 if self.snapshot.readiness == PlaybackReadiness::Waiting {
1230 self.snapshot.readiness = PlaybackReadiness::Metadata;
1231 }
1232 self.snapshot.play_failure = Some(failure);
1233 }
1234
1235 pub fn seeked(&mut self, position: f64, duration: f64) {
1236 if matches!(
1237 self.status,
1238 PlaybackStatus::Empty | PlaybackStatus::Loading | PlaybackStatus::Failed(_)
1239 ) {
1240 return;
1241 }
1242 if duration.is_finite() && duration > 0.0 && position >= duration {
1243 self.status = PlaybackStatus::Ended;
1244 self.snapshot.transport = PlaybackTransport::Ended;
1245 } else if matches!(self.status, PlaybackStatus::Ended) {
1246 self.status = PlaybackStatus::Paused;
1247 self.snapshot.transport = PlaybackTransport::Paused;
1248 }
1249 }
1250
1251 pub fn failed(&mut self, error: AudioError) {
1252 self.source_failed(PlaybackSourceFailure::Unknown(error));
1253 }
1254
1255 pub fn source_failed(&mut self, failure: PlaybackSourceFailure) {
1256 let error = failure.error().clone();
1257 self.status = PlaybackStatus::Failed(error.clone());
1258 self.snapshot.source = PlaybackSourceLifecycle::Failed;
1259 self.snapshot.transport = PlaybackTransport::Idle;
1260 self.snapshot.readiness = PlaybackReadiness::Unavailable;
1261 self.snapshot.source_failure = Some(failure);
1262 self.snapshot.alternative_failures = Arc::from([]);
1263 self.snapshot.play_failure = None;
1264 self.clear_bounded_for_lifecycle_change();
1265 self.clear_attempt_observations();
1266 }
1267
1268 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1269 fn source_exhausted(
1270 &mut self,
1271 failure: PlaybackSourceFailure,
1272 alternative_failures: Vec<PlaybackAlternativeFailure>,
1273 ) {
1274 self.source_failed(failure);
1275 self.snapshot.alternative_failures = alternative_failures.into();
1276 }
1277
1278 pub fn unload(&mut self) {
1279 self.status = PlaybackStatus::Empty;
1280 self.snapshot.source = PlaybackSourceLifecycle::Empty;
1281 self.snapshot.transport = PlaybackTransport::Idle;
1282 self.snapshot.readiness = PlaybackReadiness::Unavailable;
1283 self.clear_bounded_for_lifecycle_change();
1284 self.clear_source_observations();
1285 if self.graph_backed && self.snapshot.graph != PlaybackGraphState::Unavailable {
1286 self.snapshot.graph = PlaybackGraphState::AwaitingSource;
1287 }
1288 }
1289}
1290
1291fn normalize_time_ranges(
1292 ranges: impl IntoIterator<Item = PlaybackTimeRange>,
1293) -> Arc<[PlaybackTimeRange]> {
1294 let mut ranges: Vec<_> = ranges.into_iter().collect();
1295 ranges.sort_unstable_by_key(|range| range.start);
1296
1297 let mut normalized: Vec<PlaybackTimeRange> = Vec::with_capacity(ranges.len());
1298 for range in ranges {
1299 if let Some(last) = normalized.last_mut()
1300 && range.start <= last.end
1301 {
1302 last.end = last.end.max(range.end);
1303 } else {
1304 normalized.push(range);
1305 }
1306 }
1307 normalized.into()
1308}
1309
1310pub fn clamp_seek(position: f64, duration: f64) -> f64 {
1312 if !position.is_finite() || !duration.is_finite() || duration <= 0.0 {
1313 return 0.0;
1314 }
1315 position.clamp(0.0, duration)
1316}
1317
1318#[derive(Clone, Copy, PartialEq)]
1319pub struct AudioPlayerController {
1320 status: ReadSignal<PlaybackStatus>,
1321 snapshot: ReadSignal<PlaybackSnapshot>,
1322 position: ReadSignal<Duration>,
1323 duration: ReadSignal<Duration>,
1324 rate: ReadSignal<f64>,
1325 analyser: ReadSignal<Option<AudioAnalyser>>,
1326 play: Callback<(), Result<(), PlaybackCommandError>>,
1327 pause: Callback<(), Result<(), PlaybackCommandError>>,
1328 stop: Callback<(), Result<(), PlaybackCommandError>>,
1329 seek: Callback<Duration>,
1330 skip: Callback<f64>,
1331 play_bounded:
1332 Callback<(WaveformSelection, BoundedPlaybackMode), Result<(), PlaybackCommandError>>,
1333 retarget_bounded: Callback<WaveformSelection, Result<(), PlaybackCommandError>>,
1334 cancel_bounded: Callback<()>,
1335 set_rate: Callback<f64, Result<(), PlaybackCommandError>>,
1336 set_repeat: Callback<bool>,
1337 set_muted: Callback<bool>,
1338 set_audibility_level: Callback<f64, Result<(), PlaybackCommandError>>,
1339}
1340
1341impl AudioPlayerController {
1342 pub fn status(self) -> ReadSignal<PlaybackStatus> {
1343 self.status
1344 }
1345
1346 pub fn snapshot(self) -> ReadSignal<PlaybackSnapshot> {
1347 self.snapshot
1348 }
1349
1350 pub fn position(self) -> ReadSignal<Duration> {
1351 self.position
1352 }
1353
1354 pub fn duration(self) -> ReadSignal<Duration> {
1355 self.duration
1356 }
1357
1358 pub fn rate(self) -> ReadSignal<f64> {
1359 self.rate
1360 }
1361
1362 pub fn analyser(self) -> ReadSignal<Option<AudioAnalyser>> {
1364 self.analyser
1365 }
1366
1367 pub fn repeat(self) -> bool {
1368 self.snapshot.read().repeat
1369 }
1370
1371 pub fn muted(self) -> bool {
1372 self.snapshot.read().muted
1373 }
1374
1375 pub fn audibility_level(self) -> PlaybackAudibilityLevel {
1376 self.snapshot.read().audibility_level
1377 }
1378
1379 pub fn audibility_capability(self) -> PlaybackAudibilityCapability {
1380 self.snapshot.read().audibility_capability
1381 }
1382
1383 pub fn play(self) -> Result<(), PlaybackCommandError> {
1384 self.play.call(())
1385 }
1386
1387 pub fn pause(self) -> Result<(), PlaybackCommandError> {
1388 self.pause.call(())
1389 }
1390
1391 pub fn stop(self) -> Result<(), PlaybackCommandError> {
1393 self.stop.call(())
1394 }
1395
1396 pub fn seek(self, position: Duration) {
1397 self.seek.call(position);
1398 }
1399
1400 pub fn skip(self, seconds: f64) {
1401 self.skip.call(seconds);
1402 }
1403
1404 pub fn play_bounded_once(
1409 self,
1410 selection: WaveformSelection,
1411 ) -> Result<(), PlaybackCommandError> {
1412 self.play_bounded
1413 .call((selection, BoundedPlaybackMode::Once))
1414 }
1415
1416 pub fn play_bounded_loop(
1421 self,
1422 selection: WaveformSelection,
1423 ) -> Result<(), PlaybackCommandError> {
1424 self.play_bounded
1425 .call((selection, BoundedPlaybackMode::Loop))
1426 }
1427
1428 pub fn retarget_bounded_playback(
1433 self,
1434 selection: WaveformSelection,
1435 ) -> Result<(), PlaybackCommandError> {
1436 self.retarget_bounded.call(selection)
1437 }
1438
1439 pub(crate) fn retarget_bounded_after_selection_commit(self, selection: WaveformSelection) {
1440 let enforcing = self
1441 .snapshot
1442 .read()
1443 .bounded
1444 .is_some_and(|bounded| bounded.phase.is_enforcing());
1445 if enforcing && self.retarget_bounded_playback(selection).is_err() {
1446 self.cancel_bounded_playback();
1447 }
1448 }
1449
1450 pub fn cancel_bounded_playback(self) {
1452 self.cancel_bounded.call(());
1453 }
1454
1455 pub fn set_rate(self, rate: f64) -> Result<(), PlaybackCommandError> {
1456 self.set_rate.call(rate)
1457 }
1458
1459 pub fn set_repeat(self, repeat: bool) {
1461 self.set_repeat.call(repeat);
1462 }
1463
1464 pub fn toggle_repeat(self) {
1466 self.set_repeat(!self.repeat());
1467 }
1468
1469 pub fn set_muted(self, muted: bool) {
1471 self.set_muted.call(muted);
1472 }
1473
1474 pub fn toggle_muted(self) {
1476 self.set_muted(!self.muted());
1477 }
1478
1479 pub fn set_audibility_level(self, level: f64) -> Result<(), PlaybackCommandError> {
1484 self.set_audibility_level.call(level)
1485 }
1486}
1487
1488pub fn use_audio_player(
1489 source: ReadSignal<Option<PlaybackSource>>,
1490 initial_duration: Duration,
1491) -> AudioPlayerController {
1492 use_audio_player_with_options(source, initial_duration, PlaybackOptions::default())
1493}
1494
1495pub fn use_audio_player_with_options(
1497 source: ReadSignal<Option<PlaybackSource>>,
1498 initial_duration: Duration,
1499 options: PlaybackOptions,
1500) -> AudioPlayerController {
1501 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1502 {
1503 web::use_web_audio_player(source, initial_duration, options)
1504 }
1505
1506 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1507 {
1508 let _ = source;
1509 use_unsupported_audio_player(initial_duration, options)
1510 }
1511}
1512
1513#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1514fn use_unsupported_audio_player(
1515 initial_duration: Duration,
1516 options: PlaybackOptions,
1517) -> AudioPlayerController {
1518 let initial_lifecycle = PlaybackLifecycle::new(options);
1519 let mut status = use_signal(|| PlaybackStatus::Empty);
1520 let mut snapshot = use_signal(|| initial_lifecycle.snapshot().clone());
1521 let position = use_signal(|| Duration::ZERO);
1522 let mut duration = use_signal(|| initial_duration);
1523 let rate = use_signal(|| 1.0);
1524 let analyser = use_signal(|| None::<AudioAnalyser>);
1525 let initial_duration = use_memo(use_reactive!(|(initial_duration,)| initial_duration));
1526 use_effect(move || {
1527 duration.set(initial_duration());
1528 let error = AudioError::unsupported();
1529 let preferences = snapshot.peek().clone();
1530 status.set(PlaybackStatus::Failed(error.clone()));
1531 snapshot.set(PlaybackSnapshot {
1532 source: PlaybackSourceLifecycle::Failed,
1533 transport: PlaybackTransport::Idle,
1534 readiness: PlaybackReadiness::Unavailable,
1535 network: PlaybackNetworkActivity::Inactive,
1536 buffered: Arc::from([]),
1537 seekable: Arc::from([]),
1538 selected_alternative: None,
1539 source_failure: Some(PlaybackSourceFailure::Unsupported(error)),
1540 alternative_failures: Arc::from([]),
1541 play_failure: None,
1542 bounded: None,
1543 bounded_event: None,
1544 repeat: preferences.repeat,
1545 muted: preferences.muted,
1546 audibility_level: preferences.audibility_level,
1547 audibility_capability: PlaybackAudibilityCapability::Unavailable,
1548 graph: if preferences.graph == PlaybackGraphState::NotRequested {
1549 PlaybackGraphState::NotRequested
1550 } else {
1551 PlaybackGraphState::Unavailable
1552 },
1553 });
1554 });
1555 let unsupported: Callback<(), Result<(), PlaybackCommandError>> =
1556 use_callback(|()| Err(PlaybackCommandError("audio playback is unsupported")));
1557 let seek = use_callback(|_: Duration| {});
1558 let skip = use_callback(|_: f64| {});
1559 let play_bounded: Callback<
1560 (WaveformSelection, BoundedPlaybackMode),
1561 Result<(), PlaybackCommandError>,
1562 > = use_callback(|_: (WaveformSelection, BoundedPlaybackMode)| {
1563 Err(PlaybackCommandError("audio playback is unsupported"))
1564 });
1565 let retarget_bounded: Callback<WaveformSelection, Result<(), PlaybackCommandError>> =
1566 use_callback(|_: WaveformSelection| {
1567 Err(PlaybackCommandError("audio playback is unsupported"))
1568 });
1569 let cancel_bounded = use_callback(|()| {});
1570 let set_rate: Callback<f64, Result<(), PlaybackCommandError>> =
1571 use_callback(|_: f64| Err(PlaybackCommandError("audio playback is unsupported")));
1572 let mut snapshot_for_repeat = snapshot;
1573 let set_repeat = use_callback(move |repeat: bool| {
1574 snapshot_for_repeat.write().repeat = repeat;
1575 });
1576 let mut snapshot_for_muted = snapshot;
1577 let set_muted = use_callback(move |muted: bool| {
1578 snapshot_for_muted.write().muted = muted;
1579 });
1580 let set_audibility_level: Callback<f64, Result<(), PlaybackCommandError>> =
1581 use_callback(|_: f64| Err(PlaybackCommandError("audio playback is unsupported")));
1582 AudioPlayerController {
1583 status: status.into(),
1584 snapshot: snapshot.into(),
1585 position: position.into(),
1586 duration: duration.into(),
1587 rate: rate.into(),
1588 analyser: analyser.into(),
1589 play: unsupported,
1590 pause: unsupported,
1591 stop: unsupported,
1592 seek,
1593 skip,
1594 play_bounded,
1595 retarget_bounded,
1596 cancel_bounded,
1597 set_rate,
1598 set_repeat,
1599 set_muted,
1600 set_audibility_level,
1601 }
1602}