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 }