mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
draft parameteter linking to vst
This commit is contained in:
parent
ce673578fd
commit
bd20440661
@ -28,6 +28,14 @@ func (NullContext) BPM() (bpm float64, ok bool) {
|
|||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (NullContext) Params() (ret tracker.ExtParamArray, ok bool) {
|
||||||
|
return tracker.ExtParamArray{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NullContext) SetParams(params tracker.ExtParamArray) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
||||||
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ type VSTIProcessContext struct {
|
|||||||
events []vst2.MIDIEvent
|
events []vst2.MIDIEvent
|
||||||
eventIndex int
|
eventIndex int
|
||||||
host vst2.Host
|
host vst2.Host
|
||||||
|
parameters []*vst2.Parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *VSTIProcessContext) NextEvent() (event tracker.MIDINoteEvent, ok bool) {
|
func (c *VSTIProcessContext) NextEvent() (event tracker.MIDINoteEvent, ok bool) {
|
||||||
@ -52,6 +53,28 @@ func (c *VSTIProcessContext) BPM() (bpm float64, ok bool) {
|
|||||||
return timeInfo.Tempo, true
|
return timeInfo.Tempo, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *VSTIProcessContext) Params() (ret tracker.ExtParamArray, ok bool) {
|
||||||
|
for i, p := range c.parameters {
|
||||||
|
ret[i] = p.Value
|
||||||
|
}
|
||||||
|
return ret, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VSTIProcessContext) SetParams(val tracker.ExtParamArray) bool {
|
||||||
|
changed := false
|
||||||
|
for i, p := range c.parameters {
|
||||||
|
if p.Value != val[i] {
|
||||||
|
p.Value = val[i]
|
||||||
|
p.Name = fmt.Sprintf("P%f", val[i])
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
c.host.UpdateDisplay()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var (
|
var (
|
||||||
version = int32(100)
|
version = int32(100)
|
||||||
@ -74,7 +97,22 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
go t.Main()
|
go t.Main()
|
||||||
context := VSTIProcessContext{host: h}
|
parameters := make([]*vst2.Parameter, 0, tracker.ExtParamCount)
|
||||||
|
for i := 0; i < tracker.ExtParamCount; i++ {
|
||||||
|
parameters = append(parameters,
|
||||||
|
&vst2.Parameter{
|
||||||
|
Name: fmt.Sprintf("P%d", i),
|
||||||
|
NotAutomated: true,
|
||||||
|
Unit: "foobar",
|
||||||
|
GetValueFunc: func(value float32) float32 {
|
||||||
|
return float32(int(value*128 + 1))
|
||||||
|
},
|
||||||
|
GetValueLabelFunc: func(value float32) string {
|
||||||
|
return fmt.Sprintf("%d", int(value))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
context := VSTIProcessContext{host: h, parameters: parameters}
|
||||||
buf := make(sointu.AudioBuffer, 1024)
|
buf := make(sointu.AudioBuffer, 1024)
|
||||||
return vst2.Plugin{
|
return vst2.Plugin{
|
||||||
UniqueID: PLUGIN_ID,
|
UniqueID: PLUGIN_ID,
|
||||||
@ -85,6 +123,7 @@ func init() {
|
|||||||
Vendor: "vsariola/sointu",
|
Vendor: "vsariola/sointu",
|
||||||
Category: vst2.PluginCategorySynth,
|
Category: vst2.PluginCategorySynth,
|
||||||
Flags: vst2.PluginIsSynth,
|
Flags: vst2.PluginIsSynth,
|
||||||
|
Parameters: parameters,
|
||||||
ProcessFloatFunc: func(in, out vst2.FloatBuffer) {
|
ProcessFloatFunc: func(in, out vst2.FloatBuffer) {
|
||||||
left := out.Channel(0)
|
left := out.Channel(0)
|
||||||
right := out.Channel(1)
|
right := out.Channel(1)
|
||||||
|
2
go.mod
2
go.mod
@ -2,6 +2,8 @@ module github.com/vsariola/sointu
|
|||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
|
replace pipelined.dev/audio/vst2 => ../vst2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gioui.org v0.5.0
|
gioui.org v0.5.0
|
||||||
gioui.org/x v0.5.0
|
gioui.org/x v0.5.0
|
||||||
|
@ -176,6 +176,7 @@ func (m *Model) Undo() Action {
|
|||||||
m.undoStack = m.undoStack[:len(m.undoStack)-1]
|
m.undoStack = m.undoStack[:len(m.undoStack)-1]
|
||||||
m.prevUndoKind = ""
|
m.prevUndoKind = ""
|
||||||
(*Model)(m).send(m.d.Song.Copy())
|
(*Model)(m).send(m.d.Song.Copy())
|
||||||
|
(*Model)(m).send(m.ExtParams())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,6 +194,7 @@ func (m *Model) Redo() Action {
|
|||||||
m.redoStack = m.redoStack[:len(m.redoStack)-1]
|
m.redoStack = m.redoStack[:len(m.redoStack)-1]
|
||||||
m.prevUndoKind = ""
|
m.prevUndoKind = ""
|
||||||
(*Model)(m).send(m.d.Song.Copy())
|
(*Model)(m).send(m.d.Song.Copy())
|
||||||
|
(*Model)(m).send(m.ExtParams())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,3 +410,30 @@ func (m *Model) completeAction(checkSave bool) {
|
|||||||
m.dialog = NoDialog
|
m.dialog = NoDialog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) SetExtLink(index int) Action {
|
||||||
|
return Action{
|
||||||
|
do: func() {
|
||||||
|
defer m.change("SetExtLink", ExtParamLinkChange, MajorChange)()
|
||||||
|
p, _ := m.Params().SelectedItem().(NamedParameter)
|
||||||
|
for i := range p.m.d.ExtParamLinks {
|
||||||
|
if i == index {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.m.d.ExtParamLinks[i].UnitID == p.unit.ID && p.m.d.ExtParamLinks[i].ParamName == p.up.Name {
|
||||||
|
p.m.d.ExtParamLinks[i] = ExtParamLink{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index > -1 {
|
||||||
|
p.m.d.ExtParamLinks[index] = ExtParamLink{UnitID: p.unit.ID, ParamName: p.up.Name}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allowed: func() bool {
|
||||||
|
if index < -1 || index >= len(m.d.ExtParamLinks) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := m.Params().SelectedItem().(NamedParameter)
|
||||||
|
return ok
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,27 +24,41 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UnitEditor struct {
|
type UnitEditor struct {
|
||||||
sliderList *DragList
|
sliderList *DragList
|
||||||
searchList *DragList
|
searchList *DragList
|
||||||
Parameters []*ParameterWidget
|
Parameters []*ParameterWidget
|
||||||
DeleteUnitBtn *ActionClickable
|
DeleteUnitBtn *ActionClickable
|
||||||
CopyUnitBtn *TipClickable
|
CopyUnitBtn *TipClickable
|
||||||
ClearUnitBtn *ActionClickable
|
ClearUnitBtn *ActionClickable
|
||||||
DisableUnitBtn *BoolClickable
|
DisableUnitBtn *BoolClickable
|
||||||
SelectTypeBtn *widget.Clickable
|
SelectTypeBtn *widget.Clickable
|
||||||
caser cases.Caser
|
ExtLinkMenuBtn *TipClickable
|
||||||
|
ExtLinkMenuItems []MenuItem
|
||||||
|
ExtLinkMenu Menu
|
||||||
|
caser cases.Caser
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUnitEditor(m *tracker.Model) *UnitEditor {
|
func NewUnitEditor(m *tracker.Model) *UnitEditor {
|
||||||
ret := &UnitEditor{
|
ret := &UnitEditor{
|
||||||
DeleteUnitBtn: NewActionClickable(m.DeleteUnit()),
|
DeleteUnitBtn: NewActionClickable(m.DeleteUnit()),
|
||||||
ClearUnitBtn: NewActionClickable(m.ClearUnit()),
|
ClearUnitBtn: NewActionClickable(m.ClearUnit()),
|
||||||
DisableUnitBtn: NewBoolClickable(m.UnitDisabled().Bool()),
|
DisableUnitBtn: NewBoolClickable(m.UnitDisabled().Bool()),
|
||||||
CopyUnitBtn: new(TipClickable),
|
CopyUnitBtn: new(TipClickable),
|
||||||
SelectTypeBtn: new(widget.Clickable),
|
SelectTypeBtn: new(widget.Clickable),
|
||||||
sliderList: NewDragList(m.Params().List(), layout.Vertical),
|
sliderList: NewDragList(m.Params().List(), layout.Vertical),
|
||||||
searchList: NewDragList(m.SearchResults().List(), layout.Vertical),
|
searchList: NewDragList(m.SearchResults().List(), layout.Vertical),
|
||||||
|
ExtLinkMenuBtn: new(TipClickable),
|
||||||
|
ExtLinkMenu: Menu{},
|
||||||
|
ExtLinkMenuItems: []MenuItem{{Text: "None", IconBytes: icons.HardwarePhoneLinkOff, Doer: m.SetExtLink(-1)}},
|
||||||
}
|
}
|
||||||
|
for i := 0; i < tracker.ExtParamCount; i++ {
|
||||||
|
ret.ExtLinkMenuItems = append(ret.ExtLinkMenuItems, MenuItem{
|
||||||
|
Text: fmt.Sprintf("P%v", i),
|
||||||
|
IconBytes: icons.HardwarePhoneLink,
|
||||||
|
Doer: m.SetExtLink(i),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
ret.caser = cases.Title(language.English)
|
ret.caser = cases.Title(language.English)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@ -125,6 +139,7 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
|||||||
copyUnitBtnStyle := TipIcon(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, "Copy unit (Ctrl+C)")
|
copyUnitBtnStyle := TipIcon(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, "Copy unit (Ctrl+C)")
|
||||||
deleteUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")
|
deleteUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")
|
||||||
disableUnitBtnStyle := ToggleIcon(gtx, t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, "Disable unit (Ctrl-D)", "Enable unit (Ctrl-D)")
|
disableUnitBtnStyle := ToggleIcon(gtx, t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, "Disable unit (Ctrl-D)", "Enable unit (Ctrl-D)")
|
||||||
|
extLinkMenuBtnStyle := TipIcon(t.Theme, pe.ExtLinkMenuBtn, icons.ContentLink, "Link to external parameter")
|
||||||
text := t.Units().SelectedType()
|
text := t.Units().SelectedType()
|
||||||
if text == "" {
|
if text == "" {
|
||||||
text = "Choose unit type"
|
text = "Choose unit type"
|
||||||
@ -132,7 +147,21 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
|||||||
text = pe.caser.String(text)
|
text = pe.caser.String(text)
|
||||||
}
|
}
|
||||||
hintText := Label(text, white, t.Theme.Shaper)
|
hintText := Label(text, white, t.Theme.Shaper)
|
||||||
|
m := PopupMenu(&pe.ExtLinkMenu, t.Theme.Shaper)
|
||||||
|
|
||||||
|
for pe.ExtLinkMenuBtn.Clickable.Clicked(gtx) {
|
||||||
|
pe.ExtLinkMenu.Visible = true
|
||||||
|
}
|
||||||
|
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
|
layout.Rigid(func(gtx C) D {
|
||||||
|
dims := extLinkMenuBtnStyle.Layout(gtx)
|
||||||
|
op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops)
|
||||||
|
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(300))
|
||||||
|
gtx.Constraints.Max.X = gtx.Dp(unit.Dp(180))
|
||||||
|
m.Layout(gtx, pe.ExtLinkMenuItems...)
|
||||||
|
return dims
|
||||||
|
}),
|
||||||
layout.Rigid(deleteUnitBtnStyle.Layout),
|
layout.Rigid(deleteUnitBtnStyle.Layout),
|
||||||
layout.Rigid(copyUnitBtnStyle.Layout),
|
layout.Rigid(copyUnitBtnStyle.Layout),
|
||||||
layout.Rigid(disableUnitBtnStyle.Layout),
|
layout.Rigid(disableUnitBtnStyle.Layout),
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@ -11,6 +12,8 @@ import (
|
|||||||
"github.com/vsariola/sointu/vm"
|
"github.com/vsariola/sointu/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const ExtParamCount = 16
|
||||||
|
|
||||||
// Model implements the mutable state for the tracker program GUI.
|
// Model implements the mutable state for the tracker program GUI.
|
||||||
//
|
//
|
||||||
// Go does not have immutable slices, so there's no efficient way to guarantee
|
// Go does not have immutable slices, so there's no efficient way to guarantee
|
||||||
@ -37,6 +40,7 @@ type (
|
|||||||
RecoveryFilePath string
|
RecoveryFilePath string
|
||||||
ChangedSinceRecovery bool
|
ChangedSinceRecovery bool
|
||||||
Loop Loop
|
Loop Loop
|
||||||
|
ExtParamLinks [ExtParamCount]ExtParamLink
|
||||||
}
|
}
|
||||||
|
|
||||||
Model struct {
|
Model struct {
|
||||||
@ -106,6 +110,16 @@ type (
|
|||||||
model *Model
|
model *Model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtParamLink is for linking VST parameters to the patch parameters. There's
|
||||||
|
// fixed number of parameters in the VST plugin and these are linked to
|
||||||
|
// particular parameters of a unit.
|
||||||
|
ExtParamLink struct {
|
||||||
|
UnitID int
|
||||||
|
ParamName string
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtParamArray [ExtParamCount]float32
|
||||||
|
|
||||||
IsPlayingMsg struct{ bool }
|
IsPlayingMsg struct{ bool }
|
||||||
StartPlayMsg struct{ sointu.SongPos }
|
StartPlayMsg struct{ sointu.SongPos }
|
||||||
BPMMsg struct{ int }
|
BPMMsg struct{ int }
|
||||||
@ -133,6 +147,7 @@ const (
|
|||||||
BPMChange
|
BPMChange
|
||||||
RowsPerBeatChange
|
RowsPerBeatChange
|
||||||
LoopChange
|
LoopChange
|
||||||
|
ExtParamLinkChange
|
||||||
SongChange ChangeType = PatchChange | ScoreChange | BPMChange | RowsPerBeatChange
|
SongChange ChangeType = PatchChange | ScoreChange | BPMChange | RowsPerBeatChange
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -247,6 +262,9 @@ func (m *Model) change(kind string, t ChangeType, severity ChangeSeverity) func(
|
|||||||
if m.changeType&LoopChange != 0 {
|
if m.changeType&LoopChange != 0 {
|
||||||
m.send(m.d.Loop)
|
m.send(m.d.Loop)
|
||||||
}
|
}
|
||||||
|
if m.changeType&ExtParamLinkChange != 0 || m.changeType&PatchChange != 0 {
|
||||||
|
m.send(m.ExtParams())
|
||||||
|
}
|
||||||
m.undoSkipCounter++
|
m.undoSkipCounter++
|
||||||
var limit int
|
var limit int
|
||||||
switch m.changeSeverity {
|
switch m.changeSeverity {
|
||||||
@ -322,6 +340,7 @@ func (m *Model) UnmarshalRecovery(bytes []byte) {
|
|||||||
}
|
}
|
||||||
m.d.ChangedSinceRecovery = false
|
m.d.ChangedSinceRecovery = false
|
||||||
m.send(m.d.Song.Copy())
|
m.send(m.d.Song.Copy())
|
||||||
|
m.send(m.ExtParams())
|
||||||
m.send(m.d.Loop)
|
m.send(m.d.Loop)
|
||||||
m.updatePatternUseCount()
|
m.updatePatternUseCount()
|
||||||
}
|
}
|
||||||
@ -355,6 +374,8 @@ func (m *Model) ProcessPlayerMessage(msg PlayerMsg) {
|
|||||||
m.Alerts().AddAlert(e)
|
m.Alerts().AddAlert(e)
|
||||||
case IsPlayingMsg:
|
case IsPlayingMsg:
|
||||||
m.playing = e.bool
|
m.playing = e.bool
|
||||||
|
case ExtParamArray:
|
||||||
|
m.SetExtParams(e)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,6 +410,50 @@ func (m *Model) Instrument(index int) sointu.Instrument {
|
|||||||
return m.d.Song.Patch[index].Copy()
|
return m.d.Song.Patch[index].Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) ExtParams() (ret ExtParamArray) {
|
||||||
|
for i, l := range m.d.ExtParamLinks {
|
||||||
|
instrIndex, unitIndex, err := m.d.Song.Patch.FindUnit(l.UnitID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unit := m.d.Song.Patch[instrIndex].Units[unitIndex]
|
||||||
|
if up, ok := sointu.UnitTypes[unit.Type]; ok {
|
||||||
|
for _, p := range up {
|
||||||
|
if p.Name == l.ParamName && p.CanSet && p.MaxValue > p.MinValue {
|
||||||
|
ret[i] = float32(unit.Parameters[l.ParamName]-p.MinValue) / float32(p.MaxValue-p.MinValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Model) SetExtParams(params ExtParamArray) {
|
||||||
|
defer m.change("SetParamValue", PatchChange, MinorChange)()
|
||||||
|
changed := false
|
||||||
|
for i, l := range m.d.ExtParamLinks {
|
||||||
|
instrIndex, unitIndex, err := m.d.Song.Patch.FindUnit(l.UnitID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unit := m.d.Song.Patch[instrIndex].Units[unitIndex]
|
||||||
|
if up, ok := sointu.UnitTypes[unit.Type]; ok {
|
||||||
|
for _, p := range up {
|
||||||
|
if p.Name == l.ParamName && p.CanSet && p.MaxValue > p.MinValue {
|
||||||
|
newVal := int(math.Round(float64(params[i])*float64(p.MaxValue-p.MinValue))) + p.MinValue
|
||||||
|
if unit.Parameters[l.ParamName] != newVal {
|
||||||
|
unit.Parameters[l.ParamName] = newVal
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !changed {
|
||||||
|
m.changeCancel = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *modelData) Copy() modelData {
|
func (d *modelData) Copy() modelData {
|
||||||
ret := *d
|
ret := *d
|
||||||
ret.Song = d.Song.Copy()
|
ret.Song = d.Song.Copy()
|
||||||
|
@ -21,6 +21,14 @@ func (NullContext) BPM() (bpm float64, ok bool) {
|
|||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (NullContext) Params() (params tracker.ExtParamArray, ok bool) {
|
||||||
|
return tracker.ExtParamArray{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NullContext) SetParams(params tracker.ExtParamArray) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type modelFuzzState struct {
|
type modelFuzzState struct {
|
||||||
model *tracker.Model
|
model *tracker.Model
|
||||||
clipboard []byte
|
clipboard []byte
|
||||||
|
@ -25,6 +25,7 @@ type (
|
|||||||
voiceLevels [vm.MAX_VOICES]float32 // a level that can be used to visualize the volume of each voice
|
voiceLevels [vm.MAX_VOICES]float32 // a level that can be used to visualize the volume of each voice
|
||||||
voices [vm.MAX_VOICES]voice
|
voices [vm.MAX_VOICES]voice
|
||||||
loop Loop
|
loop Loop
|
||||||
|
extParamValues ExtParamArray
|
||||||
|
|
||||||
recState recState // is the recording off; are we waiting for a note; or are we recording
|
recState recState // is the recording off; are we waiting for a note; or are we recording
|
||||||
recording Recording // the recorded MIDI events and BPM
|
recording Recording // the recorded MIDI events and BPM
|
||||||
@ -39,6 +40,8 @@ type (
|
|||||||
PlayerProcessContext interface {
|
PlayerProcessContext interface {
|
||||||
NextEvent() (event MIDINoteEvent, ok bool)
|
NextEvent() (event MIDINoteEvent, ok bool)
|
||||||
BPM() (bpm float64, ok bool)
|
BPM() (bpm float64, ok bool)
|
||||||
|
Params() (params ExtParamArray, ok bool)
|
||||||
|
SetParams(params ExtParamArray) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// MIDINoteEvent is a MIDI event triggering or releasing a note. In
|
// MIDINoteEvent is a MIDI event triggering or releasing a note. In
|
||||||
@ -225,6 +228,10 @@ func (p *Player) advanceRow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) processMessages(context PlayerProcessContext) {
|
func (p *Player) processMessages(context PlayerProcessContext) {
|
||||||
|
if value, ok := context.Params(); ok && value != p.extParamValues {
|
||||||
|
p.extParamValues = value
|
||||||
|
p.send(p.extParamValues)
|
||||||
|
}
|
||||||
loop:
|
loop:
|
||||||
for { // process new message
|
for { // process new message
|
||||||
select {
|
select {
|
||||||
@ -293,6 +300,8 @@ loop:
|
|||||||
}
|
}
|
||||||
p.recState = recStateNone
|
p.recState = recStateNone
|
||||||
}
|
}
|
||||||
|
case ExtParamArray:
|
||||||
|
context.SetParams(m)
|
||||||
default:
|
default:
|
||||||
// ignore unknown messages
|
// ignore unknown messages
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user