Skip to content

Commit 1082f87

Browse files
committed
fixes #114, add a simple click sound
1 parent 93e2726 commit 1082f87

File tree

7 files changed

+114
-32
lines changed

7 files changed

+114
-32
lines changed

crates/notation_bevy/src/viewer/control_view.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,17 @@ impl ControlView {
186186
}
187187
if !midi_settings.bypass_hub {
188188
ui.separator();
189+
ui.horizontal(|ui| {
190+
ui.checkbox(&mut midi_settings.click_mute, "Mute");
191+
ui.add(Slider::new(&mut midi_settings.click_velocity, 0..=127).text("Click"));
192+
});
189193
ui.horizontal(|ui| {
190194
ui.checkbox(&mut midi_settings.vocal_mute, "Mute");
191-
ui.add(Slider::new(&mut midi_settings.vocal_velocity, 0..=128).text("Vocal"));
195+
ui.add(Slider::new(&mut midi_settings.vocal_velocity, 0..=127).text("Vocal"));
192196
});
193197
ui.horizontal(|ui| {
194198
ui.checkbox(&mut midi_settings.guitar_mute, "Mute");
195-
ui.add(Slider::new(&mut midi_settings.guitar_velocity, 0..=128).text("Guitar"));
199+
ui.add(Slider::new(&mut midi_settings.guitar_velocity, 0..=127).text("Guitar"));
196200
});
197201
if ui.button("Reset Audio").clicked() {
198202
let default = MidiSettings::default();

crates/notation_core/src/entry.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::prelude::{Duration, Units};
22

3+
#[derive(Copy, Clone, Debug)]
34
pub enum EntryPassMode {
45
Immediate,
56
Delayed,

crates/notation_core/src/scale.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::{Deserialize, Serialize};
22
use std::fmt::Display;
33

4-
use crate::prelude::{Key, Note, Pitch, Semitones, Syllable, SyllableNote, Chord};
4+
use crate::{prelude::{Key, Note, Pitch, Semitones, Syllable, SyllableNote, Chord, Octave}, tone::Tone};
55

66
// https://hellomusictheory.com/learn/music-scales-beginners-guide/
77
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
@@ -95,4 +95,11 @@ impl Scale {
9595
pub fn calc_note(&self, key: &Key, syllable_note: &SyllableNote) -> Note {
9696
(Semitones::from(*syllable_note) + self.calc_do_semitones(key)).into()
9797
}
98+
pub fn calc_click_note(&self, key: &Key, octave: &Octave, syllable: &Syllable) -> Note {
99+
let pitch = self.calc_pitch(key, syllable);
100+
Note::new(*octave, pitch)
101+
}
102+
pub fn calc_click_tone(&self, key: &Key, octave: &Octave, syllable: &Syllable) -> Tone {
103+
Tone::Single(self.calc_click_note(key, octave, syllable))
104+
}
98105
}

crates/notation_midi/src/midi_hub.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ impl Default for MidiHub {
1919
}
2020

2121
impl MidiHub {
22+
pub const PRINT_SYNTH_ERROR: bool = false;
23+
pub const PRINT_MIDI_ERROR: bool = false;
2224
pub fn new_output() -> Option<MidiOutput> {
2325
if let Ok(output) = MidiOutput::new("MidiHub") {
2426
let ports = output.ports();
@@ -81,13 +83,17 @@ impl MidiHub {
8183
if let Some(synth) = &self.output_synth {
8284
//println!("send to synth: {:?}", msg);
8385
if let Err(err) = synth.send(speed, msg, velocity) {
84-
println!("send to synth failed: {:?} -> {:?}", msg, err);
86+
if Self::PRINT_SYNTH_ERROR {
87+
println!("send to synth failed: {:?} -> {:?}", msg, err);
88+
}
8589
}
8690
}
8791
if let Some(conn) = &self.output_conn {
8892
//println!("send to midi: {:?}", msg);
8993
if let Err(err) = conn.lock().unwrap().send(&msg.to_midi()) {
90-
println!("send to midi failed: {:?} -> {:?}", msg, err);
94+
if Self::PRINT_MIDI_ERROR {
95+
println!("send to midi failed: {:?} -> {:?}", msg, err);
96+
}
9197
}
9298
}
9399
}

crates/notation_midi/src/midi_message.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
1-
use std::sync::Arc;
2-
31
use helgoboss_midi::{ShortMessage, StructuredShortMessage};
42
use notation_model::prelude::*;
53

64
#[derive(Clone, Debug)]
75
pub struct MidiMessage {
8-
pub entry: Arc<LaneEntry>,
9-
pub delay: Option<Units>,
6+
pub pass_mode: EntryPassMode,
7+
pos: BarPosition,
8+
delay: Option<Units>,
109
pub midi: StructuredShortMessage,
1110
}
1211
impl MidiMessage {
13-
pub fn new(entry: &Arc<LaneEntry>, delay: Option<Units>, midi: StructuredShortMessage) -> Self {
12+
pub fn new(pass_mode: EntryPassMode, pos: BarPosition, delay: Option<Units>, midi: StructuredShortMessage) -> Self {
1413
Self {
15-
entry: entry.clone(),
14+
pass_mode,
15+
pos,
1616
delay,
1717
midi,
1818
}
1919
}
20+
pub fn of_entry(entry: &LaneEntry, delay: Option<Units>, midi: StructuredShortMessage) -> Self {
21+
Self {
22+
pass_mode: entry.pass_mode(),
23+
pos: entry.bar_position(),
24+
delay,
25+
midi,
26+
}
27+
}
28+
pub fn bar_ordinal(&self) -> usize {
29+
self.pos.bar_ordinal
30+
}
2031
pub fn bar_position(&self) -> BarPosition {
2132
match self.delay {
22-
Some(delay) => self.entry.bar_position().with_delay(delay),
23-
None => self.entry.bar_position(),
33+
Some(delay) => self.pos.with_delay(delay),
34+
None => self.pos,
2435
}
2536
}
2637
pub fn units_position(&self) -> Units {

crates/notation_midi/src/midi_settings.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use midi_msg::GMSoundSet;
2-
use notation_model::prelude::TrackKind;
2+
use notation_model::prelude::{TrackKind, Octave};
33

44
#[cfg(feature = "inspector")]
55
use bevy_inspector_egui::Inspectable;
@@ -8,13 +8,17 @@ use bevy_inspector_egui::Inspectable;
88
#[cfg_attr(feature = "inspector", derive(Inspectable))]
99
pub struct MidiSettings {
1010
pub bypass_hub: bool,
11+
pub click_mute: bool,
12+
pub click_velocity: u8,
13+
pub click_octave: Octave,
1114
pub vocal_mute: bool,
1215
pub vocal_velocity: u8,
1316
pub guitar_mute: bool,
1417
pub guitar_velocity: u8,
1518
pub piano_mute: bool,
1619
pub piano_velocity: u8,
1720
pub use_internal_synth: bool,
21+
pub click_sound: u8,
1822
pub vocal_sound: u8,
1923
pub guitar_sound: u8,
2024
pub piano_sound: u8,
@@ -26,13 +30,17 @@ impl Default for MidiSettings {
2630
fn default() -> Self {
2731
Self {
2832
bypass_hub: false,
33+
click_mute: false,
34+
click_velocity: 40,
35+
click_octave: Octave::P7,
2936
vocal_mute: false,
3037
vocal_velocity: 110,
3138
guitar_mute: false,
3239
guitar_velocity: 120,
3340
piano_mute: false,
3441
piano_velocity: 110,
3542
use_internal_synth: Self::default_use_internal_synth(),
43+
click_sound: GMSoundSet::Dulcimer as u8,
3644
vocal_sound: GMSoundSet::Cello as u8,
3745
guitar_sound: GMSoundSet::AcousticGuitarSteel as u8,
3846
piano_sound: GMSoundSet::AcousticGrandPiano as u8,
@@ -59,4 +67,7 @@ impl MidiSettings {
5967
_ => None,
6068
}
6169
}
70+
pub fn get_click_channel_params(&self) -> (u8, u8) {
71+
(self.click_sound, self.click_velocity)
72+
}
6273
}

crates/notation_midi/src/midi_state.rs

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -106,28 +106,33 @@ impl MidiChannel {
106106
};
107107
let mut velocity = self.velocity.into();
108108
if !bypass {
109-
self.track.as_ref().map(|x| {
110-
match x.kind {
111-
TrackKind::Vocal => {
112-
velocity = if settings.vocal_mute { 0 } else { settings.vocal_velocity };
113-
},
114-
TrackKind::Guitar => {
115-
velocity = if settings.guitar_mute { 0 } else { settings.guitar_velocity };
116-
},
117-
TrackKind::Piano => {
118-
velocity = if settings.piano_mute { 0 } else { settings.piano_velocity };
119-
},
120-
_ => (),
109+
match &self.track {
110+
Some(track) => {
111+
match track.kind {
112+
TrackKind::Vocal => {
113+
velocity = if settings.vocal_mute { 0 } else { settings.vocal_velocity };
114+
},
115+
TrackKind::Guitar => {
116+
velocity = if settings.guitar_mute { 0 } else { settings.guitar_velocity };
117+
},
118+
TrackKind::Piano => {
119+
velocity = if settings.piano_mute { 0 } else { settings.piano_velocity };
120+
},
121+
_ => (),
122+
}
123+
},
124+
None => {
125+
velocity = if settings.click_mute { 0 } else { settings.click_velocity };
121126
}
122-
});
127+
}
123128
}
124129
let mut count = 0;
125130
loop {
126131
if let Some(next) = self.messages.get(self.next_index) {
127-
if play_control.is_bar_in_range(next.entry.bar_props().bar_ordinal)
132+
if play_control.is_bar_in_range(next.bar_ordinal())
128133
&& play_control
129134
.position
130-
.is_passed(next.entry.pass_mode(), &next.bar_position())
135+
.is_passed(next.pass_mode, &next.bar_position())
131136
{
132137
self.next_index += 1;
133138
count += 1;
@@ -156,7 +161,7 @@ impl MidiChannel {
156161
hub.send(
157162
settings,
158163
speed,
159-
&MidiMessage::new(&first_msg.entry, None, msg),
164+
&MidiMessage::new(first_msg.pass_mode, first_msg.bar_position(), None, msg),
160165
self.velocity.into(),
161166
);
162167
let msg = StructuredShortMessage::ControlChange {
@@ -167,7 +172,7 @@ impl MidiChannel {
167172
hub.send(
168173
settings,
169174
speed,
170-
&MidiMessage::new(&first_msg.entry, None, msg),
175+
&MidiMessage::new(first_msg.pass_mode, first_msg.bar_position(), None, msg),
171176
self.velocity.into(),
172177
);
173178
}
@@ -183,6 +188,16 @@ impl MidiChannel {
183188
self.program = U7::new(params.0);
184189
self.velocity = U7::new(params.1);
185190
}
191+
pub fn setup_no_track(
192+
&mut self,
193+
_settings: &MidiSettings,
194+
_hub: &mut MidiHub,
195+
params: (u8, u8),
196+
) {
197+
self.track = None;
198+
self.program = U7::new(params.0);
199+
self.velocity = U7::new(params.1);
200+
}
186201
}
187202

188203
pub struct MidiState {
@@ -241,10 +256,37 @@ impl MidiState {
241256
}
242257
None
243258
}
259+
fn create_click_channel(&mut self, settings: &MidiSettings, hub: &mut MidiHub, tab: &Tab, index: &mut usize) {
260+
let params = settings.get_click_channel_params();
261+
if let Some(channel) = self.channels.get_mut(*index) {
262+
channel.setup_no_track(settings, hub, params);
263+
println!("switch_tab(), setup click channel: [{}] -> {}, {}", index, params.0, params.1);
264+
*index += 1;
265+
let scale_root = tab.meta.scale.calc_root_syllable();
266+
let signature = tab.signature();
267+
let bar_units = tab.bar_units();
268+
let beat_delay = Some(Units::from(signature.beat_unit));
269+
for bar in tab.bars.iter() {
270+
for beat in 0..signature.bar_beats {
271+
let in_bar_pos = Units(beat as f32 * Units::from(signature.beat_unit).0);
272+
let root = bar.get_chord(Some(in_bar_pos)).map(|x| x.root).unwrap_or(scale_root);
273+
let note = tab.meta.scale.calc_click_note(&tab.meta.key, &settings.click_octave, &root);
274+
let pos = BarPosition::new(bar_units, bar.props.bar_ordinal, in_bar_pos);
275+
if let Some(midi_msg) = MidiUtil::note_midi_on_msg(&note, channel.channel, channel.velocity) {
276+
channel.add_message(MidiMessage::new(EntryPassMode::Immediate, pos, None, midi_msg));
277+
}
278+
if let Some(midi_msg) = MidiUtil::note_midi_off_msg(&note, channel.channel, channel.velocity) {
279+
channel.add_message(MidiMessage::new(EntryPassMode::Immediate, pos, beat_delay, midi_msg));
280+
}
281+
}
282+
}
283+
}
284+
}
244285
pub fn switch_tab(&mut self, settings: &MidiSettings, hub: &mut MidiHub, tab: Arc<Tab>) {
245286
self.tab = Some(tab.clone());
246287
self.reset_channels();
247288
let mut index: usize = 0;
289+
self.create_click_channel(settings, hub, &tab, &mut index);
248290
for track in tab.tracks.iter() {
249291
if index >= self.channels.len() {
250292
return;
@@ -263,7 +305,7 @@ impl MidiState {
263305
for entry in lane.entries.iter() {
264306
if let Some(msgs) = MidiUtil::get_midi_msgs(channel, bar, &entry) {
265307
for msg in msgs {
266-
channel.add_message(MidiMessage::new(entry, msg.0, msg.1));
308+
channel.add_message(MidiMessage::of_entry(entry, msg.0, msg.1));
267309
}
268310
}
269311
}

0 commit comments

Comments
 (0)