package main import ( "fmt" "math" "time" "github.com/egoist/mygo/ui" mujica "mujicaui" ) func volumeDemo(c *ui.Context, log func(string)) { type state struct { volume float64 muted, disabled bool } s := ui.Local(c.Root(), "351", func() state { return state{volume: 0.7} }) mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") }) mediaSection(c, "Live", func() { if mujica.VolumeControl(c, &s.volume, &s.muted, mujica.VolumeControlOptions{Disabled: s.disabled}).Changed() { log(fmt.Sprintf("volume %.0f%%, muted %v", s.volume*100, s.muted)) } }) } func speedDemo(c *ui.Context, log func(string)) { type state struct { rate float64 disabled bool } s := ui.Local(c.Root(), "352", func() state { return state{rate: 1} }) mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") }) mediaSection(c, "Live", func() { if mujica.PlaybackSpeedControl(c, &s.rate, mujica.PlaybackSpeedControlOptions{Disabled: s.disabled}).Changed() { log(fmt.Sprintf("rate %v×", s.rate)) } }) } func mediaControlsDemo(c *ui.Context, log func(string)) { type state struct { st mujica.MediaControlsState track int disabled bool } s := ui.Local(c.Root(), "353", func() state { return state{} }) tracks := []string{"Ave Mujica", "Imprisoned XII", "KiLLKiSS", "Symbol I: △"} s.st.NoPrevious, s.st.NoNext = s.track == 0, s.track == len(tracks)-1 mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") }) mediaSection(c, "Live", func() { ui.Text(c, fmt.Sprintf("Now: %s (%d of %d)", tracks[s.track], s.track+1, len(tracks))) mujica.MediaControls(c, s.st, mujica.MediaControlsOptions{ OnPrevious: func() { s.track--; log("previous") }, OnNext: func() { s.track++; log("next") }, OnPlay: func() { s.st.Playing = true; log("play") }, OnPause: func() { s.st.Playing = false; log("pause") }, OnLoop: func(m mujica.LoopMode) { s.st.Loop = m; log(fmt.Sprintf("loop mode %d", m)) }, OnShuffle: func(on bool) { s.st.Shuffle = on; log(fmt.Sprintf("shuffle %v", on)) }, Disabled: s.disabled, }) }) } func playlistDemo(c *ui.Context, log func(string)) { type state struct { items []mujica.PlaylistItem current string disabled bool } s := ui.Local(c.Root(), "354", func() state { titles := []string{"Ave Mujica", "Imprisoned XII", "KiLLKiSS", "Symbol I: △", "Choir 'S' Choir", "Georgette Me, Georgette You"} items := make([]mujica.PlaylistItem, len(titles)) for i, t := range titles { items[i] = mujica.PlaylistItem{ID: fmt.Sprint("t", i), Title: t, Subtitle: fmt.Sprintf("Act %d", i+1), Duration: time.Duration(200+i*17) * time.Second} } return state{items: items, current: "t1"} }) mediaSection(c, "Parameters", func() { ui.Row(c).Gap(16).AlignItems(ui.Center).Children(func() { mediaCheck(c, log, &s.disabled, "Disabled") if mujica.Button(c, "Add track", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() { id := fmt.Sprint("t", len(s.items)+int(time.Now().UnixNano()%1000000)) s.items = append(s.items, mujica.PlaylistItem{ID: id, Title: "Encore " + id, Duration: 3 * time.Minute}) log("added " + id) } }) }) mediaSection(c, "Live", func() { ui.Column(c).MaxWidth(520).Children(func() { r := mujica.Playlist(c, &s.items, mujica.PlaylistOptions{Current: s.current, Disabled: s.disabled}) switch { case r.Played != "": s.current = r.Played log("play " + r.Played) case r.Removed != "": log("removed " + r.Removed) case r.Moved: log("reordered") } }) }) } func subtitleDemo(c *ui.Context, log func(string)) { type state struct { cues []mujica.SubtitleCue disabled bool } s := ui.Local(c.Root(), "355", func() state { return state{cues: []mujica.SubtitleCue{ {Start: 0, End: 2500 * time.Millisecond, Text: "The curtain rises."}, {Start: 2 * time.Second, End: 5 * time.Second, Text: "Welcome to the masquerade."}, {Start: 6 * time.Second, End: 9 * time.Second, Text: "Ave Mujica."}, }} }) mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") }) mediaSection(c, "Live", func() { ui.Column(c).MaxWidth(720).Children(func() { if mujica.SubtitleEditor(c, &s.cues, mujica.SubtitleEditorOptions{Height: 260, Disabled: s.disabled}).Changed() { log(fmt.Sprintf("%d cues, %d issues", len(s.cues), len(mujica.CheckSubtitles(s.cues)))) } }) }) } func recorderDemo(c *ui.Context, log func(string)) { type state struct { st mujica.RecorderState since time.Time disabled bool accumulated time.Duration } s := ui.Local(c.Root(), "356", func() state { return state{} }) if s.st.Status == mujica.RecorderRecording { s.st.Elapsed = s.accumulated + c.Now().Sub(s.since) // The clock changes each second; ask for that frame only. c.After(time.Second - s.st.Elapsed%time.Second) } mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") }) mediaSection(c, "Live", func() { mujica.ScreenRecorderControls(c, s.st, mujica.ScreenRecorderControlsOptions{ OnStart: func() { s.st, s.since, s.accumulated = mujica.RecorderState{Status: mujica.RecorderRecording}, c.Now(), 0 log("start") }, OnPause: func() { s.st.Status, s.accumulated = mujica.RecorderPaused, s.st.Elapsed; log("pause") }, OnResume: func() { s.st.Status, s.since = mujica.RecorderRecording, c.Now(); log("resume") }, OnStop: func() { log("stop at " + mediaClockLabel(s.st.Elapsed)); s.st = mujica.RecorderState{} }, Disabled: s.disabled, }) }) } func cameraDemo(c *ui.Context, log func(string)) { type state struct { mirror, signal, disabled bool } s := ui.Local(c.Root(), "357", func() state { return state{mirror: true, signal: true} }) mediaSection(c, "Parameters", func() { ui.Row(c).Gap(16).Children(func() { mediaCheck(c, log, &s.signal, "Signal") mediaCheck(c, log, &s.disabled, "Disabled") }) }) frame := mujica.CameraFrame{} if s.signal { frame = mujica.CameraFrame{Image: mediaPhotos[2], Seq: 1} } mediaSection(c, "Live", func() { ui.Column(c).MaxWidth(480).Children(func() { if mujica.CameraPreview(c, frame, &s.mirror, mujica.CameraPreviewOptions{Disabled: s.disabled}).Changed() { log(fmt.Sprintf("mirror %v", s.mirror)) } }) }) } func micDemo(c *ui.Context, log func(string)) { type state struct{ live bool } s := ui.Local(c.Root(), "358", func() state { return state{live: true} }) mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.live, "Live input") }) level := float32(0.3) if s.live { t := float64(c.Now().UnixMilli()) / 1000 level = float32(math.Min(1, 0.15+0.8*math.Abs(math.Sin(t*1.3)*math.Sin(t*0.37)))) c.AnimationFrame() } mediaSection(c, "Live", func() { ui.Column(c).MaxWidth(420).Children(func() { mujica.MicLevelMeter(c, level, mujica.MicLevelMeterOptions{}) }) }) } func deviceDemo(c *ui.Context, log func(string)) { type state struct { sel mujica.DeviceSelection noCamera, disabled bool } s := ui.Local(c.Root(), "359", func() state { return state{sel: mujica.DeviceSelection{Microphone: "m1", Speaker: "s2"}} }) devices := mujica.MediaDevices{ Microphones: []mujica.MediaDevice{{ID: "m1", Name: "Built-in Microphone"}, {ID: "m2", Name: "Studio Condenser"}}, Cameras: []mujica.MediaDevice{{ID: "c1", Name: "FaceTime HD Camera"}}, Speakers: []mujica.MediaDevice{{ID: "s1", Name: "Built-in Speakers"}, {ID: "s2", Name: "Headphones"}}, } mediaSection(c, "Parameters", func() { ui.Row(c).Gap(16).Children(func() { before := s.noCamera mediaCheck(c, log, &s.noCamera, "No cameras") if s.noCamera != before { s.sel.Camera = "" } mediaCheck(c, log, &s.disabled, "Disabled") }) }) if s.noCamera { devices.Cameras = []mujica.MediaDevice{} } mediaSection(c, "Live", func() { ui.Column(c).MaxWidth(480).Children(func() { if mujica.DeviceSelector(c, &s.sel, devices, mujica.DeviceSelectorOptions{Disabled: s.disabled}).Changed() { log(fmt.Sprintf("microphone %q, camera %q, speaker %q", s.sel.Microphone, s.sel.Camera, s.sel.Speaker)) } }) }) }