Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions player/intf/channel.go → channel.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package intf
package playback

import (
"github.com/gotracker/gomixing/panning"
"github.com/gotracker/gomixing/sampling"
"github.com/gotracker/gomixing/volume"
"github.com/gotracker/voice"

"github.com/gotracker/playback/instrument"
"github.com/gotracker/playback/note"
"github.com/gotracker/playback/player/output"
"github.com/gotracker/playback/song/instrument"
"github.com/gotracker/playback/song/note"
)

// Channel is an interface for channel state
Expand Down
2 changes: 1 addition & 1 deletion player/intf/effect.go → effect.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package intf
package playback

import "fmt"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"math"

"github.com/gotracker/gomixing/volume"
"github.com/gotracker/playback/filter"
"github.com/gotracker/voice/period"
)

Expand Down Expand Up @@ -33,7 +32,7 @@ func NewAmigaLPF(instrument, playback period.Frequency) *AmigaLPF {
return &lpf
}

func (f *AmigaLPF) Clone() filter.Filter {
func (f *AmigaLPF) Clone() Filter {
c := *f
c.channels = make([]channelData, len(f.channels))
for i := range f.channels {
Expand Down
7 changes: 3 additions & 4 deletions format/internal/filter/echofilter.go → filter/echofilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package filter
import (
"math"

"github.com/gotracker/playback/filter"
"github.com/gotracker/voice/period"

"github.com/gotracker/gomixing/volume"
Expand All @@ -22,8 +21,8 @@ type EchoFilterFactory struct {
EchoFilterSettings
}

func (e *EchoFilterFactory) Factory() filter.Factory {
return func(instrument, playback period.Frequency) filter.Filter {
func (e *EchoFilterFactory) Factory() Factory {
return func(instrument, playback period.Frequency) Filter {
echo := EchoFilter{
EchoFilterSettings: e.EchoFilterSettings,
sampleRate: playback,
Expand All @@ -48,7 +47,7 @@ type EchoFilter struct {
delay [2]delayInfo // L,R
}

func (e *EchoFilter) Clone() filter.Filter {
func (e *EchoFilter) Clone() Filter {
clone := EchoFilter{
EchoFilterSettings: e.EchoFilterSettings,
sampleRate: e.sampleRate,
Expand Down
9 changes: 7 additions & 2 deletions player/intf/format.go → format.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package intf
package playback

import "github.com/gotracker/playback/format/settings"
import (
"io"

"github.com/gotracker/playback/settings"
)

// Format is an interface to a music file format loader
type Format[TChannelData any] interface {
Load(filename string, s *settings.Settings) (Playback, error)
LoadFromReader(r io.Reader, s *settings.Settings) (Playback, error)
}
22 changes: 22 additions & 0 deletions format/common/loadformat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package common

import (
"io"

"github.com/gotracker/playback/settings"
)

type ReaderFunc[TSong any] func(r io.Reader, s *settings.Settings) (*TSong, error)

type ManagerFactory[TSong, TManager any] func(*TSong) (*TManager, error)

func Load[TSong, TManager any](r io.Reader, reader ReaderFunc[TSong], factory ManagerFactory[TSong, TManager], s *settings.Settings) (*TManager, error) {
song, err := reader(r, s)
if err != nil {
return nil, err
}

m, err := factory(song)

return m, err
}
45 changes: 39 additions & 6 deletions format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ package format

import (
"errors"
"io"
"os"

"github.com/gotracker/playback"
"github.com/gotracker/playback/format/it"
"github.com/gotracker/playback/format/mod"
"github.com/gotracker/playback/format/s3m"
"github.com/gotracker/playback/format/settings"
"github.com/gotracker/playback/format/xm"
"github.com/gotracker/playback/player/intf"
"github.com/gotracker/playback/settings"
"github.com/gotracker/playback/song"
)

var (
supportedFormats = make(map[string]intf.Format[song.ChannelData])
supportedFormats = make(map[string]playback.Format[song.ChannelData])
)

// Load loads the a file into a playback manager
func Load(filename string, options ...settings.OptionFunc) (intf.Playback, intf.Format[song.ChannelData], error) {
func Load(filename string, options ...settings.OptionFunc) (playback.Playback, playback.Format[song.ChannelData], error) {
s := &settings.Settings{}
for _, opt := range options {
if err := opt(s); err != nil {
Expand All @@ -27,8 +28,40 @@ func Load(filename string, options ...settings.OptionFunc) (intf.Playback, intf.
}

for _, f := range supportedFormats {
if playback, err := f.Load(filename, s); err == nil {
return playback, f, nil
if pb, err := f.Load(filename, s); err == nil {
return pb, f, nil
} else if os.IsNotExist(err) {
return nil, nil, err
}
}
return nil, nil, errors.New("unsupported format")
}

// LoadFromReader loads a song file on a reader into a playback manager
func LoadFromReader(format string, r io.Reader, options ...settings.OptionFunc) (playback.Playback, playback.Format[song.ChannelData], error) {
s := &settings.Settings{}
for _, opt := range options {
if err := opt(s); err != nil {
return nil, nil, err
}
}

if format != "" {
f, ok := supportedFormats[format]
if !ok {
return nil, nil, errors.New("unsupported format")
}

if pb, err := f.LoadFromReader(r, s); err == nil {
return pb, f, nil
} else {
return nil, nil, err
}
}

for _, f := range supportedFormats {
if pb, err := f.LoadFromReader(r, s); err == nil {
return pb, f, nil
} else if os.IsNotExist(err) {
return nil, nil, err
}
Expand Down
23 changes: 4 additions & 19 deletions format/it/layout/channel/channel.go → format/it/channel/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,17 @@ import (
itfile "github.com/gotracker/goaudiofile/music/tracked/it"
"github.com/gotracker/gomixing/volume"

itNote "github.com/gotracker/playback/format/it/conversion/note"
itVolume "github.com/gotracker/playback/format/it/conversion/volume"
"github.com/gotracker/playback/song/instrument"
"github.com/gotracker/playback/song/note"
itNote "github.com/gotracker/playback/format/it/note"
itVolume "github.com/gotracker/playback/format/it/volume"
"github.com/gotracker/playback/instrument"
"github.com/gotracker/playback/note"
)

const MaxTotalChannels = 64

// DataEffect is the type of a channel's EffectParameter value
type DataEffect uint8

// SampleID is an InstrumentID that is a combination of InstID and SampID
type SampleID struct {
InstID uint8
Semitone note.Semitone
}

// IsEmpty returns true if the sample ID is empty
func (s SampleID) IsEmpty() bool {
return s.InstID == 0
}

func (s SampleID) String() string {
return fmt.Sprint(s.InstID)
}

// Data is the data for the channel
type Data struct {
What itfile.ChannelDataFlags
Expand Down
27 changes: 5 additions & 22 deletions format/it/layout/channel/memory.go → format/it/channel/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,12 @@ package channel
import (
"github.com/gotracker/voice/oscillator"

"github.com/gotracker/playback/format/internal/effect"
"github.com/gotracker/playback/format/internal/memory"
formatutil "github.com/gotracker/playback/format/internal/util"
"github.com/gotracker/playback/memory"
oscillatorImpl "github.com/gotracker/playback/oscillator"
"github.com/gotracker/playback/tremor"
formatutil "github.com/gotracker/playback/util"
)

type SharedMemory struct {
// LinearFreqSlides is true if linear frequency slides are enabled (false = amiga-style period-based slides)
LinearFreqSlides bool
// OldEffectMode performs somewhat different operations for some effects:
// On:
// - Vibrato does not operate on tick 0 and has double depth
// - Sample Offset will ignore the command if it would exceed the length
// Off:
// - Vibrato is updated every frame
// - Sample Offset will set the offset to the end of the sample if it would exceed the length
OldEffectMode bool
// EFGLinkMode will make effects Exx, Fxx, and Gxx share the same memory
EFGLinkMode bool
// ResetMemoryAtStartOfOrder0 if true will reset the memory registers when the first tick of the first row of the first order pattern plays
ResetMemoryAtStartOfOrder0 bool
}

// Memory is the storage object for custom effect/effect values
type Memory struct {
volumeSlide memory.Value[DataEffect] `usage:"Dxy"`
Expand All @@ -46,7 +29,7 @@ type Memory struct {
panbrello memory.Value[DataEffect] `usage:"Yxy"`
volChanVolumeSlide memory.Value[DataEffect] `usage:"vDxy"`

tremorMem effect.Tremor
tremorMem tremor.Tremor
vibratoOscillator oscillator.Oscillator
tremoloOscillator oscillator.Oscillator
panbrelloOscillator oscillator.Oscillator
Expand Down Expand Up @@ -155,7 +138,7 @@ func (m *Memory) Panbrello(input DataEffect) DataEffect {
}

// TremorMem returns the Tremor object
func (m *Memory) TremorMem() *effect.Tremor {
func (m *Memory) TremorMem() *tremor.Tremor {
return &m.tremorMem
}

Expand Down
22 changes: 22 additions & 0 deletions format/it/channel/sampleid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package channel

import (
"fmt"

"github.com/gotracker/playback/note"
)

// SampleID is an InstrumentID that is a combination of InstID and SampID
type SampleID struct {
InstID uint8
Semitone note.Semitone
}

// IsEmpty returns true if the sample ID is empty
func (s SampleID) IsEmpty() bool {
return s.InstID == 0
}

func (s SampleID) String() string {
return fmt.Sprint(s.InstID)
}
18 changes: 18 additions & 0 deletions format/it/channel/sharedmem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package channel

type SharedMemory struct {
// LinearFreqSlides is true if linear frequency slides are enabled (false = amiga-style period-based slides)
LinearFreqSlides bool
// OldEffectMode performs somewhat different operations for some effects:
// On:
// - Vibrato does not operate on tick 0 and has double depth
// - Sample Offset will ignore the command if it would exceed the length
// Off:
// - Vibrato is updated every frame
// - Sample Offset will set the offset to the end of the sample if it would exceed the length
OldEffectMode bool
// EFGLinkMode will make effects Exx, Fxx, and Gxx share the same memory
EFGLinkMode bool
// ResetMemoryAtStartOfOrder0 if true will reset the memory registers when the first tick of the first row of the first order pattern plays
ResetMemoryAtStartOfOrder0 bool
}
21 changes: 17 additions & 4 deletions format/it/it.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
package it

import (
"io"

"github.com/gotracker/playback"
"github.com/gotracker/playback/format/it/load"
"github.com/gotracker/playback/format/settings"
"github.com/gotracker/playback/player/intf"
"github.com/gotracker/playback/settings"
"github.com/gotracker/playback/util"
)

type format struct{}
Expand All @@ -15,6 +18,16 @@ var (
)

// Load loads an IT file into a playback system
func (f format) Load(filename string, s *settings.Settings) (intf.Playback, error) {
return load.IT(filename, s)
func (f format) Load(filename string, s *settings.Settings) (playback.Playback, error) {
r, err := util.ReadFile(filename)
if err != nil {
return nil, err
}

return f.LoadFromReader(r, s)
}

// LoadFromReader loads an IT file on a reader into a playback system
func (f format) LoadFromReader(r io.Reader, s *settings.Settings) (playback.Playback, error) {
return load.IT(r, s)
}
17 changes: 17 additions & 0 deletions format/it/layout/channelsetting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package layout

import (
"github.com/gotracker/gomixing/panning"
"github.com/gotracker/gomixing/volume"
"github.com/gotracker/playback/format/it/channel"
)

// ChannelSetting is settings specific to a single channel
type ChannelSetting struct {
Enabled bool
OutputChannelNum int
InitialVolume volume.Volume
ChannelVolume volume.Volume
InitialPanning panning.Position
Memory channel.Memory
}
12 changes: 12 additions & 0 deletions format/it/layout/header.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package layout

import "github.com/gotracker/gomixing/volume"

// Header is a mildly-decoded IT header definition
type Header struct {
Name string
InitialSpeed int
InitialTempo int
GlobalVolume volume.Volume
MixingVolume volume.Volume
}
12 changes: 12 additions & 0 deletions format/it/layout/noteinstrument.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package layout

import (
"github.com/gotracker/playback/instrument"
"github.com/gotracker/playback/note"
)

// NoteInstrument is the note remapping and instrument pair
type NoteInstrument struct {
NoteRemap note.Semitone
Inst *instrument.Instrument
}
Loading