module.audio.dsf.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org> //
  4. // available at https://github.com/JamesHeinrich/getID3 //
  5. // or https://www.getid3.org //
  6. // or http://getid3.sourceforge.net //
  7. // see readme.txt for more details //
  8. /////////////////////////////////////////////////////////////////
  9. // //
  10. // module.audio.dsf.php //
  11. // module for analyzing dsf/DSF Audio files //
  12. // dependencies: module.tag.id3v2.php //
  13. // ///
  14. /////////////////////////////////////////////////////////////////
  15. if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
  16. exit;
  17. }
  18. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
  19. class getid3_dsf extends getid3_handler
  20. {
  21. /**
  22. * @return bool
  23. */
  24. public function Analyze() {
  25. $info = &$this->getid3->info;
  26. $info['fileformat'] = 'dsf';
  27. $info['audio']['dataformat'] = 'dsf';
  28. $info['audio']['lossless'] = true;
  29. $info['audio']['bitrate_mode'] = 'cbr';
  30. $this->fseek($info['avdataoffset']);
  31. $dsfheader = $this->fread(28 + 12);
  32. $headeroffset = 0;
  33. $info['dsf']['dsd']['magic'] = substr($dsfheader, $headeroffset, 4);
  34. $headeroffset += 4;
  35. $magic = 'DSD ';
  36. if ($info['dsf']['dsd']['magic'] != $magic) {
  37. $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['dsf']['dsd']['magic']).'"');
  38. unset($info['fileformat']);
  39. unset($info['audio']);
  40. unset($info['dsf']);
  41. return false;
  42. }
  43. $info['dsf']['dsd']['dsd_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // should be 28
  44. $headeroffset += 8;
  45. $info['dsf']['dsd']['dsf_file_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
  46. $headeroffset += 8;
  47. $info['dsf']['dsd']['meta_chunk_offset'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
  48. $headeroffset += 8;
  49. $info['dsf']['fmt']['magic'] = substr($dsfheader, $headeroffset, 4);
  50. $headeroffset += 4;
  51. $magic = 'fmt ';
  52. if ($info['dsf']['fmt']['magic'] != $magic) {
  53. $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['fmt']['magic']).'"');
  54. return false;
  55. }
  56. $info['dsf']['fmt']['fmt_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // usually 52 bytes
  57. $headeroffset += 8;
  58. $dsfheader .= $this->fread($info['dsf']['fmt']['fmt_chunk_size'] - 12 + 12); // we have already read the entire DSD chunk, plus 12 bytes of FMT. We now want to read the size of FMT, plus 12 bytes into the next chunk to get magic and size.
  59. if (strlen($dsfheader) != ($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size'] + 12)) {
  60. $this->error('Expecting '.($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size']).' bytes header, found '.strlen($dsfheader).' bytes');
  61. return false;
  62. }
  63. $info['dsf']['fmt']['format_version'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "1"
  64. $headeroffset += 4;
  65. $info['dsf']['fmt']['format_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "0" = "DSD Raw"
  66. $headeroffset += 4;
  67. $info['dsf']['fmt']['channel_type_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
  68. $headeroffset += 4;
  69. $info['dsf']['fmt']['channels'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
  70. $headeroffset += 4;
  71. $info['dsf']['fmt']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
  72. $headeroffset += 4;
  73. $info['dsf']['fmt']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
  74. $headeroffset += 4;
  75. $info['dsf']['fmt']['sample_count'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
  76. $headeroffset += 8;
  77. $info['dsf']['fmt']['channel_block_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
  78. $headeroffset += 4;
  79. $info['dsf']['fmt']['reserved'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // zero-filled
  80. $headeroffset += 4;
  81. $info['dsf']['data']['magic'] = substr($dsfheader, $headeroffset, 4);
  82. $headeroffset += 4;
  83. $magic = 'data';
  84. if ($info['dsf']['data']['magic'] != $magic) {
  85. $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['data']['magic']).'"');
  86. return false;
  87. }
  88. $info['dsf']['data']['data_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
  89. $headeroffset += 8;
  90. $info['avdataoffset'] = $headeroffset;
  91. $info['avdataend'] = $info['avdataoffset'] + $info['dsf']['data']['data_chunk_size'];
  92. if ($info['dsf']['dsd']['meta_chunk_offset'] > 0) {
  93. $getid3_id3v2 = new getid3_id3v2($this->getid3);
  94. $getid3_id3v2->StartingOffset = $info['dsf']['dsd']['meta_chunk_offset'];
  95. $getid3_id3v2->Analyze();
  96. unset($getid3_id3v2);
  97. }
  98. $info['dsf']['fmt']['channel_type'] = $this->DSFchannelTypeLookup($info['dsf']['fmt']['channel_type_id']);
  99. $info['audio']['channelmode'] = $info['dsf']['fmt']['channel_type'];
  100. $info['audio']['bits_per_sample'] = $info['dsf']['fmt']['bits_per_sample'];
  101. $info['audio']['sample_rate'] = $info['dsf']['fmt']['sample_rate'];
  102. $info['audio']['channels'] = $info['dsf']['fmt']['channels'];
  103. $info['audio']['bitrate'] = $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'] * $info['audio']['channels'];
  104. $info['playtime_seconds'] = ($info['dsf']['data']['data_chunk_size'] * 8) / $info['audio']['bitrate'];
  105. return true;
  106. }
  107. /**
  108. * @param int $channel_type_id
  109. *
  110. * @return string
  111. */
  112. public static function DSFchannelTypeLookup($channel_type_id) {
  113. static $DSFchannelTypeLookup = array(
  114. // interleaving order:
  115. 1 => 'mono', // 1: Mono
  116. 2 => 'stereo', // 1: Front-Left; 2: Front-Right
  117. 3 => '3-channel', // 1: Front-Left; 2: Front-Right; 3: Center
  118. 4 => 'quad', // 1: Front-Left; 2: Front-Right; 3: Back-Left; 4: Back-Right
  119. 5 => '4-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency
  120. 6 => '5-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Back-Left 5: Back-Right
  121. 7 => '5.1', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency; 5: Back-Left; 6: Back-Right
  122. );
  123. return (isset($DSFchannelTypeLookup[$channel_type_id]) ? $DSFchannelTypeLookup[$channel_type_id] : '');
  124. }
  125. }