Skip to content

Commit

Permalink
Renaming of from Plugin -> EffectPlugin before creating GenericPlugin
Browse files Browse the repository at this point in the history
Also add ctrl-c handler
  • Loading branch information
SGauvin committed Jun 23, 2024
1 parent 9677840 commit f2d4ff3
Show file tree
Hide file tree
Showing 16 changed files with 406 additions and 209 deletions.
6 changes: 3 additions & 3 deletions effects/rust/raindrop/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rand::Rng;
use std::sync::Mutex;
use turbo_plugin::{make_plugin, Color, Plugin, VTable};
use turbo_plugin::{make_native_effect_plugin, effect_plugin::NativeEffectPlugin, Color};

#[derive(Clone, Copy, Debug)]
pub struct RaindropSettings {
Expand Down Expand Up @@ -31,7 +31,7 @@ impl Raindrop {
}
}

impl Plugin for Raindrop {
impl NativeEffectPlugin for Raindrop {
fn name(&self) -> *const std::ffi::c_char {
static NAME: &[u8] = b"Raindrop\0";
static CSTR_NAME: &std::ffi::CStr =
Expand Down Expand Up @@ -118,4 +118,4 @@ impl Plugin for Raindrop {
fn unload() {}
}

make_plugin!(Raindrop, Raindrop::new());
make_native_effect_plugin!(Raindrop, Raindrop::new());
1 change: 1 addition & 0 deletions turbo_audio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ bytemuck = { version = "1.14.0", features = ["derive"] }
chrono = "0.4.19"
clap = { version = "4.4.8", features = ["derive"] }
cpal = { version = "0.15.2" }
ctrlc = "3.4.4"
dasp = "0.11.0"
dasp_ring_buffer = "0.11.0"
dasp_signal = "0.11.0"
Expand Down
6 changes: 6 additions & 0 deletions turbo_audio/src/audio/audio_processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ pub struct FftResult {
fft_resolution: f32,
}

impl Drop for FftResult {
fn drop(&mut self) {
log::debug!("Dropping FftResult");
}
}

impl FftResult {
pub fn new(raw_bins: Vec<f32>, fft_resolution: f32) -> Self {
Self {
Expand Down
2 changes: 1 addition & 1 deletion turbo_audio/src/audio/pipewire_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ impl PipewireState {
.unwrap_or_default()
.to_string();

log::info!("Pipewire node: {name}");
log::debug!("Pipewire node: {name}");

self.nodes.insert(
node.id,
Expand Down
2 changes: 2 additions & 0 deletions turbo_audio/src/connections/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ pub struct TcpConnection {
should_quit: Arc<Mutex<bool>>,
}

#[allow(dead_code)]
enum TcpConnectionError {
ConnectionFailed(ConnectionAttemptError),
UnableToReconnect(ConnectionAttemptError, std::io::Error),
}

#[allow(dead_code)]
enum ConnectionAttemptError {
Unreachable(std::net::SocketAddr),
ConfigurationFailed(std::io::Error),
Expand Down
2 changes: 1 addition & 1 deletion turbo_audio/src/controller.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
audio::audio_processing::AudioSignalProcessor,
effects::{lua::LuaEffectsManager, native::NativeEffectsManager},
hot_reloader::{HotReloader, WatchablePath},
plugins::effects::{lua::LuaEffectsManager, native::NativeEffectsManager},
resources::ledstrip::LedStrip,
Connection, Effect, EffectSettings,
};
Expand Down
25 changes: 23 additions & 2 deletions turbo_audio/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ mod audio;
mod config_parser;
mod connections;
mod controller;
mod effects;
mod hot_reloader;
mod plugins;
mod resources;

use crate::hot_reloader::{HotReloader, WatchablePath};
Expand All @@ -14,8 +14,11 @@ use clap::Parser;
use config_parser::{ConnectionConfigType, EffectConfigType, SettingsConfigType, TurboAudioConfig};
use connections::{tcp::TcpConnection, usb::UsbConnection, Connection};
use controller::Controller;
use effects::{lua::LuaEffectSettings, native::NativeEffectSettings, Effect, EffectSettings};
use plugins::effects::{
lua::LuaEffectSettings, native::NativeEffectSettings, Effect, EffectSettings,
};
use std::path::PathBuf;
use std::sync::atomic::{self, AtomicBool};
use std::{fs::File, path::Path};

#[derive(Parser, Debug)]
Expand All @@ -33,6 +36,8 @@ enum RunLoopError {
StartPipewireStream,
}

pub static SHOULD_QUIT: AtomicBool = AtomicBool::new(false);

fn run_loop(
mut audio_processor: AudioSignalProcessor,
mut controller: Controller,
Expand All @@ -52,6 +57,11 @@ fn run_loop(
let duration_per_tick: chrono::Duration = chrono::Duration::seconds(1) / 60;
let mut last_loop_start = std::time::Instant::now();
loop {
if SHOULD_QUIT.load(atomic::Ordering::Relaxed) {
log::info!("Quitting");
break Ok(());
}

lag = lag
.checked_add(&chrono::Duration::from_std(last_loop_start.elapsed()).unwrap())
.unwrap();
Expand Down Expand Up @@ -156,6 +166,13 @@ fn load_controller(

fn main() -> Result<(), RunLoopError> {
env_logger::init();

ctrlc::set_handler(|| {
log::info!("Received ctrl-c, requesting to quit");
SHOULD_QUIT.store(true, atomic::Ordering::Relaxed);
})
.expect("Couldn't set the CTRL-C handler");

let Args { settings_file } = Args::parse();

loop {
Expand Down Expand Up @@ -193,6 +210,10 @@ fn main() -> Result<(), RunLoopError> {

log::info!("Starting run loop.");
run_loop(audio_processor, controller)?;
if SHOULD_QUIT.load(atomic::Ordering::Relaxed) {
log::info!("Quitting");
break Ok(());
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
}
61 changes: 61 additions & 0 deletions turbo_audio/src/plugins/audio_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use turbo_plugin::audio_api::AudioApi;

use crate::audio::audio_processing::FftResult;
use std::{
boxed::Box,
sync::{Arc, RwLock},
};

pub fn create_audio_api(fft_result: Arc<RwLock<FftResult>>) -> AudioApi {
extern "C" fn get_average_amplitude(
instance: *const std::ffi::c_void,
lower_frequency: std::ffi::c_float,
upper_frequency: std::ffi::c_float,
) -> std::ffi::c_float {
let fft_result = unsafe { &*(instance as *const Arc<RwLock<FftResult>>) };
fft_result
.read()
.unwrap()
.get_average_amplitude(lower_frequency, upper_frequency)
.unwrap_or_else(|| {
log::error!("Invalid frequencies: {lower_frequency} & {upper_frequency}");
0.0f32
})
}

extern "C" fn get_frequency_amplitude(
instance: *const std::ffi::c_void,
frequency: std::ffi::c_float,
) -> std::ffi::c_float {
let fft_result = unsafe { &*(instance as *const Arc<RwLock<FftResult>>) };
fft_result
.read()
.unwrap()
.get_frequency_amplitude(frequency)
.unwrap_or_else(|| {
log::error!("Invalid frequency: {frequency}");
0.0f32
})
}

extern "C" fn get_max_frequency(instance: *const std::ffi::c_void) -> std::ffi::c_float {
let fft_result = unsafe { &*(instance as *const Arc<RwLock<FftResult>>) };
fft_result.read().unwrap().get_max_frequency()
}

extern "C" fn free(instance: *const std::ffi::c_void) {
unsafe {
drop(Box::from_raw(instance as *mut Arc<RwLock<FftResult>>));
}
}

let fft_result = Box::new(fft_result);

AudioApi::new(
Box::into_raw(fft_result) as *const _,
get_average_amplitude,
get_frequency_amplitude,
get_max_frequency,
free,
)
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::audio::audio_processing::{AudioSignalProcessor, FftResult};
use crate::{
audio::audio_processing::{AudioSignalProcessor, FftResult},
plugins::audio_api::create_audio_api,
};
use libloading::os::unix::{RTLD_LOCAL, RTLD_NOW};
use std::{
collections::HashMap,
Expand All @@ -7,7 +10,7 @@ use std::{
sync::{Arc, RwLock},
};
use thiserror::Error;
use turbo_plugin::{Color, VTable};
use turbo_plugin::{effect_plugin::NativeEffectPluginVTable, Color};

use super::Effect;

Expand All @@ -30,7 +33,7 @@ pub struct NativeEffectsManager {
#[derive(Debug)]
struct Library {
library: Option<libloading::Library>,
vtable: *const VTable,
vtable: *const NativeEffectPluginVTable,
}

unsafe impl Send for Library {}
Expand All @@ -53,6 +56,7 @@ impl NativeEffectsManager {
fft_result: audio_processor.fft_result.clone(),
}
}

pub fn create_effect(&mut self, effect_path: impl AsRef<Path>) -> Result<Effect> {
let path = std::fs::canonicalize(&effect_path).unwrap();

Expand Down Expand Up @@ -112,52 +116,10 @@ impl NativeEffectsManager {
let vtable_fn =
library.get::<extern "C" fn() -> *const std::ffi::c_void>(b"_plugin_vtable")?;

let vtable = vtable_fn() as *const turbo_plugin::VTable;

extern "C" fn get_average_amplitude(
instance: *const std::ffi::c_void,
lower_frequency: std::ffi::c_float,
upper_frequency: std::ffi::c_float,
) -> std::ffi::c_float {
let fft_result = unsafe { &*(instance as *const Arc<RwLock<FftResult>>) };
fft_result
.read()
.unwrap()
.get_average_amplitude(lower_frequency, upper_frequency)
.unwrap_or_else(|| {
log::error!("Invalid frequencies: {lower_frequency} & {upper_frequency}");
0.0f32
})
}

extern "C" fn get_frequency_amplitude(
instance: *const std::ffi::c_void,
frequency: std::ffi::c_float,
) -> std::ffi::c_float {
let fft_result = unsafe { &*(instance as *const Arc<RwLock<FftResult>>) };
fft_result
.read()
.unwrap()
.get_frequency_amplitude(frequency)
.unwrap_or_else(|| {
log::error!("Invalid frequency: {frequency}");
0.0f32
})
}

extern "C" fn get_max_frequency(
instance: *const std::ffi::c_void,
) -> std::ffi::c_float {
let fft_result = unsafe { &*(instance as *const Arc<RwLock<FftResult>>) };
fft_result.read().unwrap().get_max_frequency()
}
let vtable =
vtable_fn() as *const turbo_plugin::effect_plugin::NativeEffectPluginVTable;

let audio_api = turbo_plugin::AudioApi {
instance: fft_result as *const _ as *const _,
get_average_amplitude,
get_frequency_amplitude,
get_max_frequency,
};
let audio_api = create_audio_api(fft_result.clone());

((*vtable).load)(audio_api);

Expand Down
2 changes: 2 additions & 0 deletions turbo_audio/src/plugins/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod audio_api;
pub mod effects;
97 changes: 97 additions & 0 deletions turbo_plugin/src/audio_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use std::{
process::abort,
sync::{Mutex, OnceLock},
};

#[derive(Copy, Clone)]
#[repr(C)]
pub struct AudioApi {
instance: *const std::ffi::c_void,
get_average_amplitude: extern "C" fn(
*const std::ffi::c_void,
std::ffi::c_float,
std::ffi::c_float,
) -> std::ffi::c_float,
get_frequency_amplitude:
extern "C" fn(*const std::ffi::c_void, std::ffi::c_float) -> std::ffi::c_float,
get_max_frequency: extern "C" fn(*const std::ffi::c_void) -> std::ffi::c_float,
free: extern "C" fn(*const std::ffi::c_void),
}

unsafe impl Send for AudioApi {}
unsafe impl Sync for AudioApi {}

impl AudioApi {
pub fn new(
instance: *const std::ffi::c_void,

get_average_amplitude: extern "C" fn(
*const std::ffi::c_void,
std::ffi::c_float,
std::ffi::c_float,
) -> std::ffi::c_float,
get_frequency_amplitude: extern "C" fn(
*const std::ffi::c_void,
std::ffi::c_float,
) -> std::ffi::c_float,
get_max_frequency: extern "C" fn(*const std::ffi::c_void) -> std::ffi::c_float,
free: extern "C" fn(*const std::ffi::c_void),
) -> Self {
Self {
instance,
get_average_amplitude,
get_frequency_amplitude,
get_max_frequency,
free,
}
}
}

static AUDIO_API_INSTANCE: OnceLock<Mutex<AudioApi>> = OnceLock::new();

pub fn on_load(audio_api: AudioApi) {
let mut api = AUDIO_API_INSTANCE
.get_or_init(|| Mutex::new(audio_api))
.lock()
.unwrap();
*api = audio_api;
}

pub fn get_average_amplitude(lower_freq: f32, upper_freq: f32) -> f32 {
let Some(api) = AUDIO_API_INSTANCE.get() else {
eprintln!("PLUGIN ERROR: Couldn't get the audio api pointer");
abort();
};
let api = api.lock().unwrap();
(api.get_average_amplitude)(api.instance, lower_freq, upper_freq)
}

pub fn get_frequency_amplitude(frequency: f32) -> f32 {
let Some(api) = AUDIO_API_INSTANCE.get() else {
eprintln!("PLUGIN ERROR: Couldn't get the audio api pointer");
abort();
};
let api = api.lock().unwrap();

(api.get_frequency_amplitude)(api.instance, frequency)
}

pub fn get_max_frequency() -> std::ffi::c_float {
let Some(api) = AUDIO_API_INSTANCE.get() else {
eprintln!("PLUGIN ERROR: Couldn't get the audio api pointer");
abort();
};
let api = api.lock().unwrap();

(api.get_max_frequency)(api.instance)
}

pub fn free() {
let Some(api) = AUDIO_API_INSTANCE.get() else {
eprintln!("PLUGIN ERROR: Couldn't get the audio api pointer");
abort();
};
let api = api.lock().unwrap();

(api.free)(api.instance)
}
Loading

0 comments on commit f2d4ff3

Please sign in to comment.