首页 > 开发 > PHP > 正文

PHP获取音频文件的相关信息

2024-05-04 23:36:46
字体:大 中 小
来源:转载
供稿:网友

这篇文章主要介绍了PHP获取音频文件的相关信息的相关资料,非常的实用,有需要的小伙伴可以参考下。

项目需求:现在有一个音频文件上传的功能,在上传后PHP需要获取这个音频文件的相关信息,例如:时长等,由于这个文件是放在买的空间上的,没有像ffmpeg这样的扩展来处理,那么PHP能不能获取到这些信息?

下面是之前在项目中用到的一个用PHP进行音频文件头部信息的读取与写入操作的实现,主要针对 WMA 和 MP3 两种格式,供参考。

 

 
  1. <?php 
  2. // AudioExif.class.php 
  3. // 用PHP进行音频文件头部信息的读取与写入 
  4. // 目前只支持 WMA 和 MP3 两种格式, 只支持常用的几个头部信息 
  5. // 
  6. // 写入信息支持: Title(名称), Artist(艺术家), Copyright(版权), Description (描述) 
  7. // Year(年代), Genre (流派), AlbumTitle (专辑标题) 
  8. // 其中 mp3 和 wma 略有不同, 具体返回的信息还可能更多, 但只有以上信息可以被写入 
  9. // mp3 还支持 Track (曲目编号写入) 
  10. // 对于 MP3 文件支持 ID3v1也支持ID3v2, 读取时优先 v2, 写入时总是会写入v1, 必要时写入v2 
  11. // 
  12. // 用法说明: (由于 wma 使用 Unicode 存取, 故还需要 mb_convert_encoding() 扩展 
  13. // 返回数据及写入数据均为 ANSI 编码, 即存什么就显示什么 (中文_GB2312) 
  14. // 
  15. // require ('AudioExif.class.php'); 
  16. // $AE = new AudioExif; 
  17. // $file = '/path/to/test.mp3'; 
  18. // 
  19. // 1. 检查文件是否完整 (only for wma, mp3始终返回 true) 
  20. // 
  21. // $AE->CheckSize($file); 
  22. // 
  23. // 2. 读取信息, 返回值由信息组成的数组, 键名解释参见上方 
  24. // 
  25. // print_r($AE->GetInfo($file)); 
  26. // 
  27. // 3. 写入信息, 第二参数是一个哈希数组, 键->值, 支持的参见上方的, mp3也支持 Track 
  28. // 要求第一参数的文件路径可由本程序写入 
  29. // $pa = array('Title' => '新标题', 'AlbumTitle' => '新的专辑名称'); 
  30. // $AE->SetInfo($file, $pa); 
  31. // 
  32. // 其它: 该插件花了不少时间搜集查找 wma及mp3 的文件格式说明文档与网页, 希望对大家有用. 
  33. // 其实网上已经有不少类似的程序, 但对 wma 实在太少了, 只能在 win 平台下通过 M$ 的 
  34. // API 来操作, 而 MP3 也很少有可以在 unix/linux 命令行操作的, 所以特意写了这个模块 
  35. // 
  36. // 如果发现 bug 或提交 patch, 或加以改进使它更加健壮, 请告诉我. 
  37. // (关于 ID3和Wma的文件格式及结构 在网上应该都可以找到参考资料) 
  38. // 
  39. if (!extension_loaded('mbstring')){ 
  40. trigger_error('PHP Extension module `mbstring` is required for AudioExif', E_USER_WARNING); 
  41. return true; 
  42. } 
  43. // the Main Class 
  44. class AudioExif{ 
  45. // public vars 
  46. var $_wma = false; 
  47. var $_mp3 = false; 
  48. // Construct 
  49. function AudioExif() { 
  50. // nothing to do 
  51. } 
  52. // check the filesize 
  53. function CheckSize($file) { 
  54. $handler = &$this->_get_handler($file); 
  55. if (!$handler) return false; 
  56. return $handler->check_size($file); 
  57. } 
  58. // get the infomations 
  59. function GetInfo($file) { 
  60. $handler = &$this->_get_handler($file); 
  61. if (!$handler) return false; 
  62. return $handler->get_info($file); 
  63. } 
  64. // write the infomations 
  65. function SetInfo($file, $pa) { 
  66. if (!is_writable($file)) { 
  67. trigger_error('AudioExif: file `' . $file . '` can not been overwritten', E_USER_WARNING); 
  68. return false; 
  69. } 
  70. $handler = &$this->_get_handler($file); 
  71. if (!$handler) return false; 
  72. return $handler->set_info($file, $pa); 
  73. } 
  74. // private methods 
  75. function &_get_handler($file) { 
  76. $ext = strtolower(strrchr($file, '.')); 
  77. $ret = false; 
  78. if ($ext == '.mp3') { 
  79. // MP3 
  80. $ret = &$this->_mp3; 
  81. if (!$ret) $ret = new _Mp3Exif(); 
  82.  
  83. } 
  84. else if ($ext == '.wma') 
  85. { // wma 
  86. $ret = &$this->_wma; 
  87. if (!$ret) $ret = new _WmaExif(); 
  88. } 
  89. else 
  90. { // unknown 
  91. trigger_error('AudioExif not supported `' . $ext . '` file.', E_USER_WARNING); 
  92. } 
  93. return $ret; 
  94. } 
  95. } 
  96.  
  97. // DBCS => gb2312 
  98. function dbcs_gbk($str) 
  99. { 
  100. // strip the last "/0/0" 
  101. $str = substr($str, 0, -2); 
  102. return mb_convert_encoding($str, 'GBK', 'UCS-2LE'); 
  103. } 
  104.  
  105. // gb2312 => DBCS 
  106. function gbk_dbcs($str) 
  107. { 
  108. $str = mb_convert_encoding($str, 'UCS-2LE', 'GBK'); 
  109. $str .= "/0/0"; 
  110. return $str; 
  111. } 
  112.  
  113. // file exif 
  114. class _AudioExif 
  115. { 
  116. var $fd; 
  117. var $head; 
  118. var $head_off; 
  119. var $head_buf; 
  120.  
  121. // init the file handler 
  122. function _file_init($fpath, $write = false) 
  123. { 
  124. $mode = ($write ? 'rb+' : 'rb'); 
  125. $this->fd = @fopen($fpath, $mode); 
  126. if (!$this->fd) 
  127. { 
  128. trigger_error('AudioExif: `' . $fpath . '` can not be opened with mode `' . $mode . '`', E_USER_WARNING); 
  129. return false; 
  130. } 
  131. $this->head = false; 
  132. $this->head_off = 0; 
  133. $this->head_buf = ''; 
  134. return true; 
  135. } 
  136.  
  137. // read buffer from the head_buf & move the off pointer 
  138. function _read_head_buf($len) 
  139. { 
  140. if ($len <= 0) return NULL; 
  141. $buf = substr($this->head_buf, $this->head_off, $len); 
  142. $this->head_off += strlen($buf); 
  143. return $buf; 
  144. } 
  145.  
  146. // read one short value 
  147. function _read_head_short() 
  148. { 
  149. $ord1 = ord(substr($this->head_buf, $this->head_off, 1)); 
  150. $ord2 = ord(substr($this->head_buf, $this->head_off+1, 1)); 
  151. $this->head_off += 2; 
  152. return ($ord1 + ($ord2<<8)); 
  153. } 
  154.  
  155. // save the file head 
  156. function _file_save($head, $olen, $nlen = 0) 
  157. { 
  158. if ($nlen == 0) $nlen = strlen($head); 
  159. if ($nlen == $olen) 
  160. { 
  161. // shorter 
  162. flock($this->fd, LOCK_EX); 
  163. fseek($this->fd, 0, SEEK_SET); 
  164. fwrite($this->fd, $head, $nlen); 
  165. flock($this->fd, LOCK_UN); 
  166. } 
  167. else 
  168. { 
  169. // longer, buffer required 
  170. $stat = fstat($this->fd); 
  171. $fsize = $stat['size']; 
  172.  
  173. // buf required (4096?) 应该不会 nlen - olen > 4096 吧 
  174. $woff = 0; 
  175. $roff = $olen; 
  176.  
  177. // read first buffer 
  178. flock($this->fd, LOCK_EX); 
  179. fseek($this->fd, $roff, SEEK_SET); 
  180. $buf = fread($this->fd, 4096); 
  181.  
  182. // seek to start 
  183. fseek($this->fd, $woff, SEEK_SET); 
  184. fwrite($this->fd, $head, $nlen); 
  185. $woff += $nlen; 
  186.  
  187. // seek to woff & write the data 
  188. do 
  189. { 
  190. $buf2 = $buf; 
  191. $roff += 4096; 
  192. if ($roff < $fsize) 
  193. { 
  194. fseek($this->fd, $roff, SEEK_SET); 
  195. $buf = fread($this->fd, 4096); 
  196. } 
  197.  
  198. // save last buffer 
  199. $len2 = strlen($buf2); 
  200. fseek($this->fd, $woff, SEEK_SET); 
  201. fwrite($this->fd, $buf2, $len2); 
  202. $woff += $len2; 
  203. } 
  204. while ($roff < $fsize); 
  205. ftruncate($this->fd, $woff); 
  206. flock($this->fd, LOCK_UN); 
  207. } 
  208. } 
  209.  
  210. // close the file 
  211. function _file_deinit() 
  212. { 
  213. if ($this->fd) 
  214. { 
  215. fclose($this->fd); 
  216. $this->fd = false; 
  217. } 
  218. } 
  219. } 
  220.  
  221. // wma class 
  222. class _WmaExif extends _AudioExif 
  223. { 
  224. var $items1 = array('Title', 'Artist', 'Copyright', 'Description', 'Reserved'); 
  225. var $items2 = array('Year', 'Genre', 'AlbumTitle'); 
  226.  
  227. // check file size (length) maybe invalid file 
  228. function check_size($file) 
  229. { 
  230. $ret = false; 
  231. if (!$this->_file_init($file)) return true; 
  232. if ($this->_init_header()) 
  233. { 
  234. $buf = fread($this->fd, 24); 
  235. $tmp = unpack('H32id/Vlen/H8unused', $buf); 
  236. if ($tmp['id'] == '3626b2758e66cf11a6d900aa0062ce6c') 
  237. { 
  238. $stat = fstat($this->fd); 
  239. $ret = ($stat['size'] == ($this->head['len'] + $tmp['len'])); 
  240. } 
  241. } 
  242. $this->_file_deinit(); 
  243. return $ret; 
  244. } 
  245.  
  246. // set info (save the infos) 
  247. function set_info($file, $pa) 
  248. { 
  249. // check the pa 
  250. settype($pa, 'array'); 
  251. if (!$this->_file_init($file, true)) return false; 
  252. if (!$this->_init_header()) 
  253. { 
  254. $this->_file_deinit(); 
  255. return false; 
  256. } 
  257.  
  258. // parse the old header & generate the new header 
  259. $head_body = ''; 
  260. $st_found = $ex_found = false; 
  261. $head_num = $this->head['num']; 
  262. while (($tmp = $this->_get_head_frame()) && ($head_num > 0)) 
  263. { 
  264. $head_num--; 
  265. if ($tmp['id'] == '3326b2758e66cf11a6d900aa0062ce6c') 
  266. { // Standard Info 
  267. // 1-4 
  268. $st_found = true; 
  269. $st_body1 = $st_body2 = ''; 
  270. $lenx = unpack('v5', $this->_read_head_buf(10)); 
  271. $tmp['len'] -= 34; // 10 + 24 
  272. for ($i = 0; $i < count($this->items1); $i++) 
  273. { 
  274. $l = $lenx[$i+1]; 
  275. $k = $this->items1[$i]; 
  276. $tmp['len'] -= $l; 
  277.  
  278. $data = $this->_read_head_buf($l); 
  279. if (isset($pa[$k])) $data = gbk_dbcs($pa[$k]); 
  280.  
  281. $st_body2 .= $data; 
  282. $st_body1 .= pack('v', strlen($data)); 
  283. } 
  284. // left length 
  285. if ($tmp['len'] > 0) $st_body2 .= $this->_read_head_buf($tmp['len']); 
  286.  
  287. // save to head_body 
  288. $head_body .= pack('H32VH8', $tmp['id'], strlen($st_body1 . $st_body2)+24, $tmp['unused']); 
  289. $head_body .= $st_body1 . $st_body2; 
  290.  
  291. $st_body2 .= $data; 
  292. } 
  293.  
  294. // save to head_body 
  295. $head_body .= pack('H32Va4', '3326b2758e66cf11a6d900aa0062ce6c', strlen($st_body1 . $st_body2)+24, ''); 
  296. $head_body .= $st_body1 . $st_body2; 
  297. $this->head['num']++; 
  298. } 
  299. // ex not found? 
  300. if (!$ex_found) 
  301. { 
  302. $inum = 0; 
  303. $et_body = ''; 
  304. foreach ($this->items2 as $k) 
  305. { 
  306. $nbuf = gbk_dbcs('WM/' . $k); 
  307. $vbuf = (isset($pa[$k]) ? gbk_dbcs($pa[$k]) : ""); 
  308. $et_body .= pack('v', strlen($nbuf)) . $nbuf . pack('vv', 0, strlen($vbuf)) . $vbuf; 
  309. $inum++; 
  310. } 
  311. $head_body .= pack('H32Va4v', '40a4d0d207e3d21197f000a0c95ea850', strlen($et_body)+26, '', $inum); 
  312. $head_body .= $et_body; 
  313. $this->head['num']++; 
  314. } 
  315.  
  316. // after save 
  317. $new_len = strlen($head_body) + 30; 
  318. $old_len = $this->head['len']; 
  319. if ($new_len < $old_len) 
  320. { 
  321. $head_body .= str_repeat("/0", $old_len - $new_len); 
  322. $new_len = $old_len; 
  323. } 
  324. $tmp = $this->head; 
  325. $head_buf = pack('H32VVVH4', $tmp['id'], $new_len, $tmp['len2'], $tmp['num'], $tmp['unused']); 
  326. $head_buf .= $head_body; 
  327. $this->_file_save($head_buf, $old_len, $new_len); 
  328.  
  329. // close the file & return 
  330. $this->_file_deinit(); 
  331. return true; 
  332. } 
  333.  
  334. // get info 
  335. function get_info($file) 
  336. { 
  337. $ret = array(); 
  338. if (!$this->_file_init($file)) return false; 
  339. if (!$this->_init_header()) 
  340. { 
  341. $this->_file_deinit(); 
  342. return false; 
  343. } 
  344.  
  345. // get the data from head_buf 
  346. $head_num = $this->head['num']; // num of head_frame 
  347. while (($tmp = $this->_get_head_frame()) && $head_num > 0) 
  348. { 
  349. $head_num--; 
  350. if ($tmp['id'] == '3326b2758e66cf11a6d900aa0062ce6c') 
  351. { // Standard Info 
  352. $lenx = unpack('v*', $this->_read_head_buf(10)); 
  353. for ($i = 1; $i <= count($this->items1); $i++) 
  354. { 
  355. $k = $this->items1[$i-1]; 
  356. $ret[$k] = dbcs_gbk($this->_read_head_buf($lenx[$i])); 
  357. } 
  358. } 
  359. else if ($tmp['id'] == '40a4d0d207e3d21197f000a0c95ea850') 
  360. { // Extended Info 
  361. $inum = $this->_read_head_short(); 
  362. $tmp['len'] -= 26; 
  363. while ($inum > 0 && $tmp['len'] > 0) 
  364. { 
  365. // attribute name 
  366. $nlen = $this->_read_head_short(); 
  367. $nbuf = $this->_read_head_buf($nlen); 
  368.  
  369. // the flag & value length 
  370. $flag = $this->_read_head_short(); 
  371. $vlen = $this->_read_head_short(); 
  372. $vbuf = $this->_read_head_buf($vlen); 
  373.  
  374. // update the XX 
  375.  
  376. $tmp['len'] -= (6 + $nlen + $vlen); 
  377. $inum--; 
  378.  
  379. $name = dbcs_gbk($nbuf); 
  380. $k = substr($name, 3); 
  381. if (in_array($k, $this->items2)) 
  382. { // all is string value (refer to falg for other tags) 
  383. $ret[$k] = dbcs_gbk($vbuf); 
  384. } 
  385. } 
  386. } 
  387.  
  388. else 
  389. { // skip only 
  390. if ($tmp['len'] > 24) $this->head_off += ($tmp['len'] - 24); 
  391. } 
  392. } 
  393. $this->_file_deinit(); 
  394. return $ret; 
  395. } 
  396.  
  397. // get the header? 
  398. function _init_header() 
  399. { 
  400. fseek($this->fd, 0, SEEK_SET); 
  401. $buf = fread($this->fd, 30); 
  402. if (strlen($buf) != 30) return false; 
  403. $tmp = unpack('H32id/Vlen/Vlen2/Vnum/H4unused', $buf); 
  404. if ($tmp['id'] != '3026b2758e66cf11a6d900aa0062ce6c') 
  405. return false; 
  406.  
  407. $this->head_buf = fread($this->fd, $tmp['len'] - 30); 
  408. $this->head = $tmp; 
  409. return true; 
  410. } 
  411.  
  412. // _get_head_frame() 
  413. function _get_head_frame() 
  414. { 
  415. $buf = $this->_read_head_buf(24); 
  416. if (strlen($buf) != 24) return false; 
  417. $tmp = unpack('H32id/Vlen/H8unused', $buf); 
  418. return $tmp; 
  419. } 
  420. } 
  421.  
  422. // mp3 class (if not IDv2 then select IDv1) 
  423. class _Mp3Exif extends _AudioExif 
  424. { 
  425. var $head1; 
  426. var $genres = array('Blues','Classic Rock','Country','Dance','Disco','Funk','Grunge','Hip-Hop','Jazz','Metal','New Age','Oldies','Other','Pop','R&B','Rap','Reggae','Rock','Techno','Industrial','Alternative','Ska','Death Metal','Pranks','Soundtrack','Euro-Techno','Ambient','Trip-Hop','Vocal','Jazz+Funk','Fusion','Trance','Classical','Instrumental','Acid','House','Game','Sound Clip','Gospel','Noise','AlternRock','Bass','Soul','Punk','Space','Meditative','Instrumental Pop','Instrumental Rock','Ethnic','Gothic','Darkwave','Techno-Industrial','Electronic','Pop-Folk','Eurodance','Dream','Southern Rock','Comedy','Cult','Gangsta','Top 40','Christian Rap','Pop/Funk','Jungle','Native American','Cabaret','New Wave','Psychadelic','Rave','Showtunes','Trailer','Lo-Fi','Tribal','Acid Punk','Acid Jazz','Polka','Retro','Musical','Rock & Roll','Hard Rock','Unknown'); 
  427.  
  428. // MP3 always return true 
  429. function check_size($file) 
  430. { 
  431. return true; 
  432. } 
  433.  
  434. // get info 
  435. function get_info($file) 
  436. { 
  437. if (!$this->_file_init($file)) return false; 
  438. $ret = false; 
  439. if ($this->_init_header()) 
  440. { 
  441. $ret = ($this->head ? $this->_get_v2_info() : $this->_get_v1_info()); 
  442. $ret['meta'] = $this->_get_meta_info(); 
  443. } 
  444. $this->_file_deinit(); 
  445. return $ret; 
  446. } 
  447.  
  448. // set info 
  449. function set_info($file, $pa) 
  450. { 
  451. if (!$this->_file_init($file, true)) return false; 
  452. if ($this->_init_header()) 
  453. { 
  454. // always save v1 info 
  455. $this->_set_v1_info($pa); 
  456. // set v2 first if need 
  457. $this->_set_v2_info($pa); 
  458. } 
  459. $this->_file_deinit(); 
  460. return true; 
  461. } 
  462.  
  463. // get the header information[v1+v2], call after file_init 
  464. function _init_header() 
  465. { 
  466. $this->head1 = false; 
  467. $this->head = false; 
  468.  
  469. // try to get ID3v1 first 
  470. fseek($this->fd, -128, SEEK_END); 
  471. $buf = fread($this->fd, 128); 
  472.  
  473. if (strlen($buf) == 128 && substr($buf, 0, 3) == 'TAG') 
  474. { 
  475. $tmp = unpack('a3id/a30Title/a30Artist/a30AlbumTitle/a4Year/a28Description/CReserved/CTrack/CGenre', $buf); 
  476. $this->head1 = $tmp; 
  477. } 
  478.  
  479. // try to get ID3v2 
  480. fseek($this->fd, 0, SEEK_SET); 
  481. $buf = fread($this->fd, 10); 
  482. if (strlen($buf) == 10 && substr($buf, 0, 3) == 'ID3') 
  483. { 
  484. $tmp = unpack('a3id/Cver/Crev/Cflag/C4size', $buf); 
  485. $tmp['size'] = ($tmp['size1']<<21)|($tmp['size2']<<14)|($tmp['size3']<<7)|$tmp['size4']; 
  486. unset($tmp['size1'], $tmp['size2'], $tmp['size3'], $tmp['size4']); 
  487.  
  488. $this->head = $tmp; 
  489. $this->head_buf = fread($this->fd, $tmp['size']); 
  490. } 
  491. return ($this->head1 || $this->head); 
  492. } 
  493.  
  494. // get v1 info 
  495. function _get_v1_info() 
  496. { 
  497. $ret = array(); 
  498. $tmpa = array('Title', 'Artist', 'Copyright', 'Description', 'Year', 'AlbumTitle'); 
  499. foreach ($tmpa as $tmp) 
  500. { 
  501. $ret[$tmp] = $this->head1[$tmp]; 
  502. if ($pos = strpos($ret[$tmp], "/0")) 
  503. $ret[$tmp] = substr($ret[$tmp], 0, $pos); 
  504. } 
  505.  
  506. // count the Genre, [Track] 
  507. if ($this->head1['Reserved'] == 0) $ret['Track'] = $this->head1['Track']; 
  508. else $ret['Description'] .= chr($ret['Reserved']) . chr($ret['Track']); 
  509.  
  510. // Genre_idx 
  511. $g = $this->head1['Genre']; 
  512. if (!isset($this->genres[$g])) $ret['Genre'] = 'Unknown'; 
  513. else $ret['Genre'] = $this->genres[$g]; 
  514.  
  515. // return the value 
  516. $ret['ID3v1'] = 'yes'; 
  517. return $ret; 
  518. } 
  519.  
  520. // get v2 info 
  521. function _get_v2_info() 
  522. { 
  523. $ret = array(); 
  524. $items = array( 'TCOP'=>'Copyright', 'TPE1'=>'Artist', 'TIT2'=>'Title', 'TRCK'=> 'Track', 
  525. 'TCON'=>'Genre', 'COMM'=>'Description', 'TYER'=>'Year', 'TALB'=>'AlbumTitle'); 
  526. while (true) 
  527. { 
  528. $buf = $this->_read_head_buf(10); 
  529. if (strlen($buf) != 10) break; 
  530. $tmp = unpack('a4fid/Nsize/nflag', $buf); 
  531. if ($tmp['size'] == 0) break; 
  532. $tmp['dat'] = $this->_read_head_buf($tmp['size']); 
  533.  
  534. // 0x6000 (11000000 00000000) 
  535. if ($tmp['flag'] & 0x6000) continue; 
  536.  
  537. // mapping the data 
  538. if ($k = $items[$tmp['fid']]) 
  539. { // If first char is "/0", just skip 
  540. if (substr($tmp['dat'], 0, 1) == "/0") $tmp['dat'] = substr($tmp['dat'], 1); 
  541. $ret[$k] = $tmp['dat']; 
  542. } 
  543. } 
  544.  
  545. // reset the genre 
  546. if ($g = $ret['Genre']) 
  547. { 
  548. if (substr($g,0,1) == '(' && substr($g,-1,1) == ')') $g = substr($g, 1, -1); 
  549. if (is_numeric($g)) 
  550. { 
  551. $g = intval($g); 
  552. $ret['Genre'] = (isset($this->genres[$g]) ? $this->genres[$g] : 'Unknown'); 
  553. } 
  554. } 
  555.  
  556. $ret['ID3v1'] = 'no'; 
  557. return $ret; 
  558. } 
  559. // get meta info of MP3 
  560. function _get_meta_info() 
  561. { 
  562. // seek to the lead buf: 0xff 
  563. $off = 0; 
  564. if ($this->head) $off = $this->head['size'] + 10; 
  565. fseek($this->fd, $off, SEEK_SET); 
  566. while (!feof($this->fd)) 
  567. { 
  568. $skip = ord(fread($this->fd, 1)); 
  569. if ($skip == 0xff) break; 
  570. } 
  571. if ($skip != 0xff) return false; 
  572. $buf = fread($this->fd, 3); 
  573. if (strlen($buf) != 3) return false; 
  574. $tmp = unpack('C3', $buf); 
  575. if (($tmp[1] & 0xf0) != 0xf0) return false; 
  576. // get the meta info 
  577. $meta = array(); 
  578. // get mpeg version 
  579. $meta['mpeg'] = ($tmp[1] & 0x08 ? 1 : 2); 
  580. $meta['layer'] = ($tmp[1] & 0x04) ? (($tmp[1] & 0x02) ? 1 : 2) : (($tmp[1] & 0x02) ? 3 : 0); 
  581. $meta['epro'] = ($tmp[1] & 0x01) ? 'no' : 'yes'; 
  582. // bit rates 
  583. $bit_rates = array( 
  584. 1 => array( 
  585. 1 => array(0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0), 
  586. 2 => array(0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0), 
  587. 3 => array(0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0) 
  588. ), 
  589. 2 => array( 
  590. 1 => array(0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0), 
  591. 2 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0), 
  592. 3 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0) 
  593. ) 
  594. ); 
  595. $i = $meta['mpeg']; 
  596. $j = $meta['layer']; 
  597. $k = ($tmp[2]>>4); 
  598. $meta['bitrate'] = $bit_rates[$i][$j][$k]; 
  599. // sample rates <采样率> 
  600. $sam_rates = array(1=>array(44100,48000,32000,0), 2=>array(22050,24000,16000,0)); 
  601. $meta['samrate'] = $sam_rates[$i][$k]; 
  602. $meta["padding"] = ($tmp[2] & 0x02) ? 'on' : 'off'; 
  603. $meta["private"] = ($tmp[2] & 0x01) ? 'on' : 'off'; 
  604. // mode & mode_ext 
  605. $k = ($tmp[3]>>6); 
  606. $channel_modes = array('stereo', 'joint stereo', 'dual channel', 'single channel'); 
  607. $meta['mode'] = $channel_modes[$k]; 
  608. $k = (($tmp[3]>>4) & 0x03); 
  609. $extend_modes = array('MPG_MD_LR_LR', 'MPG_MD_LR_I', 'MPG_MD_MS_LR', 'MPG_MD_MS_I'); 
  610. $meta['ext_mode'] = $extend_modes[$k]; 
  611. $meta['copyright'] = ($tmp[3] & 0x08) ? 'yes' : 'no'; 
  612. $meta['original'] = ($tmp[3] & 0x04) ? 'yes' : 'no'; 
  613. $emphasis = array('none', '50/15 microsecs', 'rreserved', 'CCITT J 17'); 
  614. $k = ($tmp[3] & 0x03); 
  615. $meta['emphasis'] = $emphasis[$k]; 
  616. return $meta; 
  617. } 
  618. // set v1 info 
  619. function _set_v1_info($pa) 
  620. { 
  621. // ID3v1 (simpled) 
  622. $off = -128; 
  623. if (!($tmp = $this->head1)) 
  624. { 
  625. $off = 0; 
  626. $tmp['id'] = 'TAG'; 
  627. $tmp['Title'] = $tmp['Artist'] = $tmp['AlbumTitle'] = $tmp['Year'] = $tmp['Description'] = ''; 
  628. $tmp['Reserved'] = $tmp['Track'] = $tmp['Genre'] = 0; 
  629. } 
  630.  
  631. // basic items 
  632. $items = array('Title', 'Artist', 'Copyright', 'Description', 'Year', 'AlbumTitle'); 
  633. foreach ($items as $k) 
  634. { 
  635. if (isset($pa[$k])) $tmp[$k] = $pa[$k]; 
  636. } 
  637. // genre index 
  638. if (isset($pa['Genre'])) 
  639. { 
  640. $g = 0; 
  641. foreach ($this->genres as $gtmp) 
  642. { 
  643. if (!strcasecmp($gtmp, $pa['Genre'])) 
  644. break; 
  645. $g++; 
  646. } 
  647. $tmp['Genre'] = $g; 
  648. } 
  649. if (isset($pa['Track'])) $tmp['Track'] = intval($pa['Track']); 
  650. // pack the data 
  651. $buf = pack('a3a30a30a30a4a28CCC', $tmp['id'], $tmp['Title'], $tmp['Artist'], $tmp['AlbumTitle'], 
  652. $tmp['Year'], $tmp['Description'], 0, $tmp['Track'], $tmp['Genre']); 
  653. flock($this->fd, LOCK_EX); 
  654. fseek($this->fd, $off, SEEK_END); 
  655. fwrite($this->fd, $buf, 128); 
  656. flock($this->fd, LOCK_UN); 
  657. } 
  658. // set v2 info 
  659. function _set_v2_info($pa) 
  660. { 
  661. if (!$this->head) 
  662. { // insert ID3 
  663. return; // 没有就算了 
  664. /** 
  665. $tmp = array('id'=>'ID3','ver'=>3,'rev'=>0,'flag'=>0); 
  666. $tmp['size'] = -10; // +10 => 0 
  667. $this->head = $tmp; 
  668. $this->head_buf = ''; 
  669. $this->head_off = 0; 
  670. **/ 
  671. } 
  672. $items = array( 'TCOP'=>'Copyright', 'TPE1'=>'Artist', 'TIT2'=>'Title', 'TRAC'=>'Track', 
  673. 'TCON'=>'Genre', 'COMM'=>'Description', 'TYER'=>'Year', 'TALB'=>'AlbumTitle'); 
  674. $head_body = ''; 
  675. while (true) 
  676. { 
  677. $buf = $this->_read_head_buf(10); 
  678. if (strlen($buf) != 10) break; 
  679. $tmp = unpack('a4fid/Nsize/nflag', $buf); 
  680. if ($tmp['size'] == 0) break; 
  681. $data = $this->_read_head_buf($tmp['size']); 
  682. if (($k = $items[$tmp['fid']]) && isset($pa[$k])) 
  683. { 
  684. // the data should prefix by "/0" [replace] 
  685. $data = "/0" . $pa[$k]; 
  686. unset($pa[$k]); 
  687. } 
  688. $head_body .= pack('a4Nn', $tmp['fid'], strlen($data), $tmp['flag']) . $data; 
  689. } 
  690. // reverse the items & set the new tags 
  691. $items = array_flip($items); 
  692. foreach ($pa as $k => $v) 
  693. { 
  694. if ($fid = $items[$k]) 
  695. { 
  696. $head_body .= pack('a4Nn', $fid, strlen($v) + 1, 0) . "/0" . $v; 
  697. } 
  698. } 
  699. // new length 
  700. $new_len = strlen($head_body) + 10; 
  701. $old_len = $this->head['size'] + 10; 
  702. if ($new_len < $old_len) 
  703. { 
  704. $head_body .= str_repeat("/0", $old_len - $new_len); 
  705. $new_len = $old_len; 
  706. } 
  707. // count the size1,2,3,4, no include the header 
  708. // 较为变态的算法... :p (28bytes integer) 
  709. $size = array(); 
  710. $nlen = $new_len - 10; 
  711. for ($i = 4; $i > 0; $i--) 
  712. { 
  713. $size[$i] = ($nlen & 0x7f); 
  714. $nlen >>= 7; 
  715. } 
  716. $tmp = $this->head; 
  717. //echo "old_len : $old_len new_len: $new_len/n"; 
  718. $head_buf = pack('a3CCCCCCC', $tmp['id'], $tmp['ver'], $tmp['rev'], $tmp['flag'], 
  719. $size[1], $size[2], $size[3], $size[4]); 
  720. $head_buf .= $head_body; 
  721. $this->_file_save($head_buf, $old_len, $new_len); 
  722. } 

以上所述就是本文的全部内容了,希望大家能够喜欢。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表