Skip to content

Commit

Permalink
Remove Rc<RefCell<>> from OutputManager
Browse files Browse the repository at this point in the history
  • Loading branch information
PolyMeilex committed Jan 10, 2024
1 parent eaf16ef commit 7656a53
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 64 deletions.
45 changes: 27 additions & 18 deletions neothesia/src/output_manager/midi_backend.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashSet;
use std::{cell::RefCell, collections::HashSet, rc::Rc};

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

use midi_file::midly::{
self,
Expand All @@ -14,18 +14,25 @@ struct ActiveNote {
channel: u4,
}

pub struct MidiOutputConnection {
struct MidiOutputConnectionInner {
conn: midi_io::MidiOutputConnection,
active_notes: HashSet<ActiveNote>,
buf: Vec<u8>,
}

#[derive(Clone)]
pub struct MidiOutputConnection {
inner: Rc<RefCell<MidiOutputConnectionInner>>,
}

impl From<midi_io::MidiOutputConnection> for MidiOutputConnection {
fn from(conn: midi_io::MidiOutputConnection) -> Self {
Self {
conn,
active_notes: Default::default(),
buf: Vec::with_capacity(8),
inner: Rc::new(RefCell::new(MidiOutputConnectionInner {
conn,
active_notes: Default::default(),
buf: Vec::with_capacity(8),
})),
}
}
}
Expand Down Expand Up @@ -55,38 +62,40 @@ impl MidiBackend {
}
}

impl OutputConnection for MidiOutputConnection {
fn midi_event(&mut self, channel: u4, message: midly::MidiMessage) {
impl OutputConnectionProxy for MidiOutputConnection {
fn midi_event(&self, channel: u4, message: midly::MidiMessage) {
let inner = &mut *self.inner.borrow_mut();
match message {
midly::MidiMessage::NoteOff { key, .. } => {
self.active_notes.remove(&ActiveNote { key, channel });
inner.active_notes.remove(&ActiveNote { key, channel });
}
midly::MidiMessage::NoteOn { key, .. } => {
self.active_notes.insert(ActiveNote { key, channel });
inner.active_notes.insert(ActiveNote { key, channel });
}
_ => {}
}

self.buf.clear();
inner.buf.clear();
let msg = midly::live::LiveEvent::Midi { channel, message };
msg.write(&mut self.buf).unwrap();
msg.write(&mut inner.buf).unwrap();

self.conn.send(&self.buf).ok();
inner.conn.send(&inner.buf).ok();
}

fn stop_all(&mut self) {
for note in std::mem::take(&mut self.active_notes).iter() {
self.buf.clear();
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();
let msg = LiveEvent::Midi {
channel: note.channel,
message: midly::MidiMessage::NoteOff {
key: note.key,
vel: u7::new(0),
},
};
msg.write(&mut self.buf).unwrap();
msg.write(&mut inner.buf).unwrap();

self.conn.send(&self.buf).ok();
inner.conn.send(&inner.buf).ok();
}
}
}
Expand Down
63 changes: 42 additions & 21 deletions neothesia/src/output_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,44 @@ impl Display for OutputDescriptor {
}
}

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

struct DummyOutput {}
impl OutputConnection for DummyOutput {
fn midi_event(&mut self, _channel: u4, _msg: MidiMessage) {}
fn stop_all(&mut self) {}
#[derive(Clone)]
pub enum OutputConnection {
Midi(midi_backend::MidiOutputConnection),
#[cfg(feature = "synth")]
Synth(synth_backend::SynthOutputConnection),
DummyOutput,
}

impl OutputConnection {
pub fn midi_event(&self, channel: u4, msg: MidiMessage) {
match self {
OutputConnection::Midi(b) => b.midi_event(channel, msg),
#[cfg(feature = "synth")]
OutputConnection::Synth(b) => b.midi_event(channel, msg),
OutputConnection::DummyOutput => {}
}
}
pub fn stop_all(&self) {
match self {
OutputConnection::Midi(b) => b.stop_all(),
#[cfg(feature = "synth")]
OutputConnection::Synth(b) => b.stop_all(),
OutputConnection::DummyOutput => {}
}
}
}

pub struct OutputManager {
#[cfg(feature = "synth")]
synth_backend: Option<SynthBackend>,
midi_backend: Option<MidiBackend>,

output_connection: (OutputDescriptor, Box<dyn OutputConnection>),
output_connection: (OutputDescriptor, OutputConnection),
}

impl Default for OutputManager {
Expand Down Expand Up @@ -82,7 +103,7 @@ impl OutputManager {
synth_backend,
midi_backend,

output_connection: (OutputDescriptor::DummyOutput, Box::new(DummyOutput {})),
output_connection: (OutputDescriptor::DummyOutput, OutputConnection::DummyOutput),
}
}

Expand All @@ -109,33 +130,33 @@ impl OutputManager {
OutputDescriptor::Synth(ref font) => {
if let Some(ref mut synth) = self.synth_backend {
if let Some(font) = font.clone() {
self.output_connection =
(desc, Box::new(synth.new_output_connection(&font)));
self.output_connection = (
desc,
OutputConnection::Synth(synth.new_output_connection(&font)),
);
} else if let Some(path) = crate::utils::resources::default_sf2() {
if path.exists() {
self.output_connection =
(desc, Box::new(synth.new_output_connection(&path)));
self.output_connection = (
desc,
OutputConnection::Synth(synth.new_output_connection(&path)),
);
}
}
}
}
OutputDescriptor::MidiOut(ref info) => {
if let Some(conn) = MidiBackend::new_output_connection(info) {
self.output_connection = (desc, Box::new(conn));
self.output_connection = (desc, OutputConnection::Midi(conn));
}
}
OutputDescriptor::DummyOutput => {
self.output_connection = (desc, Box::new(DummyOutput {}));
self.output_connection = (desc, OutputConnection::DummyOutput);
}
}
}
}

pub fn midi_event(&mut self, channel: u4, msg: MidiMessage) {
self.output_connection.1.midi_event(channel, msg);
}

pub fn stop_all(&mut self) {
self.output_connection.1.stop_all();
pub fn connection(&self) -> &OutputConnection {
&self.output_connection.1
}
}
20 changes: 12 additions & 8 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, sync::mpsc::Receiver};
use std::{error::Error, path::Path, rc::Rc, sync::mpsc::Receiver};

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

use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use midi_file::midly::{self, num::u4};
Expand Down Expand Up @@ -82,7 +82,7 @@ impl SynthBackend {

pub fn new_output_connection(&mut self, path: &Path) -> SynthOutputConnection {
let (tx, rx) = std::sync::mpsc::channel::<oxisynth::MidiEvent>();
let _stream = match self.sample_format {
let stream = match self.sample_format {
cpal::SampleFormat::I8 => self.run::<i8>(rx, path),
cpal::SampleFormat::I16 => self.run::<i16>(rx, path),
cpal::SampleFormat::I32 => self.run::<i32>(rx, path),
Expand All @@ -98,26 +98,30 @@ impl SynthBackend {
sample_format => unimplemented!("Unsupported sample format '{sample_format}'"),
};

SynthOutputConnection { _stream, tx }
SynthOutputConnection {
_stream: Rc::new(stream),
tx,
}
}

pub fn get_outputs(&self) -> Vec<OutputDescriptor> {
vec![OutputDescriptor::Synth(None)]
}
}

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

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

fn stop_all(&mut self) {
fn stop_all(&self) {
for channel in 0..16 {
self.tx
.send(oxisynth::MidiEvent::AllNotesOff { channel })
Expand Down
4 changes: 2 additions & 2 deletions neothesia/src/scene/menu_scene/iced_menu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl Program for AppUi {
o => o,
};

target.output_manager.borrow_mut().connect(out)
target.output_manager.connect(out)
}

if let Some(port) = self.data.selected_input.clone() {
Expand All @@ -113,7 +113,7 @@ impl Program for AppUi {
}
}
Message::Tick => {
self.data.outputs = target.output_manager.borrow().outputs();
self.data.outputs = target.output_manager.outputs();
self.data.inputs = target.input_manager.inputs();

if self.data.selected_output.is_none() {
Expand Down
18 changes: 7 additions & 11 deletions neothesia/src/scene/playing_scene/midi_player.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
use midi_file::midly::{num::u4, MidiMessage};

use crate::{
output_manager::OutputManager,
output_manager::OutputConnection,
song::{PlayerConfig, Song},
target::Target,
};
use std::{
cell::RefCell,
collections::{HashSet, VecDeque},
rc::Rc,
time::{Duration, Instant},
};

pub struct MidiPlayer {
playback: midi_file::PlaybackState,
output_manager: Rc<RefCell<OutputManager>>,
output: OutputConnection,
song: Song,
play_along: PlayAlong,
}
Expand All @@ -30,7 +28,7 @@ impl MidiPlayer {
Duration::from_secs(3),
song.file.tracks.clone(),
),
output_manager: target.output_manager.clone(),
output: target.output_manager.connection().clone(),
play_along: PlayAlong::new(user_keyboard_range),
song,
};
Expand Down Expand Up @@ -58,16 +56,14 @@ impl MidiPlayer {

match config.player {
PlayerConfig::Auto => {
self.output_manager
.borrow_mut()
self.output
.midi_event(u4::new(event.channel), event.message);
}
PlayerConfig::Human => {
// Let's play the sound, in case the user does not want it they can just set
// no-output output in settings
// TODO: Perhaps play on midi-in instead
self.output_manager
.borrow_mut()
self.output
.midi_event(u4::new(event.channel), event.message);
self.play_along
.midi_event(MidiEventSource::File, &event.message);
Expand All @@ -80,7 +76,7 @@ impl MidiPlayer {
}

fn clear(&mut self) {
self.output_manager.borrow_mut().stop_all();
self.output.stop_all();
}
}

Expand Down Expand Up @@ -111,7 +107,7 @@ impl MidiPlayer {

fn send_midi_programs_for_timestamp(&self, time: &Duration) {
for (&channel, &p) in self.song.file.program_track.program_for_timestamp(time) {
self.output_manager.borrow_mut().midi_event(
self.output.midi_event(
u4::new(channel),
midi_file::midly::MidiMessage::ProgramChange {
program: midi_file::midly::num::u7::new(p),
Expand Down
5 changes: 1 addition & 4 deletions neothesia/src/target.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
use std::cell::RefCell;
use std::rc::Rc;

use crate::config::Config;
use crate::input_manager::InputManager;
use crate::render::TextRenderer;
Expand All @@ -24,7 +21,7 @@ pub struct Target {

pub text_renderer: TextRenderer,

pub output_manager: Rc<RefCell<OutputManager>>,
pub output_manager: OutputManager,
pub input_manager: InputManager,
pub song: Option<Song>,
pub config: Config,
Expand Down

0 comments on commit 7656a53

Please sign in to comment.