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:
1 parent
dd03bcd09e
commit
dc9c7d0424
2 files changed
+11
-11
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user