Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
5b14bf9
Update to go 1.21
heucuva Dec 16, 2023
95a589f
Simplify pattern processing
heucuva Dec 16, 2023
844c8ea
simplify playback system a little
heucuva Dec 16, 2023
82f90aa
clean up more playback object duplication
heucuva Dec 17, 2023
0ed614c
Fix period/frequency calculations
heucuva Dec 19, 2023
d01d563
update go.mod
heucuva Dec 19, 2023
2645d2c
reduce generics usages
heucuva Dec 19, 2023
524b8ea
reduce complexity of period calculations
heucuva Dec 20, 2023
101e1e8
effect cleanup
heucuva Dec 21, 2023
0e64e52
spacing
heucuva Dec 21, 2023
5b894e3
drastically simplify period considerations
heucuva Dec 22, 2023
c8af97c
simplify porta of periods
heucuva Dec 22, 2023
0433069
missed in last checkin
heucuva Dec 22, 2023
c90073a
fix test and update go.mod
heucuva Dec 22, 2023
29866e0
extra simplification of period converters
heucuva Dec 22, 2023
89c447a
clean up voice cloning
heucuva Dec 22, 2023
30d81cd
clean up format field names
heucuva Dec 23, 2023
0c4148c
clean up type conversions for channel data
heucuva Dec 23, 2023
7f4ea9b
clean up envelope code
heucuva Dec 23, 2023
de71b60
Fix filter envelope
heucuva Dec 24, 2023
79d3f38
better fix for pitch filter
heucuva Dec 24, 2023
3bf62a2
better calculation of filter value
heucuva Dec 24, 2023
422c290
Fix for s3m/mod envelope crash
heucuva Dec 24, 2023
da045fb
Support mod fine-panning
heucuva Dec 24, 2023
8c477b0
s3m playback cleanup
heucuva Dec 24, 2023
e90e338
first pass on cleaning up interfaces
heucuva Dec 24, 2023
4d2177e
further cleanup
heucuva Dec 24, 2023
8e31e41
rework tracing to make issues more visible
heucuva Dec 25, 2023
8155ef6
round 1 of playback cleanup
heucuva Jan 13, 2024
c749129
round 2 of playback cleanup
heucuva Jan 13, 2024
25a00e8
round 3 of playback cleanup
heucuva Jan 13, 2024
c283063
round 4 of playback cleanup
heucuva Jan 13, 2024
b16c3b1
Fix song looping, starting order, and pattern loop
heucuva Jan 14, 2024
b832b7d
past note fixes and envelope cleanup
heucuva Jan 14, 2024
93083b8
fix for past notes being overlimited
heucuva Jan 14, 2024
7070a70
past note cleanup, voice cleanup
heucuva Jan 14, 2024
9dd2d0a
hook up new note actions flag
heucuva Jan 14, 2024
6a1e5e8
move frequency type to its own package
heucuva Jan 15, 2024
f5de2c9
consistent package imports
heucuva Jan 15, 2024
cdc86fa
make format voices understand instruments instead of generic configs
heucuva Jan 15, 2024
d945fea
Drastically simpler patterns and rows
heucuva Jan 15, 2024
5388f66
Clean up settings object
heucuva Jan 15, 2024
f5f035c
pipe opl2 code back in
heucuva Jan 15, 2024
2907fec
fix ordering
heucuva Jan 15, 2024
4fc3866
fix for runaway past notes
heucuva Jan 16, 2024
91c7806
fix vol0 opt code
heucuva Jan 16, 2024
cac239b
safer amiga limits
heucuva Jan 16, 2024
7eb270d
fix extra spammative new note actions
heucuva Jan 16, 2024
500042d
produce multiple ticks worth of output in allotted time
heucuva Jan 16, 2024
fd24f93
support s3m quirk of porta from final period
heucuva Jan 16, 2024
69957cc
Fix OPL2 deferred keyon and base freq
heucuva Jan 16, 2024
70f7c10
reintegrate play until order/row support
heucuva Jan 17, 2024
6567270
major cleanup
heucuva Jan 18, 2024
a109987
cleanup and more formalization
heucuva Jan 19, 2024
e952940
reduce complexity of xm instrument mapping
heucuva Jan 20, 2024
422589e
simplify and clean up, fix pcm format detector
heucuva Jan 20, 2024
2325489
fix instrument mapping and semitone remap
heucuva Jan 21, 2024
f6d03b7
todo cleanup
heucuva Jan 21, 2024
4be63e4
final todo work
heucuva Jan 21, 2024
ce41a19
clean up issues with opl2 linking, update gomod
heucuva Jan 21, 2024
83be144
fix autovibrato errors
heucuva Jan 21, 2024
84b0769
final cleanups
heucuva Jan 21, 2024
4094620
clean up generation interface
heucuva Jan 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
70 changes: 0 additions & 70 deletions channel.go

This file was deleted.

42 changes: 42 additions & 0 deletions channelstate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package playback

import (
"github.com/gotracker/gomixing/sampling"

"github.com/gotracker/playback/instrument"
"github.com/gotracker/playback/voice/types"
)

// ChannelState is the information needed to make an instrument play
type ChannelState[TPeriod types.Period, TVolume types.Volume, TPanning types.Panning] struct {
Instrument instrument.InstrumentIntf
Period TPeriod
vol TVolume
Pos sampling.Pos
Pan TPanning
}

// Reset sets the render state to defaults
func (s *ChannelState[TPeriod, TVolume, TPanning]) Reset() {
s.Instrument = nil
var emptyPeriod TPeriod
s.Period = emptyPeriod
s.Pos = sampling.Pos{}
var emptyPan TPanning
s.Pan = emptyPan
}

func (s *ChannelState[TPeriod, TVolume, TPanning]) GetVolume() TVolume {
return s.vol
}

func (s *ChannelState[TPeriod, TVolume, TPanning]) SetVolume(vol TVolume) {
if !vol.IsUseInstrumentVol() {
s.vol = vol
}
}

func (s *ChannelState[TPeriod, TVolume, TPanning]) NoteCut() {
var empty TPeriod
s.Period = empty
}
136 changes: 55 additions & 81 deletions effect.go
Original file line number Diff line number Diff line change
@@ -1,142 +1,116 @@
package playback

import "fmt"
import (
"fmt"
"reflect"

"github.com/gotracker/playback/index"
"github.com/gotracker/playback/period"
"github.com/gotracker/playback/player/machine"
"github.com/gotracker/playback/song"
)

// Effect is an interface to command/effect
type Effect interface {
//fmt.Stringer
TraceData() string
}

type effectPreStartIntf[TMemory, TChannelData any] interface {
PreStart(Channel[TMemory, TChannelData], Playback) error
type Effecter[TMemory song.ChannelMemory] interface {
GetEffects(TMemory, period.Period) []Effect
}

// EffectPreStart triggers when the effect enters onto the channel state
func EffectPreStart[TMemory, TChannelData any](e Effect, cs Channel[TMemory, TChannelData], p Playback) error {
if eff, ok := e.(effectPreStartIntf[TMemory, TChannelData]); ok {
if err := eff.PreStart(cs, p); err != nil {
return err
}
func GetEffects[TPeriod period.Period, TMemory song.ChannelMemory, TChannelData song.ChannelData[TVolume], TGlobalVolume, TMixingVolume, TVolume song.Volume, TPanning song.Panning](mem TMemory, d TChannelData) []Effect {
var e []Effect
if eff, ok := any(d).(Effecter[TMemory]); ok {
var p TPeriod
e = eff.GetEffects(mem, p)
}
return nil
return e
}

type effectStartIntf[TMemory, TChannelData any] interface {
Start(Channel[TMemory, TChannelData], Playback) error
type EffectNamer interface {
Names() []string
}

// EffectStart triggers on the first tick, but before the Tick() function is called
func EffectStart[TMemory, TChannelData any](e Effect, cs Channel[TMemory, TChannelData], p Playback) error {
if eff, ok := e.(effectStartIntf[TMemory, TChannelData]); ok {
if err := eff.Start(cs, p); err != nil {
return err
}
func GetEffectNames(e Effect) []string {
if namer, ok := e.(EffectNamer); ok {
return namer.Names()
} else {
typ := reflect.TypeOf(e)
return []string{typ.Name()}
}
return nil
}

type effectTickIntf[TMemory, TChannelData any] interface {
Tick(Channel[TMemory, TChannelData], Playback, int) error
// CombinedEffect specifies multiple simultaneous effects into one
type CombinedEffect[TPeriod period.Period, TGlobalVolume, TMixingVolume, TVolume song.Volume, TPanning song.Panning, TMemory song.ChannelMemory, TChannelData song.ChannelData[TVolume]] struct {
Effects []Effect
}

// EffectTick is called on every tick
func EffectTick[TMemory, TChannelData any](e Effect, cs Channel[TMemory, TChannelData], p Playback, currentTick int) error {
if eff, ok := e.(effectTickIntf[TMemory, TChannelData]); ok {
if err := eff.Tick(cs, p, currentTick); err != nil {
return err
// String returns the string for the effect list
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) String() string {
for _, eff := range e.Effects {
s := fmt.Sprint(eff)
if s != "" {
return s
}
}
return nil
return ""
}

type effectStopIntf[TMemory, TChannelData any] interface {
Stop(Channel[TMemory, TChannelData], Playback, int) error
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) Names() []string {
var names []string
for _, eff := range e.Effects {
names = append(names, GetEffectNames(eff)...)
}
return names
}

// EffectStop is called on the last tick of the row, but after the Tick() function is called
func EffectStop[TMemory, TChannelData any](e Effect, cs Channel[TMemory, TChannelData], p Playback, lastTick int) error {
if eff, ok := e.(effectStopIntf[TMemory, TChannelData]); ok {
if err := eff.Stop(cs, p, lastTick); err != nil {
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) OrderStart(ch index.Channel, m machine.Machine[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning]) error {
for _, effect := range e.Effects {
if err := m.DoInstructionOrderStart(ch, effect); err != nil {
return err
}
}
return nil
}

// CombinedEffect specifies multiple simultaneous effects into one
type CombinedEffect[TMemory, TChannelData any] struct {
Effects []Effect
}

// PreStart triggers when the effect enters onto the channel state
func (e CombinedEffect[TMemory, TChannelData]) PreStart(cs Channel[TMemory, TChannelData], p Playback) error {
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) RowStart(ch index.Channel, m machine.Machine[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning]) error {
for _, effect := range e.Effects {
if err := EffectPreStart(effect, cs, p); err != nil {
if err := m.DoInstructionRowStart(ch, effect); err != nil {
return err
}
}
return nil
}

// Start triggers on the first tick, but before the Tick() function is called
func (e CombinedEffect[TMemory, TChannelData]) Start(cs Channel[TMemory, TChannelData], p Playback) error {
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) Tick(ch index.Channel, m machine.Machine[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning], tick int) error {
for _, effect := range e.Effects {
if err := EffectStart(effect, cs, p); err != nil {
if err := m.DoInstructionTick(ch, effect); err != nil {
return err
}
}
return nil
}

// Tick is called on every tick
func (e CombinedEffect[TMemory, TChannelData]) Tick(cs Channel[TMemory, TChannelData], p Playback, currentTick int) error {
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) RowEnd(ch index.Channel, m machine.Machine[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning]) error {
for _, effect := range e.Effects {
if err := EffectTick(effect, cs, p, currentTick); err != nil {
if err := m.DoInstructionRowEnd(ch, effect); err != nil {
return err
}
}
return nil
}

// Stop is called on the last tick of the row, but after the Tick() function is called
func (e CombinedEffect[TMemory, TChannelData]) Stop(cs Channel[TMemory, TChannelData], p Playback, lastTick int) error {
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) OrderEnd(ch index.Channel, m machine.Machine[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning]) error {
for _, effect := range e.Effects {
if err := EffectStop(effect, cs, p, lastTick); err != nil {
if err := m.DoInstructionOrderEnd(ch, effect); err != nil {
return err
}
}
return nil
}

// String returns the string for the effect list
func (e CombinedEffect[TMemory, TChannelData]) String() string {
for _, eff := range e.Effects {
s := fmt.Sprint(eff)
if s != "" {
return s
}
}
return ""
}

// DoEffect runs the standard tick lifetime of an effect
func DoEffect[TMemory, TChannelData any](e Effect, cs Channel[TMemory, TChannelData], p Playback, currentTick int, lastTick bool) error {
if e == nil {
return nil
}

if currentTick == 0 {
if err := EffectStart(e, cs, p); err != nil {
return err
}
}
if err := EffectTick(e, cs, p, currentTick); err != nil {
return err
}
if lastTick {
if err := EffectStop(e, cs, p, currentTick); err != nil {
return err
}
}
return nil
func (e CombinedEffect[TPeriod, TGlobalVolume, TMixingVolume, TVolume, TPanning, TMemory, TChannelData]) TraceData() string {
return e.String()
}
Loading