use std::cell::UnsafeCell;
use std::ffi::{c_char, c_void};
use std::marker::PhantomData;
use std::slice;
use log::warn;
use vst3::{Class, ComRef, Steinberg::Vst::*, Steinberg::*};
use beamer_core::{
AuxiliaryBuffers, Buffer, BusInfo as CoreBusInfo, BusLayout,
BusType as CoreBusType, CachedBusConfig, CachedBusInfo, ChordInfo, ConversionBuffers,
Descriptor, FactoryPresets, FrameRate as CoreFrameRate, HasParameters, MidiBuffer, MidiCcState,
MidiEvent, MidiEventKind, NoPresets, NoteExpressionInt, NoteExpressionText,
NoteExpressionValue as CoreNoteExpressionValue, ParameterStore, Config, PluginSetup,
ProcessBufferStorage, ProcessContext as CoreProcessContext, Processor, ScaleInfo, SysEx,
SysExOutputPool, Transport, MAX_BUSES, MAX_CHANNELS, MAX_CHORD_NAME_SIZE,
MAX_EXPRESSION_TEXT_SIZE, MAX_SCALE_NAME_SIZE, MAX_SYSEX_SIZE,
};
use crate::factory::ComponentFactory;
use crate::util::{copy_wstring, len_wstring};
use crate::wrapper::Vst3Config;
const K_NOTE_ON_EVENT: u16 = 0;
const K_NOTE_OFF_EVENT: u16 = 1;
const K_DATA_EVENT: u16 = 2;
const K_POLY_PRESSURE_EVENT: u16 = 3;
const K_NOTE_EXPRESSION_VALUE_EVENT: u16 = 4;
const K_NOTE_EXPRESSION_TEXT_EVENT: u16 = 5;
const K_CHORD_EVENT: u16 = 6;
const K_SCALE_EVENT: u16 = 7;
const K_NOTE_EXPRESSION_INT_VALUE_EVENT: u16 = 8;
const K_LEGACY_MIDI_CC_OUT_EVENT: u16 = 65535;
const LEGACY_CC_CHANNEL_PRESSURE: u8 = 128;
const LEGACY_CC_PITCH_BEND: u8 = 129;
const LEGACY_CC_PROGRAM_CHANGE: u8 = 130;
const DATA_TYPE_MIDI_SYSEX: u32 = 0;
const PROGRAM_CHANGE_PARAM_ID: u32 = 0x20000000;
const FACTORY_PRESETS_LIST_ID: i32 = 0;
macro_rules! valid_if {
($state:expr, $flag:expr, $value:expr) => {
if $state & $flag != 0 {
Some($value)
} else {
None
}
};
}
unsafe fn extract_transport(context_ptr: *const ProcessContext) -> Transport {
if context_ptr.is_null() {
return Transport::default();
}
let context = &*context_ptr;
let state = context.state;
const K_PLAYING: u32 = 1 << 1;
const K_CYCLE_ACTIVE: u32 = 1 << 2;
const K_RECORDING: u32 = 1 << 3;
const K_SYSTEM_TIME_VALID: u32 = 1 << 8;
const K_PROJECT_TIME_MUSIC_VALID: u32 = 1 << 9;
const K_TEMPO_VALID: u32 = 1 << 10;
const K_BAR_POSITION_VALID: u32 = 1 << 11;
const K_CYCLE_VALID: u32 = 1 << 12;
const K_TIME_SIG_VALID: u32 = 1 << 13;
const K_SMPTE_VALID: u32 = 1 << 14;
const K_CLOCK_VALID: u32 = 1 << 15;
const K_CONT_TIME_VALID: u32 = 1 << 17;
Transport {
tempo: valid_if!(state, K_TEMPO_VALID, context.tempo),
time_sig_numerator: valid_if!(state, K_TIME_SIG_VALID, context.timeSigNumerator),
time_sig_denominator: valid_if!(state, K_TIME_SIG_VALID, context.timeSigDenominator),
project_time_samples: Some(context.projectTimeSamples),
project_time_beats: valid_if!(state, K_PROJECT_TIME_MUSIC_VALID, context.projectTimeMusic),
bar_position_beats: valid_if!(state, K_BAR_POSITION_VALID, context.barPositionMusic),
cycle_start_beats: valid_if!(state, K_CYCLE_VALID, context.cycleStartMusic),
cycle_end_beats: valid_if!(state, K_CYCLE_VALID, context.cycleEndMusic),
is_playing: state & K_PLAYING != 0,
is_recording: state & K_RECORDING != 0,
is_cycle_active: state & K_CYCLE_ACTIVE != 0,
system_time_ns: valid_if!(state, K_SYSTEM_TIME_VALID, context.systemTime),
continuous_time_samples: valid_if!(state, K_CONT_TIME_VALID, context.continousTimeSamples), samples_to_next_clock: valid_if!(state, K_CLOCK_VALID, context.samplesToNextClock),
smpte_offset_subframes: valid_if!(state, K_SMPTE_VALID, context.smpteOffsetSubframes),
frame_rate: if state & K_SMPTE_VALID != 0 {
let is_drop = context.frameRate.flags & 1 != 0;
CoreFrameRate::from_raw(context.frameRate.framesPerSecond, is_drop)
} else {
None
},
}
}
fn validate_speaker_arrangement(arrangement: SpeakerArrangement) -> Result<(), String> {
let channel_count = arrangement.count_ones() as usize;
if channel_count > MAX_CHANNELS {
return Err(format!(
"Speaker arrangement has {} channels, but MAX_CHANNELS is {}",
channel_count, MAX_CHANNELS
));
}
Ok(())
}
fn build_setup<S: PluginSetup>(setup: &ProcessSetup, bus_layout: &BusLayout) -> S {
use beamer_core::{HostSetup, ProcessMode};
let process_mode = match setup.processMode {
1 => ProcessMode::Offline, 2 => ProcessMode::Prefetch, _ => ProcessMode::Realtime, };
let host_setup = HostSetup::new(
setup.sampleRate,
setup.maxSamplesPerBlock as usize,
bus_layout.clone(),
process_mode,
);
S::extract(&host_setup)
}
enum PluginState<P: Descriptor> {
Unprepared {
plugin: P,
pending_state: Option<Vec<u8>>,
},
Prepared {
processor: P::Processor,
input_buses: Vec<CoreBusInfo>,
output_buses: Vec<CoreBusInfo>,
},
}
pub struct Vst3Processor<P, Presets = NoPresets<<P as HasParameters>::Parameters>>
where
P: Descriptor,
Presets: FactoryPresets<Parameters = <P as HasParameters>::Parameters>,
{
state: UnsafeCell<PluginState<P>>,
vst3_config: &'static Vst3Config,
sample_rate: UnsafeCell<f64>,
max_block_size: UnsafeCell<usize>,
symbolic_sample_size: UnsafeCell<i32>,
midi_input: UnsafeCell<MidiBuffer>,
midi_output: UnsafeCell<MidiBuffer>,
sysex_output_pool: UnsafeCell<SysExOutputPool>,
conversion_buffers: UnsafeCell<ConversionBuffers>,
buffer_storage_f32: UnsafeCell<ProcessBufferStorage<f32>>,
buffer_storage_f64: UnsafeCell<ProcessBufferStorage<f64>>,
midi_cc_state: Option<MidiCcState>,
current_preset_index: UnsafeCell<i32>,
component_handler: UnsafeCell<*mut IComponentHandler>,
_marker: PhantomData<(P, Presets)>,
}
unsafe impl<P: Descriptor, Presets> Send for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
}
unsafe impl<P: Descriptor, Presets> Sync for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
}
impl<P: Descriptor + 'static, Presets> Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
pub fn new(_config: &'static Config, vst3_config: &'static Vst3Config) -> Self {
let plugin = P::default();
let midi_cc_state = plugin.midi_cc_config().map(|cfg| MidiCcState::from_config(&cfg));
Self {
state: UnsafeCell::new(PluginState::Unprepared {
plugin,
pending_state: None,
}),
vst3_config,
sample_rate: UnsafeCell::new(44100.0),
max_block_size: UnsafeCell::new(1024),
symbolic_sample_size: UnsafeCell::new(SymbolicSampleSizes_::kSample32 as i32),
midi_input: UnsafeCell::new(MidiBuffer::new()),
midi_output: UnsafeCell::new(MidiBuffer::new()),
sysex_output_pool: UnsafeCell::new(SysExOutputPool::with_capacity(
vst3_config.sysex_slots,
vst3_config.sysex_buffer_size,
)),
conversion_buffers: UnsafeCell::new(ConversionBuffers::new()),
buffer_storage_f32: UnsafeCell::new(ProcessBufferStorage::new()),
buffer_storage_f64: UnsafeCell::new(ProcessBufferStorage::new()),
midi_cc_state,
current_preset_index: UnsafeCell::new(0), component_handler: UnsafeCell::new(std::ptr::null_mut()),
_marker: PhantomData,
}
}
#[inline]
#[allow(dead_code)] unsafe fn processor(&self) -> &P::Processor {
match &*self.state.get() {
PluginState::Prepared { processor, .. } => processor,
PluginState::Unprepared { .. } => {
panic!("Attempted to access processor before setupProcessing()")
}
}
}
#[inline]
#[allow(clippy::mut_from_ref)]
unsafe fn processor_mut(&self) -> &mut P::Processor {
match &mut *self.state.get() {
PluginState::Prepared { processor, .. } => processor,
PluginState::Unprepared { .. } => {
panic!("Attempted to access processor before setupProcessing()")
}
}
}
#[inline]
#[allow(dead_code)] unsafe fn unprepared_plugin(&self) -> &P {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin,
PluginState::Prepared { .. } => {
panic!("Attempted to access unprepared plugin after setupProcessing()")
}
}
}
#[inline]
#[allow(dead_code)] #[allow(clippy::mut_from_ref)]
unsafe fn unprepared_plugin_mut(&self) -> &mut P {
match &mut *self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin,
PluginState::Prepared { .. } => {
panic!("Attempted to access unprepared plugin after setupProcessing()")
}
}
}
#[inline]
#[allow(dead_code)] unsafe fn is_prepared(&self) -> bool {
matches!(&*self.state.get(), PluginState::Prepared { .. })
}
#[inline]
unsafe fn try_plugin(&self) -> Option<&P> {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => Some(plugin),
PluginState::Prepared { .. } => None,
}
}
#[inline]
#[allow(clippy::mut_from_ref)]
unsafe fn try_plugin_mut(&self) -> Option<&mut P> {
match &mut *self.state.get() {
PluginState::Unprepared { plugin, .. } => Some(plugin),
PluginState::Prepared { .. } => None,
}
}
#[inline]
unsafe fn input_bus_count(&self) -> usize {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin.input_bus_count(),
PluginState::Prepared { input_buses, .. } => input_buses.len(),
}
}
#[inline]
unsafe fn output_bus_count(&self) -> usize {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin.output_bus_count(),
PluginState::Prepared { output_buses, .. } => output_buses.len(),
}
}
#[inline]
unsafe fn core_input_bus_info(&self, index: usize) -> Option<CoreBusInfo> {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin.input_bus_info(index),
PluginState::Prepared { input_buses, .. } => input_buses.get(index).cloned(),
}
}
#[inline]
unsafe fn core_output_bus_info(&self, index: usize) -> Option<CoreBusInfo> {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin.output_bus_info(index),
PluginState::Prepared { output_buses, .. } => output_buses.get(index).cloned(),
}
}
#[inline]
unsafe fn parameters(&self) -> &P::Parameters {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin.parameters(),
PluginState::Prepared { processor, .. } => {
&*(processor.parameters() as *const _)
}
}
}
#[inline]
#[allow(dead_code)] #[allow(clippy::mut_from_ref)]
unsafe fn parameters_mut(&self) -> &mut P::Parameters {
match &mut *self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin.parameters_mut(),
PluginState::Prepared { processor, .. } => {
&mut *(processor.parameters_mut() as *mut _)
}
}
}
#[inline]
unsafe fn wants_midi(&self) -> bool {
match &*self.state.get() {
PluginState::Unprepared { plugin, .. } => plugin.wants_midi(),
PluginState::Prepared { processor, .. } => processor.wants_midi(),
}
}
#[inline]
unsafe fn latency_samples(&self) -> u32 {
match &*self.state.get() {
PluginState::Unprepared { .. } => 0,
PluginState::Prepared { processor, .. } => processor.latency_samples(),
}
}
#[inline]
#[allow(dead_code)] unsafe fn tail_samples(&self) -> u32 {
match &*self.state.get() {
PluginState::Unprepared { .. } => 0,
PluginState::Prepared { processor, .. } => processor.tail_samples(),
}
}
#[inline]
#[allow(dead_code)] unsafe fn supports_double_precision(&self) -> bool {
match &*self.state.get() {
PluginState::Unprepared { .. } => false,
PluginState::Prepared { processor, .. } => processor.supports_double_precision(),
}
}
#[inline]
unsafe fn process_audio_f32(
&self,
process_data: &ProcessData,
num_samples: usize,
processor: &mut P::Processor,
context: &CoreProcessContext,
) {
let storage = &mut *self.buffer_storage_f32.get();
storage.clear();
if process_data.numInputs > 0 && !process_data.inputs.is_null() {
let bus = &*process_data.inputs;
let num_channels = bus.numChannels as usize;
let max_channels = storage.main_inputs.capacity();
if num_channels > 0 && !bus.__field0.channelBuffers32.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers32, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.main_inputs.push(ptr);
}
}
}
}
if process_data.numOutputs > 0 && !process_data.outputs.is_null() {
let bus = &*process_data.outputs;
let num_channels = bus.numChannels as usize;
let max_channels = storage.main_outputs.capacity();
if num_channels > 0 && !bus.__field0.channelBuffers32.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers32, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.main_outputs.push(ptr);
}
}
}
}
if process_data.numInputs > 1 && !process_data.inputs.is_null() {
let input_buses =
slice::from_raw_parts(process_data.inputs, process_data.numInputs as usize);
for (aux_idx, bus) in input_buses[1..].iter().enumerate() {
if aux_idx < storage.aux_inputs.len() {
let num_channels = bus.numChannels as usize;
let max_channels = storage.aux_inputs[aux_idx].capacity();
if num_channels > 0 && !bus.__field0.channelBuffers32.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers32, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.aux_inputs[aux_idx].push(ptr);
}
}
}
}
}
}
if process_data.numOutputs > 1 && !process_data.outputs.is_null() {
let output_buses =
slice::from_raw_parts(process_data.outputs, process_data.numOutputs as usize);
for (aux_idx, bus) in output_buses[1..].iter().enumerate() {
if aux_idx < storage.aux_outputs.len() {
let num_channels = bus.numChannels as usize;
let max_channels = storage.aux_outputs[aux_idx].capacity();
if num_channels > 0 && !bus.__field0.channelBuffers32.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers32, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.aux_outputs[aux_idx].push(ptr);
}
}
}
}
}
}
let main_in_iter = storage
.main_inputs
.iter()
.map(|&ptr| slice::from_raw_parts(ptr, num_samples));
let main_out_iter = storage
.main_outputs
.iter()
.map(|&ptr| slice::from_raw_parts_mut(ptr, num_samples));
let aux_in_iter = storage.aux_inputs.iter().map(|bus| {
bus.iter()
.map(|&ptr| slice::from_raw_parts(ptr, num_samples))
});
let aux_out_iter = storage.aux_outputs.iter().map(|bus| {
bus.iter()
.map(|&ptr| slice::from_raw_parts_mut(ptr, num_samples))
});
let mut buffer = Buffer::new(main_in_iter, main_out_iter, num_samples);
let mut aux = AuxiliaryBuffers::new(aux_in_iter, aux_out_iter, num_samples);
processor.process(&mut buffer, &mut aux, context);
}
#[inline]
unsafe fn process_audio_f64_native(
&self,
process_data: &ProcessData,
num_samples: usize,
processor: &mut P::Processor,
context: &CoreProcessContext,
) {
let storage = &mut *self.buffer_storage_f64.get();
storage.clear();
if process_data.numInputs > 0 && !process_data.inputs.is_null() {
let bus = &*process_data.inputs;
let num_channels = bus.numChannels as usize;
let max_channels = storage.main_inputs.capacity();
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.main_inputs.push(ptr);
}
}
}
}
if process_data.numOutputs > 0 && !process_data.outputs.is_null() {
let bus = &*process_data.outputs;
let num_channels = bus.numChannels as usize;
let max_channels = storage.main_outputs.capacity();
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.main_outputs.push(ptr);
}
}
}
}
if process_data.numInputs > 1 && !process_data.inputs.is_null() {
let input_buses =
slice::from_raw_parts(process_data.inputs, process_data.numInputs as usize);
for (aux_idx, bus) in input_buses[1..].iter().enumerate() {
if aux_idx < storage.aux_inputs.len() {
let num_channels = bus.numChannels as usize;
let max_channels = storage.aux_inputs[aux_idx].capacity();
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.aux_inputs[aux_idx].push(ptr);
}
}
}
}
}
}
if process_data.numOutputs > 1 && !process_data.outputs.is_null() {
let output_buses =
slice::from_raw_parts(process_data.outputs, process_data.numOutputs as usize);
for (aux_idx, bus) in output_buses[1..].iter().enumerate() {
if aux_idx < storage.aux_outputs.len() {
let num_channels = bus.numChannels as usize;
let max_channels = storage.aux_outputs[aux_idx].capacity();
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs =
slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for &ptr in channel_ptrs.iter().take(max_channels) {
if !ptr.is_null() {
storage.aux_outputs[aux_idx].push(ptr);
}
}
}
}
}
}
let main_in_iter = storage
.main_inputs
.iter()
.map(|&ptr| slice::from_raw_parts(ptr, num_samples));
let main_out_iter = storage
.main_outputs
.iter()
.map(|&ptr| slice::from_raw_parts_mut(ptr, num_samples));
let aux_in_iter = storage.aux_inputs.iter().map(|bus| {
bus.iter()
.map(|&ptr| slice::from_raw_parts(ptr, num_samples))
});
let aux_out_iter = storage.aux_outputs.iter().map(|bus| {
bus.iter()
.map(|&ptr| slice::from_raw_parts_mut(ptr, num_samples))
});
let mut buffer: Buffer<f64> = Buffer::new(main_in_iter, main_out_iter, num_samples);
let mut aux: AuxiliaryBuffers<f64> =
AuxiliaryBuffers::new(aux_in_iter, aux_out_iter, num_samples);
processor.process_f64(&mut buffer, &mut aux, context);
}
#[inline]
unsafe fn process_audio_f64_converted(
&self,
process_data: &ProcessData,
num_samples: usize,
processor: &mut P::Processor,
context: &CoreProcessContext,
) {
let conv = &mut *self.conversion_buffers.get();
if process_data.numInputs > 0 && !process_data.inputs.is_null() {
let input_buses = slice::from_raw_parts(process_data.inputs, 1);
let bus = &input_buses[0];
let num_channels = (bus.numChannels as usize).min(conv.main_input_f32.len());
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs = slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for (ch, &ptr) in channel_ptrs.iter().enumerate() {
if !ptr.is_null() && ch < conv.main_input_f32.len() {
let src = slice::from_raw_parts(ptr, num_samples);
for (i, &s) in src.iter().enumerate() {
conv.main_input_f32[ch][i] = s as f32;
}
}
}
}
}
for (bus_idx, aux_bus) in conv.aux_input_f32.iter_mut().enumerate() {
let vst_bus_idx = bus_idx + 1; if process_data.numInputs as usize > vst_bus_idx && !process_data.inputs.is_null() {
let input_buses = slice::from_raw_parts(
process_data.inputs,
process_data.numInputs as usize,
);
let bus = &input_buses[vst_bus_idx];
let num_channels = (bus.numChannels as usize).min(aux_bus.len());
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs = slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for (ch, &ptr) in channel_ptrs.iter().enumerate() {
if !ptr.is_null() && ch < aux_bus.len() {
let src = slice::from_raw_parts(ptr, num_samples);
for (i, &s) in src.iter().enumerate() {
aux_bus[ch][i] = s as f32;
}
}
}
}
}
}
let main_input_iter = conv.main_input_f32
.iter()
.map(|v| &v[..num_samples]);
let main_output_iter = conv.main_output_f32
.iter_mut()
.map(|v| &mut v[..num_samples]);
let aux_input_iter = conv.aux_input_f32
.iter()
.map(|bus| bus.iter().map(|v| &v[..num_samples]));
let aux_output_iter = conv.aux_output_f32
.iter_mut()
.map(|bus| bus.iter_mut().map(|v| &mut v[..num_samples]));
let mut buffer = Buffer::new(main_input_iter, main_output_iter, num_samples);
let mut aux = AuxiliaryBuffers::new(aux_input_iter, aux_output_iter, num_samples);
processor.process(&mut buffer, &mut aux, context);
if process_data.numOutputs > 0 && !process_data.outputs.is_null() {
let output_buses = slice::from_raw_parts(process_data.outputs, 1);
let bus = &output_buses[0];
let num_channels = (bus.numChannels as usize).min(conv.main_output_f32.len());
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs = slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for (ch, &ptr) in channel_ptrs.iter().enumerate() {
if !ptr.is_null() && ch < conv.main_output_f32.len() {
let dst = slice::from_raw_parts_mut(ptr, num_samples);
for (i, sample) in conv.main_output_f32[ch][..num_samples].iter().enumerate() {
dst[i] = *sample as f64;
}
}
}
}
}
for (bus_idx, aux_bus) in conv.aux_output_f32.iter().enumerate() {
let vst_bus_idx = bus_idx + 1;
if process_data.numOutputs as usize > vst_bus_idx && !process_data.outputs.is_null() {
let output_buses = slice::from_raw_parts(
process_data.outputs,
process_data.numOutputs as usize,
);
let bus = &output_buses[vst_bus_idx];
let num_channels = (bus.numChannels as usize).min(aux_bus.len());
if num_channels > 0 && !bus.__field0.channelBuffers64.is_null() {
let channel_ptrs = slice::from_raw_parts(bus.__field0.channelBuffers64, num_channels);
for (ch, &ptr) in channel_ptrs.iter().enumerate() {
if !ptr.is_null() && ch < aux_bus.len() {
let dst = slice::from_raw_parts_mut(ptr, num_samples);
for (i, sample) in aux_bus[ch][..num_samples].iter().enumerate() {
dst[i] = *sample as f64;
}
}
}
}
}
}
}
}
impl<P: Descriptor + 'static, Presets> ComponentFactory for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
fn create(config: &'static Config, vst3_config: &'static Vst3Config) -> Self {
Self::new(config, vst3_config)
}
}
impl<P: Descriptor + 'static, Presets> Class for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
type Interfaces = (
IComponent,
IAudioProcessor,
IProcessContextRequirements,
IEditController,
IUnitInfo,
IMidiMapping,
IMidiLearn,
IMidiMapping2,
IMidiLearn2,
INoteExpressionController,
IKeyswitchController,
INoteExpressionPhysicalUIMapping,
IVst3WrapperMPESupport,
);
}
impl<P: Descriptor + 'static, Presets> IPluginBaseTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn initialize(&self, _context: *mut FUnknown) -> tresult {
kResultOk
}
unsafe fn terminate(&self) -> tresult {
kResultOk
}
}
impl<P: Descriptor + 'static, Presets> IComponentTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getControllerClassId(&self, class_id: *mut TUID) -> tresult {
if class_id.is_null() {
return kInvalidArgument;
}
if let Some(controller) = self.vst3_config.controller_uid {
*class_id = controller;
kResultOk
} else {
kNotImplemented
}
}
unsafe fn setIoMode(&self, _mode: IoMode) -> tresult {
kResultOk
}
unsafe fn getBusCount(&self, media_type: MediaType, dir: BusDirection) -> i32 {
match media_type as MediaTypes {
MediaTypes_::kAudio => match dir as BusDirections {
BusDirections_::kInput => self.input_bus_count() as i32,
BusDirections_::kOutput => self.output_bus_count() as i32,
_ => 0,
},
MediaTypes_::kEvent => {
if self.wants_midi() {
1
} else {
0
}
}
_ => 0,
}
}
unsafe fn getBusInfo(
&self,
media_type: MediaType,
dir: BusDirection,
index: i32,
bus: *mut BusInfo,
) -> tresult {
if bus.is_null() {
return kInvalidArgument;
}
match media_type as MediaTypes {
MediaTypes_::kAudio => {
let info = match dir as BusDirections {
BusDirections_::kInput => self.core_input_bus_info(index as usize),
BusDirections_::kOutput => self.core_output_bus_info(index as usize),
_ => None,
};
if let Some(info) = info {
let bus = &mut *bus;
bus.mediaType = MediaTypes_::kAudio as MediaType;
bus.direction = dir;
bus.channelCount = info.channel_count as i32;
copy_wstring(info.name, &mut bus.name);
bus.busType = match info.bus_type {
CoreBusType::Main => BusTypes_::kMain,
CoreBusType::Aux => BusTypes_::kAux,
} as BusType;
bus.flags = if info.is_default_active {
BusInfo_::BusFlags_::kDefaultActive
} else {
0
};
kResultOk
} else {
kInvalidArgument
}
}
MediaTypes_::kEvent => {
if index != 0 || !self.wants_midi() {
return kInvalidArgument;
}
let bus = &mut *bus;
bus.mediaType = MediaTypes_::kEvent as MediaType;
bus.direction = dir;
bus.channelCount = 1; let name = match dir as BusDirections {
BusDirections_::kInput => "MIDI In",
BusDirections_::kOutput => "MIDI Out",
_ => "MIDI",
};
copy_wstring(name, &mut bus.name);
bus.busType = BusTypes_::kMain as BusType;
bus.flags = BusInfo_::BusFlags_::kDefaultActive;
kResultOk
}
_ => kInvalidArgument,
}
}
unsafe fn getRoutingInfo(
&self,
_in_info: *mut RoutingInfo,
_out_info: *mut RoutingInfo,
) -> tresult {
kNotImplemented
}
unsafe fn activateBus(
&self,
_media_type: MediaType,
_dir: BusDirection,
_index: i32,
_state: TBool,
) -> tresult {
kResultOk
}
unsafe fn setActive(&self, state: TBool) -> tresult {
if let PluginState::Prepared { processor, .. } = &mut *self.state.get() {
processor.set_active(state != 0);
}
kResultOk
}
unsafe fn setState(&self, state: *mut IBStream) -> tresult {
if state.is_null() {
return kInvalidArgument;
}
let stream = match ComRef::from_raw(state) {
Some(s) => s,
None => return kInvalidArgument,
};
let mut buffer = Vec::new();
let mut chunk = [0u8; 4096];
loop {
let mut bytes_read: i32 = 0;
let result = stream.read(
chunk.as_mut_ptr() as *mut c_void,
chunk.len() as i32,
&mut bytes_read,
);
if result != kResultOk || bytes_read <= 0 {
break;
}
buffer.extend_from_slice(&chunk[..bytes_read as usize]);
}
if buffer.is_empty() {
return kResultOk;
}
match &mut *self.state.get() {
PluginState::Unprepared { pending_state, .. } => {
*pending_state = Some(buffer);
kResultOk
}
PluginState::Prepared { processor, .. } => {
match processor.load_state(&buffer) {
Ok(()) => {
use beamer_core::parameter_types::Parameters;
let sample_rate = *self.sample_rate.get();
if sample_rate > 0.0 {
processor.parameters_mut().set_sample_rate(sample_rate);
}
processor.parameters_mut().reset_smoothing();
kResultOk
}
Err(_) => kResultFalse,
}
}
}
}
unsafe fn getState(&self, state: *mut IBStream) -> tresult {
if state.is_null() {
return kInvalidArgument;
}
let data: Vec<u8> = match &*self.state.get() {
PluginState::Unprepared { .. } => {
return kResultOk;
}
PluginState::Prepared { processor, .. } => {
match processor.save_state() {
Ok(d) => d,
Err(_) => return kResultFalse,
}
}
};
if data.is_empty() {
return kResultOk;
}
let stream = match ComRef::from_raw(state) {
Some(s) => s,
None => return kInvalidArgument,
};
let mut bytes_written: i32 = 0;
let result = stream.write(
data.as_ptr() as *mut c_void,
data.len() as i32,
&mut bytes_written,
);
if result == kResultOk && bytes_written == data.len() as i32 {
kResultOk
} else {
kResultFalse
}
}
}
impl<P: Descriptor + 'static, Presets> IAudioProcessorTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn setBusArrangements(
&self,
inputs: *mut SpeakerArrangement,
num_ins: i32,
outputs: *mut SpeakerArrangement,
num_outs: i32,
) -> tresult {
if num_ins < 0
|| num_outs < 0
|| num_ins as usize > MAX_BUSES
|| num_outs as usize > MAX_BUSES
{
return kResultFalse;
}
if (num_ins > 0 && inputs.is_null()) || (num_outs > 0 && outputs.is_null()) {
return kInvalidArgument;
}
if num_ins as usize != self.input_bus_count()
|| num_outs as usize != self.output_bus_count()
{
return kResultFalse;
}
for i in 0..num_ins as usize {
let requested = *inputs.add(i);
if validate_speaker_arrangement(requested).is_err() {
return kResultFalse;
}
if let Some(info) = self.core_input_bus_info(i) {
let expected = channel_count_to_speaker_arrangement(info.channel_count);
if requested != expected {
return kResultFalse;
}
}
}
for i in 0..num_outs as usize {
let requested = *outputs.add(i);
if validate_speaker_arrangement(requested).is_err() {
return kResultFalse;
}
if let Some(info) = self.core_output_bus_info(i) {
let expected = channel_count_to_speaker_arrangement(info.channel_count);
if requested != expected {
return kResultFalse;
}
}
}
kResultTrue
}
unsafe fn getBusArrangement(
&self,
dir: BusDirection,
index: i32,
arr: *mut SpeakerArrangement,
) -> tresult {
if arr.is_null() {
return kInvalidArgument;
}
let info = match dir as BusDirections {
BusDirections_::kInput => self.core_input_bus_info(index as usize),
BusDirections_::kOutput => self.core_output_bus_info(index as usize),
_ => None,
};
if let Some(info) = info {
*arr = channel_count_to_speaker_arrangement(info.channel_count);
kResultOk
} else {
kInvalidArgument
}
}
unsafe fn canProcessSampleSize(&self, symbolic_sample_size: i32) -> tresult {
match symbolic_sample_size as SymbolicSampleSizes {
SymbolicSampleSizes_::kSample32 => kResultOk,
SymbolicSampleSizes_::kSample64 => kResultOk, _ => kNotImplemented,
}
}
unsafe fn getLatencySamples(&self) -> u32 {
self.latency_samples()
}
unsafe fn setupProcessing(&self, setup: *mut ProcessSetup) -> tresult {
if setup.is_null() {
return kInvalidArgument;
}
let setup = &*setup;
*self.sample_rate.get() = setup.sampleRate;
*self.max_block_size.get() = setup.maxSamplesPerBlock as usize;
*self.symbolic_sample_size.get() = setup.symbolicSampleSize;
let state = &mut *self.state.get();
match state {
PluginState::Unprepared { plugin, pending_state } => {
let input_bus_count = plugin.input_bus_count();
let output_bus_count = plugin.output_bus_count();
let input_buses: Vec<CoreBusInfo> = (0..input_bus_count)
.filter_map(|i| plugin.input_bus_info(i))
.collect();
let output_buses: Vec<CoreBusInfo> = (0..output_bus_count)
.filter_map(|i| plugin.output_bus_info(i))
.collect();
let bus_layout = BusLayout::from_plugin(plugin);
if let Err(msg) = CachedBusConfig::from_plugin(plugin).validate() {
log::error!("Plugin bus configuration exceeds limits: {}", msg);
return kResultFalse;
}
let plugin_setup = build_setup::<P::Setup>(setup, &bus_layout);
let plugin = std::mem::take(plugin);
let pending = pending_state.take();
let mut processor = plugin.prepare(plugin_setup);
if let Some(data) = pending {
let _ = processor.load_state(&data);
use beamer_core::Parameters;
processor.parameters_mut().set_sample_rate(setup.sampleRate);
}
let bus_config = CachedBusConfig::new(
input_buses.iter().map(CachedBusInfo::from_bus_info).collect(),
output_buses.iter().map(CachedBusInfo::from_bus_info).collect(),
);
let max_frames = setup.maxSamplesPerBlock as usize;
*self.buffer_storage_f32.get() =
ProcessBufferStorage::allocate_from_config(&bus_config, max_frames);
*self.buffer_storage_f64.get() =
ProcessBufferStorage::allocate_from_config(&bus_config, max_frames);
if setup.symbolicSampleSize == SymbolicSampleSizes_::kSample64 as i32
&& !processor.supports_double_precision()
{
*self.conversion_buffers.get() =
ConversionBuffers::allocate_from_buses(&input_buses, &output_buses, setup.maxSamplesPerBlock as usize);
}
*state = PluginState::Prepared {
processor,
input_buses,
output_buses,
};
}
PluginState::Prepared { processor, input_buses, output_buses } => {
let current_sample_rate = *self.sample_rate.get();
if (current_sample_rate - setup.sampleRate).abs() > 0.001 {
let bus_layout = BusLayout {
main_input_channels: input_buses
.first()
.map(|b| b.channel_count)
.unwrap_or(2),
main_output_channels: output_buses
.first()
.map(|b| b.channel_count)
.unwrap_or(2),
aux_input_count: input_buses.len().saturating_sub(1),
aux_output_count: output_buses.len().saturating_sub(1),
};
let old_processor = std::mem::replace(
processor,
unsafe { std::mem::zeroed() },
);
let plugin = old_processor.unprepare();
let plugin_setup = build_setup::<P::Setup>(setup, &bus_layout);
let new_processor = plugin.prepare(plugin_setup);
if setup.symbolicSampleSize == SymbolicSampleSizes_::kSample64 as i32
&& !new_processor.supports_double_precision()
{
*self.conversion_buffers.get() =
ConversionBuffers::allocate_from_buses(input_buses, output_buses, setup.maxSamplesPerBlock as usize);
}
*processor = new_processor;
}
}
}
kResultOk
}
unsafe fn setProcessing(&self, _state: TBool) -> tresult {
kResultOk
}
unsafe fn process(&self, data: *mut ProcessData) -> tresult {
if data.is_null() {
return kInvalidArgument;
}
let process_data = &*data;
let num_samples = process_data.numSamples as usize;
if num_samples == 0 {
return kResultOk;
}
if let Some(parameter_changes) = ComRef::from_raw(process_data.inputParameterChanges) {
let parameters = self.parameters();
let parameter_count = parameter_changes.getParameterCount();
for i in 0..parameter_count {
if let Some(queue) = ComRef::from_raw(parameter_changes.getParameterData(i)) {
let parameter_id = queue.getParameterId();
let point_count = queue.getPointCount();
if point_count > 0 {
let mut sample_offset = 0;
let mut value = 0.0;
if queue.getPoint(point_count - 1, &mut sample_offset, &mut value)
== kResultTrue
{
parameters.set_normalized(parameter_id, value);
}
}
}
}
}
let midi_input = &mut *self.midi_input.get();
midi_input.clear();
if let Some(event_list) = ComRef::from_raw(process_data.inputEvents) {
let event_count = event_list.getEventCount();
for i in 0..event_count {
let mut event: Event = std::mem::zeroed();
if event_list.getEvent(i, &mut event) == kResultOk {
if let Some(midi_event) = convert_vst3_to_midi(&event) {
midi_input.push(midi_event);
}
}
}
}
if let Some(parameter_changes) = ComRef::from_raw(process_data.inputParameterChanges) {
if let Some(cc_state) = self.midi_cc_state.as_ref() {
let parameter_count = parameter_changes.getParameterCount();
for i in 0..parameter_count {
if let Some(queue) = ComRef::from_raw(parameter_changes.getParameterData(i)) {
let parameter_id = queue.getParameterId();
if let Some(controller) = MidiCcState::parameter_id_to_controller(parameter_id) {
if cc_state.has_controller(controller) {
let point_count = queue.getPointCount();
for j in 0..point_count {
let mut sample_offset: i32 = 0;
let mut value: f64 = 0.0;
if queue.getPoint(j, &mut sample_offset, &mut value) == kResultOk {
let midi_event = convert_cc_parameter_to_midi(
controller,
value as f32,
sample_offset as u32,
);
midi_input.push(midi_event);
}
}
}
}
}
}
}
}
if midi_input.has_overflowed() {
warn!(
"MIDI input buffer overflow: {} events max, some events were dropped",
beamer_core::midi::MAX_MIDI_EVENTS
);
}
let midi_output = &mut *self.midi_output.get();
midi_output.clear();
let sysex_pool = &mut *self.sysex_output_pool.get();
sysex_pool.clear();
#[cfg(feature = "sysex-heap-fallback")]
if sysex_pool.has_fallback() {
if let Some(event_list) = ComRef::from_raw(process_data.outputEvents) {
for sysex_data in sysex_pool.take_fallback() {
if let Some((ptr, len)) = sysex_pool.allocate(&sysex_data) {
let mut event: Event = std::mem::zeroed();
event.busIndex = 0;
event.sampleOffset = 0; event.ppqPosition = 0.0;
event.flags = 0;
event.r#type = K_DATA_EVENT;
event.__field0.data.r#type = DATA_TYPE_MIDI_SYSEX;
event.__field0.data.size = len as u32;
event.__field0.data.bytes = ptr;
let _ = event_list.addEvent(&mut event);
}
}
}
warn!(
"SysEx fallback: emitted delayed messages from previous block overflow"
);
}
let processor = self.processor_mut();
processor.process_midi(midi_input.as_slice(), midi_output);
if let Some(event_list) = ComRef::from_raw(process_data.outputEvents) {
for midi_event in midi_output.iter() {
if let Some(mut vst3_event) = convert_midi_to_vst3(midi_event, sysex_pool) {
let _ = event_list.addEvent(&mut vst3_event);
}
}
}
if midi_output.has_overflowed() {
warn!(
"MIDI output buffer overflow: {} events reached capacity, some events were dropped",
midi_output.len()
);
}
if sysex_pool.has_overflowed() {
warn!(
"SysEx output pool overflow: {} slots exhausted, some SysEx messages were dropped",
sysex_pool.capacity()
);
}
let transport = extract_transport(process_data.processContext);
let sample_rate = *self.sample_rate.get();
let context = if let Some(cc_state) = self.midi_cc_state.as_ref() {
CoreProcessContext::with_midi_cc(sample_rate, num_samples, transport, cc_state)
} else {
CoreProcessContext::new(sample_rate, num_samples, transport)
};
let symbolic_sample_size = *self.symbolic_sample_size.get();
let processor = self.processor_mut();
if symbolic_sample_size == SymbolicSampleSizes_::kSample64 as i32 {
if processor.supports_double_precision() {
self.process_audio_f64_native(process_data, num_samples, processor, &context);
} else {
self.process_audio_f64_converted(process_data, num_samples, processor, &context);
}
} else {
self.process_audio_f32(process_data, num_samples, processor, &context);
}
kResultOk
}
unsafe fn getTailSamples(&self) -> u32 {
match &*self.state.get() {
PluginState::Unprepared { .. } => 0,
PluginState::Prepared { processor, .. } => {
processor.tail_samples().saturating_add(processor.bypass_ramp_samples())
}
}
}
}
impl<P: Descriptor + 'static, Presets> IProcessContextRequirementsTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getProcessContextRequirements(&self) -> u32 {
const K_NEED_SYSTEM_TIME: u32 = 1 << 0;
const K_NEED_CONTINUOUS_TIME_SAMPLES: u32 = 1 << 1;
const K_NEED_PROJECT_TIME_MUSIC: u32 = 1 << 2;
const K_NEED_BAR_POSITION_MUSIC: u32 = 1 << 3;
const K_NEED_CYCLE_MUSIC: u32 = 1 << 4;
const K_NEED_SAMPLES_TO_NEXT_CLOCK: u32 = 1 << 5;
const K_NEED_TEMPO: u32 = 1 << 6;
const K_NEED_TIME_SIGNATURE: u32 = 1 << 7;
const K_NEED_FRAME_RATE: u32 = 1 << 9;
const K_NEED_TRANSPORT_STATE: u32 = 1 << 10;
K_NEED_SYSTEM_TIME
| K_NEED_CONTINUOUS_TIME_SAMPLES
| K_NEED_PROJECT_TIME_MUSIC
| K_NEED_BAR_POSITION_MUSIC
| K_NEED_CYCLE_MUSIC
| K_NEED_SAMPLES_TO_NEXT_CLOCK
| K_NEED_TEMPO
| K_NEED_TIME_SIGNATURE
| K_NEED_FRAME_RATE
| K_NEED_TRANSPORT_STATE
}
}
impl<P: Descriptor + 'static, Presets> IEditControllerTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn setComponentState(&self, _state: *mut IBStream) -> tresult {
kResultOk
}
unsafe fn setState(&self, _state: *mut IBStream) -> tresult {
kResultOk
}
unsafe fn getState(&self, _state: *mut IBStream) -> tresult {
kResultOk
}
unsafe fn getParameterCount(&self) -> i32 {
let user_parameters = self.parameters().count();
let cc_parameters = self
.midi_cc_state
.as_ref()
.map(|s| s.enabled_count())
.unwrap_or(0);
let preset_parameter = if Presets::count() > 0 { 1 } else { 0 };
(user_parameters + cc_parameters + preset_parameter) as i32
}
unsafe fn getParameterInfo(&self, parameter_index: i32, info: *mut ParameterInfo) -> tresult {
if info.is_null() {
return kInvalidArgument;
}
let parameters = self.parameters();
let user_parameter_count = parameters.count();
if (parameter_index as usize) < user_parameter_count {
if let Some(parameter_info) = parameters.info(parameter_index as usize) {
let info = &mut *info;
info.id = parameter_info.id;
copy_wstring(parameter_info.name, &mut info.title);
copy_wstring(parameter_info.short_name, &mut info.shortTitle);
copy_wstring(parameter_info.units, &mut info.units);
info.stepCount = parameter_info.step_count;
info.defaultNormalizedValue = parameter_info.default_normalized;
info.unitId = parameter_info.group_id;
info.flags = {
let mut flags = 0;
if parameter_info.flags.can_automate {
flags |= ParameterInfo_::ParameterFlags_::kCanAutomate;
}
if parameter_info.flags.is_bypass {
flags |= ParameterInfo_::ParameterFlags_::kIsBypass;
}
if parameter_info.flags.is_list {
flags |= ParameterInfo_::ParameterFlags_::kIsList;
}
if parameter_info.flags.is_hidden {
flags |= ParameterInfo_::ParameterFlags_::kIsHidden;
}
flags
};
return kResultOk;
}
return kInvalidArgument;
}
let cc_parameter_count = self
.midi_cc_state
.as_ref()
.map(|s| s.enabled_count())
.unwrap_or(0);
if let Some(cc_state) = self.midi_cc_state.as_ref() {
let cc_index = (parameter_index as usize) - user_parameter_count;
if cc_index < cc_parameter_count {
if let Some(parameter_info) = cc_state.info(cc_index) {
let info = &mut *info;
info.id = parameter_info.id;
copy_wstring(parameter_info.name, &mut info.title);
copy_wstring(parameter_info.short_name, &mut info.shortTitle);
copy_wstring(parameter_info.units, &mut info.units);
info.stepCount = parameter_info.step_count;
info.defaultNormalizedValue = parameter_info.default_normalized;
info.unitId = parameter_info.group_id;
info.flags = ParameterInfo_::ParameterFlags_::kCanAutomate
| ParameterInfo_::ParameterFlags_::kIsHidden;
return kResultOk;
}
}
}
let preset_count = Presets::count();
if preset_count > 0 {
let preset_param_index = user_parameter_count + cc_parameter_count;
if parameter_index as usize == preset_param_index {
let info = &mut *info;
info.id = PROGRAM_CHANGE_PARAM_ID;
copy_wstring("Program", &mut info.title);
copy_wstring("Prg", &mut info.shortTitle);
copy_wstring("", &mut info.units);
info.stepCount = (preset_count - 1) as i32; info.defaultNormalizedValue = 0.0; info.unitId = 0; info.flags = ParameterInfo_::ParameterFlags_::kIsProgramChange
| ParameterInfo_::ParameterFlags_::kIsList;
return kResultOk;
}
}
kInvalidArgument
}
unsafe fn getParamStringByValue(
&self,
id: u32,
value_normalized: f64,
string: *mut String128,
) -> tresult {
if string.is_null() {
return kInvalidArgument;
}
if id == PROGRAM_CHANGE_PARAM_ID {
let preset_count = Presets::count();
if preset_count > 0 {
let step_count = (preset_count - 1).max(1) as f64;
let preset_index = (value_normalized * step_count).round() as usize;
let preset_index = preset_index.min(preset_count - 1);
if let Some(preset_info) = Presets::info(preset_index) {
copy_wstring(preset_info.name, &mut *string);
return kResultOk;
}
}
copy_wstring("", &mut *string);
return kResultOk;
}
let parameters = self.parameters();
let display = parameters.normalized_to_string(id, value_normalized);
copy_wstring(&display, &mut *string);
kResultOk
}
unsafe fn getParamValueByString(
&self,
id: u32,
string: *mut TChar,
value_normalized: *mut f64,
) -> tresult {
if string.is_null() || value_normalized.is_null() {
return kInvalidArgument;
}
let len = len_wstring(string as *const TChar);
if let Ok(s) = String::from_utf16(slice::from_raw_parts(string as *const u16, len)) {
if id == PROGRAM_CHANGE_PARAM_ID {
let preset_count = Presets::count();
for i in 0..preset_count {
if let Some(preset_info) = Presets::info(i) {
if preset_info.name == s {
let step_count = (preset_count - 1).max(1) as f64;
*value_normalized = (i as f64) / step_count;
return kResultOk;
}
}
}
return kInvalidArgument;
}
let parameters = self.parameters();
if let Some(value) = parameters.string_to_normalized(id, &s) {
*value_normalized = value;
return kResultOk;
}
}
kInvalidArgument
}
unsafe fn normalizedParamToPlain(&self, id: u32, value_normalized: f64) -> f64 {
if id == PROGRAM_CHANGE_PARAM_ID {
let preset_count = Presets::count();
if preset_count > 0 {
let step_count = (preset_count - 1).max(1) as f64;
return (value_normalized * step_count).round();
}
return 0.0;
}
self.parameters().normalized_to_plain(id, value_normalized)
}
unsafe fn plainParamToNormalized(&self, id: u32, plain_value: f64) -> f64 {
if id == PROGRAM_CHANGE_PARAM_ID {
let preset_count = Presets::count();
if preset_count > 1 {
let step_count = (preset_count - 1) as f64;
return plain_value / step_count;
}
return 0.0;
}
self.parameters().plain_to_normalized(id, plain_value)
}
unsafe fn getParamNormalized(&self, id: u32) -> f64 {
if MidiCcState::is_midi_cc_parameter(id) {
if let Some(cc_state) = self.midi_cc_state.as_ref() {
return cc_state.get_normalized(id);
}
}
if id == PROGRAM_CHANGE_PARAM_ID {
let preset_count = Presets::count();
if preset_count > 1 {
let current_index = *self.current_preset_index.get();
let step_count = (preset_count - 1) as f64;
return (current_index as f64) / step_count;
} else if preset_count == 1 {
return 0.0;
}
return 0.0;
}
self.parameters().get_normalized(id)
}
unsafe fn setParamNormalized(&self, id: u32, value: f64) -> tresult {
if MidiCcState::is_midi_cc_parameter(id) {
if let Some(cc_state) = self.midi_cc_state.as_ref() {
cc_state.set_normalized(id, value);
return kResultOk;
}
}
if id == PROGRAM_CHANGE_PARAM_ID {
let preset_count = Presets::count();
if preset_count > 0 {
let step_count = (preset_count - 1) as f64;
let preset_index = (value * step_count).round() as usize;
let preset_index = preset_index.min(preset_count - 1);
Presets::apply(preset_index, self.parameters());
*self.current_preset_index.get() = preset_index as i32;
let handler = *self.component_handler.get();
if !handler.is_null() {
((*(*handler).vtbl).restartComponent)(
handler,
RestartFlags_::kParamValuesChanged,
);
}
return kResultOk;
}
return kInvalidArgument;
}
self.parameters().set_normalized(id, value);
kResultOk
}
unsafe fn setComponentHandler(&self, handler: *mut IComponentHandler) -> tresult {
let handler_ptr = self.component_handler.get();
let old_handler = *handler_ptr;
if !old_handler.is_null() {
let unknown = old_handler as *mut FUnknown;
((*(*unknown).vtbl).release)(unknown);
}
if !handler.is_null() {
let unknown = handler as *mut FUnknown;
((*(*unknown).vtbl).addRef)(unknown);
}
*handler_ptr = handler;
kResultOk
}
unsafe fn createView(&self, name: *const c_char) -> *mut IPlugView {
if name.is_null() {
return std::ptr::null_mut();
}
let name_str = std::ffi::CStr::from_ptr(name).to_str().unwrap_or("");
if name_str != "editor" {
return std::ptr::null_mut();
}
std::ptr::null_mut()
}
}
impl<P: Descriptor + 'static, Presets> IUnitInfoTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getUnitCount(&self) -> i32 {
use beamer_core::parameter_groups::ParameterGroups;
self.parameters().group_count() as i32
}
unsafe fn getUnitInfo(&self, unit_index: i32, info: *mut UnitInfo) -> tresult {
if info.is_null() || unit_index < 0 {
return kInvalidArgument;
}
use beamer_core::parameter_groups::ParameterGroups;
let parameters = self.parameters();
if let Some(group_info) = parameters.group_info(unit_index as usize) {
let info = &mut *info;
info.id = group_info.id;
info.parentUnitId = group_info.parent_id;
info.programListId = if group_info.id == 0 && Presets::count() > 0 {
FACTORY_PRESETS_LIST_ID
} else {
kNoProgramListId
};
copy_wstring(group_info.name, &mut info.name);
kResultOk
} else {
kInvalidArgument
}
}
unsafe fn getProgramListCount(&self) -> i32 {
if Presets::count() > 0 {
1 } else {
0
}
}
unsafe fn getProgramListInfo(
&self,
list_index: i32,
info: *mut ProgramListInfo,
) -> tresult {
if info.is_null() {
return kInvalidArgument;
}
if list_index != 0 || Presets::count() == 0 {
return kInvalidArgument;
}
let info = &mut *info;
info.id = FACTORY_PRESETS_LIST_ID;
info.programCount = Presets::count() as i32;
copy_wstring("Factory Presets", &mut info.name);
kResultOk
}
unsafe fn getProgramName(&self, list_id: i32, program_index: i32, name: *mut String128) -> tresult {
if name.is_null() {
return kInvalidArgument;
}
if list_id != FACTORY_PRESETS_LIST_ID {
return kInvalidArgument;
}
if let Some(preset_info) = Presets::info(program_index as usize) {
copy_wstring(preset_info.name, &mut *name);
kResultOk
} else {
kInvalidArgument
}
}
unsafe fn getProgramInfo(
&self,
_list_id: i32,
_program_index: i32,
_attribute_id: *const c_char,
_attribute_value: *mut String128,
) -> tresult {
kNotImplemented
}
unsafe fn hasProgramPitchNames(&self, _list_id: i32, _program_index: i32) -> tresult {
kResultFalse
}
unsafe fn getProgramPitchName(
&self,
_list_id: i32,
_program_index: i32,
_midi_pitch: i16,
_name: *mut String128,
) -> tresult {
kNotImplemented
}
unsafe fn getSelectedUnit(&self) -> i32 {
0 }
unsafe fn selectUnit(&self, _unit_id: i32) -> tresult {
kResultOk }
unsafe fn getUnitByBus(
&self,
_media_type: MediaType,
_dir: BusDirection,
_bus_index: i32,
_channel: i32,
_unit_id: *mut i32,
) -> tresult {
kNotImplemented
}
unsafe fn setUnitProgramData(
&self,
_list_or_unit_id: i32,
_program_index: i32,
_data: *mut IBStream,
) -> tresult {
kNotImplemented
}
}
impl<P: Descriptor + 'static, Presets> IMidiMappingTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getMidiControllerAssignment(
&self,
bus_index: i32,
channel: i16,
midi_controller_number: i16,
id: *mut u32,
) -> tresult {
if id.is_null() {
return kInvalidArgument;
}
let controller = midi_controller_number as u8;
if let Some(plugin) = self.try_plugin() {
if let Some(parameter_id) = plugin.midi_cc_to_parameter(bus_index, channel, controller) {
*id = parameter_id;
return kResultOk;
}
}
if let Some(cc_state) = self.midi_cc_state.as_ref() {
if cc_state.has_controller(controller) {
*id = MidiCcState::parameter_id(controller);
return kResultOk;
}
}
kResultFalse
}
}
impl<P: Descriptor + 'static, Presets> IMidiLearnTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn onLiveMIDIControllerInput(
&self,
bus_index: i32,
channel: i16,
midi_cc: i16,
) -> tresult {
if let Some(plugin) = self.try_plugin_mut() {
if plugin.on_midi_learn(bus_index, channel, midi_cc as u8) {
return kResultOk;
}
}
kResultFalse
}
}
impl<P: Descriptor + 'static, Presets> IMidiMapping2Trait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getNumMidi1ControllerAssignments(&self, direction: BusDirections) -> u32 {
if direction != BusDirections_::kInput {
return 0;
}
self.try_plugin()
.map(|p| p.midi1_assignments().len() as u32)
.unwrap_or(0)
}
unsafe fn getMidi1ControllerAssignments(
&self,
direction: BusDirections,
list: *const Midi1ControllerParamIDAssignmentList,
) -> tresult {
if list.is_null() || direction != BusDirections_::kInput {
return kInvalidArgument;
}
let Some(plugin) = self.try_plugin() else {
return kResultFalse;
};
let assignments = plugin.midi1_assignments();
let list_ref = &*list;
if (list_ref.count as usize) < assignments.len() {
return kResultFalse;
}
if assignments.is_empty() {
return kResultOk;
}
let map = slice::from_raw_parts_mut(list_ref.map, assignments.len());
for (i, a) in assignments.iter().enumerate() {
map[i] = Midi1ControllerParamIDAssignment {
pId: a.assignment.parameter_id,
busIndex: a.assignment.bus_index,
channel: a.assignment.channel,
controller: a.controller as i16,
};
}
kResultOk
}
unsafe fn getNumMidi2ControllerAssignments(&self, direction: BusDirections) -> u32 {
if direction != BusDirections_::kInput {
return 0;
}
self.try_plugin()
.map(|p| p.midi2_assignments().len() as u32)
.unwrap_or(0)
}
unsafe fn getMidi2ControllerAssignments(
&self,
direction: BusDirections,
list: *const Midi2ControllerParamIDAssignmentList,
) -> tresult {
if list.is_null() || direction != BusDirections_::kInput {
return kInvalidArgument;
}
let Some(plugin) = self.try_plugin() else {
return kResultFalse;
};
let assignments = plugin.midi2_assignments();
let list_ref = &*list;
if (list_ref.count as usize) < assignments.len() {
return kResultFalse;
}
if assignments.is_empty() {
return kResultOk;
}
let map = slice::from_raw_parts_mut(list_ref.map, assignments.len());
for (i, a) in assignments.iter().enumerate() {
map[i] = Midi2ControllerParamIDAssignment {
pId: a.assignment.parameter_id,
busIndex: a.assignment.bus_index,
channel: a.assignment.channel,
controller: Midi2Controller {
bank: a.controller.bank,
registered: if a.controller.registered { 1 } else { 0 },
index: a.controller.index,
reserved: 0,
},
};
}
kResultOk
}
}
impl<P: Descriptor + 'static, Presets> IMidiLearn2Trait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn onLiveMidi1ControllerInput(
&self,
bus_index: i32,
channel: u8,
midi_cc: i16,
) -> tresult {
if let Some(plugin) = self.try_plugin_mut() {
if plugin.on_midi1_learn(bus_index, channel, midi_cc as u8) {
return kResultOk;
}
}
kResultFalse
}
unsafe fn onLiveMidi2ControllerInput(
&self,
bus_index: i32,
channel: u8,
midi_cc: Midi2Controller,
) -> tresult {
if let Some(plugin) = self.try_plugin_mut() {
let controller = beamer_core::Midi2Controller {
bank: midi_cc.bank,
registered: midi_cc.registered != 0,
index: midi_cc.index,
};
if plugin.on_midi2_learn(bus_index, channel, controller) {
return kResultOk;
}
}
kResultFalse
}
}
impl<P: Descriptor + 'static, Presets> INoteExpressionControllerTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getNoteExpressionCount(&self, bus_index: i32, channel: i16) -> i32 {
self.try_plugin()
.map(|p| p.note_expression_count(bus_index, channel) as i32)
.unwrap_or(0)
}
unsafe fn getNoteExpressionInfo(
&self,
bus_index: i32,
channel: i16,
note_expression_index: i32,
info: *mut NoteExpressionTypeInfo,
) -> tresult {
if info.is_null() {
return kInvalidArgument;
}
let Some(plugin) = self.try_plugin() else {
return kInvalidArgument;
};
if let Some(expr_info) =
plugin.note_expression_info(bus_index, channel, note_expression_index as usize)
{
let vst_info = &mut *info;
vst_info.typeId = expr_info.type_id;
copy_wstring(expr_info.title_str(), &mut vst_info.title);
copy_wstring(expr_info.short_title_str(), &mut vst_info.shortTitle);
copy_wstring(expr_info.units_str(), &mut vst_info.units);
vst_info.unitId = expr_info.unit_id;
vst_info.valueDesc.minimum = expr_info.value_desc.minimum;
vst_info.valueDesc.maximum = expr_info.value_desc.maximum;
vst_info.valueDesc.defaultValue = expr_info.value_desc.default_value;
vst_info.valueDesc.stepCount = expr_info.value_desc.step_count;
vst_info.associatedParameterId = expr_info.associated_parameter_id as u32;
vst_info.flags = expr_info.flags.0;
kResultOk
} else {
kInvalidArgument
}
}
unsafe fn getNoteExpressionStringByValue(
&self,
bus_index: i32,
channel: i16,
id: NoteExpressionTypeID,
value_normalized: NoteExpressionValue,
string: *mut String128,
) -> tresult {
if string.is_null() {
return kInvalidArgument;
}
let Some(plugin) = self.try_plugin() else {
return kInvalidArgument;
};
let display = plugin.note_expression_value_to_string(bus_index, channel, id, value_normalized);
copy_wstring(&display, &mut *string);
kResultOk
}
unsafe fn getNoteExpressionValueByString(
&self,
bus_index: i32,
channel: i16,
id: NoteExpressionTypeID,
string: *const TChar,
value_normalized: *mut NoteExpressionValue,
) -> tresult {
if string.is_null() || value_normalized.is_null() {
return kInvalidArgument;
}
let len = len_wstring(string);
if let Ok(s) = String::from_utf16(slice::from_raw_parts(string, len)) {
if let Some(plugin) = self.try_plugin() {
if let Some(value) = plugin.note_expression_string_to_value(bus_index, channel, id, &s) {
*value_normalized = value;
return kResultOk;
}
}
}
kResultFalse
}
}
impl<P: Descriptor + 'static, Presets> IKeyswitchControllerTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getKeyswitchCount(&self, bus_index: i32, channel: i16) -> i32 {
self.try_plugin()
.map(|p| p.keyswitch_count(bus_index, channel) as i32)
.unwrap_or(0)
}
unsafe fn getKeyswitchInfo(
&self,
bus_index: i32,
channel: i16,
keyswitch_index: i32,
info: *mut KeyswitchInfo,
) -> tresult {
if info.is_null() {
return kInvalidArgument;
}
let Some(plugin) = self.try_plugin() else {
return kInvalidArgument;
};
if let Some(ks_info) =
plugin.keyswitch_info(bus_index, channel, keyswitch_index as usize)
{
let vst_info = &mut *info;
vst_info.typeId = ks_info.type_id;
copy_wstring(ks_info.title_str(), &mut vst_info.title);
copy_wstring(ks_info.short_title_str(), &mut vst_info.shortTitle);
vst_info.keyswitchMin = ks_info.keyswitch_min;
vst_info.keyswitchMax = ks_info.keyswitch_max;
vst_info.keyRemapped = ks_info.key_remapped;
vst_info.unitId = ks_info.unit_id;
vst_info.flags = ks_info.flags;
kResultOk
} else {
kInvalidArgument
}
}
}
impl<P: Descriptor + 'static, Presets> INoteExpressionPhysicalUIMappingTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn getPhysicalUIMapping(
&self,
bus_index: i32,
channel: i16,
list: *mut PhysicalUIMapList,
) -> tresult {
if list.is_null() {
return kInvalidArgument;
}
let Some(plugin) = self.try_plugin() else {
return kInvalidArgument;
};
let mappings = plugin.physical_ui_mappings(bus_index, channel);
let list_ref = &mut *list;
let fill_count = (list_ref.count as usize).min(mappings.len());
if fill_count > 0 && !list_ref.map.is_null() {
let map_slice = slice::from_raw_parts_mut(list_ref.map, fill_count);
for (i, mapping) in mappings.iter().take(fill_count).enumerate() {
map_slice[i].physicalUITypeID = mapping.physical_ui_type_id;
map_slice[i].noteExpressionTypeID = mapping.note_expression_type_id;
}
}
kResultOk
}
}
impl<P: Descriptor + 'static, Presets> IVst3WrapperMPESupportTrait for Vst3Processor<P, Presets>
where
Presets: FactoryPresets<Parameters = P::Parameters>,
{
unsafe fn enableMPEInputProcessing(&self, state: TBool) -> tresult {
if let Some(plugin) = self.try_plugin_mut() {
if plugin.enable_mpe_input_processing(state != 0) {
return kResultOk;
}
}
kResultFalse
}
unsafe fn setMPEInputDeviceSettings(
&self,
master_channel: i32,
member_begin_channel: i32,
member_end_channel: i32,
) -> tresult {
if let Some(plugin) = self.try_plugin_mut() {
let settings = beamer_core::MpeInputDeviceSettings {
master_channel,
member_begin_channel,
member_end_channel,
};
if plugin.set_mpe_input_device_settings(settings) {
return kResultOk;
}
}
kResultFalse
}
}
fn utf16_to_utf8(utf16: &[u16], utf8_buf: &mut [u8]) -> usize {
let mut utf8_pos = 0;
for &code_unit in utf16 {
if code_unit == 0 {
break;
}
let (bytes_needed, char_value) = if code_unit < 0x80 {
(1, code_unit as u32)
} else if code_unit < 0x800 {
(2, code_unit as u32)
} else if (0xD800..=0xDFFF).contains(&code_unit) {
(3, 0xFFFD) } else {
(3, code_unit as u32)
};
if utf8_pos + bytes_needed > utf8_buf.len() {
break;
}
match bytes_needed {
1 => {
utf8_buf[utf8_pos] = char_value as u8;
}
2 => {
utf8_buf[utf8_pos] = (0xC0 | (char_value >> 6)) as u8;
utf8_buf[utf8_pos + 1] = (0x80 | (char_value & 0x3F)) as u8;
}
3 => {
utf8_buf[utf8_pos] = (0xE0 | (char_value >> 12)) as u8;
utf8_buf[utf8_pos + 1] = (0x80 | ((char_value >> 6) & 0x3F)) as u8;
utf8_buf[utf8_pos + 2] = (0x80 | (char_value & 0x3F)) as u8;
}
_ => unreachable!(),
}
utf8_pos += bytes_needed;
}
utf8_pos
}
fn channel_count_to_speaker_arrangement(channel_count: u32) -> SpeakerArrangement {
match channel_count {
1 => SpeakerArr::kMono,
2 => SpeakerArr::kStereo,
n => (1u64 << n) - 1,
}
}
fn convert_cc_parameter_to_midi(controller: u8, normalized_value: f32, sample_offset: u32) -> MidiEvent {
match controller {
LEGACY_CC_PITCH_BEND => {
let bend = normalized_value * 2.0 - 1.0;
MidiEvent::pitch_bend(sample_offset, 0, bend)
}
LEGACY_CC_CHANNEL_PRESSURE => {
MidiEvent::channel_pressure(sample_offset, 0, normalized_value)
}
cc => {
MidiEvent::control_change(sample_offset, 0, cc, normalized_value)
}
}
}
unsafe fn convert_vst3_to_midi(event: &Event) -> Option<MidiEvent> {
let sample_offset = event.sampleOffset as u32;
match event.r#type {
K_NOTE_ON_EVENT => {
let note_on = &event.__field0.noteOn;
let note_id = if note_on.noteId == -1 {
note_on.pitch as i32
} else {
note_on.noteId
};
Some(MidiEvent::note_on(
sample_offset,
note_on.channel as u8,
note_on.pitch as u8,
note_on.velocity,
note_id,
note_on.tuning,
note_on.length,
))
}
K_NOTE_OFF_EVENT => {
let note_off = &event.__field0.noteOff;
let note_id = if note_off.noteId == -1 {
note_off.pitch as i32
} else {
note_off.noteId
};
Some(MidiEvent::note_off(
sample_offset,
note_off.channel as u8,
note_off.pitch as u8,
note_off.velocity,
note_id,
note_off.tuning,
))
}
K_POLY_PRESSURE_EVENT => {
let poly = &event.__field0.polyPressure;
let note_id = if poly.noteId == -1 {
poly.pitch as i32
} else {
poly.noteId
};
Some(MidiEvent::poly_pressure(
sample_offset,
poly.channel as u8,
poly.pitch as u8,
poly.pressure,
note_id,
))
}
K_DATA_EVENT => {
let data_event = &event.__field0.data;
if data_event.r#type == DATA_TYPE_MIDI_SYSEX {
let mut sysex = SysEx::new();
let copy_len = (data_event.size as usize).min(MAX_SYSEX_SIZE);
if copy_len > 0 && !data_event.bytes.is_null() {
let src = std::slice::from_raw_parts(data_event.bytes, copy_len);
sysex.data[..copy_len].copy_from_slice(src);
sysex.len = copy_len as u16;
}
Some(MidiEvent {
sample_offset,
event: MidiEventKind::SysEx(Box::new(sysex)),
})
} else {
None
}
}
K_NOTE_EXPRESSION_VALUE_EVENT => {
let expr = &event.__field0.noteExpressionValue;
Some(MidiEvent {
sample_offset,
event: MidiEventKind::NoteExpressionValue(CoreNoteExpressionValue {
note_id: expr.noteId,
expression_type: expr.typeId,
value: expr.value,
}),
})
}
K_NOTE_EXPRESSION_INT_VALUE_EVENT => {
let expr = &event.__field0.noteExpressionIntValue;
Some(MidiEvent {
sample_offset,
event: MidiEventKind::NoteExpressionInt(NoteExpressionInt {
note_id: expr.noteId,
expression_type: expr.typeId,
value: expr.value,
}),
})
}
K_NOTE_EXPRESSION_TEXT_EVENT => {
let expr = &event.__field0.noteExpressionText;
let mut text_event = NoteExpressionText {
note_id: expr.noteId,
expression_type: expr.typeId,
text: [0u8; MAX_EXPRESSION_TEXT_SIZE],
text_len: 0,
};
let text_len = expr.textLen as usize;
if !expr.text.is_null() && text_len > 0 {
let text_slice = std::slice::from_raw_parts(expr.text, text_len);
let utf8_len = utf16_to_utf8(text_slice, &mut text_event.text);
text_event.text_len = utf8_len as u8;
}
Some(MidiEvent {
sample_offset,
event: MidiEventKind::NoteExpressionText(text_event),
})
}
K_CHORD_EVENT => {
let chord = &event.__field0.chord;
let mut info = ChordInfo {
root: chord.root as i8,
bass_note: chord.bassNote as i8,
mask: chord.mask as u16,
name: [0u8; MAX_CHORD_NAME_SIZE],
name_len: 0,
};
let text_len = chord.textLen as usize;
if !chord.text.is_null() && text_len > 0 {
let text_slice = std::slice::from_raw_parts(chord.text, text_len);
let utf8_len = utf16_to_utf8(text_slice, &mut info.name);
info.name_len = utf8_len as u8;
}
Some(MidiEvent {
sample_offset,
event: MidiEventKind::ChordInfo(info),
})
}
K_SCALE_EVENT => {
let scale = &event.__field0.scale;
let mut info = ScaleInfo {
root: scale.root as i8,
mask: scale.mask as u16,
name: [0u8; MAX_SCALE_NAME_SIZE],
name_len: 0,
};
let text_len = scale.textLen as usize;
if !scale.text.is_null() && text_len > 0 {
let text_slice = std::slice::from_raw_parts(scale.text, text_len);
let utf8_len = utf16_to_utf8(text_slice, &mut info.name);
info.name_len = utf8_len as u8;
}
Some(MidiEvent {
sample_offset,
event: MidiEventKind::ScaleInfo(info),
})
}
K_LEGACY_MIDI_CC_OUT_EVENT => {
let cc_event = &event.__field0.midiCCOut;
let channel = cc_event.channel as u8;
match cc_event.controlNumber {
0..=127 => {
Some(MidiEvent::control_change(
sample_offset,
channel,
cc_event.controlNumber,
cc_event.value as f32 / 127.0,
))
}
LEGACY_CC_CHANNEL_PRESSURE => Some(MidiEvent::channel_pressure(
sample_offset,
channel,
cc_event.value as f32 / 127.0,
)),
LEGACY_CC_PITCH_BEND => {
let lsb = (cc_event.value as u8) as u16;
let msb = (cc_event.value2 as u8) as u16;
let raw = (msb << 7) | (lsb & 0x7F);
let normalized = (raw as f32 - 8192.0) / 8192.0;
Some(MidiEvent::pitch_bend(sample_offset, channel, normalized))
}
LEGACY_CC_PROGRAM_CHANGE => Some(MidiEvent::program_change(
sample_offset,
channel,
cc_event.value as u8,
)),
_ => None, }
}
_ => None, }
}
fn convert_midi_to_vst3(midi: &MidiEvent, sysex_pool: &mut SysExOutputPool) -> Option<Event> {
let mut event: Event = unsafe { std::mem::zeroed() };
event.busIndex = 0;
event.sampleOffset = midi.sample_offset as i32;
event.ppqPosition = 0.0;
event.flags = 0;
match &midi.event {
MidiEventKind::NoteOn(note_on) => {
event.r#type = K_NOTE_ON_EVENT;
event.__field0.noteOn.channel = note_on.channel as i16;
event.__field0.noteOn.pitch = note_on.pitch as i16;
event.__field0.noteOn.velocity = note_on.velocity;
event.__field0.noteOn.noteId = note_on.note_id;
event.__field0.noteOn.tuning = note_on.tuning;
event.__field0.noteOn.length = note_on.length;
}
MidiEventKind::NoteOff(note_off) => {
event.r#type = K_NOTE_OFF_EVENT;
event.__field0.noteOff.channel = note_off.channel as i16;
event.__field0.noteOff.pitch = note_off.pitch as i16;
event.__field0.noteOff.velocity = note_off.velocity;
event.__field0.noteOff.noteId = note_off.note_id;
event.__field0.noteOff.tuning = note_off.tuning;
}
MidiEventKind::PolyPressure(poly) => {
event.r#type = K_POLY_PRESSURE_EVENT;
event.__field0.polyPressure.channel = poly.channel as i16;
event.__field0.polyPressure.pitch = poly.pitch as i16;
event.__field0.polyPressure.pressure = poly.pressure;
event.__field0.polyPressure.noteId = poly.note_id;
}
MidiEventKind::ControlChange(cc) => {
event.r#type = K_LEGACY_MIDI_CC_OUT_EVENT;
event.__field0.midiCCOut.controlNumber = cc.controller;
event.__field0.midiCCOut.channel = cc.channel as i8;
event.__field0.midiCCOut.value = (cc.value * 127.0) as i8;
event.__field0.midiCCOut.value2 = 0;
}
MidiEventKind::PitchBend(pb) => {
event.r#type = K_LEGACY_MIDI_CC_OUT_EVENT;
event.__field0.midiCCOut.controlNumber = LEGACY_CC_PITCH_BEND;
event.__field0.midiCCOut.channel = pb.channel as i8;
let raw = ((pb.value * 8192.0) + 8192.0).clamp(0.0, 16383.0) as i16;
event.__field0.midiCCOut.value = (raw & 0x7F) as i8;
event.__field0.midiCCOut.value2 = ((raw >> 7) & 0x7F) as i8;
}
MidiEventKind::ChannelPressure(cp) => {
event.r#type = K_LEGACY_MIDI_CC_OUT_EVENT;
event.__field0.midiCCOut.controlNumber = LEGACY_CC_CHANNEL_PRESSURE;
event.__field0.midiCCOut.channel = cp.channel as i8;
event.__field0.midiCCOut.value = (cp.pressure * 127.0) as i8;
event.__field0.midiCCOut.value2 = 0;
}
MidiEventKind::ProgramChange(pc) => {
event.r#type = K_LEGACY_MIDI_CC_OUT_EVENT;
event.__field0.midiCCOut.controlNumber = LEGACY_CC_PROGRAM_CHANGE;
event.__field0.midiCCOut.channel = pc.channel as i8;
event.__field0.midiCCOut.value = pc.program as i8;
event.__field0.midiCCOut.value2 = 0;
}
MidiEventKind::NoteExpressionValue(expr) => {
event.r#type = K_NOTE_EXPRESSION_VALUE_EVENT;
event.__field0.noteExpressionValue.noteId = expr.note_id;
event.__field0.noteExpressionValue.typeId = expr.expression_type;
event.__field0.noteExpressionValue.value = expr.value;
}
MidiEventKind::NoteExpressionInt(expr) => {
event.r#type = K_NOTE_EXPRESSION_INT_VALUE_EVENT;
event.__field0.noteExpressionIntValue.noteId = expr.note_id;
event.__field0.noteExpressionIntValue.typeId = expr.expression_type;
event.__field0.noteExpressionIntValue.value = expr.value;
}
MidiEventKind::SysEx(sysex) => {
if let Some((ptr, len)) = sysex_pool.allocate(sysex.as_slice()) {
event.r#type = K_DATA_EVENT;
event.__field0.data.r#type = DATA_TYPE_MIDI_SYSEX;
event.__field0.data.size = len as u32;
event.__field0.data.bytes = ptr;
} else {
return None;
}
}
MidiEventKind::ChordInfo(_) => return None,
MidiEventKind::ScaleInfo(_) => return None,
MidiEventKind::NoteExpressionText(_) => return None,
}
Some(event)
}