1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cmp::Ordering, fmt, marker::PhantomData, str};
4
5use glib::{
6 translate::{from_glib, from_glib_none, FromGlib, IntoGlib, ToGlibPtr, ToGlibPtrMut},
7 StaticType,
8};
9
10#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
11pub enum AudioEndianness {
12 Unknown,
13 LittleEndian = 1234,
14 BigEndian = 4321,
15}
16
17impl FromGlib<i32> for AudioEndianness {
18 #[allow(unused_unsafe)]
19 #[inline]
20 unsafe fn from_glib(value: i32) -> Self {
21 skip_assert_initialized!();
22
23 match value {
24 1234 => Self::LittleEndian,
25 4321 => Self::BigEndian,
26 _ => Self::Unknown,
27 }
28 }
29}
30
31impl IntoGlib for AudioEndianness {
32 type GlibType = i32;
33
34 #[inline]
35 fn into_glib(self) -> i32 {
36 match self {
37 Self::LittleEndian => 1234,
38 Self::BigEndian => 4321,
39 _ => 0,
40 }
41 }
42}
43
44#[doc(alias = "GstAudioFormatInfo")]
45#[derive(Copy, Clone)]
46pub struct AudioFormatInfo(&'static ffi::GstAudioFormatInfo);
47
48impl AudioFormatInfo {
49 #[inline]
50 pub fn from_format(format: crate::AudioFormat) -> Self {
51 assert_initialized_main_thread!();
52
53 unsafe {
54 let info = ffi::gst_audio_format_get_info(format.into_glib());
55 debug_assert!(!info.is_null());
56
57 Self(&*info)
58 }
59 }
60
61 #[inline]
62 pub fn format(&self) -> crate::AudioFormat {
63 unsafe { from_glib(self.0.format) }
64 }
65
66 #[inline]
67 pub fn name<'a>(&self) -> &'a glib::GStr {
68 unsafe { glib::GStr::from_ptr(self.0.name) }
69 }
70
71 #[inline]
72 pub fn description<'a>(&self) -> &'a glib::GStr {
73 unsafe { glib::GStr::from_ptr(self.0.description) }
74 }
75
76 #[inline]
77 pub fn flags(&self) -> crate::AudioFormatFlags {
78 unsafe { from_glib(self.0.flags) }
79 }
80
81 #[inline]
82 pub fn endianness(&self) -> AudioEndianness {
83 unsafe { from_glib(self.0.endianness) }
84 }
85
86 #[inline]
87 pub fn width(&self) -> u32 {
88 self.0.width as u32
89 }
90
91 #[inline]
92 pub fn depth(&self) -> u32 {
93 self.0.depth as u32
94 }
95
96 #[inline]
97 pub fn unpack_format(&self) -> crate::AudioFormat {
98 unsafe { from_glib(self.0.unpack_format) }
99 }
100
101 #[inline]
102 pub fn silence<'a>(&self) -> &'a [u8] {
103 &self.0.silence
104 }
105
106 pub fn unpack(&self, flags: crate::AudioPackFlags, dest: &mut [u8], src: &[u8]) {
107 let unpack_format = Self::from_format(self.unpack_format());
108 let unpack_width = unpack_format.width() as usize;
109
110 if unpack_width == 0 || self.0.unpack_func.is_none() {
111 panic!("No unpack format for {self:?}");
112 }
113
114 let self_width = self.width() as usize;
115 if self_width == 0 {
116 panic!("No width for {self:?}");
117 }
118
119 if src.len() % (self_width / 8) != 0 {
120 panic!("Incomplete number of samples in src");
121 }
122
123 let nsamples = src.len() / (self_width / 8);
124
125 if dest.len() != nsamples * (unpack_width / 8) {
126 panic!("Invalid dest length");
127 }
128
129 unsafe {
130 (self.0.unpack_func.as_ref().unwrap())(
131 self.0,
132 flags.into_glib(),
133 dest.as_mut_ptr() as *mut _,
134 src.as_ptr() as *const _,
135 nsamples as i32,
136 );
137 }
138 }
139
140 pub fn pack(&self, flags: crate::AudioPackFlags, dest: &mut [u8], src: &[u8]) {
141 let unpack_format = Self::from_format(self.unpack_format());
142 let unpack_width = unpack_format.width() as usize;
143
144 if unpack_width == 0 || self.0.pack_func.is_none() {
145 panic!("No unpack format for {self:?}");
146 }
147
148 let self_width = self.width() as usize;
149 if self_width == 0 {
150 panic!("No width for {self:?}");
151 }
152
153 if src.len() % (unpack_width / 8) != 0 {
154 panic!("Incomplete number of samples in src");
155 }
156
157 let nsamples = src.len() / (unpack_width / 8);
158
159 if dest.len() != nsamples * (self_width / 8) {
160 panic!("Invalid dest length");
161 }
162
163 unsafe {
164 (self.0.pack_func.as_ref().unwrap())(
165 self.0,
166 flags.into_glib(),
167 src.as_ptr() as *const _,
168 dest.as_mut_ptr() as *mut _,
169 nsamples as i32,
170 );
171 }
172 }
173
174 #[doc(alias = "gst_audio_format_info_fill_silence")]
175 #[doc(alias = "gst_audio_format_fill_silence")]
176 pub fn fill_silence(&self, dest: &mut [u8]) {
177 let self_width = self.width() as usize;
178
179 if self_width == 0 {
180 panic!("Filling with silence unsupported");
181 }
182
183 if dest.len() % (self_width / 8) != 0 {
184 panic!("Incomplete number of samples in dest");
185 }
186
187 unsafe {
188 cfg_if::cfg_if! {
189 if #[cfg(feature = "v1_20")] {
190 ffi::gst_audio_format_info_fill_silence(self.0, dest.as_mut_ptr() as *mut _, dest.len())
191 } else {
192 ffi::gst_audio_format_fill_silence(self.0, dest.as_mut_ptr() as *mut _, dest.len())
193 }
194 }
195 }
196 }
197
198 #[inline]
199 pub fn is_float(&self) -> bool {
200 self.flags().contains(crate::AudioFormatFlags::FLOAT)
201 }
202
203 #[inline]
204 pub fn is_integer(&self) -> bool {
205 self.flags().contains(crate::AudioFormatFlags::INTEGER)
206 }
207
208 #[inline]
209 pub fn is_signed(&self) -> bool {
210 self.flags().contains(crate::AudioFormatFlags::SIGNED)
211 }
212
213 #[inline]
214 pub fn is_little_endian(&self) -> bool {
215 self.endianness() == AudioEndianness::LittleEndian
216 }
217
218 #[inline]
219 pub fn is_big_endian(&self) -> bool {
220 self.endianness() == AudioEndianness::BigEndian
221 }
222}
223
224unsafe impl Sync for AudioFormatInfo {}
225unsafe impl Send for AudioFormatInfo {}
226
227impl PartialEq for AudioFormatInfo {
228 #[inline]
229 fn eq(&self, other: &Self) -> bool {
230 self.format() == other.format()
231 }
232}
233
234impl Eq for AudioFormatInfo {}
235
236impl PartialOrd for AudioFormatInfo {
237 #[inline]
238 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
239 Some(self.cmp(other))
240 }
241}
242
243impl Ord for AudioFormatInfo {
244 // See GST_AUDIO_FORMATS_ALL for the sorting algorithm
245 fn cmp(&self, other: &Self) -> Ordering {
246 self.depth()
247 .cmp(&other.depth())
248 .then_with(|| self.width().cmp(&other.width()))
249 .then_with(|| {
250 match (
251 self.flags().contains(crate::AudioFormatFlags::FLOAT),
252 other.flags().contains(crate::AudioFormatFlags::FLOAT),
253 ) {
254 (true, false) => Ordering::Greater,
255 (false, true) => Ordering::Less,
256 _ => Ordering::Equal,
257 }
258 })
259 .then_with(|| {
260 match (
261 self.flags().contains(crate::AudioFormatFlags::SIGNED),
262 other.flags().contains(crate::AudioFormatFlags::SIGNED),
263 ) {
264 (true, false) => Ordering::Greater,
265 (false, true) => Ordering::Less,
266 _ => Ordering::Equal,
267 }
268 })
269 .then_with(|| match (self.endianness(), other.endianness()) {
270 (crate::AudioEndianness::LittleEndian, crate::AudioEndianness::BigEndian) => {
271 #[cfg(target_endian = "little")]
272 {
273 Ordering::Greater
274 }
275 #[cfg(target_endian = "big")]
276 {
277 Ordering::Less
278 }
279 }
280 (crate::AudioEndianness::BigEndian, crate::AudioEndianness::LittleEndian) => {
281 #[cfg(target_endian = "little")]
282 {
283 Ordering::Less
284 }
285 #[cfg(target_endian = "big")]
286 {
287 Ordering::Greater
288 }
289 }
290 _ => Ordering::Equal,
291 })
292 }
293}
294
295impl fmt::Debug for AudioFormatInfo {
296 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297 f&mut DebugStruct<'_, '_>.debug_struct("AudioFormatInfo")
298 .field("format", &self.format())
299 .field("name", &self.name())
300 .field("description", &self.description())
301 .field("flags", &self.flags())
302 .field("endianness", &self.endianness())
303 .field("width", &self.width())
304 .field("depth", &self.depth())
305 .field(name:"silence", &self.silence())
306 .finish()
307 }
308}
309
310impl fmt::Display for AudioFormatInfo {
311 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312 f.write_str(self.name())
313 }
314}
315
316impl str::FromStr for crate::AudioFormatInfo {
317 type Err = glib::BoolError;
318
319 fn from_str(s: &str) -> Result<Self, Self::Err> {
320 skip_assert_initialized!();
321 let format: AudioFormat = s.parse()?;
322 Ok(Self::from_format(format))
323 }
324}
325
326impl From<crate::AudioFormat> for AudioFormatInfo {
327 #[inline]
328 fn from(f: crate::AudioFormat) -> Self {
329 skip_assert_initialized!();
330 Self::from_format(f)
331 }
332}
333
334impl glib::types::StaticType for AudioFormatInfo {
335 #[inline]
336 fn static_type() -> glib::types::Type {
337 unsafe { glib::translate::from_glib(val:ffi::gst_audio_format_info_get_type()) }
338 }
339}
340
341impl glib::value::ValueType for AudioFormatInfo {
342 type Type = Self;
343}
344
345#[doc(hidden)]
346unsafe impl<'a> glib::value::FromValue<'a> for AudioFormatInfo {
347 type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
348
349 unsafe fn from_value(value: &'a glib::Value) -> Self {
350 skip_assert_initialized!();
351 from_glib_none(ptr:glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0)
352 as *mut ffi::GstAudioFormatInfo)
353 }
354}
355
356#[doc(hidden)]
357impl glib::value::ToValue for AudioFormatInfo {
358 fn to_value(&self) -> glib::Value {
359 let mut value: Value = glib::Value::for_value_type::<Self>();
360 unsafe {
361 glib::gobject_ffi::g_value_set_boxed(
362 value:value.to_glib_none_mut().0,
363 self.to_glib_none().0 as *mut _,
364 )
365 }
366 value
367 }
368
369 fn value_type(&self) -> glib::Type {
370 Self::static_type()
371 }
372}
373
374#[doc(hidden)]
375impl glib::value::ToValueOptional for AudioFormatInfo {
376 fn to_value_optional(s: Option<&Self>) -> glib::Value {
377 skip_assert_initialized!();
378 let mut value: Value = glib::Value::for_value_type::<Self>();
379 unsafe {
380 glib::gobject_ffi::g_value_set_boxed(
381 value:value.to_glib_none_mut().0,
382 v_boxed:s.to_glib_none().0 as *mut _,
383 )
384 }
385 value
386 }
387}
388
389#[doc(hidden)]
390impl From<AudioFormatInfo> for glib::Value {
391 fn from(v: AudioFormatInfo) -> glib::Value {
392 skip_assert_initialized!();
393 glib::value::ToValue::to_value(&v)
394 }
395}
396
397#[doc(hidden)]
398impl glib::translate::GlibPtrDefault for AudioFormatInfo {
399 type GlibType = *mut ffi::GstAudioFormatInfo;
400}
401
402#[doc(hidden)]
403unsafe impl glib::translate::TransparentPtrType for AudioFormatInfo {}
404
405#[doc(hidden)]
406impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstAudioFormatInfo> for AudioFormatInfo {
407 type Storage = PhantomData<&'a Self>;
408
409 #[inline]
410 fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstAudioFormatInfo, Self> {
411 glib::translate::Stash(self.0, PhantomData)
412 }
413
414 fn to_glib_full(&self) -> *const ffi::GstAudioFormatInfo {
415 unimplemented!()
416 }
417}
418
419#[doc(hidden)]
420impl glib::translate::FromGlibPtrNone<*mut ffi::GstAudioFormatInfo> for AudioFormatInfo {
421 #[inline]
422 unsafe fn from_glib_none(ptr: *mut ffi::GstAudioFormatInfo) -> Self {
423 Self(&*ptr)
424 }
425}
426
427#[doc(hidden)]
428impl glib::translate::FromGlibPtrNone<*const ffi::GstAudioFormatInfo> for AudioFormatInfo {
429 #[inline]
430 unsafe fn from_glib_none(ptr: *const ffi::GstAudioFormatInfo) -> Self {
431 Self(&*ptr)
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438
439 #[test]
440 fn test_get() {
441 gst::init().unwrap();
442
443 let info = AudioFormatInfo::from_format(crate::AudioFormat::S16le);
444 assert_eq!(info.name(), "S16LE");
445
446 let other_info = "S16LE".parse().unwrap();
447 assert_eq!(info, other_info);
448 }
449
450 #[test]
451 fn pack_unpack() {
452 gst::init().unwrap();
453
454 let info = AudioFormatInfo::from_format(crate::AudioFormat::S16le);
455 let unpack_info = AudioFormatInfo::from_format(info.unpack_format());
456
457 assert!(unpack_info.width() > 0);
458
459 let input = [0, 0, 255, 255, 128, 128, 64, 64];
460 let mut unpacked = [0; 16];
461 let mut output = [0; 8];
462
463 info.unpack(crate::AudioPackFlags::empty(), &mut unpacked, &input);
464 info.pack(crate::AudioPackFlags::empty(), &mut output, &unpacked);
465
466 assert_eq!(input, output);
467 }
468}
469