4343
4444class ComplexF32SampleAdapter : public SampleAdapter {
4545public:
46- size_t sampleSize () override {
47- return sizeof (std::complex <float >);
48- }
46+ size_t sampleSize () override { return sizeof (std::complex <float >); }
47+ size_t sampleAlign () override { return alignof (float ); }
4948
5049 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
5150 auto s = reinterpret_cast <const std::complex <float >*>(src);
@@ -55,9 +54,8 @@ class ComplexF32SampleAdapter : public SampleAdapter {
5554
5655class ComplexF64SampleAdapter : public SampleAdapter {
5756public:
58- size_t sampleSize () override {
59- return sizeof (std::complex <double >);
60- }
57+ size_t sampleSize () override { return sizeof (std::complex <double >); }
58+ size_t sampleAlign () override { return alignof (double ); }
6159
6260 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
6361 auto s = reinterpret_cast <const std::complex <double >*>(src);
@@ -71,9 +69,8 @@ class ComplexF64SampleAdapter : public SampleAdapter {
7169
7270class ComplexS32SampleAdapter : public SampleAdapter {
7371public:
74- size_t sampleSize () override {
75- return sizeof (std::complex <int32_t >);
76- }
72+ size_t sampleSize () override { return sizeof (std::complex <int32_t >); }
73+ size_t sampleAlign () override { return alignof (int32_t ); }
7774
7875 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
7976 auto s = reinterpret_cast <const std::complex <int32_t >*>(src);
@@ -88,9 +85,8 @@ class ComplexS32SampleAdapter : public SampleAdapter {
8885
8986class ComplexS16SampleAdapter : public SampleAdapter {
9087public:
91- size_t sampleSize () override {
92- return sizeof (std::complex <int16_t >);
93- }
88+ size_t sampleSize () override { return sizeof (std::complex <int16_t >); }
89+ size_t sampleAlign () override { return alignof (int16_t ); }
9490
9591 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
9692 auto s = reinterpret_cast <const std::complex <int16_t >*>(src);
@@ -105,9 +101,8 @@ class ComplexS16SampleAdapter : public SampleAdapter {
105101
106102class ComplexS8SampleAdapter : public SampleAdapter {
107103public:
108- size_t sampleSize () override {
109- return sizeof (std::complex <int8_t >);
110- }
104+ size_t sampleSize () override { return sizeof (std::complex <int8_t >); }
105+ size_t sampleAlign () override { return alignof (int8_t ); }
111106
112107 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
113108 auto s = reinterpret_cast <const std::complex <int8_t >*>(src);
@@ -122,9 +117,8 @@ class ComplexS8SampleAdapter : public SampleAdapter {
122117
123118class ComplexU8SampleAdapter : public SampleAdapter {
124119public:
125- size_t sampleSize () override {
126- return sizeof (std::complex <uint8_t >);
127- }
120+ size_t sampleSize () override { return sizeof (std::complex <uint8_t >); }
121+ size_t sampleAlign () override { return alignof (uint8_t ); }
128122
129123 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
130124 auto s = reinterpret_cast <const std::complex <uint8_t >*>(src);
@@ -139,9 +133,8 @@ class ComplexU8SampleAdapter : public SampleAdapter {
139133
140134class RealF32SampleAdapter : public SampleAdapter {
141135public:
142- size_t sampleSize () override {
143- return sizeof (float );
144- }
136+ size_t sampleSize () override { return sizeof (float ); }
137+ size_t sampleAlign () override { return alignof (float ); }
145138
146139 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
147140 auto s = reinterpret_cast <const float *>(src);
@@ -155,9 +148,8 @@ class RealF32SampleAdapter : public SampleAdapter {
155148
156149class RealF64SampleAdapter : public SampleAdapter {
157150public:
158- size_t sampleSize () override {
159- return sizeof (double );
160- }
151+ size_t sampleSize () override { return sizeof (double ); }
152+ size_t sampleAlign () override { return alignof (double ); }
161153
162154 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
163155 auto s = reinterpret_cast <const double *>(src);
@@ -171,9 +163,8 @@ class RealF64SampleAdapter : public SampleAdapter {
171163
172164class RealS16SampleAdapter : public SampleAdapter {
173165public:
174- size_t sampleSize () override {
175- return sizeof (int16_t );
176- }
166+ size_t sampleSize () override { return sizeof (int16_t ); }
167+ size_t sampleAlign () override { return alignof (int16_t ); }
177168
178169 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
179170 auto s = reinterpret_cast <const int16_t *>(src);
@@ -188,9 +179,8 @@ class RealS16SampleAdapter : public SampleAdapter {
188179
189180class RealS8SampleAdapter : public SampleAdapter {
190181public:
191- size_t sampleSize () override {
192- return sizeof (int8_t );
193- }
182+ size_t sampleSize () override { return sizeof (int8_t ); }
183+ size_t sampleAlign () override { return alignof (int8_t ); }
194184
195185 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
196186 auto s = reinterpret_cast <const int8_t *>(src);
@@ -205,9 +195,8 @@ class RealS8SampleAdapter : public SampleAdapter {
205195
206196class RealU8SampleAdapter : public SampleAdapter {
207197public:
208- size_t sampleSize () override {
209- return sizeof (uint8_t );
210- }
198+ size_t sampleSize () override { return sizeof (uint8_t ); }
199+ size_t sampleAlign () override { return alignof (uint8_t ); }
211200
212201 void copyRange (const void * const src, size_t start, size_t length, std::complex <float >* const dest) override {
213202 auto s = reinterpret_cast <const uint8_t *>(src);
@@ -358,11 +347,9 @@ QJsonObject InputSource::readMetaData(const QString &filename)
358347
359348/*
360349 * Parse a RIFF/WAV header from memory-mapped data.
361- * Supports IQ WAV files as produced by SDR++ and similar tools:
362- * - PCM (codec 1): uint8, int16, int32 (2-channel IQ)
363- * - IEEE float (codec 3): float32 (2-channel IQ)
350+ * Codecs: PCM (1, uint8/int16/int32), IEEE float (3, float32),
351+ * EXTENSIBLE (0xFFFE, PCM/float wrapped with a SubFormat GUID).
364352 * Returns the byte offset where sample data begins.
365- * Sets sampleAdapter and sampleRate from the header.
366353 */
367354size_t InputSource::parseWavHeader (const uchar *data, size_t fileSize)
368355{
@@ -406,6 +393,21 @@ size_t InputSource::parseWavHeader(const uchar *data, size_t fileSize)
406393 memcpy (&numChannels, data + pos + 10 , 2 );
407394 memcpy (&wavSampleRate, data + pos + 12 , 4 );
408395 memcpy (&bitsPerSample, data + pos + 22 , 2 );
396+
397+ /* WAVE_FORMAT_EXTENSIBLE: actual codec is the first 4
398+ * bytes of the 16-byte SubFormat GUID at fmt+24 */
399+ if (audioFormat == 0xFFFE ) {
400+ if (chunkSize < 40 )
401+ throw std::runtime_error (" WAV EXTENSIBLE fmt chunk too small" );
402+ uint32_t subFormat;
403+ memcpy (&subFormat, data + pos + 8 + 24 , 4 );
404+ if (subFormat == 1 || subFormat == 3 )
405+ audioFormat = (uint16_t )subFormat;
406+ else
407+ throw std::runtime_error (" WAV: unsupported EXTENSIBLE SubFormat "
408+ + std::to_string (subFormat));
409+ }
410+
409411 foundFmt = true ;
410412 }
411413 else if (memcmp (data + pos, " data" , 4 ) == 0 ) {
@@ -576,22 +578,17 @@ void InputSource::openFile(const char *filename)
576578 throw ;
577579 }
578580
579- /*
580- * Verify (data + dataOffset) alignment matches the adapter's
581- * sample type. mmap'd files are page-aligned, but with a non-zero
582- * dataOffset (e.g., WAV header) the resulting pointer can be
583- * misaligned for complex<float> / int32_t reads. Misaligned typed
584- * access is UB on strict-alignment archs (ARM/SPARC) and a
585- * strict-aliasing violation everywhere. Refuse the file in that
586- * case rather than crash silently downstream.
587- */
581+ /* Refuse files where dataOffset breaks scalar alignment for
582+ * typed reads. Shifting by whole samples can't fix it
583+ * (sampleSize is always a multiple of sampleAlign), and
584+ * shifting by less swaps I<->Q. */
588585 if (sampleAdapter) {
589- const size_t sampleAlign = sampleAdapter->sampleSize ();
586+ const size_t sampleAlign = sampleAdapter->sampleAlign ();
590587 if (sampleAlign > 1 &&
591588 (reinterpret_cast <uintptr_t >(data + dataOffset) % sampleAlign) != 0 ) {
592589 file->unmap (data);
593590 throw std::runtime_error (
594- " File data offset is not aligned to sample size; "
591+ " File data offset is not aligned to scalar size; "
595592 " cannot mmap-access. Convert the file or remove the header." );
596593 }
597594 }
0 commit comments