mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
piano keys reappear
This commit is contained in:
parent
9db60c3429
commit
9a50ce6613
15 changed files with 856 additions and 782 deletions
248
src/midi_edit.rs
248
src/midi_edit.rs
|
|
@ -115,12 +115,8 @@ impl MidiEditor {
|
|||
fn note_range_prev (&self) -> usize {
|
||||
self.get_note_axis().saturating_sub(1)
|
||||
}
|
||||
fn time_zoom (&self) -> usize {
|
||||
self.get_time_zoom()
|
||||
}
|
||||
fn time_zoom_next (&self) -> usize {
|
||||
self.get_time_zoom() + 1
|
||||
}
|
||||
fn time_zoom (&self) -> usize { self.get_time_zoom() }
|
||||
fn time_zoom_next (&self) -> usize { self.get_time_zoom() + 1 }
|
||||
fn time_zoom_next_fine (&self) -> usize { self.get_time_zoom() + 1 }
|
||||
fn time_zoom_prev (&self) -> usize { self.get_time_zoom().saturating_sub(1).max(1) }
|
||||
fn time_zoom_prev_fine (&self) -> usize { self.get_time_zoom().saturating_sub(1).max(1) }
|
||||
|
|
@ -140,28 +136,16 @@ impl MidiEditor {
|
|||
}
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// use tek::{*, tengri::*};
|
||||
///
|
||||
/// struct Test(Option<MidiEditor>);
|
||||
/// impl_as_ref!(Option<MidiEditor>: |self: Test|&self.0);
|
||||
/// impl_as_mut!(Option<MidiEditor>: |self: Test|&mut self.0);
|
||||
///
|
||||
/// let mut host = Test(Some(MidiEditor::default()));
|
||||
/// let _ = host.editor();
|
||||
/// let _ = host.editor_mut();
|
||||
/// let _ = host.is_editing();
|
||||
/// let _ = host.editor_w();
|
||||
/// let _ = host.editor_h();
|
||||
/// ```
|
||||
|
||||
impl <T: NotePoint+TimePoint> MidiPoint for T {}
|
||||
|
||||
impl <T: TimeRange+NoteRange> MidiRange for T {}
|
||||
|
||||
pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync {
|
||||
|
||||
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize);
|
||||
|
||||
fn redraw (&self);
|
||||
|
||||
/// Make sure cursor is within note range
|
||||
fn autoscroll (&self) {
|
||||
let note_pos = self.get_note_pos().min(127);
|
||||
|
|
@ -173,6 +157,7 @@ pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync {
|
|||
self.note_lo().store((note_lo + note_pos).saturating_sub(note_hi), Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Make sure time range is within display
|
||||
fn autozoom (&self) {
|
||||
if self.time_lock().load(Relaxed) {
|
||||
|
|
@ -216,6 +201,7 @@ pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync {
|
|||
//}
|
||||
//self.time_zoom().set(time_zoom);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl_has!(Sizer: |self: MidiEditor| self.size);
|
||||
|
|
@ -244,59 +230,173 @@ impl MidiViewer for MidiEditor {
|
|||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Toggle MIDI editor.
|
||||
///
|
||||
/// ```
|
||||
/// tek::App::default().toggle_editor(None);
|
||||
/// ```
|
||||
pub fn toggle_editor (&mut self, value: Option<bool>) {
|
||||
//FIXME: self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
|
||||
if value.unwrap_or_else(||!self.editor().is_some()) {
|
||||
self.toggle_editor_on()
|
||||
} else {
|
||||
self.toggle_editor_off()
|
||||
}
|
||||
}
|
||||
/// 12 piano keys, some highlighted.
|
||||
///
|
||||
/// ```
|
||||
/// let keys = tek::OctaveVertical::default();
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct OctaveVertical {
|
||||
pub on: [bool; 12],
|
||||
pub colors: [Color; 3]
|
||||
}
|
||||
|
||||
fn toggle_editor_on (&mut self) {
|
||||
// Create new clip in pool when entering empty cell
|
||||
if let Selection::TrackClip { track: track_index, scene } = *self.selection()
|
||||
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
&& scene.clips.get(track_index).is_none()
|
||||
&& let Some(track) = self.project.tracks.get_mut(track_index)
|
||||
{
|
||||
let color = track.color.base.mix(scene.color.base, 0.5);
|
||||
let color = ItemColor::random_near(color, 0.2).into();
|
||||
let clip: MidiClip = MidiClip { color, ..Default::default() };
|
||||
let clip = self.project.clips.append(Some(clip));
|
||||
// autocolor: new clip colors from scene and track color
|
||||
if let Some(editor) = &mut self.project.editor {
|
||||
*editor.mode.clip.write() = clip.0.read().clone();
|
||||
editor.mode.color = clip.0.read().as_ref().map(|p|p.read().color)
|
||||
.unwrap_or(ItemTheme::G[64]);
|
||||
}
|
||||
*scene.clips.slot(track_index).write() = clip.0.read().clone();
|
||||
//Some(clip)
|
||||
} else {
|
||||
//None
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_editor_off (&mut self) {
|
||||
if let Selection::TrackClip { track, scene } = *self.selection()
|
||||
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
{
|
||||
// Remove clip from arrangement when exiting empty clip editor
|
||||
let mut swapped = None;
|
||||
if let Some(count) = scene.clips.slot(track).read().as_ref().map(|c|c.read().count_midi_messages())
|
||||
&& count == 0
|
||||
{
|
||||
std::mem::swap(&mut swapped, &mut scene.clips.slot(track).write());
|
||||
}
|
||||
if let Some(clip) = swapped {
|
||||
self.project.clips.remove(&*clip.read());
|
||||
}
|
||||
}
|
||||
impl OctaveVertical {
|
||||
pub fn color (&self, pitch: usize) -> Color {
|
||||
let pitch = pitch % 12;
|
||||
self.colors[if self.on[pitch] { 2 } else { match pitch { 0 | 2 | 4 | 5 | 6 | 8 | 10 => 0, _ => 1 } }]
|
||||
}
|
||||
}
|
||||
|
||||
impl_default!(OctaveVertical: Self {
|
||||
on: [false; 12], colors: [Rgb(255,255,255), Rgb(0,0,0), Rgb(255,0,0)]
|
||||
});
|
||||
|
||||
/// A clip, rendered as a horizontal piano roll.
|
||||
///
|
||||
/// Uses full blocks on note on and half blocks on legato: █▄ █▄ █▄
|
||||
///
|
||||
/// ```
|
||||
/// let piano = tek::PianoHorizontal::default();
|
||||
/// ```
|
||||
#[derive(Clone, Default)] pub struct PianoHorizontal {
|
||||
pub clip: ClipSlot,
|
||||
/// Buffer where the whole clip is rerendered on change
|
||||
pub buffer: Arc<RwLock<BigBuffer>>,
|
||||
/// Size of actual notes area
|
||||
pub size: Sizer,
|
||||
/// The display window
|
||||
pub range: MidiSelection,
|
||||
/// The note cursor
|
||||
pub point: MidiCursor,
|
||||
/// The highlight color palette
|
||||
pub color: ItemTheme,
|
||||
/// Width of the keyboard
|
||||
pub keys_width: u16,
|
||||
}
|
||||
|
||||
impl_has!(Sizer: |self: PianoHorizontal| self.size);
|
||||
|
||||
impl PianoHorizontal {
|
||||
pub fn new () -> Self {
|
||||
let size = Sizer::default();
|
||||
let mut range = MidiSelection::from((12, true));
|
||||
range.time_axis = size.0.clone();
|
||||
range.note_axis = size.1.clone();
|
||||
let piano = Self { keys_width: 5, size, range, ..Default::default() };
|
||||
piano.redraw();
|
||||
piano
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeRange for PianoHorizontal {
|
||||
fn time_len (&self) -> &AtomicUsize { self.range.time_len() }
|
||||
fn time_zoom (&self) -> &AtomicUsize { self.range.time_zoom() }
|
||||
fn time_lock (&self) -> &AtomicBool { self.range.time_lock() }
|
||||
fn time_start (&self) -> &AtomicUsize { self.range.time_start() }
|
||||
fn time_axis (&self) -> &AtomicUsize { self.range.time_axis() }
|
||||
}
|
||||
|
||||
impl NoteRange for PianoHorizontal {
|
||||
fn note_lo (&self) -> &AtomicUsize { self.range.note_lo() }
|
||||
fn note_axis (&self) -> &AtomicUsize { self.range.note_axis() }
|
||||
}
|
||||
|
||||
impl NotePoint for PianoHorizontal {
|
||||
fn note_len (&self) -> &AtomicUsize { self.point.note_len() }
|
||||
fn note_pos (&self) -> &AtomicUsize { self.point.note_pos() }
|
||||
}
|
||||
|
||||
impl TimePoint for PianoHorizontal {
|
||||
fn time_pos (&self) -> &AtomicUsize { self.point.time_pos() }
|
||||
}
|
||||
|
||||
impl MidiViewer for PianoHorizontal {
|
||||
/// Determine the required space to render the clip.
|
||||
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize) {
|
||||
let zoom = self.range.time_zoom().load(Relaxed);
|
||||
let size = if zoom > 0 { clip.length / zoom } else { 0 };
|
||||
(size, 128)
|
||||
}
|
||||
/// Draw the piano roll into the buffer.
|
||||
fn redraw (&self) {
|
||||
*self.buffer.write() = self.clip.map_inner(|clip|{
|
||||
let buf_size = self.buffer_size(&clip);
|
||||
let mut buffer = BigBuffer::from(buf_size);
|
||||
let time_zoom = self.get_time_zoom().max(1);
|
||||
self.time_len().store(clip.length, Relaxed);
|
||||
let note_len = self.get_note_len();
|
||||
let note_pos = self.get_note_pos();
|
||||
let time_pos = self.get_time_pos();
|
||||
draw_piano_fg(&mut buffer, &clip, time_zoom);
|
||||
buffer
|
||||
}).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PianoHorizontal {
|
||||
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
let buffer = self.buffer.read();
|
||||
f.debug_struct("PianoHorizontal")
|
||||
.field("time_zoom", &self.range.time_zoom)
|
||||
.field("buffer", &format!("{}x{}", buffer.width, buffer.height))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
//impl App {
|
||||
///// Toggle MIDI editor.
|
||||
/////
|
||||
///// ```
|
||||
///// tek::App::default().toggle_editor(None);
|
||||
///// ```
|
||||
//pub fn toggle_editor (&mut self, value: Option<bool>) {
|
||||
////FIXME: self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
|
||||
//if value.unwrap_or_else(||!self.editor().is_some()) {
|
||||
//self.toggle_editor_on()
|
||||
//} else {
|
||||
//self.toggle_editor_off()
|
||||
//}
|
||||
//}
|
||||
|
||||
//fn toggle_editor_on (&mut self) {
|
||||
//// Create new clip in pool when entering empty cell
|
||||
//if let Selection::TrackClip { track: track_index, scene } = *self.selection()
|
||||
//&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
//&& scene.clips.get(track_index).is_none()
|
||||
//&& let Some(track) = self.project.tracks.get_mut(track_index)
|
||||
//{
|
||||
//let color = track.color.base.mix(scene.color.base, 0.5);
|
||||
//let color = ItemColor::random_near(color, 0.2).into();
|
||||
//let clip: MidiClip = MidiClip { color, ..Default::default() };
|
||||
//let clip = self.project.clips.append(Some(clip));
|
||||
//// autocolor: new clip colors from scene and track color
|
||||
//if let Some(editor) = &mut self.project.editor {
|
||||
//*editor.mode.clip.write() = clip.0.read().clone();
|
||||
//editor.mode.color = clip.0.read().as_ref().map(|p|p.read().color)
|
||||
//.unwrap_or(ItemTheme::G[64]);
|
||||
//}
|
||||
//*scene.clips.slot(track_index).write() = clip.0.read().clone();
|
||||
////Some(clip)
|
||||
//} else {
|
||||
////None
|
||||
//}
|
||||
//}
|
||||
|
||||
//fn toggle_editor_off (&mut self) {
|
||||
//if let Selection::TrackClip { track, scene } = *self.selection()
|
||||
//&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
//{
|
||||
//// Remove clip from arrangement when exiting empty clip editor
|
||||
//let mut swapped = None;
|
||||
//if let Some(count) = scene.clips.slot(track).read().as_ref().map(|c|c.read().count_midi_messages())
|
||||
//&& count == 0
|
||||
//{
|
||||
//std::mem::swap(&mut swapped, &mut scene.clips.slot(track).write());
|
||||
//}
|
||||
//if let Some(clip) = swapped {
|
||||
//self.project.clips.remove(&*clip.read());
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue