lumon-macrodata-refine / third_party/quad-snd/src/coreaudio_snd.rs

raw file 4034 bytes
use crate::PlaySoundParams;

use std::sync::mpsc;

pub use crate::mixer::Playback;

#[path = "coreaudio/coreaudio.rs"]
mod coreaudio;

// bindgen lost some defines from coreaudio.rs
const _saudio_kAudioFormatLinearPCM: u32 = 1819304813; //'lpcm';
const _saudio_kLinearPCMFormatFlagIsFloat: u32 = 1 << 0;
const _saudio_kAudioFormatFlagIsPacked: u32 = 1 << 3;

use coreaudio::*;

mod consts {
    pub const DEVICE: &'static str = "default\0";
    pub const RATE: u32 = 44100;
    pub const CHANNELS: u32 = 2;
    pub const BUFFER_FRAMES: u32 = 4096;
}

pub struct AudioContext {
    pub(crate) mixer_ctrl: crate::mixer::MixerControl,
}

unsafe extern "C" fn saudio_coreaudio_callback(
    user_data: *mut ::std::os::raw::c_void,
    queue: _saudio_AudioQueueRef,
    buffer: _saudio_AudioQueueBufferRef,
) {
    let mut mixer = &mut *(user_data as *mut crate::mixer::Mixer);

    let num_frames = (*buffer).mAudioDataByteSize / (2 * 4);
    let buf =
        std::slice::from_raw_parts_mut((*buffer).mAudioData as *mut f32, num_frames as usize * 2);

    mixer.fill_audio_buffer(buf, num_frames as _);

    AudioQueueEnqueueBuffer(queue, buffer, 0, std::ptr::null_mut());
}

impl AudioContext {
    pub fn new() -> AudioContext {
        use crate::mixer::{self, Mixer};

        let (mixer_builder, mixer_ctrl) = Mixer::new();
        let mixer = Box::new(mixer_builder.build());

        unsafe {
            let fmt = _saudio_AudioStreamBasicDescription {
                mSampleRate: consts::RATE as f64,
                mFormatID: _saudio_kAudioFormatLinearPCM,
                mFormatFlags: _saudio_kLinearPCMFormatFlagIsFloat
                    | _saudio_kAudioFormatFlagIsPacked,
                mFramesPerPacket: 1,
                mChannelsPerFrame: consts::CHANNELS,
                mBytesPerFrame: 4 * consts::CHANNELS,
                mBytesPerPacket: 4 * consts::CHANNELS,
                mBitsPerChannel: 32,
                mReserved: 0,
            };
            let mut ca_audio_queue: _saudio_AudioQueueRef = std::mem::zeroed();
            let res = AudioQueueNewOutput(
                &fmt,
                Some(saudio_coreaudio_callback),
                Box::into_raw(mixer) as *mut _,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                0,
                &mut ca_audio_queue,
            );
            assert!(res == 0);
            assert!(ca_audio_queue.is_null() == false);

            // create 2 audio buffers
            for _ in 0..2 {
                let mut buf: _saudio_AudioQueueBufferRef = std::ptr::null_mut();
                let buf_byte_size = consts::BUFFER_FRAMES * fmt.mBytesPerFrame;
                let res = AudioQueueAllocateBuffer(ca_audio_queue, buf_byte_size, &mut buf);
                assert!(res == 0);
                assert!(buf.is_null() == false);
                (*buf).mAudioDataByteSize = buf_byte_size;
                std::ptr::write_bytes(
                    (*buf).mAudioData as *mut u8,
                    0,
                    (*buf).mAudioDataByteSize as usize,
                );
                AudioQueueEnqueueBuffer(ca_audio_queue, buf, 0, std::ptr::null_mut());
            }

            let res = AudioQueueStart(ca_audio_queue, std::ptr::null_mut());
            assert!(res == 0);
        }

        AudioContext { mixer_ctrl }
    }
}

pub struct Sound {
    sound_id: u32,
}

impl Sound {
    pub fn load(ctx: &AudioContext, data: &[u8]) -> Sound {
        let sound_id = ctx.mixer_ctrl.load(data);

        Sound { sound_id }
    }

    pub fn play(&self, ctx: &AudioContext, params: PlaySoundParams) -> Playback {
        ctx.mixer_ctrl.play(self.sound_id, params)
    }

    pub fn stop(&self, ctx: &AudioContext) {
        ctx.mixer_ctrl.stop_all(self.sound_id);
    }

    pub fn set_volume(&self, ctx: &AudioContext, volume: f32) {
        ctx.mixer_ctrl.set_volume_all(self.sound_id, volume);
    }

    pub fn delete(&self, ctx: &AudioContext) {
        ctx.mixer_ctrl.delete(self.sound_id);
    }
}