makepad_example_ironfish/
app.rs

1use crate::{
2    makepad_audio_graph::*, makepad_audio_widgets::display_audio::*,
3    makepad_audio_widgets::piano::*, makepad_synth_ironfish::ironfish::*, makepad_widgets::*,
4    sequencer::*,
5};
6//use std::fs::File;
7//use std::io::prelude::*;
8live_design! {
9    use link::widgets::*
10    use link::theme::*
11    use makepad_example_ironfish::app_desktop::AppDesktop
12    use makepad_example_ironfish::app_mobile::AppMobile
13
14    use makepad_audio_graph::mixer::Mixer;
15    use makepad_audio_graph::instrument::Instrument;
16    use makepad_synth_ironfish::ironfish::IronFish;
17    
18    use makepad_draw::shader::std::*;
19/*
20    BlurStage = <ViewBase> {
21        optimize: Texture,
22        draw_bg: {
23            texture image: texture2d
24
25            uniform blursize: 0.0,
26            uniform blurstd: 0.0,
27            uniform blurx: 1.0,
28            uniform blury: 0.0,
29            varying g1: float,
30            varying g2: float,
31            varying g3: float,
32            varying g4: float,
33            varying g5: float,
34
35            varying gaussscale: float,
36
37            varying o0: vec2,
38
39            varying o1a: vec2,
40            varying o2a: vec2,
41            varying o3a: vec2,
42            varying o4a: vec2,
43            varying o5a: vec2,
44
45            varying o1b: vec2,
46            varying o2b: vec2,
47            varying o3b: vec2,
48            varying o4b: vec2,
49            varying o5b: vec2,
50
51            fn vertex(self) -> vec4
52            {
53                let x = self.blurx;
54                let y = self.blury;
55
56                let offset = 0.003 * self.blursize / max(x,y);
57                let standard_deviation = 0.0001 + self.blurstd *0.003;
58                let st_dev_sqr = standard_deviation * standard_deviation;
59
60                let off1 = offset;
61                let off2 = 2.0*offset;
62                let off3 = 3.0*offset;
63                let off4 = 4.0*offset;
64                let off5 = 5.0*offset;
65
66                let mainscale = (1.0 / sqrt(2*PI*st_dev_sqr));
67                let stddevscale = 1.0/ (2*st_dev_sqr);
68
69                self.g1 =  pow(E, -((off1*off1)* stddevscale));
70                self.g2 =  pow(E, -((off2*off2)* stddevscale));
71                self.g3 =  pow(E, -((off3*off3)* stddevscale));
72                self.g4 =  pow(E, -((off4*off4)* stddevscale));
73                self.g5 =  pow(E, -((off5*off5)* stddevscale));
74
75                self.gaussscale = 1.0/(1.0 +  (self.g1 + self.g2 + self.g3 + self.g4 + self.g5 )*2.0);
76
77                let pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size);
78                self.o0 = self.pos;
79
80                self.o1a = self.o0 + vec2(off1*x,off1*y);
81                self.o2a = self.o0 + vec2(off2*x,off2*y);
82                self.o3a = self.o0 + vec2(off3*x,off3*y);
83                self.o4a = self.o0 + vec2(off4*x,off4*y);
84                self.o5a = self.o0 + vec2(off5*x,off5*y);
85
86                self.o1b = self.o0 - vec2(off1*x,off1*y);
87                self.o2b = self.o0 - vec2(off2*x,off2*y);
88                self.o3b = self.o0 - vec2(off3*x,off3*y);
89                self.o4b = self.o0 - vec2(off4*x,off4*y);
90                self.o5b = self.o0 - vec2(off5*x,off5*y);
91
92                return pos;
93            }
94
95            fn pixel(self) -> vec4{
96                let col = sample2d(self.image, self.o0) ;
97                col +=  (sample2d(self.image, self.o1a) + sample2d(self.image, self.o1b)) * self.g1;
98                col +=  (sample2d(self.image, self.o2a) + sample2d(self.image, self.o2b)) * self.g2 ;
99                col +=  (sample2d(self.image, self.o3a) + sample2d(self.image, self.o3b)) * self.g3 ;
100                col +=  (sample2d(self.image, self.o4a) + sample2d(self.image, self.o4b)) * self.g4 ;
101                col +=  (sample2d(self.image, self.o5a) + sample2d(self.image, self.o5b)) * self.g5 ;
102                col = col * self.gaussscale;
103
104                return col ;
105            }
106        }
107    }
108
109
110    ShadowStage = <ViewBase> {
111        optimize: Texture,
112        draw_bg: {
113            texture image: texture2d
114
115            uniform shadowopacity:  0.9,
116            uniform shadowx: 1.0,
117            uniform shadowy: 1.0,
118
119            varying o0: vec2,
120            varying oShadow: vec2,
121            
122            fn vertex(self) -> vec4
123            {
124                
125                let dpi = self.dpi_factor;
126                
127               
128                let pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size);
129
130                self.o0 = self.pos;
131                self.oShadow = self.pos - vec2(self.shadowx * dpi, self.shadowy * dpi)*0.001;
132
133                return pos;
134            }
135
136            fn pixel(self) -> vec4{
137                
138                let shadow = sample2d(self.image, self.oShadow);
139                let main = sample2d(self.image, self.o0);
140
141                let col =  (vec4(0.0,0.0,0.0,self.shadowopacity)  * shadow.a ) * ( 1 - main.a) + main;
142
143                //col +=  (sample2d(self.image, self.o0) )*0.3;
144                
145
146                return col;
147            }
148        }
149    }
150*/
151
152    App = {{App}} {
153
154        audio_graph: {
155            root: <Mixer> {
156                c1 = <Instrument> {
157                    <IronFish> {}
158                }
159            }
160        }
161        ui: <Root>{
162            main_window = <Window> {
163                window: {inner_size: vec2(1280, 1000)},
164                pass: {clear_color: #2A}
165                block_signal_event: true;
166                /*body2 = <View>{
167                    step4 = <BlurStage>{
168                        width: Fill,
169                        height: Fill,
170                        draw_bg:{blury: 0.0, blurx: 10.0}
171                        step3 = <BlurStage>{
172                            width: Fill,
173                            height: Fill,
174                            draw_bg:{blury: 10.0, blurx: 0.0}
175                            step2 = <BlurStage>{
176                                width: Fill,
177                                height: Fill,
178                                draw_bg:{blury: 7.07, blurx: 7.07}
179                                step1 = <BlurStage>{
180                                    width: Fill,
181                                    height: Fill,
182                                    draw_bg:{blury: -7.07, blurx: 7.07}
183                                    <AppDesktop> {}
184                                }
185                            }
186                        }
187                    }
188                }*/
189                body = <AppDesktop> {}
190                /*<View>
191                {
192                    
193                    width: Fill,
194                    height: Fill,
195                    
196                    shadowstep = <ShadowStage> {
197                        width: Fill,
198                        height: Fill,
199                        draw_bg:{shadowy: 10.0, shadowx: 10.0, shadowopacity: 2.0}
200                        padding: 10
201                        <AppDesktop> {}
202                    }
203                }*/
204            }
205        }
206    }
207}
208app_main!(App);
209
210pub struct SynthPreset {
211    pub id: LiveId,
212    pub name: String,
213    pub fav: bool,
214}
215
216#[derive(Live)]
217pub struct App {
218    #[live]
219    ui: WidgetRef,
220    #[rust]
221    _presets: Vec<SynthPreset>,
222    #[live]
223    audio_graph: AudioGraph,
224    #[rust]
225    midi_input: MidiInput,
226}
227
228impl LiveRegister for App {
229    fn live_register(cx: &mut Cx) {
230        crate::makepad_audio_widgets::live_design(cx);
231        crate::makepad_audio_graph::live_design(cx);
232        crate::makepad_synth_ironfish::live_design(cx);
233        crate::sequencer::live_design(cx);
234        crate::app_desktop::live_design(cx);
235        crate::app_mobile::live_design(cx);
236    }
237}
238
239impl LiveHook for App{
240    fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
241        if apply.from.is_update_from_doc(){
242            self.init_ui_state(cx);
243        }
244    }
245}
246
247impl App {
248    pub fn init_ui_state(&mut self, cx:&mut Cx){
249        let ui = self.ui.clone();
250        let ironfish = self.audio_graph.by_type::<IronFish>().unwrap();
251        let db = DataBindingStore::from_nodes(ironfish.settings.live_read());
252        Self::data_bind(db.data_to_widgets(cx, &ui));
253    }
254    
255    pub fn data_bind(mut db: DataBindingMap) {
256        // sequencer
257        db.bind(id!(sequencer.playing), ids!(playpause));
258        db.bind(id!(sequencer.bpm), ids!(speed.slider));
259        db.bind(id!(sequencer.rootnote), ids!(rootnote.dropdown));
260        db.bind(id!(sequencer.scale), ids!(scaletype.dropdown));
261        db.bind(id!(arp.enabled), ids!(arp.checkbox));
262        db.bind(id!(arp.octaves), ids!(arpoctaves.slider));
263
264        // Mixer panel
265        db.bind(id!(osc_balance), ids!(balance.slider));
266        db.bind(id!(noise), ids!(noise.slider));
267        db.bind(id!(sub_osc), ids!(sub.slider));
268        db.bind(id!(portamento), ids!(porta.slider));
269
270        // DelayFX Panel
271        db.bind(id!(delay.delaysend), ids!(delaysend.slider));
272        db.bind(id!(delay.delayfeedback), ids!(delayfeedback.slider));
273
274        db.bind(id!(bitcrush.enable), ids!(crushenable.checkbox));
275        db.bind(id!(bitcrush.amount), ids!(crushamount.slider));
276
277        db.bind(id!(delay.difference), ids!(delaydifference.slider));
278        db.bind(id!(delay.cross), ids!(delaycross.slider));
279        db.bind(id!(delay.length), ids!(delaylength.slider));
280
281        // Chorus panel
282        db.bind(id!(chorus.mix), ids!(chorusmix.slider));
283        db.bind(id!(chorus.mindelay), ids!(chorusdelay.slider));
284        db.bind(id!(chorus.moddepth), ids!(chorusmod.slider));
285        db.bind(id!(chorus.rate), ids!(chorusrate.slider));
286        db.bind(id!(chorus.phasediff), ids!(chorusphase.slider));
287        db.bind(id!(chorus.feedback), ids!(chorusfeedback.slider));
288
289        // Reverb panel
290        db.bind(id!(reverb.mix), ids!(reverbmix.slider));
291        db.bind(id!(reverb.feedback), ids!(reverbfeedback.slider));
292
293        //LFO Panel
294        db.bind(id!(lfo.rate), ids!(rate.slider));
295        db.bind(id!(filter1.lfo_amount), ids!(lfoamount.slider));
296        db.bind(id!(lfo.synconkey), ids!(sync.checkbox));
297        
298        //Volume Envelope
299        db.bind(id!(volume_envelope.a), ids!(vol_env.attack.slider));
300        db.bind(id!(volume_envelope.h), ids!(vol_env.hold.slider));
301        db.bind(id!(volume_envelope.d), ids!(vol_env.decay.slider));
302        db.bind(id!(volume_envelope.s), ids!(vol_env.sustain.slider));
303        db.bind(id!(volume_envelope.r), ids!(vol_env.release.slider));
304
305        //Mod Envelope
306        db.bind(id!(mod_envelope.a), ids!(mod_env.attack.slider));
307        db.bind(id!(mod_envelope.h), ids!(mod_env.hold.slider));
308        db.bind(id!(mod_envelope.d), ids!(mod_env.decay.slider));
309        db.bind(id!(mod_envelope.s), ids!(mod_env.sustain.slider));
310        db.bind(id!(mod_envelope.r), ids!(mod_env.release.slider));
311        db.bind(id!(filter1.envelope_amount), ids!(modamount.slider));
312
313        // Filter panel
314        //db.bind(id!(filter1.filter_type), ids!(filter_type.dropdown));
315        db.bind(id!(filter1.cutoff), ids!(cutoff.slider));
316        db.bind(id!(filter1.resonance), ids!(resonance.slider));
317
318        // Osc1 panel
319        db.bind(id!(supersaw1.spread), ids!(osc1.supersaw.spread.slider));
320        db.bind(id!(supersaw1.diffuse), ids!(osc1.supersaw.diffuse.slider));
321        db.bind(id!(supersaw1.spread), ids!(osc1.supersaw.spread.slider));
322        db.bind(id!(supersaw1.diffuse), ids!(osc1.supersaw.diffuse.slider));
323        db.bind(id!(supersaw1.spread), ids!(osc1.hypersaw.spread.slider));
324        db.bind(id!(supersaw1.diffuse), ids!(osc1.hypersaw.diffuse.slider));
325
326        db.bind(id!(osc1.osc_type), ids!(osc1.type.dropdown));
327        db.bind(id!(osc1.transpose), ids!(osc1.transpose.slider));
328        db.bind(id!(osc1.detune), ids!(osc1.detune.slider));
329        db.bind(id!(osc1.harmonic), ids!(osc1.harmonicshift.slider));
330        db.bind(id!(osc1.harmonicenv), ids!(osc1.harmonicenv.slider));
331        db.bind(id!(osc1.harmoniclfo), ids!(osc1.harmoniclfo.slider));
332
333        // Osc2 panel
334        db.bind(id!(supersaw2.spread), ids!(osc2.supersaw.spread.slider));
335        db.bind(id!(supersaw2.diffuse), ids!(osc2.supersaw.diffuse.slider));
336        db.bind(id!(supersaw2.spread), ids!(osc2.supersaw.spread.slider));
337        db.bind(id!(supersaw2.diffuse), ids!(osc2.supersaw.diffuse.slider));
338        db.bind(id!(supersaw2.spread), ids!(osc2.hypersaw.spread.slider));
339        db.bind(id!(supersaw2.diffuse), ids!(osc2.hypersaw.diffuse.slider));
340
341        db.bind(id!(osc2.osc_type), ids!(osc2.type.dropdown));
342        db.bind(id!(osc2.transpose), ids!(osc2.transpose.slider));
343        db.bind(id!(osc2.detune), ids!(osc2.detune.slider));
344        db.bind(id!(osc2.harmonic), ids!(osc2.harmonicshift.slider));
345        db.bind(id!(osc2.harmonicenv), ids!(osc2.harmonicenv.slider));
346        db.bind(id!(osc2.harmoniclfo), ids!(osc2.harmoniclfo.slider));
347
348        db.bind(id!(blur.size), ids!(blursize.slider));
349        db.bind(id!(blur.std), ids!(blurstd.slider));
350
351
352
353
354        db.bind(id!(shadow.opacity), ids!(shadowopacity.slider));
355        db.bind(id!(shadow.x), ids!(shadowx.slider));
356        db.bind(id!(shadow.y), ids!(shadowy.slider));
357
358        // sequencer
359        db.bind(id!(sequencer.steps), ids!(sequencer));
360
361        db.apply(id!(osc1.osc_type), ids!(osc1.supersaw, visible), |v| {
362            v.enum_eq(id!(SuperSaw))
363        });
364        db.apply(id!(osc2.osc_type), ids!(osc2.supersaw, visible), |v| {
365            v.enum_eq(id!(SuperSaw))
366        });
367        db.apply(id!(osc1.osc_type), ids!(osc1.hypersaw, visible), |v| {
368            v.enum_eq(id!(HyperSaw))
369        });
370        db.apply(id!(osc2.osc_type), ids!(osc2.hypersaw, visible), |v| {
371            v.enum_eq(id!(HyperSaw))
372        });
373        db.apply(id!(osc1.osc_type), ids!(osc1.harmonic, visible), |v| {
374            v.enum_eq(id!(HarmonicSeries))
375        });
376        db.apply(id!(osc2.osc_type), ids!(osc2.harmonic, visible), |v| {
377            v.enum_eq(id!(HarmonicSeries))
378        });
379
380        db.apply(
381            id!(mod_envelope.a),
382            ids!(mod_env.display, draw_bg.attack),
383            |v| v,
384        );
385        db.apply(
386            id!(mod_envelope.h),
387            ids!(mod_env.display, draw_bg.hold),
388            |v| v,
389        );
390        db.apply(
391            id!(mod_envelope.d),
392            ids!(mod_env.display, draw_bg.decay),
393            |v| v,
394        );
395        db.apply(
396            id!(mod_envelope.s),
397            ids!(mod_env.display, draw_bg.sustain),
398            |v| v,
399        );
400        db.apply(
401            id!(mod_envelope.r),
402            ids!(mod_env.display, draw_bg.release),
403            |v| v,
404        );
405        db.apply(
406            id!(volume_envelope.a),
407            ids!(vol_env.display, draw_bg.attack),
408            |v| v,
409        );
410        db.apply(
411            id!(volume_envelope.h),
412            ids!(vol_env.display, draw_bg.hold),
413            |v| v,
414        );
415        db.apply(
416            id!(volume_envelope.d),
417            ids!(vol_env.display, draw_bg.decay),
418            |v| v,
419        );
420        db.apply(
421            id!(volume_envelope.s),
422            ids!(vol_env.display, draw_bg.sustain),
423            |v| v,
424        );
425        db.apply(
426            id!(volume_envelope.r),
427            ids!(vol_env.display, draw_bg.release),
428            |v| v,
429        );
430
431        /*db.apply(id!(shadow.opacity), ids!(shadowstep, draw_bg.shadowopacity), |v| v);
432        db.apply(id!(shadow.x), ids!(shadowstep, draw_bg.shadowx), |v| v);
433        db.apply(id!(shadow.y), ids!(shadowstep, draw_bg.shadowy), |v| v);
434
435        db.apply(id!(blur.size), ids!(step1, draw_bg.blursize), |v| v);
436        db.apply(id!(blur.std), ids!(step1, draw_bg.blurstd), |v| v);
437        db.apply(id!(blur.size), ids!(step2, draw_bg.blursize), |v| v);
438        db.apply(id!(blur.std), ids!(step2, draw_bg.blurstd), |v| v);
439        db.apply(id!(blur.size), ids!(step3, draw_bg.blursize), |v| v);
440        db.apply(id!(blur.std), ids!(step3, draw_bg.blurstd), |v| v);
441        db.apply(id!(blur.size), ids!(step4, draw_bg.blursize), |v| v);
442        db.apply(id!(blur.std), ids!(step4, draw_bg.blurstd), |v| v);*/
443    }
444}
445
446impl MatchEvent for App {
447    fn handle_startup(&mut self, cx: &mut Cx) {
448        self.preset(cx,0,false);
449        self.ui.piano(id!(piano)).set_key_focus(cx);
450        self.midi_input = cx.midi_input();
451    }
452
453    fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
454        
455        let ui = self.ui.clone();
456        let piano = ui.piano(id!(piano));
457
458        ui.radio_button_set(ids!(oscillators.tab1, oscillators.tab2,))
459            .selected_to_visible(cx, &ui, actions, ids!(oscillators.osc1, oscillators.osc2,));
460
461        ui.radio_button_set(ids!(filter_modes.tab1, filter_modes.tab2,))
462            .selected_to_visible(
463                cx,
464                &ui,
465                actions,
466                ids!(preset_pages.tab1_frame, preset_pages.tab2_frame,),
467            );
468
469        ui.radio_button_set(ids!(
470            mobile_modes.tab1,
471            mobile_modes.tab2,
472            mobile_modes.tab3,
473        ))
474        .selected_to_visible(
475            cx,
476            &ui,
477            actions,
478            ids!(
479                application_pages.tab1_frame,
480                application_pages.tab2_frame,
481                application_pages.tab3_frame,
482            ),
483        );
484
485        for note in piano.notes_played(&actions) {
486            self.audio_graph.send_midi_data(
487                MidiNote {
488                    channel: 0,
489                    is_on: note.is_on,
490                    note_number: note.note_number,
491                    velocity: note.velocity,
492                }
493                .into(),
494            );
495        }
496
497        if ui.button_set(ids!(panic)).clicked(&actions) {
498            //log!("hello world");
499            cx.midi_reset();
500            self.audio_graph.all_notes_off();
501        }
502
503        let sequencer = ui.sequencer(id!(sequencer));
504        // lets fetch and update the tick.
505
506        if ui.button_set(ids!(clear_grid)).clicked(&actions) {
507            sequencer.clear_grid(cx);
508        }
509
510        if ui.button_set(ids!(grid_down)).clicked(&actions) {
511            sequencer.grid_down(cx);
512        }
513
514        if ui.button_set(ids!(grid_up)).clicked(&actions) {
515            sequencer.grid_up(cx);
516        }
517        
518      
519        if let Some((index,km)) = ui.button_set(ids!(preset_1, preset_2, preset_3, preset_4, preset_5, preset_6, preset_7,preset_8)).which_clicked_modifiers(&actions){
520            self.preset(cx, index, km.shift);
521        }
522        
523        let mut db = DataBindingStore::new();
524        db.data_bind(cx, actions, &ui, Self::data_bind);
525
526        
527        let ironfish = self.audio_graph.by_type::<IronFish>().unwrap();
528
529        //sequencer.set_step(ironfish.sequencer);
530
531        ironfish.settings.apply_over(cx, &db.nodes);
532    }
533
534    fn handle_midi_ports(&mut self, cx: &mut Cx, ports: &MidiPortsEvent) {
535        cx.use_midi_inputs(&ports.all_inputs());
536    }
537
538    fn handle_audio_devices(&mut self, cx: &mut Cx, devices: &AudioDevicesEvent) {
539        cx.use_audio_outputs(&devices.default_output());
540    }
541
542    fn handle_signal(&mut self, cx: &mut Cx) {
543        let piano = self.ui.piano_set(ids!(piano));
544        while let Some((_, data)) = self.midi_input.receive() {
545            self.audio_graph.send_midi_data(data);
546            if let Some(note) = data.decode().on_note() {
547                piano.set_note(cx, note.is_on, note.note_number)
548            }
549        }
550    }
551}
552impl App{
553    #[cfg(target_arch = "wasm32")]
554    pub fn preset(&mut self, _cx: &mut Cx, _index: usize, _save: bool) {
555        
556    }
557    
558    #[cfg(not(target_arch = "wasm32"))]
559    pub fn preset(&mut self, cx: &mut Cx, index: usize, save: bool) {
560        use std::fs::File;
561        use std::io::prelude::*;
562        
563        let ironfish = self.audio_graph.by_type::<IronFish>().unwrap();
564        
565        let file_name = format!("examples/ironfish/preset_{}.txt", index);
566        if save {
567            let nodes = ironfish.settings.live_read();
568            let data = nodes.to_cbor(0).unwrap();
569            let data = makepad_miniz::compress_to_vec(&data, 10);
570            let data = makepad_base64::base64_encode(&data, &makepad_base64::BASE64_URL_SAFE);
571            log!("Saving preset {}", file_name);
572            let mut file = File::create(&file_name).unwrap();
573            file.write_all(&data).unwrap();
574        }
575        else if let Ok(mut file) = std::fs::File::open(&file_name) {
576            log!("Loading preset {}", file_name);
577            let mut data = Vec::new();
578            file.read_to_end(&mut data).unwrap();
579            if let Ok(data) = makepad_base64::base64_decode(&data) {
580                if let Ok(data) = makepad_miniz::decompress_to_vec(&data) {
581                    let mut nodes = Vec::new();
582                    nodes.from_cbor(&data).unwrap();
583                    ironfish.settings.apply_over(cx, &nodes);
584                    self.init_ui_state(cx);
585                    //self.imgui.root_frame().bind_read(cx, &nodes);
586                }
587                else {
588                    log!("Error decompressing preset");
589                }
590            }
591            else {
592                log!("Error base64 decoding preset");
593            }
594        }
595    }
596}
597
598impl AppMain for App {
599    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
600        self.match_event(cx, event);
601        self.ui.handle_event(cx, event, &mut Scope::empty());
602
603        self.audio_graph
604            .handle_event_with(cx, event, &mut |cx, action| {
605                let display_audio = self.ui.display_audio_set(ids!(display_audio));
606                match action {
607                    AudioGraphAction::DisplayAudio { buffer, voice, .. } => {
608                        display_audio.process_buffer(cx, None, voice, buffer, 1.0);
609                    }
610                    AudioGraphAction::VoiceOff { voice } => {
611                        display_audio.voice_off(cx, voice);
612                    }
613                };
614            });
615    }
616}