From 8b666064b20447f20c5699b3b19779cb01b1d293 Mon Sep 17 00:00:00 2001 From: vsariola Date: Sun, 10 Jan 2021 17:20:42 +0200 Subject: [PATCH] feat(tracker): implement saving and loading a song file --- tracker/files.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++ tracker/layout.go | 26 +++++++++++++++++++++++ tracker/tracker.go | 4 ++++ 3 files changed, 81 insertions(+) create mode 100644 tracker/files.go diff --git a/tracker/files.go b/tracker/files.go new file mode 100644 index 0000000..8c976a8 --- /dev/null +++ b/tracker/files.go @@ -0,0 +1,51 @@ +package tracker + +import ( + "encoding/json" + "io/ioutil" + "path/filepath" + + "gopkg.in/yaml.v3" + + "github.com/sqweek/dialog" + "github.com/vsariola/sointu" +) + +func (t *Tracker) LoadSongFile() { + filename, err := dialog.File().Filter("Sointu YAML song", "yml").Filter("Sointu JSON song", "json").Title("Load song").Load() + if err != nil { + return + } + bytes, err := ioutil.ReadFile(filename) + if err != nil { + return + } + var song sointu.Song + if errJSON := json.Unmarshal(bytes, &song); errJSON != nil { + if errYaml := yaml.Unmarshal(bytes, &song); errYaml != nil { + return + } + } + t.LoadSong(song) +} + +func (t *Tracker) SaveSongFile() { + filename, err := dialog.File().Filter("Sointu YAML song", "yml").Filter("Sointu JSON song", "json").Title("Save song").Save() + if err != nil { + return + } + var extension = filepath.Ext(filename) + var contents []byte + if extension == "json" { + contents, err = json.Marshal(t.song) + } else { + contents, err = yaml.Marshal(t.song) + } + if err != nil { + return + } + if extension == "" { + filename = filename + ".yml" + } + ioutil.WriteFile(filename, contents, 0644) +} diff --git a/tracker/layout.go b/tracker/layout.go index 0a4d810..c685be1 100644 --- a/tracker/layout.go +++ b/tracker/layout.go @@ -20,6 +20,8 @@ import ( var upIcon *widget.Icon var downIcon *widget.Icon var addIcon *widget.Icon +var loadIcon *widget.Icon +var saveIcon *widget.Icon func init() { var err error @@ -35,6 +37,14 @@ func init() { if err != nil { log.Fatal(err) } + loadIcon, err = widget.NewIcon(icons.FileFolder) + if err != nil { + log.Fatal(err) + } + saveIcon, err = widget.NewIcon(icons.ContentSave) + if err != nil { + log.Fatal(err) + } } func smallButton(icStyle material.IconButtonStyle) material.IconButtonStyle { @@ -170,6 +180,14 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions { } }() + for t.LoadSongFileBtn.Clicked() { + t.LoadSongFile() + } + + for t.SaveSongFileBtn.Clicked() { + t.SaveSongFile() + } + return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(t.layoutPatterns( t.song.Tracks, @@ -190,6 +208,14 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions { iconBtn := enableButton(material.IconButton(t.Theme, t.NewInstrumentBtn, addIcon), t.song.Patch.TotalVoices() < 32) return in.Layout(gtx, iconBtn.Layout) }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + iconBtn := material.IconButton(t.Theme, t.LoadSongFileBtn, loadIcon) + return in.Layout(gtx, iconBtn.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + iconBtn := material.IconButton(t.Theme, t.SaveSongFileBtn, saveIcon) + return in.Layout(gtx, iconBtn.Layout) + }), ) } diff --git a/tracker/tracker.go b/tracker/tracker.go index 24407a1..66400f6 100644 --- a/tracker/tracker.go +++ b/tracker/tracker.go @@ -36,6 +36,8 @@ type Tracker struct { BPMDownBtn *widget.Clickable NewTrackBtn *widget.Clickable NewInstrumentBtn *widget.Clickable + LoadSongFileBtn *widget.Clickable + SaveSongFileBtn *widget.Clickable ParameterSliders []*widget.Float UnitBtns []*widget.Clickable InstrumentBtns []*widget.Clickable @@ -263,6 +265,8 @@ func New(audioContext sointu.AudioContext) *Tracker { BPMDownBtn: new(widget.Clickable), NewTrackBtn: new(widget.Clickable), NewInstrumentBtn: new(widget.Clickable), + LoadSongFileBtn: new(widget.Clickable), + SaveSongFileBtn: new(widget.Clickable), setPlaying: make(chan bool), rowJump: make(chan int), patternJump: make(chan int),