Skip to content

Commit

Permalink
Introduce synth audio gain config (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
PolyMeilex authored Jun 7, 2024
1 parent f66a54f commit 0506cc0
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 64 deletions.
8 changes: 8 additions & 0 deletions neothesia-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub struct Config {
#[serde(default = "default_playback_offset")]
pub playback_offset: f32,

#[serde(default = "default_audio_gain")]
pub audio_gain: f32,

#[serde(default = "default_vertical_guidelines")]
pub vertical_guidelines: bool,

Expand Down Expand Up @@ -70,6 +73,7 @@ impl Config {
speed_multiplier: default_speed_multiplier(),
animation_speed: default_animation_speed(),
playback_offset: default_playback_offset(),
audio_gain: default_audio_gain(),
vertical_guidelines: default_vertical_guidelines(),
horizontal_guidelines: default_horizontal_guidelines(),
color_schema: default_color_schema(),
Expand Down Expand Up @@ -122,6 +126,10 @@ fn default_playback_offset() -> f32 {
0.0
}

fn default_audio_gain() -> f32 {
0.2
}

fn default_vertical_guidelines() -> bool {
false
}
Expand Down
8 changes: 4 additions & 4 deletions neothesia/src/output_manager/midi_backend.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{cell::RefCell, collections::HashSet, rc::Rc};

use crate::output_manager::{OutputConnectionProxy, OutputDescriptor};
use crate::output_manager::OutputDescriptor;

use midi_file::midly::{
self,
Expand Down Expand Up @@ -62,8 +62,8 @@ impl MidiBackend {
}
}

impl OutputConnectionProxy for MidiOutputConnection {
fn midi_event(&self, channel: u4, message: midly::MidiMessage) {
impl MidiOutputConnection {
pub fn midi_event(&self, channel: u4, message: midly::MidiMessage) {
let inner = &mut *self.inner.borrow_mut();
match message {
midly::MidiMessage::NoteOff { key, .. } => {
Expand All @@ -82,7 +82,7 @@ impl OutputConnectionProxy for MidiOutputConnection {
inner.conn.send(&inner.buf).ok();
}

fn stop_all(&self) {
pub fn stop_all(&self) {
let inner = &mut *self.inner.borrow_mut();
for note in std::mem::take(&mut inner.active_notes).iter() {
inner.buf.clear();
Expand Down
12 changes: 7 additions & 5 deletions neothesia/src/output_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ impl Display for OutputDescriptor {
}
}

trait OutputConnectionProxy {
fn midi_event(&self, channel: u4, msg: MidiMessage);
fn stop_all(&self);
}

#[derive(Clone)]
pub enum OutputConnection {
Midi(midi_backend::MidiOutputConnection),
Expand All @@ -55,6 +50,13 @@ impl OutputConnection {
OutputConnection::DummyOutput => {}
}
}
pub fn set_gain(&self, gain: f32) {
match self {
#[cfg(feature = "synth")]
OutputConnection::Synth(b) => b.set_gain(gain),
_ => {}
}
}
pub fn stop_all(&self) {
match self {
OutputConnection::Midi(b) => b.stop_all(),
Expand Down
135 changes: 82 additions & 53 deletions neothesia/src/output_manager/synth_backend.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{error::Error, path::Path, rc::Rc, sync::mpsc::Receiver};

use crate::output_manager::{OutputConnectionProxy, OutputDescriptor};
use crate::output_manager::OutputDescriptor;

use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use midi_file::midly::{self, num::u4};
Expand All @@ -14,6 +14,7 @@ pub struct SynthBackend {

stream_config: cpal::StreamConfig,
sample_format: cpal::SampleFormat,
gain: f32,
}

impl SynthBackend {
Expand All @@ -35,19 +36,20 @@ impl SynthBackend {

stream_config,
sample_format,
gain: 0.2,
})
}

fn run<T: cpal::SizedSample + cpal::FromSample<f32>>(
&self,
rx: Receiver<oxisynth::MidiEvent>,
rx: Receiver<SynthEvent>,
path: &Path,
) -> cpal::Stream {
#[cfg(all(feature = "fluid-synth", not(feature = "oxi-synth")))]
let mut next_value = fluidsynth_adapter(self, rx, path);

#[cfg(all(feature = "oxi-synth", not(feature = "fluid-synth")))]
let mut next_value = oxisynth_adapter(self, rx, path);
let mut next_value = oxisynth_adapter(self, rx, path, self.gain);

let err_fn = |err| eprintln!("an error occurred on stream: {}", err);

Expand Down Expand Up @@ -81,7 +83,7 @@ impl SynthBackend {
}

pub fn new_output_connection(&mut self, path: &Path) -> SynthOutputConnection {
let (tx, rx) = std::sync::mpsc::channel::<oxisynth::MidiEvent>();
let (tx, rx) = std::sync::mpsc::channel::<SynthEvent>();
let stream = match self.sample_format {
cpal::SampleFormat::I8 => self.run::<i8>(rx, path),
cpal::SampleFormat::I16 => self.run::<i16>(rx, path),
Expand Down Expand Up @@ -109,25 +111,38 @@ impl SynthBackend {
}
}

enum SynthEvent {
SetGain(f32),
Midi(oxisynth::MidiEvent),
}

#[derive(Clone)]
pub struct SynthOutputConnection {
_stream: Rc<cpal::Stream>,
tx: std::sync::mpsc::Sender<oxisynth::MidiEvent>,
tx: std::sync::mpsc::Sender<SynthEvent>,
}

impl OutputConnectionProxy for SynthOutputConnection {
fn midi_event(&self, channel: u4, msg: midly::MidiMessage) {
impl SynthOutputConnection {
pub fn midi_event(&self, channel: u4, msg: midly::MidiMessage) {
let event = libmidi_to_oxisynth_event(channel, msg);
self.tx.send(event).ok();
self.tx.send(SynthEvent::Midi(event)).ok();
}

pub fn set_gain(&self, gain: f32) {
self.tx.send(SynthEvent::SetGain(gain)).ok();
}

fn stop_all(&self) {
pub fn stop_all(&self) {
for channel in 0..16 {
self.tx
.send(oxisynth::MidiEvent::AllNotesOff { channel })
.send(SynthEvent::Midi(oxisynth::MidiEvent::AllNotesOff {
channel,
}))
.ok();
self.tx
.send(oxisynth::MidiEvent::AllSoundOff { channel })
.send(SynthEvent::Midi(oxisynth::MidiEvent::AllSoundOff {
channel,
}))
.ok();
}
}
Expand Down Expand Up @@ -175,13 +190,15 @@ fn libmidi_to_oxisynth_event(channel: u4, message: midly::MidiMessage) -> oxisyn
#[cfg(all(feature = "oxi-synth", not(feature = "fluid-synth")))]
fn oxisynth_adapter(
this: &SynthBackend,
rx: Receiver<oxisynth::MidiEvent>,
rx: Receiver<SynthEvent>,
path: &Path,
gain: f32,
) -> impl FnMut() -> (f32, f32) {
let sample_rate = this.stream_config.sample_rate.0 as f32;

let mut synth = oxisynth::Synth::new(oxisynth::SynthDescriptor {
sample_rate,
gain,
..Default::default()
})
.unwrap();
Expand All @@ -196,7 +213,14 @@ fn oxisynth_adapter(
let (l, r) = synth.read_next();

if let Ok(event) = rx.try_recv() {
synth.send_event(event).ok();
match event {
SynthEvent::SetGain(gain) => {
synth.set_gain(gain);
}
SynthEvent::Midi(event) => {
synth.send_event(event).ok();
}
}
}

(l, r)
Expand All @@ -206,7 +230,7 @@ fn oxisynth_adapter(
#[cfg(all(feature = "fluid-synth", not(feature = "oxi-synth")))]
fn fluidsynth_adapter(
this: &SynthBackend,
rx: Receiver<oxisynth::MidiEvent>,
rx: Receiver<SynthEvent>,
path: &Path,
) -> impl FnMut() -> (f32, f32) {
use fluidlite::{IsSettings, Settings};
Expand Down Expand Up @@ -242,46 +266,51 @@ fn fluidsynth_adapter(

if let Ok(e) = rx.try_recv() {
match e {
oxisynth::MidiEvent::NoteOn { channel, key, vel } => {
synth.note_on(channel as u32, key as u32, vel as u32).ok();
}
oxisynth::MidiEvent::NoteOff { channel, key } => {
synth.note_off(channel as u32, key as u32).ok();
}
oxisynth::MidiEvent::PitchBend { channel, value } => {
synth.pitch_bend(channel as u32, value as u32).ok();
}
oxisynth::MidiEvent::ProgramChange {
channel,
program_id,
} => {
synth.program_change(channel as u32, program_id as u32).ok();
}
oxisynth::MidiEvent::ChannelPressure { channel, value } => {
synth.channel_pressure(channel as u32, value as u32).ok();
SynthEvent::SetGain(_g) => {
// TODO
}
oxisynth::MidiEvent::PolyphonicKeyPressure {
channel,
key,
value,
} => {
synth
.key_pressure(channel as u32, key as u32, value as u32)
.ok();
}
oxisynth::MidiEvent::SystemReset => {
synth.system_reset().ok();
}
oxisynth::MidiEvent::ControlChange {
channel,
ctrl,
value,
} => {
synth.cc(channel as u32, ctrl as u32, value as u32).ok();
}
// TODO: Where are those for fluidsynth?
oxisynth::MidiEvent::AllNotesOff { .. } => {}
oxisynth::MidiEvent::AllSoundOff { .. } => {}
SynthEvent::Midi(e) => match e {
oxisynth::MidiEvent::NoteOn { channel, key, vel } => {
synth.note_on(channel as u32, key as u32, vel as u32).ok();
}
oxisynth::MidiEvent::NoteOff { channel, key } => {
synth.note_off(channel as u32, key as u32).ok();
}
oxisynth::MidiEvent::PitchBend { channel, value } => {
synth.pitch_bend(channel as u32, value as u32).ok();
}
oxisynth::MidiEvent::ProgramChange {
channel,
program_id,
} => {
synth.program_change(channel as u32, program_id as u32).ok();
}
oxisynth::MidiEvent::ChannelPressure { channel, value } => {
synth.channel_pressure(channel as u32, value as u32).ok();
}
oxisynth::MidiEvent::PolyphonicKeyPressure {
channel,
key,
value,
} => {
synth
.key_pressure(channel as u32, key as u32, value as u32)
.ok();
}
oxisynth::MidiEvent::SystemReset => {
synth.system_reset().ok();
}
oxisynth::MidiEvent::ControlChange {
channel,
ctrl,
value,
} => {
synth.cc(channel as u32, ctrl as u32, value as u32).ok();
}
// TODO: Where are those for fluidsynth?
oxisynth::MidiEvent::AllNotesOff { .. } => {}
oxisynth::MidiEvent::AllSoundOff { .. } => {}
},
}
}

Expand Down
5 changes: 4 additions & 1 deletion neothesia/src/scene/menu_scene/iced_menu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,10 @@ fn play(data: &Data, ctx: &mut Context) {
o => o,
};

ctx.output_manager.connect(out)
ctx.output_manager.connect(out);
ctx.output_manager
.connection()
.set_gain(ctx.config.audio_gain);
}

if let Some(port) = data.selected_input.clone() {
Expand Down
21 changes: 20 additions & 1 deletion neothesia/src/scene/menu_scene/iced_menu/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub enum Event {

RangeStart(RangeUpdateKind),
RangeEnd(RangeUpdateKind),
AudioGain(RangeUpdateKind),
GoBack,
}

Expand Down Expand Up @@ -104,6 +105,18 @@ impl Page for SettingsPage {
}
}
},
Event::AudioGain(kind) => {
match kind {
RangeUpdateKind::Add => {
ctx.config.audio_gain += 0.1;
}
RangeUpdateKind::Sub => {
ctx.config.audio_gain = (ctx.config.audio_gain - 0.1).max(0.0);
}
}

ctx.config.audio_gain = (ctx.config.audio_gain * 10.0).round() / 10.0;
}
Event::GoBack => {
return PageMessage::go_back();
}
Expand Down Expand Up @@ -225,11 +238,17 @@ fn output_group<'a>(data: &'a Data, ctx: &Context) -> Element<'a, Event> {

row
});
let synth_gain_settings = is_synth.then(|| {
ActionRow::new()
.title("Audio Gain")
.suffix(counter(ctx.config.audio_gain, Event::AudioGain))
});

PreferencesGroup::new()
.title("Output")
.push(output_settings)
.push_maybe(synth_settings)
.push_maybe(synth_gain_settings)
.build()
}

Expand All @@ -245,7 +264,7 @@ fn input_group<'a>(data: &'a Data, _ctx: &Context) -> Element<'a, Event> {
.build()
}

fn counter<'a>(value: u8, msg: fn(RangeUpdateKind) -> Event) -> Element<'a, Event> {
fn counter<'a>(value: impl ToString, msg: fn(RangeUpdateKind) -> Event) -> Element<'a, Event> {
let label = centered_text(value);
let sub = button(centered_text("-").width(30).height(30))
.padding(0)
Expand Down

0 comments on commit 0506cc0

Please sign in to comment.