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

Introduce synth audio gain config #179

Merged
merged 1 commit into from
Jun 7, 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
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
Loading