-
-
Save Fredemus/af69619c3fbc79b91ce7471d7372ed7e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_use] | |
extern crate vst; | |
extern crate time; | |
#[macro_use] | |
extern crate log; | |
extern crate simplelog; | |
// use std::fs::File; | |
use std::sync::{Arc, Mutex}; | |
use vst::buffer::AudioBuffer; | |
use vst::plugin::{Category, Info, Plugin, PluginParameters}; | |
use vst::util::AtomicFloat; | |
extern crate winit; | |
use winit::{ | |
event::{Event, WindowEvent}, | |
event_loop::{ControlFlow, EventLoop}, | |
// monitor::{MonitorHandle, VideoMode}, | |
window::{Window, WindowBuilder}, | |
}; | |
// stuff i added for the gui support | |
use std::ffi::c_void; | |
use vst::editor::Editor as VstEditor; | |
use vst::plugin::HostCallback; | |
pub struct Editor { | |
pub params: Arc<Parameters>, | |
// what to do with window? | |
window: Option<Window>, | |
host_callback: Arc<Mutex<HostCallback>>, | |
} | |
impl Editor { | |
pub fn new(host_callback: Arc<Mutex<HostCallback>>, params: Arc<Parameters>) -> Self { | |
Self { | |
params, | |
window: None, | |
host_callback, | |
} | |
} | |
} | |
impl VstEditor for Editor { | |
fn size(&self) -> (i32, i32) { | |
info!("Editor::size()"); | |
if self.window.is_some() { | |
info!("returning window size"); | |
// return self.window.as_ref().unwrap().get_size() | |
//FIXME: figure out how to get window size in winit | |
let size = self.window.as_ref().unwrap().inner_size(); | |
return (size.width as i32, size.height as i32); | |
} else { | |
info!("Returning default size"); | |
// 1024 or 0? | |
return (1024, 1024); | |
} | |
// (DEFAULT_WIDTH, DEFAULT_HEIGHT) | |
} | |
// Typically ignored by DAWs. Just return (0, 0). | |
fn position(&self) -> (i32, i32) { | |
(0, 0) | |
} | |
fn close(&mut self) { | |
info!("Editor::close()"); | |
self.window = None | |
} | |
fn open(&mut self, parent: *mut c_void) -> bool { | |
info!("Editor::open()"); | |
let event_loop_1 = EventLoop::new(); | |
unsafe { | |
// transmuting parent pointer to a HWND. Only safe on windows | |
let parent_view = | |
std::mem::transmute::<*mut c_void, winapi::shared::windef::HWND>(parent); | |
// setting up a WindowBuilder using the pointer to the parent viewer. should be fine on windows | |
self.window = Some(winit::platform::windows::WindowBuilderExtWindows::with_parent_window( | |
WindowBuilder::new(), | |
parent_view, | |
) | |
.build(&event_loop_1) | |
.unwrap()); | |
// let event_loop = winit::platform_impl::windows::EventLoopEmbedded::new_dpi_unaware_any_thread() | |
let event_loop_2: winit::platform::windows::EventLoopEmbedded<()> = | |
winit::platform::windows::EventLoopExtWindows::run_embedded( | |
event_loop_1, | |
move |event, _, control_flow| { | |
*control_flow = ControlFlow::Wait; | |
match event { | |
Event::WindowEvent { | |
event: WindowEvent::CloseRequested, | |
window_id, | |
} if window_id == self.window.as_ref().unwrap().id() => *control_flow = ControlFlow::Exit, | |
_ => (), | |
} | |
}, | |
); | |
} | |
info!("success"); | |
// success | |
true | |
} | |
fn is_open(&mut self) -> bool { | |
info!("Editor::is_open()"); | |
if self.window.is_some() { | |
return true; | |
} | |
false | |
} | |
} | |
// fn ui_builder() -> impl Widget<u32> { | |
// info!("ui_builder()"); | |
// let text = | |
// LocalizedString::new("hello-counter").with_arg("count", |data: &u32, _env| (*data).into()); | |
// let label = Label::new(text); | |
// let button = Button::new("increment", |_ctx, data, _env| *data += 1); | |
// Flex::column() | |
// .with_child(Align::centered(Padding::new(5.0, label)), 1.0) | |
// .with_child(Padding::new(5.0, button), 1.0) | |
// } | |
/// Simple Gain Effect. | |
/// Note that this does not use a proper scale for sound and shouldn't be used in | |
/// a production amplification effect! This is purely for demonstration purposes, | |
/// as well as to keep things simple as this is meant to be a starting point for | |
/// any effect. | |
// #[derive(Default)] | |
struct GainEffect { | |
host: HostCallback, | |
// Store a handle to the plugin's parameter object. | |
params: Arc<Parameters>, | |
editor: Option<Box<dyn VstEditor>>, | |
} | |
impl Default for GainEffect { | |
fn default() -> Self { | |
Plugin::new(HostCallback::default()) | |
} | |
} | |
/// The plugin's parameter object contains the values of parameters that can be | |
/// adjusted from the host. If we were creating an effect that didn't allow the | |
/// user to modify it at runtime or have any controls, we could omit this part. | |
/// | |
/// The parameters object is shared between the processing and GUI threads. | |
/// For this reason, all mutable state in the object has to be represented | |
/// through thread-safe interior mutability. The easiest way to achieve this | |
/// is to store the parameters in atomic containers. | |
pub struct Parameters { | |
// The plugin's state consists of a single parameter: amplitude. | |
amplitude: AtomicFloat, | |
} | |
impl Default for Parameters { | |
fn default() -> Parameters { | |
Parameters { | |
amplitude: AtomicFloat::new(0.5), | |
} | |
} | |
} | |
// All plugins using `vst` also need to implement the `Plugin` trait. Here, we | |
// define functions that give necessary info to our host. | |
impl Plugin for GainEffect { | |
fn new(host: HostCallback) -> Self { | |
// setting up some basic logging | |
env_logger::init(); | |
// Create the plugin itself | |
let host_callback = Arc::new(Mutex::new(host)); | |
let params = Arc::new(Parameters::default()); | |
Self { | |
host, | |
params: params.clone(), | |
editor: Some(Box::new(Editor::new(host_callback, params.clone()))), | |
} | |
} | |
fn get_info(&self) -> Info { | |
Info { | |
name: "Gain Effect in Rust with gui".to_string(), | |
vendor: "Rust DSP".to_string(), | |
unique_id: 243723073, | |
version: 1, | |
inputs: 2, | |
outputs: 2, | |
// This `parameters` bit is important; without it, none of our | |
// parameters will be shown! | |
parameters: 1, | |
category: Category::Effect, | |
..Default::default() | |
} | |
} | |
fn init(&mut self) { | |
info!("init()"); | |
info!("host VST version: {}", self.host.vst_version()); | |
} | |
// Here is where the bulk of our audio processing code goes. | |
fn process(&mut self, buffer: &mut AudioBuffer<f32>) { | |
// Read the amplitude from the parameter object | |
let amplitude = self.params.amplitude.get(); | |
// First, we destructure our audio buffer into an arbitrary number of | |
// input and output buffers. Usually, we'll be dealing with stereo (2 of each) | |
// but that might change. | |
for (input_buffer, output_buffer) in buffer.zip() { | |
// Next, we'll loop through each individual sample so we can apply the amplitude | |
// value to it. | |
for (input_sample, output_sample) in input_buffer.iter().zip(output_buffer) { | |
*output_sample = *input_sample * amplitude; | |
} | |
} | |
} | |
// Return the parameter object. This method can be omitted if the | |
// plugin has no parameters. | |
fn get_parameter_object(&mut self) -> Arc<dyn PluginParameters> { | |
Arc::clone(&self.params) as Arc<dyn PluginParameters> | |
} | |
// fn get_editor(&mut self) -> Option<&mut vst::editor::Editor> { | |
fn get_editor(&mut self) -> Option<Box<dyn vst::editor::Editor>> { | |
info!("Plugin::get_editor()"); | |
Some(self.editor.take().unwrap()) | |
} | |
} | |
impl PluginParameters for Parameters { | |
// the `get_parameter` function reads the value of a parameter. | |
fn get_parameter(&self, index: i32) -> f32 { | |
match index { | |
0 => self.amplitude.get(), | |
_ => 0.0, | |
} | |
} | |
// the `set_parameter` function sets the value of a parameter. | |
fn set_parameter(&self, index: i32, val: f32) { | |
#[allow(clippy::single_match)] | |
match index { | |
0 => self.amplitude.set(val), | |
_ => (), | |
} | |
} | |
// This is what will display underneath our control. We can | |
// format it into a string that makes the most since. | |
fn get_parameter_text(&self, index: i32) -> String { | |
match index { | |
0 => format!("{:.2}", (self.amplitude.get() - 0.5) * 2f32), | |
_ => "".to_string(), | |
} | |
} | |
// This shows the control's name. | |
fn get_parameter_name(&self, index: i32) -> String { | |
match index { | |
0 => "Amplitude", | |
_ => "", | |
} | |
.to_string() | |
} | |
} | |
// This part is important! Without it, our plugin won't work. | |
plugin_main!(GainEffect); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment