extendr: Compilation error when running from R

Hi! I am trying to reproduce audio using the cpal crate from R. I added the play function and suddenly I am getting compiler errors when running from R. When I run cargo test play_test it works well. Do you know what’s going on here?

Error:

─  DONE (audior)
Error in dyn.load(library_path) : 
  unable to load shared object '/mnt/wd/projects/audior/src/audior.so':
  /mnt/wd/projects/audior/src/audior.so: undefined symbol: snd_pcm_hw_params_get_period_size_min
Error in `value[[3L]]()`:
! Failed to generate wrapper functions.
✖ error in callr subprocess

lib.rs:

mod play_audio;

/// @export
#[extendr]
pub fn play(r_arr: RMatrix<f64>, sr: i32) {
    let arr: ArrayView2<f64> = ArrayView2::from_robj(&r_arr).expect("cannot convert Robj to ArrayView2");
    play_audio::play(&arr, sr as u32)
}

extendr_module! {
    mod audior;
    fn load;
    //fn stream;
    fn to_mono;
    fn get_duration;
    fn get_samplerate;
    fn resample;
    fn fft;
    fn realfft;
    fn get_window;
    fn play;
}

play_audio.rs:

use ndarray::ArrayView2;
use cpal::{Sample, SampleFormat, SupportedStreamConfig, SampleRate, BufferSize, StreamConfig};
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};

pub fn play(arr: &ArrayView2<f64>, sr: u32) {
    let channels = arr.nrows();
    let samples = arr.ncols();
    
    // convert to interleaved
    let mut data_interleaved = Vec::with_capacity(channels * samples);
    for i in 0..samples {
        for ch in 0..channels {
            data_interleaved.push(arr[[ch, i]] as f32);
        }
    }
    
    let host = cpal::default_host();
    let device = host.default_output_device().expect("no output device available");
    let config = StreamConfig {
        channels: channels as u16,
        sample_rate: SampleRate(sr), // Audio device default sample rate is set to 192000
        buffer_size: BufferSize::Default,
    };
    
    let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);

    let mut data_interleaved_iter = data_interleaved.into_iter();
    let mut next_value = move || {
        data_interleaved_iter.next().expect("cannot get next iter value")
    };

    let stream = device.build_output_stream(
        &config,
        move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
            write_data(data, channels, &mut next_value)
        },
        err_fn
        ).unwrap();

    fn write_data<T: Sample>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32) {
        for frame in output.chunks_mut(channels) {
            for sample in frame.iter_mut() {
                let value: T = Sample::from(&next_sample());
                *sample = value
            }
        }
    }

    stream.play().unwrap();
    std::thread::sleep(std::time::Duration::from_millis(5000));
}

#[cfg(test)]
mod test_play {
    use extendr_api::NA_REAL;

    use crate::decode_symphonia;

    use super::*;
    use std::path::Path;
    
    #[test]
    fn test_play() {
        let fname = "../../test_files/mono.wav";
        let path = Path::new(fname);
        let filetype = Path::extension(path)
            .expect("couldn't extract the file extension")
            .to_str()
            .expect("cannot convert from &OsStr to &str");
        let decoded_arr = decode_symphonia::load(path, false, 0., NA_REAL, filetype);
        let sr = decode_symphonia::get_samplerate(path, filetype);

        play(&decoded_arr.view(), sr);
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 19 (14 by maintainers)

Most upvoted comments

Same, I jumped off my chair because it went over the audio of a film I was watching. Now trying to run it directly from R.

copying the full test straight into a function and export that to R does work

I’m just speculating, but if this problem happens at runtime, you may need to make sure that /mnt/wd/projects/audior/src/ is in LD_LIBRARY_PATH