1
0

MP3Info.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. #include "main.h"
  2. #include "LAMEInfo.h"
  3. #include "AACFrame.h"
  4. #include "config.h"
  5. #include "../winamp/wa_ipc.h"
  6. #include <Richedit.h>
  7. #include "api__in_mp3.h"
  8. #include "FactoryHelper.h"
  9. #include <shlwapi.h>
  10. #include <strsafe.h>
  11. #include <foundation/error.h>
  12. int fixAACCBRbitrate(int br);
  13. #if 0
  14. /*
  15. */
  16. /*
  17. int MP3Info::remove_id3v1()
  18. {
  19. char temp[3] = {0, 0, 0};
  20. DWORD x;
  21. int err = 0;
  22. HANDLE hFile = CreateFile(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0,
  23. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  24. if (hFile == INVALID_HANDLE_VALUE)
  25. {
  26. return 0; //1;
  27. }
  28. SetFilePointer(hFile, -128, NULL, FILE_END);
  29. ReadFile(hFile, temp, 3, &x, NULL);
  30. if (!memcmp(temp, "TAG", 3))
  31. {
  32. SetFilePointer(hFile, -128, NULL, FILE_END);
  33. if (!SetEndOfFile(hFile))
  34. {
  35. err = 1;
  36. }
  37. }
  38. CloseHandle(hFile);
  39. if (!err) fbuf[0] = 0;
  40. return err;
  41. return 0;
  42. }
  43. */
  44. /*
  45. da_tag.SetUnsync(false);
  46. da_tag.SetExtendedHeader(true);
  47. da_tag.SetCompression(false);
  48. da_tag.SetPadding(true);
  49. */
  50. #endif
  51. int FindAverageAACBitrate(unsigned __int8 *data, int sizeBytes)
  52. {
  53. AACFrame aacFrame;
  54. aacFrame.ReadBuffer(data);
  55. if (aacFrame.OK())
  56. {
  57. int aac_frame_length = aacFrame.frameLength;
  58. int no_rawdb = aacFrame.numDataBlocks;
  59. int fc_tot = aac_frame_length;
  60. int fc_cnt = no_rawdb + 1;
  61. unsigned char *aa = data + aac_frame_length;
  62. int tt = sizeBytes - aac_frame_length;
  63. while (tt >= 8)
  64. {
  65. AACFrame nextFrame;
  66. nextFrame.ReadBuffer(aa);
  67. if (!nextFrame.OK()) break; // error
  68. int fcaac_frame_length = nextFrame.frameLength;
  69. int fcno_rawdb = nextFrame.numDataBlocks;
  70. fc_cnt += fcno_rawdb + 1;
  71. fc_tot += fcaac_frame_length;
  72. aa += fcaac_frame_length;
  73. tt -= fcaac_frame_length;
  74. }
  75. int avg_framesize = fc_tot / (fc_cnt ? fc_cnt : 1);
  76. return fixAACCBRbitrate(MulDiv(avg_framesize * 8, aacFrame.GetSampleRate(), 1024 * 1000));
  77. }
  78. return 0;
  79. }
  80. static bool ScanForFrame(CGioFile *file, int *bytesRead)
  81. {
  82. unsigned char buffer[512] = {0}; /* don't want to read too much, since most MP3's start at 0 */
  83. int buflen = 0;
  84. int checked=0;
  85. if (file->Peek(buffer, sizeof(buffer), &buflen) != NErr_Success)
  86. return false;
  87. unsigned char *b = buffer;
  88. while (buflen >= 4)
  89. {
  90. MPEGFrame frame1;
  91. frame1.ReadBuffer(b);
  92. if (frame1.IsSync())
  93. {
  94. if (checked)
  95. file->Read(buffer, checked, &buflen);
  96. *bytesRead=checked;
  97. return true;
  98. }
  99. checked++;
  100. buflen--;
  101. b++;
  102. }
  103. if (checked)
  104. file->Read(buffer, checked, &buflen);
  105. *bytesRead=checked;
  106. return false;
  107. }
  108. static bool mp3sync(CGioFile *file)
  109. {
  110. unsigned char buffer[1448 + 4] = {0}; /* large enough for one max-size frame and the header of the second */
  111. int buflen = 0;
  112. static const unsigned long gdwHeaderSyncMask = 0xfffe0c00L;
  113. unsigned long ulHdr1=0;
  114. unsigned long ulHdr2=0;
  115. int bytesChecked=0;
  116. while (bytesChecked<32768)
  117. {
  118. int bytesRead=0;
  119. if (ScanForFrame(file, &bytesRead))
  120. {
  121. if (file->Peek(buffer, sizeof(buffer), &buflen) != NErr_Success)
  122. return false;
  123. if (buflen >= 4)
  124. {
  125. MPEGFrame frame1;
  126. frame1.ReadBuffer(buffer);
  127. if (frame1.IsSync())
  128. {
  129. ulHdr1 = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
  130. int framelength= frame1.FrameSize();
  131. if (buflen >= (framelength+4))
  132. {
  133. unsigned char *b = buffer + framelength;
  134. buflen -= frame1.FrameSize();
  135. MPEGFrame frame2;
  136. frame2.ReadBuffer(b);
  137. ulHdr2 = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
  138. if (!((ulHdr1 ^ ulHdr2) & gdwHeaderSyncMask) && frame2.IsSync())
  139. return true;
  140. else
  141. {
  142. }
  143. }
  144. }
  145. file->Read(buffer, 1, &buflen);
  146. bytesChecked++;
  147. }
  148. }
  149. else if (file->EndOf())
  150. return 0;
  151. bytesChecked+=bytesRead;
  152. }
  153. return false;
  154. }
  155. static const wchar_t *GetMPEGVersionString(int mpegVersion)
  156. {
  157. switch (mpegVersion)
  158. {
  159. case MPEGFrame::MPEG1:
  160. return L"MPEG-1";
  161. case MPEGFrame::MPEG2:
  162. return L"MPEG-2";
  163. case MPEGFrame::MPEG2_5:
  164. return L"MPEG-2.5";
  165. default:
  166. static wchar_t temp[64];
  167. return WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,temp,64);
  168. }
  169. }
  170. static const wchar_t *GetEmphasisString(int emphasis)
  171. {
  172. static wchar_t tempE[32];
  173. switch (emphasis)
  174. {
  175. case 0:
  176. return WASABI_API_LNGSTRINGW_BUF(IDS_NONE,tempE,32);
  177. case 1:
  178. return WASABI_API_LNGSTRINGW_BUF(IDS_50_15_MICROSEC,tempE,32);
  179. case 2:
  180. return WASABI_API_LNGSTRINGW_BUF(IDS_INVALID,tempE,32);
  181. case 3:
  182. return L"CITT j.17";
  183. default:
  184. return WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,tempE,32);
  185. }
  186. }
  187. static const wchar_t *GetChannelModeString(int channelMode)
  188. {
  189. static wchar_t tempM[32];
  190. switch (channelMode)
  191. {
  192. case 0:
  193. return WASABI_API_LNGSTRINGW_BUF(IDS_STEREO,tempM,32);
  194. case 1:
  195. return WASABI_API_LNGSTRINGW_BUF(IDS_JOINT_STEREO,tempM,32);
  196. case 2:
  197. return WASABI_API_LNGSTRINGW_BUF(IDS_2_CHANNEL,tempM,32);
  198. case 3:
  199. return WASABI_API_LNGSTRINGW_BUF(IDS_MONO,tempM,32);
  200. default:
  201. return WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,tempM,32);
  202. }
  203. }
  204. #define INFO_READ_SIZE 32768
  205. void GetFileDescription(const wchar_t *file, CGioFile &_file, wchar_t *data, size_t datalen)
  206. {
  207. int hdroffs = 0;
  208. size_t size = datalen;
  209. wchar_t *mt = data;
  210. wchar_t *ext = PathFindExtension(file);
  211. wchar_t langbuf[256] = {0};
  212. int flen = _file.GetContentLength();
  213. StringCchPrintfExW(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_PAYLOAD_SIZE, langbuf, 256), _file.GetContentLength());
  214. if (!_wcsicmp(ext, L".aac") || !_wcsicmp(ext, L".vlb"))
  215. {
  216. #if 0 // TODO!
  217. if (_wcsicmp(ext, L".vlb")) // aacplus can't do VLB
  218. {
  219. if (aacPlus)
  220. {
  221. aacPlus->EasyOpen(AACPLUSDEC_OUTPUTFORMAT_INT16_HOSTENDIAN, 6);
  222. AACPLUSDEC_EXPERTSETTINGS *pConf = aacPlus->GetDecoderSettingsHandle();
  223. pConf->bEnableOutputLimiter = 1;
  224. pConf->bDoUpsampling = 1;
  225. aacPlus->SetDecoderSettings();
  226. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW_BUF(IDS_FORMAT_AAC, langbuf, 256), &mt, &size, 0);
  227. char buffer[INFO_READ_SIZE] = {0};
  228. int inputRead;
  229. _file.Read(buffer, INFO_READ_SIZE, &inputRead);
  230. AACPLUSDEC_BITSTREAMBUFFERINFO bitbufInfo = { inputRead, 0, 0};
  231. aacPlus->StreamFeed((unsigned char *)buffer, &bitbufInfo);
  232. unsigned char tempBuf[65536] = {0}; // grr, can't we find a better way to do this?
  233. AACPLUSDEC_AUDIOBUFFERINFO audioBufInfo = {65536, 0, 0};
  234. aacPlus->StreamDecode(tempBuf, &audioBufInfo, 0, 0);
  235. audioBufInfo.nBytesBufferSizeIn -= audioBufInfo.nBytesWrittenOut;
  236. aacPlus->StreamDecode(tempBuf + audioBufInfo.nBytesWrittenOut, &audioBufInfo, 0, 0);
  237. AACPLUSDEC_STREAMPROPERTIES *streamProperties = aacPlus->GetStreamPropertiesHandle();
  238. if (streamProperties->nDecodingState == AACPLUSDEC_DECODINGSTATE_STREAMVERIFIED)
  239. {
  240. AACPLUSDEC_PROGRAMPROPERTIES *currentProgram = &(streamProperties->programProperties[streamProperties->nCurrentProgram]);
  241. switch (currentProgram->nStreamType)
  242. {
  243. case AACPLUSDEC_MPEG2_PROFILE_AACMAIN:
  244. StringCchCatEx(mt, size, L"\r\nMPEG-2 AAC", &mt, &size, 0);
  245. break;
  246. case AACPLUSDEC_MPEG2_PROFILE_AACLC:
  247. if (currentProgram->bProgramSbrEnabled)
  248. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW_BUF(IDS_MPEG2_HE_AAC_IS, langbuf, 256), &mt, &size, 0);
  249. else
  250. StringCchCatEx(mt, size, L"\r\nMPEG-2 AAC LC", &mt, &size, 0);
  251. break;
  252. case AACPLUSDEC_MPEG4_AOT_AACMAIN:
  253. StringCchCatEx(mt, size, L"\r\nMPEG-4 AAC", &mt, &size, 0);
  254. break;
  255. case AACPLUSDEC_MPEG4_AOT_AACLC:
  256. if (currentProgram->bProgramSbrEnabled)
  257. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW_BUF(IDS_MPEG4_HE_AAC_IS, langbuf, 256), &mt, &size, 0);
  258. else
  259. StringCchCatEx(mt, size, L"\r\nMPEG-4 AAC LC", &mt, &size, 0);
  260. break;
  261. case AACPLUSDEC_MPEG4_AOT_SBR:
  262. StringCchCatEx(mt, size, L"\r\nMPEG-4 HE-AAC", &mt, &size, 0);
  263. break;
  264. }
  265. if (currentProgram->nAacSamplingRate != currentProgram->nOutputSamplingRate)
  266. StringCchPrintfEx(mt, size, &mt, &size, 0,
  267. WASABI_API_LNGSTRINGW(IDS_SAMPLE_RATE_OUTPUT),
  268. currentProgram->nAacSamplingRate, currentProgram->nOutputSamplingRate);
  269. else
  270. StringCchPrintfEx(mt, size, &mt, &size, 0,
  271. WASABI_API_LNGSTRINGW(IDS_SAMPLE_RATE),
  272. currentProgram->nAacSamplingRate);
  273. int srate = currentProgram->nOutputSamplingRate;
  274. if (currentProgram->bProgramSbrEnabled)
  275. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_SBR_PRESENT), &mt, &size, 0);
  276. else
  277. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_SBR_NOT_PRESENT), &mt, &size, 0);
  278. if (currentProgram->nAacChannels != currentProgram->nOutputChannels)
  279. StringCchPrintfEx(mt, size, &mt, &size, 0,
  280. WASABI_API_LNGSTRINGW(IDS_CHANNELS_OUTPUT),
  281. currentProgram->nAacChannels, currentProgram->nOutputChannels);
  282. else
  283. StringCchPrintfEx(mt, size, &mt, &size, 0,
  284. WASABI_API_LNGSTRINGW(IDS_CHANNELS),
  285. currentProgram->nAacChannels);
  286. switch (currentProgram->nChannelMode)
  287. {
  288. case AACPLUSDEC_CHANNELMODE_MONO:
  289. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_MONO), &mt, &size, 0);
  290. break;
  291. case AACPLUSDEC_CHANNELMODE_STEREO:
  292. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_STEREO), &mt, &size, 0);
  293. break;
  294. case AACPLUSDEC_CHANNELMODE_PARAMETRIC_STEREO:
  295. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_PARAMETRIC_STEREO), &mt, &size, 0);
  296. break;
  297. case AACPLUSDEC_CHANNELMODE_DUAL_CHANNEL:
  298. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_DUAL_CHANNEL), &mt, &size, 0);
  299. break;
  300. case AACPLUSDEC_CHANNELMODE_4_CHANNEL_2CPE:
  301. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_4_CHANNEL_2_CPE), &mt, &size, 0);
  302. break;
  303. case AACPLUSDEC_CHANNELMODE_4_CHANNEL_MPEG:
  304. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_4_CHANNEL_MPEG), &mt, &size, 0);
  305. break;
  306. case AACPLUSDEC_CHANNELMODE_5_CHANNEL:
  307. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_5_CHANNEL), &mt, &size, 0);
  308. break;
  309. case AACPLUSDEC_CHANNELMODE_5_1_CHANNEL:
  310. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_5_1), &mt, &size, 0);
  311. break;
  312. case AACPLUSDEC_CHANNELMODE_6_1_CHANNEL:
  313. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_6_1), &mt, &size, 0);
  314. break;
  315. case AACPLUSDEC_CHANNELMODE_7_1_CHANNEL:
  316. StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_7_1), &mt, &size, 0);
  317. break;
  318. }
  319. if (streamProperties->nBitrate)
  320. {
  321. StringCchPrintfEx(mt, size, &mt, &size, 0,
  322. WASABI_API_LNGSTRINGW(IDS_BITRATE),
  323. streamProperties->nBitrate);
  324. }
  325. else
  326. {
  327. int avg_bitrate = FindAverageAACBitrate((unsigned char *)buffer, inputRead);
  328. StringCchPrintfEx(mt, size, &mt, &size, 0,
  329. WASABI_API_LNGSTRINGW(IDS_AVERAGE_BITRATE),
  330. avg_bitrate);
  331. }
  332. }
  333. }
  334. }
  335. if (!aacPlus)
  336. #endif
  337. {
  338. char buffer[INFO_READ_SIZE] = {0};
  339. int inputRead = 0;
  340. _file.Read(buffer, INFO_READ_SIZE, &inputRead);
  341. StringCchCopyEx(mt, size, WASABI_API_LNGSTRINGW(IDS_FORMAT_AAC), &mt, &size, 0);
  342. unsigned char *a = (unsigned char *)buffer;
  343. while (inputRead-- >= 8)
  344. {
  345. AACFrame aacFrame;
  346. aacFrame.ReadBuffer(a);
  347. if (aacFrame.OK())
  348. {
  349. int aac_frame_length = aacFrame.frameLength;
  350. int no_rawdb = aacFrame.numDataBlocks;
  351. /*size_t size = 1024;
  352. char *mt = mpeg_description;*/
  353. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW(IDS_HEADER_FOUND_AT_X_BYTES), hdroffs);
  354. StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nMPEG-%d AAC", aacFrame.GetMPEGVersion());
  355. int fc_tot = aac_frame_length;
  356. int fc_cnt = no_rawdb + 1;
  357. unsigned char *aa = a + aac_frame_length;
  358. int tt = inputRead - aac_frame_length;
  359. while (tt >= 8)
  360. {
  361. AACFrame nextFrame;
  362. nextFrame.ReadBuffer(aa);
  363. if (!nextFrame.OK()) break; // error
  364. int fcaac_frame_length = nextFrame.frameLength;
  365. int fcno_rawdb = nextFrame.numDataBlocks;
  366. fc_cnt += fcno_rawdb + 1;
  367. fc_tot += fcaac_frame_length;
  368. aa += fcaac_frame_length;
  369. tt -= fcaac_frame_length;
  370. }
  371. {
  372. int avg_framesize = fc_tot / (fc_cnt ? fc_cnt : 1);
  373. int srate = aacFrame.GetSampleRate();
  374. int avg_bitrate = fixAACCBRbitrate(MulDiv(avg_framesize * 8, srate, 1024 * 1000));
  375. int len_s = MulDiv(flen, 1024, avg_framesize * srate);
  376. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW(IDS_LENGTH_X_SECONDS), len_s);
  377. StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nCBR %d kbps", avg_bitrate);
  378. }
  379. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW(IDS_PROFILE), aacFrame.GetProfileName());
  380. StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\n%dHz %s", aacFrame.GetSampleRate(), aacFrame.GetChannelConfigurationName());
  381. StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nCRC: %s", WASABI_API_LNGSTRINGW((aacFrame.protection == 0 ? IDS_YES : IDS_NO)));
  382. break;
  383. }
  384. a++;
  385. hdroffs++;
  386. }
  387. }
  388. }
  389. else
  390. {
  391. unsigned char mp3syncbuf[INFO_READ_SIZE] = {0};
  392. int inputRead = 0;
  393. DWORD start = _file.GetCurrentPosition();
  394. // find position of first sync
  395. if (!mp3sync(&_file))
  396. return;
  397. int syncposition = _file.GetCurrentPosition()-start;
  398. // advance to first sync
  399. _file.Peek(mp3syncbuf, INFO_READ_SIZE, &inputRead);
  400. unsigned int padding = 0;
  401. unsigned int encoderDelay = 0;
  402. MPEGFrame frame;
  403. frame.ReadBuffer(mp3syncbuf);
  404. int framelen = frame.FrameSize();
  405. //const CMp3StreamInfo *info = decoder.GetStreamInfo();
  406. //const CMpegHeader *header = decoder.m_Mbs.GetHdr();
  407. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_HEADER_FOUND_AT_X_BYTES, langbuf, 256), _file.GetHeaderOffset() + syncposition);
  408. if (!padding) padding = _file.postpad;
  409. if (!encoderDelay) encoderDelay = _file.prepad;
  410. if (padding || encoderDelay)
  411. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ENC_DELAY_ZERO_PADDING, langbuf, 256), encoderDelay, padding);
  412. int is_vbr_lens = _file.m_vbr_ms;
  413. int iLen = (is_vbr_lens ? is_vbr_lens/1000 : ((flen * 8) / frame.GetBitrate()));
  414. if(iLen > 0)
  415. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_LENGTH_X_SECONDS, langbuf, 256), iLen);
  416. else
  417. {
  418. float fLen = (is_vbr_lens ? is_vbr_lens/1000.0f : ((flen * 8.0f) / frame.GetBitrate()));
  419. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_LENGTH_X_PART_SECONDS, langbuf, 256), fLen);
  420. }
  421. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_S_LAYER_X, langbuf, 256), GetMPEGVersionString(frame.mpegVersion), frame.GetLayer());
  422. int frames = _file.m_vbr_frames;
  423. int is_vbr = _file.m_vbr_flag || _file.m_vbr_hdr;
  424. if (!is_vbr || _file.encodingMethod == ENCODING_METHOD_CBR)
  425. {
  426. if (frames)
  427. {
  428. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT, langbuf, 256), frame.GetBitrate() / 1000, frames);
  429. }
  430. else
  431. {
  432. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT_APPROX, langbuf, 256), frame.GetBitrate() / 1000, MulDiv(flen, 8, framelen));
  433. }
  434. }
  435. else if (is_vbr && _file.encodingMethod == ENCODING_METHOD_ABR)
  436. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT_ABR, langbuf, 256), _file.GetAvgVBRBitrate(), frames);
  437. else
  438. {
  439. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT_VBR, langbuf, 256), _file.GetAvgVBRBitrate(), _file.m_vbr_hdr?L"I":L"", frames);
  440. }
  441. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_HZ_S, langbuf, 256), frame.GetSampleRate(), GetChannelModeString(frame.channelMode));
  442. StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nCRC: %s", WASABI_API_LNGSTRINGW_BUF((frame.CRC ? IDS_YES : IDS_NO), langbuf, 256));
  443. wchar_t tmp[16] = {0};
  444. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_COPYRIGHTED, langbuf, 256), WASABI_API_LNGSTRINGW_BUF((frame.copyright ? IDS_YES : IDS_NO),tmp,16));
  445. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ORIGINAL, langbuf, 256), WASABI_API_LNGSTRINGW_BUF((frame.original ? IDS_YES : IDS_NO),tmp,16));
  446. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_EMPHASIS, langbuf, 256), GetEmphasisString(frame.emphasis));
  447. if (_file.m_vbr_frame_len && !_file.lengthVerified)
  448. StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_MP3_HAS_BEEN_MODIFIED_NOT_ALL_MAY_BE_CORRECT, langbuf, 256));
  449. }
  450. }
  451. void GetAudioInfo(const wchar_t *filename, CGioFile *file, int *len, int *channels, int *bitrate, int *vbr, int *sr)
  452. {
  453. *bitrate=0;
  454. *len=0;
  455. *vbr=0;
  456. *channels=0;
  457. if (file)
  458. {
  459. wchar_t *ext = PathFindExtension(filename);
  460. if (!_wcsicmp(ext, L".aac") || !_wcsicmp(ext, L".vlb"))
  461. {
  462. unsigned char t[INFO_READ_SIZE*2] = {0};
  463. unsigned char *a = t;
  464. int n = 0;
  465. if (file->Read(t, sizeof(t), &n) != NErr_Success)
  466. return;
  467. while (n-- >= 8)
  468. {
  469. AACFrame aacFrame;
  470. aacFrame.ReadBuffer(a);
  471. if (aacFrame.OK())
  472. {
  473. int aac_frame_length = aacFrame.frameLength;
  474. int fc_tot = aac_frame_length;
  475. int fc_cnt = aacFrame.numDataBlocks + 1;
  476. unsigned char *aa = a + aac_frame_length;
  477. int tt = n - aac_frame_length;
  478. while (tt >= 8 && aac_frame_length)
  479. {
  480. AACFrame nextFrame;
  481. nextFrame.ReadBuffer(aa);
  482. if (!nextFrame.OK()) break; // error
  483. int fcaac_frame_length = nextFrame.frameLength;
  484. int fcno_rawdb = nextFrame.numDataBlocks;
  485. fc_cnt += fcno_rawdb + 1;
  486. fc_tot += fcaac_frame_length;
  487. aa += fcaac_frame_length;
  488. tt -= fcaac_frame_length;
  489. }
  490. int avg_framesize = fc_tot / (fc_cnt ? fc_cnt : 1);
  491. int br = MulDiv(avg_framesize * 8, aacFrame.GetSampleRate(), 1024);
  492. *len = MulDiv(file->GetContentLength(), 1024*8, br);
  493. *bitrate = fixAACCBRbitrate(br/1000)*1000;
  494. *sr = aacFrame.GetSampleRate();
  495. *channels = aacFrame.GetNumChannels();
  496. break;
  497. }
  498. a++;
  499. }
  500. }
  501. else
  502. {
  503. if (*bitrate = file->GetAvgVBRBitrate()*1000)
  504. {
  505. *len = file->m_vbr_ms;
  506. *vbr = file->m_vbr_flag || file->m_vbr_hdr;
  507. }
  508. if (!mp3sync(file))
  509. return;
  510. unsigned char t[4] = {0};
  511. int n = 0;
  512. if (file->Peek(t, sizeof(t), &n) != NErr_Success)
  513. return;
  514. MPEGFrame frame;
  515. frame.ReadBuffer(t);
  516. if (frame.IsSync())
  517. {
  518. if (!*bitrate)
  519. {
  520. *bitrate = frame.GetBitrate();
  521. *len = MulDiv(file->GetContentLength(), 1000*8, *bitrate);
  522. }
  523. *channels = frame.GetNumChannels();
  524. *sr = frame.GetSampleRate();
  525. }
  526. }
  527. }
  528. }