mod raw;
use crate::OperationError;
pub trait SonicSample: Clone {
fn is_f32() -> bool {
false
}
fn is_i16() -> bool {
false
}
fn zero() -> Self;
}
impl SonicSample for f32 {
fn is_f32() -> bool {
true
}
fn zero() -> Self {
0.0
}
}
impl SonicSample for i16 {
fn is_i16() -> bool {
true
}
fn zero() -> Self {
0
}
}
pub struct SonicStream {
stream: *mut raw::sonicStreamStruct,
#[allow(dead_code)]
sample_rate: usize,
channels: usize,
}
impl SonicStream {
pub fn new(sample_rate: usize, channels: usize) -> Result<Self, OperationError> {
let stream = unsafe { raw::sonicCreateStream(sample_rate as _, channels as _) };
if stream.is_null() {
return Err(OperationError::Sonic(
"Failed to create sonic stream: out of memory".to_owned(),
));
}
Ok(Self {
stream,
sample_rate,
channels,
})
}
pub fn write<S>(&mut self, samples: &[S]) -> Result<(), OperationError>
where
S: SonicSample,
{
let num_samples = samples.len() / self.channels;
let ret = unsafe {
if S::is_f32() {
raw::sonicWriteFloatToStream(self.stream, samples.as_ptr() as _, num_samples as _)
} else {
raw::sonicWriteShortToStream(self.stream, samples.as_ptr() as _, num_samples as _)
}
};
if ret == 0 {
return Err(OperationError::Sonic(
"Failed to write samples to sonic stream".to_owned(),
));
}
Ok(())
}
pub fn write_with_num<S>(
&mut self,
samples: &[S],
num_samples: usize,
) -> Result<(), OperationError>
where
S: SonicSample,
{
let ret = unsafe {
if S::is_f32() {
raw::sonicWriteFloatToStream(self.stream, samples.as_ptr() as _, num_samples as _)
} else {
raw::sonicWriteShortToStream(self.stream, samples.as_ptr() as _, num_samples as _)
}
};
if ret == 0 {
return Err(OperationError::Sonic(
"Failed to write samples to sonic stream".to_owned(),
));
}
Ok(())
}
pub fn read<S>(&mut self, max_samples: usize) -> Vec<S>
where
S: SonicSample,
{
let total_samples = max_samples * self.channels;
let mut buf = vec![S::zero(); total_samples];
let read = unsafe {
if S::is_f32() {
raw::sonicReadFloatFromStream(self.stream, buf.as_mut_ptr() as _, max_samples as _)
} else {
raw::sonicReadShortFromStream(self.stream, buf.as_mut_ptr() as _, max_samples as _)
}
};
buf.truncate(read as usize * self.channels);
buf
}
pub fn flush(&mut self) -> Result<(), OperationError> {
let ret = unsafe { raw::sonicFlushStream(self.stream) };
if ret == 0 {
return Err(OperationError::Sonic(
"Failed to flush sonic stream".to_owned(),
));
}
Ok(())
}
pub fn get_samples_available(&self) -> usize {
unsafe { raw::sonicSamplesAvailable(self.stream) as usize }
}
pub fn get_speed(&self) -> f32 {
unsafe { raw::sonicGetSpeed(self.stream) }
}
pub fn set_speed(&mut self, speed: f32) {
unsafe { raw::sonicSetSpeed(self.stream, speed) }
}
pub fn get_pitch(&self) -> f32 {
unsafe { raw::sonicGetPitch(self.stream) }
}
pub fn set_pitch(&mut self, pitch: f32) {
unsafe { raw::sonicSetPitch(self.stream, pitch) }
}
pub fn get_rate(&self) -> f32 {
unsafe { raw::sonicGetRate(self.stream) }
}
pub fn set_rate(&mut self, rate: f32) {
unsafe { raw::sonicSetRate(self.stream, rate) }
}
pub fn get_volume(&self) -> f32 {
unsafe { raw::sonicGetVolume(self.stream) }
}
pub fn set_volume(&mut self, volume: f32) {
unsafe { raw::sonicSetVolume(self.stream, volume) }
}
pub fn get_quality(&self) -> i32 {
unsafe { raw::sonicGetQuality(self.stream) }
}
pub fn set_quality(&mut self, quality: i32) {
unsafe { raw::sonicSetQuality(self.stream, quality) }
}
pub fn get_sample_rate(&self) -> usize {
unsafe { raw::sonicGetSampleRate(self.stream) as usize }
}
pub fn set_sample_rate(&mut self, sample_rate: usize) {
unsafe { raw::sonicSetSampleRate(self.stream, sample_rate as _) }
}
pub fn get_channels(&self) -> usize {
unsafe { raw::sonicGetNumChannels(self.stream) as usize }
}
pub fn set_channels(&mut self, channels: usize) {
unsafe { raw::sonicSetNumChannels(self.stream, channels as _) }
}
pub fn change_speed<S>(&mut self, samples: &[S]) -> Result<Vec<S>, OperationError>
where
S: SonicSample,
{
if samples.is_empty() {
return Ok(Default::default());
}
if self.get_speed() == 1.0 {
return Ok(samples.to_owned());
}
self.write::<S>(samples)?;
self.flush()?;
let available = self.get_samples_available();
Ok(self.read::<S>(available))
}
}
impl Drop for SonicStream {
fn drop(&mut self) {
if !self.stream.is_null() {
unsafe {
raw::sonicDestroyStream(self.stream);
}
}
}
}