/** * Generate a wav file given the $letters in the code * @todo Add ability to merge 2 sound files together to have random background sounds * @param array $letters * @return string The binary contents of the wav file */ protected function generateWAV($letters) { $wavCaptcha = new WavFile(); $first = true; // reading first wav file foreach ($letters as $letter) { $letter = strtoupper($letter); try { $l = new WavFile($this->audio_path . '/' . $letter . '.wav'); if ($first) { // set sample rate, bits/sample, and # of channels for file based on first letter $wavCaptcha->setSampleRate($l->getSampleRate())->setBitsPerSample($l->getBitsPerSample())->setNumChannels($l->getNumChannels()); $first = false; } // append letter to the captcha audio $wavCaptcha->appendWav($l); // random length of silence between $audio_gap_min and $audio_gap_max if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) { $wavCaptcha->insertSilence(mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0); } } catch (Exception $ex) { // failed to open file, or the wav file is broken or not supported // 2 wav files were not compatible, different # channels, bits/sample, or sample rate throw $ex; } } /********* Set up audio filters *****************************/ $filters = array(); if ($this->audio_use_noise == true) { // use background audio - find random file $noiseFile = $this->getRandomNoiseFile(); if ($noiseFile !== false && is_readable($noiseFile)) { try { $wavNoise = new WavFile($noiseFile, false); } catch (Exception $ex) { throw $ex; } // start at a random offset from the beginning of the wavfile // in order to add more randomness $randOffset = 0; if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) { $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks()); $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign()); } else { $wavNoise->readWavData(); $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1); } $mixOpts = array('wav' => $wavNoise, 'loop' => true, 'blockOffset' => $randOffset); $filters[WavFile::FILTER_MIX] = $mixOpts; $filters[WavFile::FILTER_NORMALIZE] = $this->audio_mix_normalization; } } if ($this->degrade_audio == true) { // add random noise. // any noise level below 95% is intensely distorted and not pleasant to the ear $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0; } if (!empty($filters)) { $wavCaptcha->filter($filters); // apply filters to captcha audio } return $wavCaptcha->__toString(); }
/** * Generate a wav file given the $letters in the code * * @param array $letters The letters making up the captcha * @return string The audio content in WAV format */ protected function generateWAV($letters) { $wavCaptcha = new WavFile(); $first = true; // reading first wav file if ($this->audio_use_sox && !is_executable($this->sox_binary_path)) { throw new Exception("Path to SoX binary is incorrect or not executable"); } foreach ($letters as $letter) { $letter = strtoupper($letter); try { $letter_file = realpath($this->audio_path) . DIRECTORY_SEPARATOR . $letter . '.wav'; if ($this->audio_use_sox) { $sox_cmd = sprintf("%s %s -t wav - %s", $this->sox_binary_path, $letter_file, $this->getSoxEffectChain()); $data = `{$sox_cmd}`; $l = new WavFile(); $l->setIgnoreChunkSizes(true); $l->setWavData($data); } else { $l = new WavFile($letter_file); } if ($first) { // set sample rate, bits/sample, and # of channels for file based on first letter $wavCaptcha->setSampleRate($l->getSampleRate())->setBitsPerSample($l->getBitsPerSample())->setNumChannels($l->getNumChannels()); $first = false; } // append letter to the captcha audio $wavCaptcha->appendWav($l); // random length of silence between $audio_gap_min and $audio_gap_max if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) { $wavCaptcha->insertSilence(mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0); } } catch (Exception $ex) { // failed to open file, or the wav file is broken or not supported // 2 wav files were not compatible, different # channels, bits/sample, or sample rate throw new Exception("Error generating audio captcha on letter '{$letter}': " . $ex->getMessage()); } } /********* Set up audio filters *****************************/ $filters = array(); if ($this->audio_use_noise == true) { // use background audio - find random file $wavNoise = false; $randOffset = 0; /* // uncomment to try experimental SoX noise generation. // warning: sounds may be considered annoying if ($this->audio_use_sox) { $duration = $wavCaptcha->getDataSize() / ($wavCaptcha->getBitsPerSample() / 8) / $wavCaptcha->getNumChannels() / $wavCaptcha->getSampleRate(); $duration = round($duration, 2); $wavNoise = new WavFile(); $wavNoise->setIgnoreChunkSizes(true); $noiseData = $this->getSoxNoiseData($duration, $wavCaptcha->getNumChannels(), $wavCaptcha->getSampleRate(), $wavCaptcha->getBitsPerSample()); $wavNoise->setWavData($noiseData, true); } else */ if (($noiseFile = $this->getRandomNoiseFile()) !== false) { try { $wavNoise = new WavFile($noiseFile, false); } catch (Exception $ex) { throw $ex; } // start at a random offset from the beginning of the wavfile // in order to add more randomness $randOffset = 0; if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) { $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks()); $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign()); } else { $wavNoise->readWavData(); $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1); } } if ($wavNoise !== false) { $mixOpts = array('wav' => $wavNoise, 'loop' => true, 'blockOffset' => $randOffset); $filters[WavFile::FILTER_MIX] = $mixOpts; $filters[WavFile::FILTER_NORMALIZE] = $this->audio_mix_normalization; } } if ($this->degrade_audio == true) { // add random noise. // any noise level below 95% is intensely distorted and not pleasant to the ear $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0; } if (!empty($filters)) { $wavCaptcha->filter($filters); // apply filters to captcha audio } return $wavCaptcha->__toString(); }