mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-19 05:24:48 -04:00
feat(tracker): show CPU load percentage in the song panel
This commit is contained in:
parent
1a13fadd75
commit
340620ed49
@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- Show CPU load percentage in the song panel ([#192][i192])
|
||||||
- Theme can be user configured, in theme.yml. This theme.yml should be placed in
|
- Theme can be user configured, in theme.yml. This theme.yml should be placed in
|
||||||
the usual sointu config directory (i.e.
|
the usual sointu config directory (i.e.
|
||||||
`os.UserConfigDir()/sointu/theme.yml`). See
|
`os.UserConfigDir()/sointu/theme.yml`). See
|
||||||
@ -332,3 +333,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
[i170]: https://github.com/vsariola/sointu/issues/170
|
[i170]: https://github.com/vsariola/sointu/issues/170
|
||||||
[i176]: https://github.com/vsariola/sointu/issues/176
|
[i176]: https://github.com/vsariola/sointu/issues/176
|
||||||
[i186]: https://github.com/vsariola/sointu/issues/186
|
[i186]: https://github.com/vsariola/sointu/issues/186
|
||||||
|
[i192]: https://github.com/vsariola/sointu/issues/192
|
||||||
|
@ -63,6 +63,7 @@ type (
|
|||||||
Panic bool
|
Panic bool
|
||||||
SongPosition sointu.SongPos
|
SongPosition sointu.SongPos
|
||||||
VoiceLevels [vm.MAX_VOICES]float32
|
VoiceLevels [vm.MAX_VOICES]float32
|
||||||
|
CPULoad float64
|
||||||
|
|
||||||
HasDetectorResult bool
|
HasDetectorResult bool
|
||||||
DetectorResult DetectorResult
|
DetectorResult DetectorResult
|
||||||
|
@ -129,6 +129,14 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
|||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", NumUpDown(tr.Theme, t.Step, "Cursor step").Layout)
|
return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", NumUpDown(tr.Theme, t.Step, "Cursor step").Layout)
|
||||||
}),
|
}),
|
||||||
|
layout.Rigid(func(gtx C) D {
|
||||||
|
cpuload := tr.Model.CPULoad()
|
||||||
|
label := Label(tr.Theme, &tr.Theme.SongPanel.RowValue, fmt.Sprintf("%.0f %%", cpuload*100))
|
||||||
|
if cpuload >= 1 {
|
||||||
|
label.Color = tr.Theme.SongPanel.ErrorColor
|
||||||
|
}
|
||||||
|
return layoutSongOptionRow(gtx, tr.Theme, "CPU load", label.Layout)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
@ -69,6 +69,7 @@ type (
|
|||||||
linkInstrTrack bool
|
linkInstrTrack bool
|
||||||
|
|
||||||
voiceLevels [vm.MAX_VOICES]float32
|
voiceLevels [vm.MAX_VOICES]float32
|
||||||
|
cpuLoad float64
|
||||||
|
|
||||||
signalAnalyzer *ScopeModel
|
signalAnalyzer *ScopeModel
|
||||||
detectorResult DetectorResult
|
detectorResult DetectorResult
|
||||||
@ -343,6 +344,7 @@ func (m *Model) ProcessMsg(msg MsgToModel) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
m.panic = msg.Panic
|
m.panic = msg.Panic
|
||||||
|
m.cpuLoad = msg.CPULoad
|
||||||
}
|
}
|
||||||
if msg.HasDetectorResult {
|
if msg.HasDetectorResult {
|
||||||
m.detectorResult = msg.DetectorResult
|
m.detectorResult = msg.DetectorResult
|
||||||
@ -377,6 +379,8 @@ func (m *Model) ProcessMsg(msg MsgToModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) CPULoad() float64 { return m.cpuLoad }
|
||||||
|
|
||||||
func (m *Model) SignalAnalyzer() *ScopeModel { return m.signalAnalyzer }
|
func (m *Model) SignalAnalyzer() *ScopeModel { return m.signalAnalyzer }
|
||||||
func (m *Model) Broker() *Broker { return m.broker }
|
func (m *Model) Broker() *Broker { return m.broker }
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"slices"
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/vsariola/sointu"
|
"github.com/vsariola/sointu"
|
||||||
"github.com/vsariola/sointu/vm"
|
"github.com/vsariola/sointu/vm"
|
||||||
@ -32,6 +33,8 @@ type (
|
|||||||
frameDeltas map[any]int64 // Player.frame (approx.)= event.Timestamp + frameDeltas[event.Source]
|
frameDeltas map[any]int64 // Player.frame (approx.)= event.Timestamp + frameDeltas[event.Source]
|
||||||
events NoteEventList
|
events NoteEventList
|
||||||
|
|
||||||
|
cpuload float64 // current CPU load of the player, used to adjust the render rate
|
||||||
|
|
||||||
synther sointu.Synther // the synther used to create new synths
|
synther sointu.Synther // the synther used to create new synths
|
||||||
broker *Broker // the broker used to communicate with different parts of the tracker
|
broker *Broker // the broker used to communicate with different parts of the tracker
|
||||||
}
|
}
|
||||||
@ -86,6 +89,9 @@ func NewPlayer(broker *Broker, synther sointu.Synther) *Player {
|
|||||||
// buffer. It is used to trigger and release notes during processing. The
|
// buffer. It is used to trigger and release notes during processing. The
|
||||||
// context is also used to get the current BPM from the host.
|
// context is also used to get the current BPM from the host.
|
||||||
func (p *Player) Process(buffer sointu.AudioBuffer, context PlayerProcessContext) {
|
func (p *Player) Process(buffer sointu.AudioBuffer, context PlayerProcessContext) {
|
||||||
|
startTime := time.Now()
|
||||||
|
startFrame := p.frame
|
||||||
|
|
||||||
p.processMessages(context)
|
p.processMessages(context)
|
||||||
p.events.adjustTimes(p.frameDeltas, p.frame, p.frame+int64(len(buffer)))
|
p.events.adjustTimes(p.frameDeltas, p.frame, p.frame+int64(len(buffer)))
|
||||||
|
|
||||||
@ -145,6 +151,7 @@ func (p *Player) Process(buffer sointu.AudioBuffer, context PlayerProcessContext
|
|||||||
}
|
}
|
||||||
// when the buffer is full, return
|
// when the buffer is full, return
|
||||||
if len(buffer) == 0 {
|
if len(buffer) == 0 {
|
||||||
|
p.updateCPULoad(time.Since(startTime), p.frame-startFrame)
|
||||||
p.send(nil)
|
p.send(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -345,7 +352,7 @@ func (p *Player) compileOrUpdateSynth() {
|
|||||||
|
|
||||||
// all sendTargets from player are always non-blocking, to ensure that the player thread cannot end up in a dead-lock
|
// all sendTargets from player are always non-blocking, to ensure that the player thread cannot end up in a dead-lock
|
||||||
func (p *Player) send(message interface{}) {
|
func (p *Player) send(message interface{}) {
|
||||||
TrySend(p.broker.ToModel, MsgToModel{HasPanicPosLevels: true, Panic: p.synth == nil, SongPosition: p.songPos, VoiceLevels: p.voiceLevels, Data: message})
|
TrySend(p.broker.ToModel, MsgToModel{HasPanicPosLevels: true, Panic: p.synth == nil, SongPosition: p.songPos, VoiceLevels: p.voiceLevels, CPULoad: p.cpuload, Data: message})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) processNoteEvent(ev NoteEvent) {
|
func (p *Player) processNoteEvent(ev NoteEvent) {
|
||||||
@ -405,3 +412,14 @@ func (p *Player) processNoteEvent(ev NoteEvent) {
|
|||||||
p.synth.Trigger(oldestVoice, ev.Note)
|
p.synth.Trigger(oldestVoice, ev.Note)
|
||||||
TrySend(p.broker.ToModel, MsgToModel{TriggerChannel: instrIndex + 1})
|
TrySend(p.broker.ToModel, MsgToModel{TriggerChannel: instrIndex + 1})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Player) updateCPULoad(duration time.Duration, frames int64) {
|
||||||
|
if frames <= 0 {
|
||||||
|
return // no frames rendered, so cannot compute CPU load
|
||||||
|
}
|
||||||
|
realtime := float64(duration) / 1e9
|
||||||
|
songtime := float64(frames) / 44100
|
||||||
|
newload := realtime / songtime
|
||||||
|
alpha := math.Exp(-songtime) // smoothing factor, time constant of 1 second
|
||||||
|
p.cpuload = float64(p.cpuload)*alpha + newload*(1-alpha)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user