1use std::time::Duration;
6
7use js_int::UInt;
8use ruma_macros::EventContent;
9use serde::{Deserialize, Serialize};
10
11#[cfg(feature = "unstable-msc3246")]
12mod amplitude_serde;
13
14use super::{
15 file::{CaptionContentBlock, FileContentBlock},
16 message::TextContentBlock,
17 room::message::Relation,
18};
19
20#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
28#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
29#[ruma_event(type = "org.matrix.msc1767.audio", kind = MessageLike, without_relation)]
30pub struct AudioEventContent {
31 #[serde(rename = "org.matrix.msc1767.text")]
33 pub text: TextContentBlock,
34
35 #[serde(rename = "org.matrix.msc1767.file")]
37 pub file: FileContentBlock,
38
39 #[serde(rename = "org.matrix.msc1767.audio_details", skip_serializing_if = "Option::is_none")]
41 pub audio_details: Option<AudioDetailsContentBlock>,
42
43 #[serde(rename = "org.matrix.msc1767.caption", skip_serializing_if = "Option::is_none")]
45 pub caption: Option<CaptionContentBlock>,
46
47 #[cfg(feature = "unstable-msc3955")]
49 #[serde(
50 default,
51 skip_serializing_if = "ruma_common::serde::is_default",
52 rename = "org.matrix.msc1767.automated"
53 )]
54 pub automated: bool,
55
56 #[serde(
58 flatten,
59 skip_serializing_if = "Option::is_none",
60 deserialize_with = "crate::room::message::relation_serde::deserialize_relation"
61 )]
62 pub relates_to: Option<Relation<AudioEventContentWithoutRelation>>,
63}
64
65impl AudioEventContent {
66 pub fn new(text: TextContentBlock, file: FileContentBlock) -> Self {
68 Self {
69 text,
70 file,
71 audio_details: None,
72 caption: None,
73 #[cfg(feature = "unstable-msc3955")]
74 automated: false,
75 relates_to: None,
76 }
77 }
78
79 pub fn with_plain_text(plain_text: impl Into<String>, file: FileContentBlock) -> Self {
82 Self {
83 text: TextContentBlock::plain(plain_text),
84 file,
85 audio_details: None,
86 caption: None,
87 #[cfg(feature = "unstable-msc3955")]
88 automated: false,
89 relates_to: None,
90 }
91 }
92}
93
94#[derive(Clone, Debug, Serialize, Deserialize)]
96#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
97pub struct AudioDetailsContentBlock {
98 #[serde(with = "ruma_common::serde::duration::secs")]
100 pub duration: Duration,
101
102 #[cfg(feature = "unstable-msc3246")]
106 #[serde(
107 rename = "org.matrix.msc3246.waveform",
108 default,
109 skip_serializing_if = "Vec::is_empty"
110 )]
111 pub waveform: Vec<Amplitude>,
112}
113
114impl AudioDetailsContentBlock {
115 pub fn new(duration: Duration) -> Self {
117 Self {
118 duration,
119 #[cfg(feature = "unstable-msc3246")]
120 waveform: Default::default(),
121 }
122 }
123}
124
125#[cfg(feature = "unstable-msc3246")]
129#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)]
130pub struct Amplitude(UInt);
131
132#[cfg(feature = "unstable-msc3246")]
133impl Amplitude {
134 pub const MIN: u16 = 0;
136
137 pub const MAX: u16 = 256;
139
140 pub fn new(value: u16) -> Self {
144 Self(value.min(Self::MAX).into())
145 }
146
147 pub fn get(&self) -> UInt {
149 self.0
150 }
151}
152
153#[cfg(feature = "unstable-msc3246")]
154impl From<u16> for Amplitude {
155 fn from(value: u16) -> Self {
156 Self::new(value)
157 }
158}