Skip to content

Instantly share code, notes, and snippets.

@Fredemus
Last active March 15, 2020 22:47
Show Gist options
  • Save Fredemus/af69619c3fbc79b91ce7471d7372ed7e to your computer and use it in GitHub Desktop.
Save Fredemus/af69619c3fbc79b91ce7471d7372ed7e to your computer and use it in GitHub Desktop.
#[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