Fix format compatibility: use S32_LE instead of FloatLE

PROBLEM: Cambridge Audio DAC only supports S32_LE format, not FloatLE.
Previous code failed with 'Invalid argument (22)' when trying to set FloatLE.

SOLUTION:
- alsa_direct.rs: Changed format from FloatLE to S32_LE
- alsa_direct.rs: Changed write() to accept i32 samples instead of f32
- playback_engine.rs: Convert i16 to i32 using bit-perfect left shift:
  i32 = (i16 as i32) << 16

This maintains full bit-perfect precision while using hardware-supported format.
This commit is contained in:
vicrodh committed 2026-01-19 03:35:43 -06:00
1 parent dd03bcd09e
commit dc9c7d0424
2 files changed
+11 -11

No files matched your search

+5 -5
View File
@@ -43,8 +43,8 @@ impl AlsaDirectStream {
hwp.set_access(Access::RWInterleaved)
.map_err(|e| format!("Failed to set access: {}", e))?;
// Set format (Float 32-bit little endian - rodio's default)
hwp.set_format(Format::FloatLE)
// Set format (Signed 32-bit little endian - most DACs support this)
hwp.set_format(Format::S32LE)
.map_err(|e| format!("Failed to set format: {}", e))?;
// Set channels
@@ -94,16 +94,16 @@ impl AlsaDirectStream {
})
}
/// Write audio samples to ALSA
/// Write audio samples to ALSA (S32_LE format)
#[cfg(target_os = "linux")]
pub fn write(&self, samples: &[f32]) -> Result<(), String> {
pub fn write(&self, samples: &[i32]) -> Result<(), String> {
let mut pcm = self.pcm.lock().unwrap();
// Convert samples to frames (samples / channels)
let frames = samples.len() / self.channels as usize;
// Write to PCM
let io = pcm.io_f32()
let io = pcm.io_i32()
.map_err(|e| format!("Failed to get PCM I/O: {}", e))?;
match io.writei(samples) {
+6 -6
View File
@@ -90,7 +90,7 @@ impl PlaybackEngine {
let chunk_samples = CHUNK_SIZE * channels as usize;
let mut buffer_i16 = Vec::with_capacity(chunk_samples);
let mut buffer_f32 = Vec::with_capacity(chunk_samples);
let mut buffer_i32 = Vec::with_capacity(chunk_samples);
let mut total_frames: u64 = 0;
let mut source_iter = source.into_iter();
@@ -117,20 +117,20 @@ impl PlaybackEngine {
break;
}
// Convert i16 to f32 (normalize to -1.0..1.0)
buffer_f32.clear();
// Convert i16 to i32 (bit-perfect: shift left 16 bits to use full 32-bit range)
buffer_i32.clear();
for &sample in &buffer_i16 {
buffer_f32.push(sample as f32 / 32768.0);
buffer_i32.push((sample as i32) << 16);
}
// Write to ALSA
if let Err(e) = stream_clone.write(&buffer_f32) {
if let Err(e) = stream_clone.write(&buffer_i32) {
log::error!("[ALSA Direct Engine] Write failed: {}", e);
break;
}
// Update position
let frames_written = buffer_f32.len() / channels as usize;
let frames_written = buffer_i32.len() / channels as usize;
total_frames += frames_written as u64;
position_clone.store(total_frames, Ordering::SeqCst);
duration_clone.store(total_frames, Ordering::SeqCst);