/ src / library / av_processing / SpeechPitchTables.php
<?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
 *
 * The tables in this file are fixed by RFC 6716, the specification for
 * the Opus audio codec. They are taken from the reference
 * implementation published by Xiph.Org, Skype Limited and others, which
 * is offered under a three clause licence allowing use in other
 * software so long as its notice is carried along:
 *
 *   Copyright 2001-2023 Xiph.Org, Skype Limited, Octasic, Jean-Marc
 *   Valin, Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark
 *   Borgerding, Erik de Castro Lopo, Mozilla, Amazon.
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the copyright notice,
 *   this list of conditions and the disclaimer are retained. This
 *   software is provided by the copyright holders as is, and any
 *   warranty is disclaimed.
 *
 * @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\library\av_processing;

/**
 * SpeechPitchTables holds the tables that say at what pitch a stretch of
 * speech was spoken and how strongly that pitch repeats.
 *
 * Speech made with the voice repeats itself: the vocal folds open and
 * close at some rate, and one opening sounds much like the one before
 * it. A stretch therefore carries how far back its own recent past to
 * look, called the lag, and a short filter saying how much of what was
 * found there to add back in. This file holds the likelihoods for
 * reading the lag and the filter, and the books of filters themselves.
 *
 * @author Chris Pollett
 */
class SpeechPitchTables
{
    /**
     * WHOLE_BITS is how many bits the likelihoods in this file add up
     * to, as in the rest of the speech decoding.
     */
    const WHOLE_BITS = 8;
    /**
     * QUARTERS_IN_STRETCH is how many quarters a stretch is cut into.
     * Each quarter carries its own lag, worked out from the stretch's
     * own lag and a shape shared across the four.
     */
    const QUARTERS_IN_STRETCH = 4;
    /**
     * LEAST_LAG_WIDE and MOST_LAG_WIDE are how far back a stretch of
     * wide sound may look, in samples at sixteen thousand a second.
     * Two milliseconds is the shortest a voice repeats in and eighteen
     * the longest.
     */
    const LEAST_LAG_WIDE = 32;
    /**
     * MOST_LAG_WIDE is the furthest back a stretch of wide sound may
     * look, in samples at sixteen thousand a second.
     */
    const MOST_LAG_WIDE = 288;
    /**
     * LAG_STEPS_WIDE is how many places within one coarse step of lag a
     * stretch of wide sound may name, which fixes how finely the pitch
     * can be written.
     */
    const LAG_STEPS_WIDE = 8;
    /**
     * REPEAT_LEAN_SCALES is how much of the sound before a stretch is
     * leaned on when the stretch stands on its own, one for each of the
     * three answers it may write. Leaning less on a lost stretch keeps a
     * lost packet from spoiling the one after it.
     */
    const REPEAT_LEAN_SCALES = [15565, 12288, 8192];
    /**
     * SOUND_KEPT_MILLISECONDS is how much of the sound before a stretch
     * is kept, so a stretch reaching back for its pitch has something to
     * reach into.
     */
    const SOUND_KEPT_MILLISECONDS = 20;
    /**
     * PITCH_FINE_LAG is how likely each place within one coarse step of
     * lag was. The eight places are equally likely, so the table counts
     * down evenly.
     */
    const PITCH_FINE_LAG = [224, 192, 160, 128, 96, 64, 32, 0];
    /**
     * PITCH_FIRST_LAG is how likely each coarse step of lag was where a
     * stretch writes its pitch outright rather than as a change from
     * the stretch before it.
     */
    const PITCH_FIRST_LAG = [
        253, 250, 244, 233, 212, 182, 150, 131, 120, 110, 98, 85, 72,
        60, 49, 40, 32, 25, 19, 15, 13, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
    /**
     * PITCH_LAG_CHANGE is how likely each change of lag was where a
     * stretch writes its pitch as a move from the stretch before. Pitch
     * moves slowly in speech, so a small change is far likelier than a
     * large one.
     */
    const PITCH_LAG_CHANGE = [
        210, 208, 206, 203, 199, 193, 183, 168, 142, 104, 74, 52, 37,
        27, 20, 14, 10, 6, 4, 2, 0];
    /**
     * PITCH_SHAPE_WIDE is how likely each shape of pitch across the
     * four quarters was, for wide sound at twenty milliseconds a
     * stretch. A shape says how the lag drifts within the stretch.
     */
    const PITCH_SHAPE_WIDE = [
        223, 201, 183, 167, 152, 138, 124, 111, 98, 88, 79, 70, 62, 56,
        50, 44, 39, 35, 31, 27, 24, 21, 18, 16, 14, 12, 10, 8, 6, 4, 3,
        2, 1, 0];
    /**
     * PITCH_SHAPE_WIDE_STEPS is the shapes themselves, four numbers to
     * a shape, each the step to add to the stretch's own lag for that
     * quarter.
     */
    const PITCH_SHAPE_WIDE_STEPS = [
        0, 0, 1, -1, 0, 1, -1, 0, -1, 1, -2, 2, -2, -2, 2, -3, 2, 3, -3,
        -4, 3, -4, 4, 4, -5, 5, -6, -5, 6, -7, 6, 5, 8, -9, 0, 0, 1, 0,
        0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 1, -1, 0, 1, -1, -1, 1, -1, 2, 1,
        -1, 2, -2, -2, 2, -2, 2, 2, 3, -3, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
        1, 0, 0, 1, -1, 1, 0, 0, 2, 1, -1, 2, -1, -1, 2, -1, 2, 2, -1,
        3, -2, -2, -2, 3, 0, 1, 0, 0, 1, 0, 1, -1, 2, -1, 2, -1, 2, 3,
        -2, 3, -2, -2, 4, 4, -3, 5, -3, -4, 6, -4, 6, 5, -5, 8, -6, -5,
        -7, 9];
    /**
     * REPEAT_STRENGTH_CHANCES_0 is how likely each filter of the
     * smallest book was. A stretch whose pitch repeats weakly uses this
     * book, which holds eight filters.
     */
    const REPEAT_STRENGTH_CHANCES_0 = [
        71, 56, 43, 30, 21, 12, 6, 0];
    /**
     * REPEAT_STRENGTH_CHANCES_1 is how likely each filter of the middle
     * book was, which holds sixteen filters.
     */
    const REPEAT_STRENGTH_CHANCES_1 = [
        199, 165, 144, 124, 109, 96, 84, 71, 61, 51, 42, 32, 23, 15, 8,
        0];
    /**
     * REPEAT_STRENGTH_CHANCES_2 is how likely each filter of the
     * largest book was, which holds thirty-two filters.
     */
    const REPEAT_STRENGTH_CHANCES_2 = [
        241, 225, 211, 199, 187, 175, 164, 153, 142, 132, 123, 114, 105,
        96, 88, 80, 72, 64, 57, 50, 44, 38, 33, 29, 24, 20, 16, 12, 9,
        5, 2, 0];
    /**
     * REPEAT_BOOK_CHANCES is how likely each of the three books of
     * filters was. A stretch names one book and then one filter within
     * it, for all four quarters together.
     */
    const REPEAT_BOOK_CHANCES = [
        179, 99, 0];
    /**
     * REPEAT_FILTERS_0 is the smallest book of filters, five taps to a
     * filter. A tap says how much of the sound one sample either side
     * of the lag to add back in.
     */
    const REPEAT_FILTERS_0 = [
        4, 6, 24, 7, 5, 0, 0, 2, 0, 0, 12, 28, 41, 13, -4, -9, 15, 42,
        25, 14, 1, -2, 62, 41, -9, -10, 37, 65, -4, 3, -6, 4, 66, 7, -8,
        16, 14, 38, -3, 33];
    /**
     * REPEAT_FILTERS_1 is the middle book of filters, five taps to a
     * filter.
     */
    const REPEAT_FILTERS_1 = [
        13, 22, 39, 23, 12, -1, 36, 64, 27, -6, -7, 10, 55, 43, 17, 1,
        1, 8, 1, 1, 6, -11, 74, 53, -9, -12, 55, 76, -12, 8, -3, 3, 93,
        27, -4, 26, 39, 59, 3, -8, 2, 0, 77, 11, 9, -8, 22, 44, -6, 7,
        40, 9, 26, 3, 9, -7, 20, 101, -7, 4, 3, -8, 42, 26, 0, -15, 33,
        68, 2, 23, -2, 55, 46, -2, 15, 3, -1, 21, 16, 41];
    /**
     * REPEAT_FILTERS_2 is the largest book of filters, five taps to a
     * filter.
     */
    const REPEAT_FILTERS_2 = [
        -6, 27, 61, 39, 5, -11, 42, 88, 4, 1, -2, 60, 65, 6, -4, -1, -5,
        73, 56, 1, -9, 19, 94, 29, -9, 0, 12, 99, 6, 4, 8, -19, 102, 46,
        -13, 3, 2, 13, 3, 2, 9, -21, 84, 72, -18, -11, 46, 104, -22, 8,
        18, 38, 48, 23, 0, -16, 70, 83, -21, 11, 5, -11, 117, 22, -8,
        -6, 23, 117, -12, 3, 3, -8, 95, 28, 4, -10, 15, 77, 60, -15, -1,
        4, 124, 2, -4, 3, 38, 84, 24, -25, 2, 13, 42, 13, 31, 21, -4,
        56, 46, -1, -1, 35, 79, -13, 19, -7, 65, 88, -9, -14, 20, 4, 81,
        49, -29, 20, 0, 75, 3, -17, 5, -9, 44, 92, -8, 1, -3, 22, 69,
        31, -6, 95, 41, -12, 5, 39, 67, 16, -4, 1, 0, -6, 120, 55, -36,
        -13, 44, 122, 4, -24, 81, 5, 11, 3, 7, 2, 0, 9, 10, 88];
}
X