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);
309 $sample = $data[1] | ($data[2] << 8) | ($data[3] << 16);
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);
861 $header .= pack(
'v', 22);
866 $header .= pack(
'v', 0);
871 $header .= pack(
'N', 0x66616374);
872 $header .= pack(
'V', 4);
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);
1037 if (strlen($header) < 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',
1065 substr($header, 12));
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);
1427 $sample = $data[1] | ($data[2] << 8) | ($data[3] << 16);
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') {