52 lines
1.5 KiB
Go
52 lines
1.5 KiB
Go
package audio
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// ParsePCM memvalidasi file WAV (RIFF) dan mengembalikan isi chunk "data" (PCM).
|
|
// Standar aset tia (PRD Appendix C): 44.1 kHz, mono, 16-bit — selain itu ditolak
|
|
// agar kesalahan aset ketahuan saat dev, bukan saat runtime.
|
|
func ParsePCM(b []byte) ([]byte, error) {
|
|
if len(b) < 44 || string(b[0:4]) != "RIFF" || string(b[8:12]) != "WAVE" {
|
|
return nil, errors.New("bukan file RIFF/WAVE")
|
|
}
|
|
var (
|
|
format, channels, bits, rate int
|
|
data []byte
|
|
)
|
|
for off := 12; off+8 <= len(b); {
|
|
id := string(b[off : off+4])
|
|
size := int(binary.LittleEndian.Uint32(b[off+4 : off+8]))
|
|
off += 8
|
|
if size < 0 || off+size > len(b) {
|
|
return nil, fmt.Errorf("chunk %q terpotong", id)
|
|
}
|
|
switch id {
|
|
case "fmt ":
|
|
if size < 16 {
|
|
return nil, errors.New("chunk fmt terlalu pendek")
|
|
}
|
|
format = int(binary.LittleEndian.Uint16(b[off : off+2]))
|
|
channels = int(binary.LittleEndian.Uint16(b[off+2 : off+4]))
|
|
rate = int(binary.LittleEndian.Uint32(b[off+4 : off+8]))
|
|
bits = int(binary.LittleEndian.Uint16(b[off+14 : off+16]))
|
|
case "data":
|
|
data = b[off : off+size]
|
|
}
|
|
off += size + size%2 // chunk berukuran ganjil dipad 1 byte
|
|
}
|
|
if format != 1 {
|
|
return nil, fmt.Errorf("format %d bukan PCM", format)
|
|
}
|
|
if channels != 1 || bits != 16 || rate != 44100 {
|
|
return nil, fmt.Errorf("hanya mono/16-bit/44100Hz didukung (dapat %dch/%dbit/%dHz)", channels, bits, rate)
|
|
}
|
|
if len(data) == 0 {
|
|
return nil, errors.New("chunk data kosong")
|
|
}
|
|
return data, nil
|
|
}
|