diff --git a/src/edn.rs b/src/edn.rs index 58f54f11..52c43e6b 100644 --- a/src/edn.rs +++ b/src/edn.rs @@ -187,6 +187,7 @@ impl Phrase { }, _ => panic!("unexpected in phrase '{name}': {edn:?}"), }); + phrase.name = name; Ok(phrase) } } diff --git a/src/model/track.rs b/src/model/track.rs index 9a56a8af..68bd8a9d 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -37,7 +37,7 @@ impl Track { Ok(Self { name: name.to_string(), midi_out: None, - midi_out_buf: vec![vec![];16384], + midi_out_buf: vec![Vec::with_capacity(16);16384], notes_in: [false;128], notes_out: [false;128], monitoring: false, @@ -142,16 +142,18 @@ impl Track { (_usec0, _usecs): (usize, usize), period: f64, ) { - // Clear the section of the output buffer that we will be using - for frame in &mut self.midi_out_buf[0..frames] { - frame.clear(); - } - // Emit "all notes off" at start of buffer if requested - if self.reset { - all_notes_off(&mut self.midi_out_buf); - self.reset = false; - } else if reset { - all_notes_off(&mut self.midi_out_buf); + if self.midi_out.is_some() { + // Clear the section of the output buffer that we will be using + for frame in &mut self.midi_out_buf[0..frames] { + frame.clear(); + } + // Emit "all notes off" at start of buffer if requested + if self.reset { + all_notes_off(&mut self.midi_out_buf); + self.reset = false; + } else if reset { + all_notes_off(&mut self.midi_out_buf); + } } // For highlighting keys and note repeat // Currently playing phrase @@ -160,12 +162,14 @@ impl Track { ) = ( playing, started, self.sequence.and_then(|id|self.phrases.get_mut(id)) ) { - phrase.process_out( - &mut self.midi_out_buf, - &mut self.notes_out, - timebase, - (frame0.saturating_sub(start_frame), frames, period) - ); + if self.midi_out.is_some() { + phrase.process_out( + &mut self.midi_out_buf, + &mut self.notes_out, + timebase, + (frame0.saturating_sub(start_frame), frames, period) + ); + } // Monitor and record input if input.is_some() && (self.recording || self.monitoring) { // For highlighting keys and note repeat @@ -201,7 +205,7 @@ impl Track { } } } - } else if input.is_some() && self.monitoring { + } else if input.is_some() && self.midi_out.is_some() && self.monitoring { for (frame, event, bytes) in parse_midi_input(input.unwrap()) { self.process_monitor_event(frame, &event, bytes) } @@ -241,22 +245,20 @@ impl Track { } impl App { + pub fn new_track_name (&self) -> String { format!("Track {}", self.tracks.len() + 1) } - pub fn add_track ( - &mut self, - name: Option<&str>, - ) -> Usually<&mut Track> { + + pub fn add_track (&mut self, name: Option<&str>) -> Usually<&mut Track> { let name = name.ok_or_else(||self.new_track_name())?; self.tracks.push(Track::new(&name, None, None)?); self.track_cursor = self.tracks.len(); Ok(&mut self.tracks[self.track_cursor - 1]) } + pub fn add_track_with_cb ( - &mut self, - name: Option<&str>, - init: impl FnOnce(&Client, &mut Track)->Usually<()>, + &mut self, name: Option<&str>, init: impl FnOnce(&Client, &mut Track)->Usually<()>, ) -> Usually<&mut Track> { let name = name.ok_or_else(||self.new_track_name())?; let mut track = Track::new(&name, None, None)?; @@ -265,16 +267,19 @@ impl App { self.track_cursor = self.tracks.len(); Ok(&mut self.tracks[self.track_cursor - 1]) } + pub fn track (&self) -> Option<(usize, &Track)> { match self.track_cursor { 0 => None, _ => { let id = self.track_cursor as usize - 1; self.tracks.get(id).map(|t|(id, t)) } } } + pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> { match self.track_cursor { 0 => None, _ => { let id = self.track_cursor as usize - 1; self.tracks.get_mut(id).map(|t|(id, t)) } } } + } diff --git a/src/view/sequencer.rs b/src/view/sequencer.rs index 290d837a..bc64f35f 100644 --- a/src/view/sequencer.rs +++ b/src/view/sequencer.rs @@ -85,6 +85,9 @@ impl<'a> SequencerView<'a> { } impl<'a> SequencerView<'a> { + + const H_KEYS_OFFSET: u16 = 5; + fn horizontal_draw (&self, buf: &mut Buffer, area: Rect) -> Usually<()> { self.horizontal_keys(buf, area)?; self.horizontal_quant(buf, area)?; @@ -103,7 +106,7 @@ impl<'a> SequencerView<'a> { fn horizontal_cursor (&self, buf: &mut Buffer, area: Rect) -> Usually { let (time, note) = (self.time_cursor, self.note_cursor); - let x = area.x + 5 + time as u16; + let x = area.x + Self::H_KEYS_OFFSET + time as u16; let y = area.y + 1 + note as u16 / 2; let c = if note % 2 == 0 { "▀" } else { "▄" }; c.blit(buf, x, y, self.style_focus()) @@ -113,10 +116,9 @@ impl<'a> SequencerView<'a> { if let Some(phrase) = phrase { let (time0, time_z, now) = (self.time_start, self.time_zoom, self.now % phrase.length); let Rect { x, width, .. } = area; - let offset = 5; - for x in x+offset..x+width-offset { - let step = (time0 + (x-offset) as usize) * time_z; - let next_step = (time0 + (x-offset) as usize + 1) * time_z; + for x in x+Self::H_KEYS_OFFSET..x+width { + let step = (time0 + (x-Self::H_KEYS_OFFSET) as usize) * time_z; + let next_step = (time0 + (x-Self::H_KEYS_OFFSET) as usize + 1) * time_z; let style = Self::style_timer_step(now, step, next_step); "-".blit(buf, x, area.y, Some(style))?; } @@ -152,11 +154,11 @@ impl<'a> SequencerView<'a> { key2.set_style(not_dim); key2.set_fg(self.index_to_color(index as usize * 2, Color::White)); key2.set_bg(self.index_to_color(index as usize * 2 + 1, Color::White)); - for x in x+5..x+width-1 { - let cell = buf.get_mut(x, y); - cell.set_char('░'); - cell.set_style(black); - } + //for x in x+Self::H_KEYS_OFFSET..x+width-1 { + //let cell = buf.get_mut(x, y); + //cell.set_char('░'); + //cell.set_style(black); + //} let note_a = note0 + (index * 2) as usize; if note_a % 12 == 0 { let octave = format!("C{}", (note_a / 12) as i8 - 2); @@ -177,26 +179,28 @@ impl<'a> SequencerView<'a> { if phrase.is_none() { return Ok(Rect { x: area.x, y: area.x, width: 0, height: 0 }) } - let phrase = phrase.unwrap(); - let (ppq, time_z, time0, note0) = - (self.ppq, self.time_zoom, self.time_start, self.note_start); - let dim = Style::default().dim(); - let Rect { x, y, width, height } = area; - - let offset = 5; - let phrase_area = Rect { - x: x + offset, y, width: width - offset, height: height - 2 - }; - - let mut steps = vec![]; + let phrase = phrase.unwrap(); + let now = self.now % phrase.length; + let ppq = self.ppq; + let time_z = self.time_zoom; + let time0 = self.time_start; + let note0 = self.note_start; + let dim = Style::default().dim(); + let offset = Self::H_KEYS_OFFSET; + let phrase_area = Rect { x: x + offset, y, width: width - offset, height: height - 2 }; + let mut steps = Vec::with_capacity(phrase_area.width as usize); for x in phrase_area.x .. phrase_area.x + phrase_area.width { let x0 = x.saturating_sub(phrase_area.x) as usize; let step = (0 + time0 + x0) * time_z; let next = (1 + time0 + x0) * time_z; - if step > phrase.length { + if step >= phrase.length { break } + let style = Self::style_timer_step(now, step, next); + let cell = buf.get_mut(x, area.y); + cell.set_char('-'); + cell.set_style(style); steps.push((x, step, next)); for y in phrase_area.y .. phrase_area.y + phrase_area.height { if y == phrase_area.y {