134 lines
3.4 KiB
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
|
|
}
|