### Basic Plugin Structure in Rust Source: https://github.com/rustaudio/vst-rs/blob/master/README.md Implement the `Plugin` trait for your struct and use the `plugin_main` macro to export the necessary functions. This example creates a plugin with no specific functionality. ```rust #[macro_use] extern crate vst; use vst::prelude::*; struct BasicPlugin; impl Plugin for BasicPlugin { fn new(_host: HostCallback) -> Self { BasicPlugin } fn get_info(&self) -> Info { Info { name: "Basic Plugin".to_string(), unique_id: 1357, // Used by hosts to differentiate between plugins. ..Default::default() } } } plugin_main!(BasicPlugin); // Important! ``` -------------------------------- ### Communicate with Host via HostCallback Source: https://context7.com/rustaudio/vst-rs/llms.txt Demonstrates how plugins use HostCallback to query host information, retrieve timing data, and report parameter changes. ```rust use vst::prelude::*; use vst::api::TimeInfo; use vst::host::Host; struct MyPlugin { host: HostCallback, params: Arc, } impl Plugin for MyPlugin { fn new(host: HostCallback) -> Self { MyPlugin { host, params: Arc::new(MyPluginParameters::default()), } } fn get_info(&self) -> Info { Info { name: "Host Communication Demo".to_string(), unique_id: 12345, parameters: 1, ..Default::default() } } fn init(&mut self) { // Query host VST version let version = self.host.vst_version(); println!("Host VST version: {}", version); // e.g., 2400 for VST 2.4 // Get host information let (plugin_id, vendor, product) = self.host.get_info(); println!("Host: {} by {}", product, vendor); } fn process(&mut self, buffer: &mut AudioBuffer) { // Request time information from host use vst::api::TimeInfoFlags; let flags = TimeInfoFlags::TEMPO_VALID | TimeInfoFlags::PPQ_POS_VALID; if let Some(time_info) = self.host.get_time_info(flags.bits()) { let tempo = time_info.tempo; // BPM let position = time_info.ppq_pos; // Position in quarter notes let sample_rate = time_info.sample_rate; // Use timing for tempo-synced effects... } // Process audio... for (input, output) in buffer.zip() { for (in_sample, out_sample) in input.iter().zip(output) { *out_sample = *in_sample; } } } fn get_parameter_object(&mut self) -> Arc { Arc::clone(&self.params) as Arc } } // In parameter handling code: impl MyPluginParameters { fn set_from_ui(&self, host: &HostCallback, index: i32, value: f32) { // Signal automation recording started host.begin_edit(index); // Update parameter value self.value.set(value); // Notify host of value change (for automation) host.automate(index, value); // Signal automation recording ended host.end_edit(index); } fn request_ui_refresh(&self, host: &HostCallback) { // Tell host to refresh plugin UI host.update_display(); } } ``` -------------------------------- ### Cargo.toml for Basic VST Plugin Source: https://github.com/rustaudio/vst-rs/blob/master/README.md Configure your Cargo.toml to build a dynamic library suitable for VST hosts. Ensure the `crate-type` is set to `cdylib`. ```toml [package] name = "basic_vst" version = "0.0.1" authors = ["Author "] [dependencies] vst = { git = "https://github.com/rustaudio/vst-rs" } [lib] name = "basicvst" crate-type = ["cdylib"] ``` -------------------------------- ### Implement a VST Host Source: https://context7.com/rustaudio/vst-rs/llms.txt Defines a custom host by implementing the Host trait and using PluginLoader to manage plugin lifecycle and audio processing. ```rust use std::env; use std::path::Path; use std::sync::{Arc, Mutex}; use vst::host::{Host, PluginLoader, HostBuffer}; use vst::plugin::Plugin; struct MyHost; impl Host for MyHost { fn automate(&self, index: i32, value: f32) { println!("Parameter {} changed to {}", index, value); } fn begin_edit(&self, index: i32) { println!("Started editing parameter {}", index); } fn end_edit(&self, index: i32) { println!("Finished editing parameter {}", index); } fn get_plugin_id(&self) -> i32 { 0 // Return shell plugin ID if applicable } fn idle(&self) { // Handle idle callbacks from plugin } } fn main() { let path = Path::new("/path/to/plugin.vst"); // Host must be wrapped in Arc> for thread safety let host = Arc::new(Mutex::new(MyHost)); // Load the plugin let mut loader = PluginLoader::load(path, Arc::clone(&host)) .expect("Failed to load plugin"); // Create plugin instance let mut instance = loader.instance() .expect("Failed to create instance"); // Get plugin information let info = instance.get_info(); println!("Loaded '{}' by {}", info.name, info.vendor); println!(" Inputs: {}, Outputs: {}", info.inputs, info.outputs); println!(" Parameters: {}", info.parameters); println!(" VST ID: {}", info.unique_id); // Initialize and configure instance.init(); instance.set_sample_rate(44100.0); instance.set_block_size(512); instance.resume(); // Process audio let mut host_buffer: HostBuffer = HostBuffer::new( info.inputs as usize, info.outputs as usize ); let inputs = vec![vec![0.0f32; 512]; info.inputs as usize]; let mut outputs = vec![vec![0.0f32; 512]; info.outputs as usize]; { let mut audio_buffer = host_buffer.bind(&inputs, &mut outputs); instance.process(&mut audio_buffer); } // Clean up instance.suspend(); // instance is dropped automatically, calling shutdown } ``` -------------------------------- ### Implement Raw Binary State Persistence in Rust Source: https://context7.com/rustaudio/vst-rs/llms.txt Implement `get_preset_data` and `load_preset_data` to save and load plugin state as raw binary data. This method is useful for complex states like wavetables or serialized structures. Ensure a version marker for forward compatibility. ```rust use vst::prelude::*; use std::sync::Arc; struct MyPluginParameters { values: Vec, } impl PluginParameters for MyPluginParameters { fn get_parameter(&self, index: i32) -> f32 { self.values.get(index as usize) .map(|v| v.get()) .unwrap_or(0.0) } fn set_parameter(&self, index: i32, value: f32) { if let Some(param) = self.values.get(index as usize) { param.set(value); } } // Save single preset as raw bytes fn get_preset_data(&self) -> Vec { let mut data = Vec::new(); // Version marker for forward compatibility data.extend_from_slice(&1u32.to_le_bytes()); // Save all parameters for param in &self.values { data.extend_from_slice(¶m.get().to_le_bytes()); } data } // Load single preset from raw bytes fn load_preset_data(&self, data: &[u8]) { if data.len() < 4 { return; } let version = u32::from_le_bytes([data[0], data[1], data[2], data[3]]); if version != 1 { return; // Unknown version } let mut offset = 4; for param in &self.values { if offset + 4 <= data.len() { let bytes = [data[offset], data[offset+1], data[offset+2], data[offset+3]]; param.set(f32::from_le_bytes(bytes)); offset += 4; } } } // Save all presets (bank) fn get_bank_data(&self) -> Vec { // For simple plugins, bank data might just be the current preset self.get_preset_data() } // Load all presets (bank) fn load_bank_data(&self, data: &[u8]) { self.load_preset_data(data); } fn get_preset_num(&self) -> i32 { 0 // Current preset index } fn change_preset(&self, preset: i32) { // Switch to preset at index } fn get_preset_name(&self, preset: i32) -> String { format!("Preset {}", preset) } fn set_preset_name(&self, name: String) { // Store preset name } } impl Plugin for MyPlugin { fn get_info(&self) -> Info { Info { name: "Preset Demo".to_string(), unique_id: 22222, preset_chunks: true, // Enable chunk-based preset saving presets: 16, // Number of presets parameters: 8, ..Default::default() } } // ... } ``` -------------------------------- ### Package VST Plugin on OS X Source: https://github.com/rustaudio/vst-rs/blob/master/README.md Use the provided `osx_vst_bundler.sh` script to package your compiled VST plugin as a loadable bundle on macOS. ```shell ./osx_vst_bundler.sh Plugin target/release/plugin.dylib Creates a Plugin.vst bundle ``` -------------------------------- ### Add vst-rs Crate to Cargo.toml Source: https://github.com/rustaudio/vst-rs/blob/master/README.md Include the vst-rs crate in your project's Cargo.toml file. You can fetch it from crates.io or directly from the GitHub repository. ```toml vst = "0.3" ``` ```toml vst = { git = "https://github.com/rustaudio/vst-rs" } ``` -------------------------------- ### Implement Audio Processing with Plugin Trait Source: https://context7.com/rustaudio/vst-rs/llms.txt Demonstrates the Plugin trait implementation for a gain effect, including buffer iteration and manual channel splitting. ```rust impl Plugin for GainEffect { fn new(_host: HostCallback) -> Self { GainEffect { params: Arc::new(GainEffectParameters::default()), } } fn get_info(&self) -> Info { Info { name: "Gain Effect".to_string(), unique_id: 243723072, inputs: 2, outputs: 2, parameters: 1, category: Category::Effect, ..Default::default() } } fn process(&mut self, buffer: &mut AudioBuffer) { // Read parameter from thread-safe storage let amplitude = self.params.amplitude.get(); // Process each input/output channel pair for (input_buffer, output_buffer) in buffer.zip() { // Process each sample for (input_sample, output_sample) in input_buffer.iter().zip(output_buffer) { *output_sample = *input_sample * amplitude; } } } // Alternative: split inputs and outputs for more control fn process(&mut self, buffer: &mut AudioBuffer) { let samples = buffer.samples(); let (inputs, mut outputs) = buffer.split(); for sample_idx in 0..samples { let left_in = inputs.get(0)[sample_idx]; let right_in = inputs.get(1)[sample_idx]; outputs.get_mut(0)[sample_idx] = left_in * 0.5; outputs.get_mut(1)[sample_idx] = right_in * 0.5; } } fn get_parameter_object(&mut self) -> Arc { Arc::clone(&self.params) as Arc } } struct GainEffect { params: Arc, } struct GainEffectParameters { amplitude: AtomicFloat, } impl Default for GainEffectParameters { fn default() -> Self { GainEffectParameters { amplitude: AtomicFloat::new(0.5), } } } ``` -------------------------------- ### Audio Processing Methods Source: https://context7.com/rustaudio/vst-rs/llms.txt The process() method is the core of the audio engine, handling real-time sample manipulation via AudioBuffer. It supports both f32 and f64 precision. ```APIDOC ## process(buffer: &mut AudioBuffer) ### Description Processes incoming audio samples. The method iterates over input and output channels to apply effects or transformations. ### Parameters - **buffer** (AudioBuffer) - Required - The audio buffer containing input and output channel data. ### Implementation Details - Use `buffer.zip()` to iterate over input and output channel pairs. - Use `buffer.split()` to gain manual control over input and output buffers. - For high-precision audio, implement `process_f64()` and set `f64_precision: true` in the plugin `Info` struct. ``` -------------------------------- ### Process MIDI Events in Rust VST Source: https://context7.com/rustaudio/vst-rs/llms.txt Implement the `process_events` method to handle incoming MIDI messages like note on/off and control changes. Use `can_do()` to advertise MIDI support to the host. ```rust use vst::prelude::*; use vst::api; use std::f64::consts::PI; struct SineSynth { sample_rate: f64, time: f64, note: Option, velocity: f32, } impl Plugin for SineSynth { fn new(_host: HostCallback) -> Self { SineSynth { sample_rate: 44100.0, time: 0.0, note: None, velocity: 0.0, } } fn get_info(&self) -> Info { Info { name: "Sine Synth".to_string(), unique_id: 6667, category: Category::Synth, inputs: 0, outputs: 2, ..Info::default() } } fn set_sample_rate(&mut self, rate: f32) { self.sample_rate = rate as f64; } fn process_events(&mut self, events: &api::Events) { for event in events.events() { match event { Event::Midi(ev) => { // ev.data[0]: status byte (note on=144, note off=128) // ev.data[1]: note number (0-127) // ev.data[2]: velocity (0-127) match ev.data[0] { 128 => { // Note Off if self.note == Some(ev.data[1]) { self.note = None; } } 144 => { // Note On if ev.data[2] > 0 { self.note = Some(ev.data[1]); self.velocity = ev.data[2] as f32 / 127.0; self.time = 0.0; } else { // Note on with velocity 0 = note off self.note = None; } } _ => (), } } _ => (), } } } fn process(&mut self, buffer: &mut AudioBuffer) { let samples = buffer.samples(); let (_, mut outputs) = buffer.split(); let per_sample = 1.0 / self.sample_rate; for i in 0..samples { let sample = if let Some(note) = self.note { let freq = 440.0 * 2.0_f64.powf((note as f64 - 69.0) / 12.0); let signal = (self.time * freq * 2.0 * PI).sin() as f32; self.time += per_sample; signal * self.velocity } else { 0.0 }; outputs[0][i] = sample; outputs[1][i] = sample; } } fn can_do(&self, can_do: CanDo) -> Supported { match can_do { CanDo::ReceiveMidiEvent => Supported::Yes, CanDo::ReceiveEvents => Supported::Yes, _ => Supported::Maybe, } } } plugin_main!(SineSynth); ``` -------------------------------- ### Implement Plugin Trait for VST Source: https://context7.com/rustaudio/vst-rs/llms.txt Implement the `Plugin` trait to define a VST plugin's behavior. Requires `new()` for instantiation and `get_info()` for plugin metadata. Ensure `plugin_main!` macro is used for exporting. ```rust #[macro_use] extern crate vst; use vst::prelude::*; use std::sync::Arc; struct MyPlugin { host: HostCallback, params: Arc, } struct MyPluginParameters { amplitude: AtomicFloat, } impl Default for MyPluginParameters { fn default() -> Self { MyPluginParameters { amplitude: AtomicFloat::new(0.5), } } } impl Plugin for MyPlugin { fn new(host: HostCallback) -> Self { MyPlugin { host, params: Arc::new(MyPluginParameters::default()), } } fn get_info(&self) -> Info { Info { name: "My Plugin".to_string(), vendor: "My Company".to_string(), unique_id: 123456789, // Register with Steinberg for unique ID version: 1, inputs: 2, outputs: 2, parameters: 1, category: Category::Effect, ..Default::default() } } fn init(&mut self) { // Called when plugin is fully initialized } fn set_sample_rate(&mut self, rate: f32) { // Called when host changes sample rate (e.g., 44100.0, 48000.0) } fn set_block_size(&mut self, size: i64) { // Called when host changes buffer size (e.g., 256, 512, 1024) } fn resume(&mut self) { // Called when plugin transitions to resumed/active state } fn suspend(&mut self) { // Called when plugin transitions to suspended state } fn get_parameter_object(&mut self) -> Arc { Arc::clone(&self.params) as Arc } } plugin_main!(MyPlugin); // Required macro to export VST entry points ``` -------------------------------- ### Send MIDI Events to Host in Rust VST Source: https://context7.com/rustaudio/vst-rs/llms.txt Use `SendEventBuffer` within the `process` method to send MIDI events back to the host. This is useful for MIDI effects, arpeggiators, or sequencers. ```rust use vst::prelude::*; use vst::api; use vst::host::Host; #[derive(Default)] struct MidiThrough { host: HostCallback, recv_buffer: SendEventBuffer, send_buffer: SendEventBuffer, } impl Plugin for MidiThrough { fn new(host: HostCallback) -> Self { MidiThrough { host, recv_buffer: SendEventBuffer::new(1024), send_buffer: SendEventBuffer::new(1024), } } fn get_info(&self) -> Info { Info { name: "MIDI Through".to_string(), unique_id: 7357001, ..Default::default() } } fn process_events(&mut self, events: &api::Events) { // Store incoming events for later processing self.recv_buffer.store_events(events.events()); } fn process(&mut self, buffer: &mut AudioBuffer) { // Pass through audio for (input, output) in buffer.zip() { for (in_sample, out_sample) in input.iter().zip(output) { *out_sample = *in_sample; } } // Forward MIDI events to host self.send_buffer.send_events( self.recv_buffer.events().events(), &mut self.host ); self.recv_buffer.clear(); } fn can_do(&self, can_do: CanDo) -> Supported { use Supported::*; use CanDo::*; match can_do { SendEvents | SendMidiEvent | ReceiveEvents | ReceiveMidiEvent => Yes, _ => No, } } } plugin_main!(MidiThrough); ``` -------------------------------- ### Efficient Parameter Updates with ParameterTransfer Source: https://context7.com/rustaudio/vst-rs/llms.txt Use ParameterTransfer to synchronize parameters between UI and audio threads. It allows iterating only over changed parameters, optimizing updates and smoothing. ```rust use std::sync::Arc; use vst::prelude::*; const PARAMETER_COUNT: usize = 100; struct MyPluginParameters { host: HostCallback, transfer: ParameterTransfer, } struct MyPlugin { params: Arc, smoothed_values: Vec, targets: Vec, } impl PluginParameters for MyPluginParameters { fn set_parameter(&self, index: i32, value: f32) { self.transfer.set_parameter(index as usize, value); } fn get_parameter(&self, index: i32) -> f32 { self.transfer.get_parameter(index as usize) } fn get_parameter_name(&self, index: i32) -> String { format!("Param {}", index) } } impl Plugin for MyPlugin { fn new(host: HostCallback) -> Self { MyPlugin { params: Arc::new(MyPluginParameters { host, transfer: ParameterTransfer::new(PARAMETER_COUNT), }), smoothed_values: vec![0.0; PARAMETER_COUNT], targets: vec![0.0; PARAMETER_COUNT], } } fn get_info(&self) -> Info { Info { name: "Parameter Transfer Demo".to_string(), unique_id: 0x500007, parameters: PARAMETER_COUNT as i32, ..Info::default() } } fn process(&mut self, buffer: &mut AudioBuffer) { // Only iterate over parameters that changed (efficient!) for (index, value) in self.params.transfer.iterate(true) { self.targets[index] = value; } // Apply smoothing to changed parameters let smooth_factor = 0.01; for i in 0..PARAMETER_COUNT { self.smoothed_values[i] += (self.targets[i] - self.smoothed_values[i]) * smooth_factor; } // Use smoothed values in audio processing... for (input, output) in buffer.zip() { for (in_sample, out_sample) in input.iter().zip(output) { *out_sample = *in_sample * self.smoothed_values[0]; } } } fn get_parameter_object(&mut self) -> Arc { Arc::clone(&self.params) as Arc } } plugin_main!(MyPlugin); ``` -------------------------------- ### Implement Editor Trait for Plugin GUI Source: https://context7.com/rustaudio/vst-rs/llms.txt Implement the `Editor` trait to define the GUI for your VST plugin. This involves handling window creation, size, and user interactions. ```rust use vst::prelude::*; use vst::editor::{Editor, KeyCode, KnobMode, Rect}; use std::os::raw::c_void; use std::sync::Arc; struct MyEditor { params: Arc, is_open: bool, width: i32, height: i32, } impl Editor for MyEditor { fn size(&self) -> (i32, i32) { (self.width, self.height) } fn position(&self) -> (i32, i32) { (0, 0) } fn open(&mut self, parent: *mut c_void) -> bool { // parent is platform-specific: // - Windows: HWND // - macOS (64-bit): NSView* // - X11: u32 window ID // Initialize your GUI framework here, parented to `parent` // e.g., create a window, attach to parent, show controls self.is_open = true; true // Return true if opened successfully } fn close(&mut self) { // Clean up GUI resources self.is_open = false; } fn is_open(&mut self) -> bool { self.is_open } fn idle(&mut self) { // Called periodically by host for UI updates // Refresh parameter displays, animations, etc. } fn key_down(&mut self, keycode: KeyCode) -> bool { // Handle keyboard input // keycode.character: ASCII character // keycode.key: Key enum (e.g., Key::Space, Key::Enter) // keycode.modifier: Modifier flags (Shift, Alt, Ctrl, Command) false // Return true if key was handled } fn key_up(&mut self, keycode: KeyCode) -> bool { false } fn set_knob_mode(&mut self, mode: KnobMode) -> bool { // mode: Circular, CircularRelative, or Linear false // Return true if supported } } ``` ```rust struct MyPlugin { params: Arc, editor: Option, } impl Plugin for MyPlugin { fn new(_host: HostCallback) -> Self { let params = Arc::new(MyPluginParameters::default()); MyPlugin { editor: Some(MyEditor { params: Arc::clone(¶ms), is_open: false, width: 400, height: 300, }), params, } } fn get_info(&self) -> Info { Info { name: "Plugin with Editor".to_string(), unique_id: 11111, ..Default::default() } } fn get_editor(&mut self) -> Option> { // Return editor only on first call self.editor.take().map(|e| Box::new(e) as Box) } fn get_parameter_object(&mut self) -> Arc { Arc::clone(&self.params) as Arc } } plugin_main!(MyPlugin); ``` -------------------------------- ### Thread-Safe Parameter Storage with AtomicFloat Source: https://context7.com/rustaudio/vst-rs/llms.txt Utilize AtomicFloat for simple, thread-safe storage of floating-point VST parameters. It uses relaxed atomic ordering, suitable when exact synchronization is not critical. ```rust use vst::util::AtomicFloat; struct Parameters { gain: AtomicFloat, pan: AtomicFloat, frequency: AtomicFloat, } impl Parameters { fn new() -> Self { Parameters { gain: AtomicFloat::new(0.5), // Default 50% pan: AtomicFloat::new(0.5), // Center frequency: AtomicFloat::new(1000.0), } } } // In audio thread (read) fn process_sample(params: &Parameters, input: f32) -> f32 { let gain = params.gain.get(); // Lock-free read input * gain } // In UI thread (write) fn on_slider_change(params: &Parameters, new_value: f32) { params.gain.set(new_value); // Lock-free write } // AtomicFloat also implements From/Into for f32 let atomic = AtomicFloat::from(0.75); let value: f32 = atomic.into(); // Default implementation returns 0.0 let default_param = AtomicFloat::default(); assert_eq!(default_param.get(), 0.0); ``` -------------------------------- ### Implement PluginParameters for Thread-Safe Access Source: https://context7.com/rustaudio/vst-rs/llms.txt Defines a parameter structure using AtomicFloat and implements the PluginParameters trait for normalized parameter interaction. ```rust use std::sync::Arc; use vst::prelude::*; struct MyPluginParameters { gain: AtomicFloat, mix: AtomicFloat, bypass: AtomicFloat, } impl Default for MyPluginParameters { fn default() -> Self { MyPluginParameters { gain: AtomicFloat::new(0.5), // 50% gain mix: AtomicFloat::new(1.0), // 100% wet bypass: AtomicFloat::new(0.0), // Not bypassed } } } impl PluginParameters for MyPluginParameters { fn get_parameter(&self, index: i32) -> f32 { match index { 0 => self.gain.get(), 1 => self.mix.get(), 2 => self.bypass.get(), _ => 0.0, } } fn set_parameter(&self, index: i32, value: f32) { match index { 0 => self.gain.set(value), 1 => self.mix.set(value), 2 => self.bypass.set(value), _ => (), } } fn get_parameter_name(&self, index: i32) -> String { match index { 0 => "Gain".to_string(), 1 => "Mix".to_string(), 2 => "Bypass".to_string(), _ => "".to_string(), } } fn get_parameter_text(&self, index: i32) -> String { match index { 0 => format!("{:.1} dB", (self.gain.get() * 2.0 - 1.0) * 24.0), 1 => format!("{:.0}%", self.mix.get() * 100.0), 2 => if self.bypass.get() > 0.5 { "On".to_string() } else { "Off".to_string() }, _ => "".to_string(), } } fn get_parameter_label(&self, index: i32) -> String { match index { 0 => "dB".to_string(), 1 => "%".to_string(), 2 => "".to_string(), _ => "".to_string(), } } fn can_be_automated(&self, index: i32) -> bool { index < 3 // All parameters can be automated } } ``` -------------------------------- ### Linux Parent Pointer Conversion Source: https://github.com/rustaudio/vst-rs/wiki/VST2-Guis On Linux, the parent pointer is an unsigned 32-bit integer representing the window ID. This conversion is specific to X11/XCB. ```rust parent as u32 ``` -------------------------------- ### Windows Parent Pointer Conversion Source: https://github.com/rustaudio/vst-rs/wiki/VST2-Guis On Windows, the parent pointer is a HWND__ and can be cast directly. Ensure you are using the winapi crate for this type. ```rust parent as *mut winapi::HWND__ ``` -------------------------------- ### PluginParameters Trait Source: https://context7.com/rustaudio/vst-rs/llms.txt The PluginParameters trait provides a thread-safe interface for hosts to access and modify plugin parameters, supporting automation and UI synchronization. ```APIDOC ## PluginParameters Trait ### Description Defines methods for parameter management. Implementations must use thread-safe interior mutability (e.g., AtomicFloat) to handle concurrent access from audio and UI threads. ### Methods - **get_parameter(index: i32) -> f32**: Returns the normalized value (0.0-1.0) of a parameter. - **set_parameter(index: i32, value: f32)**: Updates a parameter value. - **get_parameter_name(index: i32) -> String**: Returns the display name of the parameter. - **get_parameter_text(index: i32) -> String**: Returns the formatted string representation of the parameter value. - **can_be_automated(index: i32) -> bool**: Returns whether the parameter supports host automation. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.