mirror of
https://github.com/vsariola/sointu.git
synced 2026-02-16 05:03:18 -05:00
refactor(tracker): group Model methods, with each group in one source file
This commit is contained in:
parent
b93304adab
commit
86ca3fb300
@ -19,75 +19,297 @@ import (
|
||||
//go:generate go run generate/gmdls_entries.go
|
||||
//go:generate go run generate/clean_presets.go
|
||||
|
||||
//go:embed presets/*
|
||||
var instrumentPresetFS embed.FS
|
||||
// Preset returns a PresetModel, a view of the model used to manipulate
|
||||
// instrument presets.
|
||||
func (m *Model) Preset() *PresetModel { return (*PresetModel)(m) }
|
||||
|
||||
type PresetModel Model
|
||||
|
||||
// SearchTerm returns a String containing the search terms for finding the
|
||||
// presets.
|
||||
func (m *PresetModel) SearchTerm() String { return MakeString((*presetSearchTerm)(m)) }
|
||||
|
||||
type presetSearchTerm PresetModel
|
||||
|
||||
func (m *presetSearchTerm) Value() string { return m.d.PresetSearchString }
|
||||
func (m *presetSearchTerm) SetValue(value string) bool {
|
||||
if m.d.PresetSearchString == value {
|
||||
return false
|
||||
}
|
||||
m.d.PresetSearchString = value
|
||||
(*PresetModel)(m).updateCache()
|
||||
return true
|
||||
}
|
||||
|
||||
// NoGmDls returns a Bool toggling whether to show presets relying on gm.dls
|
||||
// samples.
|
||||
func (m *PresetModel) NoGmDls() Bool { return MakeBool((*presetNoGmDls)(m)) }
|
||||
|
||||
type presetNoGmDls PresetModel
|
||||
|
||||
func (m *presetNoGmDls) Value() bool { return m.presetData.cache.noGmDls }
|
||||
func (m *presetNoGmDls) SetValue(val bool) {
|
||||
if m.presetData.cache.noGmDls == val {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "g:")
|
||||
if val {
|
||||
m.d.PresetSearchString = "g:n " + m.d.PresetSearchString
|
||||
}
|
||||
(*PresetModel)(m).updateCache()
|
||||
}
|
||||
|
||||
// UserPresetsFilter returns a Bool toggling whether to show the user defined
|
||||
// presets.
|
||||
func (m *PresetModel) UserFilter() Bool { return MakeBool((*userPresetsFilter)(m)) }
|
||||
|
||||
type userPresetsFilter PresetModel
|
||||
|
||||
func (m *userPresetsFilter) Value() bool { return m.presetData.cache.kind == UserPresets }
|
||||
func (m *userPresetsFilter) SetValue(val bool) {
|
||||
if (m.presetData.cache.kind == UserPresets) == val {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "t:")
|
||||
if val {
|
||||
m.d.PresetSearchString = "t:u " + m.d.PresetSearchString
|
||||
}
|
||||
(*PresetModel)(m).updateCache()
|
||||
}
|
||||
func (m *userPresetsFilter) Enabled() bool { return true }
|
||||
|
||||
// BuiltinFilter return a Bool toggling whether to show the built-in
|
||||
// presets in the preset search results.
|
||||
func (m *PresetModel) BuiltinFilter() Bool { return MakeBool((*builtinPresetsFilter)(m)) }
|
||||
|
||||
type builtinPresetsFilter PresetModel
|
||||
|
||||
func (m *builtinPresetsFilter) Value() bool { return m.presetData.cache.kind == BuiltinPresets }
|
||||
func (m *builtinPresetsFilter) SetValue(val bool) {
|
||||
if (m.presetData.cache.kind == BuiltinPresets) == val {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "t:")
|
||||
if val {
|
||||
m.d.PresetSearchString = "t:b " + m.d.PresetSearchString
|
||||
}
|
||||
(*PresetModel)(m).updateCache()
|
||||
}
|
||||
|
||||
// ClearSearch returns an Action to clear the current preset search
|
||||
// term(s).
|
||||
func (m *PresetModel) ClearSearch() Action { return MakeAction((*clearPresetSearch)(m)) }
|
||||
|
||||
type clearPresetSearch PresetModel
|
||||
|
||||
func (m *clearPresetSearch) Enabled() bool { return len(m.d.PresetSearchString) > 0 }
|
||||
func (m *clearPresetSearch) Do() {
|
||||
m.d.PresetSearchString = ""
|
||||
(*PresetModel)(m).updateCache()
|
||||
}
|
||||
|
||||
// PresetDirList return a List of all the different preset directories.
|
||||
func (m *PresetModel) DirList() List { return MakeList((*presetDirList)(m)) }
|
||||
|
||||
type presetDirList PresetModel
|
||||
|
||||
func (m *presetDirList) Count() int { return len(m.presetData.dirs) + 1 }
|
||||
func (m *presetDirList) Selected() int { return m.presetData.cache.dirIndex + 1 }
|
||||
func (m *presetDirList) Selected2() int { return m.presetData.cache.dirIndex + 1 }
|
||||
func (m *presetDirList) SetSelected2(i int) {}
|
||||
func (m *presetDirList) SetSelected(i int) {
|
||||
i = min(max(i, 0), len(m.presetData.dirs))
|
||||
if i < 0 || i > len(m.presetData.dirs) {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "d:")
|
||||
if i > 0 {
|
||||
m.d.PresetSearchString = "d:" + m.presetData.dirs[i-1] + " " + m.d.PresetSearchString
|
||||
}
|
||||
(*PresetModel)(m).updateCache()
|
||||
}
|
||||
|
||||
// Dir returns the name of the directory at the given index in the preset
|
||||
// directory list.
|
||||
func (m *PresetModel) Dir(i int) string {
|
||||
if i < 1 || i > len(m.presetData.dirs) {
|
||||
return "---"
|
||||
}
|
||||
return m.presetData.dirs[i-1]
|
||||
}
|
||||
|
||||
// SearchResultList returns a List of the current preset search results.
|
||||
func (m *PresetModel) SearchResultList() List { return MakeList((*presetResultList)(m)) }
|
||||
|
||||
type presetResultList PresetModel
|
||||
|
||||
func (v *presetResultList) List() List { return List{v} }
|
||||
func (m *presetResultList) Count() int { return len(m.presetData.cache.results) }
|
||||
func (m *presetResultList) Selected() int {
|
||||
return min(max(m.presetData.presetIndex, 0), len(m.presetData.cache.results)-1)
|
||||
}
|
||||
func (m *presetResultList) Selected2() int { return m.Selected() }
|
||||
func (m *presetResultList) SetSelected2(i int) {}
|
||||
func (m *presetResultList) SetSelected(i int) {
|
||||
i = min(max(i, 0), len(m.presetData.cache.results)-1)
|
||||
if i < 0 || i >= len(m.presetData.cache.results) {
|
||||
return
|
||||
}
|
||||
m.presetData.presetIndex = i
|
||||
defer (*Model)(m).change("LoadPreset", PatchChange, MinorChange)()
|
||||
if m.d.InstrIndex < 0 {
|
||||
m.d.InstrIndex = 0
|
||||
}
|
||||
m.d.InstrIndex2 = m.d.InstrIndex
|
||||
for m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
m.d.Song.Patch = append(m.d.Song.Patch, defaultInstrument.Copy())
|
||||
}
|
||||
newInstr := m.presetData.cache.results[i].instr.Copy()
|
||||
newInstr.NumVoices = clamp(m.d.Song.Patch[m.d.InstrIndex].NumVoices, 1, vm.MAX_VOICES)
|
||||
(*Model)(m).assignUnitIDs(newInstr.Units)
|
||||
m.d.Song.Patch[m.d.InstrIndex] = newInstr
|
||||
}
|
||||
|
||||
// SearchResult returns the search result at the given index in the search
|
||||
// result list.
|
||||
func (m *PresetModel) SearchResult(i int) (name string, dir string, user bool) {
|
||||
if i < 0 || i >= len(m.presetData.cache.results) {
|
||||
return "", "", false
|
||||
}
|
||||
p := m.presetData.cache.results[i]
|
||||
return p.instr.Name, p.dir, p.user
|
||||
}
|
||||
|
||||
// Save returns an Action to save the current instrument as a user-defined
|
||||
// preset. It will not overwrite existing presets, but rather show a dialog to
|
||||
// confirm the overwrite.
|
||||
func (m *PresetModel) Save() Action { return MakeAction((*saveUserPreset)(m)) }
|
||||
|
||||
type saveUserPreset PresetModel
|
||||
|
||||
func (m *saveUserPreset) Enabled() bool {
|
||||
return m.d.InstrIndex >= 0 && m.d.InstrIndex < len(m.d.Song.Patch)
|
||||
}
|
||||
func (m *saveUserPreset) Do() {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userPresetsDir := filepath.Join(configDir, "sointu", "presets", m.presetData.cache.dir)
|
||||
instr := m.d.Song.Patch[m.d.InstrIndex]
|
||||
name := instrumentNameToFilename(instr.Name)
|
||||
fileName := filepath.Join(userPresetsDir, name+".yml")
|
||||
// if exists, do not overwrite
|
||||
if _, err := os.Stat(fileName); err == nil {
|
||||
m.dialog = OverwriteUserPresetDialog
|
||||
return
|
||||
}
|
||||
(*PresetModel)(m).Overwrite().Do()
|
||||
}
|
||||
|
||||
// OverwriteUserPreset returns an Action to overwrite the current instrument
|
||||
// as a user-defined preset.
|
||||
func (m *PresetModel) Overwrite() Action { return MakeAction((*overwriteUserPreset)(m)) }
|
||||
|
||||
type overwriteUserPreset PresetModel
|
||||
|
||||
func (m *overwriteUserPreset) Enabled() bool { return true }
|
||||
func (m *overwriteUserPreset) Do() {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userPresetsDir := filepath.Join(configDir, "sointu", "presets", m.presetData.cache.dir)
|
||||
instr := m.d.Song.Patch[m.d.InstrIndex]
|
||||
name := instrumentNameToFilename(instr.Name)
|
||||
fileName := filepath.Join(userPresetsDir, name+".yml")
|
||||
os.MkdirAll(userPresetsDir, 0755)
|
||||
data, err := yaml.Marshal(&instr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
os.WriteFile(fileName, data, 0644)
|
||||
m.dialog = NoDialog
|
||||
(*PresetModel)(m).presetData.load()
|
||||
(*PresetModel)(m).updateCache()
|
||||
}
|
||||
|
||||
// TryDeleteUserPreset returns an Action to display a dialog to confirm deletion
|
||||
// of an user preset.
|
||||
func (m *PresetModel) Delete() Action { return MakeAction((*tryDeleteUserPreset)(m)) }
|
||||
|
||||
type tryDeleteUserPreset PresetModel
|
||||
|
||||
func (m *tryDeleteUserPreset) Do() { m.dialog = DeleteUserPresetDialog }
|
||||
func (m *tryDeleteUserPreset) Enabled() bool {
|
||||
if m.presetData.presetIndex < 0 || m.presetData.presetIndex >= len(m.presetData.cache.results) {
|
||||
return false
|
||||
}
|
||||
return m.presetData.cache.results[m.presetData.presetIndex].user
|
||||
}
|
||||
|
||||
// DeleteUserPreset returns an Action to confirm the deletion of an user preset.
|
||||
func (m *PresetModel) ConfirmDelete() Action { return MakeAction((*deleteUserPreset)(m)) }
|
||||
|
||||
type deleteUserPreset PresetModel
|
||||
|
||||
func (m *deleteUserPreset) Enabled() bool { return (*Model)(m).Preset().Delete().Enabled() }
|
||||
func (m *deleteUserPreset) Do() {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p := m.presetData.cache.results[m.presetData.presetIndex]
|
||||
userPresetsDir := filepath.Join(configDir, "sointu", "presets")
|
||||
if p.dir != "" {
|
||||
userPresetsDir = filepath.Join(userPresetsDir, p.dir)
|
||||
}
|
||||
name := instrumentNameToFilename(p.instr.Name)
|
||||
fileName := filepath.Join(userPresetsDir, name+".yml")
|
||||
os.Remove(fileName)
|
||||
m.dialog = NoDialog
|
||||
(*PresetModel)(m).presetData.load()
|
||||
(*PresetModel)(m).updateCache()
|
||||
}
|
||||
|
||||
type (
|
||||
// GmDlsEntry is a single sample entry from the gm.dls file
|
||||
GmDlsEntry struct {
|
||||
Start int // sample start offset in words
|
||||
LoopStart int // loop start offset in words
|
||||
LoopLength int // loop length in words
|
||||
SuggestedTranspose int // suggested transpose in semitones, so that all samples play at same pitch
|
||||
Name string // sample Name
|
||||
presetData struct {
|
||||
presets []preset
|
||||
dirs []string
|
||||
presetIndex int
|
||||
|
||||
cache presetCache
|
||||
}
|
||||
|
||||
Preset struct {
|
||||
Directory string
|
||||
User bool
|
||||
NeedsGmDls bool
|
||||
Instr sointu.Instrument
|
||||
preset struct {
|
||||
dir string
|
||||
user bool
|
||||
needsGmDls bool
|
||||
instr sointu.Instrument
|
||||
}
|
||||
|
||||
Presets struct {
|
||||
Presets []Preset
|
||||
Dirs []string
|
||||
}
|
||||
|
||||
InstrumentPresetYieldFunc func(index int, item string) (ok bool)
|
||||
LoadPreset struct {
|
||||
Index int
|
||||
*Model
|
||||
}
|
||||
|
||||
PresetSearchString Model
|
||||
NoGmDlsFilter Model
|
||||
BuiltinPresetsFilter Model
|
||||
UserPresetsFilter Model
|
||||
PresetDirectory Model
|
||||
PresetKind Model
|
||||
ClearPresetSearch Model
|
||||
PresetDirList Model
|
||||
PresetResultList Model
|
||||
SaveUserPreset Model
|
||||
TryDeleteUserPreset Model
|
||||
DeleteUserPreset Model
|
||||
|
||||
ConfirmDeleteUserPresetAction Model
|
||||
OverwriteUserPreset Model
|
||||
|
||||
derivedPresetSearch struct {
|
||||
presetCache struct {
|
||||
dir string
|
||||
dirIndex int
|
||||
noGmDls bool
|
||||
kind PresetKindEnum
|
||||
kind presetKindEnum
|
||||
searchStrings []string
|
||||
results []Preset
|
||||
results []preset
|
||||
}
|
||||
|
||||
PresetKindEnum int
|
||||
presetKindEnum int
|
||||
)
|
||||
|
||||
const (
|
||||
BuiltinPresets PresetKindEnum = -1
|
||||
AllPresets PresetKindEnum = 0
|
||||
UserPresets PresetKindEnum = 1
|
||||
BuiltinPresets presetKindEnum = -1
|
||||
AllPresets presetKindEnum = 0
|
||||
UserPresets presetKindEnum = 1
|
||||
)
|
||||
|
||||
func (m *Model) updateDerivedPresetSearch() {
|
||||
func (m *PresetModel) updateCache() {
|
||||
// reset derived data, keeping the
|
||||
str := m.derived.presetSearch.searchStrings[:0]
|
||||
m.derived.presetSearch = derivedPresetSearch{searchStrings: str, dirIndex: -1}
|
||||
str := m.presetData.cache.searchStrings[:0]
|
||||
m.presetData.cache = presetCache{searchStrings: str, dirIndex: -1}
|
||||
// parse filters from the search string. in: dir, gmdls: yes/no, kind: builtin/user/all
|
||||
search := strings.TrimSpace(m.d.PresetSearchString)
|
||||
parts := strings.Fields(search)
|
||||
@ -95,69 +317,73 @@ func (m *Model) updateDerivedPresetSearch() {
|
||||
for _, part := range parts {
|
||||
if strings.HasPrefix(part, "d:") && len(part) > 2 {
|
||||
dir := strings.TrimSpace(part[2:])
|
||||
m.derived.presetSearch.dir = dir
|
||||
ind := slices.IndexFunc(m.presets.Dirs, func(c string) bool { return c == dir })
|
||||
m.derived.presetSearch.dirIndex = ind
|
||||
m.presetData.cache.dir = dir
|
||||
ind := slices.IndexFunc(m.presetData.dirs, func(c string) bool { return c == dir })
|
||||
m.presetData.cache.dirIndex = ind
|
||||
} else if strings.HasPrefix(part, "g:n") {
|
||||
m.derived.presetSearch.noGmDls = true
|
||||
m.presetData.cache.noGmDls = true
|
||||
} else if strings.HasPrefix(part, "t:") && len(part) > 2 {
|
||||
val := strings.TrimSpace(part[2:3])
|
||||
switch val {
|
||||
case "b":
|
||||
m.derived.presetSearch.kind = BuiltinPresets
|
||||
m.presetData.cache.kind = BuiltinPresets
|
||||
case "u":
|
||||
m.derived.presetSearch.kind = UserPresets
|
||||
m.presetData.cache.kind = UserPresets
|
||||
}
|
||||
} else {
|
||||
m.derived.presetSearch.searchStrings = append(m.derived.presetSearch.searchStrings, strings.ToLower(part))
|
||||
m.presetData.cache.searchStrings = append(m.presetData.cache.searchStrings, strings.ToLower(part))
|
||||
}
|
||||
}
|
||||
// update results
|
||||
m.derived.presetSearch.results = m.derived.presetSearch.results[:0]
|
||||
for _, p := range m.presets.Presets {
|
||||
if m.derived.presetSearch.kind == BuiltinPresets && p.User {
|
||||
m.presetData.cache.results = m.presetData.cache.results[:0]
|
||||
for _, p := range m.presetData.presets {
|
||||
if m.presetData.cache.kind == BuiltinPresets && p.user {
|
||||
continue
|
||||
}
|
||||
if m.derived.presetSearch.kind == UserPresets && !p.User {
|
||||
if m.presetData.cache.kind == UserPresets && !p.user {
|
||||
continue
|
||||
}
|
||||
if m.derived.presetSearch.dir != "" && p.Directory != m.derived.presetSearch.dir {
|
||||
if m.presetData.cache.dir != "" && p.dir != m.presetData.cache.dir {
|
||||
continue
|
||||
}
|
||||
if m.derived.presetSearch.noGmDls && p.NeedsGmDls {
|
||||
if m.presetData.cache.noGmDls && p.needsGmDls {
|
||||
continue
|
||||
}
|
||||
if len(m.derived.presetSearch.searchStrings) == 0 {
|
||||
if len(m.presetData.cache.searchStrings) == 0 {
|
||||
goto found
|
||||
}
|
||||
for _, s := range m.derived.presetSearch.searchStrings {
|
||||
if strings.Contains(strings.ToLower(p.Instr.Name), s) {
|
||||
for _, s := range m.presetData.cache.searchStrings {
|
||||
if strings.Contains(strings.ToLower(p.instr.Name), s) {
|
||||
goto found
|
||||
}
|
||||
}
|
||||
continue
|
||||
found:
|
||||
m.derived.presetSearch.results = append(m.derived.presetSearch.results, p)
|
||||
m.presetData.cache.results = append(m.presetData.cache.results, p)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Presets) load() {
|
||||
*m = Presets{}
|
||||
//go:embed presets/*
|
||||
var builtInPresetsFS embed.FS
|
||||
|
||||
func (m *presetData) load() {
|
||||
m.dirs = m.dirs[:0]
|
||||
m.presets = m.presets[:0]
|
||||
seenDir := make(map[string]bool)
|
||||
m.loadPresetsFromFs(instrumentPresetFS, false, seenDir)
|
||||
m.loadPresetsFromFs(builtInPresetsFS, false, seenDir)
|
||||
if configDir, err := os.UserConfigDir(); err == nil {
|
||||
userPresets := filepath.Join(configDir, "sointu")
|
||||
m.loadPresetsFromFs(os.DirFS(userPresets), true, seenDir)
|
||||
}
|
||||
sort.Sort(m)
|
||||
m.Dirs = make([]string, 0, len(seenDir))
|
||||
m.dirs = make([]string, 0, len(seenDir))
|
||||
for k := range seenDir {
|
||||
m.Dirs = append(m.Dirs, k)
|
||||
m.dirs = append(m.dirs, k)
|
||||
}
|
||||
sort.Strings(m.Dirs)
|
||||
sort.Strings(m.dirs)
|
||||
}
|
||||
|
||||
func (m *Presets) loadPresetsFromFs(fsys fs.FS, userDefined bool, seenDir map[string]bool) {
|
||||
func (m *presetData) loadPresetsFromFs(fsys fs.FS, userDefined bool, seenDir map[string]bool) {
|
||||
fs.WalkDir(fsys, "presets", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
@ -179,16 +405,16 @@ func (m *Presets) loadPresetsFromFs(fsys fs.FS, userDefined bool, seenDir map[st
|
||||
splitted = splitted[1:] // remove "presets" from the path
|
||||
instr.Name = filenameToInstrumentName(splitted[len(splitted)-1])
|
||||
dir := strings.Join(splitted[:len(splitted)-1], "/")
|
||||
preset := Preset{
|
||||
Directory: dir,
|
||||
User: userDefined,
|
||||
Instr: instr,
|
||||
NeedsGmDls: checkNeedsGmDls(instr),
|
||||
preset := preset{
|
||||
dir: dir,
|
||||
user: userDefined,
|
||||
instr: instr,
|
||||
needsGmDls: checkNeedsGmDls(instr),
|
||||
}
|
||||
if dir != "" {
|
||||
seenDir[dir] = true
|
||||
}
|
||||
m.Presets = append(m.Presets, preset)
|
||||
m.presets = append(m.presets, preset)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -217,125 +443,6 @@ func checkNeedsGmDls(instr sointu.Instrument) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Model) PresetSearchString() String { return MakeString((*PresetSearchString)(m)) }
|
||||
func (m *PresetSearchString) Value() string { return m.d.PresetSearchString }
|
||||
func (m *PresetSearchString) SetValue(value string) bool {
|
||||
if m.d.PresetSearchString == value {
|
||||
return false
|
||||
}
|
||||
m.d.PresetSearchString = value
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *Model) NoGmDls() Bool { return MakeBool((*NoGmDlsFilter)(m)) }
|
||||
func (m *NoGmDlsFilter) Value() bool { return m.derived.presetSearch.noGmDls }
|
||||
func (m *NoGmDlsFilter) SetValue(val bool) {
|
||||
if m.derived.presetSearch.noGmDls == val {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "g:")
|
||||
if val {
|
||||
m.d.PresetSearchString = "g:n " + m.d.PresetSearchString
|
||||
}
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
}
|
||||
func (m *NoGmDlsFilter) Enabled() bool { return true }
|
||||
|
||||
func (m *Model) UserPresetFilter() Bool { return MakeBool((*UserPresetsFilter)(m)) }
|
||||
func (m *UserPresetsFilter) Value() bool { return m.derived.presetSearch.kind == UserPresets }
|
||||
func (m *UserPresetsFilter) SetValue(val bool) {
|
||||
if (m.derived.presetSearch.kind == UserPresets) == val {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "t:")
|
||||
if val {
|
||||
m.d.PresetSearchString = "t:u " + m.d.PresetSearchString
|
||||
}
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
}
|
||||
func (m *UserPresetsFilter) Enabled() bool { return true }
|
||||
|
||||
func (m *Model) BuiltinPresetsFilter() Bool { return MakeBool((*BuiltinPresetsFilter)(m)) }
|
||||
func (m *BuiltinPresetsFilter) Value() bool { return m.derived.presetSearch.kind == BuiltinPresets }
|
||||
func (m *BuiltinPresetsFilter) SetValue(val bool) {
|
||||
if (m.derived.presetSearch.kind == BuiltinPresets) == val {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "t:")
|
||||
if val {
|
||||
m.d.PresetSearchString = "t:b " + m.d.PresetSearchString
|
||||
}
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
}
|
||||
func (m *BuiltinPresetsFilter) Enabled() bool { return true }
|
||||
|
||||
func (m *Model) ClearPresetSearch() Action { return MakeAction((*ClearPresetSearch)(m)) }
|
||||
func (m *ClearPresetSearch) Enabled() bool { return len(m.d.PresetSearchString) > 0 }
|
||||
func (m *ClearPresetSearch) Do() {
|
||||
m.d.PresetSearchString = ""
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
}
|
||||
|
||||
func (m *Model) PresetDirList() *PresetDirList { return (*PresetDirList)(m) }
|
||||
func (v *PresetDirList) List() List { return List{v} }
|
||||
func (m *PresetDirList) Count() int { return len(m.presets.Dirs) + 1 }
|
||||
func (m *PresetDirList) Selected() int { return m.derived.presetSearch.dirIndex + 1 }
|
||||
func (m *PresetDirList) Selected2() int { return m.derived.presetSearch.dirIndex + 1 }
|
||||
func (m *PresetDirList) SetSelected2(i int) {}
|
||||
func (m *PresetDirList) Value(i int) string {
|
||||
if i < 1 || i > len(m.presets.Dirs) {
|
||||
return "---"
|
||||
}
|
||||
return m.presets.Dirs[i-1]
|
||||
}
|
||||
func (m *PresetDirList) SetSelected(i int) {
|
||||
i = min(max(i, 0), len(m.presets.Dirs))
|
||||
if i < 0 || i > len(m.presets.Dirs) {
|
||||
return
|
||||
}
|
||||
m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "d:")
|
||||
if i > 0 {
|
||||
m.d.PresetSearchString = "d:" + m.presets.Dirs[i-1] + " " + m.d.PresetSearchString
|
||||
}
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
}
|
||||
|
||||
func (m *Model) PresetResultList() *PresetResultList { return (*PresetResultList)(m) }
|
||||
func (v *PresetResultList) List() List { return List{v} }
|
||||
func (m *PresetResultList) Count() int { return len(m.derived.presetSearch.results) }
|
||||
func (m *PresetResultList) Selected() int {
|
||||
return min(max(m.presetIndex, 0), len(m.derived.presetSearch.results)-1)
|
||||
}
|
||||
func (m *PresetResultList) Selected2() int { return m.Selected() }
|
||||
func (m *PresetResultList) SetSelected2(i int) {}
|
||||
func (m *PresetResultList) Value(i int) (name string, dir string, user bool) {
|
||||
if i < 0 || i >= len(m.derived.presetSearch.results) {
|
||||
return "", "", false
|
||||
}
|
||||
p := m.derived.presetSearch.results[i]
|
||||
return p.Instr.Name, p.Directory, p.User
|
||||
}
|
||||
func (m *PresetResultList) SetSelected(i int) {
|
||||
i = min(max(i, 0), len(m.derived.presetSearch.results)-1)
|
||||
if i < 0 || i >= len(m.derived.presetSearch.results) {
|
||||
return
|
||||
}
|
||||
m.presetIndex = i
|
||||
defer (*Model)(m).change("LoadPreset", PatchChange, MinorChange)()
|
||||
if m.d.InstrIndex < 0 {
|
||||
m.d.InstrIndex = 0
|
||||
}
|
||||
m.d.InstrIndex2 = m.d.InstrIndex
|
||||
for m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
m.d.Song.Patch = append(m.d.Song.Patch, defaultInstrument.Copy())
|
||||
}
|
||||
newInstr := m.derived.presetSearch.results[i].Instr.Copy()
|
||||
newInstr.NumVoices = clamp(m.d.Song.Patch[m.d.InstrIndex].NumVoices, 1, vm.MAX_VOICES)
|
||||
(*Model)(m).assignUnitIDs(newInstr.Units)
|
||||
m.d.Song.Patch[m.d.InstrIndex] = newInstr
|
||||
}
|
||||
|
||||
func removeFilters(str string, prefix string) string {
|
||||
parts := strings.Split(str, " ")
|
||||
newParts := make([]string, 0, len(parts))
|
||||
@ -347,175 +454,6 @@ func removeFilters(str string, prefix string) string {
|
||||
return strings.Join(newParts, " ")
|
||||
}
|
||||
|
||||
func (m *Model) SaveAsUserPreset() Action { return MakeAction((*SaveUserPreset)(m)) }
|
||||
func (m *SaveUserPreset) Enabled() bool {
|
||||
return m.d.InstrIndex >= 0 && m.d.InstrIndex < len(m.d.Song.Patch)
|
||||
}
|
||||
func (m *SaveUserPreset) Do() {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userPresetsDir := filepath.Join(configDir, "sointu", "presets", m.derived.presetSearch.dir)
|
||||
instr := m.d.Song.Patch[m.d.InstrIndex]
|
||||
name := instrumentNameToFilename(instr.Name)
|
||||
fileName := filepath.Join(userPresetsDir, name+".yml")
|
||||
// if exists, do not overwrite
|
||||
if _, err := os.Stat(fileName); err == nil {
|
||||
m.dialog = OverwriteUserPresetDialog
|
||||
return
|
||||
}
|
||||
(*Model)(m).OverwriteUserPreset().Do()
|
||||
}
|
||||
|
||||
func (m *Model) OverwriteUserPreset() Action { return MakeAction((*OverwriteUserPreset)(m)) }
|
||||
func (m *OverwriteUserPreset) Enabled() bool { return true }
|
||||
func (m *OverwriteUserPreset) Do() {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userPresetsDir := filepath.Join(configDir, "sointu", "presets", m.derived.presetSearch.dir)
|
||||
instr := m.d.Song.Patch[m.d.InstrIndex]
|
||||
name := instrumentNameToFilename(instr.Name)
|
||||
fileName := filepath.Join(userPresetsDir, name+".yml")
|
||||
os.MkdirAll(userPresetsDir, 0755)
|
||||
data, err := yaml.Marshal(&instr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
os.WriteFile(fileName, data, 0644)
|
||||
m.dialog = NoDialog
|
||||
(*Model)(m).presets.load()
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
}
|
||||
|
||||
func (m *Model) TryDeleteUserPreset() Action { return MakeAction((*TryDeleteUserPreset)(m)) }
|
||||
func (m *TryDeleteUserPreset) Do() { m.dialog = DeleteUserPresetDialog }
|
||||
func (m *TryDeleteUserPreset) Enabled() bool {
|
||||
if m.presetIndex < 0 || m.presetIndex >= len(m.derived.presetSearch.results) {
|
||||
return false
|
||||
}
|
||||
return m.derived.presetSearch.results[m.presetIndex].User
|
||||
}
|
||||
|
||||
func (m *Model) DeleteUserPreset() Action { return MakeAction((*DeleteUserPreset)(m)) }
|
||||
func (m *DeleteUserPreset) Enabled() bool { return (*Model)(m).TryDeleteUserPreset().Enabled() }
|
||||
func (m *DeleteUserPreset) Do() {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p := m.derived.presetSearch.results[m.presetIndex]
|
||||
userPresetsDir := filepath.Join(configDir, "sointu", "presets")
|
||||
if p.Directory != "" {
|
||||
userPresetsDir = filepath.Join(userPresetsDir, p.Directory)
|
||||
}
|
||||
name := instrumentNameToFilename(p.Instr.Name)
|
||||
fileName := filepath.Join(userPresetsDir, name+".yml")
|
||||
os.Remove(fileName)
|
||||
m.dialog = NoDialog
|
||||
(*Model)(m).presets.load()
|
||||
(*Model)(m).updateDerivedPresetSearch()
|
||||
}
|
||||
|
||||
// gmDlsEntryMap is a reverse map, to find the index of the GmDlsEntry in the
|
||||
// GmDlsEntries list based on the sample offset. Do not modify during runtime.
|
||||
var gmDlsEntryMap = make(map[vm.SampleOffset]int)
|
||||
|
||||
func init() {
|
||||
for i, e := range GmDlsEntries {
|
||||
key := vm.SampleOffset{Start: uint32(e.Start), LoopStart: uint16(e.LoopStart), LoopLength: uint16(e.LoopLength)}
|
||||
gmDlsEntryMap[key] = i
|
||||
}
|
||||
}
|
||||
|
||||
var defaultUnits = map[string]sointu.Unit{
|
||||
"envelope": {Type: "envelope", Parameters: map[string]int{"stereo": 0, "attack": 64, "decay": 64, "sustain": 64, "release": 64, "gain": 64}},
|
||||
"oscillator": {Type: "oscillator", Parameters: map[string]int{"stereo": 0, "transpose": 64, "detune": 64, "phase": 0, "color": 64, "shape": 64, "gain": 64, "type": sointu.Sine}},
|
||||
"noise": {Type: "noise", Parameters: map[string]int{"stereo": 0, "shape": 64, "gain": 64}},
|
||||
"mulp": {Type: "mulp", Parameters: map[string]int{"stereo": 0}},
|
||||
"mul": {Type: "mul", Parameters: map[string]int{"stereo": 0}},
|
||||
"add": {Type: "add", Parameters: map[string]int{"stereo": 0}},
|
||||
"addp": {Type: "addp", Parameters: map[string]int{"stereo": 0}},
|
||||
"push": {Type: "push", Parameters: map[string]int{"stereo": 0}},
|
||||
"pop": {Type: "pop", Parameters: map[string]int{"stereo": 0}},
|
||||
"xch": {Type: "xch", Parameters: map[string]int{"stereo": 0}},
|
||||
"receive": {Type: "receive", Parameters: map[string]int{"stereo": 0}},
|
||||
"loadnote": {Type: "loadnote", Parameters: map[string]int{"stereo": 0}},
|
||||
"loadval": {Type: "loadval", Parameters: map[string]int{"stereo": 0, "value": 64}},
|
||||
"pan": {Type: "pan", Parameters: map[string]int{"stereo": 0, "panning": 64}},
|
||||
"gain": {Type: "gain", Parameters: map[string]int{"stereo": 0, "gain": 64}},
|
||||
"invgain": {Type: "invgain", Parameters: map[string]int{"stereo": 0, "invgain": 64}},
|
||||
"dbgain": {Type: "dbgain", Parameters: map[string]int{"stereo": 0, "decibels": 64}},
|
||||
"crush": {Type: "crush", Parameters: map[string]int{"stereo": 0, "resolution": 64}},
|
||||
"clip": {Type: "clip", Parameters: map[string]int{"stereo": 0}},
|
||||
"hold": {Type: "hold", Parameters: map[string]int{"stereo": 0, "holdfreq": 64}},
|
||||
"distort": {Type: "distort", Parameters: map[string]int{"stereo": 0, "drive": 64}},
|
||||
"filter": {Type: "filter", Parameters: map[string]int{"stereo": 0, "frequency": 64, "resonance": 64, "lowpass": 1, "bandpass": 0, "highpass": 0}},
|
||||
"out": {Type: "out", Parameters: map[string]int{"stereo": 1, "gain": 64}},
|
||||
"outaux": {Type: "outaux", Parameters: map[string]int{"stereo": 1, "outgain": 64, "auxgain": 64}},
|
||||
"aux": {Type: "aux", Parameters: map[string]int{"stereo": 1, "gain": 64, "channel": 2}},
|
||||
"delay": {Type: "delay",
|
||||
Parameters: map[string]int{"damp": 0, "dry": 128, "feedback": 96, "notetracking": 2, "pregain": 40, "stereo": 0},
|
||||
VarArgs: []int{48}},
|
||||
"in": {Type: "in", Parameters: map[string]int{"stereo": 1, "channel": 2}},
|
||||
"speed": {Type: "speed", Parameters: map[string]int{}},
|
||||
"compressor": {Type: "compressor", Parameters: map[string]int{"stereo": 0, "attack": 64, "release": 64, "invgain": 64, "threshold": 64, "ratio": 64}},
|
||||
"send": {Type: "send", Parameters: map[string]int{"stereo": 0, "amount": 64, "voice": 0, "unit": 0, "port": 0, "sendpop": 1}},
|
||||
"sync": {Type: "sync", Parameters: map[string]int{}},
|
||||
"belleq": {Type: "belleq", Parameters: map[string]int{"stereo": 0, "frequency": 64, "bandwidth": 64, "gain": 64}},
|
||||
}
|
||||
|
||||
var defaultInstrument = sointu.Instrument{
|
||||
Name: "Instr",
|
||||
NumVoices: 1,
|
||||
Units: []sointu.Unit{
|
||||
defaultUnits["envelope"],
|
||||
defaultUnits["oscillator"],
|
||||
defaultUnits["mulp"],
|
||||
defaultUnits["delay"],
|
||||
defaultUnits["pan"],
|
||||
defaultUnits["outaux"],
|
||||
},
|
||||
}
|
||||
|
||||
var defaultSong = sointu.Song{
|
||||
BPM: 100,
|
||||
RowsPerBeat: 4,
|
||||
Score: sointu.Score{
|
||||
RowsPerPattern: 16,
|
||||
Length: 1,
|
||||
Tracks: []sointu.Track{
|
||||
{NumVoices: 1, Order: sointu.Order{0}, Patterns: []sointu.Pattern{{72, 0}}},
|
||||
},
|
||||
},
|
||||
Patch: sointu.Patch{defaultInstrument,
|
||||
{Name: "Global", NumVoices: 1, Units: []sointu.Unit{
|
||||
defaultUnits["in"],
|
||||
{Type: "delay",
|
||||
Parameters: map[string]int{"damp": 64, "dry": 128, "feedback": 125, "notetracking": 0, "pregain": 40, "stereo": 1},
|
||||
VarArgs: []int{1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618,
|
||||
1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642,
|
||||
}},
|
||||
{Type: "out", Parameters: map[string]int{"stereo": 1, "gain": 128}},
|
||||
}}},
|
||||
}
|
||||
|
||||
var reverbs = []delayPreset{
|
||||
{"stereo", 1, []int{1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618,
|
||||
1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642,
|
||||
}},
|
||||
{"left", 0, []int{1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618}},
|
||||
{"right", 0, []int{1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642}},
|
||||
}
|
||||
|
||||
type delayPreset struct {
|
||||
name string
|
||||
stereo int
|
||||
varArgs []int
|
||||
}
|
||||
|
||||
func splitPath(path string) []string {
|
||||
subPath := path
|
||||
var result []string
|
||||
@ -541,11 +479,11 @@ func splitPath(path string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func (p Presets) Len() int { return len(p.Presets) }
|
||||
func (p Presets) Less(i, j int) bool {
|
||||
if p.Presets[i].Instr.Name == p.Presets[j].Instr.Name {
|
||||
return p.Presets[i].User && !p.Presets[j].User
|
||||
func (p presetData) Len() int { return len(p.presets) }
|
||||
func (p presetData) Less(i, j int) bool {
|
||||
if p.presets[i].instr.Name == p.presets[j].instr.Name {
|
||||
return p.presets[i].user && !p.presets[j].user
|
||||
}
|
||||
return p.Presets[i].Instr.Name < p.Presets[j].Instr.Name
|
||||
return p.presets[i].instr.Name < p.presets[j].instr.Name
|
||||
}
|
||||
func (p Presets) Swap(i, j int) { p.Presets[i], p.Presets[j] = p.Presets[j], p.Presets[i] }
|
||||
func (p presetData) Swap(i, j int) { p.presets[i], p.presets[j] = p.presets[j], p.presets[i] }
|
||||
|
||||
Reference in New Issue
Block a user