Audacity 3.2.0
Macros | Functions
RawAudioGuess.cpp File Reference
#include "RawAudioGuess.h"
#include "AudacityException.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wx/defs.h>
#include <wx/ffile.h>
Include dependency graph for RawAudioGuess.cpp:

Go to the source code of this file.

Macros

#define RAW_GUESS_DEBUG   0
 

Functions

static float AmpStat (float *data, size_t len)
 
static float JumpStat (float *data, size_t len)
 
static float SecondDStat (float *data, size_t len)
 
static float RedundantStereo (float *data, size_t len)
 
static void ExtractFloats (bool doublePrec, bool bigendian, bool stereo, size_t offset, char *rawData, size_t dataSize, float *data1, float *data2, size_t *len1, size_t *len2)
 
static void Extract (bool bits16, bool sign, bool stereo, bool bigendian, bool offset, char *rawData, int dataSizeIn, float *data1, float *data2, size_t *len1, size_t *len2)
 
static int GuessFloatFormats (unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, size_t *out_offset, unsigned *out_channels)
 
static int Guess8Bit (unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, unsigned *out_channels)
 
static int Guess16Bit (unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, bool evenMSB, size_t *out_offset, unsigned *out_channels)
 
static int GuessIntFormats (unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, size_t *out_offset, unsigned *out_channels)
 
int RawAudioGuess (const wxString &in_fname, size_t *out_offset, unsigned *out_channels)
 

Macro Definition Documentation

◆ RAW_GUESS_DEBUG

#define RAW_GUESS_DEBUG   0

Definition at line 28 of file RawAudioGuess.cpp.

Function Documentation

◆ AmpStat()

static float AmpStat ( float *  data,
size_t  len 
)
static

Definition at line 34 of file RawAudioGuess.cpp.

35{
36 float sum, sumofsquares, avg, variance, dev;
37
38 if (len == 0)
39 return 1.0;
40
41 /* Calculate standard deviation of the amplitudes */
42
43 sum = 0.0;
44 sumofsquares = 0.0;
45
46 for (size_t i = 0; i < len; i++) {
47 float x = fabs(data[i]);
48 sum += x;
49 sumofsquares += x * x;
50 }
51
52 avg = sum / len;
53 variance = sumofsquares / len - (avg * avg);
54
55 dev = sqrt(variance);
56
57 return dev;
58}
__finl float_x4 __vecc sqrt(const float_x4 &a)

References staffpad::audio::simd::sqrt().

Referenced by GuessIntFormats().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Extract()

static void Extract ( bool  bits16,
bool  sign,
bool  stereo,
bool  bigendian,
bool  offset,
char *  rawData,
int  dataSizeIn,
float *  data1,
float *  data2,
size_t *  len1,
size_t *  len2 
)
static

Definition at line 183 of file RawAudioGuess.cpp.

190{
191 size_t rawCount = 0;
192 size_t dataCount1 = 0;
193 size_t dataCount2 = 0;
194
195 *len1 = 0;
196 *len2 = 0;
197
198 if (offset && bits16) {
199 /* Special case so as to not flip stereo channels during analysis */
200 if (stereo && !bigendian) {
201 rawData += 3;
202 dataSizeIn -= 3;
203 }
204 else {
205 rawData++;
206 dataSizeIn--;
207 }
208 }
209
210 if( dataSizeIn < 1 )
213 XO("Bad data size. Could not import audio"),
214 XO("Warning"),
215 "Error:_Importing_raw_audio"
216 };
217
218 size_t dataSize = (size_t)dataSizeIn;
219
220 if (bits16) {
221 if (sign && bigendian)
222 while (rawCount + 1 < dataSize) {
223 /* 16-bit signed BE */
224 data1[dataCount1] =
225 (wxINT16_SWAP_ON_LE(*((signed short *)
226 &rawData[rawCount])))
227 / 32768.0;
228 dataCount1++;
229 rawCount += 2;
230 }
231 if (!sign && bigendian)
232 while (rawCount + 1 < dataSize) {
233 /* 16-bit unsigned BE */
234 data1[dataCount1] =
235 (wxUINT16_SWAP_ON_LE(*((unsigned short *)
236 &rawData[rawCount])))
237 / 32768.0 - 1.0;
238 dataCount1++;
239 rawCount += 2;
240 }
241 if (sign && !bigendian)
242 while (rawCount + 1 < dataSize) {
243 /* 16-bit signed LE */
244 data1[dataCount1] =
245 (wxINT16_SWAP_ON_BE(*((signed short *)
246 &rawData[rawCount])))
247 / 32768.0;
248 dataCount1++;
249 rawCount += 2;
250 }
251 if (!sign && !bigendian)
252 while (rawCount + 1 < dataSize) {
253 /* 16-bit unsigned LE */
254 data1[dataCount1] =
255 (wxUINT16_SWAP_ON_BE(*((unsigned short *)
256 &rawData[rawCount])))
257 / 32768.0 - 1.0;
258 dataCount1++;
259 rawCount += 2;
260 }
261 }
262 else {
263 /* 8-bit */
264 if (sign) {
265 while (rawCount < dataSize) {
266 /* 8-bit signed */
267 data1[dataCount1++] =
268 (*(signed char *) (&rawData[rawCount++])) / 128.0;
269 }
270 }
271 else {
272 while (rawCount < dataSize) {
273 /* 8-bit unsigned */
274 data1[dataCount1++] =
275 (*(unsigned char *) &rawData[rawCount++]) / 128.0 - 1.0;
276 }
277 }
278 }
279
280 if (stereo) {
281 dataCount1 /= 2;
282 for(size_t i = 0; i < dataCount1; i++) {
283 data2[i] = data1[2*i+1];
284 data1[i] = data1[2*i];
285 }
286 dataCount2 = dataCount1;
287 }
288
289 *len1 = dataCount1;
290 *len2 = dataCount2;
291}
@ BadUserAction
Indicates that the user performed an action that is not allowed.
XO("Cut/Copy/Paste")
A MessageBoxException that shows a given, unvarying string.

References BadUserAction, and XO().

Referenced by Guess16Bit(), Guess8Bit(), and GuessIntFormats().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ExtractFloats()

static void ExtractFloats ( bool  doublePrec,
bool  bigendian,
bool  stereo,
size_t  offset,
char *  rawData,
size_t  dataSize,
float *  data1,
float *  data2,
size_t *  len1,
size_t *  len2 
)
static

Definition at line 105 of file RawAudioGuess.cpp.

111{
112 size_t rawCount = 0;
113 size_t dataCount1 = 0;
114 size_t dataCount2 = 0;
115 bool swap;
116
117 *len1 = 0;
118 *len2 = 0;
119
120 if (offset) {
121 rawData += offset;
122 dataSize -= std::min(dataSize, offset);
123 }
124
125 #if WORDS_BIGENDIAN
126 swap = !bigendian;
127 #else
128 swap = bigendian;
129 #endif
130
131 if (doublePrec) {
132 union {
133 unsigned char c[8];
134 double d;
135 } u;
136
137 u.d = 0.0f;
138 while (rawCount + 7 < dataSize) {
139 if (swap)
140 for(size_t i = 0; i < 8; i++)
141 u.c[7-i] = rawData[rawCount+i];
142 else
143 for(size_t i = 0; i < 8; i++)
144 u.c[i] = rawData[rawCount+i];
145 data1[dataCount1] = (float)u.d;
146 dataCount1++;
147 rawCount += 8;
148 }
149 }
150 else {
151 union {
152 unsigned char c[4];
153 float f;
154 } u;
155
156 u.f = 0.0f;
157 while (rawCount + 3 < dataSize) {
158 if (swap)
159 for(size_t i = 0; i < 4; i++)
160 u.c[3-i] = rawData[rawCount+i];
161 else
162 for(size_t i = 0; i < 4; i++)
163 u.c[i] = rawData[rawCount+i];
164 data1[dataCount1] = u.f;
165 dataCount1++;
166 rawCount += 4;
167 }
168 }
169
170 if (stereo) {
171 dataCount1 /= 2;
172 for(size_t i = 0; i < dataCount1; i++) {
173 data2[i] = data1[2*i+1];
174 data1[i] = data1[2*i];
175 }
176 dataCount2 = dataCount1;
177 }
178
179 *len1 = dataCount1;
180 *len2 = dataCount2;
181}
int min(int a, int b)
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:628

References min(), and anonymous_namespace{NoteTrack.cpp}::swap().

Referenced by GuessFloatFormats().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Guess16Bit()

static int Guess16Bit ( unsigned  numTests,
const ArrayOf< char >  rawData[],
size_t  dataSize,
bool  evenMSB,
size_t *  out_offset,
unsigned *  out_channels 
)
static

Definition at line 664 of file RawAudioGuess.cpp.

667{
668 int format;
669 bool guessSigned = false;
670 bool guessStereo = false;
671 bool guessBigEndian = false;
672 bool guessOffset = false;
673 unsigned signvotes = 0;
674 unsigned unsignvotes = 0;
675 unsigned stereoVotes = 0;
676 unsigned monoVotes = 0;
677 unsigned formerVotes = 0;
678 unsigned latterVotes = 0;
679 ArrayOf<char> rawData2{ dataSize + 4 };
680 ArrayOf<float> data1{ dataSize + 4 };
681 ArrayOf<float> data2{ dataSize + 4 };
682 size_t len1;
683 size_t len2;
684
685 #if RAW_GUESS_DEBUG
686 FILE *af = g_raw_debug_file;
687 wxFprintf(af, "16-bit\n");
688 #endif
689
690 /*
691 * Do the signed/unsigned test by using only the MSB.
692 */
693
694 for (unsigned test = 0; test < numTests; test++) {
695
696 float signL, signR, unsignL, unsignR;
697
698 /* Extract a NEW array of the MSBs only: */
699
700 for (size_t i = 0; i < dataSize / 2; i++)
701 rawData2[i] = rawData[test][2 * i + (evenMSB ? 0 : 1)];
702
703 /* Test signed/unsigned of the MSB */
704
705 Extract(0, 1, 1, 0, /* 8-bit signed stereo */
706 0, rawData2.get(), dataSize / 2, data1.get(), data2.get(), &len1, &len2);
707 signL = JumpStat(data1.get(), len1);
708 signR = JumpStat(data2.get(), len2);
709 Extract(0, 0, 1, 0, /* 8-bit unsigned stereo */
710 0, rawData2.get(), dataSize / 2, data1.get(), data2.get(), &len1, &len2);
711 unsignL = JumpStat(data1.get(), len1);
712 unsignR = JumpStat(data2.get(), len2);
713
714 if (signL > unsignL)
715 signvotes++;
716 else
717 unsignvotes++;
718
719 if (signR > unsignR)
720 signvotes++;
721 else
722 unsignvotes++;
723 }
724
725 #if RAW_GUESS_DEBUG
726 wxFprintf(af, "sign: %ud unsign: %ud\n", signvotes, unsignvotes);
727 #endif
728
729 guessSigned = (signvotes > unsignvotes);
730
731 #if RAW_GUESS_DEBUG
732 if (guessSigned)
733 wxFprintf(af, "signed\n");
734 else
735 wxFprintf(af, "unsigned\n");
736 #endif
737
738 /*
739 * Test mono/stereo using only the MSB
740 */
741
742 for (unsigned test = 0; test < numTests; test++) {
743 float leftChannel, rightChannel, combinedChannel;
744
745 /* Extract a NEW array of the MSBs only: */
746
747 for (size_t i = 0; i < dataSize / 2; i++)
748 rawData2[i] = rawData[test][2 * i + (evenMSB ? 0 : 1)];
749
750 Extract(0, guessSigned, 1, 0, 0,
751 rawData2.get(), dataSize / 2, data1.get(), data2.get(), &len1, &len2);
752 leftChannel = JumpStat(data1.get(), len1);
753 rightChannel = JumpStat(data2.get(), len2);
754 Extract(0, guessSigned, 0, 0, 0,
755 rawData2.get(), dataSize / 2, data1.get(), data2.get(), &len1, &len2);
756 combinedChannel = JumpStat(data1.get(), len1);
757
758 if (leftChannel > combinedChannel
759 && rightChannel > combinedChannel)
760 stereoVotes++;
761 else
762 monoVotes++;
763 }
764
765 #if RAW_GUESS_DEBUG
766 wxFprintf(af, "stereoVotes: %ud monoVotes: %ud\n", stereoVotes, monoVotes);
767 #endif
768
769 guessStereo = (stereoVotes > monoVotes);
770
771 if (!guessStereo) {
772
773 /* Test for repeated-byte, redundant stereo */
774
775 unsigned rstereoVotes = 0;
776 unsigned rmonoVotes = 0;
777
778 for (unsigned test = 0; test < numTests; test++) {
779
780 float redundant;
781
782 /* Extract a NEW array of the MSBs only: */
783
784 for (size_t i = 0; i < dataSize / 2; i++)
785 rawData2[i] = rawData[test][2 * i + (evenMSB ? 0 : 1)];
786
787 Extract(0, guessSigned, 0, 0, 0, rawData2.get(), dataSize / 2,
788 data1.get(), data2.get(), &len1, &len2);
789
790 redundant = RedundantStereo(data1.get(), len1);
791
792 if (redundant > 0.8)
793 rstereoVotes++;
794 else
795 rmonoVotes++;
796 }
797
798 #if RAW_GUESS_DEBUG
799 wxFprintf(af, "rstereoVotes: %ud rmonoVotes: %ud\n",
800 rstereoVotes, rmonoVotes);
801 #endif
802
803 guessStereo = (rstereoVotes > rmonoVotes);
804
805 }
806
807 #if RAW_GUESS_DEBUG
808 if (guessStereo)
809 wxFprintf(af, "stereo\n");
810 else
811 wxFprintf(af, "mono\n");
812 #endif
813
814 /*
815 * Finally, determine the endianness and offset.
816 *
817 * Even MSB -> BigEndian or LittleEndian with Offset
818 * Odd MSB -> LittleEndian or BigEndian with Offset
819 */
820
821 guessBigEndian = evenMSB;
822 guessOffset = 0;
823
824 #if RAW_GUESS_DEBUG
825 wxFprintf(af, "evenMSB: %d BE: %d\n", evenMSB, guessBigEndian);
826 #endif
827
828 for (unsigned test = 0; test < numTests; test++) {
829
830 float former, latter;
831 int offs;
832
833 /* Extract a NEW array of the MSBs only: */
834
835 if (guessStereo)
836 for (size_t i = 0; i + 1 < (dataSize / 4); i++)
837 rawData2[i] =
838 rawData[test][4 * i + (evenMSB ? 0 : 1)];
839 else
840 for (size_t i = 0; i + 1 < (dataSize / 2); i++)
841 rawData2[i] =
842 rawData[test][2 * i + (evenMSB ? 0 : 1)];
843
844 former = 0.0;
845 Extract(1, guessSigned, guessStereo, guessBigEndian, guessOffset,
846 rawData[test].get(), dataSize-4, data1.get(), data2.get(), &len1, &len2);
847
848 offs=(!guessBigEndian);
849
850 for(size_t i = 3; i + 4 < len1; i++) {
851 if (rawData2[offs+i-2]==rawData2[offs+i-1] &&
852 rawData2[offs+i]==rawData2[offs+i-1]+1 &&
853 rawData2[offs+i]==rawData2[offs+i+1]) {
854 former += data1[i]-data1[i-1];
855 }
856 }
857
858 latter = 0.0;
859 Extract(1, guessSigned, guessStereo, !guessBigEndian,
860 !guessOffset, rawData[test].get(), dataSize, data1.get(), data2.get(),
861 &len1, &len2);
862
863 offs=(guessBigEndian);
864
865 for(size_t i = 3; i + 4 < len1; i++) {
866 if (rawData2[offs+i-2]==rawData2[offs+i-1] &&
867 rawData2[offs+i]==rawData2[offs+i-1]+1 &&
868 rawData2[offs+i]==rawData2[offs+i+1]) {
869
870 latter += data1[i]-data1[i-1];
871 }
872 }
873
874 #if RAW_GUESS_DEBUG
875 wxFprintf(af, "former: %f latter: %f\n", former, latter);
876 #endif
877
878 if (former <= latter)
879 formerVotes++;
880 else
881 latterVotes++;
882 }
883
884 #if RAW_GUESS_DEBUG
885 wxFprintf(af, "former (BE/LE): %ud latter (LE+/BE+): %ud\n",
886 formerVotes, latterVotes);
887 #endif
888
889 // High barrier, since odd byte offsets are very rare
890 if (latterVotes > formerVotes*2) {
891 guessBigEndian = !guessBigEndian;
892 guessOffset = !guessOffset;
893 }
894
895 #if RAW_GUESS_DEBUG
896 if (guessBigEndian)
897 wxFprintf(af, "big endian\n");
898 else
899 wxFprintf(af, "little endian\n");
900 #endif
901
902 #if RAW_GUESS_DEBUG
903 if (guessOffset)
904 wxFprintf(af, "offset 1 byte\n");
905 else
906 wxFprintf(af, "no byte offset\n");
907 #endif
908
909 format = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
910
911 if (guessBigEndian)
912 format |= SF_ENDIAN_BIG;
913 else
914 format |= SF_ENDIAN_LITTLE;
915
916 if (guessOffset)
917 *out_offset = 1;
918
919 if (guessStereo)
920 *out_channels = 2;
921 else
922 *out_channels = 1;
923
924 return format;
925}
static void Extract(bool bits16, bool sign, bool stereo, bool bigendian, bool offset, char *rawData, int dataSizeIn, float *data1, float *data2, size_t *len1, size_t *len2)
static float JumpStat(float *data, size_t len)
static float RedundantStereo(float *data, size_t len)

References Extract(), anonymous_namespace{ExportPCM.cpp}::format, JumpStat(), and RedundantStereo().

Referenced by GuessIntFormats().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Guess8Bit()

static int Guess8Bit ( unsigned  numTests,
const ArrayOf< char >  rawData[],
size_t  dataSize,
unsigned *  out_channels 
)
static

Definition at line 516 of file RawAudioGuess.cpp.

517{
518 bool guessSigned = false;
519 bool guessStereo = false;
520 unsigned signvotes = 0;
521 unsigned unsignvotes = 0;
522 unsigned stereoVotes = 0;
523 unsigned monoVotes = 0;
524
525 ArrayOf<float> data1 { dataSize + 4 };
526 ArrayOf<float> data2 { dataSize + 4 };
527 size_t len1;
528 size_t len2;
529
530 #if RAW_GUESS_DEBUG
531 FILE *af = g_raw_debug_file;
532 wxFprintf(af, "8-bit\n");
533 #endif
534
535 /*
536 * Compare signed to unsigned, interpreted as if the file were
537 * stereo just to be safe. If the file is actually mono, the test
538 * still works, and we lose a tiny bit of accuracy. (It would not make
539 * sense to assume the file is mono, because if the two tracks are not
540 * very similar we would get inaccurate results.)
541 *
542 * The JumpTest measures the average jump between two successive samples
543 * and returns a value 0-1. 0 is maximally discontinuous, 1 is smooth.
544 */
545
546 for (unsigned test = 0; test < numTests; test++) {
547 float signL, signR, unsignL, unsignR;
548
549 Extract(0, 1, 1, 0, /* 8-bit signed stereo */
550 false, rawData[test].get(), dataSize,
551 data1.get(), data2.get(), &len1, &len2);
552 signL = JumpStat(data1.get(), len1);
553 signR = JumpStat(data2.get(), len2);
554 Extract(0, 0, 1, 0, /* 8-bit unsigned stereo */
555 false, rawData[test].get(), dataSize,
556 data1.get(), data2.get(), &len1, &len2);
557 unsignL = JumpStat(data1.get(), len1);
558 unsignR = JumpStat(data2.get(), len2);
559
560 if (signL > unsignL)
561 signvotes++;
562 else
563 unsignvotes++;
564
565 if (signR > unsignR)
566 signvotes++;
567 else
568 unsignvotes++;
569 }
570
571 #if RAW_GUESS_DEBUG
572 wxFprintf(af, "sign: %ud unsign: %ud\n", signvotes, unsignvotes);
573 #endif
574
575 guessSigned = (signvotes > unsignvotes);
576
577 #if RAW_GUESS_DEBUG
578 if (guessSigned)
579 wxFprintf(af, "signed\n");
580 else
581 wxFprintf(af, "unsigned\n");
582 #endif
583
584 /* Finally we test stereo/mono. We use the same JumpStat, and say
585 * that the file is stereo if and only if for the majority of the
586 * tests, the left channel and the right channel are more smooth than
587 * the entire stream interpreted as one channel.
588 */
589
590 for (unsigned test = 0; test < numTests; test++) {
591 float leftChannel, rightChannel, combinedChannel;
592
593 Extract(0, guessSigned, 1, 0, 0, rawData[test].get(), dataSize, data1.get(),
594 data2.get(), &len1, &len2);
595 leftChannel = JumpStat(data1.get(), len1);
596 rightChannel = JumpStat(data2.get(), len2);
597 Extract(0, guessSigned, 0, 0, 0, rawData[test].get(), dataSize, data1.get(),
598 data2.get(), &len1, &len2);
599 combinedChannel = JumpStat(data1.get(), len1);
600
601 if (leftChannel > combinedChannel
602 && rightChannel > combinedChannel)
603 stereoVotes++;
604 else
605 monoVotes++;
606 }
607
608 #if RAW_GUESS_DEBUG
609 wxFprintf(af, "stereo: %ud mono: %ud\n", stereoVotes, monoVotes);
610 #endif
611
612 guessStereo = (stereoVotes > monoVotes);
613
614 if (!guessStereo) {
615
616 /* test for repeated-byte, redundant stereo */
617
618 unsigned rstereoVotes = 0;
619 unsigned rmonoVotes = 0;
620
621 for (unsigned test = 0; test < numTests; test++) {
622 float redundant;
623
624 Extract(0, guessSigned, 0, 0, 0, rawData[test].get(), dataSize,
625 data1.get(), data2.get(), &len1, &len2);
626 redundant = RedundantStereo(data1.get(), len1);
627
628 #if RAW_GUESS_DEBUG
629 wxFprintf(af, "redundant: %f\n", redundant);
630 #endif
631
632 if (redundant > 0.8)
633 rstereoVotes++;
634 else
635 rmonoVotes++;
636 }
637
638 #if RAW_GUESS_DEBUG
639 wxFprintf(af, "rstereo: %ud rmono: %ud\n", rstereoVotes, rmonoVotes);
640 #endif
641
642 guessStereo = (rstereoVotes > rmonoVotes);
643
644 }
645
646 #if RAW_GUESS_DEBUG
647 if (guessStereo)
648 wxFprintf(af, "stereo\n");
649 else
650 wxFprintf(af, "mono\n");
651 #endif
652
653 if (guessStereo)
654 *out_channels = 2;
655 else
656 *out_channels = 1;
657
658 if (guessSigned)
659 return SF_FORMAT_RAW | SF_FORMAT_PCM_S8;
660 else
661 return SF_FORMAT_RAW | SF_FORMAT_PCM_U8;
662}

References Extract(), JumpStat(), and RedundantStereo().

Referenced by GuessIntFormats().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GuessFloatFormats()

static int GuessFloatFormats ( unsigned  numTests,
const ArrayOf< char >  rawData[],
size_t  dataSize,
size_t *  out_offset,
unsigned *  out_channels 
)
static

Definition at line 293 of file RawAudioGuess.cpp.

295{
296 int format;
297 size_t bestOffset = 0;
298 int bestEndian = 0;
299 int bestPrec = 0;
300 float bestSmoothAvg = 1000.0;
301 size_t len1;
302 size_t len2;
303 bool guessStereo = false;
304 unsigned stereoVotes = 0;
305 unsigned monoVotes = 0;
306
307 #if RAW_GUESS_DEBUG
308 FILE *af = g_raw_debug_file;
309 wxFprintf(af, "Testing float\n");
310 #endif
311
312 ArrayOf<float> data1{ dataSize + 4 };
313 ArrayOf<float> data2{ dataSize + 4 };
314
315 /*
316 * First determine if it is possibly in a floating-point
317 * format. The nice thing about floating-point formats
318 * is that random bytes or even random integers are
319 * extremely unlikely to result in meaningful floats.
320 * All we do is try interpreting the raw bytes as floating-point,
321 * with a variety of offsets, both endiannesses, and both
322 * precisions (32-bit float and 64-bit double), and if any of
323 * them result in all finite numbers with reasonable ranges,
324 * we accept them.
325 *
326 * Sometimes there is more than one plausible candidate, in
327 * which case we take the smoothest one. This usually happens
328 * because big-endian floats actually still look and act
329 * like floats when you interpret them as little-endian
330 * floats with a 1-byte offset.
331 */
332
333 for(unsigned int prec = 0; prec < 2; prec++) {
334 for(int endian = 0; endian < 2; endian++) {
335 for(size_t offset = 0; offset < (4 * prec + 4); offset++) {
336 unsigned finiteVotes = 0;
337 unsigned maxminVotes = 0;
338 float smoothAvg = 0;
339
340 #if RAW_GUESS_DEBUG
341 wxFprintf(af, "prec=%d endian=%d offset=%d\n",
342 prec, endian, (int)offset);
343 #endif
344
345 for(unsigned test = 0; test < numTests; test++) {
346 float min, max;
347
348 ExtractFloats(prec == 1, endian == 1,
349 true, /* stereo */
350 offset,
351 rawData[test].get(), dataSize,
352 data1.get(), data2.get(), &len1, &len2);
353
354 size_t i = 0;
355 for(; i < len1; i++)
356 // This code is testing for NaNs.
357 // We'd like to know if all data is finite.
358 if (!(data1[i]>=0 || data1[i]<=0) ||
359 !(data2[i]>=0 || data2[i]<=0))
360 break;
361 if (i == len1)
362 // all data is finite.
363 finiteVotes++;
364
365 min = data1[0];
366 max = data1[0];
367 for(i = 1; i < len1; i++) {
368 if (data1[i]<min)
369 min = data1[i];
370 if (data1[i]>max)
371 max = data1[i];
372 }
373 for(i = 1; i < len2; i++) {
374 if (data2[i]<min)
375 min = data2[i];
376 if (data2[i]>max)
377 max = data2[i];
378 }
379
380 if (min < -0.01 && min >= -100000 &&
381 max > 0.01 && max <= 100000)
382 maxminVotes++;
383
384 smoothAvg += SecondDStat(data1.get(), len1) / max;
385 }
386
387 smoothAvg /= numTests;
388
389 #if RAW_GUESS_DEBUG
390 wxFprintf(af, "finite: %ud/%ud maxmin: %ud/%ud smooth: %f\n",
391 finiteVotes, numTests, maxminVotes, numTests,
392 smoothAvg);
393 #endif
394
395 if (finiteVotes > numTests/2 &&
396 finiteVotes > numTests-2 &&
397 maxminVotes > numTests/2 &&
398 smoothAvg < bestSmoothAvg) {
399
400 bestSmoothAvg = smoothAvg;
401 bestOffset = offset;
402 bestPrec = prec;
403 bestEndian = endian;
404 }
405 }
406 }
407 }
408
409 /*
410 * If none of those tests succeeded, it's probably not
411 * actually floating-point data. Return 0 so the
412 * main function will try guessing an integer format.
413 */
414
415 if (bestSmoothAvg >= 1000.0)
416 return 0;
417
418 /*
419 * We still have to test for mono/stereo. For an explanation
420 * of these tests, see the comments next to the stereo/mono
421 * tests for 8-bit or 16-bit data.
422 */
423
424 for (unsigned test = 0; test < numTests; test++) {
425 float leftChannel, rightChannel, combinedChannel;
426
427 ExtractFloats(bestPrec == 1, bestEndian == 1,
428 true, /* stereo */
429 bestOffset,
430 rawData[test].get(), dataSize,
431 data1.get(), data2.get(), &len1, &len2);
432 leftChannel = JumpStat(data1.get(), len1);
433 rightChannel = JumpStat(data2.get(), len2);
434 ExtractFloats(bestPrec == 1, bestEndian == 1,
435 false, /* stereo */
436 bestOffset,
437 rawData[test].get(), dataSize,
438 data1.get(), data2.get(), &len1, &len2);
439 combinedChannel = JumpStat(data1.get(), len1);
440
441 if (leftChannel > combinedChannel
442 && rightChannel > combinedChannel)
443 stereoVotes++;
444 else
445 monoVotes++;
446 }
447
448 #if RAW_GUESS_DEBUG
449 wxFprintf(af, "stereo: %ud mono: %ud\n", stereoVotes, monoVotes);
450 #endif
451
452 guessStereo = (stereoVotes > monoVotes);
453
454 if (!guessStereo) {
455
456 /* test for repeated-byte, redundant stereo */
457
458 unsigned rstereoVotes = 0;
459 unsigned rmonoVotes = 0;
460
461 for (unsigned test = 0; test < numTests; test++) {
462 float redundant;
463
464 ExtractFloats(bestPrec == 1, bestEndian == 1,
465 false, /* stereo */
466 bestOffset,
467 rawData[test].get(), dataSize,
468 data1.get(), data2.get(), &len1, &len2);
469 redundant = RedundantStereo(data1.get(), len1);
470
471 #if RAW_GUESS_DEBUG
472 wxFprintf(af, "redundant: %f\n", redundant);
473 #endif
474
475 if (redundant > 0.8)
476 rstereoVotes++;
477 else
478 rmonoVotes++;
479 }
480
481 #if RAW_GUESS_DEBUG
482 wxFprintf(af, "rstereo: %ud rmono: %ud\n", rstereoVotes, rmonoVotes);
483 #endif
484
485 guessStereo = (rstereoVotes > rmonoVotes);
486
487 }
488
489 #if RAW_GUESS_DEBUG
490 if (guessStereo)
491 wxFprintf(af, "stereo\n");
492 else
493 wxFprintf(af, "mono\n");
494 #endif
495
496 *out_offset = bestOffset;
497
498 if (guessStereo)
499 *out_channels = 2;
500 else
501 *out_channels = 1;
502
503 if (bestPrec)
504 format = SF_FORMAT_RAW | SF_FORMAT_DOUBLE;
505 else
506 format = SF_FORMAT_RAW | SF_FORMAT_FLOAT;
507
508 if (bestEndian)
509 format |= SF_ENDIAN_BIG;
510 else
511 format |= SF_ENDIAN_LITTLE;
512
513 return format;
514}
static float SecondDStat(float *data, size_t len)
static void ExtractFloats(bool doublePrec, bool bigendian, bool stereo, size_t offset, char *rawData, size_t dataSize, float *data1, float *data2, size_t *len1, size_t *len2)

References ExtractFloats(), anonymous_namespace{ExportPCM.cpp}::format, JumpStat(), min(), RedundantStereo(), and SecondDStat().

Referenced by RawAudioGuess().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GuessIntFormats()

static int GuessIntFormats ( unsigned  numTests,
const ArrayOf< char >  rawData[],
size_t  dataSize,
size_t *  out_offset,
unsigned *  out_channels 
)
static

Definition at line 927 of file RawAudioGuess.cpp.

929{
930 int format = SF_FORMAT_RAW;
931 bool guess16bit = false;
932 bool evenMSB;
933 ArrayOf<float> data1{ dataSize + 4 };
934 ArrayOf<float> data2{ dataSize + 4 };
935 size_t len1;
936 size_t len2;
937 unsigned vote8 = 0;
938 unsigned vote16 = 0;
939 unsigned evenMSBVotes = 0;
940 unsigned oddMSBVotes = 0;
941
942 #if RAW_GUESS_DEBUG
943 FILE *af = g_raw_debug_file;
944 #endif
945
946 *out_channels = 1;
947 *out_offset = 0;
948
949 /*
950 * First test: we attempt to determine if the data is 8-bit or 16-bit.
951 * We extract the odd and even bytes interpreted as signed-valued samples,
952 * and compare their amplitude distributions. Noting that in 16-bit values,
953 * the less significant 8 bits should have roughly flat distribution, while
954 * the more significant 8 bits should have a tighter distribution, with a
955 * smaller standard deviation.
956 *
957 * Note that this correctly makes the distinction whether we are dealing
958 * with mono or stereo data.
959 */
960
961 for (unsigned test = 0; test < numTests; test++) {
962 float even, odd;
963
964 Extract(0, 1, 1, 0, /* 8-bit signed stereo */
965 false, rawData[test].get(), dataSize,
966 data1.get(), data2.get(), &len1, &len2);
967 even = AmpStat(data1.get(), len1);
968 odd = AmpStat(data2.get(), len2);
969 if ((even > 0.15) && (odd > 0.15)) {
970 #if RAW_GUESS_DEBUG
971 wxFprintf(af, "Both appear random: %.2f, %.2f.\n", even, odd);
972 #endif
973 }
974 else if ((even > 0.15) || (odd > 0.15))
975 vote16++;
976 else
977 vote8++;
978
979 /* Record which of the two was the MSB for future reference */
980 if (even < odd)
981 evenMSBVotes++;
982 else
983 oddMSBVotes++;
984 }
985
986 evenMSB = (evenMSBVotes > oddMSBVotes);
987
988 #if RAW_GUESS_DEBUG
989 wxFprintf(af, "evenMSBVote: %ud oddMSBVote: %ud\n",
990 evenMSBVotes, oddMSBVotes);
991 wxFprintf(af, "vote8: %ud vote16: %ud\n", vote8, vote16);
992 #endif
993
994 guess16bit = (vote8 <= vote16);
995
996 if (!guess16bit)
997 format = Guess8Bit(numTests, rawData, dataSize, out_channels);
998 else
999 format = Guess16Bit(numTests, rawData,
1000 dataSize, evenMSB,
1001 out_offset, out_channels);
1002
1003 return format;
1004}
static float AmpStat(float *data, size_t len)
static int Guess8Bit(unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, unsigned *out_channels)
static int Guess16Bit(unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, bool evenMSB, size_t *out_offset, unsigned *out_channels)

References AmpStat(), Extract(), anonymous_namespace{ExportPCM.cpp}::format, Guess16Bit(), and Guess8Bit().

Referenced by RawAudioGuess().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ JumpStat()

static float JumpStat ( float *  data,
size_t  len 
)
static

Definition at line 60 of file RawAudioGuess.cpp.

61{
62 float avg;
63
64 /* Calculate 1.0 - avg jump
65 * A score near 1.0 means avg jump is pretty small
66 */
67
68 avg = 0.0;
69 for (size_t i = 0; i + 1 < len; i++)
70 avg += fabs(data[i + 1] - data[i]);
71 avg = 1.0 - (avg / (len - 1) / 2.0);
72
73 return avg;
74}

Referenced by Guess16Bit(), Guess8Bit(), and GuessFloatFormats().

Here is the caller graph for this function:

◆ RawAudioGuess()

int RawAudioGuess ( const wxString &  in_fname,
size_t *  out_offset,
unsigned *  out_channels 
)

Definition at line 1006 of file RawAudioGuess.cpp.

1008{
1009 const unsigned numTests = 11;
1010 size_t headerSkipSize = 64;
1011 size_t dataSize = 16384;
1012 int format = SF_FORMAT_RAW;
1013 FILE *inf;
1014 size_t fileLen;
1015 size_t read_data;
1016
1017 #if RAW_GUESS_DEBUG
1018 FILE *af = fopen("raw.txt", "a");
1019 g_raw_debug_file = af;
1020 wxFprintf(af, "File: %s\n", in_fname);
1021 #endif
1022
1023 *out_offset = 0;
1024 *out_channels = 1;
1025
1026 wxFFile in_wxFFile(in_fname, wxT("rb"));
1027
1028 // JKC FALSE changed to -1.
1029 if (!in_wxFFile.IsOpened())
1030 return -1;
1031 inf = in_wxFFile.fp();
1032
1033 if (!inf) {
1034 #if RAW_GUESS_DEBUG
1035 fclose(af);
1036 g_raw_debug_file = NULL;
1037 #endif
1038
1039 return -1;
1040 }
1041
1042 // FIXME: TRAP_ERR fseek return in RawAudioGuess unchecked.
1043 fseek(inf, 0, SEEK_END);
1044 fileLen = ftell(inf);
1045
1046 if (fileLen < 8)
1047 return -1;
1048
1049 if (fileLen < headerSkipSize)
1050 headerSkipSize = 0;
1051
1052 if (fileLen < dataSize)
1053 dataSize = fileLen / 2;
1054
1055 wxASSERT( dataSize >= 4 );
1056 wxASSERT( dataSize <= fileLen );
1057
1058 ArraysOf<char> rawData{ numTests, dataSize + 4 };
1059
1060 for (unsigned test = 0; test < numTests; test++) {
1061 int startPoint;
1062
1063 startPoint = (fileLen - dataSize) * (test + 1) / (numTests + 2);
1064
1065 /* Make it a multiple of 16 (stereo double-precision) */
1066 startPoint = (startPoint/16)*16;
1067
1068 // FIXME: TRAP_ERR fseek return in MultiFormatReader unchecked.
1069 fseek(inf, headerSkipSize + startPoint, SEEK_SET);
1070 read_data = fread(rawData[test].get(), 1, dataSize, inf);
1071 if (read_data != dataSize && ferror(inf)) {
1072 perror("fread error in RawAudioGuess");
1073 }
1074 }
1075
1076 in_wxFFile.Close();
1077
1078 /*
1079 * The floating-point tests will only return a valid format
1080 * if it's almost certainly floating-point data. On the other
1081 * hand, the integer tests will always return something, since
1082 * almost anything looks like it could be integer data...
1083 */
1084
1085 format = GuessFloatFormats(numTests,
1086 rawData.get(),
1087 dataSize,
1088 out_offset, out_channels);
1089
1090 if (format == 0) {
1091 format = GuessIntFormats(numTests,
1092 rawData.get(),
1093 dataSize,
1094 out_offset, out_channels);
1095 }
1096
1097 #if RAW_GUESS_DEBUG
1098 fclose(af);
1099 g_raw_debug_file = NULL;
1100 #endif
1101
1102 return format;
1103}
wxT("CloseDown"))
static int GuessIntFormats(unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, size_t *out_offset, unsigned *out_channels)
static int GuessFloatFormats(unsigned numTests, const ArrayOf< char > rawData[], size_t dataSize, size_t *out_offset, unsigned *out_channels)

References anonymous_namespace{ExportPCM.cpp}::format, GuessFloatFormats(), GuessIntFormats(), and wxT().

Here is the call graph for this function:

◆ RedundantStereo()

static float RedundantStereo ( float *  data,
size_t  len 
)
static

Definition at line 93 of file RawAudioGuess.cpp.

94{
95 int c = 0;
96
97 for (size_t i = 1; i + 1 < len; i += 2)
98 if (fabs(data[i + 1] - data[i]) > 2*fabs(data[i] - data[i - 1]) ||
99 2*fabs(data[i + 1] - data[i]) < fabs(data[i] - data[i - 1]))
100 c++;
101
102 return ((c * 2.0) / (len - 2));
103}

Referenced by Guess16Bit(), Guess8Bit(), and GuessFloatFormats().

Here is the caller graph for this function:

◆ SecondDStat()

static float SecondDStat ( float *  data,
size_t  len 
)
static

Definition at line 76 of file RawAudioGuess.cpp.

77{
78 float v1=0, v2=0;
79 float a1=0, a2=0;
80 float sum=0;
81
82 for (size_t i = 1; i < len; i++) {
83 a2 = a1;
84 v2 = v1;
85 v1 = data[i]-data[i-1];
86 a1 = v1 - v2;
87 sum += fabs(a1-a2);
88 }
89
90 return sum/len;
91}

Referenced by GuessFloatFormats().

Here is the caller graph for this function: