<?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
*
* AacDecoder turns compressed AAC frames back into samples of sound.
*/
namespace seekquarry\yioop\library\av_processing;
/**
* AacDecoder turns the compressed frames of an AAC sound track back
* into samples. A frame carries the strengths of the tones a stretch of
* sound is made of, rounded coarsely where the ear would not notice, so
* decoding one means reading those strengths, scaling them back up, and
* turning them from tones into sound again.
*
* Frames overlap: each covers twice as much sound as it advances by,
* and neighboring frames are faded into one another, so a decoder holds
* the tail of one frame until the next arrives.
*
* What this reads so far is the AAC that AacEncoder writes: one channel
* to a part, long windows, and the table of codes the standard numbers
* eleven. Parts that pad a frame out or carry a writer's own notes are
* stepped over, since a real file begins with one.
*
* A file from another encoder decodes: all eleven tables of codes are
* read, bands of noise are made rather than read, and a pair of
* channels written as a sum and a difference is put back and averaged
* into one. Measured against ffmpeg's own decoding of a stereo song at
* a hundred and fifty thousand bits a second, the samples differ by six
* thousandths on average.
*
* A frame written as eight short stretches is read as well. A stream
* writes one where the sound changes quickly, such as at the start of a
* word or a drum beat, because one long stretch would smear that change
* across the whole frame. Every frame of the tone ffmpeg wrote now
* decodes.
*
* One kind of frame is still refused, with a sentence naming what it
* met: a pair of channels that each carry their own settings rather
* than sharing one set. In that song those are eight frames in four
* hundred and seventy.
*
* @author Chris Pollett
*/
class AacDecoder
{
/**
* NAME_BITS is how many bits name the kind of part a frame begins
* with, such as a channel of sound or the end of the frame.
* @var int
*/
const NAME_BITS = 3;
/**
* ONE_CHANNEL is the number naming a part that carries a single
* channel of sound.
* @var int
*/
const ONE_CHANNEL = 0;
/**
* END_OF_FRAME is the number naming the part that closes a frame.
* @var int
*/
const END_OF_FRAME = 7;
/**
* TWO_CHANNELS is the number naming a part that carries a pair of
* channels coded together.
* @var int
*/
const TWO_CHANNELS = 1;
/**
* FILL_PART is the number naming a part that carries no sound. An
* encoder writes one to pad a frame out to the length it aimed at,
* or to carry a note about the sound that a player may ignore.
* @var int
*/
const FILL_PART = 6;
/**
* SPARE_PART is the number naming a part that carries data of the
* writer's own rather than sound.
* @var int
*/
const SPARE_PART = 4;
/**
* SETTINGS_PART is the number naming the part in which a stream
* describes how its channels are laid out.
* @var int
*/
const SETTINGS_PART = 5;
/**
* FILL_LENGTH_BITS is how many bits say how long a fill part is.
* Where the length is as large as those bits can hold, another
* eight bits follow saying how much longer.
* @var int
*/
const FILL_LENGTH_BITS = 4;
/**
* FILL_LENGTH_CARRIES_ON is the length that says a fill part is
* longer than its first count can hold.
* @var int
*/
const FILL_LENGTH_CARRIES_ON = 15;
/**
* SPARE_TAG_BITS is how many bits name which of the writer's own
* parts this is.
* @var int
*/
const SPARE_TAG_BITS = 4;
/**
* BASE_BITS is how many bits the loudness a frame counts from
* takes.
* @var int
*/
const BASE_BITS = 8;
/**
* TABLE_BITS is how many bits name which table of codes a run of
* bands was written with.
* @var int
*/
const TABLE_BITS = 4;
/**
* RUN_BITS is how many bits say how many bands a run covers.
* @var int
*/
const RUN_BITS = 5;
/**
* RUN_CARRIES_ON is the count that says a run is longer than one
* number can hold and carries on into the next.
* @var int
*/
const RUN_CARRIES_ON = 31;
/**
* EMPTY_TABLE is the number naming the table used for a run of
* bands that carry nothing at all.
* @var int
*/
const EMPTY_TABLE = 0;
/**
* NOISE_TABLE is the number a band names instead of a table where
* it carries noise rather than coded values. Sound that is close to
* noise costs many bits to write out and sounds the same when made
* afresh, so a stream says how loud that noise is and leaves the
* decoder to make it.
* @var int
*/
const NOISE_TABLE = 13;
/**
* INTENSITY_TABLES are the numbers a band names where its sound is
* taken from the other channel of a pair rather than written out.
* A single channel never carries these.
* @var array
*/
const INTENSITY_TABLES = [14, 15];
/**
* SHORT_RUN_BITS is how many bits say how many bands a run covers
* in a frame of short stretches. A short stretch has fewer bands
* than a long one, so fewer bits are needed to count them.
* @var int
*/
const SHORT_RUN_BITS = 3;
/**
* SHORT_RUN_CARRIES_ON is the count that says such a run is longer
* than one number can hold and carries on into the next.
* @var int
*/
const SHORT_RUN_CARRIES_ON = 7;
/**
* NOISE_START_BITS is how many bits the first noise loudness of a
* frame takes. The ones after it are written as steps from the one
* before, like any other loudness.
* @var int
*/
const NOISE_START_BITS = 9;
/**
* ESCAPE_AT is the size at which a code stops carrying the number
* itself and the rest of it is written separately.
* @var int
*/
const ESCAPE_AT = 16;
/**
* DECODER_SCALE is what the encoder multiplied every sample by
* before compressing it, so the decoder divides by the same to give
* back sound at the loudness it arrived at.
* @var float
*/
const DECODER_SCALE = 32768.0;
/**
* OVERLAP_SHARE is what each frame's sound is multiplied by before
* neighboring frames are added together. The transform hands back a
* whole frame's worth, and two frames overlap over every sample, so
* each carries half the weight. Measured by encoding a tone and
* decoding it: at this share the sound comes back within a
* thousandth of what went in, and at twice it comes back twice as
* loud.
* @var float
*/
const OVERLAP_SHARE = 0.5;
/**
* $carried stores the tail of the frame decoded before this one.
* Frames overlap by half their length, so the second half of one
* frame is added to the first half of the next.
* @var array
*/
public $carried = [];
/**
* $transform stores the transform that turns tone strengths back
* into sound, made once and used for every frame.
* @var Mdct
*/
public $transform = null;
/**
* $fade stores the curve each frame is faded in and out with. Two
* neighboring frames fade into one another, and their fades add to
* one so that steady sound comes back unchanged.
* @var array
*/
public $fade = [];
/**
* __construct builds the transform and the fade a decoder needs,
* which are the same for every frame.
*/
public function __construct()
{
$this->transform = Mdct::forSize(AacEncoder::HOP);
$this->fade = AacBands::fadeFor(AacBands::LONG);
$this->carried = array_fill(0, AacEncoder::HOP, 0.0);
}
/**
* decodeFrame turns one compressed frame into samples: it reads the
* tone strengths, scales them back up, turns them into sound, fades
* that sound in, and adds the tail the frame before it left.
*
* @param string $frame One compressed frame.
* @return array The samples this frame gives, as fractions of one.
*/
public function decodeFrame($frame)
{
$tones = $this->readTones($frame);
$sound = $this->transform->inverse($tones);
$span = AacEncoder::SPAN;
$hop = AacEncoder::HOP;
$faded = [];
for ($at = 0; $at < $span; $at++) {
$faded[$at] = $sound[$at] * $this->fade[$at];
}
$given = [];
for ($at = 0; $at < $hop; $at++) {
$given[$at] = ($faded[$at] + $this->carried[$at]) *
self::OVERLAP_SHARE / self::DECODER_SCALE;
}
$tail = [];
for ($at = 0; $at < $hop; $at++) {
$tail[$at] = $faded[$hop + $at];
}
$this->carried = $tail;
return $given;
}
/**
* readTones reads the strengths of the tones out of one frame and
* scales each back to its own size. A frame writes those strengths
* as small whole numbers together with a step for each band, and
* the strength is the number raised to four thirds and multiplied
* by the step.
*
* @param string $frame One compressed frame.
* @return array One strength for each tone the frame covers.
*/
public function readTones($frame)
{
$reader = new BitReader($frame);
/* A frame is built of parts, each named by three bits. Only the
part carrying one channel of sound is read here; the parts
that pad a frame out or carry a writer's own notes are
stepped over, and the rest are refused by name. */
while (true) {
if ($reader->bitsLeft() < AacFrame::NAME_BITS) {
throw new \RuntimeException("this frame ends before it "
. "carries any sound");
}
$named = $reader->readBits(AacFrame::NAME_BITS);
if ($named === AacFrame::ONE_CHANNEL) {
break;
}
if ($named === self::FILL_PART) {
self::skipFill($reader);
continue;
}
if ($named === self::SPARE_PART) {
self::skipSpare($reader);
continue;
}
if ($named === self::TWO_CHANNELS) {
return $this->readPairedChannels($reader);
}
if ($named === AacFrame::END_OF_FRAME) {
throw new \RuntimeException("this frame closes before "
. "it carries any sound");
}
throw new \RuntimeException("this frame carries a part "
. "named $named, which is not read yet");
}
$reader->readBits(4);
$base = $reader->readBits(AacFrame::BASE_BITS);
$said = $this->readShape($reader);
if ($said["shape"] === AacBands::SHORT) {
return $this->readShortFrame($reader, $said, $base);
}
$bands = $said["bands"];
if ($bands <= 0 || $bands > AacBands::longBandCount()) {
$bands = AacBands::longBandCount();
}
$tables = $this->readRuns($reader, $bands);
$steps = $this->readSteps($reader, $bands, $tables, $base);
/* Three flags say whether the frame carries the extra
settings this encoder never writes: added pulses, a filter
over the tones, and a gain that changes within the frame. */
for ($at = 0; $at < 3; $at++) {
if ($reader->readBit() !== 0) {
throw new \RuntimeException("this frame carries extra "
. "settings that are not read yet");
}
}
return $this->readBands($reader, $bands, $tables, $steps);
}
/**
* readPairedChannels reads a part carrying two channels coded
* together and hands back one run of tone strengths. Two channels
* often hold nearly the same sound, so a stream may write their sum
* and their difference rather than each on its own; those are put
* back into a left and a right channel, which are then averaged,
* since what this folder writes carries a single channel.
*
* @param BitReader $reader The frame's bits.
* @return array One strength for each tone the frame covers.
*/
public function readPairedChannels($reader)
{
$reader->readBits(4);
$shared = $reader->readBit();
if ($shared !== 1) {
throw new \RuntimeException("this pair of channels each "
. "carry their own settings, which is not decoded yet");
}
$said = $this->readShape($reader);
if ($said["shape"] === AacBands::SHORT) {
throw new \RuntimeException("this frame uses the shorter "
. "windows, which are not decoded yet");
}
$bands = $said["bands"];
if ($bands <= 0 || $bands > AacBands::longBandCount()) {
$bands = AacBands::longBandCount();
}
/* A pair may say, band by band, that it wrote the sum and the
difference of its two channels rather than each channel. */
$joined = $reader->readBits(2);
$summed = array_fill(0, $bands, false);
if ($joined === 1) {
for ($band = 0; $band < $bands; $band++) {
$summed[$band] = ($reader->readBit() === 1);
}
} elseif ($joined === 2) {
$summed = array_fill(0, $bands, true);
}
$left = $this->readChannel($reader, $bands);
$right = $this->readChannel($reader, $bands);
return self::joinChannels($left, $right, $summed, $bands);
}
/**
* joinChannels turns the two runs of strengths a pair carries into
* one. Where a band holds a sum and a difference, the two channels
* are worked back out of them first, and the two are then averaged
* into the single channel this folder writes.
*
* @param array $left The first channel's strengths.
* @param array $right The second channel's strengths.
* @param array $summed Whether each band holds a sum and a
* difference rather than two channels.
* @param int $bands How many bands the frame covers.
* @return array One strength for each tone.
*/
public static function joinChannels($left, $right, $summed, $bands)
{
$joined = [];
for ($band = 0; $band < $bands; $band++) {
$from = AacBands::LONG_EDGES[$band];
$past = AacBands::LONG_EDGES[$band + 1];
for ($at = $from; $at < $past; $at++) {
$one = $left[$at] ?? 0.0;
$two = $right[$at] ?? 0.0;
if ($summed[$band]) {
$joined[$at] = $one;
} else {
$joined[$at] = ($one + $two) / 2.0;
}
}
}
for ($at = 0; $at < AacEncoder::HOP; $at++) {
if (!isset($joined[$at])) {
$joined[$at] = 0.0;
}
}
ksort($joined);
return array_values($joined);
}
/**
* readChannel reads one channel of a pair: the loudness it counts
* from, which table each band was written with, the step for each
* band, and the values themselves.
*
* @param BitReader $reader The frame's bits.
* @param int $bands How many bands the frame covers.
* @return array One strength for each tone of that channel.
*/
public function readChannel($reader, $bands)
{
$base = $reader->readBits(AacFrame::BASE_BITS);
$tables = $this->readRuns($reader, $bands);
$steps = $this->readSteps($reader, $bands, $tables, $base);
for ($at = 0; $at < 3; $at++) {
if ($reader->readBit() !== 0) {
throw new \RuntimeException("this frame carries extra "
. "settings that are not read yet");
}
}
return $this->readBands($reader, $bands, $tables, $steps);
}
/**
* skipFill steps over a part that carries no sound. Such a part
* says how long it is, and where it is longer than that count can
* hold, a further count follows.
*
* @param BitReader $reader The frame's bits.
*/
public static function skipFill($reader)
{
$length = $reader->readBits(self::FILL_LENGTH_BITS);
if ($length === self::FILL_LENGTH_CARRIES_ON) {
$length += $reader->readBits(8) - 1;
}
$reader->skipBits($length * 8);
}
/**
* skipSpare steps over a part carrying data of the writer's own
* rather than sound. Such a part names itself, says whether its
* length is counted in bytes, then gives that length.
*
* @param BitReader $reader The frame's bits.
*/
public static function skipSpare($reader)
{
$reader->readBits(self::SPARE_TAG_BITS);
$aligned = $reader->readBit();
$length = $reader->readBits(8);
if ($length === 255) {
$length += $reader->readBits(8);
}
if ($aligned === 1) {
$reader->skipBits((8 - ($reader->position % 8)) % 8);
}
$reader->skipBits($length * 8);
}
/**
* readShortFrame reads a frame written as eight short stretches
* rather than one long one. A long stretch would smear a sudden
* change across the whole frame, so a stream splits the frame into
* eight where the sound changes quickly, such as at the start of a
* word or a drum beat.
*
* @param BitReader $reader The frame's bits.
* @param array $said What readShape read: the shape, how many bands
* one short stretch covers, and how the eight are grouped.
* @param int $base The loudness the frame counts from, already read.
* @return array The eight stretches of strengths, one after
* another.
*/
public function readShortFrame($reader, $said, $base)
{
$bands = $said["bands"];
if ($bands <= 0 ||
$bands > count(AacBands::SHORT_EDGES) - 1) {
$bands = count(AacBands::SHORT_EDGES) - 1;
}
$groups = self::groupsWithin($said["grouping"]);
$tables = $this->readShortRuns($reader, $bands, count($groups));
$steps = $this->readShortSteps($reader, $bands, $tables, $base);
for ($at = 0; $at < 3; $at++) {
if ($reader->readBit() !== 0) {
throw new \RuntimeException("this frame carries extra "
. "settings that are not read yet");
}
}
return $this->readShortBands($reader, $bands, $tables, $steps,
$groups);
}
/**
* groupsWithin works out how the eight short stretches of a frame
* are grouped. Stretches that sound alike share one set of steps
* and one set of tables, and the frame marks where each group
* begins with seven bits, one for each stretch after the first.
*
* @param int $grouping The seven bits the frame wrote.
* @return array How many stretches each group holds.
*/
public static function groupsWithin($grouping)
{
$groups = [];
$held = 1;
for ($at = 6; $at >= 0; $at--) {
if ((($grouping >> $at) & 1) === 1) {
$held++;
} else {
$groups[] = $held;
$held = 1;
}
}
$groups[] = $held;
return $groups;
}
/**
* readShortRuns reads which table of codes each band of each group
* was written with. A short frame says its runs with fewer bits
* than a long one, since a short stretch has fewer bands.
*
* @param BitReader $reader The frame's bits.
* @param int $bands How many bands one short stretch covers.
* @param int $groups How many groups the eight stretches make.
* @return array Which table each band of each group uses.
*/
public function readShortRuns($reader, $bands, $groups)
{
$tables = [];
for ($group = 0; $group < $groups; $group++) {
$at = 0;
while ($at < $bands) {
$table = $reader->readBits(AacFrame::TABLE_BITS);
$length = 0;
do {
$piece = $reader->readBits(self::SHORT_RUN_BITS);
$length += $piece;
} while ($piece === self::SHORT_RUN_CARRIES_ON);
if ($length <= 0) {
throw new \RuntimeException("this frame names a run "
. "of no bands, so it cannot be read");
}
for ($seen = 0; $seen < $length && $at < $bands;
$seen++) {
$tables[$group][$at] = $table;
$at++;
}
}
}
return $tables;
}
/**
* readShortSteps reads the step each band of each group is scaled
* by. The steps run on from group to group as they do from band to
* band, each written as how far it sits from the one before.
*
* @param BitReader $reader The frame's bits.
* @param int $bands How many bands one short stretch covers.
* @param array $tables Which table each band of each group uses.
* @param int $base The loudness the frame counts from.
* @return array The step for each band of each group.
*/
public function readShortSteps($reader, $bands, $tables, $base)
{
$steps = [];
$last = $base;
$noise_last = 0;
$noise_started = false;
foreach ($tables as $group => $row) {
for ($band = 0; $band < $bands; $band++) {
$table = $row[$band] ?? AacFrame::EMPTY_TABLE;
if ($table === AacFrame::EMPTY_TABLE) {
$steps[$group][$band] = 0;
continue;
}
if (in_array($table, self::INTENSITY_TABLES, true)) {
self::readLoudnessCode($reader);
$steps[$group][$band] = 0;
continue;
}
if ($table === self::NOISE_TABLE) {
if (!$noise_started) {
$noise_last = $base - 256 +
$reader->readBits(self::NOISE_START_BITS);
$noise_started = true;
} else {
$noise_last += self::readLoudnessCode($reader) -
AacTables::LOUDNESS_SPAN;
}
$steps[$group][$band] = $noise_last;
continue;
}
$last += self::readLoudnessCode($reader) -
AacTables::LOUDNESS_SPAN;
$steps[$group][$band] = $last;
}
}
return $steps;
}
/**
* readShortBands reads the values of every band of every stretch.
* A group's values are written once for the whole group, stretch by
* stretch within it, and the eight stretches are laid end to end in
* what this hands back.
*
* @param BitReader $reader The frame's bits.
* @param int $bands How many bands one short stretch covers.
* @param array $tables Which table each band of each group uses.
* @param array $steps The step each band of each group is scaled
* by.
* @param array $groups How many stretches each group holds.
* @return array The eight stretches of strengths, one after
* another.
*/
public function readShortBands($reader, $bands, $tables, $steps,
$groups)
{
$short = AacBands::SHORT_TONES;
$tones = array_fill(0, AacEncoder::HOP, 0.0);
$stretch = 0;
foreach ($groups as $group => $holds) {
$held = [];
for ($band = 0; $band < $bands; $band++) {
$table = $tables[$group][$band] ?? AacFrame::EMPTY_TABLE;
$from = AacBands::SHORT_EDGES[$band];
$past = AacBands::SHORT_EDGES[$band + 1];
for ($within = 0; $within < $holds; $within++) {
if ($table === AacFrame::EMPTY_TABLE) {
continue;
}
if ($table === self::NOISE_TABLE) {
continue;
}
if (in_array($table, self::INTENSITY_TABLES, true)) {
throw new \RuntimeException("this band takes "
. "its sound from the other channel of a "
. "pair, which is not decoded yet");
}
$code_table = self::tableFor($table);
$escapes = ($table === AacTables::SOUND_TABLE);
$scale = pow(2.0, AacQuantizer::SQUASHED_STEP *
($steps[$group][$band] -
AacQuantizer::MIDDLE_STEP) /
AacQuantizer::SQUASH);
for ($at = $from; $at < $past;
$at += $code_table["holds"]) {
$values = self::readGroup($reader, $code_table,
$escapes);
foreach ($values as $which => $value) {
if ($at + $which >= $past) {
break;
}
$held[$within][$at + $which] =
self::sizeOfNumber($value) * $scale;
}
}
}
}
for ($within = 0; $within < $holds; $within++) {
$start = ($stretch + $within) * $short;
for ($at = 0; $at < $short; $at++) {
$tones[$start + $at] = $held[$within][$at] ?? 0.0;
}
}
$stretch += $holds;
}
return $tones;
}
/**
* readShape reads how the frame was faded. This decoder reads the
* one fade the encoder writes, so a frame saying it used the softer
* fade is refused rather than decoded with the wrong curve.
*
* @param BitReader $reader The frame's bits.
* @return array Which shape the frame used under the key shape, how
* many bands it covers under the key bands, and how its
* stretches are grouped under the key grouping.
*/
public function readShape($reader)
{
$reader->readBit();
$shape = $reader->readBits(AacFrame::SHAPE_BITS);
$reader->readBit();
if ($shape === AacBands::SHORT) {
/* A frame of short windows counts its bands over one short
stretch and says how its eight stretches are grouped, so
both are read before anything else. */
$bands = $reader->readBits(4);
$grouping = $reader->readBits(7);
return ["shape" => $shape, "bands" => $bands,
"grouping" => $grouping];
}
$bands = $reader->readBits(AacFrame::BAND_COUNT_BITS);
$reader->readBit();
return ["shape" => $shape, "bands" => $bands, "grouping" => 0];
}
/**
* readRuns reads which table of codes each band was written with.
* The frame says it once for a run of bands rather than for each,
* and a run longer than one number can hold carries on into the
* next.
*
* @param BitReader $reader The frame's bits.
* @param int $bands How many bands the frame covers.
* @return array Which table each band uses.
*/
public function readRuns($reader, $bands)
{
$tables = [];
$at = 0;
while ($at < $bands) {
$table = $reader->readBits(AacFrame::TABLE_BITS);
$length = 0;
do {
$piece = $reader->readBits(AacFrame::RUN_BITS);
$length += $piece;
} while ($piece === AacFrame::RUN_CARRIES_ON);
if ($length <= 0) {
throw new \RuntimeException("this frame names a run of "
. "no bands, so it cannot be read");
}
for ($seen = 0; $seen < $length && $at < $bands; $seen++) {
$tables[$at] = $table;
$at++;
}
}
return $tables;
}
/**
* readSteps reads the step each band's numbers are scaled by. A
* step is written as how far it sits from the step before, so they
* are added up as they are read, starting from the loudness the
* frame counts from.
*
* @param BitReader $reader The frame's bits.
* @param int $bands How many bands the frame covers.
* @param array $tables Which table each band uses.
* @param int $base The loudness the frame counts from.
* @return array The step for each band.
*/
public function readSteps($reader, $bands, $tables, $base)
{
$steps = [];
$last = $base;
$noise_last = 0;
$noise_started = false;
for ($band = 0; $band < $bands; $band++) {
$table = $tables[$band] ?? AacFrame::EMPTY_TABLE;
if ($table === AacFrame::EMPTY_TABLE) {
$steps[$band] = 0;
continue;
}
if (in_array($table, self::INTENSITY_TABLES, true)) {
$steps[$band] = 0;
self::readLoudnessCode($reader);
continue;
}
if ($table === self::NOISE_TABLE) {
/* The first noise loudness of a frame is written out in
full; each one after it is a step from the last. */
if (!$noise_started) {
$noise_last = $base - 256 +
$reader->readBits(self::NOISE_START_BITS);
$noise_started = true;
} else {
$noise_last += self::readLoudnessCode($reader) -
AacTables::LOUDNESS_SPAN;
}
$steps[$band] = $noise_last;
continue;
}
$which = self::readLoudnessCode($reader);
$last += $which - AacTables::LOUDNESS_SPAN;
$steps[$band] = $last;
}
return $steps;
}
/**
* readLoudnessCode reads one of the codes that say how far a step
* sits from the step before it. The codes are of different lengths,
* so bits are taken one at a time until they spell one out.
*
* @param BitReader $reader The frame's bits.
* @return int Which of the codes was read, counting from zero.
*/
public static function readLoudnessCode($reader)
{
$held = 0;
$length = 0;
while ($length < 20) {
$held = ($held << 1) | $reader->readBit();
$length++;
foreach (AacTables::LOUDNESS_CODE_BITS as $which => $bits) {
if ($bits === $length
&& AacTables::LOUDNESS_CODES[$which] === $held) {
return $which;
}
}
}
throw new \RuntimeException("a step in this frame is written "
. "with a code that is not in the table");
}
/**
* tableFor hands back the table of codes a band was written with,
* along with how many values each of its codes stands for and
* whether the code carries their signs.
*
* @param int $table Which table, as the frame named it.
* @return array The rows of the table under the key rows, how many
* values a code stands for under the key holds, and whether
* signs follow under the key signed.
*/
public static function tableFor($table)
{
$held = [
1 => [AacCodeTables::VALUE_CODES_ONE, 4, false],
2 => [AacCodeTables::VALUE_CODES_TWO, 4, false],
3 => [AacCodeTables::VALUE_CODES_THREE, 4, true],
4 => [AacCodeTables::VALUE_CODES_FOUR, 4, true],
5 => [AacCodeTables::VALUE_CODES_FIVE, 2, false],
6 => [AacCodeTables::VALUE_CODES_SIX, 2, false],
7 => [AacCodeTables::VALUE_CODES_SEVEN, 2, true],
8 => [AacCodeTables::VALUE_CODES_EIGHT, 2, true],
9 => [AacCodeTables::VALUE_CODES_NINE, 2, true],
10 => [AacCodeTables::VALUE_CODES_TEN, 2, true],
11 => [AacCodeTables::VALUE_CODES_ELEVEN, 2, true],
];
if (!isset($held[$table])) {
throw new \RuntimeException("this frame names table $table "
. "of codes, which the standard does not fix");
}
return ["rows" => $held[$table][0], "holds" => $held[$table][1],
"signed" => $held[$table][2]];
}
/**
* rowFor takes bits until they spell out one of a table's codes,
* and hands back the row that code names. A code may be anywhere
* from one to nineteen bits long, so the reading grows a bit at a
* time until it matches.
*
* @param BitReader $reader The frame's bits.
* @param array $rows The rows of the table being read.
* @return array The row the bits spelled out.
*/
public static function rowFor($reader, $rows)
{
$at = 0;
$length = $rows[0][0];
$held = $reader->readBits($length);
while ($held !== $rows[$at][1]) {
$at++;
if (!isset($rows[$at])) {
throw new \RuntimeException("a code in this frame is "
. "not in the table it was written with");
}
$wider = $rows[$at][0] - $length;
$length = $rows[$at][0];
$held = ($held << $wider) | $reader->readBits($wider);
}
return $rows[$at];
}
/**
* readGroup reads one group of values written with a given table:
* either a pair or four at a time, with the signs following where
* the table does not carry them, and the rest of any value too
* large for the table to hold.
*
* @param BitReader $reader The frame's bits.
* @param array $table The table, as tableFor gave it.
* @param bool $escapes Whether values too large for the table are
* written with the rest following.
* @return array The values of this group, with their signs.
*/
public static function readGroup($reader, $table, $escapes)
{
$row = self::rowFor($reader, $table["rows"]);
$values = array_slice($row, 2, $table["holds"]);
if ($table["signed"]) {
foreach ($values as $at => $value) {
if ($value !== 0 && $reader->readBit() === 1) {
$values[$at] = -$value;
}
}
}
if ($escapes) {
foreach ($values as $at => $value) {
if (abs($value) === self::ESCAPE_AT) {
$rest = self::readRest($reader);
$values[$at] = ($value < 0) ? -$rest : $rest;
}
}
}
return $values;
}
/**
* readBands reads the rounded numbers of every band and turns each
* back into the strength of a tone. A band that carries nothing
* gives silence, and a band that carries something is read a pair
* of numbers at a time.
*
* @param BitReader $reader The frame's bits.
* @param int $bands How many bands the frame covers.
* @param array $tables Which table each band uses.
* @param array $steps The step each band is scaled by.
* @return array One strength for each tone the frame covers.
*/
public function readBands($reader, $bands, $tables, $steps)
{
$tones = array_fill(0, AacEncoder::HOP, 0.0);
for ($band = 0; $band < $bands; $band++) {
$table = $tables[$band] ?? AacFrame::EMPTY_TABLE;
if ($table === AacFrame::EMPTY_TABLE) {
continue;
}
if ($table === self::NOISE_TABLE) {
self::fillWithNoise($tones, $band, $steps[$band]);
continue;
}
if (in_array($table, self::INTENSITY_TABLES, true)) {
throw new \RuntimeException("this band takes its sound "
. "from the other channel of a pair, which is not "
. "decoded yet");
}
$held = self::tableFor($table);
$escapes = ($table === AacTables::SOUND_TABLE);
$from = AacBands::LONG_EDGES[$band];
$past = AacBands::LONG_EDGES[$band + 1];
$scale = pow(2.0, AacQuantizer::SQUASHED_STEP *
($steps[$band] - AacQuantizer::MIDDLE_STEP) /
AacQuantizer::SQUASH);
for ($at = $from; $at < $past; $at += $held["holds"]) {
$values = self::readGroup($reader, $held, $escapes);
foreach ($values as $which => $value) {
if ($at + $which >= $past) {
break;
}
$tones[$at + $which] = self::sizeOfNumber($value) *
$scale;
}
}
}
return $tones;
}
/**
* fillWithNoise fills one band with noise at the loudness the frame
* asked for. A band whose sound is close to noise is not written
* out: the stream says how loud it should be, and any noise of that
* loudness sounds the same to a listener.
*
* @param array $tones The strengths being built, changed in place.
* @param int $band Which band to fill.
* @param int $loudness How loud that noise should be.
*/
public static function fillWithNoise(&$tones, $band, $loudness)
{
$from = AacBands::LONG_EDGES[$band];
$past = AacBands::LONG_EDGES[$band + 1];
$made = [];
$total = 0.0;
for ($at = $from; $at < $past; $at++) {
$made[$at] = mt_rand(-1000, 1000) / 1000.0;
$total += $made[$at] * $made[$at];
}
$wanted = pow(2.0, ($loudness - AacQuantizer::MIDDLE_STEP) / 4.0);
$fit = ($total > 0.0) ?
$wanted * sqrt(($past - $from) / $total) : 0.0;
for ($at = $from; $at < $past; $at++) {
$tones[$at] = $made[$at] * $fit;
}
}
/**
* readPair reads one pair of rounded numbers. The pair is written
* as a single code saying how large each of the two is, followed by
* a sign for each that is not zero, and by the rest of any number
* too large for the code to carry.
*
* @param BitReader $reader The frame's bits.
* @return array The two numbers, with their signs.
*/
public static function readPair($reader)
{
$where = self::readSoundCode($reader);
$first = intdiv($where, AacTables::PAIR_RANGE);
$second = $where % AacTables::PAIR_RANGE;
if ($first !== 0 && $reader->readBit() === 1) {
$first = -$first;
}
if ($second !== 0 && $reader->readBit() === 1) {
$second = -$second;
}
if (abs($first) >= self::ESCAPE_AT) {
$first = self::readRest($reader) * ($first < 0 ? -1 : 1);
}
if (abs($second) >= self::ESCAPE_AT) {
$second = self::readRest($reader) * ($second < 0 ? -1 : 1);
}
return [$first, $second];
}
/**
* readSoundCode reads one of the codes that stand for a pair of
* rounded numbers. The codes are of different lengths, so bits are
* taken one at a time until they spell one out.
*
* @param BitReader $reader The frame's bits.
* @return int Which of the codes was read, counting from zero.
*/
public static function readSoundCode($reader)
{
$held = 0;
$length = 0;
while ($length < 20) {
$held = ($held << 1) | $reader->readBit();
$length++;
foreach (AacTables::SOUND_CODE_BITS as $where => $bits) {
if ($bits === $length
&& AacTables::SOUND_CODES[$where] === $held) {
return $where;
}
}
}
throw new \RuntimeException("a pair in this frame is written "
. "with a code that is not in the table");
}
/**
* readRest reads a number too large for its code to carry. Such a
* number is written as a run of ones saying how many bits follow,
* then a zero, then the bits themselves.
*
* @param BitReader $reader The frame's bits.
* @return int The number those bits spell out.
*/
public static function readRest($reader)
{
$length = 4;
while ($reader->readBit() === 1) {
$length++;
if ($length > 24) {
throw new \RuntimeException("a number in this frame is "
. "written as longer than any number can be");
}
}
return $reader->readBits($length) + (1 << $length);
}
/**
* sizeOfNumber turns a rounded number back into the strength it
* stands for. The encoder took each strength to the power of three
* quarters before rounding it, so the decoder raises the rounded
* number to four thirds.
*
* @param int $number The rounded number, with its sign.
* @return float The strength it stands for.
*/
public static function sizeOfNumber($number)
{
if ($number === 0) {
return 0.0;
}
$size = pow(abs($number), AacQuantizer::UNSQUASH);
return ($number < 0) ? -$size : $size;
}
}