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