package audio import ( "bytes" "encoding/binary" "testing" ) func TestAudioDuration(t *testing.T) { t.Parallel() // 16k mono int16 ⇒ 2 bytes/sample ⇒ 32000 bytes/second. a := Audio{Format: PCM16kMono, Bytes: make([]byte, 32000)} if got, want := a.Duration(), 1.0; got != want { t.Fatalf("Duration: got %v, want %v", got, want) } // 4 seconds a.Bytes = make([]byte, 128000) if got, want := a.Duration(), 4.0; got != want { t.Fatalf("Duration: got %v, want %v", got, want) } } func TestAudioDurationEmptyAndBad(t *testing.T) { t.Parallel() if (Audio{}).Duration() != 0 { t.Fatalf("empty audio: Duration should be 0") } a := Audio{Format: Format{}, Bytes: make([]byte, 32000)} if a.Duration() != 0 { t.Fatalf("zero format: Duration should be 0") } a = Audio{Format: Format{SampleRate: 16000, Channels: 1, SampleBits: 16, Encoding: "opus"}, Bytes: make([]byte, 32000)} if a.Duration() != 0 { t.Fatalf("unsupported encoding: Duration should be 0") } } func TestFormatIsValid(t *testing.T) { t.Parallel() if !PCM16kMono.IsValid() { t.Fatalf("PCM16kMono should be valid") } if (Format{SampleRate: 8000, Channels: 1, SampleBits: 16, Encoding: "pcm_s16le"}).IsValid() { t.Fatalf("8k should be rejected") } if (Format{SampleRate: 16000, Channels: 2, SampleBits: 16, Encoding: "pcm_s16le"}).IsValid() { t.Fatalf("stereo should be rejected") } } func TestWAVRoundTrip(t *testing.T) { t.Parallel() // 0.5s of square wave (alternating samples) — sndfile/aplay can play it. const nsamp = 8000 pcm := make([]byte, nsamp*2) for i := 0; i < nsamp; i++ { var v int16 = -16384 if i%2 == 0 { v = 16384 } binary.LittleEndian.PutUint16(pcm[i*2:], uint16(v)) } wav, err := WAVFromPCM(PCM16kMono, pcm) if err != nil { t.Fatalf("WAVFromPCM: %v", err) } if len(wav) != 44+len(pcm) { t.Fatalf("wav length: got %d, want %d", len(wav), 44+len(pcm)) } if string(wav[0:4]) != "RIFF" || string(wav[8:12]) != "WAVE" { t.Fatalf("missing RIFF/WAVE marker: %q", wav[0:12]) } f, pcm2, err := PCMFromWAV(wav) if err != nil { t.Fatalf("PCMFromWAV: %v", err) } if !f.IsValid() { t.Fatalf("parsed format invalid: %+v", f) } if !bytes.Equal(pcm, pcm2) { t.Fatalf("PCM mismatch after round-trip: in=%d bytes, out=%d bytes", len(pcm), len(pcm2)) } } func TestPCMFromWAVRejectsNonCanonical(t *testing.T) { t.Parallel() // too short if _, _, err := PCMFromWAV([]byte("RIFF")); err == nil { t.Fatalf("short input should error") } // bad RIFF marker bad := make([]byte, 44) copy(bad[0:4], []byte("RIFF")) copy(bad[8:12], []byte("XXXX")) if _, _, err := PCMFromWAV(bad); err == nil { t.Fatalf("non-WAVE marker should error") } // format code 3 (float), canonical otherwise wav, _ := WAVFromPCM(PCM16kMono, []byte{0, 0}) binary.LittleEndian.PutUint16(wav[20:22], 3) // IEEE float, not PCM if _, _, err := PCMFromWAV(wav); err == nil { t.Fatalf("non-PCM format should error") } }