package audio import ( "encoding/binary" "fmt" "io" "os" ) type WAVSource struct { frames []Frame index 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") } channels := binary.LittleEndian.Uint16(header[22:24]) bitsPerSample := binary.LittleEndian.Uint16(header[34:36]) dataSize := binary.LittleEndian.Uint32(header[40:44]) 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") } 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}, 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() }