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

Remove Rc<RefCell<>> from OutputManager #128

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