PageRenderTime 308ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/source/audio_quality_ident.cpp

https://github.com/snowlesswinter/audio_quality_identification
C++ | 278 lines | 219 code | 46 blank | 13 comment | 33 complexity | e10f982831a65b005390d5b58bcf17fc MD5 | raw file
  1. #include "audio_quality_ident.h"
  2. #include <fstream>
  3. #include <vector>
  4. #include <boost/filesystem.hpp>
  5. #include <windows.h>
  6. #include "third_party/multimedia_core/player_interface.h"
  7. #include "third_party/multimedia_core/audio_information_extracter_interface.h"
  8. #include "third_party/multimedia_core/audio_spectrum_extracter_interface.h"
  9. #include "third_party/multimedia_core/common/unknown_impl.h"
  10. using std::unique_ptr;
  11. using std::wstring;
  12. using std::ifstream;
  13. using std::vector;
  14. using std::max;
  15. using std::shared_ptr;
  16. using boost::filesystem::path;
  17. using base::CancellationFlag;
  18. namespace {
  19. typedef unique_ptr<void, void (__stdcall*)(void*)> FuncHostType;
  20. typedef HRESULT (__stdcall* MMCoreFactoryProc)(ICorePlayer** , void*);
  21. typedef HRESULT (__stdcall* AudioInfoExtrFactoryProc)(
  22. IAudioInformationExtracter**);
  23. bool LoadMultiMediaCoreFunctions(
  24. FuncHostType* funcHost, scoped_refptr<ICorePlayer>* mediaInfo,
  25. scoped_refptr<IAudioInformationExtracter>* spectrumSource)
  26. {
  27. // We need multimedia core to help accomplish the work.
  28. unique_ptr<wchar_t[]> buf(new wchar_t[MAX_PATH]);
  29. GetModuleFileName(NULL, buf.get(), MAX_PATH);
  30. path p(buf.get());
  31. wstring location = p.remove_filename().wstring() + L"/kgplayer.dll";
  32. FuncHostType dll(LoadLibrary(location.c_str()),
  33. reinterpret_cast<void (__stdcall*)(void*)>(FreeLibrary));
  34. if (!dll)
  35. return false;
  36. // Export all necessary functions.
  37. MMCoreFactoryProc mmCoreFactoryProc =
  38. reinterpret_cast<MMCoreFactoryProc>(
  39. GetProcAddress(reinterpret_cast<HMODULE>(dll.get()),
  40. reinterpret_cast<char*>(3)));
  41. if (!mmCoreFactoryProc)
  42. return false;
  43. AudioInfoExtrFactoryProc audioInfoExtrFactoryProc =
  44. reinterpret_cast<AudioInfoExtrFactoryProc>(
  45. GetProcAddress(reinterpret_cast<HMODULE>(dll.get()),
  46. reinterpret_cast<char*>(7)));
  47. if (!audioInfoExtrFactoryProc)
  48. return false;
  49. scoped_refptr<ICorePlayer> interface1;
  50. HRESULT r = mmCoreFactoryProc(reinterpret_cast<ICorePlayer**>(&interface1),
  51. NULL);
  52. if (FAILED(r))
  53. return false;
  54. scoped_refptr<IAudioInformationExtracter> interface2;
  55. r = audioInfoExtrFactoryProc(
  56. reinterpret_cast<IAudioInformationExtracter**>(&interface2));
  57. if (FAILED(r))
  58. return false;
  59. funcHost->swap(dll);
  60. *mediaInfo = interface1;
  61. *spectrumSource = interface2;
  62. return true;
  63. }
  64. class PassString : public kugou::CUnknown, public IPassString
  65. {
  66. public:
  67. PassString(wstring* str) : kugou::CUnknown(NULL, NULL), str_(str) {}
  68. DELEGATE_IUNKNOWN;
  69. virtual const wchar_t* __stdcall GetContent() { return str_->c_str(); };
  70. virtual void __stdcall SetContent(const wchar_t* content)
  71. {
  72. *str_ = content;
  73. }
  74. private:
  75. wstring* str_;
  76. };
  77. class MySpectrumReceiver : public kugou::CUnknown, public IAudioSpectrumReceiver
  78. {
  79. public:
  80. MySpectrumReceiver(int sampleRate,
  81. const shared_ptr<base::CancellationFlag>& cancelFlag);
  82. virtual ~MySpectrumReceiver() {}
  83. DELEGATE_IUNKNOWN;
  84. virtual bool __stdcall Receive(IAudioSpectrum* samples);
  85. int GetAverageFreq()
  86. {
  87. vector<int> freqs = freqs_;
  88. // Exclude some data at the end(about 10 sec).
  89. int toRemove = 10;
  90. while (freqs.size() && (toRemove-- >= 0))
  91. freqs.pop_back();
  92. double sum = 0.0;
  93. for (auto i = freqs.begin(), e = freqs.end(); i != e; ++i)
  94. sum += *i;
  95. return freqs.empty() ? 0 : static_cast<int>(sum / freqs.size());
  96. }
  97. private:
  98. int receiveCount_;
  99. unique_ptr<double[]> power_;
  100. vector<int> freqs_;
  101. int sampleRate_;
  102. std::shared_ptr<base::CancellationFlag> cancelFlag_;
  103. };
  104. MySpectrumReceiver::MySpectrumReceiver(
  105. int sampleRate, const shared_ptr<base::CancellationFlag>& cancelFlag)
  106. : kugou::CUnknown(NULL, NULL)
  107. , receiveCount_(1)
  108. , power_()
  109. , freqs_()
  110. , sampleRate_(sampleRate)
  111. , cancelFlag_(cancelFlag)
  112. {
  113. }
  114. bool MySpectrumReceiver::Receive(IAudioSpectrum* spectrum)
  115. {
  116. if (cancelFlag_ && cancelFlag_->IsSet())
  117. return false;
  118. int* ptr = spectrum->GetFrequencies();
  119. const int amount = spectrum->GetFrequenciesCount();
  120. if (amount <= 1)
  121. return false;
  122. if (!power_.get()) {
  123. power_.reset(new double[amount]);
  124. for (int i = 0; i < amount; ++i)
  125. power_[i] = 0.0;
  126. }
  127. for (int i = 0; i < amount; ++i) {
  128. const double d = 10 * log(static_cast<double>(ptr[i]));
  129. // if (d > 0.0)
  130. // power_[i] += d;
  131. power_[i] = max(power_[i], d);
  132. }
  133. const int checkPointInterval = 20;
  134. const bool checkPoint = !!(receiveCount_++ % checkPointInterval);
  135. if (!checkPoint) {
  136. // for (int i = amount - 1; i >= 0; --i)
  137. // power_[i] /= 20;
  138. double prev = power_[amount - 1];
  139. int cutOffFreqIndex = 0;
  140. for (int i = amount - 2; i >= 0; --i) {
  141. double diff = abs(prev - power_[i]);
  142. if (diff > 3.0) {
  143. cutOffFreqIndex = i;
  144. break;
  145. }
  146. prev = power_[i];
  147. }
  148. int freq = (cutOffFreqIndex + 1) * (sampleRate_ / 2) / (amount - 1);
  149. freqs_.push_back(freq);
  150. for (int i = 0; i < amount; ++i)
  151. power_[i] = 0.0;
  152. }
  153. return true;
  154. }
  155. }
  156. AudioQualityIdent::AudioQualityIdent(
  157. const shared_ptr<CancellationFlag>& cancelFlag)
  158. : funcHost_(NULL, reinterpret_cast<void (__stdcall*)(void*)>(FreeLibrary))
  159. , mediaInfo_()
  160. , spectrumSource_()
  161. , cancelFlag_(cancelFlag)
  162. {
  163. }
  164. AudioQualityIdent::~AudioQualityIdent()
  165. {
  166. }
  167. bool AudioQualityIdent::Init()
  168. {
  169. return LoadMultiMediaCoreFunctions(&funcHost_, &mediaInfo_,
  170. &spectrumSource_);
  171. }
  172. bool AudioQualityIdent::Identify(const wstring& fullPathName, int* sampleRate,
  173. int* bitrate, int* channels, int* cutoff,
  174. int64* duration, wstring* format)
  175. {
  176. assert(mediaInfo_);
  177. assert(spectrumSource_);
  178. assert(sampleRate);
  179. assert(bitrate);
  180. assert(channels);
  181. assert(cutoff);
  182. assert(duration);
  183. assert(format);
  184. if (!mediaInfo_ || !spectrumSource_ || !sampleRate || !bitrate ||
  185. !channels || !cutoff || !duration || !format)
  186. return false;
  187. const wchar_t* unknownFormat = L"[Unknown]";
  188. *sampleRate = 0;
  189. *bitrate = 0;
  190. *channels = 0;
  191. *cutoff = 0;
  192. *duration = 0;
  193. *format = unknownFormat;
  194. ifstream audioFile(fullPathName.c_str(), std::ios::binary);
  195. audioFile.seekg(0, std::ios::end);
  196. int fileSize = static_cast<int>(audioFile.tellg());
  197. if (fileSize > 0) {
  198. unique_ptr<int8[]> buf(new int8[fileSize]);
  199. audioFile.seekg(0);
  200. audioFile.read(reinterpret_cast<char*>(buf.get()), fileSize);
  201. // Retrieve all necessary media information.
  202. PassString ps(format);
  203. if (!mediaInfo_->GetInstantMediaInfo(buf.get(), fileSize, duration,
  204. bitrate, &ps, NULL, sampleRate,
  205. channels, NULL)) {
  206. // Return true and mark this file as an unrecognized format.
  207. return true;
  208. }
  209. if (format->empty())
  210. *format = unknownFormat;
  211. // Retrieve the average cutoff frequency.
  212. scoped_refptr<MySpectrumReceiver> receiver(
  213. new MySpectrumReceiver(*sampleRate, cancelFlag_));
  214. if (!spectrumSource_->Open(fullPathName.c_str())) {
  215. // Return true and mark this file as an unrecognized format.
  216. return true;
  217. }
  218. // Exclude the first 10 sec data.
  219. if (!spectrumSource_->Seek(10.0)) {
  220. // Return true so that we know it is a short-duration music.
  221. return true;
  222. }
  223. int channel = 0;
  224. IAudioSpectrumReceiver* r = receiver.get();
  225. spectrumSource_->ExtractSpectrum(&channel, 1, 1024, &r);
  226. if (cancelFlag_ && cancelFlag_->IsSet())
  227. return false;
  228. *cutoff = receiver->GetAverageFreq();
  229. return true;
  230. }
  231. return false;
  232. }