Skip to content

Instantly share code, notes, and snippets.

@emily33901
Created October 11, 2022 20:18
Show Gist options
  • Save emily33901/b3710e1ec3dfdfdd9cb5812823a4ffb7 to your computer and use it in GitHub Desktop.
Save emily33901/b3710e1ec3dfdfdd9cb5812823a4ffb7 to your computer and use it in GitHub Desktop.
use std::io::{self, Read};
use std::sync::Once;
use std::time::{SystemTime, UNIX_EPOCH};
use bincode;
use log::{error, info, trace, LevelFilter};
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
#[cfg(windows)]
use simple_logging;
use fpsdk::host::{self, Event, GetName, Host};
use fpsdk::plugin::message;
use fpsdk::plugin::{self, Info, InfoBuilder, Plugin, StateReader, StateWriter};
use fpsdk::{create_plugin, AsRawPtr, MidiMessage, ProcessParamFlags, TimeFormat, ValuePtr};
static ONCE: Once = Once::new();
const LOG_PATH: &str = "S:/emilydotgg-random.log";
#[derive(Debug)]
struct Simple {
host: Host,
tag: plugin::Tag,
state: State,
}
#[derive(Debug, Default, Deserialize, Serialize)]
struct State {
seed: u64,
speed: u64,
tick: u64,
test_val: u64,
}
impl Plugin for Simple {
fn new(host: Host, tag: plugin::Tag) -> Self {
init_log();
info!("init plugin with tag {}", tag);
Self {
host,
tag,
state: Default::default(),
}
}
fn info(&self) -> Info {
info!("plugin {} will return info", self.tag);
InfoBuilder::new_effect("emilydotgg-random", "Random", 3)
.want_new_tick()
.with_out_ctrls(100)
.build()
}
fn save_state(&mut self, writer: StateWriter) {
let now = SystemTime::now();
let time = now.duration_since(UNIX_EPOCH).expect("").as_secs();
match bincode::serialize_into(writer, &self.state) {
Ok(_) => info!("state {:?} saved", self.state),
Err(e) => error!("error serializing state {}", e),
}
}
fn load_state(&mut self, mut reader: StateReader) {
let mut buf = [0; std::mem::size_of::<State>()];
reader
.read(&mut buf)
.and_then(|_| {
bincode::deserialize::<State>(&buf).map_err(|e| {
io::Error::new(
io::ErrorKind::Other,
format!("error deserializing value {}", e),
)
})
})
.and_then(|value| {
self.state = value;
Ok(info!("read state {:?}", self.state))
})
.unwrap_or_else(|e| error!("error reading value from state {}", e));
}
fn on_message(&mut self, message: host::Message) -> Box<dyn AsRawPtr> {
// self.host.on_message(
// self.tag,
// message::DebugLogMsg(format!("{} got message from host: {:?}", self.tag, message)),
// );
if let host::Message::SetEnabled(enabled) = message {
self.on_set_enabled(enabled, message);
}
// self.host.midi_out(self.tag, MidiMessage {
// status: 0x90,
// data1: 60,
// data2: 100,
// port: 2,
// });
Box::new(0)
}
fn name_of(&self, message: GetName) -> String {
info!("{} host asks name of {:?}", self.tag, message);
match message {
GetName::OutCtrl(index) => format!("Control {index}"),
_ => "What?".into(),
}
}
fn process_event(&mut self, event: Event) {
info!("{} host sends event {:?}", self.tag, event);
}
fn tick(&mut self) {
self.state.tick += 1;
if self.state.speed == 0 {
self.state.speed = 1
}
if self.state.tick % self.state.speed == 0 {
let mut rng = thread_rng();
for i in 0..100 {
let random_value = rng.gen_range(0..0x1000);
let random_value = random_value << 32;
self.host.on_controller(self.tag, i, random_value);
}
}
}
fn idle(&mut self) {
trace!("{} idle", self.tag);
}
// looks like it doesn't work in SDK
fn loop_in(&mut self, message: ValuePtr) {
trace!("{} loop_in", message.get::<String>());
}
fn process_param(
&mut self,
index: usize,
value: ValuePtr,
flags: ProcessParamFlags,
) -> Box<dyn AsRawPtr> {
info!(
"{} process param: index {}, value {}, flags {:?}",
self.tag,
index,
value.get::<f32>(),
flags
);
if flags.contains(ProcessParamFlags::FROM_MIDI | ProcessParamFlags::UPDATE_VALUE) {
// will work if assigned to itself
// Scale speed into a more appropriate range
// it will be 0 - 65535 coming in and we want it to be less
let speed = value.get::<u16>() as f64;
let speed = (speed * 200.0) / 65535.0;
self.state.speed = speed as u64;
}
Box::new(0)
}
fn midi_in(&mut self, message: MidiMessage) {
trace!("receive MIDI message {:?}", message);
}
}
impl Simple {
fn on_set_enabled(&mut self, enabled: bool, message: host::Message) {
self.log_selection();
self.say_hello_hint();
if enabled {
// probably do something here
}
}
fn log_selection(&mut self) {
let selection = self
.host
.on_message(self.tag, message::GetSelTime(TimeFormat::Beats));
self.host.on_message(
self.tag,
message::DebugLogMsg(format!(
"current selection or full song range is: {:?}",
selection
)),
);
}
fn say_hello_hint(&mut self) {
self.host.on_hint(self.tag, "^c Hello".to_string());
}
}
fn init_log() {
ONCE.call_once(|| {
_init_log();
info!("init log");
});
}
#[cfg(windows)]
fn _init_log() {
simple_logging::log_to_file(LOG_PATH, LevelFilter::Warn).unwrap();
}
create_plugin!(Simple);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment