|
- package audio
-
- import (
- "encoding/binary"
- "fmt"
- "io"
- "os"
- )
-
- type WAVSource struct {
- frames []Frame
- index int
- SampleRate int
- Channels int
- }
-
- func LoadWAVSource(path string) (*WAVSource, error) {
- f, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer f.Close()
-
- header := make([]byte, 44)
- if _, err := io.ReadFull(f, header); err != nil {
- return nil, fmt.Errorf("read wav header: %w", err)
- }
- if string(header[0:4]) != "RIFF" || string(header[8:12]) != "WAVE" {
- return nil, fmt.Errorf("unsupported wav header")
- }
-
- audioFormat := binary.LittleEndian.Uint16(header[20:22])
- channels := binary.LittleEndian.Uint16(header[22:24])
- sampleRate := binary.LittleEndian.Uint32(header[24:28])
- bitsPerSample := binary.LittleEndian.Uint16(header[34:36])
- dataSize := binary.LittleEndian.Uint32(header[40:44])
-
- if audioFormat != 1 {
- return nil, fmt.Errorf("only PCM wav supported")
- }
- if bitsPerSample != 16 {
- return nil, fmt.Errorf("only 16-bit PCM wav supported")
- }
- if channels != 1 && channels != 2 {
- return nil, fmt.Errorf("only mono/stereo wav supported")
- }
- if sampleRate == 0 {
- return nil, fmt.Errorf("invalid wav sample rate")
- }
-
- raw := make([]byte, dataSize)
- if _, err := io.ReadFull(f, raw); err != nil {
- return nil, fmt.Errorf("read wav data: %w", err)
- }
-
- step := int(channels) * 2
- frames := make([]Frame, 0, len(raw)/step)
- for i := 0; i+step <= len(raw); i += step {
- l := pcm16ToSample(int16(binary.LittleEndian.Uint16(raw[i : i+2])))
- r := l
- if channels == 2 {
- r = pcm16ToSample(int16(binary.LittleEndian.Uint16(raw[i+2 : i+4])))
- }
- frames = append(frames, NewFrame(l, r))
- }
-
- return &WAVSource{
- frames: frames,
- SampleRate: int(sampleRate),
- Channels: int(channels),
- }, nil
- }
-
- func (s *WAVSource) NextFrame() Frame {
- if len(s.frames) == 0 {
- return NewFrame(0, 0)
- }
- frame := s.frames[s.index]
- s.index++
- if s.index >= len(s.frames) {
- s.index = 0
- }
- return frame
- }
-
- func pcm16ToSample(v int16) Sample {
- return Sample(float64(v) / 32768.0).Clamp()
- }
|