makepad_audio_widgets/
piano.rs

1
2use {
3    crate::{
4        makepad_draw::*,
5        makepad_widgets::*,
6    }
7};
8
9live_design!{
10    use link::shaders::*;
11    
12    DrawKey= {{DrawKey}} {
13        
14        fn height_map(self, pos: vec2) -> float {
15            let fx = 1 - pow(1.2 - sin(pos.x * PI), 10.8);
16            let fy = 1 - pow(1.2 - self.pressed * 0.2 - cos(pos.y * 0.5 * PI), 25.8)
17            return fx + fy
18        }
19        
20        fn black_key(self) -> vec4 {
21            let delta = 0.001;
22            // differentiate heightmap to get the surface normal
23            let d = self.height_map(self.pos)
24            let dy = self.height_map(self.pos + vec2(0, delta))
25            let dx = self.height_map(self.pos + vec2(delta, 0))
26            let normal = normalize(cross(vec3(delta, 0, dx - d), vec3(0, delta, dy - d)))
27            //let light = normalize(vec3(1.5, 0.5, 1.1))
28            let light = normalize(vec3(0.65, 0.5, 0.5))
29            let light_hover = normalize(vec3(0.75, 0.5, 1.5))
30            let diff = pow(max(dot(mix(light, light_hover, self.hover * (1 - self.pressed)), normal), 0), 3)
31            return mix(#181818, #bc, diff)
32        }
33        
34        fn white_key(self) -> vec4 {
35            return mix(
36                #DEDAD3FF,
37                mix(
38                    mix(
39                        #EAE7E2FF,
40                        #ff,
41                        self.hover
42                    ),
43                    mix(#96989CFF, #131820FF, pow(1.0 - sin(self.pos.x * PI), 1.8)),
44                    self.pressed
45                ),
46                self.pos.y
47            )
48        }
49        
50        fn pixel(self) -> vec4 {
51            //return #f00
52            let sdf = Sdf2d::viewport(self.pos * self.rect_size);
53            if self.is_black > 0.5 {
54                sdf.box(0., -4, self.rect_size.x, self.rect_size.y + 4, 1);
55                sdf.fill_keep(self.black_key())
56            }
57            else {
58                sdf.box(0., -4.0, self.rect_size.x, self.rect_size.y + 4.0, 2.0);
59                sdf.fill_keep(self.white_key())
60            }
61            return sdf.result
62        }
63    }
64    
65    PianoKey= {{PianoKey}} {
66        
67        animator: {
68            hover = {
69                default: off,
70                off = {
71                    from: {all: Forward {duration: 0.2}}
72                    apply: {draw_key: {hover: 0.0}}
73                }
74                
75                on = {
76                    from: {all: Snap}
77                    apply: {draw_key: {hover: 1.0}}
78                }
79            }
80            
81            focus = {
82                default: off
83                
84                off = {
85                    from: {all: Snap}
86                    apply: {draw_key: {focussed: 1.0}}
87                }
88                
89                on = {
90                    from: {all: Forward {duration: 0.05}}
91                    apply: {draw_key: {focussed: 0.0}}
92                }
93            }
94            pressed = {
95                default: off
96                off = {
97                    from: {all: Forward {duration: 0.05}}
98                    apply: {draw_key: {pressed: 0.0}}
99                }
100                
101                on = {
102                    from: {all: Snap}
103                    apply: {draw_key: {pressed: 1.0}}
104                }
105            }
106        }
107    }
108    
109    pub Piano= {{Piano}} {
110        piano_key: <PianoKey> {}
111        white_size: vec2(20.0, 75.0),
112        black_size: vec2(15.0, 50.0),
113        width: Fit,
114        height: Fit
115    }
116}
117
118// TODO support a shared 'inputs' struct on drawshaders
119#[derive(Live, LiveHook, LiveRegister)]#[repr(C)]
120struct DrawKey {
121    #[deref] draw_super: DrawQuad,
122    #[live] is_black: f32,
123    #[live] pressed: f32,
124    #[live] focussed: f32,
125    #[live] hover: f32,
126}
127
128#[derive(Live, LiveHook, LiveRegister)]
129pub struct PianoKey {
130    #[live] draw_key: DrawKey,
131    #[animator] animator: Animator,
132}
133
134#[derive(Live, Widget)]
135pub struct Piano {
136    #[redraw] #[rust] area: Area,
137    #[walk] walk: Walk,
138    #[live] piano_key: Option<LivePtr>,
139    
140    #[rust([0; 20])]
141    keyboard_keys_down: [u8; 20],
142    
143    #[rust(4)]
144    keyboard_octave: u8,
145    
146    #[rust(100)]
147    keyboard_velocity: u8,
148    
149    #[live] black_size: Vec2,
150    #[live] white_size: Vec2,
151    
152    #[rust] white_keys: ComponentMap<PianoKeyId, PianoKey>,
153    #[rust] black_keys: ComponentMap<PianoKeyId, PianoKey>,
154}
155
156impl LiveHook for Piano {
157    fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
158        for piano_key in self.white_keys.values_mut().chain(self.black_keys.values_mut()) {
159            if let Some(index) = nodes.child_by_name(index, live_id!(piano_key).as_field()) {
160                piano_key.apply(cx, apply, index, nodes);
161            }
162        }
163        self.area.redraw(cx);
164    }
165}
166
167#[derive(Clone,  Debug)]
168pub struct PianoNote {
169    pub is_on: bool,
170    pub note_number: u8,
171    pub velocity: u8
172}
173
174#[derive(Clone, Debug, DefaultNone)]
175pub enum PianoAction {
176    Note(PianoNote),
177    None
178}
179
180pub enum PianoKeyAction {
181    Pressed(u8),
182    Up,
183}
184
185impl PianoKey {
186    
187    pub fn draw_abs(&mut self, cx: &mut Cx2d, depth: f32, is_black: f32, rect: Rect) {
188        self.draw_key.draw_depth = depth;
189        self.draw_key.is_black = is_black;
190        self.draw_key.draw_abs(cx, rect);
191    }
192    
193    fn set_is_pressed(&mut self, cx: &mut Cx, is: bool, animate: Animate) {
194        self.animator_toggle(cx, is, animate, id!(pressed.on), id!(pressed.off))
195    }
196    
197    fn set_is_focussed(&mut self, cx: &mut Cx, is: bool, animate: Animate) {
198        self.animator_toggle(cx, is, animate, id!(focus.on), id!(focus.off))
199    }
200    
201    pub fn handle_event(
202        &mut self,
203        cx: &mut Cx,
204        event: &Event,
205        sweep_area: Area,
206        key_id: PianoKeyId,
207        actions: &mut Vec<(PianoKeyId, PianoKeyAction)>
208    ) {
209        if self.animator_handle_event(cx, event).must_redraw() {
210            self.draw_key.area().redraw(cx);
211        }
212        match event.hits_with_sweep_area(cx, self.draw_key.area(), sweep_area) {
213            Hit::FingerHoverIn(_) => {
214                cx.set_cursor(MouseCursor::Hand);
215                self.animator_play(cx, id!(hover.on));
216            }
217            Hit::FingerHoverOut(_) => {
218                self.animator_play(cx, id!(hover.off));
219            }
220            Hit::FingerDown(_) => {
221                self.animator_play(cx, id!(hover.on));
222                self.animator_play(cx, id!(pressed.on));
223                actions.push((key_id, PianoKeyAction::Pressed(127)));
224            }
225            Hit::FingerUp(e) => {
226                if !e.is_sweep && e.device.has_hovers(){
227                    self.animator_play(cx, id!(hover.on));
228                }
229                else{
230                    self.animator_play(cx, id!(hover.off));
231                }
232                self.animator_play(cx, id!(pressed.off));
233                actions.push((key_id, PianoKeyAction::Up));
234            }
235            _ => {}
236        }
237    }
238}
239
240impl Piano {
241       
242    pub fn set_key_focus(&self, cx: &mut Cx) {
243        cx.set_key_focus(self.area);
244    }
245    
246    pub fn set_note(&mut self, cx: &mut Cx, is_on: bool, note_number: u8) {
247        let id = LiveId(note_number as u64).into();
248        if let Some(key) = self.black_keys.get_mut(&id) {
249            key.set_is_pressed(cx, is_on, Animate::No)
250        }
251        if let Some(key) = self.white_keys.get_mut(&id) {
252            key.set_is_pressed(cx, is_on, Animate::No)
253        }
254    }
255    
256}
257
258impl Widget for Piano{
259   fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
260       
261       let uid = self.widget_uid();
262       let mut key_actions = Vec::new();
263       
264        for (key_id, piano_key) in self.black_keys.iter_mut().chain(self.white_keys.iter_mut()) {
265           piano_key.handle_event(cx, event, self.area, *key_id, &mut key_actions);
266       }
267               
268       for (node_id, action) in key_actions {
269           match action {
270               PianoKeyAction::Pressed(velocity) => {
271                   self.set_key_focus(cx);
272                   cx.widget_action(uid, &scope.path, PianoAction::Note(PianoNote {
273                       is_on: true,
274                       note_number: node_id.0.0 as u8,
275                       velocity
276                   }));
277               }
278               PianoKeyAction::Up => {
279                   cx.widget_action(uid, &scope.path, PianoAction::Note(PianoNote {
280                       is_on: false,
281                       note_number: node_id.0.0 as u8,
282                       velocity: 127
283                   }));
284               }
285           }
286       }
287               
288       // handle sweeping the notes
289               
290       fn key_map(kk: KeyCode) -> Option<u8> {
291           match kk {
292               KeyCode::KeyA => Some(0),
293               KeyCode::KeyW => Some(1),
294               KeyCode::KeyS => Some(2),
295               KeyCode::KeyE => Some(3),
296               KeyCode::KeyD => Some(4),
297               KeyCode::KeyF => Some(5),
298               KeyCode::KeyT => Some(6),
299               KeyCode::KeyG => Some(7),
300               KeyCode::KeyY => Some(8),
301               KeyCode::KeyH => Some(9),
302               KeyCode::KeyU => Some(10),
303               KeyCode::KeyJ => Some(11),
304               KeyCode::KeyK => Some(12),
305               KeyCode::KeyO => Some(13),
306               KeyCode::KeyL => Some(14),
307               KeyCode::KeyP => Some(15),
308               KeyCode::Semicolon => Some(16),
309               KeyCode::Quote => Some(17),
310               _ => None
311           }
312       }
313               
314       match event {
315           Event::KeyDown(ke) => if !ke.is_repeat {
316               if let Some(nn) = key_map(ke.key_code) {
317                   let note_number = nn + self.keyboard_octave * 12;
318                   self.keyboard_keys_down[nn as usize] = note_number;
319                   self.set_note(cx, true, note_number);
320                   cx.widget_action(uid, &scope.path, PianoAction::Note(PianoNote {
321                       is_on: true,
322                       note_number,
323                       velocity: self.keyboard_velocity
324                   }));
325               }
326               else {match ke.key_code {
327                   KeyCode::KeyZ => {
328                       self.keyboard_octave -= 1;
329                       self.keyboard_octave = self.keyboard_octave.max(1);
330                   }
331                   KeyCode::KeyX => {
332                       self.keyboard_octave += 1;
333                       self.keyboard_octave = self.keyboard_octave.min(7);
334                   }
335                   KeyCode::KeyC => {
336                       self.keyboard_velocity -= 16;
337                       self.keyboard_velocity = self.keyboard_velocity.max(16);
338                   }
339                   KeyCode::KeyV => {
340                       self.keyboard_velocity += 16;
341                       self.keyboard_velocity = self.keyboard_velocity.min(127);
342                   }
343                   _ => ()
344               }}
345           },
346           Event::KeyUp(ke) => if let Some(nn) = key_map(ke.key_code) {
347               let note_number = self.keyboard_keys_down[nn as usize];
348               self.keyboard_keys_down[nn as usize] = 0;
349               self.set_note(cx, false, note_number);
350               cx.widget_action(uid, &scope.path, PianoAction::Note(PianoNote {
351                   is_on: false,
352                   note_number,
353                   velocity: self.keyboard_velocity
354               }));
355           },
356           _ => ()
357       }
358               
359       match event.hits(cx, self.area) {
360           Hit::KeyFocus(_) => {
361               for piano_key in self.white_keys.values_mut().chain(self.black_keys.values_mut()) {
362                   piano_key.set_is_focussed(cx, true, Animate::Yes)
363               }
364           }
365           Hit::KeyFocusLost(_) => {
366               for piano_key in self.white_keys.values_mut().chain(self.black_keys.values_mut()) {
367                   piano_key.set_is_focussed(cx, true, Animate::No)
368               }
369           }
370           _ => ()
371       }
372   }
373    
374    fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
375        
376        cx.begin_turtle(walk, Layout::default());
377        
378        let start_pos = cx.turtle().pos(); //+ vec2(10., 10.);
379        let mut pos = start_pos;
380                
381        let midi_a0 = 21;
382        let midi_c8 = 108+24;
383                
384        fn black_key_shift(key: u32) -> Option<f64> {
385            match key % 12 {
386                0 => None, // C
387                1 => Some(0.6), // C#
388                2 => None, // D
389                3 => Some(0.4), // D#
390                4 => None, // E
391                5 => None, // F
392                6 => Some(0.7), // F#
393                7 => None, // G
394                8 => Some(0.5), // G#
395                9 => None, // A
396                10 => Some(0.3), // A#
397                11 => None, // B
398                _ => None
399            }
400        }
401                
402        let white_size:DVec2 = self.white_size.into();//dvec2(20.0, 100.0);
403        let black_size:DVec2 = self.black_size.into();//vec2(15.0, 62.0);
404        let piano_key = self.piano_key;
405        // draw the white keys first because they go below the black ones
406        let mut depth = 1.0;
407        for i in midi_a0..midi_c8 {
408            if black_key_shift(i).is_some() {
409                continue;
410            }
411            let key_id = LiveId(i as u64).into();
412            let key = self.white_keys.get_or_insert(cx, key_id, | cx | {
413                PianoKey::new_from_ptr(cx, piano_key)
414            });
415            key.draw_abs(cx, depth, 0.0, Rect {pos: pos, size: white_size});
416            depth += cx.micro_zbias_step();
417            pos.x += white_size.x;
418        }
419        // draw the black keys
420        let mut pos = start_pos;
421for i in midi_a0..midi_c8 {
422            if let Some(shift) = black_key_shift(i) {
423                let key_id = LiveId(i as u64).into();
424                let key = self.black_keys.get_or_insert(cx, key_id, | cx | {
425                    PianoKey::new_from_ptr(cx, piano_key)
426                });
427                key.draw_abs(cx, depth, 1.0, Rect {
428                    pos: pos - dvec2(black_size.x * shift, 0.),
429                    size: black_size
430                });
431                depth += cx.micro_zbias_step();
432            }
433            else {
434                pos.x += white_size.x;
435            }
436        }
437        cx.turtle_mut().set_used(white_size.x * (midi_c8 - midi_a0) as f64, white_size.y);
438        cx.end_turtle_with_area(&mut self.area);
439        self.white_keys.retain_visible();
440        self.black_keys.retain_visible();
441        
442        DrawStep::done()
443    }
444}
445
446#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
447pub struct PianoKeyId(pub LiveId);
448
449impl PianoRef {
450    pub fn notes_played(&self, actions:&Actions) -> Vec<PianoNote> {
451        let mut notes = Vec::new();
452        for action in actions {
453            match action.as_widget_action().widget_uid_eq(self.widget_uid()).cast() {
454                PianoAction::Note(note) => {
455                    notes.push(note)
456                }
457                PianoAction::None=>()
458            }
459        }
460        notes
461    }
462    
463    pub fn set_note(&self, cx: &mut Cx, is_on: bool, note_number: u8) {
464        if let Some(mut inner) = self.borrow_mut() {
465            inner.set_note(cx, is_on, note_number)
466        }
467    }
468    
469    pub fn set_key_focus(&self, cx: &mut Cx) {
470        if let Some(inner) = self.borrow_mut() {
471            inner.set_key_focus(cx)
472        }
473    }
474}
475
476impl PianoSet {
477    pub fn notes_played(&self, actions:&Actions) -> Vec<PianoNote> {
478        let mut notes = Vec::new();
479        for item in self.iter() {
480             for action in actions {
481                 match action.as_widget_action().widget_uid_eq(item.widget_uid()).cast() {
482                    PianoAction::Note(note) =>{
483                        notes.push(note)
484                    }
485                    PianoAction::None=>()
486                }
487            }
488        }
489        notes
490    }
491    
492    pub fn set_note(&self, cx: &mut Cx, is_on: bool, note_number: u8) {
493        for item in self.iter(){
494            item.set_note(cx, is_on, note_number);
495        }
496    }
497}