/** * Append a wav file to the current wav. <br /> * The wav files must have the same sample rate, number of bits per sample, and number of channels. * * @param WavFile $wav (Required) The wav file to append. * @throws WavFileException */ public function appendWav(WavFile $wav) { // basic checks if ($wav->getSampleRate() != $this->getSampleRate()) { throw new WavFileException("Sample rate for wav files do not match."); } else { if ($wav->getBitsPerSample() != $this->getBitsPerSample()) { throw new WavFileException("Bits per sample for wav files do not match."); } else { if ($wav->getNumChannels() != $this->getNumChannels()) { throw new WavFileException("Number of channels for wav files do not match."); } } } $this->_samples .= $wav->_samples; $this->setDataSize(); // implicit setSize(), setActualSize(), setNumBlocks() return $this; }
/** * 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(); }
/** * 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(); }
protected function _createAudio() { if (is_null($this->_key)) { throw new \Exception('Key must be generated'); } // TODO need get IT, DE and ES audio files //http://www.gilles-joyeux.fr/MP3/NEn.mp3 letters //http://www.gilles-joyeux.fr/MP3/ten.mp3 numbers // revoir les sons S et F en anglais ... try { $globalWavFile = new \WavFile(); // Set sample rate, bits/sample, and Num of channels // TODO setter and getter this params ? $globalWavFile->setSampleRate(8000); $globalWavFile->setBitsPerSample(8); $globalWavFile->setNumChannels(1); $letters = str_split(strtoupper($this->_key)); foreach ($letters as $letter) { if (!file_exists($this->_audioLangDirectory . $letter . '.wav')) { throw new \Exception('Audio file : "' . $this->_audioLangDirectory . $letter . '.wav' . '" miss'); } $l = new \WavFile($this->_audioLangDirectory . $letter . '.wav'); // append letter to the captcha audio $globalWavFile->appendWav($l); // random silence between letters $globalWavFile->insertSilence(mt_rand($this->_audioLettersGapMin, $this->_audioLettersGapMax) / 1000.0); //rand min and max } // Add filters $filters = array(); // noise by sound file if ($this->_audioNoise) { // use background audio $wavNoise = new \WavFile($this->_audioNoiseFile, false); $wavNoise->setSampleRate($globalWavFile->getSampleRate())->setBitsPerSample($globalWavFile->getBitsPerSample())->setNumChannels($globalWavFile->getNumChannels()); // start at a random offset from the beginning of the wav file in order to add more randomness $randOffset = 0; if ($wavNoise->getNumBlocks() > 2 * $globalWavFile->getNumBlocks()) { $randBlock = rand(0, $wavNoise->getNumBlocks() - $globalWavFile->getNumBlocks()); $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $globalWavFile->getNumBlocks() * $wavNoise->getBlockAlign()); } else { $wavNoise->readWavData(); $randOffset = rand(0, $wavNoise->getNumBlocks() - 1); } $mixOpts = array('wav' => $wavNoise, 'loop' => true, 'blockOffset' => $randOffset); $filters[\WavFile::FILTER_MIX] = $mixOpts; $filters[\WavFile::FILTER_NORMALIZE] = $this->_audioNoiseMixNormalization; } // add random noise. Any noise level below 95% is intensely distorted and not pleasing to the ear if ($this->_audioDegrade) { $filters[\WavFile::FILTER_DEGRADE] = rand(95, 98) / 100.0; } // apply filters to audio file if (count($filters) > 0) { $globalWavFile->filter($filters); } // save $this->_audioContents = $globalWavFile->makeHeader(); $this->_audioContents .= $globalWavFile->getDataSubchunk(); unset($globalWavFile); } catch (\Exception $e) { Logger::getInstance()->debug('Security captcha generate audio file for form : "' . $this->getFormName() . '" error : "' . $e . '"', 'security'); if (file_exists($this->_audioLangDirectory . 'error.wav')) { $this->_audioContents = file_get_contents($this->_audioLangDirectory . 'error.wav'); } } }