mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Refactor sample conversion into separate struct
This commit is contained in:
parent
001d3ca1cf
commit
74b2fea338
5 changed files with 61 additions and 53 deletions
|
@ -38,33 +38,6 @@ pub enum AudioPacket {
|
||||||
OggData(Vec<u8>),
|
OggData(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(AsBytes, Copy, Clone, Debug)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct i24([u8; 3]);
|
|
||||||
impl i24 {
|
|
||||||
fn pcm_from_i32(sample: i32) -> Self {
|
|
||||||
// drop the least significant byte
|
|
||||||
let [a, b, c, _d] = (sample >> 8).to_le_bytes();
|
|
||||||
i24([a, b, c])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity.
|
|
||||||
macro_rules! convert_samples_to {
|
|
||||||
($type: ident, $samples: expr) => {
|
|
||||||
convert_samples_to!($type, $samples, 0)
|
|
||||||
};
|
|
||||||
($type: ident, $samples: expr, $shift: expr) => {
|
|
||||||
$samples
|
|
||||||
.iter()
|
|
||||||
.map(|sample| {
|
|
||||||
(*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $shift
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AudioPacket {
|
impl AudioPacket {
|
||||||
pub fn samples(&self) -> &[f32] {
|
pub fn samples(&self) -> &[f32] {
|
||||||
match self {
|
match self {
|
||||||
|
@ -86,23 +59,53 @@ impl AudioPacket {
|
||||||
AudioPacket::OggData(d) => d.is_empty(),
|
AudioPacket::OggData(d) => d.is_empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn f32_to_s32(samples: &[f32]) -> Vec<i32> {
|
#[derive(AsBytes, Copy, Clone, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct i24([u8; 3]);
|
||||||
|
impl i24 {
|
||||||
|
fn pcm_from_i32(sample: i32) -> Self {
|
||||||
|
// drop the least significant byte
|
||||||
|
let [a, b, c, _d] = (sample >> 8).to_le_bytes();
|
||||||
|
i24([a, b, c])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity.
|
||||||
|
macro_rules! convert_samples_to {
|
||||||
|
($type: ident, $samples: expr) => {
|
||||||
|
convert_samples_to!($type, $samples, 0)
|
||||||
|
};
|
||||||
|
($type: ident, $samples: expr, $drop_bits: expr) => {
|
||||||
|
$samples
|
||||||
|
.iter()
|
||||||
|
.map(|sample| {
|
||||||
|
(*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $drop_bits
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SamplesConverter {}
|
||||||
|
impl SamplesConverter {
|
||||||
|
pub fn to_s32(samples: &[f32]) -> Vec<i32> {
|
||||||
convert_samples_to!(i32, samples)
|
convert_samples_to!(i32, samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn f32_to_s24(samples: &[f32]) -> Vec<i32> {
|
pub fn to_s24(samples: &[f32]) -> Vec<i32> {
|
||||||
convert_samples_to!(i32, samples, 8)
|
convert_samples_to!(i32, samples, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn f32_to_s24_3(samples: &[f32]) -> Vec<i24> {
|
pub fn to_s24_3(samples: &[f32]) -> Vec<i24> {
|
||||||
Self::f32_to_s32(samples)
|
Self::to_s32(samples)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|sample| i24::pcm_from_i32(*sample))
|
.map(|sample| i24::pcm_from_i32(*sample))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn f32_to_s16(samples: &[f32]) -> Vec<i16> {
|
pub fn to_s16(samples: &[f32]) -> Vec<i16> {
|
||||||
convert_samples_to!(i16, samples)
|
convert_samples_to!(i16, samples)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,24 +24,25 @@ fn mk_sink<S: Sink + Open + 'static>(device: Option<String>, format: AudioFormat
|
||||||
macro_rules! sink_as_bytes {
|
macro_rules! sink_as_bytes {
|
||||||
() => {
|
() => {
|
||||||
fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
|
fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
|
||||||
|
use crate::audio::{i24, SamplesConverter};
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
match packet {
|
match packet {
|
||||||
AudioPacket::Samples(samples) => match self.format {
|
AudioPacket::Samples(samples) => match self.format {
|
||||||
AudioFormat::F32 => self.write_bytes(samples.as_bytes()),
|
AudioFormat::F32 => self.write_bytes(samples.as_bytes()),
|
||||||
AudioFormat::S32 => {
|
AudioFormat::S32 => {
|
||||||
let samples_s32 = AudioPacket::f32_to_s32(samples);
|
let samples_s32: &[i32] = &SamplesConverter::to_s32(samples);
|
||||||
self.write_bytes(samples_s32.as_bytes())
|
self.write_bytes(samples_s32.as_bytes())
|
||||||
}
|
}
|
||||||
AudioFormat::S24 => {
|
AudioFormat::S24 => {
|
||||||
let samples_s24 = AudioPacket::f32_to_s24(samples);
|
let samples_s24: &[i32] = &SamplesConverter::to_s24(samples);
|
||||||
self.write_bytes(samples_s24.as_bytes())
|
self.write_bytes(samples_s24.as_bytes())
|
||||||
}
|
}
|
||||||
AudioFormat::S24_3 => {
|
AudioFormat::S24_3 => {
|
||||||
let samples_s24_3 = AudioPacket::f32_to_s24_3(samples);
|
let samples_s24_3: &[i24] = &SamplesConverter::to_s24_3(samples);
|
||||||
self.write_bytes(samples_s24_3.as_bytes())
|
self.write_bytes(samples_s24_3.as_bytes())
|
||||||
}
|
}
|
||||||
AudioFormat::S16 => {
|
AudioFormat::S16 => {
|
||||||
let samples_s16 = AudioPacket::f32_to_s16(samples);
|
let samples_s16: &[i16] = &SamplesConverter::to_s16(samples);
|
||||||
self.write_bytes(samples_s16.as_bytes())
|
self.write_bytes(samples_s16.as_bytes())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{Open, Sink};
|
use super::{Open, Sink};
|
||||||
use crate::audio::AudioPacket;
|
use crate::audio::{AudioPacket, SamplesConverter};
|
||||||
use crate::config::AudioFormat;
|
use crate::config::AudioFormat;
|
||||||
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};
|
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};
|
||||||
use portaudio_rs;
|
use portaudio_rs;
|
||||||
|
@ -146,18 +146,19 @@ impl<'a> Sink for PortAudioSink<'a> {
|
||||||
$stream.as_mut().unwrap().write($samples)
|
$stream.as_mut().unwrap().write($samples)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let samples = packet.samples();
|
||||||
let result = match self {
|
let result = match self {
|
||||||
Self::F32(stream, _parameters) => {
|
Self::F32(stream, _parameters) => {
|
||||||
let samples = packet.samples();
|
write_sink!(stream, samples)
|
||||||
write_sink!(stream, &samples)
|
|
||||||
}
|
}
|
||||||
Self::S32(stream, _parameters) => {
|
Self::S32(stream, _parameters) => {
|
||||||
let samples_s32: Vec<i32> = AudioPacket::f32_to_s32(packet.samples());
|
let samples_s32: &[i32] = &SamplesConverter::to_s32(samples);
|
||||||
write_sink!(stream, &samples_s32)
|
write_sink!(stream, samples_s32)
|
||||||
}
|
}
|
||||||
Self::S16(stream, _parameters) => {
|
Self::S16(stream, _parameters) => {
|
||||||
let samples_s16: Vec<i16> = AudioPacket::f32_to_s16(packet.samples());
|
let samples_s16: &[i16] = &SamplesConverter::to_s16(samples);
|
||||||
write_sink!(stream, &samples_s16)
|
write_sink!(stream, samples_s16)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match result {
|
match result {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use super::{Open, Sink};
|
use super::{Open, Sink};
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
extern crate rodio;
|
extern crate rodio;
|
||||||
use crate::audio::AudioPacket;
|
use crate::audio::{AudioPacket, SamplesConverter};
|
||||||
use crate::config::AudioFormat;
|
use crate::config::AudioFormat;
|
||||||
|
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};
|
||||||
use cpal::traits::{DeviceTrait, HostTrait};
|
use cpal::traits::{DeviceTrait, HostTrait};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::{io, thread, time};
|
use std::{io, thread, time};
|
||||||
|
@ -25,12 +26,12 @@ macro_rules! rodio_sink {
|
||||||
let samples = packet.samples();
|
let samples = packet.samples();
|
||||||
match self.format {
|
match self.format {
|
||||||
AudioFormat::F32 => {
|
AudioFormat::F32 => {
|
||||||
let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples);
|
let source = rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples);
|
||||||
self.rodio_sink.append(source)
|
self.rodio_sink.append(source)
|
||||||
},
|
},
|
||||||
AudioFormat::S16 => {
|
AudioFormat::S16 => {
|
||||||
let samples_s16: Vec<i16> = AudioPacket::f32_to_s16(samples);
|
let samples_s16: &[i16] = &SamplesConverter::to_s16(samples);
|
||||||
let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples_s16);
|
let source = rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples_s16);
|
||||||
self.rodio_sink.append(source)
|
self.rodio_sink.append(source)
|
||||||
},
|
},
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{Open, Sink};
|
use super::{Open, Sink};
|
||||||
use crate::audio::AudioPacket;
|
use crate::audio::{AudioPacket, SamplesConverter};
|
||||||
use crate::config::AudioFormat;
|
use crate::config::AudioFormat;
|
||||||
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};
|
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};
|
||||||
use sdl2::audio::{AudioQueue, AudioSpecDesired};
|
use sdl2::audio::{AudioQueue, AudioSpecDesired};
|
||||||
|
@ -89,20 +89,22 @@ impl Sink for SdlSink {
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let samples = packet.samples();
|
||||||
match self {
|
match self {
|
||||||
Self::F32(queue) => {
|
Self::F32(queue) => {
|
||||||
drain_sink!(queue, mem::size_of::<f32>());
|
drain_sink!(queue, mem::size_of::<f32>());
|
||||||
queue.queue(packet.samples())
|
queue.queue(samples)
|
||||||
}
|
}
|
||||||
Self::S32(queue) => {
|
Self::S32(queue) => {
|
||||||
|
let samples_s32: &[i32] = &SamplesConverter::to_s32(samples);
|
||||||
drain_sink!(queue, mem::size_of::<i32>());
|
drain_sink!(queue, mem::size_of::<i32>());
|
||||||
let samples_s32: Vec<i32> = AudioPacket::f32_to_s32(packet.samples());
|
queue.queue(samples_s32)
|
||||||
queue.queue(&samples_s32)
|
|
||||||
}
|
}
|
||||||
Self::S16(queue) => {
|
Self::S16(queue) => {
|
||||||
|
let samples_s16: &[i16] = &SamplesConverter::to_s16(samples);
|
||||||
drain_sink!(queue, mem::size_of::<i16>());
|
drain_sink!(queue, mem::size_of::<i16>());
|
||||||
let samples_s16: Vec<i16> = AudioPacket::f32_to_s16(packet.samples());
|
queue.queue(samples_s16)
|
||||||
queue.queue(&samples_s16)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue