Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Groundwork for making general plugins #33

Merged
merged 1 commit into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading