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};
6live_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::*;
19App = {{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 body = <AppDesktop> {}
190 }
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 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 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 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 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 db.bind(id!(reverb.mix), ids!(reverbmix.slider));
291 db.bind(id!(reverb.feedback), ids!(reverbfeedback.slider));
292
293 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 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 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 db.bind(id!(filter1.cutoff), ids!(cutoff.slider));
316 db.bind(id!(filter1.resonance), ids!(resonance.slider));
317
318 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 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 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 }
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 cx.midi_reset();
500 self.audio_graph.all_notes_off();
501 }
502
503 let sequencer = ui.sequencer(id!(sequencer));
504 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 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 }
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}