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

Add ability to modify buffer window and ignore range in realtime synth #105

Merged
merged 7 commits into from
Oct 10, 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
10 changes: 10 additions & 0 deletions clib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::result_unit_err)]
#![allow(clippy::too_long_first_doc_paragraph)]

pub mod consts;
pub mod group;
Expand Down Expand Up @@ -45,3 +46,12 @@ pub extern "C" fn XSynth_GenDefault_StreamParams() -> XSynth_StreamParams {
audio_channels: XSYNTH_AUDIO_CHANNELS_STEREO,
}
}

/// A helper struct to specify a range of bytes.
/// - start: The start of the range
/// - end: The end of the range
#[repr(C)]
pub struct XSynth_ByteRange {
pub start: u8,
pub end: u8,
}
44 changes: 32 additions & 12 deletions clib/src/realtime.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{handles::*, utils::*, XSynth_StreamParams};
use crate::{handles::*, utils::*, XSynth_ByteRange, XSynth_StreamParams};
use xsynth_core::{
channel::{ChannelConfigEvent, ChannelEvent, ChannelInitOptions},
channel_group::SynthEvent,
Expand All @@ -17,14 +17,14 @@ use xsynth_realtime::{RealtimeSynth, XSynthRealtimeConfig};
/// usually causing clicking but improving performance.
/// - render_window_ms: The length of the buffer reader in ms
/// - ignore_range: A range of velocities that will not be played
/// LOBYTE = start (0-127), HIBYTE = end (start-127)
/// (see XSynth_ByteRange)
#[repr(C)]
pub struct XSynth_RealtimeConfig {
pub channels: u32,
pub multithreading: i32,
pub fade_out_killing: bool,
pub render_window_ms: f64,
pub ignore_range: u16,
pub ignore_range: XSynth_ByteRange,
}

/// Generates the default values for the XSynth_RealtimeConfig struct
Expand All @@ -41,7 +41,7 @@ pub extern "C" fn XSynth_GenDefault_RealtimeConfig() -> XSynth_RealtimeConfig {
multithreading: -1,
fade_out_killing: false,
render_window_ms: 10.0,
ignore_range: 0,
ignore_range: XSynth_ByteRange { start: 0, end: 0 },
}
}

Expand Down Expand Up @@ -72,18 +72,12 @@ pub extern "C" fn XSynth_Realtime_Create(config: XSynth_RealtimeConfig) -> XSynt
fade_out_killing: config.fade_out_killing,
};

let ignore_range = {
let low = (config.ignore_range & 255) as u8;
let high = (config.ignore_range >> 8) as u8;
low..=high
};

let options = XSynthRealtimeConfig {
channel_init_options,
render_window_ms: config.render_window_ms,
format: convert_synth_format(config.channels),
multithreading: convert_threadcount(config.multithreading),
ignore_range,
ignore_range: config.ignore_range.start..=config.ignore_range.end,
};

let new = RealtimeSynth::open_with_default_output(options);
Expand Down Expand Up @@ -169,6 +163,32 @@ pub extern "C" fn XSynth_Realtime_SendConfigEventAll(
}
}

/// Sets the length of the buffer reader to the desired value in ms.
///
/// --Parameters--
/// - handle: The handle of the realtime synthesizer instance
/// - render_window_ms: The length of the buffer reader in ms
#[no_mangle]
pub extern "C" fn XSynth_Realtime_SetBuffer(handle: XSynth_RealtimeSynth, render_window_ms: f64) {
handle.as_ref().set_buffer(render_window_ms);
}

/// Sets the range of velocities that will be ignored.
///
/// --Parameters--
/// - handle: The handle of the realtime synthesizer instance
/// - ignore_range: The range. LOBYTE = start (0-127), HIBYTE = end (start-127)
#[no_mangle]
pub extern "C" fn XSynth_Realtime_SetIgnoreRange(
handle: XSynth_RealtimeSynth,
ignore_range: XSynth_ByteRange,
) {
handle
.as_mut()
.get_sender_mut()
.set_ignore_range(ignore_range.start..=ignore_range.end);
}

/// Sets a list of soundfonts to be used in the specified realtime synth
/// instance. To load a new soundfont, see the XSynth_Soundfont_LoadNew
/// function.
Expand Down Expand Up @@ -248,7 +268,7 @@ pub extern "C" fn XSynth_Realtime_GetStats(handle: XSynth_RealtimeSynth) -> XSyn
/// - handle: The handle of the realtime synthesizer instance
#[no_mangle]
pub extern "C" fn XSynth_Realtime_Reset(handle: XSynth_RealtimeSynth) {
handle.as_ref().get_senders().reset_synth();
handle.as_mut().get_sender_mut().reset_synth();
}

/// Drops the specified realtime synth instance.
Expand Down
2 changes: 1 addition & 1 deletion core/src/effects/limiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl VolumeLimiter {
&'a mut self,
samples: T,
) -> VolumeLimiterIter<'a, 'b, T> {
impl<'a, 'b, T: 'b + Iterator<Item = f32>> Iterator for VolumeLimiterIter<'a, 'b, T> {
impl<'b, T: 'b + Iterator<Item = f32>> Iterator for VolumeLimiterIter<'_, 'b, T> {
type Item = f32;

fn next(&mut self) -> Option<Self::Item> {
Expand Down
3 changes: 2 additions & 1 deletion kdmapi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(non_snake_case)]
#![allow(static_mut_refs)]

use hotwatch::{Event, EventKind, Hotwatch};
use std::{
Expand Down Expand Up @@ -59,7 +60,7 @@ pub extern "C" fn InitializeKDMAPIStream() -> i32 {
let sflist = Config::<SFList>::new().load().unwrap();

let realtime_synth = RealtimeSynth::open_with_default_output(config.get_synth_config());
let mut sender = realtime_synth.get_senders();
let mut sender = realtime_synth.get_sender_ref().clone();
let params = realtime_synth.stream_params();

sender.send_event(SynthEvent::AllChannels(ChannelEvent::Config(
Expand Down
2 changes: 1 addition & 1 deletion realtime/examples/midi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn main() {
};

let synth = RealtimeSynth::open_with_all_defaults();
let mut sender = synth.get_senders();
let mut sender = synth.get_sender_ref().clone();

let params = synth.stream_params();

Expand Down
28 changes: 15 additions & 13 deletions realtime/src/event_senders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,11 @@ impl EventSender {
return;
}

let in_ignore_range = self.ignore_range.contains(vel);

let nps = self.nps.calculate_nps();
if should_send_for_vel_and_nps(*vel, nps, self.max_nps.read()) && !in_ignore_range {

if should_send_for_vel_and_nps(*vel, nps, self.max_nps.read())
&& !self.ignore_range.contains(vel)
{
self.sender.send(ChannelEvent::Audio(event)).ok();
self.nps.add_note();
} else {
Expand Down Expand Up @@ -179,16 +180,9 @@ impl EventSender {
self.sender.send(ChannelEvent::Config(event)).ok();
}

// pub fn send(&mut self, event: ChannelEvent) {
// match event {
// ChannelEvent::Audio(event) => {
// self.send_audio(event);
// }
// ChannelEvent::Config(event) => {
// self.send_config(event);
// }
// }
// }
pub fn set_ignore_range(&mut self, ignore_range: RangeInclusive<u8>) {
self.ignore_range = ignore_range;
}
}

impl Clone for EventSender {
Expand Down Expand Up @@ -333,4 +327,12 @@ impl RealtimeEventSender {
ChannelAudioEvent::ResetControl,
)));
}

/// Changes the range of velocities that will be ignored for the
/// specific sender instance.
pub fn set_ignore_range(&mut self, ignore_range: RangeInclusive<u8>) {
for sender in self.senders.iter_mut() {
sender.set_ignore_range(ignore_range.clone());
}
}
}
32 changes: 28 additions & 4 deletions realtime/src/realtime_synth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl RealtimeSynth {
let buffered = Arc::new(Mutex::new(BufferedRenderer::new(
render,
stream_params,
(sample_rate as f64 * config.render_window_ms / 1000.0) as usize,
calculate_render_size(sample_rate, config.render_window_ms),
)));

fn build_stream<T: SizedSample + ConvertSample>(
Expand Down Expand Up @@ -289,13 +289,25 @@ impl RealtimeSynth {
data.event_senders.send_event(event);
}

/// Returns the event sender of the realtime synthesizer.
/// Returns a reference to the event sender of the realtime synthesizer.
/// This can be used to clone the sender so it can be passed in threads.
///
/// See the `RealtimeEventSender` documentation for more information
/// on how to use.
pub fn get_senders(&self) -> RealtimeEventSender {
pub fn get_sender_ref(&self) -> &RealtimeEventSender {
let data = self.data.as_ref().unwrap();
data.event_senders.clone()
&data.event_senders
}

/// Returns a mutable reference the event sender of the realtime synthesizer.
/// This can be used to modify its parameters (eg. ignore range).
/// Please note that each clone will store its own distinct parameters.
///
/// See the `RealtimeEventSender` documentation for more information
/// on how to use.
pub fn get_sender_mut(&mut self) -> &mut RealtimeEventSender {
let data = self.data.as_mut().unwrap();
&mut data.event_senders
}

/// Returns the statistics reader of the realtime synthesizer.
Expand Down Expand Up @@ -325,6 +337,14 @@ impl RealtimeSynth {
let data = self.data.as_mut().unwrap();
data.stream.play()
}

/// Changes the length of the buffer reader.
pub fn set_buffer(&self, render_window_ms: f64) {
let data = self.data.as_ref().unwrap();
let sample_rate = self.stream_params.sample_rate;
let size = calculate_render_size(sample_rate, render_window_ms);
data.buffered_renderer.lock().unwrap().set_render_size(size);
}
}

impl Drop for RealtimeSynth {
Expand Down Expand Up @@ -359,3 +379,7 @@ impl ConvertSample for u16 {
((s * u16::MAX as f32) as i32 + i16::MIN as i32) as u16
}
}

fn calculate_render_size(sample_rate: u32, buffer_ms: f64) -> usize {
(sample_rate as f64 * buffer_ms / 1000.0) as usize
}
4 changes: 2 additions & 2 deletions soundfonts/src/sfz/grammar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ bnf! {
impl<'a> OpcodeValue<'a> {
pub fn as_string(&self) -> Cow<'a, str> {
if self.rest.is_empty() {
return Cow::Borrowed(self.first.value.text.text);
Cow::Borrowed(self.first.value.text.text)
} else {
let mut result = String::from(self.first.value.text.text);
for part in self.rest.iter() {
result.push_str(part.value.text.text);
}
return Cow::Owned(result);
Cow::Owned(result)
}
}
}
Expand Down
Loading