<?php
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Chris Pollett chris@pollett.org
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*/
namespace seekquarry\yioop\tests;
use seekquarry\yioop\library\av_processing\RangeDecoder;
use seekquarry\yioop\library\av_processing\SpeechPulses;
use seekquarry\yioop\library\av_processing\SpeechPulseTables;
use seekquarry\yioop\library\UnitTest;
/**
* SpeechPulsesTest checks the rules SpeechPulses must obey whatever bits
* it is handed: that spreading a block's count over its sixteen samples
* keeps the count, that a block holding nothing spreads to nothing, and
* that doubling a loud block's counts leaves them where they should be.
*
* These are checks on the arithmetic rather than on a recording. The
* reading of a real recording is measured against ffmpeg's own decoding
* of it, which is a separate step of the work and not yet right.
*
* @author Chris Pollett
*/
class SpeechPulsesTest extends UnitTest
{
/**
* $reader stores a range decoder over made-up bytes, which the cases
* hand to the parts of SpeechPulses that read from one.
* @var RangeDecoder
*/
public $reader;
/**
* setUp makes a range decoder over a fixed run of bytes, so a case
* reading from it gets the same answers on every run.
*/
public function setUp()
{
$this->reader = new RangeDecoder(str_repeat("\x5A", 64));
}
/**
* tearDown does nothing, since no case here writes a file.
*/
public function tearDown()
{
}
/**
* blocksCountIsKeptWhenItIsSpreadTestCase checks that the sixteen
* counts a block spreads into add up to the count the block was
* given. A split that lost or gained a pulse would put every sample
* after it in the wrong place.
*/
public function blocksCountIsKeptWhenItIsSpreadTestCase()
{
foreach ([1, 2, 3, 5, 8, 13, 16] as $count) {
$spread = SpeechPulses::splitCount($this->reader, $count);
$this->assertEqual($count, array_sum($spread),
"a count of $count spreads to the same count");
$this->assertEqual(SpeechPulseTables::SAMPLES_IN_BLOCK,
count($spread),
"and spreads over sixteen samples");
}
}
/**
* blockHoldingNothingSpreadsToNothingTestCase checks that a block
* with no pulses spreads to sixteen zeroes without reading any bits.
* A block of silence is common in speech, so reading bits for one
* would take them from whatever follows.
*/
public function blockHoldingNothingSpreadsToNothingTestCase()
{
$spread = SpeechPulses::splitCount($this->reader, 0);
$this->assertEqual(0, array_sum($spread),
"a block holding nothing spreads to nothing");
$this->assertEqual(SpeechPulseTables::SAMPLES_IN_BLOCK,
count($spread), "and still covers sixteen samples");
}
/**
* loudBlockDoublesItsCountsTestCase checks that each doubling
* leaves every count at least twice what it was. A loud block writes
* its counts small and makes up the difference with extra bits, so a
* doubling that did not double would leave the block too quiet.
*/
public function loudBlockDoublesItsCountsTestCase()
{
$spread = array_fill(0, SpeechPulseTables::SAMPLES_IN_BLOCK, 3);
$doubled = SpeechPulses::addExtraBits($this->reader, $spread, 1);
foreach ($doubled as $at => $one) {
$this->assertTrue($one >= 6 && $one <= 7,
"a count of three doubles to six or seven, and became " .
$one);
}
$twice = SpeechPulses::addExtraBits($this->reader, $spread, 2);
foreach ($twice as $one) {
$this->assertTrue($one >= 12 && $one <= 15,
"two doublings take three to between twelve and " .
"fifteen, and gave " . $one);
}
}
/**
* noDoublingLeavesTheCountsAloneTestCase checks that a block with no
* doublings comes back exactly as it went in, and that no bits are
* read for it.
*/
public function noDoublingLeavesTheCountsAloneTestCase()
{
$spread = [0, 1, 2, 3, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0];
$same = SpeechPulses::addExtraBits($this->reader, $spread, 0);
$this->assertEqual($spread, $same,
"a block with no doublings is left as it was");
}
}