mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-25 08:24:50 -04:00
drafting
This commit is contained in:
parent
c09a3f04db
commit
4e295a3a2f
14
patch.go
14
patch.go
@ -386,8 +386,8 @@ var stackUseMonoStereo = map[string][2]StackUse{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
var stackUseSendNoPop = [2]StackUse{
|
var stackUseSendNoPop = [2]StackUse{
|
||||||
{Inputs: [][]int{{0}}, Modifies: []bool{false}, NumOutputs: 1},
|
{Inputs: [][]int{{0}}, Modifies: []bool{true}, NumOutputs: 1},
|
||||||
{Inputs: [][]int{{0}, {1}}, Modifies: []bool{false, false}, NumOutputs: 2},
|
{Inputs: [][]int{{0}, {1}}, Modifies: []bool{true, true}, NumOutputs: 2},
|
||||||
}
|
}
|
||||||
var stackUseSendPop = [2]StackUse{
|
var stackUseSendPop = [2]StackUse{
|
||||||
{Inputs: [][]int{{0}}, Modifies: []bool{true}, NumOutputs: 0}, // mono
|
{Inputs: [][]int{{0}}, Modifies: []bool{true}, NumOutputs: 0}, // mono
|
||||||
@ -544,19 +544,19 @@ func (p Patch) FindUnit(id int) (instrIndex int, unitIndex int, err error) {
|
|||||||
return 0, 0, fmt.Errorf("could not find a unit with id %v", id)
|
return 0, 0, fmt.Errorf("could not find a unit with id %v", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindParamForModulationPort(unitName string, index int) (UnitParameter, bool) {
|
func FindParamForModulationPort(unitName string, index int) (up UnitParameter, upIndex int, ok bool) {
|
||||||
unitType, ok := UnitTypes[unitName]
|
unitType, ok := UnitTypes[unitName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return UnitParameter{}, false
|
return UnitParameter{}, 0, false
|
||||||
}
|
}
|
||||||
for _, param := range unitType {
|
for i, param := range unitType {
|
||||||
if !param.CanModulate {
|
if !param.CanModulate {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
return param, true
|
return param, i, true
|
||||||
}
|
}
|
||||||
index--
|
index--
|
||||||
}
|
}
|
||||||
return UnitParameter{}, false
|
return UnitParameter{}, 0, false
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ func (m *Model) updateDerivedScoreData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) updateDerivedPatchData() {
|
func (m *Model) updateDerivedPatchData() {
|
||||||
m.derived.rail.update(m.d.Song.Patch)
|
m.SignalRail().update()
|
||||||
clear(m.derived.forUnit)
|
clear(m.derived.forUnit)
|
||||||
for i, instr := range m.d.Song.Patch {
|
for i, instr := range m.d.Song.Patch {
|
||||||
for u, unit := range instr.Units {
|
for u, unit := range instr.Units {
|
||||||
@ -177,8 +177,8 @@ func (m *Model) deriveParams(unit *sointu.Unit) []Parameter {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
for i, up := range unitType {
|
for i, up := range unitType {
|
||||||
if !up.CanSet {
|
if !up.CanSet && !up.CanModulate {
|
||||||
continue
|
continue // skip parameters that cannot be set or modulated
|
||||||
}
|
}
|
||||||
if unit.Type == "oscillator" && unit.Parameters["type"] != sointu.Sample && (up.Name == "samplestart" || up.Name == "loopstart" || up.Name == "looplength") {
|
if unit.Type == "oscillator" && unit.Parameters["type"] != sointu.Sample && (up.Name == "samplestart" || up.Name == "loopstart" || up.Name == "looplength") {
|
||||||
continue // don't show the sample related params unless necessary
|
continue // don't show the sample related params unless necessary
|
||||||
@ -230,7 +230,7 @@ func (m *Model) collectSendSources(unit sointu.Unit, paramName string) iter.Seq[
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
port := u.Parameters["port"]
|
port := u.Parameters["port"]
|
||||||
unitParam, ok := sointu.FindParamForModulationPort(unit.Type, port)
|
unitParam, _, ok := sointu.FindParamForModulationPort(unit.Type, port)
|
||||||
if !ok || unitParam.Name != paramName {
|
if !ok || unitParam.Name != paramName {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,13 @@ import (
|
|||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
)
|
)
|
||||||
|
|
||||||
const numSignalsDrawn = 8
|
const maxSignalsDrawn = 16
|
||||||
|
|
||||||
type (
|
type (
|
||||||
SignalRailStyle struct {
|
SignalRailStyle struct {
|
||||||
Color color.NRGBA
|
Color color.NRGBA
|
||||||
LineWidth unit.Dp
|
LineWidth unit.Dp
|
||||||
|
SignalWidth unit.Dp
|
||||||
PortDiameter unit.Dp
|
PortDiameter unit.Dp
|
||||||
PortColor color.NRGBA
|
PortColor color.NRGBA
|
||||||
}
|
}
|
||||||
@ -25,7 +26,6 @@ type (
|
|||||||
SignalRailWidget struct {
|
SignalRailWidget struct {
|
||||||
Style *SignalRailStyle
|
Style *SignalRailStyle
|
||||||
Signal tracker.Signal
|
Signal tracker.Signal
|
||||||
Width unit.Dp
|
|
||||||
Height unit.Dp
|
Height unit.Dp
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -34,63 +34,69 @@ func SignalRail(th *Theme, signal tracker.Signal) SignalRailWidget {
|
|||||||
return SignalRailWidget{
|
return SignalRailWidget{
|
||||||
Style: &th.SignalRail,
|
Style: &th.SignalRail,
|
||||||
Signal: signal,
|
Signal: signal,
|
||||||
Width: th.UnitEditor.Width,
|
|
||||||
Height: th.UnitEditor.Height,
|
Height: th.UnitEditor.Height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SignalRailWidget) Layout(gtx C) D {
|
func (s SignalRailWidget) Layout(gtx C) D {
|
||||||
w := gtx.Dp(s.Width)
|
sw := gtx.Dp(s.Style.SignalWidth)
|
||||||
h := gtx.Dp(s.Height)
|
h := gtx.Dp(s.Height)
|
||||||
l := gtx.Dp(s.Style.LineWidth)
|
lw := gtx.Dp(s.Style.LineWidth)
|
||||||
d := gtx.Dp(s.Style.PortDiameter)
|
pd := gtx.Dp(s.Style.PortDiameter)
|
||||||
c := max(l, d) / 2
|
center := sw / 2
|
||||||
stride := (w - c*2) / numSignalsDrawn
|
|
||||||
var path clip.Path
|
var path clip.Path
|
||||||
path.Begin(gtx.Ops)
|
path.Begin(gtx.Ops)
|
||||||
// Draw pass through signals
|
// Draw pass through signals
|
||||||
for i := range min(numSignalsDrawn, s.Signal.PassThrough) {
|
for i := range min(maxSignalsDrawn, s.Signal.PassThrough) {
|
||||||
x := float32(i*stride + c)
|
x := float32(i*sw + center)
|
||||||
path.MoveTo(f32.Pt(x, 0))
|
path.MoveTo(f32.Pt(x, 0))
|
||||||
path.LineTo(f32.Pt(x, float32(h)))
|
path.LineTo(f32.Pt(x, float32(h)))
|
||||||
}
|
}
|
||||||
// Draw the routing of input signals
|
// Draw the routing of input signals
|
||||||
for i := range min(len(s.Signal.StackUse.Inputs), numSignalsDrawn-s.Signal.PassThrough) {
|
for i := range min(len(s.Signal.StackUse.Inputs), maxSignalsDrawn-s.Signal.PassThrough) {
|
||||||
input := s.Signal.StackUse.Inputs[i]
|
input := s.Signal.StackUse.Inputs[i]
|
||||||
x1 := float32((i+s.Signal.PassThrough)*stride + c)
|
x1 := float32((i+s.Signal.PassThrough)*sw + center)
|
||||||
for _, link := range input {
|
for _, link := range input {
|
||||||
x2 := float32((link+s.Signal.PassThrough)*stride + c)
|
x2 := float32((link+s.Signal.PassThrough)*sw + center)
|
||||||
path.MoveTo(f32.Pt(x1, 0))
|
path.MoveTo(f32.Pt(x1, 0))
|
||||||
path.LineTo(f32.Pt(x2, float32(h/2)))
|
path.LineTo(f32.Pt(x2, float32(h/2)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.Signal.Send {
|
||||||
|
for i := range min(len(s.Signal.StackUse.Inputs), maxSignalsDrawn-s.Signal.PassThrough) {
|
||||||
|
from := f32.Pt(float32((i+s.Signal.PassThrough)*sw+center), float32(h/2))
|
||||||
|
to := f32.Pt(float32(gtx.Constraints.Max.X), float32(h)-float32(sw/2))
|
||||||
|
ctrl := f32.Pt(from.X, to.Y)
|
||||||
|
path.MoveTo(from)
|
||||||
|
path.QuadTo(ctrl, to)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Draw the routing of output signals
|
// Draw the routing of output signals
|
||||||
for i := range min(s.Signal.StackUse.NumOutputs, numSignalsDrawn-s.Signal.PassThrough) {
|
for i := range min(s.Signal.StackUse.NumOutputs, maxSignalsDrawn-s.Signal.PassThrough) {
|
||||||
x := float32((i+s.Signal.PassThrough)*stride + c)
|
x := float32((i+s.Signal.PassThrough)*sw + center)
|
||||||
path.MoveTo(f32.Pt(x, float32(h/2)))
|
path.MoveTo(f32.Pt(x, float32(h/2)))
|
||||||
path.LineTo(f32.Pt(x, float32(h)))
|
path.LineTo(f32.Pt(x, float32(h)))
|
||||||
}
|
}
|
||||||
|
// Signal paths finished
|
||||||
paint.FillShape(gtx.Ops, s.Style.Color,
|
paint.FillShape(gtx.Ops, s.Style.Color,
|
||||||
clip.Stroke{
|
clip.Stroke{
|
||||||
Path: path.End(),
|
Path: path.End(),
|
||||||
Width: float32(l),
|
Width: float32(lw),
|
||||||
}.Op())
|
}.Op())
|
||||||
// Draw the circles on modified signals
|
// Draw the circles on signals that get modified
|
||||||
|
var circle clip.Path
|
||||||
for i := range min(len(s.Signal.StackUse.Modifies), numSignalsDrawn-s.Signal.PassThrough) {
|
circle.Begin(gtx.Ops)
|
||||||
|
for i := range min(len(s.Signal.StackUse.Modifies), maxSignalsDrawn-s.Signal.PassThrough) {
|
||||||
if !s.Signal.StackUse.Modifies[i] {
|
if !s.Signal.StackUse.Modifies[i] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var circle clip.Path
|
f := f32.Pt(float32((i+s.Signal.PassThrough)*sw+center), float32(h/2))
|
||||||
x := float32((i + s.Signal.PassThrough) * stride)
|
circle.MoveTo(f32.Pt(f.X-float32(pd/2), float32(h/2)))
|
||||||
circle.Begin(gtx.Ops)
|
|
||||||
circle.MoveTo(f32.Pt(x, float32(h/2)))
|
|
||||||
f := f32.Pt(x+float32(c), float32(h/2))
|
|
||||||
circle.ArcTo(f, f, float32(2*math.Pi))
|
circle.ArcTo(f, f, float32(2*math.Pi))
|
||||||
p := clip.Outline{Path: circle.End()}.Op().Push(gtx.Ops)
|
|
||||||
paint.ColorOp{Color: s.Style.PortColor}.Add(gtx.Ops)
|
|
||||||
paint.PaintOp{}.Add(gtx.Ops)
|
|
||||||
p.Pop()
|
|
||||||
}
|
}
|
||||||
return D{Size: image.Pt(w, h)}
|
p := clip.Outline{Path: circle.End()}.Op().Push(gtx.Ops)
|
||||||
|
paint.ColorOp{Color: s.Style.PortColor}.Add(gtx.Ops)
|
||||||
|
paint.PaintOp{}.Add(gtx.Ops)
|
||||||
|
p.Pop()
|
||||||
|
return D{Size: image.Pt(sw, h)}
|
||||||
}
|
}
|
||||||
|
@ -85,16 +85,16 @@ type Theme struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
UnitEditor struct {
|
UnitEditor struct {
|
||||||
Name LabelStyle
|
Name LabelStyle
|
||||||
Chooser LabelStyle
|
Chooser LabelStyle
|
||||||
Hint LabelStyle
|
Hint LabelStyle
|
||||||
InvalidParam color.NRGBA
|
InvalidParam color.NRGBA
|
||||||
SendTarget color.NRGBA
|
SendTarget color.NRGBA
|
||||||
Width unit.Dp
|
Width unit.Dp
|
||||||
Height unit.Dp
|
Height unit.Dp
|
||||||
RowTitleWidth unit.Dp
|
RowTitle LabelStyle
|
||||||
ColumnTitleHeight unit.Dp
|
RowTitleWidth unit.Dp
|
||||||
RowTitle LabelStyle
|
Error color.NRGBA
|
||||||
}
|
}
|
||||||
Cursor CursorStyle
|
Cursor CursorStyle
|
||||||
Selection CursorStyle
|
Selection CursorStyle
|
||||||
|
@ -211,12 +211,12 @@ uniteditor:
|
|||||||
name:
|
name:
|
||||||
{ textsize: 12, alignment: 2, color: *highemphasis, shadowcolor: *black }
|
{ textsize: 12, alignment: 2, color: *highemphasis, shadowcolor: *black }
|
||||||
invalidparam: { r: 120, g: 120, b: 120, a: 190 }
|
invalidparam: { r: 120, g: 120, b: 120, a: 190 }
|
||||||
sendtarget: { r: 120, g: 120, b: 210, a: 63 }
|
sendtarget: *secondarycolor
|
||||||
width: 60
|
width: 60
|
||||||
height: 60
|
height: 70
|
||||||
rowtitlewidth: 16
|
|
||||||
columntitleheight: 16
|
|
||||||
rowtitle: { textsize: 12, color: *white, alignment: 2 }
|
rowtitle: { textsize: 12, color: *white, alignment: 2 }
|
||||||
|
rowtitlewidth: 16
|
||||||
|
error: *errorcolor
|
||||||
knob:
|
knob:
|
||||||
diameter: 36
|
diameter: 36
|
||||||
value: { textsize: 12, color: *highemphasis }
|
value: { textsize: 12, color: *highemphasis }
|
||||||
@ -226,7 +226,8 @@ knob:
|
|||||||
neg: { color: *secondarycolor, bg: { r: 32, g: 55, b: 58, a: 255 } }
|
neg: { color: *secondarycolor, bg: { r: 32, g: 55, b: 58, a: 255 } }
|
||||||
indicator: { color: *white, width: 2, innerdiam: 24, outerdiam: 36 }
|
indicator: { color: *white, width: 2, innerdiam: 24, outerdiam: 36 }
|
||||||
signalrail:
|
signalrail:
|
||||||
color: *primarycolor
|
color: *secondarycolor
|
||||||
|
signalwidth: 10
|
||||||
linewidth: 2
|
linewidth: 2
|
||||||
portdiameter: 8
|
portdiameter: 8
|
||||||
portcolor: *secondarycolor
|
portcolor: *primarycolor
|
||||||
|
@ -71,7 +71,7 @@ func (pe *UnitEditor) Layout(gtx C) D {
|
|||||||
t := TrackerFromContext(gtx)
|
t := TrackerFromContext(gtx)
|
||||||
pe.update(gtx, t)
|
pe.update(gtx, t)
|
||||||
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()
|
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()
|
||||||
editorFunc := pe.layoutSliders
|
editorFunc := pe.layoutRack
|
||||||
if pe.showingChooser() {
|
if pe.showingChooser() {
|
||||||
editorFunc = pe.layoutUnitTypeChooser
|
editorFunc = pe.layoutUnitTypeChooser
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ func (pe *UnitEditor) ChooseUnitType(t *Tracker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *UnitEditor) layoutSliders(gtx C) D {
|
func (pe *UnitEditor) layoutRack(gtx C) D {
|
||||||
t := TrackerFromContext(gtx)
|
t := TrackerFromContext(gtx)
|
||||||
// create enough parameter widget to match the number of parameters
|
// create enough parameter widget to match the number of parameters
|
||||||
width := pe.paramTable.Table.Width()
|
width := pe.paramTable.Table.Width()
|
||||||
@ -161,8 +161,11 @@ func (pe *UnitEditor) layoutSliders(gtx C) D {
|
|||||||
}
|
}
|
||||||
cellWidth := gtx.Dp(t.Theme.UnitEditor.Width)
|
cellWidth := gtx.Dp(t.Theme.UnitEditor.Width)
|
||||||
cellHeight := gtx.Dp(t.Theme.UnitEditor.Height)
|
cellHeight := gtx.Dp(t.Theme.UnitEditor.Height)
|
||||||
rowTitleWidth := gtx.Dp(t.Theme.UnitEditor.RowTitleWidth)
|
rowTitleLabelWidth := gtx.Dp(t.Theme.UnitEditor.RowTitleWidth)
|
||||||
columnTitleHeight := gtx.Dp(t.Theme.UnitEditor.ColumnTitleHeight)
|
rowTitleSignalWidth := gtx.Dp(t.Theme.SignalRail.SignalWidth) * t.SignalRail().MaxWidth()
|
||||||
|
rowTitleWidth := rowTitleLabelWidth + rowTitleSignalWidth
|
||||||
|
signalError := t.SignalRail().Error()
|
||||||
|
columnTitleHeight := gtx.Dp(0)
|
||||||
for i := range pe.Parameters {
|
for i := range pe.Parameters {
|
||||||
for len(pe.Parameters[i]) < width {
|
for len(pe.Parameters[i]) < width {
|
||||||
pe.Parameters[i] = append(pe.Parameters[i], &ParameterWidget{})
|
pe.Parameters[i] = append(pe.Parameters[i], &ParameterWidget{})
|
||||||
@ -171,20 +174,29 @@ func (pe *UnitEditor) layoutSliders(gtx C) D {
|
|||||||
coltitle := func(gtx C, x int) D {
|
coltitle := func(gtx C, x int) D {
|
||||||
return D{Size: image.Pt(cellWidth, columnTitleHeight)}
|
return D{Size: image.Pt(cellWidth, columnTitleHeight)}
|
||||||
}
|
}
|
||||||
|
rowTitleBg := func(gtx C, j int) D {
|
||||||
|
paint.FillShape(gtx.Ops, t.Theme.NoteEditor.Play, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, 1)}.Op())
|
||||||
|
return D{}
|
||||||
|
}
|
||||||
rowtitle := func(gtx C, y int) D {
|
rowtitle := func(gtx C, y int) D {
|
||||||
//defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
if y < 0 || y >= len(pe.Parameters) {
|
||||||
defer op.Affine(f32.Affine2D{}.Rotate(f32.Pt(0, 0), -90*math.Pi/180).Offset(f32.Point{X: 0, Y: float32(cellHeight)})).Push(gtx.Ops).Pop()
|
return D{}
|
||||||
gtx.Constraints = layout.Exact(image.Pt(cellHeight, rowTitleWidth))
|
}
|
||||||
Label(t.Theme, &t.Theme.UnitEditor.RowTitle, t.Units().Item(y).Type).Layout(gtx)
|
|
||||||
|
sr := SignalRail(t.Theme, t.SignalRail().Item(y))
|
||||||
|
label := Label(t.Theme, &t.Theme.UnitEditor.RowTitle, t.Units().Item(y).Type)
|
||||||
|
if signalError.Err != nil && signalError.UnitIndex == y {
|
||||||
|
label.Color = t.Theme.UnitEditor.Error
|
||||||
|
}
|
||||||
|
gtx.Constraints = layout.Exact(image.Pt(rowTitleWidth, cellHeight))
|
||||||
|
sr.Layout(gtx)
|
||||||
|
defer op.Affine(f32.Affine2D{}.Rotate(f32.Pt(0, 0), -90*math.Pi/180).Offset(f32.Point{X: float32(rowTitleSignalWidth), Y: float32(cellHeight)})).Push(gtx.Ops).Pop()
|
||||||
|
gtx.Constraints = layout.Exact(image.Pt(cellHeight, rowTitleLabelWidth))
|
||||||
|
label.Layout(gtx)
|
||||||
return D{Size: image.Pt(rowTitleWidth, cellHeight)}
|
return D{Size: image.Pt(rowTitleWidth, cellHeight)}
|
||||||
}
|
}
|
||||||
cursor := t.Model.Params().Cursor()
|
cursor := t.Model.Params().Cursor()
|
||||||
cell := func(gtx C, x, y int) D {
|
cell := func(gtx C, x, y int) D {
|
||||||
if x == 0 {
|
|
||||||
sr := SignalRail(t.Theme, t.SignalRail().Item(y))
|
|
||||||
return sr.Layout(gtx)
|
|
||||||
}
|
|
||||||
x--
|
|
||||||
gtx.Constraints = layout.Exact(image.Pt(cellWidth, cellHeight))
|
gtx.Constraints = layout.Exact(image.Pt(cellWidth, cellHeight))
|
||||||
point := tracker.Point{X: x, Y: y}
|
point := tracker.Point{X: x, Y: y}
|
||||||
if y < 0 || y >= len(pe.Parameters) || x < 0 || x >= len(pe.Parameters[y]) {
|
if y < 0 || y >= len(pe.Parameters) || x < 0 || x >= len(pe.Parameters[y]) {
|
||||||
@ -206,63 +218,76 @@ func (pe *UnitEditor) layoutSliders(gtx C) D {
|
|||||||
return D{Size: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)}
|
return D{Size: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)}
|
||||||
}
|
}
|
||||||
table := FilledScrollTable(t.Theme, pe.paramTable)
|
table := FilledScrollTable(t.Theme, pe.paramTable)
|
||||||
table.RowTitleWidth = t.Theme.UnitEditor.RowTitleWidth
|
table.RowTitleWidth = gtx.Metric.PxToDp(rowTitleWidth)
|
||||||
table.ColumnTitleHeight = t.Theme.UnitEditor.ColumnTitleHeight
|
table.ColumnTitleHeight = 0
|
||||||
table.CellWidth = t.Theme.UnitEditor.Width
|
table.CellWidth = t.Theme.UnitEditor.Width
|
||||||
table.CellHeight = t.Theme.UnitEditor.Height
|
table.CellHeight = t.Theme.UnitEditor.Height
|
||||||
pe.drawSignals(gtx)
|
pe.drawSignals(gtx, rowTitleWidth)
|
||||||
dims := table.Layout(gtx, cell, coltitle, rowtitle, nil, nil)
|
dims := table.Layout(gtx, cell, coltitle, rowtitle, nil, rowTitleBg)
|
||||||
return dims
|
return dims
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *UnitEditor) drawSignals(gtx C) {
|
func (pe *UnitEditor) drawSignals(gtx C, rowTitleWidth int) {
|
||||||
t := TrackerFromContext(gtx)
|
t := TrackerFromContext(gtx)
|
||||||
units := t.Units()
|
units := t.Units()
|
||||||
colP := pe.paramTable.ColTitleList.List.Position
|
colP := pe.paramTable.ColTitleList.List.Position
|
||||||
rowP := pe.paramTable.RowTitleList.List.Position
|
rowP := pe.paramTable.RowTitleList.List.Position
|
||||||
p := image.Pt(gtx.Dp(t.Theme.UnitEditor.RowTitleWidth), gtx.Dp(t.Theme.UnitEditor.ColumnTitleHeight))
|
p := image.Pt(rowTitleWidth, 0)
|
||||||
defer op.Offset(p).Push(gtx.Ops).Pop()
|
defer op.Offset(p).Push(gtx.Ops).Pop()
|
||||||
gtx.Constraints.Max = gtx.Constraints.Max.Sub(p)
|
gtx.Constraints.Max = gtx.Constraints.Max.Sub(p)
|
||||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
||||||
defer op.Offset(image.Pt(-colP.Offset, -rowP.Offset)).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Pt(-colP.Offset, -rowP.Offset)).Push(gtx.Ops).Pop()
|
||||||
for i := 0; i < units.Count(); i++ {
|
for i := 0; i < units.Count(); i++ {
|
||||||
item := units.Item(i)
|
item := units.Item(i)
|
||||||
if item.TargetUnit > 0 {
|
if item.TargetOk {
|
||||||
pe.drawSignal(gtx, 3-colP.First, i-rowP.First, item.TargetPort-colP.First, item.TargetUnit-1-rowP.First)
|
pe.drawSignal(gtx, i-rowP.First, item.TargetX-colP.First, item.TargetY-rowP.First)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *UnitEditor) drawSignal(gtx C, sx, sy, ex, ey int) {
|
func (pe *UnitEditor) drawSignal(gtx C, sy, ex, ey int) {
|
||||||
t := TrackerFromContext(gtx)
|
t := TrackerFromContext(gtx)
|
||||||
width := float32(gtx.Dp(t.Theme.UnitEditor.Width))
|
width := float32(gtx.Dp(t.Theme.UnitEditor.Width))
|
||||||
height := float32(gtx.Dp(t.Theme.UnitEditor.Height))
|
height := float32(gtx.Dp(t.Theme.UnitEditor.Height))
|
||||||
diam := gtx.Dp(t.Theme.Knob.Diameter)
|
diam := gtx.Dp(t.Theme.Knob.Diameter)
|
||||||
from := f32.Pt((float32(sx)+.5)*width, (float32(sy)+.6)*height)
|
from := f32.Pt(0, float32((sy+1)*gtx.Dp(t.Theme.UnitEditor.Height))-float32(gtx.Dp(t.Theme.SignalRail.SignalWidth)/2))
|
||||||
to := f32.Pt((float32(ex)+.5)*width, (float32(ey)+.6)*height)
|
corner := f32.Pt(1, 1)
|
||||||
var c1, c2 f32.Point
|
if ex > 0 {
|
||||||
if sy < ey {
|
corner.X = -corner.X
|
||||||
from.Y += float32(diam) / 2
|
|
||||||
to.Y -= float32(diam) / 2
|
|
||||||
c1 = from.Add(f32.Pt(0, height/2))
|
|
||||||
c2 = to.Sub(f32.Pt(0, height/2))
|
|
||||||
} else {
|
|
||||||
from.Y -= float32(diam) / 2
|
|
||||||
to.Y += float32(diam) / 2
|
|
||||||
c1 = from.Sub(f32.Pt(0, height/2))
|
|
||||||
c2 = to.Add(f32.Pt(0, height/2))
|
|
||||||
}
|
}
|
||||||
|
if sy < ey {
|
||||||
|
corner.Y = -corner.Y
|
||||||
|
}
|
||||||
|
c := float32(diam) / 2 / float32(math.Sqrt2)
|
||||||
|
topLeft := f32.Pt(float32(ex)*width, float32(ey)*height)
|
||||||
|
center := topLeft.Add(f32.Pt(width/2, height/2))
|
||||||
|
to := mulVec(corner, f32.Pt(c, c)).Add(center)
|
||||||
|
p2 := mulVec(corner, f32.Pt(width/2, height/2)).Add(center)
|
||||||
|
p1 := f32.Pt(p2.X, float32((sy+1)*gtx.Dp(t.Theme.UnitEditor.Height)))
|
||||||
|
if sy > ey {
|
||||||
|
p1 = f32.Pt(p2.X, (float32(sy)+0.5)*float32(gtx.Dp(t.Theme.UnitEditor.Height))+float32(diam)/2)
|
||||||
|
}
|
||||||
|
k := float32(width) / 4
|
||||||
|
//toTan := mulVec(corner, f32.Pt(-k, -k))
|
||||||
|
p2Tan := mulVec(corner, f32.Pt(-k, -k))
|
||||||
|
p1Tan := f32.Pt(k, p2Tan.Y)
|
||||||
|
fromTan := f32.Pt(k, 0)
|
||||||
var path clip.Path
|
var path clip.Path
|
||||||
path.Begin(gtx.Ops)
|
path.Begin(gtx.Ops)
|
||||||
path.MoveTo(from)
|
path.MoveTo(from)
|
||||||
path.CubeTo(c1, c2, to)
|
path.CubeTo(from.Add(fromTan), p1.Sub(p1Tan), p1)
|
||||||
|
path.CubeTo(p1.Add(p1Tan), p2, to)
|
||||||
paint.FillShape(gtx.Ops, t.Theme.UnitEditor.SendTarget,
|
paint.FillShape(gtx.Ops, t.Theme.UnitEditor.SendTarget,
|
||||||
clip.Stroke{
|
clip.Stroke{
|
||||||
Path: path.End(),
|
Path: path.End(),
|
||||||
Width: float32(gtx.Dp(4)),
|
Width: float32(gtx.Dp(t.Theme.SignalRail.LineWidth)),
|
||||||
}.Op())
|
}.Op())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mulVec(a, b f32.Point) f32.Point {
|
||||||
|
return f32.Pt(a.X*b.X, a.Y*b.Y)
|
||||||
|
}
|
||||||
|
|
||||||
func (pe *UnitEditor) layoutFooter(gtx C) D {
|
func (pe *UnitEditor) layoutFooter(gtx C) D {
|
||||||
t := TrackerFromContext(gtx)
|
t := TrackerFromContext(gtx)
|
||||||
st := t.Units().SelectedType()
|
st := t.Units().SelectedType()
|
||||||
@ -426,9 +451,9 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
}
|
}
|
||||||
return D{}
|
return D{}
|
||||||
}
|
}
|
||||||
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
|
title.Layout(gtx)
|
||||||
layout.Rigid(title.Layout),
|
layout.Center.Layout(gtx, widget)
|
||||||
layout.Flexed(1, func(gtx C) D { return layout.Center.Layout(gtx, widget) }),
|
return D{Size: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)}
|
||||||
/* layout.Rigid(func(gtx C) D {
|
/* layout.Rigid(func(gtx C) D {
|
||||||
if p.w.Parameter.Type() != tracker.IDParameter {
|
if p.w.Parameter.Type() != tracker.IDParameter {
|
||||||
hint := p.w.Parameter.Hint()
|
hint := p.w.Parameter.Hint()
|
||||||
@ -445,7 +470,6 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
}
|
}
|
||||||
return D{}
|
return D{}
|
||||||
}),*/
|
}),*/
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawCircle(gtx C, i int, nRGBA color.NRGBA) D {
|
func drawCircle(gtx C, i int, nRGBA color.NRGBA) D {
|
||||||
|
@ -39,7 +39,8 @@ type (
|
|||||||
Type, Comment string
|
Type, Comment string
|
||||||
Disabled bool
|
Disabled bool
|
||||||
StackNeed, StackBefore, StackAfter int
|
StackNeed, StackBefore, StackAfter int
|
||||||
TargetUnit, TargetPort int
|
TargetOk bool // TargetOk indicates if the target unit is valid
|
||||||
|
TargetX, TargetY int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range is used to represent a range [Start,End) of integers
|
// Range is used to represent a range [Start,End) of integers
|
||||||
@ -326,10 +327,17 @@ func (v *Units) Item(index int) UnitListItem {
|
|||||||
return UnitListItem{}
|
return UnitListItem{}
|
||||||
}
|
}
|
||||||
unit := v.d.Song.Patch[v.d.InstrIndex].Units[index]
|
unit := v.d.Song.Patch[v.d.InstrIndex].Units[index]
|
||||||
targetUnit := 0
|
targetOk := false
|
||||||
|
targetY := 0
|
||||||
|
targetX := 0
|
||||||
if unit.Type == "send" {
|
if unit.Type == "send" {
|
||||||
if _, tu, err := v.d.Song.Patch.FindUnit(unit.Parameters["target"]); err == nil {
|
if i, y, err := v.d.Song.Patch.FindUnit(unit.Parameters["target"]); err == nil {
|
||||||
targetUnit = tu + 1
|
targetUnit := v.d.Song.Patch[i].Units[y]
|
||||||
|
if _, x, ok := sointu.FindParamForModulationPort(targetUnit.Type, unit.Parameters["port"]); ok {
|
||||||
|
targetX = x
|
||||||
|
targetY = y
|
||||||
|
targetOk = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UnitListItem{
|
return UnitListItem{
|
||||||
@ -337,8 +345,9 @@ func (v *Units) Item(index int) UnitListItem {
|
|||||||
Comment: unit.Comment,
|
Comment: unit.Comment,
|
||||||
Disabled: unit.Disabled,
|
Disabled: unit.Disabled,
|
||||||
StackNeed: unit.StackNeed(),
|
StackNeed: unit.StackNeed(),
|
||||||
TargetUnit: targetUnit,
|
TargetOk: targetOk,
|
||||||
TargetPort: unit.Parameters["port"],
|
TargetY: targetY,
|
||||||
|
TargetX: targetX,
|
||||||
StackBefore: 0,
|
StackBefore: 0,
|
||||||
StackAfter: 0,
|
StackAfter: 0,
|
||||||
}
|
}
|
||||||
|
@ -10,17 +10,25 @@ type (
|
|||||||
SignalRail struct {
|
SignalRail struct {
|
||||||
signals [][]Signal
|
signals [][]Signal
|
||||||
scratch []signalScratch
|
scratch []signalScratch
|
||||||
|
|
||||||
|
error SignalError
|
||||||
}
|
}
|
||||||
|
|
||||||
signalScratch struct {
|
SignalError struct {
|
||||||
instr, unit int
|
InstrIndex, UnitIndex int
|
||||||
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
Signal struct {
|
Signal struct {
|
||||||
PassThrough int
|
PassThrough int
|
||||||
|
Send bool
|
||||||
StackUse sointu.StackUse
|
StackUse sointu.StackUse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signalScratch struct {
|
||||||
|
instr, unit int
|
||||||
|
}
|
||||||
|
|
||||||
SignalRailType Model
|
SignalRailType Model
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,21 +44,51 @@ func (s *SignalRailType) Item(u int) Signal {
|
|||||||
return s.derived.rail.signals[i][u]
|
return s.derived.rail.signals[i][u]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SignalRail) update(patch sointu.Patch) (err error) {
|
func (s *SignalRailType) Error() SignalError {
|
||||||
|
i := s.d.InstrIndex
|
||||||
|
if i < 0 || i >= len(s.derived.rail.signals) {
|
||||||
|
return SignalError{}
|
||||||
|
}
|
||||||
|
if i == s.derived.rail.error.InstrIndex {
|
||||||
|
return s.derived.rail.error
|
||||||
|
}
|
||||||
|
return SignalError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignalRailType) MaxWidth() int {
|
||||||
|
i := s.d.InstrIndex
|
||||||
|
if i < 0 || i >= len(s.derived.rail.signals) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
ret := 0
|
||||||
|
for _, signal := range s.derived.rail.signals[i] {
|
||||||
|
ret = max(ret, signal.PassThrough+max(len(signal.StackUse.Inputs), signal.StackUse.NumOutputs))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SignalRailType) update() {
|
||||||
|
s := &st.derived.rail
|
||||||
|
patch := st.d.Song.Patch
|
||||||
s.scratch = s.scratch[:0]
|
s.scratch = s.scratch[:0]
|
||||||
|
s.error = SignalError{}
|
||||||
for i, instr := range patch {
|
for i, instr := range patch {
|
||||||
for len(s.signals) <= i {
|
for len(s.signals) <= i {
|
||||||
s.signals = append(s.signals, make([]Signal, len(instr.Units)))
|
s.signals = append(s.signals, make([]Signal, len(instr.Units)))
|
||||||
}
|
}
|
||||||
start := len(s.scratch)
|
start := len(s.scratch)
|
||||||
for u, unit := range instr.Units {
|
for u, unit := range instr.Units {
|
||||||
for len(s.signals[i]) <= i {
|
for len(s.signals[i]) <= u {
|
||||||
s.signals[i] = append(s.signals[i], Signal{})
|
s.signals[i] = append(s.signals[i], Signal{})
|
||||||
}
|
}
|
||||||
stackUse := unit.StackUse()
|
stackUse := unit.StackUse()
|
||||||
numInputs := len(stackUse.Inputs)
|
numInputs := len(stackUse.Inputs)
|
||||||
if len(s.scratch) < numInputs && err != nil {
|
if len(s.scratch) < numInputs {
|
||||||
err = fmt.Errorf("%s unit in instrument %d / %s needs %d inputs, but got only %d", unit.Type, i, instr.Name, numInputs, len(s.scratch))
|
if s.error.Err == nil {
|
||||||
|
s.error.Err = fmt.Errorf("%s unit in instrument %d / %s needs %d inputs, but got only %d", unit.Type, i, instr.Name, numInputs, len(s.scratch))
|
||||||
|
s.error.InstrIndex = i
|
||||||
|
s.error.UnitIndex = u
|
||||||
|
}
|
||||||
s.scratch = s.scratch[:0]
|
s.scratch = s.scratch[:0]
|
||||||
} else {
|
} else {
|
||||||
s.scratch = s.scratch[:len(s.scratch)-numInputs]
|
s.scratch = s.scratch[:len(s.scratch)-numInputs]
|
||||||
@ -58,6 +96,7 @@ func (s *SignalRail) update(patch sointu.Patch) (err error) {
|
|||||||
s.signals[i][u] = Signal{
|
s.signals[i][u] = Signal{
|
||||||
PassThrough: len(s.scratch),
|
PassThrough: len(s.scratch),
|
||||||
StackUse: stackUse,
|
StackUse: stackUse,
|
||||||
|
Send: unit.Type == "send",
|
||||||
}
|
}
|
||||||
for _ = range stackUse.NumOutputs {
|
for _ = range stackUse.NumOutputs {
|
||||||
s.scratch = append(s.scratch, signalScratch{instr: i, unit: u})
|
s.scratch = append(s.scratch, signalScratch{instr: i, unit: u})
|
||||||
@ -67,8 +106,12 @@ func (s *SignalRail) update(patch sointu.Patch) (err error) {
|
|||||||
if instr.NumVoices > 1 && diff != 0 {
|
if instr.NumVoices > 1 && diff != 0 {
|
||||||
if diff < 0 {
|
if diff < 0 {
|
||||||
morepop := (instr.NumVoices - 1) * diff
|
morepop := (instr.NumVoices - 1) * diff
|
||||||
if morepop > len(s.scratch) && err != nil {
|
if morepop > len(s.scratch) {
|
||||||
err = fmt.Errorf("each voice of instrument %d / %s consumes %d signals, but there was not enough signals available", i, instr.Name, -diff)
|
if s.error.Err == nil {
|
||||||
|
s.error.Err = fmt.Errorf("each voice of instrument %d / %s consumes %d signals, but there was not enough signals available", i, instr.Name, -diff)
|
||||||
|
s.error.InstrIndex = i
|
||||||
|
s.error.UnitIndex = -1
|
||||||
|
}
|
||||||
s.scratch = s.scratch[:0]
|
s.scratch = s.scratch[:0]
|
||||||
} else {
|
} else {
|
||||||
s.scratch = s.scratch[:len(s.scratch)-morepop]
|
s.scratch = s.scratch[:len(s.scratch)-morepop]
|
||||||
@ -80,5 +123,16 @@ func (s *SignalRail) update(patch sointu.Patch) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
if len(s.scratch) > 0 && s.error.Err == nil {
|
||||||
|
s.error.Err = fmt.Errorf("instrument %d / %s unit %d / %s leave a signal on stack ", s.scratch[0].instr, patch[s.scratch[0].instr].Name, s.scratch[0].unit, patch[s.scratch[0].instr].Units[s.scratch[0].unit].Type)
|
||||||
|
s.error.InstrIndex = s.scratch[0].instr
|
||||||
|
s.error.UnitIndex = s.scratch[0].unit
|
||||||
|
}
|
||||||
|
if s.error.Err != nil {
|
||||||
|
(*Model)(st).Alerts().AddNamed("SignalError", s.error.Error(), Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SignalError) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user