Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to play a WAV file with Rust SDL2?

I am trying to play a WAV file using rust-sdl2.

I found AudioSpecWAV, but none of the audio initialisation methods seem to take it as a type, and it doesn't implement AudioCallback. I tried implementing this myself with my own callback looking something like:

struct MyWav {
    wav: AudioSpecWAV,
    volume: f32,
    pos: usize,
}

impl AudioCallback for MyWav {
    type Channel = f32;

    fn callback(&mut self, out: &mut [f32]) {
        for x in out.iter_mut() {
            *x = match self.wav.buffer().get(self.pos) {
                Some(v) => { self.pos += 1; v as f32 },
                None => { 0.0 }
            }
        }
    }
}

... but I don't know how to work around the following error I get:

the traitcore::marker::Sync is not implemented for the type *mut u8

This seems to be the audio_buf field of AudioSpecWAV, but if that's not Sync how am I supposed to pass a buffer to the callback?

(for reference, here is an example of playing a generated sound)

like image 225
Xavier Shay Avatar asked Oct 30 '22 17:10

Xavier Shay


1 Answers

AudioCallback requires implementers to be Send. You could do that by wrapping AudioSpecWAV in a struct and doing an unsafe impl for Send on that struct, or you could copy the data. Since you shouldn't typically use unsafe unless you know what you are doing is actually safe, you may want to look at the copy approach.

Here is an example of both approaches:

extern crate sdl2;

use std::thread::{self};

use sdl2::{Sdl};
use sdl2::audio::{self, AudioSpecDesired, AudioSpecWAV, AudioCallback, AudioDevice};

//----------------------------------------------------------------------------//

struct CopiedData {
    bytes: Vec<u8>,
    position: usize
}

impl AudioCallback for CopiedData {
    type Channel = u8;

    fn callback(&mut self, data: &mut [u8]) {
        let (start, end) = (self.position, self.position + data.len());
        self.position += data.len();

        let audio_data = &self.bytes[start..end];
        for (src, dst) in audio_data.iter().zip(data.iter_mut()) {
            *dst = *src;
        }
    }
}

//----------------------------------------------------------------------------//

struct WrappedData {
    audio: AudioSpecWAV,
    position: usize
}

impl AudioCallback for WrappedData {
    type Channel = u8;

    fn callback(&mut self, data: &mut [u8]) {
        let (start, end) = (self.position, self.position + data.len());
        self.position += data.len();

        let audio_data = &self.audio.buffer()[start..end];
        for (src, dst) in audio_data.iter().zip(data.iter_mut()) {
            *dst = *src;
        }
    }
}

unsafe impl Send for WrappedData { }

//----------------------------------------------------------------------------//

pub fn main() {
    let sdl_context = sdl2::init().unwrap();
    let audio_system = sdl_context.audio().unwrap();

    let audio_spec = AudioSpecDesired{ freq: None, channels: None, samples: None };
    let audio_wav = AudioSpecWAV::load_wav("test.wav").unwrap();

    let copied_data = CopiedData{ bytes: audio_wav.buffer().to_vec(), position: 0 };
    //let wrapped_data = WrappedData{ audio: audio_wav, position: 0 };

    let audio_device = audio_system.open_playback(None, audio_spec, move |spec| {
        copied_data
    }).unwrap();

    audio_device.resume();

    thread::sleep_ms(5000);
}

Note: The WAV I was playing was quite loud (to the point where it sounded distorted) and I am not a sound guy so I am not sure if that had something to do with my code or the WAV file I was using in general.

like image 156
Andrew Miller Avatar answered Jan 03 '23 00:01

Andrew Miller