1use std::ops::{Deref, DerefMut};
2use std::time::Duration;
3
4use crate::{Error, Result, Timestamp};
5
6pub use web_sys::AudioSampleFormat as AudioDataFormat;
7
8#[derive(Debug)]
11pub struct AudioData(Option<web_sys::AudioData>);
12
13impl AudioData {
14 pub fn new<'a>(
17 channels: impl ExactSizeIterator<Item = &'a [f32]>,
18 sample_rate: u32,
19 timestamp: Timestamp,
20 ) -> Result<Self> {
21 let mut channels = channels.enumerate();
22 let channel_count = channels.size_hint().0;
23 let (_, channel) = channels.next().ok_or(Error::NoChannels)?;
24
25 let frame_count = channel.len();
26 let total_samples = channel_count * frame_count;
27
28 let data = js_sys::Float32Array::new_with_length(total_samples as _);
30
31 let slice = js_sys::Float32Array::new_with_byte_offset_and_length(&data.buffer(), 0, frame_count as _);
33 slice.copy_from(channel);
34
35 for (i, channel) in channels {
36 let slice = js_sys::Float32Array::new_with_byte_offset_and_length(
38 &data.buffer(),
39 (i * frame_count) as u32,
40 frame_count as _,
41 );
42 slice.copy_from(channel);
43 }
44
45 let init = web_sys::AudioDataInit::new(
46 &data,
47 AudioDataFormat::F32Planar,
48 channel_count as _,
49 frame_count as _,
50 sample_rate as _,
51 timestamp.as_micros() as _,
52 );
53
54 let transfer = js_sys::Array::new();
58 transfer.push(&data.buffer());
59 js_sys::Reflect::set(&init, &js_sys::JsString::from("transfer"), &transfer)?;
60
61 let audio_data = web_sys::AudioData::new(&init)?;
62 Ok(Self(Some(audio_data)))
63 }
64
65 pub fn timestamp(&self) -> Timestamp {
66 Timestamp::from_micros(self.0.as_ref().unwrap().timestamp() as _)
67 }
68
69 pub fn duration(&self) -> Duration {
70 Duration::from_micros(self.0.as_ref().unwrap().duration() as _)
71 }
72
73 pub fn sample_rate(&self) -> u32 {
74 self.0.as_ref().unwrap().sample_rate() as u32
75 }
76
77 pub fn append_to<T: AudioAppend>(&self, dst: &mut T, channel: usize, options: AudioCopyOptions) -> Result<()> {
78 dst.append_to(self, channel, options)
79 }
80
81 pub fn copy_to<T: AudioCopy>(&self, dst: &mut T, channel: usize, options: AudioCopyOptions) -> Result<()> {
82 dst.copy_to(self, channel, options)
83 }
84
85 pub fn leak(mut self) -> web_sys::AudioData {
86 self.0.take().unwrap()
87 }
88}
89
90impl Clone for AudioData {
91 fn clone(&self) -> Self {
92 Self(self.0.clone())
93 }
94}
95
96impl Deref for AudioData {
97 type Target = web_sys::AudioData;
98
99 fn deref(&self) -> &Self::Target {
100 self.0.as_ref().unwrap()
101 }
102}
103
104impl DerefMut for AudioData {
105 fn deref_mut(&mut self) -> &mut Self::Target {
106 self.0.as_mut().unwrap()
107 }
108}
109
110impl Drop for AudioData {
112 fn drop(&mut self) {
113 if let Some(audio_data) = self.0.take() {
114 audio_data.close();
115 }
116 }
117}
118
119impl From<web_sys::AudioData> for AudioData {
120 fn from(this: web_sys::AudioData) -> Self {
121 Self(Some(this))
122 }
123}
124
125pub trait AudioCopy {
126 fn copy_to(&mut self, data: &AudioData, channel: usize, options: AudioCopyOptions) -> Result<()>;
127}
128
129impl AudioCopy for [u8] {
130 fn copy_to(&mut self, data: &AudioData, channel: usize, options: AudioCopyOptions) -> Result<()> {
131 let options = options.into_web_sys(channel);
132 data.0.as_ref().unwrap().copy_to_with_u8_slice(self, &options)?;
135 Ok(())
136 }
137}
138
139impl AudioCopy for [f32] {
140 fn copy_to(&mut self, data: &AudioData, channel: usize, options: AudioCopyOptions) -> Result<()> {
141 let options = options.into_web_sys(channel);
142 options.set_format(AudioDataFormat::F32Planar);
143
144 let bytes = bytemuck::cast_slice_mut(self);
146 data.0.as_ref().unwrap().copy_to_with_u8_slice(bytes, &options)?;
147 Ok(())
148 }
149}
150
151impl AudioCopy for js_sys::Uint8Array {
152 fn copy_to(&mut self, data: &AudioData, channel: usize, options: AudioCopyOptions) -> Result<()> {
153 let options = options.into_web_sys(channel);
154 data.0.as_ref().unwrap().copy_to_with_u8_array(self, &options)?;
155 Ok(())
156 }
157}
158
159impl AudioCopy for js_sys::Float32Array {
160 fn copy_to(&mut self, data: &AudioData, channel: usize, options: AudioCopyOptions) -> Result<()> {
161 let options = options.into_web_sys(channel);
162 data.0.as_ref().unwrap().copy_to_with_buffer_source(self, &options)?;
163 Ok(())
164 }
165}
166
167pub trait AudioAppend {
168 fn append_to(&mut self, data: &AudioData, channel: usize, options: AudioCopyOptions) -> Result<()>;
169}
170
171impl AudioAppend for Vec<f32> {
172 fn append_to(&mut self, data: &AudioData, channel: usize, options: AudioCopyOptions) -> Result<()> {
173 let grow = options.count.unwrap_or(data.number_of_frames() as _) - options.offset;
175 let offset = self.len();
176 self.resize(offset + grow, 0.0);
177
178 let options = options.into_web_sys(channel);
179 let bytes = bytemuck::cast_slice_mut(&mut self[offset..]);
180 data.0.as_ref().unwrap().copy_to_with_u8_slice(bytes, &options)?;
181
182 Ok(())
183 }
184}
185
186#[derive(Debug, Default)]
187pub struct AudioCopyOptions {
188 pub offset: usize, pub count: Option<usize>, }
191
192impl AudioCopyOptions {
193 fn into_web_sys(self, channel: usize) -> web_sys::AudioDataCopyToOptions {
194 let options = web_sys::AudioDataCopyToOptions::new(channel as _);
195 options.set_frame_offset(self.offset as _);
196 if let Some(count) = self.count {
197 options.set_frame_count(count as _);
198 }
199 options
200 }
201}