Commit af8cb75
authored
Accept PCM8/32, float64, A-law, mu-law and WAVEFORMATEXTENSIBLE in the WAV reader (#319)
* feat(audio): accept PCM8/32, float64, A-law, mu-law and WAVEFORMATEXTENSIBLE
The reader handled PCM16, PCM24 and float32. Everything else -- including
ordinary PCM16 that happens to be tagged 0xFFFE -- came back as "unsupported WAV
encoding (need PCM16, PCM24, or float32)", which is confusing when PCM16 is
exactly what is inside. Encoders emit WAVEFORMATEXTENSIBLE routinely for more
than two channels or whenever a channel mask is set, and the real format tag
then lives in the SubFormat GUID rather than in wFormatTag.
Adds PCM8 (unsigned, biased by 128), PCM32, float64, G.711 A-law and mu-law, and
unwraps WAVEFORMATEXTENSIBLE to whatever its GUID names. Files that still cannot
be decoded now say what they are: a FLAC, Ogg, MP3, MP4, AIFF, RF64 or CAF given
to the reader is named as such instead of failing with "invalid WAV RIFF header".
Tests cover each added format. The G.711 cases are pinned to the published
decode values (mu-law 0x00 -> -32124, A-law 0x2A -> +32256, and A-law's absence
of an exact zero) rather than to a re-derivation of the same bit manipulation,
which would prove nothing. Two negative cases check that widening the accepted
set did not turn into accepting everything: ADPCM is still rejected, and a FLAC
is still identified as a FLAC.
Verified by execution: the new cases fail against the previous reader with the
old message and pass against this one; wav_reader_chunk_bounds_test still
passes.
This lands separately from #180 at @0xShug0's request. The decoders originate
from @dignome's contributed tree, where they existed so the CLI and server path
would accept the same files the web UI already did.
* fix(audio): correct the A-law sign and validate extensible headers
Four defects found by review before merge.
**A-law polarity was inverted on all 256 codes.** G.711 sets the sign bit for
*positive* samples in A-law and for *negative* ones in mu-law; this treated both
the same way. Decoded audio came out at the right amplitude, phase-inverted --
which is inaudible on its own and survives every spot check.
The tests did not catch it because they were circular: four "published" anchors
whose expected values had been worked out from the same shift-and-bias
arithmetic under test, so they confirmed the bug rather than finding it. They are
replaced with the full 256-entry decode tables for both codings, taken from
outside this codebase -- ffmpeg 9.0.1 decoding a 256-byte file, cross-checked
against the values implied by the ITU-T G.711 segment definitions. Reinstating
the old sign now fails on `A-law code 0`.
**The extensible SubFormat GUID was trusted on its first two bytes.** Only those
carry the format tag; the remaining fourteen are a fixed suffix shared by every
KSDATAFORMAT_SUBTYPE_*. Without checking them, an unrelated codec whose GUID
merely starts 0x0001 decoded as PCM16. Now compared, and cbSize is required to
be at least 22 as the structure demands. A sub-40-byte extensible fmt chunk gets
a clear error instead of falling through with a stale format tag.
**PCM32 and float64 silently dropped a trailing partial sample.** Integer
division trimmed it, so a truncated download decoded as valid audio. Both now
reject it, matching what PCM24 already did. PCM16 and float32 keep their existing
behaviour -- tightening those is not this PR's business.
Verified: full ctest green, an A-law file transcoded by ffmpeg works as a CLI
--voice-ref end to end, and each new negative case fails against the code as it
stood before this commit.
* fix(audio): stop the header sniff and the RIFF pad from breaking valid files
Two defects a second reviewer found in the parse loop, both introduced by the
first commit in this PR, and both invisible to tests that only ever read from a
file path.
**The 12-byte header sniff rewound absolutely.** After reading the RIFF header
for container identification it did `seekg(header_read, beg)`, which is
redundant for a stream that started at offset 0, wrong for one that did not, and
impossible for one that cannot seek. `read_wav_f32(std::istream &)` is public;
handed a pipe it set failbit and reported `incomplete WAV file` for a perfectly
good WAV. The bytes are already consumed, so the rewind is simply removed.
**The RIFF pad byte was required at EOF.** After an odd-sized chunk the reader
always seeks one byte. Seeking past the end is legal on an ifstream but not on
the in-memory buffer behind the string_view overload, so the same bytes parsed
from disk and threw from an upload. Plenty of writers omit the final pad, and
this PR is what makes it matter: PCM8, A-law and mu-law are one byte per sample,
so odd data chunks go from rare to routine. A pad byte carries no data, so a
missing one at EOF now ends the chunk loop instead of failing.
Also rejects a `fmt ` chunk shorter than 16 bytes, which previously read on into
whatever followed.
The PCM8, PCM32 and float64 expectations were still derived from the
implementation, the same construction that hid the A-law inversion. They are now
frozen from `ffmpeg -f f32le` output and extended to the endpoints that
distinguish a correct conversion from a plausible one: INT32_MAX, which float32
rounding maps to exactly 1.0, and a float64 value not representable in float32.
Verified: reinstating any of the three defects fails the suite with the matching
message; full ctest green; and our decode of a real ffmpeg-transcoded A-law file
matches ffmpeg on all 153280 samples with zero mismatches.1 parent 3ce15a6 commit af8cb75
2 files changed
Lines changed: 643 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
| 6 | + | |
4 | 7 | | |
5 | 8 | | |
6 | 9 | | |
| |||
63 | 66 | | |
64 | 67 | | |
65 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
66 | 163 | | |
67 | 164 | | |
68 | 165 | | |
69 | 166 | | |
70 | 167 | | |
71 | 168 | | |
72 | 169 | | |
73 | | - | |
74 | | - | |
75 | | - | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
76 | 188 | | |
77 | 189 | | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | 190 | | |
85 | 191 | | |
86 | 192 | | |
| |||
97 | 203 | | |
98 | 204 | | |
99 | 205 | | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
100 | 211 | | |
101 | 212 | | |
102 | 213 | | |
103 | 214 | | |
104 | 215 | | |
105 | | - | |
106 | | - | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
107 | 254 | | |
108 | 255 | | |
109 | 256 | | |
| |||
131 | 278 | | |
132 | 279 | | |
133 | 280 | | |
134 | | - | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
135 | 291 | | |
136 | 292 | | |
137 | 293 | | |
| |||
143 | 299 | | |
144 | 300 | | |
145 | 301 | | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
146 | 356 | | |
147 | 357 | | |
148 | 358 | | |
| |||
184 | 394 | | |
185 | 395 | | |
186 | 396 | | |
187 | | - | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
188 | 401 | | |
189 | 402 | | |
190 | 403 | | |
| |||
0 commit comments