134        2.513, 2.667, 2.841, 3.038, 3.262,
 
  135        3.520, 3.819, 4.171, 4.589, 5.093,
 
  136        5.711, 6.487, 7.483, 8.806, 10.634,
 
  137        13.302, 17.510, 24.970, 41.155, 96.088
 
  221    public function __construct($numChannelsOrFileName = 
null, $sampleRateOrReadData = 
null, $bitsPerSample = 
null)
 
  223        $this->_actualSize         = 44;
 
  224        $this->_chunkSize          = 36;
 
  225        $this->_fmtChunkSize       = 16;
 
  226        $this->_fmtExtendedSize    = 0;
 
  227        $this->_factChunkSize      = 0;
 
  228        $this->_dataSize           = 0;
 
  229        $this->_dataSize_fp        = 0;
 
  230        $this->_dataSize_valid     = 
true;
 
  231        $this->_dataOffset         = 44;
 
  233        $this->_audioSubFormat     = 
null;
 
  234        $this->_numChannels        = 1;
 
  236        $this->_sampleRate         = 8000;
 
  237        $this->_bitsPerSample      = 8;
 
  238        $this->_validBitsPerSample = 8;
 
  239        $this->_blockAlign         = 1;
 
  240        $this->_numBlocks          = 0;
 
  241        $this->_byteRate           = 8000;
 
  242        $this->_samples            = 
'';
 
  246        if (is_string($numChannelsOrFileName)) {
 
  247            $this->
openWav($numChannelsOrFileName, is_bool($sampleRateOrReadData) ? $sampleRateOrReadData : 
true);
 
  250            $this->
setNumChannels(is_null($numChannelsOrFileName) ? 1 : $numChannelsOrFileName)
 
  251                 ->setSampleRate(is_null($sampleRateOrReadData) ? 8000 : $sampleRateOrReadData)
 
  252                 ->setBitsPerSample(is_null($bitsPerSample) ? 8 : $bitsPerSample);
 
  257        if (is_resource($this->_fp)) $this->
closeWav();
 
  288        if ($bitDepth === 
null) {
 
  289            $bitDepth = strlen($sampleBinary) * 8;
 
  295                return ord($sampleBinary);
 
  299                $data = unpack(
'v', $sampleBinary);
 
  301                if ($sample >= 0x8000) {
 
  308                $data = unpack(
'C3', $sampleBinary);
 
  310                if ($sample >= 0x800000) {
 
  311                    $sample -= 0x1000000;
 
  317                $data = unpack(
'f', $sampleBinary);
 
  344                return pack(
'v', $sample);
 
  349                    $sample += 0x1000000;
 
  351                return pack(
'C3', $sample & 0xff, ($sample >>  8) & 0xff, ($sample >> 16) & 0xff);
 
  355                return pack(
'f', $sample);
 
  371        $sampleBytes = $bitDepth / 8;
 
  372        if ($numChannels === 
null) {
 
  373            $numChannels = strlen($sampleBlock) / $sampleBytes;
 
  377        for (
$i = 0; 
$i < $numChannels; 
$i++) {
 
  378            $sampleBinary = substr($sampleBlock, 
$i * $sampleBytes, $sampleBytes);
 
  394        foreach($samples as $sample) {
 
  421        if ($threshold >= 1) {
 
  422            return $sampleFloat * $threshold;
 
  426        if ($threshold <= -1) {
 
  427            return $sampleFloat / -$threshold;
 
  430        $sign = $sampleFloat < 0 ? -1 : 1;
 
  431        $sampleAbs = abs($sampleFloat);
 
  434        if ($threshold >= 0 && $threshold < 1 && $sampleAbs > $threshold) {
 
  435            $loga = self::$LOOKUP_LOGBASE[(int)($threshold * 20)]; 
 
  436            return $sign * ($threshold + (1 - $threshold) * log(1 + $loga * ($sampleAbs - $threshold) / (2 - $threshold)) / log(1 + $loga));
 
  440        $thresholdAbs = abs($threshold);
 
  441        if ($threshold > -1 && $threshold < 0 && $sampleAbs > $thresholdAbs) {
 
  442            return $sign * ($thresholdAbs + (1 - $thresholdAbs) / (2 - $thresholdAbs) * ($sampleAbs - $thresholdAbs));
 
  458        if (is_null($actualSize)) {
 
  461            $this->_actualSize = $actualSize;
 
  472        if (is_null($chunkSize)) {
 
  473            $this->_chunkSize = 4 +                                                            
 
  474                                8 + $this->_fmtChunkSize +                                     
 
  475                                ($this->_factChunkSize > 0 ? 8 + $this->_factChunkSize : 0) +  
 
  476                                8 + $this->_dataSize +                                         
 
  477                                ($this->_dataSize & 1);                                        
 
  479            $this->_chunkSize = $chunkSize;
 
  492        if (is_null($fmtChunkSize)) {
 
  495            $this->_fmtChunkSize = $fmtChunkSize;
 
  509        if (is_null($fmtExtendedSize)) {
 
  510            if ($this->_audioFormat == self::WAVE_FORMAT_EXTENSIBLE) {
 
  511                $this->_fmtExtendedSize = 2 + 22;                          
 
  512            } elseif ($this->_audioFormat != self::WAVE_FORMAT_PCM) {
 
  513                $this->_fmtExtendedSize = 2 + 0;                           
 
  515                $this->_fmtExtendedSize = 0;                               
 
  518            $this->_fmtExtendedSize = $fmtExtendedSize;
 
  531        if (is_null($factChunkSize)) {
 
  532            if ($this->_audioFormat != self::WAVE_FORMAT_PCM) {
 
  533                $this->_factChunkSize = 4;
 
  535                $this->_factChunkSize = 0;
 
  538            $this->_factChunkSize = $factChunkSize;
 
  552        if (is_null($dataSize)) {
 
  553            $this->_dataSize = strlen($this->_samples);
 
  555            $this->_dataSize = $dataSize;
 
  560        $this->_dataSize_valid = 
true;
 
  570        if (is_null($dataOffset)) {
 
  571            $this->_dataOffset = 8 +                                                            
 
  573                                 8 + $this->_fmtChunkSize +                                     
 
  574                                 ($this->_factChunkSize > 0 ? 8 + $this->_factChunkSize : 0) +  
 
  577            $this->_dataOffset = $dataOffset;
 
  588        if (is_null($audioFormat)) {
 
  589            if (($this->_bitsPerSample <= 16 || $this->_bitsPerSample == 32)
 
  590              && $this->_validBitsPerSample == $this->_bitsPerSample
 
  591              && $this->_channelMask == self::SPEAKER_DEFAULT
 
  592              && $this->_numChannels <= 2) {
 
  593                if ($this->_bitsPerSample <= 16) {
 
  602            $this->_audioFormat = $audioFormat;
 
  607             ->setFmtExtendedSize();  
 
  617        if (is_null($audioSubFormat)) {
 
  618            if ($this->_bitsPerSample == 32) {
 
  624            $this->_audioSubFormat = $audioSubFormat;
 
  635        if ($numChannels < 1 || $numChannels > self::MAX_CHANNEL) {
 
  636            throw new WavFileException(
'Unsupported number of channels. Only up to ' . self::MAX_CHANNEL . 
' channels are supported.');
 
  637        } elseif ($this->_samples !== 
'') {
 
  638            trigger_error(
'Wav already has sample data. Changing the number of channels does not convert and may corrupt the data.', E_USER_NOTICE);
 
  641        $this->_numChannels = (int)$numChannels;
 
  655        if ($channelMask != 0) {
 
  657            $c = (int)$channelMask;
 
  663            if (
$n != $this->_numChannels || (((
int)$channelMask | self::SPEAKER_ALL) != self::SPEAKER_ALL)) {
 
  664                throw new WavFileException(
'Invalid channel mask. The number of channels does not match the number of locations in the mask.');
 
  668        $this->_channelMask = (int)$channelMask;
 
  680        if ($sampleRate < 1 || $sampleRate > self::MAX_SAMPLERATE) {
 
  682        } elseif ($this->_samples !== 
'') {
 
  683            trigger_error(
'Wav already has sample data. Changing the sample rate does not convert the data and may yield undesired results.', E_USER_NOTICE);
 
  686        $this->_sampleRate = (int)$sampleRate;
 
  698        if (!in_array($bitsPerSample, array(8, 16, 24, 32))) {
 
  699            throw new WavFileException(
'Unsupported bits per sample. Only 8, 16, 24 and 32 bits are supported.');
 
  700        } elseif ($this->_samples !== 
'') {
 
  701            trigger_error(
'Wav already has sample data. Changing the bits per sample does not convert and may corrupt the data.', E_USER_NOTICE);
 
  704        $this->_bitsPerSample = (int)$bitsPerSample;
 
  718        if (is_null($validBitsPerSample)) {
 
  721            if ($validBitsPerSample < 1 || $validBitsPerSample > $this->_bitsPerSample) {
 
  722                throw new WavFileException(
'ValidBitsPerSample cannot be greater than BitsPerSample.');
 
  724            $this->_validBitsPerSample = (int)$validBitsPerSample;
 
  737        if (is_null($blockAlign)) {
 
  738            $this->_blockAlign = $this->_numChannels * $this->_bitsPerSample / 8;
 
  740            $this->_blockAlign = $blockAlign;
 
  754        if (is_null($numBlocks)) {
 
  755            $this->_numBlocks = (int)($this->_dataSize / $this->_blockAlign);  
 
  757            $this->_numBlocks = $numBlocks;
 
  768        if (is_null($byteRate)) {
 
  769            $this->_byteRate = $this->_sampleRate * $this->_numChannels * $this->_bitsPerSample / 8;
 
  771            $this->_byteRate = $byteRate;
 
  782        if (strlen($samples) % $this->_blockAlign != 0) {
 
  783            throw new WavFileException(
'Incorrect samples size. Has to be a multiple of BlockAlign.');
 
  786        $this->_samples = $samples;
 
  799        if ($this->_bitsPerSample == 8) {
 
  801        } elseif ($this->_bitsPerSample == 32) {
 
  804            return -(1 << ($this->_bitsPerSample - 1));
 
  810        if ($this->_bitsPerSample == 8) {
 
  812        } elseif ($this->_bitsPerSample == 32) {
 
  821        if($this->_bitsPerSample == 8) {
 
  823        } elseif($this->_bitsPerSample == 32) {
 
  826            return (1 << ($this->_bitsPerSample - 1)) - 1;
 
  847        $header = pack(
'N', 0x52494646);                            
 
  849        $header .= pack(
'N', 0x57415645);                           
 
  852        $header .= pack(
'N', 0x666d7420);                           
 
  871            $header .= pack(
'N', 0x66616374);                       
 
  887        if (!$this->_dataSize_valid) {
 
  893        return pack(
'N', 0x64617461) .                    
 
  908        if (!is_resource($fp)) {
 
  934        } elseif (is_resource($this->_fp)) {
 
  941        if (!is_resource($this->_fp)) {
 
  946        return $this->
readWav($readData);
 
  954        if (is_resource($this->_fp)) fclose($this->_fp);
 
  970        if (is_resource($this->_fp)) $this->
closeWav();
 
  974        $this->_fp = @fopen(
'php://memory', 
'w+b');
 
  975        if (!is_resource($this->_fp)) {
 
  976            throw new WavFileException(
'Failed to open memory stream to write wav data. Use openWav() instead.');
 
  980        fwrite($this->_fp, 
$data);
 
  984        if ($free) 
$data = 
null;
 
  999        if (!is_resource($this->_fp)) {
 
 1024        if (!is_resource($this->_fp)) {
 
 1029        $stat = fstat($this->_fp);
 
 1030        $actualSize = $stat[
'size'];
 
 1032        $this->_actualSize = $actualSize;
 
 1036        $header = fread($this->_fp, 36);  
 
 1043        $RIFF = unpack(
'NChunkID/VChunkSize/NFormat', 
$header);
 
 1045        if ($RIFF[
'ChunkID'] != 0x52494646) {  
 
 1049        if ($actualSize - 8 < $RIFF[
'ChunkSize']) {
 
 1050            trigger_error(
'"RIFF" chunk size does not match actual file size. Found ' . $RIFF[
'ChunkSize'] . 
', expected ' . ($actualSize - 8) . 
'.', E_USER_NOTICE);
 
 1051            $RIFF[
'ChunkSize'] = $actualSize - 8;
 
 1055        if ($RIFF[
'Format'] != 0x57415645) {  
 
 1056            throw new WavFormatException(
'Not wav format. "RIFF" chunk format is not "WAVE".', 4);
 
 1059        $this->_chunkSize = $RIFF[
'ChunkSize'];
 
 1063        $fmt = unpack(
'NSubchunkID/VSubchunkSize/vAudioFormat/vNumChannels/' 
 1064                     .
'VSampleRate/VByteRate/vBlockAlign/vBitsPerSample',
 
 1067        if ($fmt[
'SubchunkID'] != 0x666d7420) {  
 
 1071        if ($fmt[
'SubchunkSize'] < 16) {
 
 1075        if (   $fmt[
'AudioFormat'] != self::WAVE_FORMAT_PCM
 
 1076            && $fmt[
'AudioFormat'] != self::WAVE_FORMAT_IEEE_FLOAT
 
 1077            && $fmt[
'AudioFormat'] != self::WAVE_FORMAT_EXTENSIBLE)
 
 1079            throw new WavFormatException(
'Unsupported audio format. Only PCM or IEEE FLOAT (EXTENSIBLE) audio is supported.', 13);
 
 1082        if ($fmt[
'NumChannels'] < 1 || $fmt[
'NumChannels'] > self::MAX_CHANNEL) {
 
 1086        if ($fmt[
'SampleRate'] < 1 || $fmt[
'SampleRate'] > self::MAX_SAMPLERATE) {
 
 1090        if (   ($fmt[
'AudioFormat'] == self::WAVE_FORMAT_PCM && !in_array($fmt[
'BitsPerSample'], array(8, 16, 24)))
 
 1091            || ($fmt[
'AudioFormat'] == self::WAVE_FORMAT_IEEE_FLOAT && $fmt[
'BitsPerSample'] != 32)
 
 1092            || ($fmt[
'AudioFormat'] == self::WAVE_FORMAT_EXTENSIBLE && !in_array($fmt[
'BitsPerSample'], array(8, 16, 24, 32))))
 
 1094            throw new WavFormatException(
'Only 8, 16 and 24-bit PCM and 32-bit IEEE FLOAT (EXTENSIBLE) audio is supported.', 16);
 
 1097        $blockAlign = $fmt[
'NumChannels'] * $fmt[
'BitsPerSample'] / 8;
 
 1098        if ($blockAlign != $fmt[
'BlockAlign']) {
 
 1099            trigger_error(
'Invalid block align in "fmt " subchunk. Found ' . $fmt[
'BlockAlign'] . 
', expected ' . $blockAlign . 
'.', E_USER_NOTICE);
 
 1100            $fmt[
'BlockAlign'] = $blockAlign;
 
 1104        $byteRate = $fmt[
'SampleRate'] * $blockAlign;
 
 1105        if ($byteRate != $fmt[
'ByteRate']) {
 
 1106            trigger_error(
'Invalid average byte rate in "fmt " subchunk. Found ' . $fmt[
'ByteRate'] . 
', expected ' . $byteRate . 
'.', E_USER_NOTICE);
 
 1107            $fmt[
'ByteRate'] = $byteRate;
 
 1111        $this->_fmtChunkSize  = $fmt[
'SubchunkSize'];
 
 1112        $this->_audioFormat   = $fmt[
'AudioFormat'];
 
 1113        $this->_numChannels   = $fmt[
'NumChannels'];
 
 1114        $this->_sampleRate    = $fmt[
'SampleRate'];
 
 1115        $this->_byteRate      = $fmt[
'ByteRate'];
 
 1116        $this->_blockAlign    = $fmt[
'BlockAlign'];
 
 1117        $this->_bitsPerSample = $fmt[
'BitsPerSample'];
 
 1122        if ($fmt[
'SubchunkSize'] > 16) {
 
 1124            $extendedFmt = fread($this->_fp, $fmt[
'SubchunkSize'] - 16 + ($fmt[
'SubchunkSize'] & 1));  
 
 1125            if (strlen($extendedFmt) < $fmt[
'SubchunkSize'] - 16) {
 
 1132        if ($fmt[
'AudioFormat'] == self::WAVE_FORMAT_EXTENSIBLE) {
 
 1133            if (strlen($extendedFmt) < 24) {
 
 1134                throw new WavFormatException(
'Invalid EXTENSIBLE "fmt " subchunk size. Found ' . $fmt[
'SubchunkSize'] . 
', expected at least 40.', 19);
 
 1137            $extensibleFmt = unpack(
'vSize/vValidBitsPerSample/VChannelMask/H32SubFormat', substr($extendedFmt, 0, 24));
 
 1139            if (   $extensibleFmt[
'SubFormat'] != self::WAVE_SUBFORMAT_PCM
 
 1140                && $extensibleFmt[
'SubFormat'] != self::WAVE_SUBFORMAT_IEEE_FLOAT)
 
 1142                throw new WavFormatException(
'Unsupported audio format. Only PCM or IEEE FLOAT (EXTENSIBLE) audio is supported.', 13);
 
 1145            if (   ($extensibleFmt[
'SubFormat'] == self::WAVE_SUBFORMAT_PCM && !in_array($fmt[
'BitsPerSample'], array(8, 16, 24)))
 
 1146                || ($extensibleFmt[
'SubFormat'] == self::WAVE_SUBFORMAT_IEEE_FLOAT && $fmt[
'BitsPerSample'] != 32))
 
 1148                throw new WavFormatException(
'Only 8, 16 and 24-bit PCM and 32-bit IEEE FLOAT (EXTENSIBLE) audio is supported.', 16);
 
 1151            if ($extensibleFmt[
'Size'] != 22) {
 
 1152                trigger_error(
'Invaid extension size in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
 
 1153                $extensibleFmt[
'Size'] = 22;
 
 1157            if ($extensibleFmt[
'ValidBitsPerSample'] != $fmt[
'BitsPerSample']) {
 
 1158                trigger_error(
'Invaid or unsupported valid bits per sample in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
 
 1159                $extensibleFmt[
'ValidBitsPerSample'] = $fmt[
'BitsPerSample'];
 
 1163            if ($extensibleFmt[
'ChannelMask'] != 0) {
 
 1165                $c = (int)$extensibleFmt[
'ChannelMask'];
 
 1171                if (
$n != $fmt[
'NumChannels'] || (((
int)$extensibleFmt[
'ChannelMask'] | self::SPEAKER_ALL) != self::SPEAKER_ALL)) {
 
 1172                    trigger_error(
'Invalid channel mask in EXTENSIBLE "fmt " subchunk. The number of channels does not match the number of locations in the mask.', E_USER_NOTICE);
 
 1173                    $extensibleFmt[
'ChannelMask'] = 0;
 
 1178            $this->_fmtExtendedSize    = strlen($extendedFmt);
 
 1179            $this->_validBitsPerSample = $extensibleFmt[
'ValidBitsPerSample'];
 
 1180            $this->_channelMask        = $extensibleFmt[
'ChannelMask'];
 
 1181            $this->_audioSubFormat     = $extensibleFmt[
'SubFormat'];
 
 1184            $this->_fmtExtendedSize    = strlen($extendedFmt);
 
 1185            $this->_validBitsPerSample = $fmt[
'BitsPerSample'];
 
 1186            $this->_channelMask        = 0;
 
 1187            $this->_audioSubFormat     = 
null;
 
 1192        $factSubchunk = array();
 
 1193        $dataSubchunk = array();
 
 1195        while (!feof($this->_fp)) {
 
 1196            $subchunkHeader = fread($this->_fp, 8);
 
 1197            if (strlen($subchunkHeader) < 8) {
 
 1201            $subchunk = unpack(
'NSubchunkID/VSubchunkSize', $subchunkHeader);
 
 1203            if ($subchunk[
'SubchunkID'] == 0x66616374) {        
 
 1205                $subchunkData = fread($this->_fp, $subchunk[
'SubchunkSize'] + ($subchunk[
'SubchunkSize'] & 1));  
 
 1206                if (strlen($subchunkData) < 4) {
 
 1210                $factParams = unpack(
'VSampleLength', substr($subchunkData, 0, 4));
 
 1211                $factSubchunk = array_merge($subchunk, $factParams);
 
 1213            } elseif ($subchunk[
'SubchunkID'] == 0x64617461) {  
 
 1214                $dataSubchunk = $subchunk;
 
 1218            } elseif ($subchunk[
'SubchunkID'] == 0x7761766C) {  
 
 1219                throw new WavFormatException(
'Wave List Chunk ("wavl" subchunk) is not supported.', 106);
 
 1223                if ( $subchunk[
'SubchunkSize'] < 0
 
 1224                  || fseek($this->_fp, $subchunk[
'SubchunkSize'] + ($subchunk[
'SubchunkSize'] & 1), SEEK_CUR) !== 0) {  
 
 1225                    throw new WavFormatException(
'Invalid subchunk (0x' . dechex($subchunk[
'SubchunkID']) . 
') encountered.', 103);
 
 1230        if (empty($dataSubchunk)) {
 
 1236        $dataOffset = ftell($this->_fp);
 
 1237        if ($dataSubchunk[
'SubchunkSize'] < 0 || $actualSize - $dataOffset < $dataSubchunk[
'SubchunkSize']) {
 
 1238            trigger_error(
'Invalid "data" subchunk size.', E_USER_NOTICE);
 
 1239            $dataSubchunk[
'SubchunkSize'] = $actualSize - $dataOffset;
 
 1243        $this->_dataOffset     = $dataOffset;
 
 1244        $this->_dataSize       = $dataSubchunk[
'SubchunkSize'];
 
 1245        $this->_dataSize_fp    = $dataSubchunk[
'SubchunkSize'];
 
 1246        $this->_dataSize_valid = 
false;
 
 1247        $this->_samples        = 
'';
 
 1251        $numBlocks = (int)($dataSubchunk[
'SubchunkSize'] / $fmt[
'BlockAlign']);
 
 1253        if (empty($factSubchunk)) {  
 
 1254            $factSubchunk = array(
'SubchunkSize' => 0, 
'SampleLength' => $numBlocks);
 
 1257        if ($factSubchunk[
'SampleLength'] != $numBlocks) {
 
 1258            trigger_error(
'Invalid sample length in "fact" subchunk.', E_USER_NOTICE);
 
 1259            $factSubchunk[
'SampleLength'] = $numBlocks;
 
 1263        $this->_factChunkSize = $factSubchunk[
'SubchunkSize'];
 
 1264        $this->_numBlocks     = $factSubchunk[
'SampleLength'];
 
 1281        if (!is_resource($this->_fp)) {
 
 1285        if ($dataOffset < 0 || $dataOffset % $this->
getBlockAlign() > 0) {
 
 1286            throw new WavFileException(
'Invalid data offset. Has to be a multiple of BlockAlign.');
 
 1289        if (is_null($dataSize)) {
 
 1290            $dataSize = $this->_dataSize_fp - ($this->_dataSize_fp % $this->
getBlockAlign());  
 
 1291        } elseif ($dataSize < 0 || $dataSize % $this->
getBlockAlign() > 0) {
 
 1292            throw new WavFileException(
'Invalid data size to read. Has to be a multiple of BlockAlign.');
 
 1297        if ($dataOffset > 0 && fseek($this->_fp, $dataOffset, SEEK_CUR) !== 0) {
 
 1302        $this->_samples .= fread($this->_fp, $dataSize);  
 
 1322        if (!$this->_dataSize_valid) {
 
 1327        if ($offset + $this->_blockAlign > $this->_dataSize || $offset < 0) {
 
 1333        return substr($this->_samples, $offset, $this->_blockAlign);
 
 1348        if (!isset($sampleBlock[$blockAlign - 1]) || isset($sampleBlock[$blockAlign])) {  
 
 1349            throw new WavFileException(
'Incorrect sample block size. Got ' . strlen($sampleBlock) . 
', expected ' . $blockAlign . 
'.');
 
 1352        if (!$this->_dataSize_valid) {
 
 1356        $numBlocks = (int)($this->_dataSize / $blockAlign);
 
 1357        $offset = $blockNum * $blockAlign;
 
 1358        if ($blockNum > $numBlocks || $blockNum < 0) {  
 
 1364        if ($blockNum == $numBlocks) {
 
 1366            $this->_samples    .= $sampleBlock;
 
 1367            $this->_dataSize   += $blockAlign;
 
 1368            $this->_chunkSize  += $blockAlign;
 
 1369            $this->_actualSize += $blockAlign;
 
 1370            $this->_numBlocks++;
 
 1373            for (
$i = 0; 
$i < $blockAlign; ++
$i) {
 
 1374                $this->_samples[$offset + 
$i] = $sampleBlock[
$i];
 
 1392        if ($channelNum < 1 || $channelNum > $this->_numChannels) {
 
 1396        if (!$this->_dataSize_valid) {
 
 1400        $sampleBytes = $this->_bitsPerSample / 8;
 
 1401        $offset = $blockNum * $this->_blockAlign + ($channelNum - 1) * $sampleBytes;
 
 1402        if ($offset + $sampleBytes > $this->_dataSize || $offset < 0) {
 
 1407        $sampleBinary = substr($this->_samples, $offset, $sampleBytes);
 
 1410        switch ($this->_bitsPerSample) {
 
 1413                return (
float)((ord($sampleBinary) - 0x80) / 0x80);
 
 1417                $data = unpack(
'v', $sampleBinary);
 
 1419                if ($sample >= 0x8000) {
 
 1422                return (
float)($sample / 0x8000);
 
 1426                $data = unpack(
'C3', $sampleBinary);
 
 1428                if ($sample >= 0x800000) {
 
 1429                    $sample -= 0x1000000;
 
 1431                return (
float)($sample / 0x800000);
 
 1435                $data = unpack(
'f', $sampleBinary);
 
 1436                return (
float)
$data[1];
 
 1456        if ($channelNum < 1 || $channelNum > $this->_numChannels) {
 
 1460        if (!$this->_dataSize_valid) {
 
 1466        $sampleBytes = $bitsPerSample / 8;
 
 1467        $offset = $blockNum * $this->_blockAlign + ($channelNum - 1) * $sampleBytes;
 
 1468        if (($offset + $sampleBytes > $dataSize && $offset != $dataSize) || $offset < 0) { 
 
 1469            throw new WavFileException(
'Sample block or channel number is out of range.');
 
 1474        if ($bitsPerSample == 32) {
 
 1475            $sample = $sampleFloat < -1.0 ? -1.0 : ($sampleFloat > 1.0 ? 1.0 : $sampleFloat);
 
 1477            $p = 1 << ($bitsPerSample - 1); 
 
 1480            $sample = $sampleFloat < 0 ? (int)($sampleFloat * $p - 0.5) : (int)($sampleFloat * $p + 0.5);
 
 1483            if ($sample < -$p) {
 
 1485            } elseif ($sample > $p - 1) {
 
 1491        switch ($bitsPerSample) {
 
 1494                $sampleBinary = chr($sample + 0x80);
 
 1502                $sampleBinary = pack(
'v', $sample);
 
 1508                    $sample += 0x1000000;
 
 1510                $sampleBinary = pack(
'C3', $sample & 0xff, ($sample >>  8) & 0xff, ($sample >> 16) & 0xff);
 
 1515                $sampleBinary = pack(
'f', $sample);
 
 1519                $sampleBinary = 
null;
 
 1525        if ($offset == $dataSize) {
 
 1527            $this->_samples    .= $sampleBinary;
 
 1528            $this->_dataSize   += $sampleBytes;
 
 1529            $this->_chunkSize  += $sampleBytes;
 
 1530            $this->_actualSize += $sampleBytes;
 
 1531            $this->_numBlocks = (int)($this->_dataSize / $this->_blockAlign);
 
 1534            for (
$i = 0; 
$i < $sampleBytes; ++
$i) {
 
 1535                $this->_samples{$offset + 
$i} = $sampleBinary{
$i};
 
 1571    public function filter($filters, $blockOffset = 0, $numBlocks = 
null)
 
 1576        if (is_null($numBlocks)) $numBlocks = $totalBlocks - $blockOffset;
 
 1578        if (!is_array($filters) || empty($filters) || $blockOffset < 0 || $blockOffset > $totalBlocks || $numBlocks <= 0) {
 
 1584        $filter_mix = 
false;
 
 1585        if (array_key_exists(self::FILTER_MIX, $filters)) {
 
 1586            if (!is_array($filters[self::FILTER_MIX])) {
 
 1592            if (!($mix_wav instanceof 
WavFile)) {
 
 1594            } elseif ($mix_wav->getSampleRate() != $this->getSampleRate()) {
 
 1595                throw new WavFileException(
"Sample rate of WavFile to mix does not match.");
 
 1596            } 
else if ($mix_wav->getNumChannels() != $this->getNumChannels()) {
 
 1597                throw new WavFileException(
"Number of channels of WavFile to mix does not match.");
 
 1601            if (is_null($mix_loop)) $mix_loop = 
false;
 
 1604            if (is_null($mix_blockOffset)) $mix_blockOffset = 0;
 
 1606            $mix_totalBlocks = $mix_wav->getNumBlocks();
 
 1608            if (is_null($mix_numBlocks)) $mix_numBlocks = $mix_loop ? $mix_totalBlocks : $mix_totalBlocks - $mix_blockOffset;
 
 1609            $mix_maxBlock = min($mix_blockOffset + $mix_numBlocks, $mix_totalBlocks);
 
 1614        $filter_normalize = 
false;
 
 1615        if (array_key_exists(self::FILTER_NORMALIZE, $filters)) {
 
 1618            if (!is_null($normalize_threshold) && abs($normalize_threshold) != 1) $filter_normalize = 
true;
 
 1621        $filter_degrade = 
false;
 
 1622        if (array_key_exists(self::FILTER_DEGRADE, $filters)) {
 
 1624            if (is_null($degrade_quality)) $degrade_quality = 1;
 
 1626            if ($degrade_quality >= 0 && $degrade_quality < 1) $filter_degrade = 
true;
 
 1631        for ($block = 0; $block < $numBlocks; ++$block) {
 
 1633            for ($channel = 1; $channel <= $numChannels; ++$channel) {
 
 1635                $currentBlock = $blockOffset + $block;
 
 1642                        $mixBlock = ($mix_blockOffset + ($block % $mix_numBlocks)) % $mix_totalBlocks;
 
 1644                        $mixBlock = $mix_blockOffset + $block;
 
 1647                    if ($mixBlock < $mix_maxBlock) {
 
 1648                        $sampleFloat += $mix_wav->getSampleValue($mixBlock, $channel);
 
 1653                if ($filter_normalize) {
 
 1654                    $sampleFloat = $this->
normalizeSample($sampleFloat, $normalize_threshold);
 
 1658                if ($filter_degrade) {
 
 1659                    $sampleFloat += rand(1000000 * ($degrade_quality - 1), 1000000 * (1 - $degrade_quality)) / 1000000;
 
 1685            throw new WavFileException(
"Number of channels for wav files do not match.");
 
 1688        $this->_samples .= $wav->_samples;
 
 1703        return $this->
filter(array(
 
 1716        $numSamples  = (int)($this->
getSampleRate() * abs($duration));
 
 1720        if ($duration >= 0) {
 
 1721            $this->_samples .= 
$data;
 
 1738        return $this->
filter(self::FILTER_DEGRADE, array(
 
 1757        for (
$s = 0; 
$s < $numSamples; ++
$s) {
 
 1758            if ($bitDepth == 32) {
 
 1759                $val = rand(-$percent * 10000, $percent * 10000) / 1000000;
 
 1761                $val = rand($minAmp, $maxAmp);
 
 1762                $val = (int)($val * $percent / 100);
 
 1765            $this->_samples .= str_repeat(self::packSample($val, $bitDepth), $numChannels);
 
 1786            array(self::FILTER_MIX => $this),
 
 1792             ->setBitsPerSample($bitsPerSample);  
 
 1793        $this->_samples = $tempWav->_samples;
 
 1808        $s = 
"File Size: %u\n" 
 1810            .
"fmt Subchunk Size: %u\n" 
 1811            .
"Extended fmt Size: %u\n" 
 1812            .
"fact Subchunk Size: %u\n" 
 1813            .
"Data Offset: %u\n" 
 1815            .
"Audio Format: %s\n" 
 1816            .
"Audio SubFormat: %s\n" 
 1818            .
"Channel Mask: 0x%s\n" 
 1819            .
"Sample Rate: %u\n" 
 1820            .
"Bits Per Sample: %u\n" 
 1821            .
"Valid Bits Per Sample: %u\n" 
 1822            .
"Sample Block Size: %u\n" 
 1823            .
"Number of Sample Blocks: %u\n" 
 1824            .
"Byte Rate: %uBps\n";
 
 1833                         $this->
getAudioFormat() == self::WAVE_FORMAT_PCM ? 
'PCM' : ($this->
getAudioFormat() == self::WAVE_FORMAT_IEEE_FLOAT ? 
'IEEE FLOAT' : 
'EXTENSIBLE'),
 
 1844        if (php_sapi_name() == 
'cli') {
 
sprintf('%.4f', $callTime)
An exception for terminatinating execution or to throw for unit testing.
WavFileException indicates an illegal state or argument in this class.
const SPEAKER_TOP_BACK_CENTER
getSampleBlock($blockNum)
Return a single sample block from the file.
static unpackSampleBlock($sampleBlock, $bitDepth, $numChannels=null)
Unpacks a binary sample block to numeric values.
setAudioSubFormat($audioSubFormat=null)
openWav($filename, $readData=true)
Reads a wav header and data from a file.
static unpackSample($sampleBinary, $bitDepth=null)
Unpacks a single binary sample to numeric value.
const SPEAKER_TOP_FRONT_CENTER
static packSampleBlock($samples, $bitDepth)
Packs an array of numeric channel samples to a binary sample block.
const WAVE_FORMAT_EXTENSIBLE
setDataSize($dataSize=null)
makeHeader()
Construct a wav header from this object.
setSampleBlock($sampleBlock, $blockNum)
Set a single sample block.
setChunkSize($chunkSize=null)
setFmtChunkSize($fmtChunkSize=null)
setActualSize($actualSize=null)
mergeWav(WavFile $wav, $normalizeThreshold=null)
Mix 2 wav files together.
setNumBlocks($numBlocks=null)
setChannelMask($channelMask=self::SPEAKER_DEFAULT)
convertBitsPerSample($bitsPerSample)
Convert sample data to different bits per sample.
degrade($quality=1.0)
Degrade the quality of the wav file by introducing random noise.
save($filename)
Save the wav data to a file.
static normalizeSample($sampleFloat, $threshold)
Normalizes a float audio sample.
setFactChunkSize($factChunkSize=null)
insertSilence($duration=1.0)
Add silence to the wav file.
filter($filters, $blockOffset=0, $numBlocks=null)
Run samples through audio processing filters.
setFmtExtendedSize($fmtExtendedSize=null)
setValidBitsPerSample($validBitsPerSample=null)
getSampleValue($blockNum, $channelNum)
Get a float sample value for a specific sample block and channel number.
const SPEAKER_DEFAULT
Channel Locations for ChannelMask.
setSampleValue($sampleFloat, $blockNum, $channelNum)
Sets a float sample value for a specific sample block number and channel.
const SPEAKER_TOP_BACK_RIGHT
displayInfo()
Output information about the wav object.
const SPEAKER_TOP_BACK_LEFT
getDataSubchunk()
Construct wav DATA chunk.
const SPEAKER_FRONT_LEFT_OF_CENTER
const SPEAKER_LOW_FREQUENCY
const SPEAKER_TOP_FRONT_LEFT
setByteRate($byteRate=null)
setAudioFormat($audioFormat=null)
readWavData($dataOffset=0, $dataSize=null)
Read the wav data from the file into the buffer.
setWavData(&$data, $free=true)
Set the wav file data and properties from a wav file in a string.
generateNoise($duration=1.0, $percent=100)
Generate noise at the end of the wav for the specified duration and volume.
const SPEAKER_BACK_CENTER
setNumChannels($numChannels)
const SPEAKER_FRONT_CENTER
closeWav()
Close a with openWav() previously opened wav file or free the buffer of setWavData().
setBitsPerSample($bitsPerSample)
static packSample($sample, $bitDepth)
Packs a single numeric sample to binary.
setBlockAlign($blockAlign=null)
readWav($readData=true)
Read wav file from a stream.
__construct($numChannelsOrFileName=null, $sampleRateOrReadData=null, $bitsPerSample=null)
WavFile Constructor.
const SPEAKER_FRONT_RIGHT
const WAVE_SUBFORMAT_IEEE_FLOAT
setDataOffset($dataOffset=null)
setSampleRate($sampleRate)
readWavHeader()
Parse a wav header.
const SPEAKER_TOP_FRONT_RIGHT
__toString()
Output the wav file headers and data.
const SPEAKER_FRONT_RIGHT_OF_CENTER
const WAVE_FORMAT_IEEE_FLOAT
appendWav(WavFile $wav)
Append a wav file to the current wav.