From 81ad74ec320642b5fb51e3a034d3d360762341ab Mon Sep 17 00:00:00 2001 From: dsgallups Date: Thu, 25 Sep 2025 07:04:07 -0400 Subject: [PATCH 1/6] refactor: remove bevy_resources feature --- Cargo.toml | 49 ++++++++++++++-------------- src/byte/mod.rs | 4 +-- src/channel.rs | 2 +- src/controller.rs | 2 +- src/events/live.rs | 2 +- src/file/format.rs | 2 +- src/file/header.rs | 2 +- src/file/mod.rs | 2 +- src/file/track.rs | 4 +-- src/file_repr/chunk/header.rs | 6 ++-- src/file_repr/meta/smpte_offset.rs | 2 +- src/file_repr/meta/tempo.rs | 2 +- src/file_repr/meta/text.rs | 2 +- src/file_repr/meta/time_signature.rs | 2 +- src/file_repr/smpte.rs | 2 +- src/lib.rs | 6 ++-- src/message/channel/mod.rs | 2 +- src/message/channel/voice.rs | 2 +- src/message/channel/voice_event.rs | 2 +- src/message/mod.rs | 2 +- src/message/system/common.rs | 2 +- src/message/system/exclusive.rs | 2 +- src/message/system/mod.rs | 2 +- src/message/system/realtime.rs | 2 +- src/message/time.rs | 2 +- src/note.rs | 2 +- src/pitch_bend.rs | 2 +- src/program.rs | 2 +- src/song_position_pointer.rs | 2 +- src/velocity.rs | 2 +- 30 files changed, 59 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae575af..fd12fe0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,37 +15,36 @@ exclude = ["assets/*"] [features] default = ["std"] -all = ["std", "bevy_resources", "web"] std = ["thiserror/std", "num_enum/std", "crossbeam-channel/std"] web = ["bevy_platform/web"] -bevy_resources = [ - "bevy", - "dep:midir", - "dep:tinyaudio", - "dep:itertools", - "dep:rustysynth", - "dep:crossbeam-channel", -] -bevy = ["bevy_resources", "bevy/bevy_log", "std", "bevy/bevy_asset"] -debug = ["bevy_resources"] -example = [ - "bevy_resources", - "std", - "bevy/bevy_color", - "bevy/bevy_core_pipeline", - "bevy/bevy_ui", - "bevy/bevy_ui_picking_backend", - "bevy/bevy_winit", - "bevy/bevy_window", - "bevy/x11", - # note that the wasm example freezes since when this feature is not enabled! TODO - "bevy/multi_threaded", -] +# bevy_resources = [ +# "bevy", +# "dep:midir", +# "dep:tinyaudio", +# "dep:itertools", +# "dep:rustysynth", +# "dep:crossbeam-channel", +# ] +bevy = ["dep:bevy", "std", "bevy/bevy_asset"] +# debug = ["bevy_resources"] +# example = [ +# "bevy_resources", +# "std", +# "bevy/bevy_color", +# "bevy/bevy_core_pipeline", +# "bevy/bevy_ui", +# "bevy/bevy_ui_picking_backend", +# "bevy/bevy_winit", +# "bevy/bevy_window", +# "bevy/x11", +# # note that the wasm example freezes since when this feature is not enabled! TODO +# "bevy/multi_threaded", +# ] serde = ["dep:serde"] [dependencies.bevy] -version = "0.17.0-rc.1" +version = "0.17.0-rc" optional = true default-features = false # features = ["async_executor", "bevy_log", "bevy_state", "bevy_asset", "std"] diff --git a/src/byte/mod.rs b/src/byte/mod.rs index 0d36579..432894b 100644 --- a/src/byte/mod.rs +++ b/src/byte/mod.rs @@ -161,7 +161,7 @@ Except for Real-Time messages, new Status bytes will always command a receiver t even if the last message was not completed. "#] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct StatusByte(u8); @@ -206,7 +206,7 @@ impl TryFrom for StatusByte { Data Byte is between [0x00 and 0x7F] "#] #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DataByte(pub(crate) u8); impl Debug for DataByte { diff --git a/src/channel.rs b/src/channel.rs index aacf34e..72b4310 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -18,7 +18,7 @@ use crate::message::{ChannelVoiceMessage, VoiceEvent}; Clone, Copy, PartialEq, Eq, Debug, Hash, IntoPrimitive, TryFromPrimitive, PartialOrd, Ord, )] #[cfg_attr( - feature = "bevy_resources", + feature = "bevy", derive(bevy::prelude::Component, bevy::prelude::Reflect) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/src/controller.rs b/src/controller.rs index a4c6d85..38bdb4c 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -15,7 +15,7 @@ use crate::{prelude::*, reader::ReaderError}; /// in a "coarse" manner. #[non_exhaustive] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Controller { /// 0x00 diff --git a/src/events/live.rs b/src/events/live.rs index 3010a17..6142292 100644 --- a/src/events/live.rs +++ b/src/events/live.rs @@ -44,7 +44,7 @@ An emittable message to/from a streaming MIDI device. There is currently no `StreamReader` type, so this type is most often manually constructed. "] #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub enum LiveEvent<'a> { /// A MIDI voice message associated with a channel ChannelVoice(ChannelVoiceMessage), diff --git a/src/file/format.rs b/src/file/format.rs index 2398dd5..c7e6b74 100644 --- a/src/file/format.rs +++ b/src/file/format.rs @@ -21,7 +21,7 @@ sequence number.*/ Holds the tracks based on the supplied format "#] #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub enum Format<'a> { /// Format 0 SingleMultiChannel(Track<'a>), diff --git a/src/file/header.rs b/src/file/header.rs index 25d113a..c8a4e8d 100644 --- a/src/file/header.rs +++ b/src/file/header.rs @@ -3,7 +3,7 @@ use crate::prelude::*; #[doc = r#" Information about the timing of the MIDI file "#] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct Header { timing: Timing, } diff --git a/src/file/mod.rs b/src/file/mod.rs index 552f606..35a362a 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -24,7 +24,7 @@ use crate::{ #[doc = r#" TODO "#] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct ParsedMidiFile<'a> { header: Header, format: Format<'a>, diff --git a/src/file/track.rs b/src/file/track.rs index 7f57b11..5e87b18 100644 --- a/src/file/track.rs +++ b/src/file/track.rs @@ -11,7 +11,7 @@ use crate::{ A set of track events "#] #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct Track<'a> { info: TrackInfo<'a>, events: Vec>>, @@ -73,7 +73,7 @@ impl<'a> Track<'a> { /// Provides information about the track #[allow(missing_docs)] #[derive(Default, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct TrackInfo<'a> { pub time_signature: TimeSignature, pub name: Option>, diff --git a/src/file_repr/chunk/header.rs b/src/file_repr/chunk/header.rs index 6ae2c89..e69eb22 100644 --- a/src/file_repr/chunk/header.rs +++ b/src/file_repr/chunk/header.rs @@ -113,7 +113,7 @@ impl RawHeaderChunk { /// This is either the number of ticks per quarter note or /// the alternative SMTPE format. See the [`RawHeaderChunk`] docs for more information. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub enum Timing { /// The midi file's delta times are defined using a tick rate per quarter note TicksPerQuarterNote(TicksPerQuarterNote), @@ -124,7 +124,7 @@ pub enum Timing { /// A representation of the `tpqn` timing for a MIDI file #[derive(Debug, Clone, PartialEq, Eq, Copy)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct TicksPerQuarterNote { inner: [u8; 2], } @@ -138,7 +138,7 @@ impl TicksPerQuarterNote { /// A representation of the `smpte` timing for a MIDI file #[derive(Debug, Clone, PartialEq, Eq, Copy)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct SmpteHeader { fps: SmpteFps, ticks_per_frame: DataByte, diff --git a/src/file_repr/meta/smpte_offset.rs b/src/file_repr/meta/smpte_offset.rs index 3666164..672c39e 100644 --- a/src/file_repr/meta/smpte_offset.rs +++ b/src/file_repr/meta/smpte_offset.rs @@ -2,7 +2,7 @@ use crate::{SmpteError, prelude::SmpteFps}; /// A representation of a track's offset from the beginning of a midi file. #[derive(Clone, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct SmpteOffset { /// the track's fps. Note: this should be identical to a file's FPS if /// the file is defined in terms of `smpte` diff --git a/src/file_repr/meta/tempo.rs b/src/file_repr/meta/tempo.rs index 18399f6..f2d7b17 100644 --- a/src/file_repr/meta/tempo.rs +++ b/src/file_repr/meta/tempo.rs @@ -2,7 +2,7 @@ /// /// FF 51 03 tttttt #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct Tempo(u32); impl Default for Tempo { diff --git a/src/file_repr/meta/text.rs b/src/file_repr/meta/text.rs index 4bf9343..4da7943 100644 --- a/src/file_repr/meta/text.rs +++ b/src/file_repr/meta/text.rs @@ -6,7 +6,7 @@ use crate::ParseError; /// Some text, usually identified by a ['MetaMessage'](super::MetaMessage)s #[derive(Clone, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct BytesText<'a> { inner: Cow<'a, [u8]>, } diff --git a/src/file_repr/meta/time_signature.rs b/src/file_repr/meta/time_signature.rs index eeb6c34..192e231 100644 --- a/src/file_repr/meta/time_signature.rs +++ b/src/file_repr/meta/time_signature.rs @@ -26,7 +26,7 @@ That is, 6/8 time (8 is 2 to the 3rd power, so this is 06 03), eight notated 32nd-notes per quarter-note. "#] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub struct TimeSignature([u8; 4]); impl Default for TimeSignature { diff --git a/src/file_repr/smpte.rs b/src/file_repr/smpte.rs index a426d37..6dd9cfb 100644 --- a/src/file_repr/smpte.rs +++ b/src/file_repr/smpte.rs @@ -6,7 +6,7 @@ /// - 29: dropframe 30 (30,000 frames / 1001 seconds) /// - 30: 30fps #[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] pub enum SmpteFps { /// 24 TwentyFour, diff --git a/src/lib.rs b/src/lib.rs index 5831cbb..79ab807 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![warn(missing_docs)] -#![cfg_attr(not(feature = "debug"), warn(clippy::print_stdout))] +#![warn(clippy::print_stdout)] #![doc = include_str!("../README.md")] #![no_std] @@ -47,7 +47,7 @@ pub use song_position_pointer::*; mod target; pub use target::*; -//#[cfg(feature = "bevy_resources")] +//#[cfg(feature = "bevy")] //pub mod bevy; // #[cfg(feature = "bevy")] @@ -76,6 +76,6 @@ pub mod prelude { pub use core::fmt::Display; - //#[cfg(feature = "bevy_resources")] + //#[cfg(feature = "bevy")] //pub use crate::bevy::prelude::*; } diff --git a/src/message/channel/mod.rs b/src/message/channel/mod.rs index 9f89e77..9e571e4 100644 --- a/src/message/channel/mod.rs +++ b/src/message/channel/mod.rs @@ -23,7 +23,7 @@ pub use voice_event::*; #[doc = r#" The set of possible Channel messages "#] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ChannelMessage { /// A channel voice message diff --git a/src/message/channel/voice.rs b/src/message/channel/voice.rs index f0d8a8f..7c342cd 100644 --- a/src/message/channel/voice.rs +++ b/src/message/channel/voice.rs @@ -16,7 +16,7 @@ use crate::{ /// [`LiveEvent::parse`](live/enum.LiveEvent.html#method.parse) method instead and ignore all /// variants except for [`LiveEvent::Midi`](live/enum.LiveEvent.html#variant.Midi). #[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::reflect::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::reflect::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ChannelVoiceMessage { /// The MIDI channel that this event is associated with. diff --git a/src/message/channel/voice_event.rs b/src/message/channel/voice_event.rs index a90b973..84deb78 100644 --- a/src/message/channel/voice_event.rs +++ b/src/message/channel/voice_event.rs @@ -16,7 +16,7 @@ use crate::prelude::*; /// [`LiveEvent::parse`](live/enum.LiveEvent.html#method.parse) method instead and ignore all /// variants except for [`LiveEvent::Midi`](live/enum.LiveEvent.html#variant.Midi). #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum VoiceEvent { /// Modify the value of a MIDI controller. diff --git a/src/message/mod.rs b/src/message/mod.rs index 8df04e7..dbf246c 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -55,7 +55,7 @@ pub use time::*; An enumeration of all possible midi messages "#] #[derive(Debug)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum MidiMessage<'a> { /// A system common message diff --git a/src/message/system/common.rs b/src/message/system/common.rs index 397f906..ad9b4af 100644 --- a/src/message/system/common.rs +++ b/src/message/system/common.rs @@ -6,7 +6,7 @@ use crate::{prelude::*, utils::check_u7}; A System Common Message, used to relay data for ALL receivers, regardless of channel. "#] #[derive(Clone, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SystemCommonMessage<'a> { /// A system-exclusive message. diff --git a/src/message/system/exclusive.rs b/src/message/system/exclusive.rs index 80c753c..fca7bdc 100644 --- a/src/message/system/exclusive.rs +++ b/src/message/system/exclusive.rs @@ -42,7 +42,7 @@ is used for extensions called Universal Exclusive Messages. ``` "#] #[derive(Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SystemExclusiveMessage<'a>(Cow<'a, [u8]>); diff --git a/src/message/system/mod.rs b/src/message/system/mod.rs index 4826b68..1cab8ea 100644 --- a/src/message/system/mod.rs +++ b/src/message/system/mod.rs @@ -25,7 +25,7 @@ pub use exclusive::*; #[doc = r#" The set of possible System messages "#] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SystemMessage<'a> { /// The set of common messages. Only found in MIDI files diff --git a/src/message/system/realtime.rs b/src/message/system/realtime.rs index 04a8c89..7958529 100644 --- a/src/message/system/realtime.rs +++ b/src/message/system/realtime.rs @@ -13,7 +13,7 @@ They are usually time-sensitive, get top priority and can even be transmitted in messages. "#] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SystemRealTimeMessage { /// If sent, they should be sent 24 times per quarter note. diff --git a/src/message/time.rs b/src/message/time.rs index f548af0..063cd32 100644 --- a/src/message/time.rs +++ b/src/message/time.rs @@ -37,7 +37,7 @@ impl Ticked { /// /// This differs from `Ticked`, which does not necessarily represent itself in time. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Timed { /// Micros diff --git a/src/note.rs b/src/note.rs index a7975d0..d9c03c6 100644 --- a/src/note.rs +++ b/src/note.rs @@ -26,7 +26,7 @@ assert_eq!(key.octave(), Octave::new(4)) "#] #[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Hash)] #[cfg_attr( - feature = "bevy_resources", + feature = "bevy", derive(bevy::prelude::Component, bevy::prelude::Reflect) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/src/pitch_bend.rs b/src/pitch_bend.rs index 56194d9..4baf712 100644 --- a/src/pitch_bend.rs +++ b/src/pitch_bend.rs @@ -8,7 +8,7 @@ use crate::prelude::*; /// /// This value is available via [`PitchBend::value`] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PitchBend { lsb: DataByte, diff --git a/src/program.rs b/src/program.rs index 421b34e..48d4857 100644 --- a/src/program.rs +++ b/src/program.rs @@ -5,7 +5,7 @@ use core::fmt; /// /// TODO docs #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Program(DataByte); diff --git a/src/song_position_pointer.rs b/src/song_position_pointer.rs index a62d657..2376189 100644 --- a/src/song_position_pointer.rs +++ b/src/song_position_pointer.rs @@ -13,7 +13,7 @@ where "#] #[derive(Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "bevy_resources", derive(bevy::prelude::Reflect))] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SongPositionPointer { lsb: DataByte, diff --git a/src/velocity.rs b/src/velocity.rs index c396bdc..f53dc41 100644 --- a/src/velocity.rs +++ b/src/velocity.rs @@ -4,7 +4,7 @@ use core::fmt; /// Identifies the velocity of a key press, or a key unpress, or an aftertouch. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] #[cfg_attr( - feature = "bevy_resources", + feature = "bevy", derive(bevy::prelude::Reflect, bevy::prelude::Component) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] From bc60d171d597dde9661f82dd2f491674affb93d8 Mon Sep 17 00:00:00 2001 From: dsgallups Date: Thu, 25 Sep 2025 07:14:09 -0400 Subject: [PATCH 2/6] add bevy_asset feature --- Cargo.toml | 51 ++++++++++++++++++++++++++----------------------- src/file/mod.rs | 4 ++-- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd12fe0..72a9d1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,29 +17,8 @@ exclude = ["assets/*"] default = ["std"] std = ["thiserror/std", "num_enum/std", "crossbeam-channel/std"] web = ["bevy_platform/web"] -# bevy_resources = [ -# "bevy", -# "dep:midir", -# "dep:tinyaudio", -# "dep:itertools", -# "dep:rustysynth", -# "dep:crossbeam-channel", -# ] -bevy = ["dep:bevy", "std", "bevy/bevy_asset"] -# debug = ["bevy_resources"] -# example = [ -# "bevy_resources", -# "std", -# "bevy/bevy_color", -# "bevy/bevy_core_pipeline", -# "bevy/bevy_ui", -# "bevy/bevy_ui_picking_backend", -# "bevy/bevy_winit", -# "bevy/bevy_window", -# "bevy/x11", -# # note that the wasm example freezes since when this feature is not enabled! TODO -# "bevy/multi_threaded", -# ] +bevy = ["dep:bevy", "std"] +bevy_asset = ["bevy/bevy_asset"] serde = ["dep:serde"] @@ -47,7 +26,6 @@ serde = ["dep:serde"] version = "0.17.0-rc" optional = true default-features = false -# features = ["async_executor", "bevy_log", "bevy_state", "bevy_asset", "std"] [dependencies] num_enum = { version = "0.7.3", default-features = false } @@ -109,3 +87,28 @@ pretty_assertions = { default-features = false, features = [ # [[example]] # name = "scale" # required-features = ["example"] + +# debug = ["bevy_resources"] +# example = [ +# "bevy_resources", +# "std", +# "bevy/bevy_color", +# "bevy/bevy_core_pipeline", +# "bevy/bevy_ui", +# "bevy/bevy_ui_picking_backend", +# "bevy/bevy_winit", +# "bevy/bevy_window", +# "bevy/x11", +# # note that the wasm example freezes since when this feature is not enabled! TODO +# "bevy/multi_threaded", +# ] +# + +# bevy_resources = [ +# "bevy", +# "dep:midir", +# "dep:tinyaudio", +# "dep:itertools", +# "dep:rustysynth", +# "dep:crossbeam-channel", +# ] diff --git a/src/file/mod.rs b/src/file/mod.rs index 35a362a..759e116 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -29,10 +29,10 @@ pub struct ParsedMidiFile<'a> { header: Header, format: Format<'a>, } -#[cfg(feature = "bevy")] +#[cfg(feature = "bevy_asset")] impl bevy::asset::Asset for ParsedMidiFile<'static> {} -#[cfg(feature = "bevy")] +#[cfg(feature = "bevy_asset")] impl bevy::asset::VisitAssetDependencies for ParsedMidiFile<'static> { fn visit_dependencies(&self, _visit: &mut impl FnMut(bevy::asset::UntypedAssetId)) {} } From a208437b7e11dca2a93d0eeed55f8cb9f549ecd0 Mon Sep 17 00:00:00 2001 From: dsgallups Date: Thu, 25 Sep 2025 07:15:07 -0400 Subject: [PATCH 3/6] update version to be alpha.0 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 72a9d1e..a6455fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "midix" -version = "4.0.0-alpha" +version = "4.0.0-alpha.0" authors = ["dsgallups "] edition = "2024" description = "MIDI structures designed for humans" @@ -17,7 +17,7 @@ exclude = ["assets/*"] default = ["std"] std = ["thiserror/std", "num_enum/std", "crossbeam-channel/std"] web = ["bevy_platform/web"] -bevy = ["dep:bevy", "std"] +bevy = ["dep:bevy"] bevy_asset = ["bevy/bevy_asset"] serde = ["dep:serde"] From 177d5e3098da0f0c4ba16272ffe14b509ba13439 Mon Sep 17 00:00:00 2001 From: dsgallups Date: Thu, 25 Sep 2025 07:19:06 -0400 Subject: [PATCH 4/6] delete dead modules/crates --- src/bevy/asset/midi_file.rs | 251 -------- src/bevy/asset/mod.rs | 5 - src/bevy/asset/sound_font.rs | 67 -- src/bevy/input/connection.rs | 61 -- src/bevy/input/error.rs | 36 -- src/bevy/input/mod.rs | 243 ------- src/bevy/input/plugin.rs | 21 - src/bevy/mod.rs | 150 ----- src/bevy/output/connection.rs | 31 - src/bevy/output/error.rs | 36 -- src/bevy/output/mod.rs | 240 ------- src/bevy/output/plugin.rs | 19 - src/bevy/plugin.rs | 60 -- src/bevy/settings.rs | 36 -- src/bevy/song/builder/channel.rs | 76 --- src/bevy/song/builder/mod.rs | 54 -- src/bevy/song/mod.rs | 107 ---- src/bevy/song/simple/beat.rs | 50 -- src/bevy/song/simple/channel_settings.rs | 103 --- src/bevy/song/simple/mod.rs | 159 ----- src/bevy/song/simple/section.rs | 35 - src/bevy/song/song_writer.rs | 56 -- src/bevy/synth/mod.rs | 252 -------- src/bevy/synth/plugin.rs | 295 --------- src/bevy/synth/sink/commands.rs | 28 - src/bevy/synth/sink/mod.rs | 11 - src/bevy/synth/sink/task.rs | 249 -------- src/lib.rs | 6 - src/synthesizer/Cargo.toml | 15 - src/synthesizer/README.md | 14 - src/synthesizer/TODO.md | 4 - src/synthesizer/src/lib.rs | 19 - src/synthesizer/src/reader/binary.rs | 130 ---- src/synthesizer/src/reader/counter.rs | 23 - src/synthesizer/src/reader/mod.rs | 4 - src/synthesizer/src/soundfont/error.rs | 203 ------ .../src/soundfont/generator/mod.rs | 46 -- .../src/soundfont/generator/type.rs | 71 --- src/synthesizer/src/soundfont/info.rs | 160 ----- .../src/soundfont/instrument/info.rs | 45 -- .../src/soundfont/instrument/mod.rs | 69 -- .../src/soundfont/instrument/region.rs | 366 ----------- src/synthesizer/src/soundfont/mod.rs | 150 ----- src/synthesizer/src/soundfont/parameters.rs | 101 --- src/synthesizer/src/soundfont/preset/info.rs | 60 -- src/synthesizer/src/soundfont/preset/mod.rs | 107 ---- .../src/soundfont/preset/region.rs | 302 --------- .../src/soundfont/sample_header.rs | 118 ---- src/synthesizer/src/soundfont/sampledata.rs | 58 -- src/synthesizer/src/soundfont/version.rs | 34 - src/synthesizer/src/soundfont/zone/info.rs | 46 -- src/synthesizer/src/soundfont/zone/mod.rs | 48 -- src/synthesizer/src/synthesizer/array_math.rs | 17 - src/synthesizer/src/synthesizer/channel.rs | 242 ------- src/synthesizer/src/synthesizer/chorus.rs | 121 ---- src/synthesizer/src/synthesizer/error.rs | 31 - src/synthesizer/src/synthesizer/loop_mode.rs | 20 - src/synthesizer/src/synthesizer/mod.rs | 602 ------------------ src/synthesizer/src/synthesizer/reverb.rs | 383 ----------- src/synthesizer/src/synthesizer/settings.rs | 66 -- .../src/synthesizer/voice/bi_quad_filter.rs | 104 --- .../src/synthesizer/voice/envelope/common.rs | 49 -- .../src/synthesizer/voice/envelope/mod.rs | 30 - .../synthesizer/voice/envelope/modulation.rs | 130 ---- .../src/synthesizer/voice/envelope/volume.rs | 133 ---- src/synthesizer/src/synthesizer/voice/mod.rs | 330 ---------- .../src/synthesizer/voice/oscillator.rs | 143 ----- .../src/synthesizer/voice/region/lfo.rs | 61 -- .../src/synthesizer/voice/region/mod.rs | 4 - .../src/synthesizer/voice/region/pair.rs | 194 ------ src/synthesizer/src/utils.rs | 36 -- src/utils.rs | 1 - 72 files changed, 7627 deletions(-) delete mode 100644 src/bevy/asset/midi_file.rs delete mode 100644 src/bevy/asset/mod.rs delete mode 100644 src/bevy/asset/sound_font.rs delete mode 100644 src/bevy/input/connection.rs delete mode 100644 src/bevy/input/error.rs delete mode 100644 src/bevy/input/mod.rs delete mode 100644 src/bevy/input/plugin.rs delete mode 100644 src/bevy/mod.rs delete mode 100644 src/bevy/output/connection.rs delete mode 100644 src/bevy/output/error.rs delete mode 100644 src/bevy/output/mod.rs delete mode 100644 src/bevy/output/plugin.rs delete mode 100644 src/bevy/plugin.rs delete mode 100644 src/bevy/settings.rs delete mode 100644 src/bevy/song/builder/channel.rs delete mode 100644 src/bevy/song/builder/mod.rs delete mode 100644 src/bevy/song/mod.rs delete mode 100644 src/bevy/song/simple/beat.rs delete mode 100644 src/bevy/song/simple/channel_settings.rs delete mode 100644 src/bevy/song/simple/mod.rs delete mode 100644 src/bevy/song/simple/section.rs delete mode 100644 src/bevy/song/song_writer.rs delete mode 100644 src/bevy/synth/mod.rs delete mode 100644 src/bevy/synth/plugin.rs delete mode 100644 src/bevy/synth/sink/commands.rs delete mode 100644 src/bevy/synth/sink/mod.rs delete mode 100644 src/bevy/synth/sink/task.rs delete mode 100644 src/synthesizer/Cargo.toml delete mode 100644 src/synthesizer/README.md delete mode 100644 src/synthesizer/TODO.md delete mode 100644 src/synthesizer/src/lib.rs delete mode 100644 src/synthesizer/src/reader/binary.rs delete mode 100644 src/synthesizer/src/reader/counter.rs delete mode 100644 src/synthesizer/src/reader/mod.rs delete mode 100644 src/synthesizer/src/soundfont/error.rs delete mode 100644 src/synthesizer/src/soundfont/generator/mod.rs delete mode 100644 src/synthesizer/src/soundfont/generator/type.rs delete mode 100644 src/synthesizer/src/soundfont/info.rs delete mode 100644 src/synthesizer/src/soundfont/instrument/info.rs delete mode 100644 src/synthesizer/src/soundfont/instrument/mod.rs delete mode 100644 src/synthesizer/src/soundfont/instrument/region.rs delete mode 100644 src/synthesizer/src/soundfont/mod.rs delete mode 100644 src/synthesizer/src/soundfont/parameters.rs delete mode 100644 src/synthesizer/src/soundfont/preset/info.rs delete mode 100644 src/synthesizer/src/soundfont/preset/mod.rs delete mode 100644 src/synthesizer/src/soundfont/preset/region.rs delete mode 100644 src/synthesizer/src/soundfont/sample_header.rs delete mode 100644 src/synthesizer/src/soundfont/sampledata.rs delete mode 100644 src/synthesizer/src/soundfont/version.rs delete mode 100644 src/synthesizer/src/soundfont/zone/info.rs delete mode 100644 src/synthesizer/src/soundfont/zone/mod.rs delete mode 100644 src/synthesizer/src/synthesizer/array_math.rs delete mode 100644 src/synthesizer/src/synthesizer/channel.rs delete mode 100644 src/synthesizer/src/synthesizer/chorus.rs delete mode 100644 src/synthesizer/src/synthesizer/error.rs delete mode 100644 src/synthesizer/src/synthesizer/loop_mode.rs delete mode 100644 src/synthesizer/src/synthesizer/mod.rs delete mode 100644 src/synthesizer/src/synthesizer/reverb.rs delete mode 100644 src/synthesizer/src/synthesizer/settings.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/bi_quad_filter.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/envelope/common.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/envelope/mod.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/envelope/modulation.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/envelope/volume.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/mod.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/oscillator.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/region/lfo.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/region/mod.rs delete mode 100644 src/synthesizer/src/synthesizer/voice/region/pair.rs delete mode 100644 src/synthesizer/src/utils.rs diff --git a/src/bevy/asset/midi_file.rs b/src/bevy/asset/midi_file.rs deleted file mode 100644 index 0bcd23d..0000000 --- a/src/bevy/asset/midi_file.rs +++ /dev/null @@ -1,251 +0,0 @@ -#![doc = r#" -Asset types - -TODO -"#] -#![allow(dead_code)] -#![allow(unused_variables)] - -use bevy::{ - asset::{AssetLoader, LoadContext, io::Reader}, - prelude::*, -}; - -use crate::{ - events::LiveEvent, - file::ParsedMidiFile as Mf, - prelude::{FormatType, Timed, Timing}, - reader::ReaderError, -}; - -use crate::bevy::song::MidiSong; - -/// Sound font asset. Wraps a midix MidiFile -/// -/// TODO(before v4: do not wrap midix MidiFile) -#[derive(Asset, TypePath)] -pub struct MidiFile { - inner: Mf<'static>, -} - -impl MidiFile { - /// Create a new midifile with the given inner midix MidiFile - pub fn new(file: Mf<'static>) -> Self { - Self { inner: file } - } - - /// Get a reference to the inner midifile - pub fn inner(&self) -> &Mf<'static> { - &self.inner - } - - /// uses owned self to make a song sendable to the synth - pub fn into_song(self) -> MidiSong { - self.inner.into() - } - /// uses reference to make a song - pub fn to_song(&self) -> MidiSong { - (&self.inner).into() - } -} - -impl<'a> From> for MidiSong { - fn from(midi: Mf<'a>) -> Self { - let mut commands = Vec::new(); - let tracks = midi.tracks(); - - // is Some if the tempo is set for the whole file. - // None if the format is sequentially independent - let file_tempo = match midi.format_type() { - FormatType::SequentiallyIndependent => None, - FormatType::Simultaneous | FormatType::SingleMultiChannel => { - let first_track = tracks.first().unwrap(); - Some(first_track.info().tempo) - } - }; - - for track in tracks { - let track_tempo = file_tempo.unwrap_or(track.info().tempo); - let micros_per_quarter_note = track_tempo.micros_per_quarter_note(); - - let (micros_per_tick, offset_in_micros) = match midi.header().timing() { - Timing::Smpte(v) => { - //µs_per_tick = 1 000 000 / (fps × ticks_per_frame) - //FPS is −24/−25/−29/−30 in the high byte of division; - // ticks per frame is the low byte. - - let frames_per_second = v.fps().as_division() as u32; - let ticks_per_frame = v.ticks_per_frame() as u32; - let ticks_per_second = frames_per_second * ticks_per_frame; - let micros_per_tick = 1_000_000. / ticks_per_second as f64; - - //NOTE: if the file header uses smpte, that overrides any track smpte offset. - let offset_in_micros = track - .info() - .smpte_offset - .as_ref() - .map(|offset| { - if offset.fps != v.fps() { - warn!( - "Header's fps({}) does not align with track's fps({}). \ - The file's fps will override the track's!", - v.fps().as_f64(), - offset.fps.as_f64() - ); - } - offset.as_micros_with_override(v.fps()) - }) - .unwrap_or(0.); - - (micros_per_tick, offset_in_micros) - } - Timing::TicksPerQuarterNote(tpqn) => { - // µs_per_tick = tempo_meta / TPQN - // micro_seconds/quarternote * quarternote_per_tick (1/ticks per qn) - let micros_per_tick = - micros_per_quarter_note as f64 / tpqn.ticks_per_quarter_note() as f64; - - let offset_in_micros = track - .info() - .smpte_offset - .as_ref() - .map(|offset| offset.as_micros()) - .unwrap_or(0.); - - (micros_per_tick, offset_in_micros) - } - }; - - for event in track.events() { - match event.event() { - LiveEvent::ChannelVoice(cv) => { - let tick = event.accumulated_ticks(); - let micros = micros_per_tick * tick as f64; - - commands.push(Timed::new(micros as u64, *cv)); - } - _ => { - //idk - } - } - } - } - MidiSong::new(commands) - } -} - -impl<'a> From<&Mf<'a>> for MidiSong { - fn from(midi: &Mf<'a>) -> Self { - let mut commands = Vec::new(); - let tracks = midi.tracks(); - - let ticks_per_qn = midi.header().timing().ticks_per_quarter_note().unwrap(); - - // is Some if the tempo is set for the whole file. - // None if the format is sequentially independent - let file_tempo = match midi.format_type() { - FormatType::SequentiallyIndependent => None, - FormatType::Simultaneous | FormatType::SingleMultiChannel => { - let first_track = tracks.first().unwrap(); - Some(first_track.info().tempo) - } - }; - - for track in tracks { - let track_tempo = file_tempo.unwrap_or(track.info().tempo); - let micros_per_quarter_note = track_tempo.micros_per_quarter_note(); - - let (micros_per_tick, offset_in_micros) = match midi.header().timing() { - Timing::Smpte(v) => { - //µs_per_tick = 1 000 000 / (fps × ticks_per_frame) - //FPS is −24/−25/−29/−30 in the high byte of division; - // ticks per frame is the low byte. - - let frames_per_second = v.fps().as_division() as u32; - let ticks_per_frame = v.ticks_per_frame() as u32; - let ticks_per_second = frames_per_second * ticks_per_frame; - let micros_per_tick = 1_000_000. / ticks_per_second as f64; - - //NOTE: if the file header uses smpte, that overrides any track smpte offset. - let offset_in_micros = track - .info() - .smpte_offset - .as_ref() - .map(|offset| { - if offset.fps != v.fps() { - warn!( - "Header's fps({}) does not align with track's fps({}). \ - The file's fps will override the track's!", - v.fps().as_f64(), - offset.fps.as_f64() - ); - } - offset.as_micros_with_override(v.fps()) - }) - .unwrap_or(0.); - - (micros_per_tick, offset_in_micros) - } - Timing::TicksPerQuarterNote(tpqn) => { - // µs_per_tick = tempo_meta / TPQN - // micro_seconds/quarternote * quarternote_per_tick (1/ticks per qn) - let micros_per_tick = - micros_per_quarter_note as f64 / tpqn.ticks_per_quarter_note() as f64; - - let offset_in_micros = track - .info() - .smpte_offset - .as_ref() - .map(|offset| offset.as_micros()) - .unwrap_or(0.); - - (micros_per_tick, offset_in_micros) - } - }; - - for event in track.events() { - match event.event() { - LiveEvent::ChannelVoice(cv) => { - let tick = event.accumulated_ticks(); - let micros = micros_per_tick * tick as f64; - - commands.push(Timed::new(micros as u64, *cv)); - } - _ => { - //idk - } - } - } - } - MidiSong::new(commands) - } -} - -/// Loader for sound fonts -#[derive(Default)] -pub struct MidiFileLoader; - -impl AssetLoader for MidiFileLoader { - type Asset = MidiFile; - type Settings = (); - type Error = ReaderError; - async fn load( - &self, - reader: &mut dyn Reader, - _settings: &(), - _load_context: &mut LoadContext<'_>, - ) -> Result { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await.unwrap(); - - let inner = Mf::parse(bytes)?; - - let res = MidiFile::new(inner); - - Ok(res) - } - - fn extensions(&self) -> &[&str] { - &["mid"] - } -} diff --git a/src/bevy/asset/mod.rs b/src/bevy/asset/mod.rs deleted file mode 100644 index 3aaa003..0000000 --- a/src/bevy/asset/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! asset types -mod midi_file; -pub use midi_file::*; -mod sound_font; -pub use sound_font::*; diff --git a/src/bevy/asset/sound_font.rs b/src/bevy/asset/sound_font.rs deleted file mode 100644 index 1968e59..0000000 --- a/src/bevy/asset/sound_font.rs +++ /dev/null @@ -1,67 +0,0 @@ -#![doc = r#" -Asset types - -TODO -"#] - -use alloc::sync::Arc; -use bevy_platform::prelude::*; -use thiserror::Error; - -use bevy::{ - asset::{AssetLoader, LoadContext, io::Reader}, - prelude::*, -}; -use rustysynth::SoundFont as Sf; - -/// Sound font asset -#[derive(Asset, TypePath)] -pub struct SoundFont { - pub(crate) file: Arc, -} - -impl SoundFont { - /// Create a new - fn new(file: &mut &[u8]) -> Self { - let sf = Sf::new(file).unwrap(); - Self { file: Arc::new(sf) } - } -} -/// Possible errors that can be produced by [`CustomAssetLoader`] -#[derive(Debug, Error)] -pub enum SoundFontLoadError { - /// An [IO](std::io) Error - #[error("Could not load asset: {0}")] - Io(#[from] std::io::Error), -} - -/// Loader for sound fonts -#[derive(Default)] -pub struct SoundFontLoader; - -impl AssetLoader for SoundFontLoader { - type Asset = SoundFont; - type Settings = (); - type Error = SoundFontLoadError; - async fn load( - &self, - reader: &mut dyn Reader, - _settings: &(), - _load_context: &mut LoadContext<'_>, - ) -> Result { - let mut bytes = Vec::new(); - info!( - "Loading bytes...this might take a while. If taking too long, run with --release or with opt-level = 3!" - ); - reader.read_to_end(&mut bytes).await?; - - info!("Loaded!"); - let res = SoundFont::new(&mut bytes.as_slice()); - - Ok(res) - } - - fn extensions(&self) -> &[&str] { - &["custom"] - } -} diff --git a/src/bevy/input/connection.rs b/src/bevy/input/connection.rs deleted file mode 100644 index a956cd2..0000000 --- a/src/bevy/input/connection.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::events::{FromLiveEventBytes, LiveEvent}; -use bevy::prelude::*; -use crossbeam_channel::{Receiver, TryRecvError}; -use midir::MidiInputPort; - -use super::MidiInputError; - -/// An [`Event`] for incoming midi data. -#[derive(Event, Debug)] -pub struct MidiData { - /// Returns the timestamp of the data - pub stamp: Option, - - /// The underlying message of the event - pub message: LiveEvent<'static>, -} -pub(crate) struct MidiInputConnection { - data: Receiver, - conn: midir::MidiInputConnection<()>, -} - -impl MidiInputConnection { - pub fn new( - midir_input: midir::MidiInput, - port: &MidiInputPort, - port_name: &str, - ) -> Result { - let (sender, receiver) = crossbeam_channel::unbounded::(); - - let conn = midir_input.connect( - port, - port_name, - { - move |timestamp, data, _| { - let Ok(message) = LiveEvent::from_bytes(data) else { - return; - }; - sender - .send(MidiData { - stamp: Some(timestamp), - message, - }) - .unwrap(); - } - }, - (), - )?; - - Ok(Self { - data: receiver, - conn, - }) - } - pub fn read(&self) -> Result { - self.data.try_recv() - } - pub fn close(self) -> midir::MidiInput { - let (listener, _) = self.conn.close(); - listener - } -} diff --git a/src/bevy/input/error.rs b/src/bevy/input/error.rs deleted file mode 100644 index dc75ff5..0000000 --- a/src/bevy/input/error.rs +++ /dev/null @@ -1,36 +0,0 @@ -use bevy::prelude::*; -use midir::{ConnectError, ConnectErrorKind}; // XXX: do we expose this? -use thiserror::Error; - -/// The [`Error`] type for midi input operations, accessible as an [`Event`]. -#[derive(Debug, Event, Error)] -pub enum MidiInputError { - /// There was something wrong connecting to the input - #[error("Couldn't reconnect to input port: {0}")] - ConnectionError(ConnectErrorKind), - - /// The port, passed by id, was not found. - #[error("Port not found (id: {0}")] - PortNotFound(String), - - /// Something happened when refreshing the port statuses - #[error("Couldn't refersh input ports")] - PortRefreshError, - /// Invalid state - #[error("Invalid State: {0}")] - InvalidState(String), -} -impl MidiInputError { - pub(crate) fn invalid(msg: impl ToString) -> Self { - Self::InvalidState(msg.to_string()) - } - pub(crate) fn port_not_found(id: impl Into) -> Self { - Self::PortNotFound(id.into()) - } -} - -impl From> for MidiInputError { - fn from(value: ConnectError) -> Self { - Self::ConnectionError(value.kind()) - } -} diff --git a/src/bevy/input/mod.rs b/src/bevy/input/mod.rs deleted file mode 100644 index 675c6e0..0000000 --- a/src/bevy/input/mod.rs +++ /dev/null @@ -1,243 +0,0 @@ -#![doc = r#" -a plugin and types for handling MIDI input -"#] -use crossbeam_channel::TryRecvError; - -mod connection; -pub use connection::*; - -mod error; -pub use error::*; - -mod plugin; -pub use plugin::*; - -use bevy::prelude::*; -use midir::MidiInputPort; - -use crate::bevy::settings::MidiSettings; - -// you can't actually have multiple MidiInputs on one device, it's really strange. -enum MidiInputState { - Listening(midir::MidiInput), - Active(MidiInputConnection), -} -/// SAFETY: This applies to linux alsa. -/// -/// There is only one instance of MidiInput at any time using this crate. -/// -/// However, this may not satisfy the requirements for safety. If another instance of -/// MidiInput exists in the external program, then UB is possible. -/// -/// Therefore, the assumption is, that when using this crate, that the user -/// will NOT instantiate another [`midir::MidiInput`] at any point while -/// [`MidiInput`] has been inserted as a resource -unsafe impl Sync for MidiInputState {} -unsafe impl Send for MidiInputState {} - -/// The central resource for interacting with midi inputs -/// -/// `MidiInput` does many things: -/// - Fetches a list of ports with connected midi devices -/// - Allows one to connect to a particular midi device and read output -/// - Close that connection and search for other devices -#[derive(Resource)] -pub struct MidiInput { - settings: MidiSettings, - state: Option, - ports: Vec, -} - -/// SAFETY: -/// -/// `JsValue`s in WASM cannot be `Send`: -/// -/// Quote: -/// > The JsValue type wraps a slab/heap of js objects which is managed by -/// > the wasm-bindgen shim, and everything here is not actually able to cross -/// > any thread boundaries. -/// -/// Therefore, `MidiOutput` nor `MidiInput` should not be able to implement Send and Sync. -/// -/// HOWEVER: Because the main scheduler does not run on worker threads, it is safe, -/// for the wasm target, to implement Send (until this issue is resolved.) -/// -#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "web"))] -unsafe impl Send for MidiInput {} -/// SAFETY: -/// -/// See [`MidiInput`]'s Send implementation -#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "web"))] -unsafe impl Sync for MidiInput {} - -impl MidiInput { - /// Creates a new midi input with the provided settings. This is done automatically - /// by [`MidiInputPlugin`]. - pub fn new(settings: MidiSettings) -> Self { - let listener = match midir::MidiInput::new(settings.client_name) { - Ok(input) => input, - Err(e) => { - panic!("Error initializing midi input! {e:?}"); - } - }; - let ports = listener.ports(); - Self { - state: Some(MidiInputState::Listening(listener)), - settings, - ports, - } - } - - /// Return a list of ports updated since calling [`MidiInput::new`] or - /// [`MidiInput::refresh_ports`] - pub fn ports(&self) -> &[MidiInputPort] { - &self.ports - } - /// Attempts to connects to the port at the given index returned by [`MidiInput::ports`] - /// - /// # Errors - /// - If already connected to a device - /// - If the index is out of bounds - /// - An input connection cannot be established - pub fn connect_to_index(&mut self, index: usize) -> Result<(), MidiInputError> { - if self - .state - .as_ref() - .is_none_or(|s| matches!(s, MidiInputState::Active(_))) - { - return Err(MidiInputError::invalid( - "Cannot connect: not currently active!", - )); - } - let Some(port) = self.ports.get(index) else { - return Err(MidiInputError::port_not_found( - "Port was not found at {index}!", - )); - }; - - let MidiInputState::Listening(listener) = self.state.take().unwrap() else { - unreachable!() - }; - - self.state = Some(MidiInputState::Active( - MidiInputConnection::new(listener, port, self.settings.port_name).unwrap(), - )); - Ok(()) - } - - /// A method you should call if [`MidiInput::is_listening`] and [`MidiInput::is_active`] are both false. - pub fn reset(&mut self) { - let listener = match midir::MidiInput::new(self.settings.client_name) { - Ok(input) => input, - Err(e) => { - error!("Failed to reset listening state! {e:?}"); - return; - } - }; - self.state = Some(MidiInputState::Listening(listener)); - } - /// Attempts to connects to the passed port - /// - /// # Errors - /// - If already connected to a device - /// - An input connection cannot be established - pub fn connect_to_port(&mut self, port: &MidiInputPort) -> Result<(), MidiInputError> { - if self - .state - .as_ref() - .is_none_or(|s| matches!(s, MidiInputState::Active(_))) - { - return Err(MidiInputError::invalid( - "Cannot connect: not currently active!", - )); - } - let MidiInputState::Listening(listener) = self.state.take().unwrap() else { - unreachable!() - }; - - self.state = Some(MidiInputState::Active( - MidiInputConnection::new(listener, port, self.settings.port_name).unwrap(), - )); - Ok(()) - } - /// Attempts to connects to the passed port - /// - /// # Errors - /// - If already connected to a device - /// - If the port ID cannot be currently found - /// - Note that this case can occur if you have not refreshed ports - /// and the device is no longer available. - /// - An input connection cannot be established - pub fn connect_to_id(&mut self, id: String) -> Result<(), MidiInputError> { - if self - .state - .as_ref() - .is_none_or(|s| matches!(s, MidiInputState::Active(_))) - { - return Err(MidiInputError::invalid( - "Cannot connect: not currently active!", - )); - } - let MidiInputState::Listening(listener) = self.state.take().unwrap() else { - unreachable!() - }; - let Some(port) = listener.find_port_by_id(id.clone()) else { - return Err(MidiInputError::port_not_found(id)); - }; - self.state = Some(MidiInputState::Active( - MidiInputConnection::new(listener, &port, self.settings.port_name).unwrap(), - )); - Ok(()) - } - /// True if a device is currently connected - pub fn is_active(&self) -> bool { - self.state - .as_ref() - .is_some_and(|s| matches!(s, MidiInputState::Active(_))) - } - - /// True if input is waiting to connect to a device - pub fn is_listening(&self) -> bool { - self.state - .as_ref() - .is_some_and(|s| matches!(s, MidiInputState::Listening(_))) - } - - /// Refreshes the available port list - /// - /// Does nothing if [`MidiInput::is_active`] is true - pub fn refresh_ports(&mut self) { - let Some(MidiInputState::Listening(listener)) = &self.state else { - return; - }; - self.ports = listener.ports(); - } - - /// Disconnects from the active device - /// - /// Does nothing if the [`MidiInput::is_listening`] is true. - pub fn disconnect(&mut self) { - if self - .state - .as_ref() - .is_none_or(|s| matches!(s, MidiInputState::Listening(_))) - { - return; - } - let MidiInputState::Active(conn) = self.state.take().unwrap() else { - unreachable!() - }; - let listener = conn.close(); - self.state = Some(MidiInputState::Listening(listener)); - } - - /// will return data if connected. Note, this CONSUMES the event. - /// - /// You will need to propagate this data out to other systems if need be. - pub fn read(&self) -> Result { - let Some(MidiInputState::Active(conn)) = &self.state else { - return Err(TryRecvError::Disconnected); - }; - conn.read() - } -} diff --git a/src/bevy/input/plugin.rs b/src/bevy/input/plugin.rs deleted file mode 100644 index 59a1258..0000000 --- a/src/bevy/input/plugin.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::bevy::settings::MidiSettings; - -use super::MidiInput; -use bevy::prelude::*; - -#[doc = r#" -Inserts [`MidiInput`] as a resource. - -See [`MidiSettings`] for configuration options. -"#] -#[derive(Clone, Copy, Debug, Default)] -pub struct MidiInputPlugin { - /// The settings to apply to [`MidiInput`] on instantiation. - pub settings: MidiSettings, -} - -impl Plugin for MidiInputPlugin { - fn build(&self, app: &mut App) { - app.insert_resource(MidiInput::new(self.settings)); - } -} diff --git a/src/bevy/mod.rs b/src/bevy/mod.rs deleted file mode 100644 index b923042..0000000 --- a/src/bevy/mod.rs +++ /dev/null @@ -1,150 +0,0 @@ -#![doc = r#" -Bevy plugin that uses [`midix`](https://crates.io/crates/midix), -[`midir`](https://github.com/Boddlnagg/midir), and a [`rustysynth`](https://github.com/sinshu/rustysynth) fork to play midi sounds! - -Read from MIDI devices, MIDI files, and programmable input, and output to user audio with a soundfont! - -## Features -- Enable `web` for WASM compatibility - -## Example -```rust, no_run -use std::time::Duration; -use bevy_platform::prelude::*;use bevy::{ - log::{Level, LogPlugin}, - prelude::*, -}; -use midix::prelude::*; -fn main() { - App::new() - .add_plugins(( - DefaultPlugins.set(LogPlugin { - level: Level::INFO, - ..default() - }), - MidiPlugin { - input: None, - ..Default::default() - }, - )) - .add_systems(Startup, load_sf2) - .add_systems(Update, scale_me) - .run(); -} -/// Take a look here for some soundfonts: -/// -/// -fn load_sf2(asset_server: Res, mut synth: ResMut) { - synth.use_soundfont(asset_server.load("soundfont.sf2")); -} - -struct Scale { - timer: Timer, - current_key: Key, - note_on: bool, - forward: bool, - incremented_by: u8, - max_increment: u8, -} - -impl Scale { - pub fn calculate_next_key(&mut self) { - if self.forward { - if self.incremented_by == self.max_increment { - self.forward = false; - self.incremented_by -= 1; - self.current_key -= 1; - } else { - self.incremented_by += 1; - self.current_key += 1; - } - } else if self.incremented_by == 0 { - self.forward = true; - self.incremented_by += 1; - self.current_key += 1; - } else { - self.incremented_by -= 1; - self.current_key -= 1; - } - } -} - -impl Default for Scale { - fn default() -> Self { - let timer = Timer::new(Duration::from_millis(200), TimerMode::Repeating); - Scale { - timer, - current_key: Key::new(Note::C, Octave::new(2)), - note_on: true, - forward: true, - incremented_by: 0, - max_increment: 11, - } - } -} - -fn scale_me(synth: Res, time: Res