diff --git a/src/wrapper/clap/plugin.rs b/src/wrapper/clap/plugin.rs index 4c92d4a2..8fe37311 100644 --- a/src/wrapper/clap/plugin.rs +++ b/src/wrapper/clap/plugin.rs @@ -48,8 +48,8 @@ use crate::buffer::Buffer; use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY}; use crate::param::internals::ParamPtr; use crate::plugin::{BufferConfig, BusConfig, ClapPlugin, NoteEvent, ProcessStatus}; +use crate::wrapper::state; use crate::wrapper::util::{hash_param_id, process_wrapper, strlcpy}; -use crate::Plugin; /// Right now the wrapper adds its own bypass parameter. /// @@ -849,7 +849,7 @@ impl Wrapper

{ let name = match channel_count { 1 => format!("Mono {port_type_name}"), 2 => format!("Stereo {port_type_name}"), - n => format!("{channel_count} channel {port_type_name}"), + n => format!("{n} channel {port_type_name}"), }; let port_type = match channel_count { 1 => CLAP_PORT_MONO, @@ -1072,14 +1072,96 @@ impl Wrapper

{ check_null_ptr!(false, plugin, stream); let wrapper = &*(plugin as *const Self); - todo!() + let serialized = state::serialize( + wrapper.plugin.read().params(), + &wrapper.param_by_hash, + &wrapper.param_id_to_hash, + BYPASS_PARAM_ID, + &wrapper.bypass_state, + ); + match serialized { + Ok(serialized) => { + let num_bytes_written = ((*stream).write)( + stream, + serialized.as_ptr() as *const c_void, + serialized.len() as u64, + ); + + nih_debug_assert_eq!(num_bytes_written as usize, serialized.len()); + true + } + Err(err) => { + nih_debug_assert_failure!("Could not save state: {}", err); + false + } + } } unsafe extern "C" fn ext_state_load( plugin: *const clap_plugin, stream: *mut clap_istream, ) -> bool { - todo!() + check_null_ptr!(false, plugin, stream); + let wrapper = &*(plugin as *const Self); + + // CLAP does not have a way to tell you about the size of a stream, so the workaround would + // be to keep reading 1 MiB chunks until we reach the end of file, reallocating the buffer + // each time as we go. + const CHUNK_SIZE: usize = 1 << 20; + let mut actual_read_buffer_size = 0usize; + let mut read_buffer: Vec = Vec::with_capacity(CHUNK_SIZE); + loop { + let num_bytes_read = ((*stream).read)( + stream, + // Make sure to start reading from where we left off if we're going through this + // loop multiple times + read_buffer.as_mut_ptr().add(actual_read_buffer_size) as *mut c_void, + CHUNK_SIZE as u64, + ); + if num_bytes_read < 0 { + nih_debug_assert_failure!("Error while reading plugin state"); + return false; + } + + actual_read_buffer_size += num_bytes_read as usize; + if num_bytes_read != CHUNK_SIZE as i64 { + // If we read anything below `CHUNK_SIZE` bytes, then we've reached the end of file + // on this read + break; + } + + // Otherwise, reallocate the buffer with enough room for another chunk and try again + nih_debug_assert_eq!(num_bytes_read, CHUNK_SIZE as i64); + read_buffer.reserve(CHUNK_SIZE); + } + + // After reading, trim the additional capacity near the end of the buffer + read_buffer.set_len(actual_read_buffer_size as usize); + + let success = state::deserialize( + &read_buffer, + wrapper.plugin.read().params(), + &wrapper.param_by_hash, + &wrapper.param_id_to_hash, + wrapper.current_buffer_config.load().as_ref(), + BYPASS_PARAM_ID, + &wrapper.bypass_state, + ); + if !success { + return false; + } + + // Reinitialize the plugin after loading state so it can respond to the new parameter values + let bus_config = wrapper.current_bus_config.load(); + if let Some(buffer_config) = wrapper.current_buffer_config.load() { + wrapper.plugin.write().initialize( + &bus_config, + &buffer_config, + &mut wrapper.make_process_context(), + ); + } + + true } } diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index 49052e95..363f3750 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -209,7 +209,7 @@ impl IComponent for Wrapper

{ ); read_buffer.set_len(num_bytes_read as usize); - // If the size is zero, some hsots will always return `kResultFalse` even if the read was + // If the size is zero, some hosts will always return `kResultFalse` even if the read was // 'successful', so we can't check the return value but we can check the number of bytes // read. if read_buffer.len() != stream_byte_size as usize {