added implementation in GO lang
This commit is contained in:
130
internal/audio/manager.go
Normal file
130
internal/audio/manager.go
Normal file
@ -0,0 +1,130 @@
|
||||
package audio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
ebiaudio "github.com/hajimehoshi/ebiten/v2/audio"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio/wav"
|
||||
)
|
||||
|
||||
const sampleRate = 44100
|
||||
|
||||
type Manager struct {
|
||||
context *ebiaudio.Context
|
||||
players []*ebiaudio.Player
|
||||
current *ebiaudio.Player
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
return &Manager{context: ebiaudio.NewContext(sampleRate)}
|
||||
}
|
||||
|
||||
func (m *Manager) Update() {
|
||||
alive := m.players[:0]
|
||||
for _, p := range m.players {
|
||||
if p.IsPlaying() {
|
||||
alive = append(alive, p)
|
||||
continue
|
||||
}
|
||||
_ = p.Close()
|
||||
}
|
||||
m.players = alive
|
||||
if m.current != nil && !m.current.IsPlaying() {
|
||||
_ = m.current.Close()
|
||||
m.current = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) PlayTone(freq float64, d time.Duration, volume float64) {
|
||||
m.playPCM(generateTone(freq, d, volume), false)
|
||||
}
|
||||
|
||||
func (m *Manager) PlayToneInterrupt(freq float64, d time.Duration, volume float64) {
|
||||
m.playPCM(generateTone(freq, d, volume), true)
|
||||
}
|
||||
|
||||
func (m *Manager) PlayBeep() {
|
||||
m.PlayTone(880, 80*time.Millisecond, 0.22)
|
||||
}
|
||||
|
||||
func (m *Manager) PlayError() {
|
||||
data := append(generateTone(160, 110*time.Millisecond, 0.28), generateTone(110, 150*time.Millisecond, 0.28)...)
|
||||
m.playPCM(data, false)
|
||||
}
|
||||
|
||||
func (m *Manager) PlayWAV(path string, interrupt bool) bool {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
stream, err := wav.DecodeWithSampleRate(sampleRate, f)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
data, err := io.ReadAll(stream)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
m.playPCM(data, interrupt)
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *Manager) StopCurrent() {
|
||||
if m.current == nil {
|
||||
return
|
||||
}
|
||||
m.current.Pause()
|
||||
_ = m.current.Close()
|
||||
m.current = nil
|
||||
}
|
||||
|
||||
func (m *Manager) playPCM(data []byte, interrupt bool) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
if interrupt {
|
||||
m.StopCurrent()
|
||||
}
|
||||
player := m.context.NewPlayerFromBytes(data)
|
||||
player.Play()
|
||||
if interrupt {
|
||||
m.current = player
|
||||
return
|
||||
}
|
||||
m.players = append(m.players, player)
|
||||
}
|
||||
|
||||
func generateTone(freq float64, d time.Duration, volume float64) []byte {
|
||||
samples := int(float64(sampleRate) * d.Seconds())
|
||||
buf := bytes.NewBuffer(make([]byte, 0, samples*4))
|
||||
for i := 0; i < samples; i++ {
|
||||
t := float64(i) / sampleRate
|
||||
env := envelope(i, samples)
|
||||
v := int16(math.Sin(2*math.Pi*freq*t) * volume * env * math.MaxInt16)
|
||||
_ = binary.Write(buf, binary.LittleEndian, v)
|
||||
_ = binary.Write(buf, binary.LittleEndian, v)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func envelope(i, samples int) float64 {
|
||||
if samples <= 0 {
|
||||
return 0
|
||||
}
|
||||
attack := sampleRate / 200
|
||||
release := sampleRate / 100
|
||||
if i < attack {
|
||||
return float64(i) / float64(attack)
|
||||
}
|
||||
if samples-i < release {
|
||||
return float64(samples-i) / float64(release)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
Reference in New Issue
Block a user