draft multicore processing

This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-10-21 20:07:06 +03:00
parent c583156d1b
commit 7f03664870
13 changed files with 302 additions and 25 deletions

View File

@ -29,6 +29,10 @@ type (
InstrEditor Model
InstrPresets Model
InstrComment Model
Core1 Model
Core2 Model
Core3 Model
Core4 Model
)
func MakeBool(valueEnabler interface {
@ -66,6 +70,43 @@ func (v Bool) Enabled() bool {
return v.enabler.Enabled()
}
// Core methods
func (m *Model) getCoresBit(bit int) bool {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return false
}
return m.d.Song.Patch[m.d.InstrIndex].CoreBitMask&(1<<bit) != 0
}
func (m *Model) setCoresBit(bit int, value bool) {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return
}
defer (*Model)(m).change("CoreBitMask", PatchChange, MinorChange)()
if value {
m.d.Song.Patch[m.d.InstrIndex].CoreBitMask |= (1 << bit)
} else {
m.d.Song.Patch[m.d.InstrIndex].CoreBitMask &^= (1 << bit)
}
}
func (m *Model) Core1() Bool { return MakeEnabledBool((*Core1)(m)) }
func (m *Core1) Value() bool { return (*Model)(m).getCoresBit(0) }
func (m *Core1) SetValue(val bool) { (*Model)(m).setCoresBit(0, val) }
func (m *Model) Core2() Bool { return MakeEnabledBool((*Core2)(m)) }
func (m *Core2) Value() bool { return (*Model)(m).getCoresBit(1) }
func (m *Core2) SetValue(val bool) { (*Model)(m).setCoresBit(1, val) }
func (m *Model) Core3() Bool { return MakeEnabledBool((*Core3)(m)) }
func (m *Core3) Value() bool { return (*Model)(m).getCoresBit(2) }
func (m *Core3) SetValue(val bool) { (*Model)(m).setCoresBit(2, val) }
func (m *Model) Core4() Bool { return MakeEnabledBool((*Core4)(m)) }
func (m *Core4) Value() bool { return (*Model)(m).getCoresBit(3) }
func (m *Core4) SetValue(val bool) { (*Model)(m).setCoresBit(3, val) }
// Panic methods
func (m *Model) Panic() Bool { return MakeEnabledBool((*Panic)(m)) }

View File

@ -19,6 +19,7 @@ type (
list *layout.List
soloBtn *Clickable
muteBtn *Clickable
coreBtns [4]*Clickable
soloHint string
unsoloHint string
muteHint string
@ -38,6 +39,7 @@ func NewInstrumentProperties() *InstrumentProperties {
muteBtn: new(Clickable),
voices: NewNumericUpDownState(),
splitInstrumentBtn: new(Clickable),
coreBtns: [4]*Clickable{new(Clickable), new(Clickable), new(Clickable), new(Clickable)},
}
ret.soloHint = makeHint("Solo", " (%s)", "SoloToggle")
ret.unsoloHint = makeHint("Unsolo", " (%s)", "SoloToggle")
@ -66,7 +68,21 @@ func (ip *InstrumentProperties) layout(gtx C) D {
)
}
return ip.list.Layout(gtx, 9, func(gtx C, index int) D {
core1btn := ToggleIconBtn(tr.Core1(), tr.Theme, ip.coreBtns[0], icons.ImageCropSquare, icons.ImageFilter1, "Do not render instrument on core 1", "Render instrument on core 1")
core2btn := ToggleIconBtn(tr.Core2(), tr.Theme, ip.coreBtns[1], icons.ImageCropSquare, icons.ImageFilter2, "Do not render instrument on core 2", "Render instrument on core 2")
core3btn := ToggleIconBtn(tr.Core3(), tr.Theme, ip.coreBtns[2], icons.ImageCropSquare, icons.ImageFilter3, "Do not render instrument on core 3", "Render instrument on core 3")
core4btn := ToggleIconBtn(tr.Core4(), tr.Theme, ip.coreBtns[3], icons.ImageCropSquare, icons.ImageFilter4, "Do not render instrument on core 4", "Render instrument on core 4")
corebtnline := func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(core1btn.Layout),
layout.Rigid(core2btn.Layout),
layout.Rigid(core3btn.Layout),
layout.Rigid(core4btn.Layout),
)
}
return ip.list.Layout(gtx, 11, func(gtx C, index int) D {
switch index {
case 0:
return layoutInstrumentPropertyLine(gtx, "Name", func(gtx C) D {
@ -81,6 +97,8 @@ func (ip *InstrumentProperties) layout(gtx C) D {
soloBtn := ToggleIconBtn(tr.Solo(), tr.Theme, ip.soloBtn, icons.ToggleCheckBoxOutlineBlank, icons.ToggleCheckBox, ip.soloHint, ip.unsoloHint)
return layoutInstrumentPropertyLine(gtx, "Solo", soloBtn.Layout)
case 8:
return layoutInstrumentPropertyLine(gtx, "Cores", corebtnline)
case 10:
return layout.UniformInset(unit.Dp(6)).Layout(gtx, func(gtx C) D {
return ip.commentEditor.Layout(gtx, tr.InstrumentComment(), tr.Theme, &tr.Theme.InstrumentEditor.InstrumentComment, "Comment")
})

View File

@ -127,12 +127,12 @@ func (p *Player) Process(buffer sointu.AudioBuffer, context PlayerProcessContext
if p.synth != nil {
rendered, timeAdvanced, err = p.synth.Render(buffer[:framesUntilEvent], timeUntilRowAdvance)
if err != nil {
p.synth = nil
p.destroySynth()
p.send(Alert{Message: fmt.Sprintf("synth.Render: %s", err.Error()), Priority: Error, Name: "PlayerCrash", Duration: defaultAlertDuration})
}
// for performance, we don't check for NaN of every sample, because typically NaNs propagate
if rendered > 0 && (isNaN(buffer[0][0]) || isNaN(buffer[0][1]) || isInf(buffer[0][0]) || isInf(buffer[0][1])) {
p.synth = nil
p.destroySynth()
p.send(Alert{Message: "Inf or NaN detected in synth output", Priority: Error, Name: "PlayerCrash", Duration: defaultAlertDuration})
}
} else {
@ -170,11 +170,18 @@ func (p *Player) Process(buffer sointu.AudioBuffer, context PlayerProcessContext
}
}
// we were not able to fill the buffer with NUM_RENDER_TRIES attempts, destroy synth and throw an error
p.synth = nil
p.destroySynth()
p.events = p.events[:0] // clear events, so we don't try to process them again
p.SendAlert("PlayerCrash", fmt.Sprintf("synth did not fill the audio buffer even with %d render calls", numRenderTries), Error)
}
func (p *Player) destroySynth() {
if p.synth != nil {
p.synth.Close()
p.synth = nil
}
}
func (p *Player) advanceRow() {
if p.song.Score.Length == 0 || p.song.Score.RowsPerPattern == 0 {
return
@ -227,7 +234,7 @@ loop:
switch m := msg.(type) {
case PanicMsg:
if m.bool {
p.synth = nil
p.destroySynth()
} else {
p.compileOrUpdateSynth()
}
@ -283,7 +290,7 @@ loop:
}
case sointu.Synther:
p.synther = m
p.synth = nil
p.destroySynth()
p.compileOrUpdateSynth()
default:
// ignore unknown messages
@ -355,7 +362,7 @@ func (p *Player) compileOrUpdateSynth() {
if p.synth != nil {
err := p.synth.Update(p.song.Patch, p.song.BPM)
if err != nil {
p.synth = nil
p.destroySynth()
p.SendAlert("PlayerCrash", fmt.Sprintf("synth.Update: %v", err), Error)
return
}
@ -363,7 +370,7 @@ func (p *Player) compileOrUpdateSynth() {
var err error
p.synth, err = p.synther.Synth(p.song.Patch, p.song.BPM)
if err != nil {
p.synth = nil
p.destroySynth()
p.SendAlert("PlayerCrash", fmt.Sprintf("synther.Synth: %v", err), Error)
return
}