diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index dad00916..154c2f4c 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -521,51 +521,53 @@ impl<'a> Content for VerticalArranger<'a, Tui> { }; //let height = rows.last().map(|(w,y)|(y+w)/ppq).unwrap_or(16); let tracks: &[Sequencer] = state.tracks.as_ref(); - let scenes = state.scenes.as_ref(); + let scenes: &[Scene] = state.scenes.as_ref(); let offset = 4 + scene_name_max_len(scenes) as u16; Layers::new(move |add|{ - let rows = rows.as_ref(); - let cols = cols.as_ref(); - add(&VerticalArrangerGrid(offset, &rows, &cols))?; - add(&VerticalArrangerCursor(state.focused, state.selected, offset, &cols, &rows))?; - let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) => { + let rows: &[(usize, usize)] = rows.as_ref(); + let cols: &[(usize, usize)] = cols.as_ref(); + let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) => (&track.name.read().unwrap().as_str() as &dyn Widget) - .bg(COLOR_BG1).min_xy(*w as u16, 2) - }.push_x(offset)); - let track_clips = col!((scene, (pulses, _)) in scenes.iter().zip(rows) => { + .min_xy(*w as u16, 2).push_x(offset)); + let scene_name = |scene, playing: bool, height|row!( + if playing { "▶ " } else { " " }, + (scene as &Scene).name.read().unwrap().as_str(), + ).fixed_xy(offset.saturating_sub(1), height); + let scene_clip = |scene, track: usize, w: u16, h: u16|Layers::new(move |add|{ + let mut color = Color::Rgb(40, 50, 30); + match (tracks.get(track), (scene as &Scene).clips.get(track)) { + (Some(track), Some(Some(clip))) => match track.phrases.get(*clip) { + Some(phrase) => { + let name = &(phrase as &Arc>).read().unwrap().name; + let name = name.read().unwrap(); + let name = format!("{clip:02} {}", name); + add(&name.as_str().push_x(1))?; + if (track as &Sequencer<_>).sequence == Some(*clip) { + color = COLOR_PLAYING + } else { + color = COLOR_BG1 + }; + }, + _ => {} + }, + _ => {} + }; + add(&Background(color)) + }).fixed_xy(w, h); + let tracks_clips = col!((scene, (pulses, _)) in scenes.iter().zip(rows) => { let height = 1.max((pulses / 96) as u16); let playing = scene.is_playing(tracks); Stack::right(move |add| { - add(&Stack::right(|add|{ - add(&if playing { "▶ " } else { " " })?; - add(&scene.name.read().unwrap().as_str()) - }).fixed_xy(offset.saturating_sub(1), height))?; + add(&scene_name(scene, playing, height))?; for (track, (w, _x)) in cols.iter().enumerate() { - add(&Layers::new(move |add|{ - let mut color = Color::Rgb(40, 50, 30); - if let (Some(track), Some(Some(clip))) = ( - tracks.get(track), - scene.clips.get(track), - ) { - if let Some(phrase) = track.phrases.get(*clip) { - add(&format!( - "{clip:02} {}", - phrase.read().unwrap().name.read().unwrap() - ).as_str().push_x(1))?; - color = if track.sequence == Some(*clip) { - COLOR_PLAYING - } else { - COLOR_BG1 - }; - } - } - add(&Background(color)) - }).fixed_xy(*w as u16, height))?; + add(&scene_clip(scene, track, *w as u16, height))?; } Ok(()) }).fixed_y(height) }); - add(&col!(track_titles, track_clips))?; + add(&VerticalArrangerGrid(offset, &rows, &cols))?; + add(&VerticalArrangerCursor(state.focused, state.selected, offset, &cols, &rows))?; + add(&col!(track_titles, tracks_clips))?; Ok(()) }) .bg(Color::Rgb(28, 35, 25)) @@ -686,7 +688,6 @@ impl<'a> Widget for VerticalArrangerCursor<'a> { to.fill_bg(scene_area, Color::Rgb(40, 50, 30)); } } - //Ok(Some(area)) Ok(()) } } @@ -1286,8 +1287,8 @@ impl Sequencer { let width = usize::MAX.min(phrase.read().unwrap().length); let mut buffer = BigBuffer::new(width, 64); let phrase = phrase.read().unwrap(); - fill_seq_bg(&mut buffer, phrase.length, self.ppq)?; - fill_seq_fg(&mut buffer, &phrase)?; + Self::fill_seq_bg(&mut buffer, phrase.length, self.ppq)?; + Self::fill_seq_fg(&mut buffer, &phrase)?; self.buffer = buffer; } else { self.buffer = Default::default(); @@ -1295,6 +1296,83 @@ impl Sequencer { Ok(()) } + fn fill_seq_bg (buf: &mut BigBuffer, length: usize, ppq: usize) -> Usually<()> { + for x in 0..buf.width { + if x as usize >= length { + break + } + let style = Style::default(); + buf.get_mut(x, 0).map(|cell|{ + cell.set_char('-'); + cell.set_style(style); + }); + for y in 0 .. buf.height { + buf.get_mut(x, y).map(|cell|{ + cell.set_char(if ppq == 0 { + '·' + } else if x % (4 * ppq) == 0 { + '│' + } else if x % ppq == 0 { + '╎' + } else { + '·' + }); + cell.set_fg(Color::Gray); + cell.modifier = Modifier::DIM; + }); + } + } + Ok(()) + } + + fn fill_seq_fg (buf: &mut BigBuffer, phrase: &Phrase) -> Usually<()> { + let mut notes_on = [false;128]; + for x in 0..buf.width { + if x as usize >= phrase.length { + break + } + if let Some(notes) = phrase.notes.get(x as usize) { + if phrase.percussive { + for note in notes { + match note { + MidiMessage::NoteOn { key, .. } => + notes_on[key.as_int() as usize] = true, + _ => {} + } + } + } else { + for note in notes { + match note { + MidiMessage::NoteOn { key, .. } => + notes_on[key.as_int() as usize] = true, + MidiMessage::NoteOff { key, .. } => + notes_on[key.as_int() as usize] = false, + _ => {} + } + } + } + for y in 0..buf.height/2 { + if y >= 64 { + break + } + if let Some(block) = half_block( + notes_on[y as usize * 2], + notes_on[y as usize * 2 + 1], + ) { + buf.get_mut(x, y).map(|cell|{ + cell.set_char(block); + cell.set_fg(Color::White); + }); + } + } + if phrase.percussive { + notes_on.fill(false); + } + } + } + Ok(()) + } + pub(crate) fn style_focus (&self) -> Option