Files
ngzz-mc/internal/audio/synth.go

70 lines
1.5 KiB
Go
Raw Normal View History

// 程序化音效合成:正弦脉冲与噪声脉冲(无音频文件的占位实现,音频.md §6)。
package audio
import (
"math"
"math/rand"
"time"
"github.com/faiface/beep"
)
// sineBurst 正弦脉冲(频率 Hz、时长秒)。
func sineBurst(freq float64, dur float64) *burst {
n := int(44100 * dur)
return &burst{n: n, gen: func(t int) float64 {
env := 1 - float64(t)/float64(n) // 线性衰减包络
return env * 0.5 * math.Sin(2*math.Pi*freq*float64(t)/44100)
}}
}
// noiseBurst 噪声脉冲(破坏音效感)。
func noiseBurst(dur float64) *burst {
n := int(44100 * dur)
rng := rand.New(rand.NewSource(42))
return &burst{n: n, gen: func(t int) float64 {
env := 1 - float64(t)/float64(n)
return env * 0.4 * (rng.Float64()*2 - 1)
}}
}
// burst 有限长度合成流。
type burst struct {
n int
pos int
gen func(t int) float64
}
// Stream beep.Streamer:填充采样。
func (b *burst) Stream(samples [][2]float64) (int, bool) {
i := 0
for ; i < len(samples) && b.pos < b.n; i++ {
v := b.gen(b.pos)
samples[i][0] = v
samples[i][1] = v
b.pos++
}
return i, b.pos >= b.n
}
// Err 无错误。
func (b *burst) Err() error { return nil }
// Len 时长采样数。
func (b *burst) Len() int { return b.n }
// Position 当前位置。
func (b *burst) Position() int { return b.pos }
// Seek 跳转。
func (b *burst) Seek(p int) error {
if p < 0 || p > b.n {
p = 0
}
b.pos = p
return nil
}
var _ beep.StreamSeeker = (*burst)(nil)
var _ = time.Second