<?php
/**
 * 語音驗證碼
 */
!defined('P_W') && exit('Forbidden');

class PW_Audio {
	/**
	 * @var string	$_audioPath 	語音文件存放目錄'/'結尾
	 * @var string  $_code			 驗證碼
	 * @var string	$_audioFormat 	音頻格式
	 * @var string	$_interference 	是否干擾, 1-是, 0-否
	 */
	
	var $_audioPath;
	var $_code;
	var $_audioFormat = 'mp3';
	var $_interference = 0;
	
	/**
	 * 設置語音文件路徑
	 * @param $audioPath	路徑
	 * @return bool			true-成功, false-失敗
	 */
	function setAudioPath($audioPath) {
		if (empty($audioPath)) return false;
		$this->_audioPath = S::escapePath($audioPath);
		return true;
	}
	
	/**
	 * 設置驗證碼
	 * @param $code			驗證碼
	 * @return bool			true-成功, false-失敗
	 */
	function setCode($code) {
		if (empty($code)) return false;
		$this->_code = $code;
		return true;
	}
	
	/**
	 * 設置音頻格式
	 * @param $audioFormat	音頻格式
	 * @return bool			true-成功, false-失敗
	 */
	function setAudioFormat($audioFormat) {
		if (!S::inArray($audioFormat, array('wav', 'mp3'))) return false;
		$this->_audioFormat = $audioFormat;
		return true;
	}
	
	/**
	 * 設置是否干擾
	 * @param $state		1-是, 0-否
	 * @return bool			true-成功
	 */
	function setInterference($state) {
		if ($state !==1 && $state !== 0) return false;
		$this->_interference = $state;
		return true;
	}
	
	/**
	 * 輸出語音
	 */
	function outputAudio() {
		if ($this->_audioFormat == 'wav') {
			$contentType = 'audio/x-wav';
			$type = 'wav';
		} elseif ($this->_audioFormat == 'mp3') {
			$contentType = 'audio/mpeg';
			$type = 'mp3';
		}
		if (empty($contentType)) return false;
		header("Content-type: $contentType");
		$audioContent = ($type == 'wav') ? $this->_getWavAudio() : $this->_getMp3Audio();
		$this->_interference == 1 && $audioContent = $this->_addInterference($audioContent);
		header('Content-Length: ' . strlen($audioContent));
		echo $audioContent;
		exit;
	}
	
	/**
	 * 生成wav格式的語音驗證碼
	 * @return $outputData	語音數據
	 */
	function _getWavAudio() {
		if (empty($this->_code)) return false;
		$audioData = array();
		$totalDataLength = '';
		for ($i = 0; $i < strlen($this->_code); $i++) {
			$wavFile = $this->_audioPath . strtoupper($this->_code[$i]) . '.wav';
			$wavData = readover($wavFile);
			$headerInfo = substr($wavData, 0, 36);
			$data = unpack('Nriffid/Vfilesize/Nfiletype/Nfmtid/Vfmtsize/vformattag/vchannels/Vsamplespersec/Vbytespersec/vblockalign/vbitspersample', $headerInfo);
			$data['filesize'] = $data['filesize'] + 8;
			$trunkLength = $data['fmtsize'] == 18 ? 46 : 44;
			$data['datainfo'] = substr($wavData, $trunkLength);
			if (($position = strpos($data['datainfo'], 'LIST')) !== false) {
				$data['filesize'] = $data['filesize'] - (strlen($data['datainfo']) - $position);
				$data['datainfo'] = substr($data['datainfo'], 0, $position);
			}
			$totalDataLength += strlen($data['datainfo']);
			$audioData[] = $data;
		}
		$outputData = '';
		foreach ($audioData as $key => $value) {
			if ($key == 0) {
				$wavHeader = pack('C4VC4', ord('R'), ord('I'), ord('F'), ord('F'), $totalDataLength + 36, ord('W'), ord('A'), ord('V'), ord('E'));
				$wavHeader .= pack('C4VvvVVvv',
					ord('f'),
					ord('m'),
					ord('t'),
					ord(' '),
					16,
					$value['formattag'],
					$value['channels'],
					$value['samplespersec'],
					$value['bytespersec'],
					$value['blockalign'],
					$value['bitspersample']
				);
				$wavHeader .= pack('C4V', ord('d'), ord('a'), ord('t'), ord('a'), $totalDataLength);
				$outputData .= $wavHeader;
			}
			$outputData .= $value['datainfo'];
		}
		return $outputData;
	}
	
	/**
	 * 生成mp3格式的語音驗證碼
	 * @return $outputData	語音數據
	 */
	function _getMp3Audio() {
		if (empty($this->_code)) return false;
		$outputData = '';
		for ($i = 0; $i < strlen($this->_code); $i++) {
			$wavFile = $this->_audioPath . strtoupper($this->_code[$i]) . '.mp3';
			$wavData = readover($wavFile);
			$outputData .= $wavData;
		}
		return $outputData;
	}
	
	/**
	 * 加干擾
	 * @param 	$audioData	音頻數據
	 * @return 	$audioData	處理後的數據
	 */
	function _addInterference($audioData) {
		if ($this->_audioFormat == 'wav') {
			$startpos = strpos($audioData, 'data') + 8;	//wav格式的音頻數據
			$startpos += rand(1, 32);
		} elseif ($this->_audioFormat == 'mp3') {
			$startpos = 4;	//沒有ID3V2標簽
			if (stripos($audioData, 'ID3') !== false) {
				$startpos = 24; //ID3V2頭跟幀
				($pos = stripos($audioData, '3DI')) !== false && $startpos = $pos + 14; //更準確的獲取音頻數據
			}
		}
		$dataLength = strlen($audioData) - $startpos - 128;	//末128個字節是MP3格式的ID3V1標簽
		for ($i = $startpos; $i < $dataLength; $i += 256) {
			$ord = ord($audioData[$i]);
			if ($ord < 17 || $ord > 111) continue;
			$audioData[$i] = chr($ord + rand(-16, 16));
		}
		return $audioData;
	}
}
?>