You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

875 lines
22KB

  1. #define MY_VERSION "0.9.13"
  2. /*
  3. changelog
  4. 2019-02-01 08:15 UTC - kode54
  5. - Implemented fast CRC algorithm from:
  6. - https://freac.org/developer-blog-mainmenu-9/14-freac/277-fastcrc
  7. - Version is now 0.9.13
  8. 2019-01-11 03:48 UTC - kode54
  9. - Fixed the packet decoder for MP4
  10. - Version is now 0.9.12
  11. 2019-01-05 09:18 UTC - kode54
  12. - Improved the packet decoder, including support for MP4 container
  13. - Version is now 0.9.11
  14. 2018-01-26 02:57 UTC - kode54
  15. - Hopefully fix the SSE2 compiler issue with some workaround switch
  16. - Version is now 0.9.10
  17. 2018-01-13 03:58 UTC - kode54
  18. - Updated to v1.4 SDK
  19. - Version is now 0.9.9
  20. 2017-02-04 01:41 UTC - kode54
  21. - Add link to about string
  22. - Version is now 0.9.8
  23. 2012-08-30 00:56 UTC - kode54
  24. - Added channel info reporting
  25. - Version is now 0.9.7
  26. 2012-03-03 12:51 UTC - kode54
  27. - Packet decoder now correctly disables dynamic range compression
  28. - Version is now 0.9.6
  29. 2010-04-13 14:51 UTC - kode54
  30. - Amended preferences WM_INITDIALOG handler
  31. - Version is now 0.9.5
  32. 2010-01-11 04:40 UTC - kode54
  33. - Updated preferences page to 1.0 API
  34. - Version is now 0.9.4
  35. 2009-04-09 01:43 UTC - kode54
  36. - Fixed AC3 parser and caller to handle EOF when there's extra data after the last packet.
  37. - Version is now 0.9.3
  38. 2009-03-02 21:02 UTC - kode54
  39. - Fixed AC3 parser to ignore end of file when refilling the buffer, caller handles EOF already.
  40. - Version is now 0.9.2
  41. 2008-05-29 01:21 UTC - kode54
  42. - Fixed AC3 parser to handle end of file.
  43. - Version is now 0.9.1
  44. 2007-02-07 03:07 UTC - kode54
  45. - Implemented a new buffering packet processor which includes CRC checking to hopefully avoid any
  46. further garbage data which resembles AC3 packet headers.
  47. - Now uses tag processor on open to detect position of trailing (APEv2) tags, and corrals the decoder
  48. within the range detected from (first detected packet) <-> (beginning of tag or end of file).
  49. - Version is now 0.9
  50. 2006-10-08 11:26 UTC - kode54
  51. - Moved common sample handling code to functions.
  52. - Configured liba52 to use DJBFFT with ppro optimizations.
  53. - Added packet decoder.
  54. - Version is now 0.8
  55. 2005-12-04 20:07 UTC - kode54
  56. - Fixed synchronization from get_info
  57. - Version is now 0.7
  58. 2004-03-07 15:47 UTC - kode54
  59. - Added VBR scanning threshold because scanning takes too damn long on xxbog hueg files
  60. - Restricted seektable and accurate seeking/scanning to VBR files, because its ficking
  61. slow unless the files are small, or your drive is faaaaast
  62. - Version is now 0.6
  63. 2004-03-07 07:02 UTC - kode54
  64. - Added seek table manager from foo_input_std\mp3.cpp, now supports accurate seeking even with VBR files
  65. - Also skips over whole frames more efficiently
  66. - Load-time also scans over the entire file for accurate length even for VBR files
  67. 2004-01-14 16:50 UTC - kode54
  68. - Fixed playback to skip over blocks with unwanted bsid, but length and seeking will still be broken
  69. - Version is now 0.5.2
  70. 2003-12-23 12:30 UTC - kode54
  71. - Hotfix for new stereo and quad mappings
  72. - Version is now 0.5.1
  73. 2003-12-22 18:02 UTC - kode54
  74. - Added smaller speaker count mappings for mono, stereo, and quad without LFE
  75. - Version is now 0.5
  76. 2003-10-29 15:51 UTC - kode54
  77. - Added APEv2 tag writing
  78. - Version is now 0.4
  79. 2003-07-16 18:29 - kode54
  80. - Added sample accuracy to seeking
  81. - Version is now 0.3
  82. 2003-06-26 07:34 - kode54
  83. - Updated to 0.7 API
  84. - Version is now 0.2
  85. */
  86. #include <foobar2000.h>
  87. #include "../ATLHelpers/ATLHelpers.h"
  88. #include <inttypes.h>
  89. #include <a52.h>
  90. #include <mm_accel.h>
  91. #include "ac3_parser.h"
  92. #include "resource.h"
  93. static int mp4_ac3 = 165;
  94. enum
  95. {
  96. default_cfg_dynrng = 0
  97. };
  98. // {49A08985-F4FF-4610-85DC-E084731AD7E8}
  99. static const GUID guid_cfg_dynrng =
  100. { 0x49a08985, 0xf4ff, 0x4610, { 0x85, 0xdc, 0xe0, 0x84, 0x73, 0x1a, 0xd7, 0xe8 } };
  101. static cfg_int cfg_dynrng(guid_cfg_dynrng, default_cfg_dynrng);
  102. /*#define DBG(a) \
  103. OutputDebugString( # a); \
  104. a; \
  105. OutputDebugString("success");*/
  106. static class init_a52
  107. {
  108. public:
  109. init_a52()
  110. {
  111. a52_imdct_init();
  112. }
  113. ~init_a52() {}
  114. } g_initializer;
  115. unsigned get_channels( int flags )
  116. {
  117. static const uint8_t ch[] = { 2, 1, 2, 3, 3, 4, 4, 5, 0, 0, 2, 0, 0, 0, 0, 0 };
  118. return ch[ flags & 15 ] + ( ( flags & 16 ) >> 4 );
  119. }
  120. unsigned get_speaker_config( int flags )
  121. {
  122. static const unsigned config[] =
  123. {
  124. audio_chunk::channel_config_stereo,
  125. audio_chunk::channel_config_mono,
  126. audio_chunk::channel_config_stereo,
  127. audio_chunk::channel_config_stereo | audio_chunk::channel_front_center,
  128. audio_chunk::channel_config_stereo | audio_chunk::channel_back_center,
  129. audio_chunk::channel_config_stereo | audio_chunk::channel_front_center | audio_chunk::channel_back_center,
  130. audio_chunk::channel_config_stereo | audio_chunk::channel_back_left | audio_chunk::channel_back_right,
  131. audio_chunk::channel_config_stereo | audio_chunk::channel_front_center | audio_chunk::channel_back_left | audio_chunk::channel_back_right,
  132. 0, 0,
  133. audio_chunk::channel_config_stereo,
  134. 0, 0, 0, 0, 0
  135. };
  136. pfc::static_assert_t< audio_chunk::channel_lfe == 8 >();
  137. return config[ flags & 15 ] + ( ( flags & 16 ) >> 1 );
  138. }
  139. void get_mode_description( int p_flags, pfc::string8 & p_out )
  140. {
  141. static const char *ac3_mode_list[11] = {
  142. { "Dual Mono" },
  143. { "Mono" },
  144. { "Stereo" },
  145. { "3 front channels" },
  146. { "2 front, 1 rear surround channel" },
  147. { "3 front, 1 rear surround channel" },
  148. { "2 front, 2 rear surround channels" },
  149. { "3 front, 2 rear surround channels" },
  150. { "Channel 1" },
  151. { "Channel 2" },
  152. { "Dolby Stereo" }
  153. };
  154. unsigned channel_mode = p_flags & A52_CHANNEL_MASK;
  155. if ( channel_mode < _countof(ac3_mode_list) ) p_out = ac3_mode_list[ channel_mode ];
  156. else { p_out = "Unknown mode ("; p_out += pfc::format_int( channel_mode ); p_out += ")"; }
  157. if ( p_flags & A52_LFE ) p_out += " + LFE";
  158. }
  159. void prepare_chunk( const sample_t * p_src, audio_sample * p_dst, unsigned sample_count, int flags )
  160. {
  161. int i;
  162. const sample_t * src = p_src;
  163. audio_sample * dst = p_dst;
  164. if ( flags & A52_LFE )
  165. {
  166. switch ( flags & A52_CHANNEL_MASK )
  167. {
  168. case A52_MONO:
  169. for (i = 0; i < sample_count; ++i)
  170. {
  171. *dst++ = src[256];
  172. *dst++ = *src++;
  173. }
  174. break;
  175. case A52_STEREO:
  176. case A52_CHANNEL:
  177. case A52_DOLBY:
  178. for (i = 0; i < sample_count; ++i)
  179. {
  180. *dst++ = src[256];
  181. *dst++ = src[512];
  182. *dst++ = *src++;
  183. }
  184. break;
  185. case A52_3F:
  186. for (i = 0; i < sample_count; ++i)
  187. {
  188. *dst++ = src[256];
  189. *dst++ = src[768];
  190. *dst++ = src[512];
  191. *dst++ = *src++;
  192. }
  193. break;
  194. case A52_2F1R:
  195. for (i = 0; i < sample_count; ++i)
  196. {
  197. *dst++ = src[256];
  198. *dst++ = src[512];
  199. *dst++ = *src;
  200. *dst++ = src[768];
  201. src++;
  202. }
  203. break;
  204. case A52_3F1R:
  205. for (i = 0; i < sample_count; ++i)
  206. {
  207. *dst++ = src[256];
  208. *dst++ = src[768];
  209. *dst++ = src[512];
  210. *dst++ = *src;
  211. *dst++ = src[1024];
  212. src++;
  213. }
  214. break;
  215. case A52_2F2R:
  216. for (i = 0; i < sample_count; ++i)
  217. {
  218. *dst++ = src[256];
  219. *dst++ = src[512];
  220. *dst++ = *src;
  221. *dst++ = src[768];
  222. *dst++ = src[1024];
  223. src++;
  224. }
  225. break;
  226. case A52_3F2R:
  227. for (i = 0; i < sample_count; ++i)
  228. {
  229. *dst++ = src[256];
  230. *dst++ = src[768];
  231. *dst++ = src[512];
  232. *dst++ = *src;
  233. *dst++ = src[1024];
  234. *dst++ = src[1280];
  235. src++;
  236. }
  237. break;
  238. }
  239. }
  240. else
  241. {
  242. switch (flags & A52_CHANNEL_MASK)
  243. {
  244. case A52_MONO:
  245. memcpy(dst, src, sample_count * sizeof(audio_sample));
  246. break;
  247. case A52_STEREO:
  248. case A52_CHANNEL:
  249. case A52_DOLBY:
  250. for (i = 0; i < sample_count; ++i)
  251. {
  252. *dst++ = *src;
  253. *dst++ = src[256];
  254. src++;
  255. }
  256. break;
  257. case A52_2F2R:
  258. for (i = 0; i < sample_count; ++i)
  259. {
  260. *dst++ = *src;
  261. *dst++ = src[256];
  262. *dst++ = src[512];
  263. *dst++ = src[768];
  264. src++;
  265. }
  266. break;
  267. case A52_3F:
  268. for (i = 0; i < sample_count; ++i)
  269. {
  270. *dst++ = *src;
  271. *dst++ = src[512];
  272. *dst++ = src[256];
  273. src++;
  274. }
  275. break;
  276. case A52_2F1R:
  277. for (i = 0; i < sample_count; ++i)
  278. {
  279. *dst++ = *src;
  280. *dst++ = src[256];
  281. *dst++ = src[512];
  282. src++;
  283. }
  284. break;
  285. case A52_3F1R:
  286. for (i = 0; i < sample_count; ++i)
  287. {
  288. *dst++ = *src;
  289. *dst++ = src[512];
  290. *dst++ = src[256];
  291. *dst++ = src[768];
  292. src++;
  293. }
  294. break;
  295. case A52_3F2R:
  296. for (i = 0; i < sample_count; ++i)
  297. {
  298. *dst++ = *src;
  299. *dst++ = src[512];
  300. *dst++ = src[256];
  301. *dst++ = src[768];
  302. *dst++ = src[1024];
  303. src++;
  304. }
  305. break;
  306. }
  307. }
  308. }
  309. class input_ac3 : public input_stubs
  310. {
  311. private:
  312. int srate, flags, bitrate; //, remain;
  313. bool is_seekable, noseeking;
  314. //sample_t bias, level;
  315. bool do_dynrng, ignore_broken_files;
  316. service_ptr_t<file> m_file_base;
  317. service_ptr_t<file> m_file;
  318. file_info_impl m_info;
  319. t_filesize m_size;
  320. t_filesize first_frame;
  321. t_uint64 total_frames;
  322. t_uint64 frames_done;
  323. //t_uint64 skip_frames;
  324. t_uint32 skip_samples;
  325. a52_state_t * state;
  326. parser_ac3 parser;
  327. public:
  328. input_ac3()
  329. {
  330. state = 0;
  331. }
  332. ~input_ac3()
  333. {
  334. if (state) a52_free(state);
  335. }
  336. void open( service_ptr_t<file> p_filehint, const char * p_path, t_input_open_reason p_reason, abort_callback & p_abort )
  337. {
  338. if ( p_filehint.is_empty() )
  339. {
  340. filesystem::g_open( p_filehint, p_path, ( p_reason == input_open_info_write ) ? filesystem::open_mode_write_existing : filesystem::open_mode_read, p_abort );
  341. }
  342. m_size = p_filehint->get_size_ex( p_abort );
  343. try
  344. {
  345. tag_processor::read_trailing_ex( p_filehint, m_info, m_size, p_abort );
  346. }
  347. catch ( const exception_io_data & ) { }
  348. p_filehint->seek( 0, p_abort );
  349. unsigned block_size = parser.sync( p_filehint, flags, srate, bitrate, p_abort );
  350. service_ptr_t<reader_limited> p_file = new service_impl_t<reader_limited> ();
  351. p_file->init( p_filehint, parser.packet_offset( p_filehint->get_position( p_abort ) ), m_size, p_abort );
  352. m_file_base = p_filehint;
  353. m_file = p_file;
  354. total_frames = m_file->get_size_ex( p_abort ) / block_size;
  355. m_info.info_set_int( "bitrate", bitrate / 1000 );
  356. m_info.set_length( double( total_frames * 1536 ) / double( srate ) );
  357. m_info.info_set( "codec", "ATSC A/52" );
  358. m_info.info_set_int( "channels", get_channels( flags ) );
  359. m_info.info_set_int( "samplerate", srate );
  360. m_info.info_set( "encoding", "lossy" );
  361. pfc::string8 channel_mode;
  362. get_mode_description( flags, channel_mode );
  363. m_info.info_set( "channel_mode", channel_mode );
  364. }
  365. void get_info( file_info & p_info, abort_callback & p_abort )
  366. {
  367. p_info.copy( m_info );
  368. }
  369. t_filestats get_file_stats( abort_callback & p_abort )
  370. {
  371. return m_file->get_stats( p_abort );
  372. }
  373. void decode_initialize( unsigned p_flags, abort_callback & p_abort )
  374. {
  375. ignore_broken_files = ! ( p_flags & input_flag_testing_integrity );
  376. if (state) a52_free(state);
  377. state = a52_init( /*0 /*MM_ACCEL_DJBFFT*/ );
  378. if ( ! state ) throw std::bad_alloc();
  379. m_file->seek( 0, p_abort );
  380. parser.reset();
  381. do_dynrng = !! cfg_dynrng;
  382. //remain = 0;
  383. frames_done = 0;
  384. //skip_frames = 0;
  385. skip_samples = 0;
  386. }
  387. bool decode_run( audio_chunk & p_chunk, abort_callback & p_abort )
  388. {
  389. try
  390. {
  391. sample_t level = 1.0, bias = 0;
  392. unsigned remain = 0;
  393. while (!remain)
  394. {
  395. p_abort.check();
  396. if ( total_frames && frames_done >= total_frames )
  397. {
  398. //console::info(string_printf("Hit end of file. (current: %d, total: %d)", (int)frames_done, (int)total_frames));
  399. return false;
  400. }
  401. parser.packet_flush();
  402. parser.sync( m_file, flags, srate, bitrate, p_abort );
  403. ++frames_done;
  404. /*while ( skip_frames-- )
  405. {
  406. parser.packet_flush();
  407. parser.sync( m_file, flags, srate, bitrate, p_abort );
  408. }*/
  409. if ( a52_frame( state, parser.packet_get(), & flags, & level, bias ) ) throw exception_io_data();
  410. if ( ! do_dynrng ) a52_dynrng(state, NULL, NULL);
  411. remain = 6;
  412. while ( skip_samples >= 256 && remain )
  413. {
  414. p_abort.check();
  415. --remain;
  416. if ( a52_block( state ) ) throw exception_io_data();
  417. skip_samples -= 256;
  418. }
  419. }
  420. unsigned channels = get_channels( flags );
  421. unsigned samples = remain * 256 - skip_samples;
  422. p_chunk.set_data_size( samples * channels );
  423. p_chunk.set_channels( channels, get_speaker_config( flags ) );
  424. p_chunk.set_srate( srate );
  425. p_chunk.set_sample_count( samples );
  426. const sample_t * in = a52_samples( state );
  427. audio_sample * out = p_chunk.get_data();
  428. if ( a52_block( state ) ) throw exception_io_data();
  429. prepare_chunk( in + skip_samples, out, 256 - skip_samples, flags );
  430. out += ( 256 - skip_samples ) * channels;
  431. skip_samples = 0;
  432. for ( unsigned i = 1; i < remain; ++i )
  433. {
  434. if ( a52_block( state ) ) throw exception_io_data();
  435. prepare_chunk( in, out, 256, flags );
  436. out += 256 * channels;
  437. }
  438. return true;
  439. }
  440. catch ( const exception_io_data_truncation & )
  441. {
  442. return false;
  443. }
  444. catch ( const exception_io_data & )
  445. {
  446. if ( ignore_broken_files ) return false;
  447. throw;
  448. }
  449. }
  450. void decode_seek( double p_seconds,abort_callback & p_abort )
  451. {
  452. if ( ! m_file->can_seek() ) throw exception_io_data();
  453. t_uint64 dest_sample = audio_math::time_to_samples( p_seconds, srate );
  454. frames_done = dest_sample / 1536;
  455. if ( frames_done < total_frames )
  456. {
  457. t_uint64 dest_skip = dest_sample % 1536;
  458. t_uint64 dest_offset = frames_done * 1536 * ( bitrate >> 3 ) / srate;
  459. m_file->seek( dest_offset, p_abort );
  460. parser.reset();
  461. skip_samples = t_uint32( dest_skip );
  462. //remain = 0;
  463. }
  464. }
  465. bool decode_can_seek()
  466. {
  467. return m_file.is_valid() ? m_file->can_seek() : false;
  468. }
  469. bool decode_get_dynamic_info( file_info & p_out, double & p_timestamp_delta )
  470. {
  471. return false;
  472. }
  473. bool decode_get_dynamic_info_track( file_info & p_out, double & p_timestamp_delta )
  474. {
  475. return false;
  476. }
  477. void decode_on_idle( abort_callback & p_abort )
  478. {
  479. m_file->on_idle( p_abort );
  480. }
  481. void retag( const file_info & p_info, abort_callback & p_abort )
  482. {
  483. tag_processor::write_apev2( m_file_base, p_info, p_abort );
  484. m_info.copy( p_info );
  485. }
  486. static bool g_is_our_content_type( const char * p_content_type )
  487. {
  488. return false;
  489. }
  490. static bool g_is_our_path( const char * p_full_path, const char * p_extension )
  491. {
  492. return !stricmp( p_extension, "ac3" );
  493. }
  494. static GUID g_get_guid()
  495. {
  496. static const GUID guid = { 0xd11f990, 0xf41b, 0x4e45,{ 0x83, 0x89, 0x2f, 0x22, 0xa, 0x1e, 0x55, 0x33 } };
  497. return guid;
  498. }
  499. static const char * g_get_name()
  500. {
  501. return "AC3 decoder";
  502. }
  503. static GUID g_get_preferences_guid()
  504. {
  505. static const GUID guid = { 0x69c304f0, 0xb305, 0x40d8,{ 0x9c, 0xf6, 0x19, 0x1e, 0x92, 0x10, 0xa7, 0x4c } };
  506. return guid;
  507. }
  508. };
  509. class packet_decoder_ac3 : public packet_decoder_streamparse
  510. {
  511. a52_state_t * m_state;
  512. bool m_dynrng, m_decode;
  513. int srate, flags, bitrate;
  514. public:
  515. packet_decoder_ac3()
  516. {
  517. m_state = NULL;
  518. m_dynrng = !!cfg_dynrng;
  519. cleanup();
  520. }
  521. ~packet_decoder_ac3()
  522. {
  523. cleanup();
  524. }
  525. void cleanup()
  526. {
  527. srate = 0;
  528. if ( m_state )
  529. {
  530. a52_free( m_state );
  531. m_state = NULL;
  532. }
  533. }
  534. static bool g_is_our_setup( const GUID & p_owner, t_size p_param1, const void * p_param2, t_size p_param2size )
  535. {
  536. if ( p_owner == owner_matroska )
  537. {
  538. if ( p_param2size == sizeof( matroska_setup ) )
  539. {
  540. const matroska_setup * setup = ( const matroska_setup * ) p_param2;
  541. if ( !strncmp( setup->codec_id, "A_AC3", 5 ) )
  542. {
  543. return true;
  544. }
  545. }
  546. }
  547. // Maybe this block isn't necessary?
  548. else if (p_owner == owner_MP4)
  549. {
  550. if (p_param1 == mp4_ac3)
  551. {
  552. return true;
  553. }
  554. }
  555. // This one seems to be correct
  556. else if (p_owner == owner_MP4_AC3)
  557. {
  558. return true;
  559. }
  560. return false;
  561. }
  562. void open( const GUID & p_owner, bool p_decode, t_size p_param1, const void * p_param2, t_size p_param2size, abort_callback & p_abort )
  563. {
  564. assert( g_is_our_setup( p_owner, p_param1, p_param2, p_param2size ) );
  565. cleanup();
  566. m_state = a52_init( /*MM_ACCEL_DJBFFT*/ );
  567. if ( ! m_state ) throw std::bad_alloc();
  568. if (p_owner == owner_matroska)
  569. {
  570. const matroska_setup *setup = (const matroska_setup *)p_param2;
  571. srate = setup->sample_rate;
  572. }
  573. m_decode = p_decode;
  574. }
  575. virtual t_size set_stream_property( const GUID & p_type, t_size p_param1, const void * p_param2, t_size p_param2size )
  576. {
  577. return 0;
  578. }
  579. virtual void get_info( file_info & p_info )
  580. {
  581. p_info.info_set_int( "samplerate", srate );
  582. p_info.info_set( "codec", "ATSC A/52" );
  583. p_info.info_set( "encoding", "lossy" );
  584. if ( bitrate )
  585. {
  586. pfc::string8 channel_mode;
  587. get_mode_description( flags, channel_mode );
  588. p_info.info_set_int("channels", get_channels(flags));
  589. p_info.info_set( "channel_mode", channel_mode );
  590. }
  591. }
  592. virtual unsigned get_max_frame_dependency() { return 0; }
  593. virtual double get_max_frame_dependency_time() { return 0; }
  594. virtual void reset_after_seek() {}
  595. virtual void decode( const void * p_buffer, t_size p_bytes, audio_chunk & p_chunk, abort_callback & p_abort )
  596. {
  597. t_size temp;
  598. decode_ex( p_buffer, p_bytes, temp, p_chunk, p_abort );
  599. }
  600. virtual bool analyze_first_frame_supported() { return true; }
  601. virtual void analyze_first_frame( const void * p_buffer, t_size p_bytes, abort_callback & p_abort )
  602. {
  603. t_size p_bytes_processed;
  604. analyze_first_frame_ex(p_buffer, p_bytes, p_bytes_processed, p_abort);
  605. }
  606. virtual void analyze_first_frame_ex(const void * p_buffer, t_size p_bytes, t_size & p_bytes_processed, abort_callback & p_abort)
  607. {
  608. if (p_bytes < 7) throw exception_io_data();
  609. int frame_size = a52_syncinfo((uint8_t *)p_buffer, &flags, &srate, &bitrate);
  610. if (!frame_size) throw exception_io_data();
  611. p_bytes_processed = frame_size;
  612. }
  613. virtual void decode_ex( const void * p_buffer, t_size p_bytes, t_size & p_bytes_processed, audio_chunk & p_chunk, abort_callback & p_abort )
  614. {
  615. int frame_size = 0;
  616. int i = -1;
  617. int flags, sample_rate, bit_rate;
  618. while ( frame_size == 0 && i + 7 < p_bytes )
  619. {
  620. ++i;
  621. frame_size = a52_syncinfo( ( uint8_t * ) p_buffer + i, &flags, &sample_rate, &bit_rate );
  622. }
  623. if ( !frame_size || frame_size + i < p_bytes ) throw exception_io_data();
  624. p_bytes_processed = frame_size + i;
  625. sample_t level = 1.0, bias = 0;
  626. if ( a52_frame( m_state, ( uint8_t * ) p_buffer + i, &flags, &level, bias ) ) throw exception_io_data();
  627. if ( ! m_dynrng ) a52_dynrng( m_state, NULL, NULL );
  628. if ( ! m_decode )
  629. {
  630. for ( i = 0; i < 6; ++i )
  631. {
  632. if ( a52_block( m_state ) ) throw exception_io_data();
  633. }
  634. }
  635. else
  636. {
  637. int channels = get_channels( flags );
  638. p_chunk.set_data_size( 256 * 6 * channels );
  639. p_chunk.set_channels( channels, get_speaker_config( flags ) );
  640. p_chunk.set_srate( sample_rate );
  641. p_chunk.set_sample_count( 256 * 6 );
  642. const sample_t * in = a52_samples( m_state );
  643. audio_sample * out = p_chunk.get_data();
  644. for ( i = 0; i < 6; ++i )
  645. {
  646. if ( a52_block( m_state ) ) throw exception_io_data();
  647. prepare_chunk( in, out, 256, flags );
  648. out += 256 * channels;
  649. }
  650. }
  651. }
  652. };
  653. class CMyPreferences : public CDialogImpl<CMyPreferences>, public preferences_page_instance {
  654. public:
  655. //Constructor - invoked by preferences_page_impl helpers - don't do Create() in here, preferences_page_impl does this for us
  656. CMyPreferences(preferences_page_callback::ptr callback) : m_callback(callback) {}
  657. //Note that we don't bother doing anything regarding destruction of our class.
  658. //The host ensures that our dialog is destroyed first, then the last reference to our preferences_page_instance object is released, causing our object to be deleted.
  659. //dialog resource ID
  660. enum {IDD = IDD_CONFIG};
  661. // preferences_page_instance methods (not all of them - get_wnd() is supplied by preferences_page_impl helpers)
  662. t_uint32 get_state();
  663. void apply();
  664. void reset();
  665. //WTL message map
  666. BEGIN_MSG_MAP(CMyPreferences)
  667. MSG_WM_INITDIALOG(OnInitDialog)
  668. COMMAND_HANDLER_EX(IDC_DYNRNG, BN_CLICKED, OnButtonClick)
  669. END_MSG_MAP()
  670. private:
  671. BOOL OnInitDialog(CWindow, LPARAM);
  672. void OnButtonClick(UINT, int, CWindow);
  673. bool HasChanged();
  674. void OnChanged();
  675. const preferences_page_callback::ptr m_callback;
  676. };
  677. BOOL CMyPreferences::OnInitDialog(CWindow, LPARAM) {
  678. SendDlgItemMessage( IDC_DYNRNG, BM_SETCHECK, cfg_dynrng, FALSE );
  679. return FALSE;
  680. }
  681. void CMyPreferences::OnButtonClick(UINT, int, CWindow) {
  682. // not much to do here
  683. OnChanged();
  684. }
  685. t_uint32 CMyPreferences::get_state() {
  686. t_uint32 state = preferences_state::resettable;
  687. if (HasChanged()) state |= preferences_state::changed;
  688. return state;
  689. }
  690. void CMyPreferences::reset() {
  691. SendDlgItemMessage( IDC_DYNRNG, BM_SETCHECK, default_cfg_dynrng, FALSE );
  692. OnChanged();
  693. }
  694. void CMyPreferences::apply() {
  695. cfg_dynrng = SendDlgItemMessage( IDC_DYNRNG, BM_GETCHECK, FALSE, FALSE );
  696. OnChanged(); //our dialog content has not changed but the flags have - our currently shown values now match the settings so the apply button can be disabled
  697. }
  698. bool CMyPreferences::HasChanged() {
  699. //returns whether our dialog content is different from the current configuration (whether the apply button should be enabled or not)
  700. return SendDlgItemMessage( IDC_DYNRNG, BM_GETCHECK, FALSE, FALSE ) != cfg_dynrng;
  701. }
  702. void CMyPreferences::OnChanged() {
  703. //tell the host that our state has changed to enable/disable the apply button appropriately.
  704. m_callback->on_state_changed();
  705. }
  706. class preferences_page_myimpl : public preferences_page_impl<CMyPreferences> {
  707. // preferences_page_impl<> helper deals with instantiation of our dialog; inherits from preferences_page_v3.
  708. public:
  709. const char * get_name() {return input_ac3::g_get_name();}
  710. GUID get_guid() {return input_ac3::g_get_preferences_guid();}
  711. GUID get_parent_guid() {return guid_input;}
  712. };
  713. DECLARE_FILE_TYPE("AC3 files", "*.AC3");
  714. static input_singletrack_factory_t< input_ac3 > g_input_factory_ac3;
  715. static packet_decoder_factory_t< packet_decoder_ac3 > g_packet_decoder_factory_ac3;
  716. static preferences_page_factory_t< preferences_page_myimpl > g_preferences_page_factory_ac3;
  717. DECLARE_COMPONENT_VERSION("AC3 decoder", MY_VERSION, "Based on liba52 v0.7.4\n\nhttps://www.patreon.com/kode54");
  718. VALIDATE_COMPONENT_FILENAME("foo_ac3.dll");