<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></title> 
<link>http://www.jackxiang.com/index.php</link> 
<description><![CDATA[赢在IT，Playin' with IT,Focus on Killer Application,Marketing Meets Technology.]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></copyright>
<item>
<link>http://www.jackxiang.com/post//</link>
<title><![CDATA[[源码学习] 用PHP读写音频文件的信息(支持WMA和MP3)]]></title> 
<author>jack &lt;xdy108@126.com&gt;</author>
<category><![CDATA[WEB2.0]]></category>
<pubDate>Wed, 18 Apr 2007 01:30:33 +0000</pubDate> 
<guid>http://www.jackxiang.com/post//</guid> 
<description>
<![CDATA[ 
	花了不少时间和心思作了这个东西, 使用PHP去读取或修改 MP3或WMA文件的头信息（主要有专辑，歌名，歌手等） &nbsp;<br/><br/>其实网上搜索有不少 MP3 的类似程序，但 WMA 的几乎没有，如果有也是windows平台下直接使用API的，想在 Unix/Linux 下使用简直没门。。。。 <br/><br/>由于我的音乐站刚开通，需要这方面的功能，所以就下决心花时间去查资料搞清楚MP3和wma的数据头格式，全部代码由PHP编写，有兴趣可以仔细研究，或加以完善。 <br/><br/>其实这东西技术上并没有难点，麻烦在于先要搞清楚它的结构和格式，这个代码也是一个使用PHP操作二进制(binary data)的例子，这几天群里刚好也有人问起这个，主要就是pack和unpack2个函数啦，呵呵，像MP3的原始头信息几乎是一个bit表示一个信息，这时就要用到大量的“按位操作”来分析它。 <br/><br/>下面是源码照贴（含例子），呵，这也作为实习版主的一个小贡献吧，挺希望这里能见到一些原创并且对它人有用的代码（类库）出现。！！在此抛砖先了。 &nbsp;<br/><br/><br/><?php<br/>// AudioExif.class.php<br/>// 用PHP进行音频文件头部信息的读取与写入<br/>// 目前只支持 WMA 和 MP3 两种格式, 只支持常用的几个头部信息<br/>//<br/>// 写入信息支持: Title(名称), Artist(艺术家), Copyright(版权), Description (描述)<br/>// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Year(年代), &nbsp;Genre (流派), &nbsp; AlbumTitle (专辑标题)<br/>// 其中 mp3 和 wma 略有不同, 具体返回的信息还可能更多, 但只有以上信息可以被写入<br/>// mp3 还支持 Track (曲目编号写入)<br/>// 对于 MP3 文件支持 ID3v1也支持ID3v2, 读取时优先 v2, 写入时总是会写入v1, 必要时写入v2<br/>//<br/>// 用法说明: (由于 wma 使用 Unicode 存取, 故还需要 mb_convert_encoding() 扩展<br/>// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 返回数据及写入数据均为 ANSI 编码, 即存什么就显示什么 (中文_GB2312)<br/>//<br/>// require ('AudioExif.class.php');<br/>// $AE = new AudioExif;<br/>// $file = '/path/to/test.mp3';<br/>//<br/>// 1. 检查文件是否完整 (only for wma, mp3始终返回 true)<br/>// <br/>// $AE->CheckSize($file);<br/>//<br/>// 2. 读取信息, 返回值由信息组成的数组, 键名解释参见上方<br/>//<br/>// print_r($AE->GetInfo($file));<br/>//<br/>// 3. 写入信息, 第二参数是一个哈希数组, 键->值, 支持的参见上方的, mp3也支持 Track<br/>// &nbsp; &nbsp;要求第一参数的文件路径可由本程序写入<br/>// $pa = array('Title' => '新标题', 'AlbumTitle' => '新的专辑名称');<br/>// $AE->SetInfo($file, $pa);<br/>//<br/>// 版本: 0.1<br/>// 作者: hightman<br/>// QQ群: 17708754 &nbsp;(非纯PHP进阶交流群)<br/>// 时间: 2007/01/25<br/>// 其它: 该插件花了不少时间搜集查找 wma及mp3 的文件格式说明文档与网页, 希望对大家有用.<br/>// &nbsp; &nbsp; &nbsp; 其实网上已经有不少类似的程序, 但对 wma 实在太少了, 只能在 win 平台下通过 M$ 的<br/>// &nbsp; &nbsp; &nbsp; API 来操作, 而 MP3 也很少有可以在 unix/linux 命令行操作的, 所以特意写了这个模块<br/>//<br/>// 如果发现 bug 或提交 patch, 或加以改进使它更加健壮, 请告诉我. <br/>// (关于 ID3和Wma的文件格式及结构 在网上应该都可以找到参考资料)<br/>//<br/><br/>if (!extension_loaded('mbstring'))<br/>&#123;<br/> &nbsp;trigger_error('PHP Extension module `mbstring` is required for AudioExif', E_USER_WARNING);<br/> &nbsp;return true;<br/>&#125;<br/><br/>// the Main Class<br/>class AudioExif<br/>&#123;<br/> &nbsp;// public vars<br/> &nbsp;var $_wma = false;<br/> &nbsp;var $_mp3 = false;<br/><br/> &nbsp;// Construct<br/> &nbsp;function AudioExif()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;// nothing to do<br/> &nbsp;&#125;<br/><br/> &nbsp;// check the filesize<br/> &nbsp;function CheckSize($file)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$handler = &$this->_get_handler($file);<br/> &nbsp; &nbsp;if (!$handler) return false;<br/> &nbsp; &nbsp;return $handler->check_size($file);<br/> &nbsp;&#125;<br/><br/> &nbsp;// get the infomations<br/> &nbsp;function GetInfo($file)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$handler = &$this->_get_handler($file);<br/> &nbsp; &nbsp;if (!$handler) return false;<br/> &nbsp; &nbsp;return $handler->get_info($file);<br/> &nbsp;&#125;<br/><br/> &nbsp;// write the infomations<br/> &nbsp;function SetInfo($file, $pa)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;if (!is_writable($file))<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;trigger_error('AudioExif: file `' . $file . '` can not been overwritten', E_USER_WARNING);<br/> &nbsp; &nbsp; &nbsp;return false;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$handler = &$this->_get_handler($file);<br/> &nbsp; &nbsp;if (!$handler) return false;<br/> &nbsp; &nbsp;return $handler->set_info($file, $pa);<br/> &nbsp;&#125;<br/><br/> &nbsp;// private methods<br/> &nbsp;function &_get_handler($file)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$ext = strtolower(strrchr($file, '.'));<br/> &nbsp; &nbsp;$ret = false;<br/> &nbsp; &nbsp;if ($ext == '.mp3')<br/> &nbsp; &nbsp;&#123; &nbsp;// MP3<br/> &nbsp; &nbsp; &nbsp;$ret = &$this->_mp3;<br/> &nbsp; &nbsp; &nbsp;if (!$ret) $ret = new _Mp3Exif();<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;else if ($ext == '.wma')<br/> &nbsp; &nbsp;&#123; &nbsp;// wma<br/> &nbsp; &nbsp; &nbsp;$ret = &$this->_wma;<br/> &nbsp; &nbsp; &nbsp;if (!$ret) $ret = new _WmaExif();<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;else<br/> &nbsp; &nbsp;&#123; &nbsp;// unknown<br/> &nbsp; &nbsp; &nbsp;trigger_error('AudioExif not supported `' . $ext . '` file.', E_USER_WARNING);<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;return $ret;<br/> &nbsp;&#125;<br/>&#125;<br/><br/>// DBCS => gb2312<br/>function dbcs_gbk($str)<br/>&#123;<br/> &nbsp;// strip the last "&#92;0&#92;0"<br/> &nbsp;$str = substr($str, 0, -2);<br/> &nbsp;return mb_convert_encoding($str, 'GBK', 'UCS-2LE');<br/>&#125;<br/><br/>// gb2312 => DBCS<br/>function gbk_dbcs($str)<br/>&#123;<br/> &nbsp;$str &nbsp;= mb_convert_encoding($str, 'UCS-2LE', 'GBK');<br/> &nbsp;$str .= "&#92;0&#92;0";<br/> &nbsp;return $str;<br/>&#125;<br/><br/>// file exif<br/>class _AudioExif<br/>&#123;<br/> &nbsp;var $fd;<br/> &nbsp;var $head;<br/> &nbsp;var $head_off;<br/> &nbsp;var $head_buf;<br/> &nbsp;<br/> &nbsp;// init the file handler<br/> &nbsp;function _file_init($fpath, $write = false)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$mode = ($write ? 'rb+' : 'rb');<br/> &nbsp; &nbsp;$this->fd = @fopen($fpath, $mode);<br/> &nbsp; &nbsp;if (!$this->fd)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;trigger_error('AudioExif: `' . $fpath . '` can not be opened with mode `' . $mode . '`', E_USER_WARNING);<br/> &nbsp; &nbsp; &nbsp;return false;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$this->head = false;<br/> &nbsp; &nbsp;$this->head_off = 0;<br/> &nbsp; &nbsp;$this->head_buf = '';<br/> &nbsp; &nbsp;return true;<br/> &nbsp;&#125;<br/><br/> &nbsp;// read buffer from the head_buf & move the off pointer<br/> &nbsp;function _read_head_buf($len)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;if ($len <= 0) return NULL;<br/> &nbsp; &nbsp;$buf = substr($this->head_buf, $this->head_off, $len);<br/> &nbsp; &nbsp;$this->head_off += strlen($buf);<br/> &nbsp; &nbsp;return $buf;<br/> &nbsp;&#125;<br/><br/> &nbsp;// read one short value<br/> &nbsp;function _read_head_short()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$ord1 = ord(substr($this->head_buf, $this->head_off, 1));<br/> &nbsp; &nbsp;$ord2 = ord(substr($this->head_buf, $this->head_off+1, 1));<br/> &nbsp; &nbsp;$this->head_off += 2;<br/> &nbsp; &nbsp;return ($ord1 + ($ord2<<8));<br/> &nbsp;&#125;<br/><br/> &nbsp;// save the file head<br/> &nbsp;function _file_save($head, $olen, $nlen = 0)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;if ($nlen == 0) $nlen = strlen($head);<br/> &nbsp; &nbsp;if ($nlen == $olen)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;// shorter<br/> &nbsp; &nbsp; &nbsp;flock($this->fd, LOCK_EX);<br/> &nbsp; &nbsp; &nbsp;fseek($this->fd, 0, SEEK_SET);<br/> &nbsp; &nbsp; &nbsp;fwrite($this->fd, $head, $nlen);<br/> &nbsp; &nbsp; &nbsp;flock($this->fd, LOCK_UN);<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;else<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;// longer, buffer required<br/> &nbsp; &nbsp; &nbsp;$stat = fstat($this->fd);<br/> &nbsp; &nbsp; &nbsp;$fsize = $stat['size'];<br/><br/> &nbsp; &nbsp; &nbsp;// buf required (4096?) 应该不会 nlen - olen > 4096 吧<br/> &nbsp; &nbsp; &nbsp;$woff = 0;<br/> &nbsp; &nbsp; &nbsp;$roff = $olen;<br/><br/> &nbsp; &nbsp; &nbsp;// read first buffer<br/> &nbsp; &nbsp; &nbsp;flock($this->fd, LOCK_EX);<br/> &nbsp; &nbsp; &nbsp;fseek($this->fd, $roff, SEEK_SET);<br/> &nbsp; &nbsp; &nbsp;$buf = fread($this->fd, 4096);<br/><br/> &nbsp; &nbsp; &nbsp;// seek to start<br/> &nbsp; &nbsp; &nbsp;fseek($this->fd, $woff, SEEK_SET);<br/> &nbsp; &nbsp; &nbsp;fwrite($this->fd, $head, $nlen);<br/> &nbsp; &nbsp; &nbsp;$woff += $nlen;<br/><br/> &nbsp; &nbsp; &nbsp;// seek to woff & write the data<br/> &nbsp; &nbsp; &nbsp;do<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$buf2 = $buf;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$roff += 4096;<br/> &nbsp; &nbsp; &nbsp; &nbsp;if ($roff < $fsize) <br/> &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fseek($this->fd, $roff, SEEK_SET);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$buf = fread($this->fd, 4096); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp;// save last buffer<br/> &nbsp; &nbsp; &nbsp; &nbsp;$len2 = strlen($buf2);<br/> &nbsp; &nbsp; &nbsp; &nbsp;fseek($this->fd, $woff, SEEK_SET);<br/> &nbsp; &nbsp; &nbsp; &nbsp;fwrite($this->fd, $buf2, $len2);<br/> &nbsp; &nbsp; &nbsp; &nbsp;$woff += $len2;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;while ($roff < $fsize);<br/> &nbsp; &nbsp; &nbsp;ftruncate($this->fd, $woff);<br/> &nbsp; &nbsp; &nbsp;flock($this->fd, LOCK_UN);<br/> &nbsp; &nbsp;&#125;<br/> &nbsp;&#125;<br/><br/> &nbsp;// close the file<br/> &nbsp;function _file_deinit()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;if ($this->fd)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;fclose($this->fd);<br/> &nbsp; &nbsp; &nbsp;$this->fd = false;<br/> &nbsp; &nbsp;&#125; &nbsp; &nbsp;<br/> &nbsp;&#125;<br/>&#125;<br/><br/>// wma class<br/>class _WmaExif extends _AudioExif<br/>&#123;<br/> &nbsp;var $items1 = array('Title', 'Artist', 'Copyright', 'Description', 'Reserved');<br/> &nbsp;var $items2 = array('Year', 'Genre', 'AlbumTitle');<br/><br/> &nbsp;// check file size (length) maybe invalid file<br/> &nbsp;function check_size($file)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$ret = false;<br/> &nbsp; &nbsp;if (!$this->_file_init($file)) return true;<br/> &nbsp; &nbsp;if ($this->_init_header())<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$buf = fread($this->fd, 24);<br/> &nbsp; &nbsp; &nbsp;$tmp = unpack('H32id/Vlen/H8unused', $buf); &nbsp;<br/> &nbsp; &nbsp; &nbsp;if ($tmp['id'] == '3626b2758e66cf11a6d900aa0062ce6c')<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$stat = fstat($this->fd);<br/> &nbsp; &nbsp; &nbsp; &nbsp;$ret = ($stat['size'] == ($this->head['len'] + $tmp['len']));<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$this->_file_deinit();<br/> &nbsp; &nbsp;return $ret;<br/> &nbsp;&#125;<br/><br/> &nbsp;// set info (save the infos)<br/> &nbsp;function set_info($file, $pa)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;// check the pa<br/> &nbsp; &nbsp;settype($pa, 'array');<br/> &nbsp; &nbsp;if (!$this->_file_init($file, true)) return false;<br/> &nbsp; &nbsp;if (!$this->_init_header())<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$this->_file_deinit();<br/> &nbsp; &nbsp; &nbsp;return false;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;<br/> &nbsp; &nbsp;// parse the old header & generate the new header<br/> &nbsp; &nbsp;$head_body = '';<br/> &nbsp; &nbsp;$st_found = $ex_found = false;<br/> &nbsp; &nbsp;$head_num = $this->head['num'];<br/> &nbsp; &nbsp;while (($tmp = $this->_get_head_frame()) && ($head_num > 0))<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$head_num--;<br/> &nbsp; &nbsp; &nbsp;if ($tmp['id'] == '3326b2758e66cf11a6d900aa0062ce6c')<br/> &nbsp; &nbsp; &nbsp;&#123; &nbsp;// Standard Info<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 1-4<br/> &nbsp; &nbsp; &nbsp; &nbsp;$st_found = true;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$st_body1 = $st_body2 = ''; &nbsp; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$lenx = unpack('v5', $this->_read_head_buf(10));<br/> &nbsp; &nbsp; &nbsp; &nbsp;$tmp['len'] -= 34; &nbsp;// 10 + 24<br/> &nbsp; &nbsp; &nbsp; &nbsp;for ($i = 0; $i < count($this->items1); $i++)<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$l = $lenx[$i+1];<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$k = $this->items1[$i];<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$tmp['len'] -= $l;<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$data = $this->_read_head_buf($l);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (isset($pa[$k])) $data = gbk_dbcs($pa[$k]);<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$st_body2 .= $data;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$st_body1 .= pack('v', strlen($data));<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp;// left length<br/> &nbsp; &nbsp; &nbsp; &nbsp;if ($tmp['len'] > 0) $st_body2 .= $this->_read_head_buf($tmp['len']);<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp;// save to head_body<br/> &nbsp; &nbsp; &nbsp; &nbsp;$head_body .= pack('H32VH8', $tmp['id'], strlen($st_body1 . $st_body2)+24, $tmp['unused']);<br/> &nbsp; &nbsp; &nbsp; &nbsp;$head_body .= $st_body1 . $st_body2; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;else if ($tmp['id'] == '40a4d0d207e3d21197f000a0c95ea850')<br/> &nbsp; &nbsp; &nbsp;&#123; &nbsp;// extended info<br/> &nbsp; &nbsp; &nbsp; &nbsp;$ex_found = true;<br/> &nbsp; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$inum = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp;$inum2 = $inum;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$tmp['len'] -= 26; &nbsp;// 24 + 2<br/> &nbsp; &nbsp; &nbsp; &nbsp;$et_body = '';<br/> &nbsp; &nbsp; &nbsp; &nbsp;while ($tmp['len'] > 0 && $inum > 0)<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// attribute name<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$nlen = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$nbuf = $this->_read_head_buf($nlen);<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// the flag & value &nbsp;length<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$flag = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vlen = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vbuf = $this->_read_head_buf($vlen);<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// set the length<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$tmp['len'] -= (6 + $nlen + $vlen);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$inum--;<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// save the data?<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$name = dbcs_gbk($nbuf);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$k = substr($name, 3);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (in_array($k, $this->items2) && isset($pa[$k]))<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vbuf = gbk_dbcs($pa[$k]);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vlen = strlen($vbuf);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;unset($pa[$k]);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$et_body .= pack('v', $nlen) . $nbuf . pack('vv', $flag, $vlen) . $vbuf;<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp;// new tag insert??<br/> &nbsp; &nbsp; &nbsp; &nbsp;foreach ($this->items2 as $k)<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (isset($pa[$k]))<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$inum2++;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$nbuf = gbk_dbcs('WM/' . $k);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$nlen = strlen($nbuf);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vbuf = gbk_dbcs($pa[$k]);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vlen = strlen($vbuf);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$et_body .= pack('v', $nlen) . $nbuf . pack('vv', 0, $vlen) . $vbuf;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp;// left buf?<br/> &nbsp; &nbsp; &nbsp; &nbsp;if ($tmp['len'] > 0) $et_body .= $this->_read_head_buf($tmp['len']);<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp;// save to head_body<br/> &nbsp; &nbsp; &nbsp; &nbsp;$head_body .= pack('H32VH8v', $tmp['id'], strlen($et_body)+26, $tmp['unused'], $inum2);<br/> &nbsp; &nbsp; &nbsp; &nbsp;$head_body .= $et_body; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;else<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;// just keep other head frame<br/> &nbsp; &nbsp; &nbsp; &nbsp;$head_body .= pack('H32VH8', $tmp['id'], $tmp['len'], $tmp['unused']);<br/> &nbsp; &nbsp; &nbsp; &nbsp;if ($tmp['len'] > 24) $head_body .= $this->_read_head_buf($tmp['len']-24);<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// st not found?<br/> &nbsp; &nbsp;if (!$st_found)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$st_body1 = $st_body2 = '';<br/> &nbsp; &nbsp; &nbsp;foreach ($this->items1 as $k)<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$data = (isset($pa[$k]) ? gbk_dbcs($pa[$k]) : "");<br/> &nbsp; &nbsp; &nbsp; &nbsp;$st_body1 .= pack('v', strlen($data));<br/> &nbsp; &nbsp; &nbsp; &nbsp;$st_body2 .= $data;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;// save to head_body<br/> &nbsp; &nbsp; &nbsp;$head_body .= pack('H32Va4', '3326b2758e66cf11a6d900aa0062ce6c', strlen($st_body1 . $st_body2)+24, '');<br/> &nbsp; &nbsp; &nbsp;$head_body .= $st_body1 . $st_body2;<br/> &nbsp; &nbsp; &nbsp;$this->head['num']++;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;// ex not found?<br/> &nbsp; &nbsp;if (!$ex_found)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$inum = 0;<br/> &nbsp; &nbsp; &nbsp;$et_body = '';<br/> &nbsp; &nbsp; &nbsp;foreach ($this->items2 as $k)<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$nbuf = gbk_dbcs('WM/' . $k);<br/> &nbsp; &nbsp; &nbsp; &nbsp;$vbuf = (isset($pa[$k]) ? gbk_dbcs($pa[$k]) : "");<br/> &nbsp; &nbsp; &nbsp; &nbsp;$et_body .= pack('v', strlen($nbuf)) . $nbuf . pack('vv', 0, strlen($vbuf)) . $vbuf;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$inum++;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;$head_body .= pack('H32Va4v', '40a4d0d207e3d21197f000a0c95ea850', strlen($et_body)+26, '', $inum);<br/> &nbsp; &nbsp; &nbsp;$head_body .= $et_body;<br/> &nbsp; &nbsp; &nbsp;$this->head['num']++;<br/> &nbsp; &nbsp;&#125; &nbsp; &nbsp;<br/><br/> &nbsp; &nbsp;// after save<br/> &nbsp; &nbsp;$new_len = strlen($head_body) + 30;<br/> &nbsp; &nbsp;$old_len = $this->head['len'];<br/> &nbsp; &nbsp;if ($new_len < $old_len)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$head_body .= str_repeat("&#92;0", $old_len - $new_len);<br/> &nbsp; &nbsp; &nbsp;$new_len = $old_len;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$tmp = $this->head;<br/> &nbsp; &nbsp;$head_buf = pack('H32VVVH4', $tmp['id'], $new_len, $tmp['len2'], $tmp['num'], $tmp['unused']);<br/> &nbsp; &nbsp;$head_buf .= $head_body;<br/> &nbsp; &nbsp;$this->_file_save($head_buf, $old_len, $new_len);<br/><br/> &nbsp; &nbsp;// close the file & return<br/> &nbsp; &nbsp;$this->_file_deinit();<br/> &nbsp; &nbsp;return true;<br/> &nbsp;&#125;<br/><br/> &nbsp;// get info<br/> &nbsp;function get_info($file)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$ret = array();<br/> &nbsp; &nbsp;if (!$this->_file_init($file)) return false;<br/> &nbsp; &nbsp;if (!$this->_init_header())<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$this->_file_deinit();<br/> &nbsp; &nbsp; &nbsp;return false;<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// get the data from head_buf<br/> &nbsp; &nbsp;$head_num = $this->head['num']; &nbsp;// num of head_frame<br/> &nbsp; &nbsp;while (($tmp = $this->_get_head_frame()) && $head_num > 0)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$head_num--;<br/> &nbsp; &nbsp; &nbsp;if ($tmp['id'] == '3326b2758e66cf11a6d900aa0062ce6c')<br/> &nbsp; &nbsp; &nbsp;&#123; &nbsp;// Standard Info<br/> &nbsp; &nbsp; &nbsp; &nbsp;$lenx = unpack('v*', $this->_read_head_buf(10));<br/> &nbsp; &nbsp; &nbsp; &nbsp;for ($i = 1; $i <= count($this->items1); $i++)<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$k = $this->items1[$i-1];<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ret[$k] = dbcs_gbk($this->_read_head_buf($lenx[$i]));<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;else if ($tmp['id'] == '40a4d0d207e3d21197f000a0c95ea850')<br/> &nbsp; &nbsp; &nbsp;&#123; &nbsp;// Extended Info<br/> &nbsp; &nbsp; &nbsp; &nbsp;$inum = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp;$tmp['len'] -= 26;<br/> &nbsp; &nbsp; &nbsp; &nbsp;while ($inum > 0 && $tmp['len'] > 0)<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// attribute name<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$nlen = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$nbuf = $this->_read_head_buf($nlen);<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// the flag & value &nbsp;length<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$flag = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vlen = $this->_read_head_short();<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$vbuf = $this->_read_head_buf($vlen);<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// update the XX<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$tmp['len'] -= (6 + $nlen + $vlen);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$inum--;<br/><br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$name = dbcs_gbk($nbuf);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$k = substr($name, 3);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (in_array($k, $this->items2))<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&#123; &nbsp;// all is string value (refer to falg for other tags)<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ret[$k] = dbcs_gbk($vbuf);<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;else<br/> &nbsp; &nbsp; &nbsp;&#123; &nbsp;// skip only<br/> &nbsp; &nbsp; &nbsp; &nbsp;if ($tmp['len'] > 24) $this->head_off += ($tmp['len'] - 24);<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$this->_file_deinit();<br/> &nbsp; &nbsp;return $ret;<br/> &nbsp;&#125;<br/><br/> &nbsp;// get the header?<br/> &nbsp;function _init_header()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;fseek($this->fd, 0, SEEK_SET);<br/> &nbsp; &nbsp;$buf = fread($this->fd, 30);<br/> &nbsp; &nbsp;if (strlen($buf) != 30) return false;<br/> &nbsp; &nbsp;$tmp = unpack('H32id/Vlen/Vlen2/Vnum/H4unused', $buf);<br/> &nbsp; &nbsp;if ($tmp['id'] != '3026b2758e66cf11a6d900aa0062ce6c')<br/> &nbsp; &nbsp; &nbsp;return false;<br/><br/> &nbsp; &nbsp;$this->head_buf = fread($this->fd, $tmp['len'] - 30);<br/> &nbsp; &nbsp;$this->head = $tmp;<br/> &nbsp; &nbsp;return true;<br/> &nbsp;&#125;<br/><br/> &nbsp;// _get_head_frame()<br/> &nbsp;function _get_head_frame()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$buf = $this->_read_head_buf(24); &nbsp; &nbsp;<br/> &nbsp; &nbsp;if (strlen($buf) != 24) return false;<br/> &nbsp; &nbsp;$tmp = unpack('H32id/Vlen/H8unused', $buf);<br/> &nbsp; &nbsp;return $tmp;<br/> &nbsp;&#125;<br/>&#125;<br/><br/>// mp3 class (if not IDv2 then select IDv1)<br/>class _Mp3Exif extends _AudioExif<br/>&#123;<br/> &nbsp;var $head1;<br/> &nbsp;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');<br/><br/> &nbsp;// MP3 always return true<br/> &nbsp;function check_size($file)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;return true;<br/> &nbsp;&#125;<br/><br/> &nbsp;// get info<br/> &nbsp;function get_info($file)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;if (!$this->_file_init($file)) return false; &nbsp; &nbsp;<br/> &nbsp; &nbsp;$ret = false;<br/> &nbsp; &nbsp;if ($this->_init_header())<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$ret = ($this->head ? $this->_get_v2_info() : $this->_get_v1_info());<br/> &nbsp; &nbsp; &nbsp;$ret['meta'] = $this->_get_meta_info();<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$this->_file_deinit();<br/> &nbsp; &nbsp;return $ret;<br/> &nbsp;&#125;<br/><br/> &nbsp;// set info<br/> &nbsp;function set_info($file, $pa)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;if (!$this->_file_init($file, true)) return false;<br/> &nbsp; &nbsp;if ($this->_init_header())<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;// always save v1 info<br/> &nbsp; &nbsp; &nbsp;$this->_set_v1_info($pa); &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;// set v2 first if need<br/> &nbsp; &nbsp; &nbsp;$this->_set_v2_info($pa);<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$this->_file_deinit();<br/> &nbsp; &nbsp;return true;<br/> &nbsp;&#125;<br/><br/> &nbsp;// get the header information[v1+v2], call after file_init<br/> &nbsp;function _init_header()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$this->head1 = false;<br/> &nbsp; &nbsp;$this->head = false;<br/><br/> &nbsp; &nbsp;// try to get ID3v1 first<br/> &nbsp; &nbsp;fseek($this->fd, -128, SEEK_END);<br/> &nbsp; &nbsp;$buf = fread($this->fd, 128);<br/> &nbsp; &nbsp;if (strlen($buf) == 128 && substr($buf, 0, 3) == 'TAG')<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$tmp = unpack('a3id/a30Title/a30Artist/a30AlbumTitle/a4Year/a28Description/CReserved/CTrack/CGenre', $buf);<br/> &nbsp; &nbsp; &nbsp;$this->head1 = $tmp; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// try to get ID3v2<br/> &nbsp; &nbsp;fseek($this->fd, 0, SEEK_SET);<br/> &nbsp; &nbsp;$buf = fread($this->fd, 10);<br/> &nbsp; &nbsp;if (strlen($buf) == 10 && substr($buf, 0, 3) == 'ID3') <br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$tmp = unpack('a3id/Cver/Crev/Cflag/C4size', $buf);<br/> &nbsp; &nbsp; &nbsp;$tmp['size'] = ($tmp['size1']<<21)&#124;($tmp['size2']<<14)&#124;($tmp['size3']<<7)&#124;$tmp['size4'];<br/> &nbsp; &nbsp; &nbsp;unset($tmp['size1'], $tmp['size2'], $tmp['size3'], $tmp['size4']);<br/><br/> &nbsp; &nbsp; &nbsp;$this->head = $tmp;<br/> &nbsp; &nbsp; &nbsp;$this->head_buf = fread($this->fd, $tmp['size']);<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;return ($this->head1 &#124;&#124; $this->head);<br/> &nbsp;&#125;<br/><br/> &nbsp;// get v1 info<br/> &nbsp;function _get_v1_info()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$ret = array();<br/> &nbsp; &nbsp;$tmpa = array('Title', 'Artist', 'Copyright', 'Description', 'Year', 'AlbumTitle');<br/> &nbsp; &nbsp;foreach ($tmpa as $tmp)<br/> &nbsp; &nbsp;&#123; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;$ret[$tmp] = $this->head1[$tmp];<br/> &nbsp; &nbsp; &nbsp;if ($pos = strpos($ret[$tmp], "&#92;0")) <br/> &nbsp; &nbsp; &nbsp; &nbsp;$ret[$tmp] = substr($ret[$tmp], 0, $pos);<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// count the Genre, [Track]<br/> &nbsp; &nbsp;if ($this->head1['Reserved'] == 0) $ret['Track'] = $this->head1['Track'];<br/> &nbsp; &nbsp;else $ret['Description'] .= chr($ret['Reserved']) . chr($ret['Track']);<br/><br/> &nbsp; &nbsp;// Genre_idx<br/> &nbsp; &nbsp;$g = $this->head1['Genre'];<br/> &nbsp; &nbsp;if (!isset($this->genres[$g])) $ret['Genre'] = 'Unknown';<br/> &nbsp; &nbsp;else $ret['Genre'] = $this->genres[$g];<br/><br/> &nbsp; &nbsp;// return the value<br/> &nbsp; &nbsp;$ret['ID3v1'] = 'yes';<br/> &nbsp; &nbsp;return $ret;<br/> &nbsp;&#125;<br/><br/> &nbsp;// get v2 info<br/> &nbsp;function _get_v2_info()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;$ret = array();<br/> &nbsp; &nbsp;$items = array( &nbsp;'TCOP'=>'Copyright', 'TPE1'=>'Artist', 'TIT2'=>'Title', 'TRCK'=> 'Track',<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'TCON'=>'Genre', 'COMM'=>'Description', 'TYER'=>'Year', 'TALB'=>'AlbumTitle');<br/> &nbsp; &nbsp;while (true)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$buf = $this->_read_head_buf(10);<br/> &nbsp; &nbsp; &nbsp;if (strlen($buf) != 10) break; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;$tmp = unpack('a4fid/Nsize/nflag', $buf);<br/> &nbsp; &nbsp; &nbsp;if ($tmp['size'] == 0) break;<br/> &nbsp; &nbsp; &nbsp;$tmp['dat'] = $this->_read_head_buf($tmp['size']);<br/><br/> &nbsp; &nbsp; &nbsp;// 0x6000 (11000000 00000000) &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;if ($tmp['flag'] & 0x6000) continue;<br/><br/> &nbsp; &nbsp; &nbsp;// mapping the data<br/> &nbsp; &nbsp; &nbsp;if ($k = $items[$tmp['fid']])<br/> &nbsp; &nbsp; &nbsp;&#123; &nbsp;// If first char is "&#92;0", just skip<br/> &nbsp; &nbsp; &nbsp; &nbsp;if (substr($tmp['dat'], 0, 1) == "&#92;0") $tmp['dat'] = substr($tmp['dat'], 1);<br/> &nbsp; &nbsp; &nbsp; &nbsp;$ret[$k] = $tmp['dat'];<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// reset the genre<br/> &nbsp; &nbsp;if ($g = $ret['Genre'])<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;if (substr($g,0,1) == '(' && substr($g,-1,1) == ')') $g = substr($g, 1, -1);<br/> &nbsp; &nbsp; &nbsp;if (is_numeric($g))<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$g = intval($g);<br/> &nbsp; &nbsp; &nbsp; &nbsp;$ret['Genre'] = (isset($this->genres[$g]) ? $this->genres[$g] : 'Unknown');<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;$ret['ID3v1'] = 'no';<br/> &nbsp; &nbsp;return $ret;<br/> &nbsp;&#125;<br/><br/> &nbsp;// get meta info of MP3<br/> &nbsp;function _get_meta_info()<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;// seek to the lead buf: 0xff<br/> &nbsp; &nbsp;$off = 0;<br/> &nbsp; &nbsp;if ($this->head) $off = $this->head['size'] + 10;<br/> &nbsp; &nbsp;fseek($this->fd, $off, SEEK_SET);<br/> &nbsp; &nbsp;while (!feof($this->fd))<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$skip = ord(fread($this->fd, 1));<br/> &nbsp; &nbsp; &nbsp;if ($skip == 0xff) break;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;if ($skip != 0xff) return false;<br/> &nbsp; &nbsp;$buf = fread($this->fd, 3);<br/> &nbsp; &nbsp;if (strlen($buf) != 3) return false;<br/> &nbsp; &nbsp;$tmp = unpack('C3', $buf);<br/> &nbsp; &nbsp;if (($tmp[1] & 0xf0) != 0xf0) return false;<br/><br/> &nbsp; &nbsp;// get the meta info<br/> &nbsp; &nbsp;$meta = array();<br/><br/> &nbsp; &nbsp;// get mpeg version<br/> &nbsp; &nbsp;$meta['mpeg'] &nbsp;= ($tmp[1] & 0x08 ? 1 : 2);<br/> &nbsp; &nbsp;$meta['layer'] &nbsp;= ($tmp[1] & 0x04) ? (($tmp[1] & 0x02) ? 1 : 2) : (($tmp[1] & 0x02) ? 3 : 0);<br/> &nbsp; &nbsp;$meta['epro'] &nbsp;= ($tmp[1] & 0x01) ? 'no' : 'yes';<br/><br/> &nbsp; &nbsp;// bit rates<br/> &nbsp; &nbsp;$bit_rates = array(<br/> &nbsp; &nbsp; &nbsp;1 => array(<br/> &nbsp; &nbsp; &nbsp; &nbsp;1 => array(0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),<br/> &nbsp; &nbsp; &nbsp; &nbsp;2 => array(0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0),<br/> &nbsp; &nbsp; &nbsp; &nbsp;3 => array(0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0)<br/> &nbsp; &nbsp; &nbsp;),<br/> &nbsp; &nbsp; &nbsp;2 => array(<br/> &nbsp; &nbsp; &nbsp; &nbsp;1 => array(0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0),<br/> &nbsp; &nbsp; &nbsp; &nbsp;2 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0),<br/> &nbsp; &nbsp; &nbsp; &nbsp;3 => array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0)<br/> &nbsp; &nbsp; &nbsp;)<br/> &nbsp; &nbsp;);<br/> &nbsp; &nbsp;$i = $meta['mpeg'];<br/> &nbsp; &nbsp;$j = $meta['layer'];<br/> &nbsp; &nbsp;$k = ($tmp[2]>>4);<br/> &nbsp; &nbsp;$meta['bitrate'] = $bit_rates[$i][$j][$k];<br/><br/> &nbsp; &nbsp;// sample rates <采样率><br/> &nbsp; &nbsp;$sam_rates = array(1=>array(44100,48000,32000,0), 2=>array(22050,24000,16000,0));<br/> &nbsp; &nbsp;$meta['samrate'] = $sam_rates[$i][$k];<br/> &nbsp; &nbsp;$meta["padding"] = ($tmp[2] & 0x02) ? 'on' : 'off';<br/> &nbsp; &nbsp;$meta["private"] = ($tmp[2] & 0x01) ? 'on' : 'off';<br/><br/> &nbsp; &nbsp;// mode & mode_ext<br/> &nbsp; &nbsp;$k = ($tmp[3]>>6);<br/> &nbsp; &nbsp;$channel_modes = array('stereo', 'joint stereo', 'dual channel', 'single channel');<br/> &nbsp; &nbsp;$meta['mode'] = $channel_modes[$k];<br/><br/> &nbsp; &nbsp;$k = (($tmp[3]>>4) & 0x03);<br/> &nbsp; &nbsp;$extend_modes = array('MPG_MD_LR_LR', 'MPG_MD_LR_I', 'MPG_MD_MS_LR', 'MPG_MD_MS_I');<br/> &nbsp; &nbsp;$meta['ext_mode'] = $extend_modes[$k];<br/><br/> &nbsp; &nbsp;$meta['copyright'] = ($tmp[3] & 0x08) ? 'yes' : 'no';<br/> &nbsp; &nbsp;$meta['original'] = ($tmp[3] & 0x04) ? 'yes' : 'no';<br/><br/> &nbsp; &nbsp;$emphasis = array('none', '50/15 microsecs', 'rreserved', 'CCITT J 17');<br/> &nbsp; &nbsp;$k = ($tmp[3] & 0x03);<br/> &nbsp; &nbsp;$meta['emphasis'] = $emphasis[$k];<br/><br/> &nbsp; &nbsp;return $meta;<br/> &nbsp;&#125;<br/><br/> &nbsp;// set v1 info<br/> &nbsp;function _set_v1_info($pa)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;// ID3v1 (simpled)<br/> &nbsp; &nbsp;$off = -128;<br/> &nbsp; &nbsp;if (!($tmp = $this->head1))<br/> &nbsp; &nbsp;&#123; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;$off = 0;<br/> &nbsp; &nbsp; &nbsp;$tmp['id'] = 'TAG';<br/> &nbsp; &nbsp; &nbsp;$tmp['Title'] = $tmp['Artist'] = $tmp['AlbumTitle'] = $tmp['Year'] = $tmp['Description'] = '';<br/> &nbsp; &nbsp; &nbsp;$tmp['Reserved'] = $tmp['Track'] = $tmp['Genre'] = 0;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;<br/> &nbsp; &nbsp;// basic items<br/> &nbsp; &nbsp;$items = array('Title', 'Artist', 'Copyright', 'Description', 'Year', 'AlbumTitle');<br/> &nbsp; &nbsp;foreach ($items as $k)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;if (isset($pa[$k])) $tmp[$k] = $pa[$k];<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// genre index<br/> &nbsp; &nbsp;if (isset($pa['Genre']))<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$g = 0;<br/> &nbsp; &nbsp; &nbsp;foreach ($this->genres as $gtmp)<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;if (!strcasecmp($gtmp, $pa['Genre'])) <br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$g++;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;$tmp['Genre'] = $g;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;if (isset($pa['Track'])) $tmp['Track'] = intval($pa['Track']);<br/><br/> &nbsp; &nbsp;// pack the data<br/> &nbsp; &nbsp;$buf = pack('a3a30a30a30a4a28CCC', &nbsp;$tmp['id'], $tmp['Title'], $tmp['Artist'], $tmp['AlbumTitle'],<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$tmp['Year'], $tmp['Description'], 0, $tmp['Track'], $tmp['Genre']);<br/><br/> &nbsp; &nbsp;flock($this->fd, LOCK_EX);<br/> &nbsp; &nbsp;fseek($this->fd, $off, SEEK_END);<br/> &nbsp; &nbsp;fwrite($this->fd, $buf, 128);<br/> &nbsp; &nbsp;flock($this->fd, LOCK_UN);<br/> &nbsp;&#125;<br/><br/> &nbsp;// set v2 info<br/> &nbsp;function _set_v2_info($pa)<br/> &nbsp;&#123;<br/> &nbsp; &nbsp;if (!$this->head)<br/> &nbsp; &nbsp;&#123; &nbsp;// insert ID3<br/> &nbsp; &nbsp; &nbsp;return; &nbsp;// 没有就算了<br/> &nbsp; &nbsp; &nbsp;/**<br/> &nbsp; &nbsp; &nbsp;$tmp = array('id'=>'ID3','ver'=>3,'rev'=>0,'flag'=>0);<br/> &nbsp; &nbsp; &nbsp;$tmp['size'] = -10; &nbsp;// +10 => 0<br/> &nbsp; &nbsp; &nbsp;$this->head = $tmp;<br/> &nbsp; &nbsp; &nbsp;$this->head_buf = '';<br/> &nbsp; &nbsp; &nbsp;$this->head_off = 0;<br/> &nbsp; &nbsp; &nbsp;**/<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$items = array( &nbsp;'TCOP'=>'Copyright', 'TPE1'=>'Artist', 'TIT2'=>'Title', 'TRAC'=>'Track',<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'TCON'=>'Genre', 'COMM'=>'Description', 'TYER'=>'Year', 'TALB'=>'AlbumTitle');<br/><br/> &nbsp; &nbsp;$head_body = '';<br/> &nbsp; &nbsp;while (true)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$buf = $this->_read_head_buf(10);<br/> &nbsp; &nbsp; &nbsp;if (strlen($buf) != 10) break;<br/> &nbsp; &nbsp; &nbsp;$tmp = unpack('a4fid/Nsize/nflag', $buf);<br/> &nbsp; &nbsp; &nbsp;if ($tmp['size'] == 0) break;<br/> &nbsp; &nbsp; &nbsp;$data = $this->_read_head_buf($tmp['size']);<br/><br/> &nbsp; &nbsp; &nbsp;if (($k = $items[$tmp['fid']]) && isset($pa[$k]))<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;// the data should prefix by "&#92;0" [replace]<br/> &nbsp; &nbsp; &nbsp; &nbsp;$data = "&#92;0" . $pa[$k];<br/> &nbsp; &nbsp; &nbsp; &nbsp;unset($pa[$k]);<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp;$head_body .= pack('a4Nn', $tmp['fid'], strlen($data), $tmp['flag']) . $data;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;// reverse the items & set the new tags<br/> &nbsp; &nbsp;$items = array_flip($items);<br/> &nbsp; &nbsp;foreach ($pa as $k => $v)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;if ($fid = $items[$k])<br/> &nbsp; &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;$head_body .= pack('a4Nn', $fid, strlen($v) + 1, 0) . "&#92;0" . $v;<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// new length<br/> &nbsp; &nbsp;$new_len = strlen($head_body) + 10;<br/> &nbsp; &nbsp;$old_len = $this->head['size'] + 10;<br/> &nbsp; &nbsp;if ($new_len < $old_len)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$head_body .= str_repeat("&#92;0", $old_len - $new_len);<br/> &nbsp; &nbsp; &nbsp;$new_len = $old_len; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp; &nbsp;// count the size1,2,3,4, no include the header<br/> &nbsp; &nbsp;// 较为变态的算法... :p (28bytes integer)<br/> &nbsp; &nbsp;$size = array();<br/> &nbsp; &nbsp;$nlen = $new_len - 10;<br/> &nbsp; &nbsp;for ($i = 4; $i > 0; $i--)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;$size[$i] = ($nlen & 0x7f);<br/> &nbsp; &nbsp; &nbsp;$nlen >>= 7;<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;$tmp = $this->head;<br/> &nbsp; &nbsp;//echo "old_len : $old_len new_len: $new_len&#92;n";<br/> &nbsp; &nbsp;$head_buf = pack('a3CCCCCCC', $tmp['id'], $tmp['ver'], $tmp['rev'], $tmp['flag'],<br/> &nbsp; &nbsp; &nbsp;$size[1], $size[2], $size[3], $size[4]);<br/> &nbsp; &nbsp;$head_buf .= $head_body;<br/> &nbsp; &nbsp;$this->_file_save($head_buf, $old_len, $new_len);<br/> &nbsp;&#125;<br/>&#125;<br/>?><br/><br/>
]]>
</description>
</item><item>
<link>http://www.jackxiang.com/post//#blogcomment</link>
<title><![CDATA[[评论] [源码学习] 用PHP读写音频文件的信息(支持WMA和MP3)]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.jackxiang.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>