use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use core::ffi::c_void;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use super::super::audio_sample::AudioSample;
use super::super::midi::track_note::TrackNote;
use super::super::signals::synth_signal::SynthSignal;
use super::super::volume::Volume;
use super::sound_source::SoundSource;
use crate::capi_state::CApiState;
use crate::ctypes::*;
use crate::ctypes_enums::SoundWaveform;
use crate::error::Error;
use crate::time::{TimeDelta, TimeSpan, TimeTicks};
#[derive(Debug)]
pub struct Synth {
source: ManuallyDrop<SoundSource>,
ptr: NonNull<CSynth>,
frequency_modulator: Option<SynthSignal>,
amplitude_modulator: Option<SynthSignal>,
parameter_modulators: BTreeMap<i32, SynthSignal>,
sample: Option<AudioSample>, }
impl Synth {
fn new() -> Self {
let ptr = unsafe { Self::fns().newSynth.unwrap()() };
Synth {
source: ManuallyDrop::new(SoundSource::from_ptr(ptr as *mut CSoundSource)),
ptr: NonNull::new(ptr).unwrap(),
frequency_modulator: None,
amplitude_modulator: None,
parameter_modulators: BTreeMap::new(),
sample: None,
}
}
pub fn new_with_waveform(waveform: SoundWaveform) -> Self {
let mut synth = Self::new();
unsafe { Self::fns().setWaveform.unwrap()(synth.cptr_mut(), waveform) };
synth
}
pub fn new_with_sample(sample: AudioSample, sustain_region: Option<TimeSpan>) -> Synth {
let mut synth = Self::new();
unsafe {
Self::fns().setSample.unwrap()(
synth.cptr_mut(),
sample.cptr() as *mut _,
sustain_region.as_ref().map_or(0, |r| r.start.to_sample_frames()),
sustain_region.as_ref().map_or(0, |r| r.end.to_sample_frames()),
)
};
synth.sample = Some(sample);
synth
}
pub fn new_with_generator(generator: SynthGenerator) -> Self {
let mut synth = Self::new();
unsafe {
Self::fns().setGenerator.unwrap()(
synth.cptr_mut(),
c_render_func as *mut Option<CRenderFunc>,
c_note_on_func as *mut Option<CNoteOnFunc>,
c_release_func as *mut Option<CReleaseFunc>,
c_set_parameter_func as *mut Option<CSetParameterFunc>,
c_dealloc_func as *mut Option<CDeallocFunc>,
Box::into_raw(Box::new(generator)) as *mut c_void,
)
};
synth
}
pub fn set_attack_time(&mut self, attack_time: TimeDelta) {
unsafe { Self::fns().setAttackTime.unwrap()(self.cptr_mut(), attack_time.to_seconds()) }
}
pub fn set_decay_time(&mut self, decay_time: TimeDelta) {
unsafe { Self::fns().setDecayTime.unwrap()(self.cptr_mut(), decay_time.to_seconds()) }
}
pub fn set_sustain_level(&mut self, level: f32) {
unsafe { Self::fns().setSustainLevel.unwrap()(self.cptr_mut(), level) }
}
pub fn set_release_time(&mut self, release_time: TimeDelta) {
unsafe { Self::fns().setReleaseTime.unwrap()(self.cptr_mut(), release_time.to_seconds()) }
}
pub fn set_transpose(&mut self, half_steps: f32) {
unsafe { Self::fns().setTranspose.unwrap()(self.cptr_mut(), half_steps) }
}
pub fn set_frequency_modulator<T: AsRef<SynthSignal>>(&mut self, signal: Option<&T>) {
let modulator_ptr = signal.map_or_else(core::ptr::null_mut, |signal|
signal.as_ref().cptr() as *mut _);
unsafe { Self::fns().setFrequencyModulator.unwrap()(self.cptr_mut(), modulator_ptr) }
self.frequency_modulator = signal.map(|signal| signal.as_ref().clone());
}
pub fn frequency_modulator(&mut self) -> Option<&SynthSignal> {
self.frequency_modulator.as_ref()
}
pub fn set_amplitude_modulator<T: AsRef<SynthSignal>>(&mut self, signal: Option<&T>) {
let modulator_ptr = signal.map_or_else(core::ptr::null_mut, |signal|
signal.as_ref().cptr() as *mut _);
unsafe { Self::fns().setAmplitudeModulator.unwrap()(self.cptr_mut(), modulator_ptr) }
self.amplitude_modulator = signal.map(|signal| signal.as_ref().clone());
}
pub fn amplitude_modulator(&mut self) -> Option<&SynthSignal> {
self.amplitude_modulator.as_ref()
}
pub fn set_parameter_modulator<T: AsRef<SynthSignal>>(&mut self, i: i32, signal: Option<&T>) {
let modulator_ptr = signal.map_or_else(core::ptr::null_mut, |signal|
signal.as_ref().cptr() as *mut _);
unsafe { Self::fns().setParameterModulator.unwrap()(self.cptr_mut(), i, modulator_ptr) }
match signal.map(|signal| signal.as_ref().clone()) {
Some(signal) => self.parameter_modulators.insert(i, signal),
None => self.parameter_modulators.remove(&i),
};
}
pub fn parameter_modulator(&mut self, i: i32) -> Option<&SynthSignal> {
self.parameter_modulators.get(&i)
}
pub fn parameter_count(&self) -> i32 {
unsafe { Self::fns().getParameterCount.unwrap()(self.cptr() as *mut _) }
}
pub fn set_parameter(&mut self, i: i32, value: f32) -> Result<(), Error> {
let r = unsafe { Self::fns().setParameter.unwrap()(self.cptr_mut(), i, value) };
match r {
0 => Err(Error::NotFoundError),
_ => Ok(()),
}
}
pub fn play_frequency_note(
&mut self,
frequency: f32,
volume: Volume,
length: Option<TimeDelta>,
when: Option<TimeTicks>,
) {
unsafe {
Self::fns().playNote.unwrap()(
self.cptr_mut(),
frequency,
volume.into(),
length.map_or(-1.0, |l| l.to_seconds()),
when.map_or(0, |w| w.to_sample_frames()),
)
}
}
pub fn play_midi_note(
&mut self,
note: TrackNote,
length: Option<TimeDelta>,
when: Option<TimeTicks>,
) {
unsafe {
Self::fns().playMIDINote.unwrap()(
self.cptr_mut(),
note.midi_note as f32,
note.velocity.into(),
length.map_or(-1.0, |l| l.to_seconds()),
when.map_or(0, |w| w.to_sample_frames()),
)
}
}
pub fn stop(&mut self, when: Option<TimeTicks>) {
unsafe {
Self::fns().noteOff.unwrap()(self.cptr_mut(), when.map_or(0, |w| w.to_sample_frames()))
}
}
pub(crate) fn cptr(&self) -> *const CSynth {
self.ptr.as_ptr()
}
pub(crate) fn cptr_mut(&mut self) -> *mut CSynth {
self.ptr.as_ptr()
}
fn fns() -> &'static craydate_sys::playdate_sound_synth {
unsafe { &*CApiState::get().csound.synth }
}
}
impl Drop for Synth {
fn drop(&mut self) {
unsafe { ManuallyDrop::drop(&mut self.source) };
unsafe { Self::fns().freeSynth.unwrap()(self.cptr_mut()) };
}
}
impl AsRef<SoundSource> for Synth {
fn as_ref(&self) -> &SoundSource {
&self.source
}
}
impl AsMut<SoundSource> for Synth {
fn as_mut(&mut self) -> &mut SoundSource {
&mut self.source
}
}
#[allow(dead_code)]
pub struct SynthRender<'a> {
left: &'a mut [i32],
right: &'a mut [i32],
rate: u32,
drate: i32,
l: i32,
dl: i32,
r: i32,
dr: i32,
}
pub struct SynthGeneratorVTable {
pub render_func: fn(userdata: *const (), SynthRender<'_>) -> bool,
pub note_on_func: fn(userdata: *const (), note: f32, volume: f32, length: Option<TimeTicks>),
pub release_func: fn(userdata: *const (), ended: bool),
pub set_parameter_func: fn(userdata: *const (), parameter: u8, value: f32) -> bool,
}
pub struct SynthGenerator {
data: *const (),
vtable: &'static SynthGeneratorVTable,
}
impl SynthGenerator {
pub fn new<T: Send + Sync>(data: T, vtable: &'static SynthGeneratorVTable) -> Self {
SynthGenerator {
data: Box::into_raw(Box::new(data)) as *const (),
vtable,
}
}
}
impl Drop for SynthGenerator {
fn drop(&mut self) {
drop(unsafe { Box::from_raw(self.data as *mut ()) });
}
}
impl core::fmt::Debug for SynthGenerator {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SynthGenerator").field("data", &self.data).finish()
}
}
type CRenderFunc =
unsafe extern "C" fn(*mut c_void, *mut i32, *mut i32, i32, u32, i32, i32, i32, i32, i32) -> i32;
unsafe extern "C" fn c_render_func(
generator: *mut c_void,
left: *mut i32,
right: *mut i32,
nsamples: i32,
rate: u32,
drate: i32,
l: i32,
dl: i32,
r: i32,
dr: i32,
) -> i32 {
let generator = generator as *const SynthGenerator;
let func = (*generator).vtable.render_func;
let userdata = (*generator).data;
func(
userdata,
SynthRender {
left: alloc::slice::from_raw_parts_mut(left, nsamples as usize),
right: alloc::slice::from_raw_parts_mut(right, nsamples as usize),
rate,
drate,
l,
dl,
r,
dr,
},
) as i32
}
type CNoteOnFunc = unsafe extern "C" fn(*mut c_void, f32, f32, f32);
unsafe extern "C" fn c_note_on_func(generator: *mut c_void, note: f32, volume: f32, length: f32) {
let generator = generator as *const SynthGenerator;
let func = (*generator).vtable.note_on_func;
let userdata = (*generator).data;
let length = if length == -1.0 {
None
} else {
Some(TimeTicks::from_seconds_lossy(length))
};
func(userdata, note, volume, length)
}
type CReleaseFunc = unsafe extern "C" fn(*mut c_void, i32);
unsafe extern "C" fn c_release_func(generator: *mut c_void, ended: i32) {
let generator = generator as *const SynthGenerator;
let func = (*generator).vtable.release_func;
let userdata = (*generator).data;
func(userdata, ended != 0)
}
type CSetParameterFunc = unsafe extern "C" fn(*mut c_void, u8, f32) -> i32;
unsafe extern "C" fn c_set_parameter_func(
generator: *mut c_void,
parameter: u8,
value: f32,
) -> i32 {
let generator = generator as *const SynthGenerator;
let func = (*generator).vtable.set_parameter_func;
let userdata = (*generator).data;
func(userdata, parameter, value) as i32
}
type CDeallocFunc = unsafe extern "C" fn(*mut c_void);
unsafe extern "C" fn c_dealloc_func(generator: *mut c_void) {
drop(Box::from_raw(generator as *mut SynthGenerator))
}