mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-27 19:00:25 -04:00
feat: save recovery data to disk and/or DAW project
This commit is contained in:
parent
97a1b2f766
commit
462faf5f4e
@ -6,8 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
## Unreleased
|
||||
### Added
|
||||
- Save the GUI state periodically to a recovery file and load it on
|
||||
startup of the app, if present. The recovery file is located in the
|
||||
home directory of the user.
|
||||
startup of the app, if present. The recovery files are located in the
|
||||
app config directory (e.g. AppData/Roaming/Sointu on Windows).
|
||||
- Save the VSTI GUI state to the DAW project file, through GetChunk /
|
||||
SetChunk mechanisms.
|
||||
- Instrument presets. The presets are embedded in the executable and
|
||||
there's a button to open a menu to load one of the presets.
|
||||
- Frequency modulation target for oscillator, as it was in 4klang
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
|
||||
@ -50,10 +51,11 @@ func main() {
|
||||
defer audioContext.Close()
|
||||
modelMessages := make(chan interface{}, 1024)
|
||||
playerMessages := make(chan tracker.PlayerMessage, 1024)
|
||||
model, err := tracker.LoadRecovery(modelMessages, playerMessages)
|
||||
if err != nil {
|
||||
model = tracker.NewModel(modelMessages, playerMessages)
|
||||
recoveryFile := ""
|
||||
if configDir, err := os.UserConfigDir(); err == nil {
|
||||
recoveryFile = filepath.Join(configDir, "Sointu", "sointu-track-recovery")
|
||||
}
|
||||
model := tracker.NewModel(modelMessages, playerMessages, recoveryFile)
|
||||
player := tracker.NewPlayer(cmd.DefaultService, playerMessages, modelMessages)
|
||||
tracker := gioui.NewTracker(model, cmd.DefaultService)
|
||||
output := audioContext.Output()
|
||||
|
@ -3,6 +3,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/vsariola/sointu/cmd"
|
||||
"github.com/vsariola/sointu/tracker"
|
||||
"github.com/vsariola/sointu/tracker/gioui"
|
||||
@ -49,10 +54,13 @@ func init() {
|
||||
vst2.PluginAllocator = func(h vst2.Host) (vst2.Plugin, vst2.Dispatcher) {
|
||||
modelMessages := make(chan interface{}, 1024)
|
||||
playerMessages := make(chan tracker.PlayerMessage, 1024)
|
||||
model, err := tracker.LoadRecovery(modelMessages, playerMessages)
|
||||
if err != nil {
|
||||
model = tracker.NewModel(modelMessages, playerMessages)
|
||||
recoveryFile := ""
|
||||
if configDir, err := os.UserConfigDir(); err == nil {
|
||||
randBytes := make([]byte, 16)
|
||||
rand.Read(randBytes)
|
||||
recoveryFile = filepath.Join(configDir, "Sointu", "sointu-vsti-recovery-"+hex.EncodeToString(randBytes))
|
||||
}
|
||||
model := tracker.NewModel(modelMessages, playerMessages, recoveryFile)
|
||||
player := tracker.NewPlayer(cmd.DefaultService, playerMessages, modelMessages)
|
||||
tracker := gioui.NewTracker(model, cmd.DefaultService)
|
||||
tracker.SetInstrEnlarged(true) // start the vsti with the instrument editor enlarged
|
||||
@ -102,6 +110,12 @@ func init() {
|
||||
tracker.Quit(true)
|
||||
tracker.WaitQuitted()
|
||||
},
|
||||
GetChunkFunc: func(isPreset bool) []byte {
|
||||
return tracker.SafeMarshalRecovery()
|
||||
},
|
||||
SetChunkFunc: func(data []byte, isPreset bool) {
|
||||
tracker.SafeUnmarshalRecovery(data)
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -7,10 +7,11 @@ require (
|
||||
gioui.org/x v0.1.0
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/hajimehoshi/oto v0.6.6
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20230718065422-be1242fca4ab
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20231016195025-8c5c6a64c826
|
||||
)
|
||||
|
||||
require (
|
||||
@ -28,7 +29,6 @@ require (
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.6.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 // indirect
|
||||
golang.org/x/image v0.7.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
|
29
go.sum
29
go.sum
@ -1,9 +1,4 @@
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
|
||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
|
||||
gioui.org v0.1.0/go.mod h1:a3hz8FyrPMkt899D9YrxMGtyRzpPrJpz1Lzbssn81vI=
|
||||
gioui.org v0.2.1-0.20230823193131-cf5ae4aad92e h1:EdWCy7gaaBZoVFF0OCdo2Tj/LCJ4AI6HOGG8p5Zcljs=
|
||||
gioui.org v0.2.1-0.20230823193131-cf5ae4aad92e/go.mod h1:1H72sKEk/fNFV+l0JNeM2Dt3co3Y4uaQcD+I+/GQ0e4=
|
||||
gioui.org v0.3.0 h1:xZty/uLl1+/HNKpumX60JPQd46n8Zy6lc5T3IRMKoR4=
|
||||
gioui.org v0.3.0/go.mod h1:1H72sKEk/fNFV+l0JNeM2Dt3co3Y4uaQcD+I+/GQ0e4=
|
||||
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
@ -13,7 +8,6 @@ gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y=
|
||||
gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||
gioui.org/x v0.1.0 h1:CvphvaQSroRaNEZ+JbXBkV3J3klA76U3JpieyEwHFX4=
|
||||
gioui.org/x v0.1.0/go.mod h1:5qZxjtK/TVznMlcEOyn8OheiCZlArxF3IKnLqSehKXQ=
|
||||
git.sr.ht/~jackmordaunt/go-toast v1.0.0/go.mod h1:aIuRX/HdBOz7yRS8rOVYQCwJQlFS7DbYBTpUV0SHeeg=
|
||||
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 h1:bGG/g4ypjrCJoSvFrP5hafr9PPB5aw8SjcOWWila7ZI=
|
||||
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
@ -23,22 +17,13 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/andybalholm/stroke v0.0.0-20221221101821-bd29b49d73f0/go.mod h1:ccdDYaY5+gO+cbnQdFxEXqfy0RkoV25H3jLXUDNM3wg=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/esiqveland/notify v0.11.0/go.mod h1:63UbVSaeJwF0LVJARHFuPgUAoM7o1BEvCZyknsuonBc=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-text/typesetting v0.0.0-20230602202114-9797aefac433/go.mod h1:KmrpWuSMFcO2yjmyhGpnBGQHSKAoEgMTSSzvLDzCuEA=
|
||||
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo=
|
||||
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20230412163830-89e4bcfa3ecc/go.mod h1:RaqFwjcYyM5BjbYGwON0H5K0UqwO3sJlo9ukKha80ZE=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hajimehoshi/oto v0.6.6 h1:HYSZ8cYZqOL4iHugvbcfhNN2smiSOsBMaoSBi4nnWcw=
|
||||
@ -47,7 +32,6 @@ github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/jezek/xgb v1.0.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
@ -71,8 +55,6 @@ golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFd
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A=
|
||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
@ -90,7 +72,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -98,25 +79,19 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
@ -135,8 +110,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20230718065422-be1242fca4ab h1:rhVxuLIl32iwa3IaGwLKmEsPCV0XK3czP3pD4TDy/Ik=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20230718065422-be1242fca4ab/go.mod h1:wETLxsbBPftj6t4iVBCXvH/Xgd27ZgIC4hNnHDYNuz8=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20231016195025-8c5c6a64c826 h1:4c7O6PJ/Zl677O2VhXHUZK7LJyVBhUI7Q39+ri+gKUs=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20231016195025-8c5c6a64c826/go.mod h1:wETLxsbBPftj6t4iVBCXvH/Xgd27ZgIC4hNnHDYNuz8=
|
||||
pipelined.dev/pipe v0.10.0/go.mod h1:aIt+NPlW0QLYByqYniG77lTxSvl7OtCNLws/m+Xz5ww=
|
||||
pipelined.dev/pipe v0.11.0 h1:yRrbntKdqw/nbFqkz9dPaSHBoM7pK1LRHHDqdBJuqtc=
|
||||
pipelined.dev/pipe v0.11.0/go.mod h1:aIt+NPlW0QLYByqYniG77lTxSvl7OtCNLws/m+Xz5ww=
|
||||
|
@ -57,16 +57,20 @@ type Tracker struct {
|
||||
|
||||
lastVolume tracker.Volume
|
||||
|
||||
wavFilePath string
|
||||
quitChannel chan struct{}
|
||||
quitWG sync.WaitGroup
|
||||
errorChannel chan error
|
||||
quitted bool
|
||||
synthService sointu.SynthService
|
||||
wavFilePath string
|
||||
quitChannel chan struct{}
|
||||
quitWG sync.WaitGroup
|
||||
errorChannel chan error
|
||||
quitted bool
|
||||
unmarshalRecoveryChannel chan []byte
|
||||
marshalRecoveryChannel chan (chan []byte)
|
||||
synthService sointu.SynthService
|
||||
|
||||
*tracker.Model
|
||||
*trackerModel
|
||||
}
|
||||
|
||||
type trackerModel = tracker.Model
|
||||
|
||||
func (t *Tracker) UnmarshalContent(bytes []byte) error {
|
||||
var units []sointu.Unit
|
||||
if errJSON := json.Unmarshal(bytes, &units); errJSON == nil {
|
||||
@ -143,7 +147,10 @@ func NewTracker(model *tracker.Model, synthService sointu.SynthService) *Tracker
|
||||
|
||||
errorChannel: make(chan error, 32),
|
||||
synthService: synthService,
|
||||
Model: model,
|
||||
trackerModel: model,
|
||||
|
||||
marshalRecoveryChannel: make(chan (chan []byte)),
|
||||
unmarshalRecoveryChannel: make(chan []byte),
|
||||
}
|
||||
t.Theme.Palette.Fg = primaryColor
|
||||
t.Theme.Palette.ContrastFg = black
|
||||
@ -213,6 +220,10 @@ mainloop:
|
||||
}
|
||||
case <-recoveryTicker.C:
|
||||
t.SaveRecovery()
|
||||
case retChn := <-t.marshalRecoveryChannel:
|
||||
retChn <- t.MarshalRecovery()
|
||||
case bytes := <-t.unmarshalRecoveryChannel:
|
||||
t.UnmarshalRecovery(bytes)
|
||||
}
|
||||
}
|
||||
w.Perform(system.ActionClose)
|
||||
@ -220,6 +231,18 @@ mainloop:
|
||||
t.quitWG.Done()
|
||||
}
|
||||
|
||||
// thread safe, executed in the GUI thread
|
||||
func (t *Tracker) SafeMarshalRecovery() []byte {
|
||||
retChn := make(chan []byte)
|
||||
t.marshalRecoveryChannel <- retChn
|
||||
return <-retChn
|
||||
}
|
||||
|
||||
// thread safe, executed in the GUI thread
|
||||
func (t *Tracker) SafeUnmarshalRecovery(data []byte) {
|
||||
t.unmarshalRecoveryChannel <- data
|
||||
}
|
||||
|
||||
func (t *Tracker) sendQuit() {
|
||||
select {
|
||||
case t.quitChannel <- struct{}{}:
|
||||
|
115
tracker/model.go
115
tracker/model.go
@ -13,7 +13,6 @@ import (
|
||||
"github.com/vsariola/sointu"
|
||||
"github.com/vsariola/sointu/vm"
|
||||
"golang.org/x/exp/slices"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Model implements the mutable state for the tracker program GUI.
|
||||
@ -26,25 +25,27 @@ import (
|
||||
type (
|
||||
// modelData is the part of the model that gets save to recovery file
|
||||
modelData struct {
|
||||
Song sointu.Song
|
||||
SelectionCorner SongPoint
|
||||
Cursor SongPoint
|
||||
LowNibble bool
|
||||
InstrIndex int
|
||||
UnitIndex int
|
||||
ParamIndex int
|
||||
Octave int
|
||||
NoteTracking bool
|
||||
UsedIDs map[int]bool
|
||||
MaxID int
|
||||
FilePath string
|
||||
ChangedSinceSave bool
|
||||
PatternUseCount [][]int
|
||||
Panic bool
|
||||
Playing bool
|
||||
Recording bool
|
||||
PlayPosition SongRow
|
||||
InstrEnlarged bool
|
||||
Song sointu.Song
|
||||
SelectionCorner SongPoint
|
||||
Cursor SongPoint
|
||||
LowNibble bool
|
||||
InstrIndex int
|
||||
UnitIndex int
|
||||
ParamIndex int
|
||||
Octave int
|
||||
NoteTracking bool
|
||||
UsedIDs map[int]bool
|
||||
MaxID int
|
||||
FilePath string
|
||||
ChangedSinceSave bool
|
||||
PatternUseCount [][]int
|
||||
Panic bool
|
||||
Playing bool
|
||||
Recording bool
|
||||
PlayPosition SongRow
|
||||
InstrEnlarged bool
|
||||
RecoveryFilePath string
|
||||
ChangedSinceRecovery bool
|
||||
|
||||
PrevUndoType string
|
||||
UndoSkipCounter int
|
||||
@ -116,63 +117,76 @@ const (
|
||||
const maxUndo = 64
|
||||
const RECOVERY_FILE = ".sointu_recovery"
|
||||
|
||||
func NewModel(modelMessages chan<- interface{}, playerMessages <-chan PlayerMessage) *Model {
|
||||
func NewModel(modelMessages chan<- interface{}, playerMessages <-chan PlayerMessage, recoveryFilePath string) *Model {
|
||||
ret := new(Model)
|
||||
ret.modelMessages = modelMessages
|
||||
ret.PlayerMessages = playerMessages
|
||||
ret.setSongNoUndo(defaultSong.Copy())
|
||||
ret.d.Octave = 4
|
||||
ret.d.RecoveryFilePath = recoveryFilePath
|
||||
if recoveryFilePath != "" {
|
||||
if bytes2, err := os.ReadFile(ret.d.RecoveryFilePath); err == nil {
|
||||
json.Unmarshal(bytes2, &ret.d)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func LoadRecovery(modelMessages chan<- interface{}, playerMessages <-chan PlayerMessage) (*Model, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
func (m *Model) MarshalRecovery() []byte {
|
||||
out, err := json.Marshal(m.d)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get user home directory: %w", err)
|
||||
return nil
|
||||
}
|
||||
filePath := filepath.Join(homeDir, RECOVERY_FILE)
|
||||
b, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read recovery file: %w", err)
|
||||
if m.d.RecoveryFilePath != "" {
|
||||
os.Remove(m.d.RecoveryFilePath)
|
||||
}
|
||||
var ret Model
|
||||
err = json.Unmarshal(b, &ret.d)
|
||||
if err != nil {
|
||||
err = yaml.Unmarshal(b, &ret.d)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal recovery file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
ret.modelMessages = modelMessages
|
||||
ret.PlayerMessages = playerMessages
|
||||
ret.notifyPatchChange()
|
||||
ret.notifySamplesPerRowChange()
|
||||
ret.notifyScoreChange()
|
||||
return &ret, nil
|
||||
m.d.ChangedSinceRecovery = false
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *Model) SaveRecovery() error {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get user home directory: %w", err)
|
||||
if !m.d.ChangedSinceRecovery {
|
||||
return nil
|
||||
}
|
||||
if m.d.RecoveryFilePath == "" {
|
||||
return errors.New("no backup file path")
|
||||
}
|
||||
out, err := json.Marshal(m.d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not marshal the model: %w", err)
|
||||
return fmt.Errorf("could not marshal recovery data: %w", err)
|
||||
}
|
||||
filePath := filepath.Join(homeDir, RECOVERY_FILE)
|
||||
file, err := os.Create(filePath)
|
||||
dir := filepath.Dir(m.d.RecoveryFilePath)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
file, err := os.Create(m.d.RecoveryFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open recovery file: %w", err)
|
||||
return fmt.Errorf("could not create recovery file: %w", err)
|
||||
}
|
||||
_, err = file.Write(out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write recovery file: %w", err)
|
||||
}
|
||||
m.d.ChangedSinceRecovery = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) UnmarshalRecovery(bytes []byte) {
|
||||
err := json.Unmarshal(bytes, &m.d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if m.d.RecoveryFilePath != "" { // check if there's a recovery file on disk and load it instead
|
||||
if bytes2, err := os.ReadFile(m.d.RecoveryFilePath); err == nil {
|
||||
json.Unmarshal(bytes2, &m.d)
|
||||
}
|
||||
}
|
||||
m.d.ChangedSinceRecovery = false
|
||||
m.notifyPatchChange()
|
||||
m.notifySamplesPerRowChange()
|
||||
m.notifyScoreChange()
|
||||
}
|
||||
|
||||
func (m *Model) FilePath() string {
|
||||
return m.d.FilePath
|
||||
}
|
||||
@ -1416,6 +1430,7 @@ func (m *Model) notifySamplesPerRowChange() {
|
||||
|
||||
func (m *Model) saveUndo(undoType string, undoSkipping int) {
|
||||
m.d.ChangedSinceSave = true
|
||||
m.d.ChangedSinceRecovery = true
|
||||
if m.d.PrevUndoType == undoType && m.d.UndoSkipCounter < undoSkipping {
|
||||
m.d.UndoSkipCounter++
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user