1
0
Fork 0
mirror of https://github.com/librespot-org/librespot.git synced 2025-10-06 03:50:06 +02:00

Add support for S24 and S24_3 output formats

This commit is contained in:
Roderick van Domburg 2021-03-17 00:00:27 +01:00
parent 9dcaeee6d4
commit 770ea15498
14 changed files with 155 additions and 80 deletions

View file

@ -1,4 +1,4 @@
use super::{Open, Sink};
use super::{Open, Sink, SinkAsBytes};
use crate::audio::AudioPacket;
use crate::config::AudioFormat;
use crate::player::{NUM_CHANNELS, SAMPLES_PER_SECOND, SAMPLE_RATE};
@ -7,8 +7,8 @@ use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
use alsa::{Direction, Error, ValueOr};
use std::cmp::min;
use std::ffi::CString;
use std::io;
use std::process::exit;
use std::{io, mem};
const BUFFERED_LATENCY: f32 = 0.125; // seconds
const BUFFERED_PERIODS: Frames = 4;
@ -17,7 +17,7 @@ pub struct AlsaSink {
pcm: Option<PCM>,
format: AudioFormat,
device: String,
buffer: Vec<f32>,
buffer: Vec<u8>,
}
fn list_outputs() {
@ -39,16 +39,18 @@ fn list_outputs() {
fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box<Error>> {
let pcm = PCM::new(dev_name, Direction::Playback, false)?;
let (alsa_format, sample_size) = match format {
AudioFormat::F32 => (Format::float(), mem::size_of::<f32>()),
AudioFormat::S32 => (Format::s32(), mem::size_of::<i32>()),
AudioFormat::S16 => (Format::s16(), mem::size_of::<i16>()),
let alsa_format = match format {
AudioFormat::F32 => Format::float(),
AudioFormat::S32 => Format::s32(),
AudioFormat::S24 => Format::s24(),
AudioFormat::S24_3 => Format::S243LE,
AudioFormat::S16 => Format::s16(),
};
// http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
// latency = period_size * periods / (rate * bytes_per_frame)
// For stereo samples encoded as 32-bit float, one frame has a length of eight bytes.
let mut period_size = ((SAMPLES_PER_SECOND * sample_size as u32) as f32
let mut period_size = ((SAMPLES_PER_SECOND * format.size() as u32) as f32
* (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as Frames;
// Set hardware parameters: 44100 Hz / stereo / 32-bit float or 16-bit signed integer
@ -85,7 +87,7 @@ impl Open for AlsaSink {
}
.to_string();
AlsaSink {
Self {
pcm: None,
format: format,
device: name,
@ -102,7 +104,9 @@ impl Sink for AlsaSink {
Ok((p, period_size)) => {
self.pcm = Some(p);
// Create a buffer for all samples for a full period
self.buffer = Vec::with_capacity((period_size * BUFFERED_PERIODS) as usize);
self.buffer = Vec::with_capacity(
period_size as usize * BUFFERED_PERIODS as usize * self.format.size(),
);
}
Err(e) => {
error!("Alsa error PCM open {}", e);
@ -121,7 +125,7 @@ impl Sink for AlsaSink {
{
// Write any leftover data in the period buffer
// before draining the actual buffer
self.write_buf().expect("could not flush buffer");
self.write_bytes(&[]).expect("could not flush buffer");
let pcm = self.pcm.as_mut().unwrap();
pcm.drain().unwrap();
}
@ -129,9 +133,12 @@ impl Sink for AlsaSink {
Ok(())
}
fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
sink_as_bytes!();
}
impl SinkAsBytes for AlsaSink {
fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> {
let mut processed_data = 0;
let data = packet.samples();
while processed_data < data.len() {
let data_to_buffer = min(
self.buffer.capacity() - self.buffer.len(),
@ -153,23 +160,8 @@ impl Sink for AlsaSink {
impl AlsaSink {
fn write_buf(&mut self) -> io::Result<()> {
let pcm = self.pcm.as_mut().unwrap();
let io_result = match self.format {
AudioFormat::F32 => {
let io = pcm.io_f32().unwrap();
io.writei(&self.buffer)
}
AudioFormat::S32 => {
let io = pcm.io_i32().unwrap();
let buf_s32: Vec<i32> = AudioPacket::f32_to_s32(&self.buffer);
io.writei(&buf_s32[..])
}
AudioFormat::S16 => {
let io = pcm.io_i16().unwrap();
let buf_s16: Vec<i16> = AudioPacket::f32_to_s16(&self.buffer);
io.writei(&buf_s16[..])
}
};
match io_result {
let io = pcm.io_bytes();
match io.writei(&self.buffer) {
Ok(_) => (),
Err(err) => pcm.try_recover(err, false).unwrap(),
};