wav.go (4040B)
1 // Package wav implements access to WAV (or WAVE) files. 2 // WAVE is a file format for storing digitised audio, 3 // most commonly as raw PCM signals. 4 // 5 // See also https://en.wikipedia.org/wiki/WAV 6 package wav 7 8 import ( 9 "bytes" 10 "encoding/binary" 11 "fmt" 12 "io" 13 ) 14 15 type File struct { 16 Header Header 17 Bitstream io.Reader 18 } 19 20 const ( 21 headerLength = 44 22 extensionLength = 24 23 ) 24 25 const ( 26 AudioFormatPCMInteger uint16 = 1 27 AudioFormatFloat uint16 = 3 28 AudioFormatExtensible uint16 = 0xfffe 29 ) 30 31 var ( 32 riffID = [4]byte{'R', 'I', 'F', 'F'} 33 waveID = [4]byte{'W', 'A', 'V', 'E'} 34 ) 35 36 var formatChunkID = [4]byte{'f', 'm', 't', ' '} 37 38 var dataChunkID = [4]byte{'d', 'a', 't', 'a'} 39 40 type Header struct { 41 FileSize uint32 42 43 AudioFormat uint16 44 ChannelCount uint16 45 Frequency uint32 46 BytesPerSecond uint32 47 BytesPerBloc uint16 48 BitsPerSample uint16 49 Extension *FormatExtension 50 51 DataSize uint32 52 } 53 54 func ReadFile(r io.Reader) (*File, error) { 55 h, err := readHeader(r) 56 if err != nil { 57 return nil, fmt.Errorf("read header: %w", err) 58 } 59 return &File{ 60 Header: Header{ 61 h.File.Length, 62 h.Format.AudioFormat, 63 h.Format.ChannelCount, 64 h.Format.Frequency, 65 h.Format.BytesPerSecond, 66 h.Format.BytesPerBloc, 67 h.Format.BitsPerSample, 68 h.FormatExtension, 69 h.Data.Length, 70 }, 71 Bitstream: r, 72 }, nil 73 } 74 75 func EncodeHeader(hdr Header) []byte { 76 h := header{ 77 File: fileChunk{riffID, hdr.FileSize, waveID}, 78 Format: formatChunk{ 79 formatChunkID, 80 formatChunkLength, 81 hdr.AudioFormat, 82 hdr.ChannelCount, 83 hdr.Frequency, 84 hdr.BytesPerSecond, 85 hdr.BytesPerBloc, 86 hdr.BitsPerSample, 87 }, 88 FormatExtension: hdr.Extension, 89 Data: dataChunk{dataChunkID, hdr.DataSize}, 90 } 91 if h.Format.AudioFormat == AudioFormatExtensible { 92 h.Format.Length = extendedFormatChunkLength 93 } 94 95 bcap := headerLength 96 if h.Format.AudioFormat == AudioFormatExtensible { 97 bcap += extensionLength 98 } 99 buf := bytes.NewBuffer(make([]byte, 0, bcap)) 100 binary.Write(buf, binary.LittleEndian, h.File) 101 binary.Write(buf, binary.LittleEndian, h.Format) 102 if h.FormatExtension != nil { 103 binary.Write(buf, binary.LittleEndian, h.FormatExtension) 104 } 105 binary.Write(buf, binary.LittleEndian, h.Data) 106 return buf.Bytes() 107 } 108 109 type header struct { 110 File fileChunk 111 Format formatChunk 112 // Extension holds optional extra audio format information for 113 FormatExtension *FormatExtension 114 Data dataChunk 115 } 116 117 type fileChunk struct { 118 ID [4]byte 119 Length uint32 120 FormatID [4]byte 121 } 122 123 const ( 124 formatChunkLength = 16 125 extendedFormatChunkLength = formatChunkLength + extensionLength 126 ) 127 128 type formatChunk struct { 129 ID [4]byte 130 Length uint32 131 AudioFormat uint16 132 ChannelCount uint16 133 Frequency uint32 134 BytesPerSecond uint32 135 BytesPerBloc uint16 136 BitsPerSample uint16 137 } 138 139 type dataChunk struct { 140 ID [4]byte 141 Length uint32 142 } 143 144 type FormatExtension struct { 145 Length uint16 146 ValidBits uint16 147 ChannelMask uint32 148 SubFormat [16]byte 149 } 150 151 func readHeader(rd io.Reader) (*header, error) { 152 var head header 153 if err := binary.Read(rd, binary.LittleEndian, &head.File); err != nil { 154 return nil, fmt.Errorf("read file chunk: %w", err) 155 } 156 if head.File.ID != riffID { 157 return nil, fmt.Errorf("bad RIFF id %x", head.File.ID) 158 } else if head.File.FormatID != waveID { 159 return nil, fmt.Errorf("bad WAVE file format id %x", head.File.FormatID) 160 } 161 162 if err := binary.Read(rd, binary.LittleEndian, &head.Format); err != nil { 163 return nil, fmt.Errorf("read file chunk: %w", err) 164 } 165 if head.Format.ID != formatChunkID { 166 return nil, fmt.Errorf("bad format chunk id %x", head.Format.ID) 167 } 168 169 if head.Format.AudioFormat == AudioFormatExtensible { 170 var ext FormatExtension 171 if err := binary.Read(rd, binary.LittleEndian, &ext); err != nil { 172 return nil, fmt.Errorf("read format chunk extension: %w", err) 173 } 174 head.FormatExtension = &ext 175 } 176 177 if err := binary.Read(rd, binary.LittleEndian, &head.Data); err != nil { 178 return nil, fmt.Errorf("read data chunk: %w", err) 179 } 180 return &head, nil 181 }