1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use std::{convert::TryInto};

use crate::db_internal::{audio_alloc, audio_allocCompressed, audio_free, audio_getUsage, audio_queueSetParam_i, audio_queueSetParam_f, audio_queueStartVoice, audio_queueStopVoice, audio_getVoiceState, audio_getTime, audio_setReverbParams, audio_initSynth, audio_playMidi, audio_setMidiReverb, audio_setMidiVolume};

pub const VOICE_COUNT: usize = 32;

#[repr(C)]
#[derive(Clone)]
pub struct AudioSample {
    pub handle: i32,
    pub samplerate: i32,
}

#[repr(C)]
#[derive(Clone, Copy)]
pub enum AudioVoiceParam {
    Volume,
    Pitch,
    Detune,
    Pan,
    SampleData,
    Samplerate,
    LoopEnabled,
    LoopStart,
    LoopEnd,
    Reverb,
    FadeInDuration,
    FadeOutDuration,
    Start,
    Stop,
}

impl AudioSample {
    /// Create a new signed 8-bit PCM audio sample
    pub fn create_s8(pcm_data: &[i8], samplerate: i32) -> Result<AudioSample,()> {
        let handle = unsafe { audio_alloc(pcm_data.as_ptr().cast(), pcm_data.len().try_into().unwrap(), 0) };
        if handle == -1 {
            return Err(());
        }

        return Ok(AudioSample {
            handle: handle,
            samplerate: samplerate
        });
    }

    /// Create a new signed 16-bit PCM audio sample
    pub fn create_s16(pcm_data: &[i16], samplerate: i32) -> Result<AudioSample,()> {
        let handle = unsafe { audio_alloc(pcm_data.as_ptr().cast(), (pcm_data.len() * 2).try_into().unwrap(), 1) };
        if handle == -1 {
            return Err(());
        }

        return Ok(AudioSample {
            handle: handle,
            samplerate: samplerate
        });
    }

    /// Create a new IMA ADPCM encoded audio sample
    pub fn create_adpcm(adpcm_data: &[u8], chunk_size: i32, samplerate: i32) -> Result<AudioSample,()> {
        let handle = unsafe { audio_allocCompressed(adpcm_data.as_ptr().cast(), adpcm_data.len().try_into().unwrap(), chunk_size) };
        if handle == -1 {
            return Err(());
        }

        return Ok(AudioSample {
            handle: handle,
            samplerate: samplerate
        });
    }
}

impl Drop for AudioSample {
    fn drop(&mut self) {
        unsafe { audio_free(self.handle); }
    }
}

/// Get the current sample memory usage in bytes
pub fn get_usage() -> i32 {
    unsafe { return audio_getUsage(); }
}

/// Schedule an audio voice integer parameter change at some point in the future
pub fn queue_set_voice_param_i(slot: i32, param: AudioVoiceParam, value: i32, time: f64) {
    assert!(slot >= 0 && slot < VOICE_COUNT.try_into().unwrap(), "Tried to set parameter for invalid voice handle");
    unsafe { audio_queueSetParam_i(slot, param, value, time); }
}

/// Schedule an audio voice float parameter change at some point in the future
pub fn queue_set_voice_param_f(slot: i32, param: AudioVoiceParam, value: f32, time: f64) {
    assert!(slot >= 0 && slot < VOICE_COUNT.try_into().unwrap(), "Tried to set parameter for invalid voice handle");
    unsafe { audio_queueSetParam_f(slot, param, value, time); }
}

/// Schedule an audio voice to start playing at some point in the future
pub fn queue_start_voice(slot: i32, time: f64) {
    assert!(slot >= 0 && slot < VOICE_COUNT.try_into().unwrap(), "Tried to start invalid voice handle");
    unsafe { audio_queueStartVoice(slot, time) };
}

/// Schedule an audio voice to stop playing at some point in the future
pub fn queue_stop_voice(slot: i32, time: f64) {
    assert!(slot >= 0 && slot < VOICE_COUNT.try_into().unwrap(), "Tried to stop invalid voice handle");
    unsafe { audio_queueStopVoice(slot, time) };
}

/// Gets whether the given voice is currently playing
pub fn get_voice_state(slot: i32) -> bool {
    assert!(slot >= 0 && slot < VOICE_COUNT.try_into().unwrap(), "Tried to get state of invalid voice handle");
    unsafe { return audio_getVoiceState(slot) };
}

/// Get the current audio timer value for scheduling
pub fn get_time() -> f64 {
    unsafe { return audio_getTime(); }
}

/// Set the current reverb unit parameters
pub fn set_reverb(room_size: f32, damping: f32, width: f32, wet: f32, dry: f32) {
    unsafe { audio_setReverbParams(room_size, damping, width, wet, dry); }
}

/// Initialize the MIDI synth using the given soundfont data
pub fn init_synth(sf2_data: &[u8]) -> Result<(),()> {
    unsafe {
        let result = audio_initSynth(sf2_data.as_ptr(), sf2_data.len().try_into().unwrap());
        if result {
            return Ok(());
        } else {
            return Err(());
        }
    }
}

/// Start playing a MIDI file using the initialized MIDI synth, optionally looping the file when it reaches the end
pub fn play_midi(midi_data: &[u8], looping: bool) -> Result<(),()> {
    unsafe {
        let result = audio_playMidi(midi_data.as_ptr(), midi_data.len().try_into().unwrap(), looping);
        if result {
            return Ok(());
        } else {
            return Err(());
        }
    }
}

/// Set whether to route MIDI playback through the reverb unit
pub fn set_midi_reverb(enabled: bool) {
    unsafe { audio_setMidiReverb(enabled); }
}

/// Set the volume of MIDI playback
pub fn set_midi_volume(volume: f32) {
    unsafe { audio_setMidiVolume(volume); }
}