├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── gui ├── CMakeLists.txt ├── Windows │ ├── MyForm.cpp │ ├── MyForm.h │ └── mqa_gui.vcxproj ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui └── mqa_id_gui.pro ├── instructions.md ├── main.cc └── mqa_identifier.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /cmake-build-debug/ 3 | /cmake-build-minsizerel/ 4 | /cmake-build-release/ 5 | /cmake-build-relwithdebinfo/ 6 | /Samples/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(MQA_identifier) 3 | 4 | # for android build, unhide this 5 | # set(Boost_USE_STATIC_LIBS OFF) 6 | # set(Boost_USE_DEBUG_LIBS OFF) 7 | # set(Boost_USE_RELEASE_LIBS ON) 8 | # set(Boost_USE_MULTITHREADED ON) 9 | # set(Boost_USE_STATIC_RUNTIME OFF) 10 | # find_package(Boost 1.75.0 COMPONENTS date_time filesystem system ) 11 | 12 | set(CMAKE_CXX_STANDARD 17) 13 | 14 | if (NOT (MSVC)) 15 | set(CMAKE_CXX_FLAGS "-O2 -fpermissive") 16 | else (NOT (MSVC)) 17 | set(CMAKE_CXX_FLAGS "/O2") 18 | STRING(REPLACE "/O2" "/Od" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) 19 | endif () 20 | 21 | find_library(FLAC++_LIBRARIES NAMES FLAC++ FLAC) 22 | find_library(ogg NAMES ogg) 23 | 24 | add_executable(${PROJECT_NAME} main.cc) 25 | 26 | # for android build, hide this 27 | target_link_libraries(${PROJECT_NAME} FLAC++ FLAC ogg) 28 | 29 | # for android build, unhide this 30 | # target_link_libraries(${PROJECT_NAME} FLAC++ FLAC ogg ${Boost_LIBRARIES}) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MQA Identifier 2 | - 3 | Small tool to identify MQA encoding in *.flac* files. 4 | 5 | 6 | **Download latest release** [***here***](https://github.com/purpl3F0x/MQA_identifier/releases) 7 | 8 | **Gui tool availlable on releases** 9 | 10 | # 11 | **Usage** 12 | 13 | ```./MQA_identifier {name_of_file.flac} or {name_of_folder_to_scan} ++``` 14 | 15 | For example 16 | ```./MQA_identifier "C:\Music\Mike Oldfield - Tubular Bells\(01) [Mike Oldfield] Part One.flac" "C:\Music\Queen - News Of The World"``` 17 | 18 |
19 | 20 | 21 | **More detailed instructions here** [***here***](instructions.md) 22 | 23 |
24 |
25 | 26 | *This project isn't related nor endorsed with MQA Ltd. and is made for purely educational purposes)* 27 | 28 |

29 | > Stavros Avramidis Never Settle & Keep Running 30 | -------------------------------------------------------------------------------- /gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(MQA_identifier_gui) 3 | 4 | # Required Qt settings 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTOUIC ON) 8 | set(QT_USE_QTMAIN TRUE) 9 | 10 | if (NOT (MSVC)) 11 | set(CMAKE_CXX_FLAGS "-O2 -Wall -static -static-libstdc++ -static-libgcc") 12 | 13 | else (NOT (MSVC)) 14 | set(CMAKE_CXX_FLAGS "/O2") 15 | STRING(REPLACE "/O2" "/Od" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) 16 | endif () 17 | 18 | include_directories(${CMAKE_PARENT_LIST_FILE}/../mainwindow.h) 19 | 20 | if (WIN32) 21 | set(GUI_TYPE WIN32) 22 | elseif (APPLE) 23 | set(GUI_TYPE MACOSX_BUNDLE) 24 | endif () 25 | 26 | find_package(Qt5Core REQUIRED) 27 | find_package(Qt5Widgets REQUIRED) 28 | 29 | 30 | add_executable(${PROJECT_NAME} ${GUI_TYPE} ${CMAKE_PARENT_LIST_FILE}/../main.cpp ${CMAKE_PARENT_LIST_FILE}/../mainwindow.cpp ${CMAKE_PARENT_LIST_FILE}/../mainwindow.ui) 31 | target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::Core) 32 | -------------------------------------------------------------------------------- /gui/Windows/MyForm.cpp: -------------------------------------------------------------------------------- 1 | #include "MyForm.h" 2 | 3 | using namespace System; 4 | 5 | using namespace System::Windows::Forms; 6 | 7 | [STAThreadAttribute] 8 | 9 | void Main(array^args) { 10 | 11 | Application::EnableVisualStyles(); 12 | Application::SetCompatibleTextRenderingDefault(false); 13 | mqagui::MyForm form; 14 | Application::Run( % form); 15 | 16 | } -------------------------------------------------------------------------------- /gui/Windows/MyForm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #using 4 | 5 | 6 | namespace mqagui { 7 | 8 | 9 | using namespace System; 10 | using namespace System::Diagnostics; 11 | using namespace System::ComponentModel; 12 | using namespace System::Collections; 13 | using namespace System::Windows::Forms; 14 | using namespace System::Data; 15 | using namespace System::Drawing; 16 | 17 | /// 18 | /// Summary for MyForm 19 | /// 20 | public ref class MyForm : public System::Windows::Forms::Form { 21 | public: 22 | MyForm(void) { 23 | InitializeComponent(); 24 | // 25 | //TODO: Add the constructor code here 26 | // 27 | } 28 | 29 | protected: 30 | /// 31 | /// Clean up any resources being used. 32 | /// 33 | ~MyForm() { 34 | if (components) { 35 | delete components; 36 | } 37 | } 38 | 39 | private: 40 | System::Windows::Forms::Button^fileBtn; 41 | private: 42 | System::Windows::Forms::Button^folderBtn; 43 | protected: 44 | 45 | protected: 46 | 47 | private: 48 | System::Windows::Forms::TextBox^outputTextBox; 49 | 50 | 51 | private: 52 | /// 53 | /// Required designer variable. 54 | /// 55 | System::ComponentModel::Container ^components; 56 | 57 | #pragma region Windows Form Designer generated code 58 | 59 | /// 60 | /// Required method for Designer support - do not modify 61 | /// the contents of this method with the code editor. 62 | /// 63 | void InitializeComponent(void) { 64 | this->fileBtn = (gcnew 65 | System::Windows::Forms::Button()); 66 | this->folderBtn = (gcnew 67 | System::Windows::Forms::Button()); 68 | this->outputTextBox = (gcnew 69 | System::Windows::Forms::TextBox()); 70 | this->SuspendLayout(); 71 | // 72 | // fileBtn 73 | // 74 | this->fileBtn->Location = System::Drawing::Point(12, 12); 75 | this->fileBtn->Name = L"fileBtn"; 76 | this->fileBtn->Size = System::Drawing::Size(75, 23); 77 | this->fileBtn->TabIndex = 0; 78 | this->fileBtn->Text = L"Open file(s)"; 79 | this->fileBtn->UseVisualStyleBackColor = true; 80 | this->fileBtn->Click += gcnew 81 | System::EventHandler(this, &MyForm::fileBtn_Click); 82 | // 83 | // folderBtn 84 | // 85 | this->folderBtn->Location = System::Drawing::Point(93, 12); 86 | this->folderBtn->Name = L"folderBtn"; 87 | this->folderBtn->Size = System::Drawing::Size(75, 23); 88 | this->folderBtn->TabIndex = 1; 89 | this->folderBtn->Text = L"Open Folder"; 90 | this->folderBtn->UseVisualStyleBackColor = true; 91 | this->folderBtn->Click += gcnew 92 | System::EventHandler(this, &MyForm::folderBtn_Click); 93 | // 94 | // outputTextBox 95 | // 96 | this->outputTextBox->Anchor = static_cast(( 97 | ((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Bottom) 98 | | System::Windows::Forms::AnchorStyles::Left) 99 | | System::Windows::Forms::AnchorStyles::Right)); 100 | this->outputTextBox->BackColor = System::Drawing::SystemColors::Window; 101 | this->outputTextBox->Font = (gcnew 102 | System::Drawing::Font(L"Lucida Console", 103 | 9.75F, 104 | System::Drawing::FontStyle::Regular, 105 | System::Drawing::GraphicsUnit::Point, 106 | static_cast(0))); 107 | this->outputTextBox->Location = System::Drawing::Point(13, 42); 108 | this->outputTextBox->Multiline = true; 109 | this->outputTextBox->Name = L"outputTextBox"; 110 | this->outputTextBox->ReadOnly = true; 111 | this->outputTextBox->ScrollBars = System::Windows::Forms::ScrollBars::Both; 112 | this->outputTextBox->Size = System::Drawing::Size(762, 378); 113 | this->outputTextBox->TabIndex = 2; 114 | // 115 | // MyForm 116 | // 117 | this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); 118 | this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; 119 | this->ClientSize = System::Drawing::Size(787, 432); 120 | this->Controls->Add(this->outputTextBox); 121 | this->Controls->Add(this->folderBtn); 122 | this->Controls->Add(this->fileBtn); 123 | this->Name = L"MyForm"; 124 | this->Text = L"MyForm"; 125 | this->ResumeLayout(false); 126 | this->PerformLayout(); 127 | 128 | } 129 | 130 | private: 131 | System::Void fileBtn_Click(System::Object^sender, System::EventArgs^e) { 132 | 133 | OpenFileDialog ^ sfd = gcnew 134 | OpenFileDialog(); 135 | sfd->Multiselect = true; 136 | sfd->Filter = "Flac Audio Files|*.flac"; 137 | 138 | if (sfd->ShowDialog() != System::Windows::Forms::DialogResult::OK) 139 | return; 140 | 141 | 142 | String ^ filenames = gcnew 143 | String(""); 144 | 145 | for 146 | each(auto 147 | i 148 | in 149 | sfd->FileNames) 150 | filenames += "\"" + i + "\" "; 151 | 152 | // this->outputTextBox->AppendText(filenames + System::Environment::NewLine); 153 | this->RunMQAid(filenames); 154 | } 155 | 156 | private: 157 | System::Void folderBtn_Click(System::Object^sender, System::EventArgs^e) { 158 | FolderBrowserDialog ^ sfd = gcnew 159 | FolderBrowserDialog(); 160 | 161 | 162 | if (sfd->ShowDialog() != System::Windows::Forms::DialogResult::OK) 163 | return; 164 | 165 | 166 | this->RunMQAid("\"" + sfd->SelectedPath + "\""); 167 | 168 | } 169 | 170 | private: 171 | void RunMQAid(String^args) { 172 | Process ^ myProcess = gcnew 173 | Process; 174 | myProcess->StartInfo->FileName = "./MQA_identifier.exe"; 175 | myProcess->StartInfo->Arguments = args; 176 | myProcess->StartInfo->CreateNoWindow = true; 177 | myProcess->StartInfo->UseShellExecute = false; 178 | myProcess->StartInfo->RedirectStandardOutput = true; 179 | myProcess->Start(); 180 | myProcess->OutputDataReceived += gcnew 181 | DataReceivedEventHandler(this, &MyForm::OutputHandler); 182 | 183 | // Asynchronously read the standard output of the spawned process. 184 | // This raises OutputDataReceived events for each line of output. 185 | myProcess->BeginOutputReadLine(); 186 | //myProcess->WaitForExit(); 187 | 188 | while (!myProcess->HasExited) 189 | Application::DoEvents(); 190 | 191 | myProcess->Close(); 192 | } 193 | 194 | private: 195 | delegate void TextUpdateDelegate(String^str); 196 | 197 | private: 198 | void OutputHandler(System::Object^sender, System::Diagnostics::DataReceivedEventArgs^e) { 199 | 200 | 201 | //outputTextBox->AppendText(e->Data + System::Environment::NewLine); 202 | outputTextBox->BeginInvoke(gcnew 203 | TextUpdateDelegate(this, &MyForm::UpdateOutput), e->Data ); 204 | } 205 | 206 | 207 | private: 208 | void UpdateOutput(System::String^data) { 209 | this->outputTextBox->AppendText(data + System::Environment::NewLine); 210 | } 211 | 212 | 213 | }; 214 | } 215 | -------------------------------------------------------------------------------- /gui/Windows/mqa_gui.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {F0DBC8EE-A6AF-4158-A2DB-E6B6307B573C} 24 | v4.6.1 25 | ManagedCProj 26 | mqagui 27 | 10.0.18362.0 28 | 29 | 30 | 31 | Application 32 | true 33 | v141 34 | true 35 | Unicode 36 | 37 | 38 | Application 39 | false 40 | v141 41 | true 42 | Unicode 43 | 44 | 45 | Application 46 | true 47 | v141 48 | true 49 | Unicode 50 | 51 | 52 | Application 53 | false 54 | v141 55 | true 56 | Unicode 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | false 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | WIN32;_DEBUG;%(PreprocessorDefinitions) 93 | 94 | 95 | 96 | Windows 97 | Main 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | _DEBUG;%(PreprocessorDefinitions) 105 | 106 | 107 | 108 | Windows 109 | Main 110 | 111 | 112 | 113 | 114 | Level3 115 | WIN32;NDEBUG;%(PreprocessorDefinitions) 116 | MultiThreadedDLL 117 | true 118 | 119 | 120 | 121 | Windows 122 | Main 123 | 124 | 125 | 126 | 127 | Level3 128 | NDEBUG;%(PreprocessorDefinitions) 129 | MultiThreadedDLL 130 | true 131 | 132 | 133 | 134 | Windows 135 | Main 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | CppForm 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /gui/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) { 5 | QApplication a(argc, argv); 6 | MainWindow w; 7 | w.show(); 8 | 9 | return a.exec(); 10 | } 11 | -------------------------------------------------------------------------------- /gui/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(Q_OS_WIN) 9 | #define MQABIN "./MQA_identifier.exe" 10 | #else 11 | #define MQABIN ".//MQA_identifier" 12 | #endif 13 | 14 | 15 | MainWindow::MainWindow(QWidget *parent) : 16 | QMainWindow(parent), 17 | ui(new Ui::MainWindow) { 18 | ui->setupUi(this); 19 | QObject::connect(&process, SIGNAL(readyReadStandardOutput()), this, SLOT(got_output())); 20 | 21 | } 22 | 23 | MainWindow::~MainWindow() { 24 | delete ui; 25 | } 26 | 27 | void MainWindow::on_folderButton_clicked() { 28 | QString folder = QFileDialog::getExistingDirectory( 29 | this, 30 | tr("Select Folder"), 31 | "." 32 | ); 33 | ui->outputTextBox->clear(); 34 | ui->outputTextBox->moveCursor(QTextCursor::End); 35 | 36 | process.start(MQABIN, QStringList(folder)); 37 | process.waitForFinished(); 38 | } 39 | 40 | void MainWindow::on_FileButton_clicked() { 41 | QStringList filenames = QFileDialog::getOpenFileNames( 42 | this, 43 | tr("Selct File(s)"), 44 | ".", 45 | "flac Files (*.flac)" 46 | ); 47 | ui->outputTextBox->clear(); 48 | ui->outputTextBox->moveCursor(QTextCursor::End); 49 | 50 | process.start(MQABIN, filenames); 51 | process.waitForFinished(); 52 | 53 | } 54 | 55 | 56 | void MainWindow::got_output() { 57 | QProcess *p = reinterpret_cast(sender()); 58 | ui->outputTextBox->insertPlainText(p->readAllStandardOutput()); 59 | ui->outputTextBox->moveCursor(QTextCursor::End); 60 | } 61 | -------------------------------------------------------------------------------- /gui/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit MainWindow(QWidget *parent = nullptr); 16 | ~MainWindow(); 17 | 18 | private slots: 19 | void on_folderButton_clicked(); 20 | 21 | void on_FileButton_clicked(); 22 | 23 | void got_output(); 24 | 25 | private: 26 | Ui::MainWindow *ui; 27 | QProcess process; 28 | }; 29 | 30 | 31 | #endif // MAINWINDOW_H 32 | -------------------------------------------------------------------------------- /gui/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 568 10 | 411 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 0 20 | 0 21 | 22 | 23 | 24 | 25 | 0 26 | 300 27 | 28 | 29 | 30 | 31 | 16777215 32 | 16777215 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 0 41 | 0 42 | 43 | 44 | 45 | Scan Folder 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 0 54 | 0 55 | 56 | 57 | 58 | Scan File(s) 59 | 60 | 61 | 62 | 63 | 64 | 65 | Qt::Horizontal 66 | 67 | 68 | 69 | 40 70 | 20 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | false 79 | 80 | 81 | 82 | 83 | 84 | true 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /gui/mqa_id_gui.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2019-12-31T12:46:34 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = mqa_id_gui 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | CONFIG += c++11 26 | CONFIG += static 27 | CONFIG += static-runtime 28 | 29 | 30 | SOURCES += \ 31 | main.cpp \ 32 | mainwindow.cpp 33 | 34 | HEADERS += \ 35 | mainwindow.h 36 | 37 | FORMS += \ 38 | mainwindow.ui 39 | 40 | # Default rules for deployment. 41 | qnx: target.path = /tmp/$${TARGET}/bin 42 | else: unix:!android: target.path = /opt/$${TARGET}/bin 43 | !isEmpty(target.path): INSTALLS += target 44 | -------------------------------------------------------------------------------- /instructions.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | - For windows in search type *"cmd"* and open the application. For 4 | MacOS press **Command** + **Space** and type *"terminal"*. 5 | 6 | 7 | 8 | - type `cd Downloads` (or the location you downloaded the app) 9 | ![](https://i.imgur.com/hKOhgld.png) 10 | 11 | - `mqa_identifer.exe {paths to files or folders}...` for Windows or`./mqa_identifier` on OSX. 12 | To get the path easily simple drag n drop the file/folder to terminal. 13 | 14 | For example 15 | > ` mqa_identifer.exe "E:\Music\Deep Purple - Machine Head\03 Pictures of Home.flac" "D:\Music\1000mods - Super Van Vacation (2011) [FLAC-CD]"` 16 | 17 | Gives output: 18 | ``` 19 | ************************************************** 20 | *********** MQA flac identifier tool *********** 21 | ******** Stavros Avramidis (@purpl3F0x) ******** 22 | ** https://github.com/purpl3F0x/MQA_identifier ** 23 | ************************************************** 24 | Found 11 file for scanning... 25 | 26 | # Encoding Name 27 | 1 MQA 96K 03 Pictures of Home.flac 28 | 2 NOT MQA 01 1000mods - Road To Burn.flac 29 | 3 NOT MQA 02 1000mods - 7 Flies.flac 30 | 4 NOT MQA 03 1000mods - El Rollito.flac 31 | 5 NOT MQA 04 1000mods - Set You Free.flac 32 | 6 NOT MQA 05 1000mods - Vidage.flac 33 | 7 NOT MQA 06 1000mods - Navy In Alice.flac 34 | 8 NOT MQA 07 1000mods - Track Me.flac 35 | 9 NOT MQA 08 1000mods - Johny's.flac 36 | 10 NOT MQA 09 1000mods - Abell 1835.flac 37 | 11 NOT MQA 10 1000mods - Super Van Vacation.flac 38 | 39 | ************************************************** 40 | Scanned 11 files 41 | Found 1 MQA files 42 | 43 | Process finished with exit code 0 44 | ``` -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.cc 3 | * @author Stavros Avramidis (@purpl3F0x) 4 | * @date 16/12/2019 5 | * @copyright 2019 Stavros Avramidis under Apache 2.0 License 6 | */ 7 | 8 | #ifdef __ANDROID__ 9 | #include 10 | #else 11 | #include 12 | #endif 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "mqa_identifier.h" 21 | #include 22 | 23 | #ifdef __ANDROID__ 24 | namespace fs = boost::filesystem; 25 | #else 26 | namespace fs = std::filesystem; // for old VS use namespace fs = std::experimental::filesystem; 27 | #endif 28 | 29 | auto getSampleRateString(const uint32_t fs) { 30 | std::stringstream ss; 31 | if (fs <= 768000) 32 | ss << fs / 1000. << "K"; 33 | else if (fs % 44100 == 0) 34 | ss << "DSD" << fs / 44100; 35 | else 36 | ss << "DSD" << fs / 48000 << "x48"; 37 | 38 | return ss.str(); 39 | } 40 | 41 | 42 | /** 43 | * @short Recursively scan a directory for .flac files 44 | * @param curDir directory to scan 45 | * @param files vector to add the file paths 46 | */ 47 | void recursiveScan(const fs::directory_entry &curDir, std::vector &files) { 48 | for (const auto &entry : fs::directory_iterator(curDir)) { 49 | if (fs::is_regular_file(entry) && (fs::path(entry).extension() == ".flac")) 50 | files.push_back(entry.path().string()); 51 | 52 | else if (fs::is_directory(entry)) 53 | recursiveScan(entry, files); 54 | } 55 | } 56 | 57 | 58 | int main(int argc, char *argv[]) { 59 | 60 | std::vector files; 61 | bool add_mqaencoder = false; 62 | bool rewrite_founded_tags = false; 63 | 64 | if (argc == 1) { 65 | std::cout << "HINT: To use the tool provide files and/or directories as program arguments\n" \ 66 | " If yor want add tags use flag --add-mqaencoder and -rw if yor want rewrite existing ones.\n\n"; 67 | } 68 | 69 | for (auto argn = 1; argn < argc; argn++) { 70 | 71 | if (fs::is_directory(argv[argn])) 72 | recursiveScan(fs::directory_entry(argv[argn]), files); 73 | 74 | else if (fs::is_regular_file(argv[argn])) { 75 | if (fs::path(argv[argn]).extension() == ".flac") 76 | files.emplace_back(argv[argn]); 77 | else 78 | std::cerr << argv[argn] << " not .flac file\n"; 79 | } 80 | if (std::string(argv[argn]) == "--add-mqaencoder") 81 | add_mqaencoder = true; 82 | 83 | if (add_mqaencoder && std::string(argv[argn]) == "-rw") 84 | rewrite_founded_tags = true; 85 | } 86 | 87 | // Flush error buffer (just to make sure our print is pretty and no error line get in between) 88 | std::cerr << std::flush; 89 | 90 | // Let's do some printing 91 | std::cout << "**************************************************\n"; 92 | std::cout << "*********** MQA flac identifier tool ***********\n"; 93 | std::cout << "******** Stavros Avramidis (@purpl3F0x) ********\n"; 94 | std::cout << "** https://github.com/purpl3F0x/MQA_identifier **\n"; 95 | std::cout << "**************************************************\n"; 96 | 97 | std::cout << "Found " << files.size() << " file for scanning...\n\n"; 98 | 99 | 100 | // Start parsing the files 101 | size_t count = 0; 102 | size_t mqa_files = 0; 103 | size_t added_tags = 0; 104 | std::cout << " #\tEncoding\t\tName\n"; 105 | for (const auto &file : files) 106 | { 107 | std::cout << std::setw(3) << ++count << "\t"; 108 | 109 | //auto id = MQA_identifier(file); 110 | MQA_identifier id(file); 111 | if (id.detect()) 112 | { 113 | std::cout << "MQA " << (id.isMQAStudio() ? "Studio " : "") 114 | << getSampleRateString(id.originalSampleRate()) << " \t" 115 | << fs::path(file).filename().string() << "\n"; 116 | mqa_files++; 117 | 118 | // add tags if use flag --add-mqaencoder and -rw if yor want rewrite existing ones 119 | if (add_mqaencoder) 120 | { 121 | ////////////////////////////////////////// 122 | // read a file using FLAC::Metadata::Chain class 123 | FLAC::Metadata::Chain chain; 124 | chain.read(file.c_str()); 125 | // now, find vorbis comment block and make changes in it 126 | { 127 | FLAC::Metadata::Iterator iterator; 128 | iterator.init(chain); 129 | // find vorbis comment block 130 | FLAC::Metadata::VorbisComment* vcBlock = 0; 131 | do { 132 | FLAC::Metadata::Prototype* block = iterator.get_block(); 133 | if (block->get_type() == FLAC__METADATA_TYPE_VORBIS_COMMENT) { 134 | vcBlock = (FLAC::Metadata::VorbisComment*) block; 135 | break; 136 | } 137 | } while (iterator.next()); 138 | // if not found, create a new one 139 | if (vcBlock == 0) { 140 | // create a new block 141 | vcBlock = new FLAC::Metadata::VorbisComment(); 142 | // move iterator to the end 143 | while (iterator.next()) { 144 | } 145 | // insert a new block at the end 146 | if (!iterator.insert_block_after(vcBlock)) { 147 | delete vcBlock; 148 | } 149 | } 150 | //if the tags (ENCODER,MQAENCODER,ORIGINALSAMPLERATE) is found, we simply delete it 151 | int ENCODERs = -1; 152 | int MQAENCODERs = -1; 153 | int ORIGINALSAMPLERATEs = -1; 154 | for (int i = 0; i < vcBlock->get_num_comments(); ++i) 155 | { 156 | int ENCODER = vcBlock->find_entry_from(i, "ENCODER"); 157 | if (ENCODER > -1) 158 | ENCODERs = ENCODER; 159 | } 160 | if (ENCODERs > -1 && rewrite_founded_tags) 161 | { 162 | vcBlock->delete_comment(ENCODERs); 163 | } 164 | for (int i = 0; i < vcBlock->get_num_comments(); ++i) 165 | { 166 | int MQAENCODER = vcBlock->find_entry_from(i, "MQAENCODER"); 167 | if (MQAENCODER > -1) 168 | MQAENCODERs = MQAENCODER; 169 | } 170 | if (MQAENCODERs > -1 && rewrite_founded_tags) 171 | { 172 | vcBlock->delete_comment(MQAENCODERs); 173 | } 174 | for (int i = 0; i < vcBlock->get_num_comments(); ++i) 175 | { 176 | int ORIGINALSAMPLERATE = vcBlock->find_entry_from(i, "ORIGINALSAMPLERATE"); 177 | if (ORIGINALSAMPLERATE > -1) 178 | ORIGINALSAMPLERATEs = ORIGINALSAMPLERATE; 179 | } 180 | if (ORIGINALSAMPLERATEs > -1 && rewrite_founded_tags) 181 | { 182 | vcBlock->delete_comment(ORIGINALSAMPLERATEs); 183 | } 184 | //add tags (ENCODER,MQAENCODER,ORIGINALSAMPLERATE) to the flac file if not found or if -rw flag rewrite existing 185 | std::string OrigSamp = std::to_string(id.originalSampleRate()); 186 | if(ENCODERs == -1 || ENCODERs > -1 && rewrite_founded_tags) 187 | vcBlock->append_comment(FLAC::Metadata::VorbisComment::Entry("ENCODER", "MQAEncode v1.1, 2.3.3+800 (a505918), F8EC1703-7616-45E5-B81E-D60821434062, Dec 01 2017 22:19:30")); 188 | if (MQAENCODERs == -1 || MQAENCODERs > -1 && rewrite_founded_tags) 189 | vcBlock->append_comment(FLAC::Metadata::VorbisComment::Entry("MQAENCODER", "MQAEncode v1.1, 2.3.3+800 (a505918), F8EC1703-7616-45E5-B81E-D60821434062, Dec 01 2017 22:19:30")); 190 | if (ORIGINALSAMPLERATEs == -1 || ORIGINALSAMPLERATEs > -1 && rewrite_founded_tags) 191 | vcBlock->append_comment(FLAC::Metadata::VorbisComment::Entry("ORIGINALSAMPLERATE", OrigSamp.c_str())); 192 | 193 | if (ENCODERs == -1 || ENCODERs > -1 && rewrite_founded_tags 194 | || MQAENCODERs == -1 || MQAENCODERs > -1 && rewrite_founded_tags 195 | || ORIGINALSAMPLERATEs == -1 || ORIGINALSAMPLERATEs > -1 && rewrite_founded_tags) 196 | added_tags += 1; 197 | } 198 | chain.write();//save flac file 199 | } 200 | 201 | } 202 | else 203 | std::cout << "NOT MQA \t" << fs::path(file).filename().string() << "\n"; 204 | 205 | } 206 | 207 | std::cout << "\n**************************************************\n"; 208 | std::cout << "Scanned " << files.size() << " files\n"; 209 | std::cout << "Found " << mqa_files << " MQA files\n"; 210 | std::cout << "Added " << added_tags << " tags for MQA files\n"; 211 | } 212 | -------------------------------------------------------------------------------- /mqa_identifier.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mqa_identifier.h 3 | * @author Stavros Avramidis (@purpl3F0x) 4 | * @date 16/12/2019 5 | * @copyright 2019 2.0 License 6 | * @short Library to Identify MQA encoding 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | 21 | /** 22 | * Returns original Sample rate (in Hz) from waveform bytecode. 23 | * @param c 4bit bytecode 24 | * @return 25 | */ 26 | uint32_t OriginalSampleRateDecoder(unsigned c) { 27 | /* 28 | * If LSB is 0 then base is 44100 else 48000 29 | * 3 MSB need to be rotated and raised to the power of 2 (so 1, 2, 4, 8, ...) 30 | * output is base * multiplier 31 | */ 32 | const uint32_t base = (c & 1u) ? 48000 : 44100; 33 | 34 | uint32_t multiplier = 1u << (((c >> 3u) & 1u) | (((c >> 2u) & 1u) << 1u) | (((c >> 1u) & 1u) << 2u)); 35 | // Double for DSD 36 | if (multiplier > 16) multiplier *= 2; 37 | 38 | return base * multiplier; 39 | } 40 | 41 | 42 | class MQA_identifier { 43 | private: 44 | class MyDecoder : public FLAC::Decoder::File { 45 | public: 46 | uint32_t sample_rate = 0; 47 | uint32_t channels = 0; 48 | uint32_t bps = 0; 49 | FLAC__uint64 decoded_samples = 0; 50 | std::vector> samples; 51 | std::string mqa_encoder; 52 | uint32_t original_sample_rate = 0; 53 | 54 | 55 | explicit MyDecoder(std::string file) : FLAC::Decoder::File(), file_(std::move(file)) {}; 56 | 57 | ::FLAC__StreamDecoderInitStatus decode(); 58 | 59 | protected: 60 | std::string file_; 61 | using FLAC::Decoder::File::init; 62 | virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, 63 | const FLAC__int32 *const buffer[]) override; 64 | void metadata_callback(const ::FLAC__StreamMetadata *metadata) override; 65 | void error_callback(::FLAC__StreamDecoderErrorStatus status) override; 66 | 67 | private: 68 | MyDecoder(const MyDecoder &); 69 | MyDecoder &operator=(const MyDecoder &); 70 | }; 71 | 72 | 73 | std::string file_; 74 | MyDecoder decoder; 75 | bool isMQA_; 76 | bool isMQAStudio_ = false; 77 | 78 | public: 79 | explicit MQA_identifier(std::string file) : file_(std::move(file)), decoder(file_), isMQA_(false) {} 80 | 81 | 82 | bool detect(); 83 | 84 | [[nodiscard]] std::string getMQA_encoder() const noexcept; 85 | [[nodiscard]] uint32_t originalSampleRate() const noexcept; 86 | [[nodiscard]] bool isMQA() const noexcept; 87 | [[nodiscard]] bool isMQAStudio() const noexcept; 88 | [[nodiscard]] std::string filename() const noexcept; 89 | }; 90 | 91 | 92 | ::FLAC__StreamDecoderWriteStatus MQA_identifier::MyDecoder::write_callback(const ::FLAC__Frame *frame, 93 | const FLAC__int32 *const buffer[]) { 94 | 95 | if (channels != 2 || (bps != 16 && bps != 24)) { 96 | std::cerr << "ERROR: this tool only supports 16bit/24bit stereo streams\n"; 97 | return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; 98 | } 99 | 100 | /* increase number of read samples */ 101 | this->decoded_samples += frame->header.blocksize; 102 | 103 | /* write decoded PCM samples */ 104 | for (size_t i = 0; i < frame->header.blocksize; i++) 105 | this->samples.push_back(std::array < const FLAC__int32, 2 > {buffer[0][i], buffer[1][i]}); 106 | 107 | return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; 108 | } 109 | 110 | 111 | void MQA_identifier::MyDecoder::metadata_callback(const ::FLAC__StreamMetadata *metadata) { 112 | 113 | if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { 114 | this->sample_rate = metadata->data.stream_info.sample_rate; 115 | this->channels = metadata->data.stream_info.channels; 116 | this->bps = metadata->data.stream_info.bits_per_sample; 117 | 118 | } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { 119 | for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { 120 | const auto comment = reinterpret_cast(metadata->data.vorbis_comment.comments[i].entry); 121 | 122 | if (std::strncmp("MQAENCODER", comment, 10) == 0) 123 | this->mqa_encoder = 124 | std::string(comment + 10, comment + metadata->data.vorbis_comment.comments[i].length); 125 | } 126 | } 127 | 128 | } 129 | 130 | 131 | void MQA_identifier::MyDecoder::error_callback(::FLAC__StreamDecoderErrorStatus status) { 132 | std::cerr << "Got error callback: " << FLAC__StreamDecoderErrorStatusString[status] << "\n"; 133 | } 134 | 135 | 136 | ::FLAC__StreamDecoderInitStatus MQA_identifier::MyDecoder::decode() { 137 | bool ok = true; 138 | 139 | (void) this->set_md5_checking(true); 140 | (void) this->set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT); /* instruct decoder to parse vorbis_comments */ 141 | FLAC__StreamDecoderInitStatus init_status = this->init(this->file_); 142 | 143 | if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { 144 | std::cerr << "ERROR: initializing decoder: " << FLAC__StreamDecoderInitStatusString[init_status] << "\n"; 145 | ok = false; 146 | } 147 | 148 | this->process_until_end_of_metadata(); 149 | 150 | // pre-allocate samples vector 151 | this->samples.reserve(this->sample_rate * 3); 152 | 153 | while (this->decoded_samples < this->sample_rate * 3 /* read only 3 first seconds */ ) 154 | ok = this->process_single(); 155 | 156 | 157 | if (!ok) { 158 | std::cerr << "decoding FAILED\n"; 159 | std::cerr << this->get_state().resolved_as_cstring(*this); 160 | } 161 | return init_status; 162 | } 163 | 164 | 165 | bool MQA_identifier::detect() { 166 | this->decoder.decode(); 167 | //check the bufer three times from the P,P+1,P+2 value 168 | uint64_t buffer = 0; 169 | uint64_t buffer1 = 0; 170 | uint64_t buffer2 = 0; 171 | const auto pos = (this->decoder.bps - 16u); // aim for 16th bit 172 | 173 | for (const auto &s: this->decoder.samples) { 174 | buffer |= ((static_cast(s[0]) ^ static_cast(s[1])) >> pos ) & 1u; 175 | buffer1 |= ((static_cast(s[0]) ^ static_cast(s[1])) >> pos + 1) & 1u; 176 | buffer2 |= ((static_cast(s[0]) ^ static_cast(s[1])) >> pos + 2) & 1u; 177 | 178 | if (buffer == 0xbe0498c88) { // MQA magic word 179 | this->isMQA_ = true; 180 | // Get Original Sample Rate 181 | uint8_t orsf = 0; 182 | for (auto m = 3u; m < 7; m++) { // TODO: this need fix (orsf is 5bits) 183 | auto cur = *(&s + m); 184 | auto j = ((static_cast(cur[0]) ^ static_cast(cur[1])) >> pos) & 1u; 185 | orsf |= j << (6u - m); 186 | } 187 | this->decoder.original_sample_rate = OriginalSampleRateDecoder(orsf); 188 | 189 | // Get MQA Studio 190 | uint8_t provenance = 0u; 191 | for (auto m = 29u; m < 34; m++) { 192 | auto cur = *(&s + m); 193 | auto j = ((static_cast(cur[0]) ^ static_cast(cur[1])) >> pos) & 1u; 194 | provenance |= j << (33u - m); 195 | } 196 | this->isMQAStudio_ = provenance > 8; 197 | 198 | // We are done return true 199 | return true; 200 | } else 201 | if (buffer1 == 0xbe0498c88) { // MQA magic word 202 | this->isMQA_ = true; 203 | // Get Original Sample Rate 204 | uint8_t orsf = 0; 205 | for (auto m = 3u; m < 7; m++) { // TODO: this need fix (orsf is 5bits) 206 | auto cur = *(&s + m); 207 | auto j = ((static_cast(cur[0]) ^ static_cast(cur[1])) >> pos + 1) & 1u; 208 | orsf |= j << (6u - m); 209 | } 210 | this->decoder.original_sample_rate = OriginalSampleRateDecoder(orsf); 211 | 212 | // Get MQA Studio 213 | uint8_t provenance = 0u; 214 | for (auto m = 29u; m < 34; m++) { 215 | auto cur = *(&s + m); 216 | auto j = ((static_cast(cur[0]) ^ static_cast(cur[1])) >> pos + 1) & 1u; 217 | provenance |= j << (33u - m); 218 | } 219 | this->isMQAStudio_ = provenance > 8; 220 | 221 | // We are done return true 222 | return true; 223 | } else 224 | if (buffer2 == 0xbe0498c88) { // MQA magic word 225 | this->isMQA_ = true; 226 | // Get Original Sample Rate 227 | uint8_t orsf = 0; 228 | for (auto m = 3u; m < 7; m++) { // TODO: this need fix (orsf is 5bits) 229 | auto cur = *(&s + m); 230 | auto j = ((static_cast(cur[0]) ^ static_cast(cur[1])) >> pos + 2) & 1u; 231 | orsf |= j << (6u - m); 232 | } 233 | this->decoder.original_sample_rate = OriginalSampleRateDecoder(orsf); 234 | 235 | // Get MQA Studio 236 | uint8_t provenance = 0u; 237 | for (auto m = 29u; m < 34; m++) { 238 | auto cur = *(&s + m); 239 | auto j = ((static_cast(cur[0]) ^ static_cast(cur[1])) >> pos + 2) & 1u; 240 | provenance |= j << (33u - m); 241 | } 242 | this->isMQAStudio_ = provenance > 8; 243 | 244 | // We are done return true 245 | return true; 246 | 247 | } else 248 | buffer = (buffer << 1u) & 0xFFFFFFFFFu; 249 | buffer1 = (buffer1 << 1u) & 0xFFFFFFFFFu; 250 | buffer2 = (buffer2 << 1u) & 0xFFFFFFFFFu; 251 | } 252 | return false; 253 | } 254 | 255 | 256 | std::string MQA_identifier::getMQA_encoder() const noexcept { 257 | return this->decoder.mqa_encoder; 258 | } 259 | 260 | 261 | uint32_t MQA_identifier::originalSampleRate() const noexcept { 262 | return this->decoder.original_sample_rate; 263 | } 264 | 265 | 266 | bool MQA_identifier::isMQA() const noexcept { 267 | return this->isMQA_; 268 | } 269 | 270 | 271 | bool MQA_identifier::isMQAStudio() const noexcept { 272 | return this->isMQAStudio_; 273 | } 274 | 275 | 276 | std::string MQA_identifier::filename() const noexcept { 277 | return this->file_; 278 | } 279 | --------------------------------------------------------------------------------