1818
1919#include < FLAC++/decoder.h>
2020
21- const std::map<unsigned char , uint32_t > OriginalSampleRateTable = {
22- /* MQA 's encoded value - sample rate in Hz */
23-
24- /* 1x Sample Rate */
25- {0b0000 , 44100 },
26- {0b0001 , 48000 },
27- /* 2x Sample Rate */
28- {0b1000 , 88200 },
29- {0b1001 , 96000 },
30- /* 4x Sample Rate */
31- {0b0100 , 176400 },
32- {0b0101 , 192000 },
33- /* 8x Sample Rate */
34- {0b1100 , 352800 },
35- {0b1101 , 384000 } /* ? */
36-
37- /* ?? 16x Sample Rate ?? */
38- /* 2 bits left - probably 705.6K and 786K
39- * No recordings on those values so no worries
21+
22+ /* *
23+ * Returns original Sample rate (in Hz) from waveform bytecode.
24+ * @param c 4bit bytecode
25+ * @return
26+ */
27+ uint32_t OriginalSampleRateDecoder (unsigned c) {
28+ if (c > 0b1111u ) throw std::logic_error (" Invalid bytecode" );
29+ /*
30+ * If LSB is 0 then base is 44100 else 48000
31+ * 3 MSB need to be rotated and raised to the power of 2 (so 1, 2, 4, 8, ...)
32+ * output is base * multiplier
4033 */
41- };
34+ const uint32_t base = (c & 1u ) ? 48000 : 44100 ;
35+
36+ const uint32_t multiplier = 1u << (((c >> 3u ) & 1u ) | (((c >> 2u ) & 1u ) << 1u ) | (((c >> 1u ) & 1u ) << 2u ));
37+
38+ return base * multiplier;
39+ }
4240
4341
4442class MQA_identifier {
@@ -50,9 +48,10 @@ class MQA_identifier {
5048 uint32_t bps = 0 ;
5149 FLAC__uint64 decoded_samples = 0 ;
5250 std::vector<std::array<const FLAC__int32, 2 >> samples;
53- std::string mqa_endoder ;
51+ std::string mqa_encoder ;
5452 uint32_t original_sample_rate = 0 ;
5553
54+
5655 explicit MyDecoder (std::string file) : FLAC::Decoder::File(), file_(std::move(file)) {};
5756
5857 ::FLAC__StreamDecoderInitStatus decode ();
@@ -78,6 +77,7 @@ class MQA_identifier {
7877 public:
7978 explicit MQA_identifier (std::string file) : file_(std::move(file)), decoder(file_), isMQA_(false ) {}
8079
80+
8181 bool detect ();
8282
8383 [[nodiscard]] std::string getMQA_encoder () const noexcept ;
@@ -118,23 +118,19 @@ void MQA_identifier::MyDecoder::metadata_callback(const ::FLAC__StreamMetadata *
118118 const auto comment = reinterpret_cast <char *>(metadata->data .vorbis_comment .comments [i].entry );
119119
120120 if (std::strncmp (" MQAENCODER" , comment, 10 ) == 0 )
121- this ->mqa_endoder =
121+ this ->mqa_encoder =
122122 std::string (comment + 10 , comment + metadata->data .vorbis_comment .comments [i].length );
123-
124- else if (std::strncmp (" ORIGINALSAMPLERATE" , comment, 18 ) == 0 )
125- this ->original_sample_rate = std::stoul (comment + 19 );
126-
127- else if (std::strncmp (" MQASAMPLERATE" , comment, 13 ) == 0 )
128- this ->original_sample_rate = std::stoul (comment + 14 );
129123 }
130124 }
131125
132126}
133127
128+
134129void MQA_identifier::MyDecoder::error_callback (::FLAC__StreamDecoderErrorStatus status) {
135130 std::cerr << " Got error callback: " << FLAC__StreamDecoderErrorStatusString[status] << " \n " ;
136131}
137132
133+
138134::FLAC__StreamDecoderInitStatus MQA_identifier::MyDecoder::decode () {
139135 bool ok = true ;
140136
@@ -155,6 +151,7 @@ ::FLAC__StreamDecoderInitStatus MQA_identifier::MyDecoder::decode() {
155151 while (this ->decoded_samples < this ->sample_rate * 3 /* read only 3 first seconds */ )
156152 ok = this ->process_single ();
157153
154+
158155 if (!ok) {
159156 std::cerr << " decoding FAILED\n " ;
160157 std::cerr << this ->get_state ().resolved_as_cstring (*this );
@@ -166,51 +163,52 @@ ::FLAC__StreamDecoderInitStatus MQA_identifier::MyDecoder::decode() {
166163bool MQA_identifier::detect () {
167164 this ->decoder .decode ();
168165
169- for (auto p = 0u ; p < this ->decoder .bps ; p++) {
170- uint64_t buffer = 0 ;
171- for (const auto &s: this ->decoder .samples ) {
172- buffer |=
173- ((static_cast <uint32_t >(s[0 ]) ^ static_cast <uint32_t >(s[1 ])) >> p) & 1u ; // static_cast for clang-tidy
174- if (buffer == 0xbe0498c88 ) { // MQA magic word
175- this ->isMQA_ = true ;
176-
177- // Get Original Sample Rate
178- unsigned char orsf = 0 ;
179- for (auto m = 3u ; m < 7 ; m++) { // Skip 2 bits nd get next 4
180- auto cur = *(&s + m);
181- auto j = ((static_cast <uint32_t >(cur[0 ]) ^ static_cast <uint32_t >(cur[1 ])) >> p) & 1u ;
182- orsf |= j << (6u - m);
183- }
184- try {
185- if (decoder.original_sample_rate != 0
186- && decoder.original_sample_rate != OriginalSampleRateTable.at (orsf))
187- std::cerr << decoder.original_sample_rate << " \n " ;
188- this ->decoder .original_sample_rate = OriginalSampleRateTable.at (orsf);
189- } catch (std::exception &e) {
190- std::cerr << e.what () << " \n " ;
191- }
192-
193- // We are done return true
194- return true ;
195- } else
196- buffer = (buffer << 1u ) & 0xFFFFFFFFFu ;
197- }
166+ uint64_t buffer = 0 ;
167+ const auto pos = (this ->decoder .bps - 16u ); // aim for 16th bit
168+ for (const auto &s: this ->decoder .samples ) {
169+ buffer |= ((static_cast <uint32_t >(s[0 ]) ^ static_cast <uint32_t >(s[1 ])) >> pos) & 1u ; // cast for clang-tidy
170+
171+ if (buffer == 0xbe0498c88 ) { // MQA magic word
172+ this ->isMQA_ = true ;
173+
174+ // Get Original Sample Rate
175+ unsigned char orsf = 0 ;
176+ for (auto m = 3u ; m < 7 ; m++) { // Skip 2 bits nd get next 4
177+ auto cur = *(&s + m);
178+ auto j =
179+ ((static_cast <uint32_t >(cur[0 ]) ^ static_cast <uint32_t >(cur[1 ])) >> pos) & 1u ;
180+ orsf |= j << (6u - m);
181+ }
182+ try {
183+ this ->decoder .original_sample_rate = OriginalSampleRateDecoder (orsf);
184+ }
185+ catch (std::exception &e) { std::cerr << e.what () << " \n " ; }
186+ // We are done return true
187+ return true ;
188+
189+ } else
190+ buffer = (buffer << 1u ) & 0xFFFFFFFFFu ;
198191 }
192+
199193 return false ;
200194}
201195
196+
202197std::string MQA_identifier::getMQA_encoder () const noexcept {
203- return this ->decoder .mqa_endoder ;
198+ return this ->decoder .mqa_encoder ;
204199}
205200
201+
206202uint32_t MQA_identifier::originalSampleRate () const noexcept {
207203 return this ->decoder .original_sample_rate ;
208204}
209205
206+
210207bool MQA_identifier::isMQA () const noexcept {
211208 return this ->isMQA_ ;
212209}
213210
211+
214212std::string MQA_identifier::filename () const noexcept {
215213 return this ->file_ ;
216214}
0 commit comments