| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- // Package app is the Wails service the frontend calls. It owns the current
- // project and returns a fresh State after every call; the frontend keeps no
- // project data of its own (build plan 4.6 and 5).
- package app
- import (
- "fmt"
- "sync"
- "git.monoexp.com/bajdev/mpckit/internal/project"
- )
- // Version is set at build time with
- // -ldflags "-X git.monoexp.com/bajdev/mpckit/internal/app.Version=v1.0.0".
- var Version = "dev"
- // DefaultName is the name of a new project.
- const DefaultName = "NewKit"
- // Pad is one pad as the frontend shows it.
- type Pad struct {
- Slot int `json:"slot"`
- Label string `json:"label"` // "A01" .. "H16"
- Name string `json:"name"` // sample name; empty for an empty pad
- File string `json:"file"` // pool file name
- Coarse int `json:"coarse"`
- Fine int `json:"fine"`
- MuteGroup int `json:"muteGroup"`
- HasNotes bool `json:"hasNotes"`
- }
- // Sequence is one existing sequence.
- type Sequence struct {
- Slot int `json:"slot"`
- Label string `json:"label"` // slot label, "A01" .. "H16"
- Name string `json:"name"`
- BPM float64 `json:"bpm"`
- Bars int `json:"bars"`
- HasNotes bool `json:"hasNotes"`
- }
- // Limits are the ranges the UI offers, from the device checks.
- type Limits struct {
- Pads int `json:"pads"`
- MaxSamples int `json:"maxSamples"`
- MaxSequences int `json:"maxSequences"`
- MinCoarse int `json:"minCoarse"`
- MaxCoarse int `json:"maxCoarse"`
- MinFine int `json:"minFine"`
- MaxFine int `json:"maxFine"`
- MaxMuteGroup int `json:"maxMuteGroup"`
- MinBPM float64 `json:"minBPM"`
- MaxBPM float64 `json:"maxBPM"`
- MaxBars int `json:"maxBars"`
- }
- // State is everything the frontend shows about the project.
- type State struct {
- Name string `json:"name"`
- Dirty bool `json:"dirty"`
- Pads []Pad `json:"pads"` // all 128, in slot order
- Sequences []Sequence `json:"sequences"` // existing ones, in slot order
- SampleCount int `json:"sampleCount"`
- SequenceCount int `json:"sequenceCount"`
- Limits Limits `json:"limits"`
- }
- // About is the text for the About dialog.
- type About struct {
- Version string `json:"version"`
- License string `json:"license"`
- Repo string `json:"repo"`
- }
- // Service is registered with Wails; its exported methods become the
- // frontend bindings.
- type Service struct {
- mu sync.Mutex
- p *project.Project
- }
- // NewService returns a service holding a new, empty project.
- func NewService() *Service {
- s := &Service{}
- s.reset()
- return s
- }
- func (s *Service) reset() {
- p, err := project.New(DefaultName)
- if err != nil {
- panic(err) // the embedded template and a constant name
- }
- s.p = p
- }
- // New replaces the current project with a new, empty one.
- func (s *Service) New() State {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.reset()
- return s.state()
- }
- // State returns the current project's state.
- func (s *Service) State() State {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.state()
- }
- // Version returns the app version.
- func (s *Service) Version() string { return Version }
- // About returns the About dialog text.
- func (s *Service) About() About {
- return About{
- Version: Version,
- License: "MIT License, copyright (c) 2026 bajdev",
- Repo: "https://git.monoexp.com/bajdev/mpckit",
- }
- }
- // Label returns the label the UI and the device use for a pad or sequence
- // slot: bank letter and two-digit pad number, e.g. 0 -> "A01", 17 -> "B02".
- func Label(slot int) string {
- return fmt.Sprintf("%c%02d", 'A'+slot/16, slot%16+1)
- }
- func (s *Service) state() State {
- p := s.p
- st := State{
- Name: p.Name,
- Dirty: p.Dirty,
- Pads: make([]Pad, 0, project.Pads),
- Sequences: []Sequence{},
- SampleCount: p.SampleCount(),
- Limits: Limits{
- Pads: project.Pads,
- MaxSamples: project.MaxSamples,
- MaxSequences: project.MaxSequences,
- MinCoarse: project.MinCoarse,
- MaxCoarse: project.MaxCoarse,
- MinFine: project.MinFine,
- MaxFine: project.MaxFine,
- MaxMuteGroup: project.MaxMuteGroup,
- MinBPM: project.MinBPM,
- MaxBPM: project.MaxBPM,
- MaxBars: project.MaxBars,
- },
- }
- for slot := 0; slot < project.Pads; slot++ {
- v, err := p.Pad(slot)
- if err != nil {
- // A loaded file with an odd pad; show it as empty.
- st.Pads = append(st.Pads, Pad{Slot: slot, Label: Label(slot)})
- continue
- }
- pad := Pad{Slot: slot, Label: Label(slot), Name: v.SampleName, File: v.File,
- Coarse: v.Coarse, Fine: v.Fine, MuteGroup: v.MuteGroup}
- if !v.Empty() {
- pad.HasNotes = p.PadHasNotes(slot)
- }
- st.Pads = append(st.Pads, pad)
- }
- for _, q := range p.Sequences() {
- st.Sequences = append(st.Sequences, Sequence{Slot: q.Slot, Label: Label(q.Slot),
- Name: q.Name, BPM: q.BPM, Bars: q.Bars, HasNotes: q.HasNotes})
- }
- st.SequenceCount = len(st.Sequences)
- return st
- }
|