forked from mirrors/gstreamer-rs
Add helper for converting a raw audio buffer to an array of integers/floats
This commit is contained in:
parent
4276cb6228
commit
b655c838b2
6 changed files with 223 additions and 15 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -112,6 +112,7 @@ dependencies = [
|
|||
"glib 0.1.3 (git+https://github.com/gtk-rs/glib)",
|
||||
"gstreamer 0.1.0",
|
||||
"gstreamer-app 0.1.0",
|
||||
"gstreamer-audio 0.1.0",
|
||||
"gstreamer-player 0.1.0",
|
||||
"gtk 0.1.3 (git+https://github.com/gtk-rs/gtk)",
|
||||
"send-cell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -7,6 +7,7 @@ authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
|||
glib = { version = "0.1.3", git = "https://github.com/gtk-rs/glib" }
|
||||
gstreamer = { path = "../gstreamer" }
|
||||
gstreamer-app = { path = "../gstreamer-app" }
|
||||
gstreamer-audio = { path = "../gstreamer-audio" }
|
||||
gstreamer-player = { path = "../gstreamer-player", optional = true }
|
||||
gtk = { version = "0.1.3", git = "https://github.com/gtk-rs/gtk", features = ["v3_6"] }
|
||||
gio = { version = "0.1.3", git = "https://github.com/gtk-rs/gio" }
|
||||
|
|
|
@ -2,6 +2,7 @@ extern crate gstreamer as gst;
|
|||
use gst::*;
|
||||
extern crate gstreamer_app as gst_app;
|
||||
use gst_app::*;
|
||||
extern crate gstreamer_audio as gst_audio;
|
||||
|
||||
extern crate glib;
|
||||
|
||||
|
@ -30,7 +31,7 @@ fn create_pipeline() -> Result<Pipeline, utils::ExampleError> {
|
|||
appsink.set_caps(&Caps::new_simple(
|
||||
"audio/x-raw",
|
||||
&[
|
||||
("format", &"S16BE"),
|
||||
("format", &gst_audio::AUDIO_FORMAT_S16.to_string()),
|
||||
("layout", &"interleaved"),
|
||||
("channels", &(1i32)),
|
||||
("rate", &IntRange::<i32>::new(1, i32::MAX)),
|
||||
|
@ -52,19 +53,27 @@ fn create_pipeline() -> Result<Pipeline, utils::ExampleError> {
|
|||
let buffer = sample
|
||||
.get_buffer()
|
||||
.expect("Unable to extract buffer from the sample");
|
||||
assert_eq!(buffer.get_size() % 2, 0);
|
||||
|
||||
let map = buffer
|
||||
.map_readable()
|
||||
.expect("Unable to map buffer for reading");
|
||||
let data = map.as_slice();
|
||||
let sum: f64 = data.chunks(2)
|
||||
|
||||
let data =
|
||||
gst_audio::AudioData::new(map.as_slice(), gst_audio::AUDIO_FORMAT_S16).unwrap();
|
||||
let samples = if let gst_audio::AudioData::S16(samples) = data {
|
||||
samples
|
||||
} else {
|
||||
return FlowReturn::NotNegotiated;
|
||||
};
|
||||
|
||||
let sum: f64 = samples
|
||||
.iter()
|
||||
.map(|sample| {
|
||||
let u: u16 = ((sample[0] as u16) << 8) | (sample[1] as u16);
|
||||
let f = (u as i16 as f64) / (i16::MAX as f64);
|
||||
let f = (*sample as f64) / (i16::MAX as f64);
|
||||
f * f
|
||||
})
|
||||
.sum();
|
||||
let rms = (sum / ((data.len() / 2) as f64)).sqrt();
|
||||
let rms = (sum / (samples.len() as f64)).sqrt();
|
||||
println!("rms: {}", rms);
|
||||
|
||||
FlowReturn::Ok
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
extern crate gstreamer as gst;
|
||||
use gst::*;
|
||||
|
||||
extern crate gstreamer_audio as gst_audio;
|
||||
|
||||
use std::u64;
|
||||
use std::i16;
|
||||
|
||||
fn main() {
|
||||
gst::init().unwrap();
|
||||
|
||||
let pipeline = gst::parse_launch(
|
||||
"audiotestsrc name=src ! audio/x-raw,format=S16BE,channels=1 ! fakesink",
|
||||
).unwrap();
|
||||
let pipeline = gst::parse_launch(&format!(
|
||||
"audiotestsrc name=src ! audio/x-raw,format={},channels=1 ! fakesink",
|
||||
gst_audio::AUDIO_FORMAT_S16.to_string()
|
||||
)).unwrap();
|
||||
let bus = pipeline.get_bus().unwrap();
|
||||
|
||||
let src = pipeline
|
||||
|
@ -22,15 +25,23 @@ fn main() {
|
|||
src_pad.add_probe(PAD_PROBE_TYPE_BUFFER, |_, probe_info| {
|
||||
if let Some(PadProbeData::Buffer(ref buffer)) = probe_info.data {
|
||||
let map = buffer.map_readable().unwrap();
|
||||
let data = map.as_slice();
|
||||
let sum: f64 = data.chunks(2)
|
||||
|
||||
let data =
|
||||
gst_audio::AudioData::new(map.as_slice(), gst_audio::AUDIO_FORMAT_S16).unwrap();
|
||||
let samples = if let gst_audio::AudioData::S16(samples) = data {
|
||||
samples
|
||||
} else {
|
||||
return PadProbeReturn::Ok;
|
||||
};
|
||||
|
||||
let sum: f64 = samples
|
||||
.iter()
|
||||
.map(|sample| {
|
||||
let u: u16 = ((sample[0] as u16) << 8) | (sample[1] as u16);
|
||||
let f = (u as i16 as f64) / (i16::MAX as f64);
|
||||
let f = (*sample as f64) / (i16::MAX as f64);
|
||||
f * f
|
||||
})
|
||||
.sum();
|
||||
let rms = (sum / ((data.len() / 2) as f64)).sqrt();
|
||||
let rms = (sum / (samples.len() as f64)).sqrt();
|
||||
println!("rms: {}", rms);
|
||||
}
|
||||
|
||||
|
|
184
gstreamer-audio/src/audio_data.rs
Normal file
184
gstreamer-audio/src/audio_data.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::slice;
|
||||
use std::fmt;
|
||||
use libc::uintptr_t;
|
||||
|
||||
use std::error::Error as StdError;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
WrongAlignment,
|
||||
WrongEndianness,
|
||||
IncompleteSamples,
|
||||
UnsupportedFormat,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.write_str(self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
use self::Error::*;
|
||||
|
||||
match *self {
|
||||
WrongAlignment => "Wrong Alignment",
|
||||
WrongEndianness => "Wrong Endianness",
|
||||
IncompleteSamples => "Incomplete Samples",
|
||||
UnsupportedFormat => "Unsupported Format",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AudioData<'a> {
|
||||
S8(&'a [i8]),
|
||||
U8(&'a [u8]),
|
||||
S16(&'a [i16]),
|
||||
U16(&'a [u16]),
|
||||
S32(&'a [i32]),
|
||||
U32(&'a [u32]),
|
||||
F32(&'a [f32]),
|
||||
F64(&'a [f64]),
|
||||
}
|
||||
|
||||
impl<'a> AudioData<'a> {
|
||||
pub fn new(data: &'a [u8], format: ::AudioFormat) -> Result<AudioData<'a>, Error> {
|
||||
use AudioFormat::*;
|
||||
|
||||
let alignment = if (data.as_ptr() as uintptr_t) % 8 == 0 {
|
||||
8
|
||||
} else if (data.as_ptr() as uintptr_t) % 4 == 0 {
|
||||
4
|
||||
} else if (data.as_ptr() as uintptr_t) % 2 == 0 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let format_info = ::AudioFormatInfo::from_format(format);
|
||||
let width = (format_info.width() / 8) as usize;
|
||||
|
||||
if width != 1 && cfg!(target_endian = "big") && format_info.is_little_endian() {
|
||||
return Err(Error::WrongEndianness);
|
||||
} else if width != 1 && cfg!(target_endian = "little") && format_info.is_big_endian() {
|
||||
return Err(Error::WrongEndianness);
|
||||
}
|
||||
|
||||
if alignment < width {
|
||||
return Err(Error::WrongAlignment);
|
||||
}
|
||||
|
||||
if data.len() % width != 0 {
|
||||
return Err(Error::IncompleteSamples);
|
||||
}
|
||||
|
||||
match format {
|
||||
S8 => Ok(AudioData::S8(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len())
|
||||
})),
|
||||
U8 => Ok(AudioData::U8(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len())
|
||||
})),
|
||||
S16le | S16be => Ok(AudioData::S16(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len() / 2)
|
||||
})),
|
||||
U16le | U16be => Ok(AudioData::U16(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len() / 2)
|
||||
})),
|
||||
S32le | S2432le | S32be | S2432be => Ok(AudioData::S32(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len() / 4)
|
||||
})),
|
||||
U32le | U2432le | U32be | U2432be => Ok(AudioData::U32(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len() / 4)
|
||||
})),
|
||||
F32le | F32be => Ok(AudioData::F32(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len() / 4)
|
||||
})),
|
||||
F64le | F64be => Ok(AudioData::F64(unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const _, data.len() / 8)
|
||||
})),
|
||||
_ => Err(Error::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AudioDataMut<'a> {
|
||||
S8(&'a mut [i8]),
|
||||
U8(&'a mut [u8]),
|
||||
S16(&'a mut [i16]),
|
||||
U16(&'a mut [u16]),
|
||||
S32(&'a mut [i32]),
|
||||
U32(&'a mut [u32]),
|
||||
F32(&'a mut [f32]),
|
||||
F64(&'a mut [f64]),
|
||||
}
|
||||
|
||||
impl<'a> AudioDataMut<'a> {
|
||||
pub fn new(data: &'a mut [u8], format: ::AudioFormat) -> Result<AudioDataMut<'a>, Error> {
|
||||
use AudioFormat::*;
|
||||
|
||||
let alignment = if (data.as_ptr() as uintptr_t) % 8 == 0 {
|
||||
8
|
||||
} else if (data.as_ptr() as uintptr_t) % 4 == 0 {
|
||||
4
|
||||
} else if (data.as_ptr() as uintptr_t) % 2 == 0 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let format_info = ::AudioFormatInfo::from_format(format);
|
||||
let width = (format_info.width() / 8) as usize;
|
||||
|
||||
if width != 1 && cfg!(target_endian = "big") && format_info.is_little_endian() {
|
||||
return Err(Error::WrongEndianness);
|
||||
} else if width != 1 && cfg!(target_endian = "little") && format_info.is_big_endian() {
|
||||
return Err(Error::WrongEndianness);
|
||||
}
|
||||
|
||||
if alignment < width {
|
||||
return Err(Error::WrongAlignment);
|
||||
}
|
||||
|
||||
if data.len() % width != 0 {
|
||||
return Err(Error::IncompleteSamples);
|
||||
}
|
||||
|
||||
match format {
|
||||
S8 => Ok(AudioDataMut::S8(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len())
|
||||
})),
|
||||
U8 => Ok(AudioDataMut::U8(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len())
|
||||
})),
|
||||
S16le | S16be => Ok(AudioDataMut::S16(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len() / 2)
|
||||
})),
|
||||
U16le | U16be => Ok(AudioDataMut::U16(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len() / 2)
|
||||
})),
|
||||
S32le | S2432le | S32be | S2432be => Ok(AudioDataMut::S32(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len() / 4)
|
||||
})),
|
||||
U32le | U2432le | U32be | U2432be => Ok(AudioDataMut::U32(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len() / 4)
|
||||
})),
|
||||
F32le | F32be => Ok(AudioDataMut::F32(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len() / 4)
|
||||
})),
|
||||
F64le | F64be => Ok(AudioDataMut::F64(unsafe {
|
||||
slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len() / 8)
|
||||
})),
|
||||
_ => Err(Error::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,6 +49,8 @@ mod audio_info;
|
|||
pub use audio_info::*;
|
||||
mod audio_channel_position;
|
||||
pub use audio_channel_position::*;
|
||||
mod audio_data;
|
||||
pub use audio_data::{AudioData, AudioDataMut};
|
||||
|
||||
use glib::translate::{from_glib_full, ToGlibPtr};
|
||||
pub fn audio_buffer_clip(
|
||||
|
|
Loading…
Reference in a new issue