|
- package audio
-
- import (
- "encoding/binary"
- "os"
- "path/filepath"
- "testing"
- )
-
- func TestPCM16ToSample(t *testing.T) {
- if pcm16ToSample(32767) <= 0 {
- t.Fatal("expected positive sample")
- }
- if pcm16ToSample(-32768) < -1.0 {
- t.Fatal("expected clamped lower bound")
- }
- }
-
- func TestLoadWAVSource(t *testing.T) {
- dir := t.TempDir()
- path := filepath.Join(dir, "test.wav")
- wav := buildMinimalWAV(48000, 1, []int16{0, 32767, -32768, 0})
- if err := os.WriteFile(path, wav, 0o644); err != nil {
- t.Fatalf("write wav: %v", err)
- }
- src, err := LoadWAVSource(path)
- if err != nil {
- t.Fatalf("LoadWAVSource failed: %v", err)
- }
- if src.SampleRate != 48000 {
- t.Fatalf("unexpected sample rate: %d", src.SampleRate)
- }
- if src.Channels != 1 {
- t.Fatalf("unexpected channels: %d", src.Channels)
- }
- _ = src.NextFrame()
- }
-
- func TestLoadWAVWithExtraChunks(t *testing.T) {
- dir := t.TempDir()
- path := filepath.Join(dir, "extra.wav")
-
- // Build a WAV with a LIST chunk between fmt and data
- wav := buildWAVWithExtraChunks(44100, 1, []int16{100, -100})
- if err := os.WriteFile(path, wav, 0o644); err != nil {
- t.Fatalf("write wav: %v", err)
- }
- src, err := LoadWAVSource(path)
- if err != nil {
- t.Fatalf("LoadWAVSource with extra chunks failed: %v", err)
- }
- if src.SampleRate != 44100 {
- t.Fatalf("unexpected sample rate: %d", src.SampleRate)
- }
- if len(src.frames) != 2 {
- t.Fatalf("expected 2 frames, got %d", len(src.frames))
- }
- }
-
- func TestRejectInvalidWAV(t *testing.T) {
- dir := t.TempDir()
- path := filepath.Join(dir, "bad.wav")
- if err := os.WriteFile(path, []byte("nope"), 0o644); err != nil {
- t.Fatalf("write wav: %v", err)
- }
- if _, err := LoadWAVSource(path); err == nil {
- t.Fatal("expected wav load error")
- }
- }
-
- func TestStereoWAV(t *testing.T) {
- dir := t.TempDir()
- path := filepath.Join(dir, "stereo.wav")
- wav := buildMinimalWAV(48000, 2, []int16{1000, -1000, 2000, -2000})
- if err := os.WriteFile(path, wav, 0o644); err != nil {
- t.Fatalf("write wav: %v", err)
- }
- src, err := LoadWAVSource(path)
- if err != nil {
- t.Fatalf("LoadWAVSource stereo failed: %v", err)
- }
- if src.Channels != 2 {
- t.Fatalf("expected 2 channels, got %d", src.Channels)
- }
- if len(src.frames) != 2 {
- t.Fatalf("expected 2 frames, got %d", len(src.frames))
- }
- }
-
- // -- helpers --
-
- func buildMinimalWAV(sampleRate int, channels int, samples []int16) []byte {
- dataSize := len(samples) * 2
- fileSize := 36 + dataSize
-
- buf := make([]byte, 0, 44+dataSize)
- buf = append(buf, []byte("RIFF")...)
- buf = binary.LittleEndian.AppendUint32(buf, uint32(fileSize))
- buf = append(buf, []byte("WAVE")...)
-
- // fmt chunk
- buf = append(buf, []byte("fmt ")...)
- buf = binary.LittleEndian.AppendUint32(buf, 16)
- buf = binary.LittleEndian.AppendUint16(buf, 1) // PCM
- buf = binary.LittleEndian.AppendUint16(buf, uint16(channels))
- buf = binary.LittleEndian.AppendUint32(buf, uint32(sampleRate))
- buf = binary.LittleEndian.AppendUint32(buf, uint32(sampleRate*channels*2)) // byte rate
- buf = binary.LittleEndian.AppendUint16(buf, uint16(channels*2)) // block align
- buf = binary.LittleEndian.AppendUint16(buf, 16) // bits per sample
-
- // data chunk
- buf = append(buf, []byte("data")...)
- buf = binary.LittleEndian.AppendUint32(buf, uint32(dataSize))
- for _, s := range samples {
- buf = binary.LittleEndian.AppendUint16(buf, uint16(s))
- }
- return buf
- }
-
- func buildWAVWithExtraChunks(sampleRate int, channels int, samples []int16) []byte {
- dataSize := len(samples) * 2
- // Add a fake LIST chunk of 12 bytes between fmt and data
- listChunkData := []byte("INFOtest") // 8 bytes
- listChunkSize := uint32(len(listChunkData))
- totalExtraChunk := 8 + len(listChunkData) // "LIST" + size + data
- fileSize := 36 + totalExtraChunk + dataSize
-
- buf := make([]byte, 0, 44+totalExtraChunk+dataSize)
- buf = append(buf, []byte("RIFF")...)
- buf = binary.LittleEndian.AppendUint32(buf, uint32(fileSize))
- buf = append(buf, []byte("WAVE")...)
-
- // fmt chunk
- buf = append(buf, []byte("fmt ")...)
- buf = binary.LittleEndian.AppendUint32(buf, 16)
- buf = binary.LittleEndian.AppendUint16(buf, 1)
- buf = binary.LittleEndian.AppendUint16(buf, uint16(channels))
- buf = binary.LittleEndian.AppendUint32(buf, uint32(sampleRate))
- buf = binary.LittleEndian.AppendUint32(buf, uint32(sampleRate*channels*2))
- buf = binary.LittleEndian.AppendUint16(buf, uint16(channels*2))
- buf = binary.LittleEndian.AppendUint16(buf, 16)
-
- // LIST chunk (extra, should be skipped)
- buf = append(buf, []byte("LIST")...)
- buf = binary.LittleEndian.AppendUint32(buf, listChunkSize)
- buf = append(buf, listChunkData...)
-
- // data chunk
- buf = append(buf, []byte("data")...)
- buf = binary.LittleEndian.AppendUint32(buf, uint32(dataSize))
- for _, s := range samples {
- buf = binary.LittleEndian.AppendUint16(buf, uint16(s))
- }
- return buf
- }
|