feat(audio): 音频管理器——3D 衰减、声源池淘汰、分类音量、程序化合成音(beep+oto 纯 Go)
This commit is contained in:
69
internal/audio/synth.go
Normal file
69
internal/audio/synth.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// 程序化音效合成:正弦脉冲与噪声脉冲(无音频文件的占位实现,音频.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
|
||||
Reference in New Issue
Block a user