Files
muse/lib/audio/effects.go

134 lines
3.4 KiB
Go

package audio
import (
"git.atri.dad/atridad/muse/schema"
)
// Defines the interface for audio effects
type EffectProcessor interface {
Process(samples []float64) []float64
}
// Holds a sequence of effects to be applied
type EffectChain struct {
effects []EffectProcessor
}
// Adds an effect to the chain
func (ec *EffectChain) AddEffect(effect EffectProcessor) {
ec.effects = append(ec.effects, effect)
}
// Applies all effects in the chain sequentially
func (ec *EffectChain) Process(samples []float64) []float64 {
for _, effect := range ec.effects {
samples = effect.Process(samples)
}
return samples
}
// Implements a simple reverb effect
type ReverbEffect struct {
decay float64
wet float64
delayBuffer []float64
delayIndex int
sampleRate float64
}
// Creates a new reverb effect with specified parameters
func NewReverbEffect(decay, wet, sampleRate float64) *ReverbEffect {
delaySize := int(0.1 * sampleRate)
return &ReverbEffect{
decay: decay,
wet: wet,
delayBuffer: make([]float64, delaySize),
delayIndex: 0,
sampleRate: sampleRate,
}
}
// Applies reverb effect to audio samples
func (r *ReverbEffect) Process(samples []float64) []float64 {
output := make([]float64, len(samples))
for i, sample := range samples {
delayed := r.delayBuffer[r.delayIndex]
r.delayBuffer[r.delayIndex] = sample + delayed*r.decay
output[i] = sample*(1.0-r.wet) + delayed*r.wet
r.delayIndex = (r.delayIndex + 1) % len(r.delayBuffer)
}
return output
}
// Implements a simple delay effect
type DelayEffect struct {
delayTime float64
feedback float64
wet float64
delayBuffer []float64
delayIndex int
}
// Creates a new delay effect with specified parameters
func NewDelayEffect(delayTime, feedback, wet, sampleRate float64) *DelayEffect {
delaySize := int(delayTime * sampleRate)
return &DelayEffect{
delayTime: delayTime,
feedback: feedback,
wet: wet,
delayBuffer: make([]float64, delaySize),
delayIndex: 0,
}
}
// Applies delay effect to audio samples
func (d *DelayEffect) Process(samples []float64) []float64 {
output := make([]float64, len(samples))
for i, sample := range samples {
delayed := d.delayBuffer[d.delayIndex]
d.delayBuffer[d.delayIndex] = sample + delayed*d.feedback
output[i] = sample*(1.0-d.wet) + delayed*d.wet
d.delayIndex = (d.delayIndex + 1) % len(d.delayBuffer)
}
return output
}
// Creates an effect chain from schema effect definitions
func CreateEffectChain(effects []schema.Effect, sampleRate float64) *EffectChain {
chain := &EffectChain{}
for _, effect := range effects {
switch effect.Type {
case "reverb":
decay := getFloatParam(effect.Params, "decay", 0.5)
wet := getFloatParam(effect.Params, "wet", 0.3)
chain.AddEffect(NewReverbEffect(decay, wet, sampleRate))
case "delay":
delayTime := getFloatParam(effect.Params, "time", 0.25)
feedback := getFloatParam(effect.Params, "feedback", 0.4)
wet := getFloatParam(effect.Params, "wet", 0.3)
chain.AddEffect(NewDelayEffect(delayTime, feedback, wet, sampleRate))
}
}
return chain
}
// Safely extracts a float parameter with default fallback
func getFloatParam(params map[string]interface{}, key string, defaultValue float64) float64 {
if val, exists := params[key]; exists {
switch v := val.(type) {
case float64:
return v
case int:
return float64(v)
}
}
return defaultValue
}