|
@@ -8,6 +8,7 @@ import (
|
|
|
"encoding/hex"
|
|
"encoding/hex"
|
|
|
"errors"
|
|
"errors"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
|
|
+ "os"
|
|
|
"path/filepath"
|
|
"path/filepath"
|
|
|
"strings"
|
|
"strings"
|
|
|
"sync"
|
|
"sync"
|
|
@@ -40,6 +41,10 @@ type Pad struct {
|
|
|
// plays only this region, as the device does.
|
|
// plays only this region, as the device does.
|
|
|
Start int64 `json:"start"`
|
|
Start int64 `json:"start"`
|
|
|
End int64 `json:"end"`
|
|
End int64 `json:"end"`
|
|
|
|
|
+ // Source is where the sample is copied from at export; Missing is set
|
|
|
|
|
+ // when that file no longer exists.
|
|
|
|
|
+ Source string `json:"source"`
|
|
|
|
|
+ Missing bool `json:"missing"`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Sequence is one existing sequence.
|
|
// Sequence is one existing sequence.
|
|
@@ -74,6 +79,7 @@ type State struct {
|
|
|
Pads []Pad `json:"pads"` // all 128, in slot order
|
|
Pads []Pad `json:"pads"` // all 128, in slot order
|
|
|
Sequences []Sequence `json:"sequences"` // existing ones, in slot order
|
|
Sequences []Sequence `json:"sequences"` // existing ones, in slot order
|
|
|
SampleCount int `json:"sampleCount"`
|
|
SampleCount int `json:"sampleCount"`
|
|
|
|
|
+ MissingCount int `json:"missingCount"` // pool files whose source is missing
|
|
|
SequenceCount int `json:"sequenceCount"`
|
|
SequenceCount int `json:"sequenceCount"`
|
|
|
Limits Limits `json:"limits"`
|
|
Limits Limits `json:"limits"`
|
|
|
}
|
|
}
|
|
@@ -116,6 +122,29 @@ type Service struct {
|
|
|
mu sync.Mutex
|
|
mu sync.Mutex
|
|
|
p *project.Project
|
|
p *project.Project
|
|
|
dialogs Dialogs
|
|
dialogs Dialogs
|
|
|
|
|
+ closer func() // closes the window without asking; set by main.go
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SetCloser sets what CloseWindow calls.
|
|
|
|
|
+//
|
|
|
|
|
+//wails:ignore
|
|
|
|
|
+func (s *Service) SetCloser(f func()) { s.closer = f }
|
|
|
|
|
+
|
|
|
|
|
+// Dirty reports whether the project has changes not yet exported.
|
|
|
|
|
+//
|
|
|
|
|
+//wails:ignore
|
|
|
|
|
+func (s *Service) Dirty() bool {
|
|
|
|
|
+ s.mu.Lock()
|
|
|
|
|
+ defer s.mu.Unlock()
|
|
|
|
|
+ return s.p.Dirty
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// CloseWindow closes the app without asking about unsaved changes. The
|
|
|
|
|
+// frontend calls it after the user chose to discard them.
|
|
|
|
|
+func (s *Service) CloseWindow() {
|
|
|
|
|
+ if s.closer != nil {
|
|
|
|
|
+ s.closer()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// NewService returns a service holding a new, empty project.
|
|
// NewService returns a service holding a new, empty project.
|
|
@@ -188,6 +217,12 @@ func (s *Service) state() State {
|
|
|
MaxBars: project.MaxBars,
|
|
MaxBars: project.MaxBars,
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
+ missing := map[string]bool{} // pool file -> source missing
|
|
|
|
|
+ for _, f := range p.PoolFiles() {
|
|
|
|
|
+ src := p.Sources[f]
|
|
|
|
|
+ info, err := os.Stat(src)
|
|
|
|
|
+ missing[f] = src == "" || err != nil || !info.Mode().IsRegular()
|
|
|
|
|
+ }
|
|
|
for slot := 0; slot < project.Pads; slot++ {
|
|
for slot := 0; slot < project.Pads; slot++ {
|
|
|
v, err := p.Pad(slot)
|
|
v, err := p.Pad(slot)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -199,7 +234,11 @@ func (s *Service) state() State {
|
|
|
Coarse: v.Coarse, Fine: v.Fine, MuteGroup: v.MuteGroup, Start: v.Start, End: v.End}
|
|
Coarse: v.Coarse, Fine: v.Fine, MuteGroup: v.MuteGroup, Start: v.Start, End: v.End}
|
|
|
if !v.Empty() {
|
|
if !v.Empty() {
|
|
|
pad.HasNotes = p.PadHasNotes(slot)
|
|
pad.HasNotes = p.PadHasNotes(slot)
|
|
|
- pad.Audio = audioURL(slot, p.Sources[v.File])
|
|
|
|
|
|
|
+ pad.Source = p.Sources[v.File]
|
|
|
|
|
+ pad.Missing = missing[v.File]
|
|
|
|
|
+ if !pad.Missing {
|
|
|
|
|
+ pad.Audio = audioURL(slot, pad.Source)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
st.Pads = append(st.Pads, pad)
|
|
st.Pads = append(st.Pads, pad)
|
|
|
}
|
|
}
|
|
@@ -208,6 +247,11 @@ func (s *Service) state() State {
|
|
|
Name: q.Name, BPM: q.BPM, Bars: q.Bars, HasNotes: q.HasNotes})
|
|
Name: q.Name, BPM: q.BPM, Bars: q.Bars, HasNotes: q.HasNotes})
|
|
|
}
|
|
}
|
|
|
st.SequenceCount = len(st.Sequences)
|
|
st.SequenceCount = len(st.Sequences)
|
|
|
|
|
+ for _, m := range missing {
|
|
|
|
|
+ if m {
|
|
|
|
|
+ st.MissingCount++
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
return st
|
|
return st
|
|
|
}
|
|
}
|
|
|
|
|
|