Created
October 11, 2022 20:18
-
-
Save emily33901/b3710e1ec3dfdfdd9cb5812823a4ffb7 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
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