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