extern crate alloc;
use crate::fft::{ComplexFft, FftSize, RealFft};
use cmsis_dsp_sys_pregenerated::{arm_cfft_instance_f32, arm_rfft_fast_instance_f32};
use core::slice;
use core::{ffi::CStr, mem::MaybeUninit};
use num::FromPrimitive;
#[cfg(target_arch = "arm")]
use cfg_if::cfg_if;
use crate::{ffi::program_vector as ffi, volts_per_octave::VoltsPerSample};
use ffi::ProgramVector as FfiProgramVector;
mod audio;
use audio::AudioFormat;
pub use audio::{AudioBuffers, AudioSettings};
mod parameters;
pub use parameters::Parameters;
mod messages;
use messages::Messages;
pub use messages::{debug_message, error};
mod midi;
pub use midi::Midi;
mod screen;
pub use screen::Screen;
mod meta;
pub use meta::*;
mod resources;
pub use resources::Resources;
mod service_call;
use service_call::{ServiceCall, SystemFunction};
const CONFIGURATION_ERROR_STATUS: i8 = ffi::CONFIGURATION_ERROR_STATUS as i8;
const AUDIO_FORMAT_24B16: u8 = ffi::AUDIO_FORMAT_24B16 as u8;
const AUDIO_FORMAT_24B32: u8 = ffi::AUDIO_FORMAT_24B32 as u8;
const AUDIO_FORMAT_FORMAT_MASK: u8 = ffi::AUDIO_FORMAT_FORMAT_MASK as u8;
const AUDIO_FORMAT_CHANNEL_MASK: u8 = ffi::AUDIO_FORMAT_CHANNEL_MASK as u8;
pub struct ProgramVector {
pub meta: Meta,
pub audio: AudioBuffers,
pub parameters: Parameters,
service_call: ServiceCall,
}
#[doc(hidden)]
#[cfg_attr(target_arch = "arm", link_section = ".pv")]
pub static mut PROGRAM_VECTOR: core::mem::MaybeUninit<FfiProgramVector> =
core::mem::MaybeUninit::uninit();
impl ProgramVector {
#[doc(hidden)]
pub unsafe fn new(
pv: &'static mut FfiProgramVector,
patch_name: *const core::ffi::c_char,
) -> Self {
Messages::init(&mut pv.message, &mut pv.error, pv.programStatus);
let checksum = ProgramVectorChecksum::from_u8(pv.checksum)
.expect("Program Vector checksum error - is your firmware up to date?");
let meta = Meta::new(
&pv.cycles_per_block,
&mut pv.heap_bytes_used,
checksum,
pv.hardware_version,
pv.heapLocations,
pv.registerPatch,
);
#[cfg(all(feature = "talc", target_arch = "arm"))]
{
let mut talc = talc_heap::ALLOCATOR.lock();
cfg_if! {
if #[cfg(feature = "stack_hack")] {
extern "C" {
static mut _stack: u8;
}
meta.memory_segments().iter().for_each(|seg|
if seg.location != &raw mut _stack {
let _ = unsafe {talc.claim(seg.into())};
}
);
} else {
meta.memory_segments().iter().for_each(|seg| {
let _ = unsafe {talc.claim(seg.into())};
});
}
}
}
meta.register_patch(CStr::from_ptr(patch_name));
let (format, channels) = AudioFormat::parse(pv.audio_format);
let audio_settings = AudioSettings {
sample_rate: pv.audio_samplingrate as usize,
blocksize: pv.audio_blocksize as usize,
channels,
format,
};
let parameters = Parameters::new(
unsafe { slice::from_raw_parts(pv.parameters, pv.parameters_size as usize) },
&pv.buttons,
pv.registerPatchParameter,
pv.setPatchParameter,
pv.setButton,
&mut pv.buttonChangedCallback,
);
let audio = AudioBuffers::new(
&pv.audio_input,
&pv.audio_output,
audio_settings,
pv.programReady,
);
let service_call = ServiceCall::new(pv.serviceCall, pv.hardware_version);
#[cfg(feature = "fastmaths")]
{
use service_call::SystemTable;
if let Ok(table) = service_call.get_array(SystemTable::SystemTablePow) {
crate::fastmaths::set_pow_table(table);
}
if let Ok(table) = service_call.get_array(SystemTable::SystemTableLog) {
crate::fastmaths::set_log_table(table);
}
}
Self {
parameters,
meta,
audio,
service_call,
}
}
pub fn midi(&mut self) -> Midi {
Midi::init(&mut self.service_call)
}
pub fn screen(&mut self) -> Screen<'_> {
Screen::new(&self.service_call)
}
pub fn volts_per_sample(&mut self) -> (VoltsPerSample, VoltsPerSample) {
let parameters = self.service_call.device_parameters();
(
VoltsPerSample::new(parameters.input_scalar, parameters.input_offset),
VoltsPerSample::new(parameters.output_scalar, parameters.output_offset),
)
}
pub fn resources(&self) -> Resources<'_> {
Resources::new(&self.service_call)
}
pub fn fft_real(&self, size: FftSize) -> Result<RealFft, &str> {
let mut instance = MaybeUninit::<arm_rfft_fast_instance_f32>::zeroed();
self.service_call
.init_rfft(instance.as_mut_ptr(), size as usize)?;
if unsafe { instance.assume_init_ref().fftLenRFFT } as usize != size as usize {
Err("rfft instance was not initialised")
} else {
Ok(unsafe { RealFft::new(instance.assume_init()) })
}
}
pub fn fft_complex(&self, size: FftSize) -> Result<ComplexFft, &str> {
let mut instance = MaybeUninit::<arm_cfft_instance_f32>::zeroed();
self.service_call
.init_cfft(instance.as_mut_ptr(), size as usize)?;
if unsafe { instance.assume_init_ref().fftLen } as usize != size as usize {
Err("rfft instance was not initialised")
} else {
Ok(unsafe { ComplexFft::new(instance.assume_init()) })
}
}
}
#[cfg(all(feature = "talc", target_arch = "arm"))]
mod talc_heap {
use core::sync::atomic::AtomicBool;
use core::sync::atomic::Ordering;
use lock_api::GuardNoSend;
use talc::*;
use crate::ffi::program_vector::MemorySegment;
impl From<&MemorySegment> for talc::Span {
fn from(segment: &MemorySegment) -> talc::Span {
talc::Span::from_base_size(segment.location, segment.size as usize)
}
}
pub struct CriticalSectionRawMutex {
restore: AtomicBool
}
unsafe impl lock_api::RawMutex for CriticalSectionRawMutex {
const INIT: Self = Self{restore: AtomicBool::new(false)};
type GuardMarker = GuardNoSend;
fn lock(&self) {
self.restore.store( cortex_m::register::primask::read().is_active(), Ordering::Relaxed);
cortex_m::interrupt::disable();
}
fn try_lock(&self) -> bool {
self.lock();
true
}
unsafe fn unlock(&self) {
if self.restore.load(Ordering::Relaxed) {
cortex_m::interrupt::enable();
}
}
}
#[global_allocator]
pub static ALLOCATOR: Talck<CriticalSectionRawMutex, ErrOnOom> = Talc::new(ErrOnOom).lock();
pub fn heap_bytes_used() -> usize {
ALLOCATOR.lock().get_counters().allocated_bytes as usize
}
}
#[cfg(all(feature = "talc", target_arch = "arm"))]
pub use talc_heap::heap_bytes_used;
#[cfg(all(feature = "talc", not(target_arch = "arm")))]
#[doc(hidden)]
pub fn heap_bytes_used() -> usize {
0
}