snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Code scanning - action / CodeQL-Build (push) Failing after 2s
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
# Change Log
## 0.31.26
### Patch Changes
- [#1481](https://github.com/RocketChat/fuselage/pull/1481) [`8084488`](https://github.com/RocketChat/fuselage/commit/80844881787fb8c5679ac5bcd18e43ddc23a8673) Thanks [@tassoevan](https://github.com/tassoevan)! - Suppressed assertions at the LAME encoding due to unknown `NaN` values
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.31.0](https://github.com/RocketChat/fuselage/compare/v0.30.1...v0.31.0) (2021-12-28)
### Features
- New hooks for element size tracking ([#413](https://github.com/RocketChat/fuselage/issues/413)) ([8ca682c](https://github.com/RocketChat/fuselage/commit/8ca682c636d2e4813f7d346cb881513382be63cf))
# [0.30.0](https://github.com/RocketChat/fuselage/compare/v0.29.0...v0.30.0) (2021-10-06)
### Bug Fixes
- **jest:** Adjust jest and ts-jest dependencies ([#547](https://github.com/RocketChat/fuselage/issues/547)) ([91a4fa1](https://github.com/RocketChat/fuselage/commit/91a4fa1365394001afe1bd46480bda3bafed5505))
# [0.29.0](https://github.com/RocketChat/fuselage/compare/v0.28.0...v0.29.0) (2021-08-31)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.28.0](https://github.com/RocketChat/fuselage/compare/v0.27.0...v0.28.0) (2021-07-30)
### Features
- **onboarding-ui:** Administrator information form and Organization information form ([#489](https://github.com/RocketChat/fuselage/issues/489)) ([b289f68](https://github.com/RocketChat/fuselage/commit/b289f68676954b91c792d8d97680314178bf2c60))
- styled API; monorepo grooming ([#482](https://github.com/RocketChat/fuselage/issues/482)) ([1b6b70c](https://github.com/RocketChat/fuselage/commit/1b6b70cf67ec16927b1566adc2350295a8927223))
# [0.27.0](https://github.com/RocketChat/fuselage/compare/v0.26.0...v0.27.0) (2021-06-28)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.26.0](https://github.com/RocketChat/fuselage/compare/v0.25.0...v0.26.0) (2021-05-28)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.25.0](https://github.com/RocketChat/fuselage/compare/v0.24.0...v0.25.0) (2021-05-19)
### Features
- [@rocket](https://github.com/rocket).chat/message-parser ([#443](https://github.com/RocketChat/fuselage/issues/443)) ([4722cdf](https://github.com/RocketChat/fuselage/commit/4722cdff46f5987f335d989be59649c7652bb12a))
# [0.24.0](https://github.com/RocketChat/fuselage/compare/v0.23.0...v0.24.0) (2021-04-28)
### Features
- [@rocket](https://github.com/rocket).chat/string-helpers ([#431](https://github.com/RocketChat/fuselage/issues/431)) ([2509d6a](https://github.com/RocketChat/fuselage/commit/2509d6acdbe5ec8b216e8d4430373797c5f5dfe2))
# [0.23.0](https://github.com/RocketChat/fuselage/compare/v0.22.0...v0.23.0) (2021-04-01)
### Bug Fixes
- **npm:** Wrong paths in "files" field of package.json ([6d3c811](https://github.com/RocketChat/fuselage/commit/6d3c811f6fd747de7f47aff145902d88476272ee))
# [0.22.0](https://github.com/RocketChat/fuselage/compare/v0.21.0...v0.22.0) (2021-02-26)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.21.0](https://github.com/RocketChat/fuselage/compare/v0.20.3...v0.21.0) (2021-01-31)
### Features
- Built modules for design tokens ([#356](https://github.com/RocketChat/fuselage/issues/356)) ([f9c3449](https://github.com/RocketChat/fuselage/commit/f9c344953b8161a4385cab3a3dcc8b6a7210446f))
## [0.20.1](https://github.com/RocketChat/fuselage/compare/v0.20.0...v0.20.1) (2020-12-22)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.20.0](https://github.com/RocketChat/fuselage/compare/v0.19.0...v0.20.0) (2020-12-21)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.19.0](https://github.com/RocketChat/fuselage/compare/v0.18.0...v0.19.0) (2020-11-28)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.18.0](https://github.com/RocketChat/fuselage/compare/v0.17.3...v0.18.0) (2020-11-16)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
## [0.17.2](https://github.com/RocketChat/fuselage/compare/v0.17.1...v0.17.2) (2020-10-28)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
## [0.17.1](https://github.com/RocketChat/fuselage/compare/v0.17.0...v0.17.1) (2020-10-26)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.17.0](https://github.com/RocketChat/fuselage/compare/v0.16.0...v0.17.0) (2020-10-25)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.16.0](https://github.com/RocketChat/fuselage/compare/v0.15.1...v0.16.0) (2020-09-30)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
## [0.15.1](https://github.com/RocketChat/fuselage/compare/v0.15.0...v0.15.1) (2020-09-22)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.15.0](https://github.com/RocketChat/fuselage/compare/v0.14.1...v0.15.0) (2020-09-17)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
## [0.14.1](https://github.com/RocketChat/fuselage/compare/v0.14.0...v0.14.1) (2020-08-22)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.14.0](https://github.com/RocketChat/fuselage/compare/v0.13.2...v0.14.0) (2020-08-18)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
## [0.13.1](https://github.com/RocketChat/fuselage/compare/v0.13.0...v0.13.1) (2020-07-17)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.13.0](https://github.com/RocketChat/fuselage/compare/v0.12.0...v0.13.0) (2020-07-14)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.12.0](https://github.com/RocketChat/fuselage/compare/v0.11.0...v0.12.0) (2020-07-14)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.11.0](https://github.com/RocketChat/fuselage/compare/v0.10.0...v0.11.0) (2020-07-11)
**Note:** Version bump only for package @rocket.chat/mp3-encoder
# [0.10.0](https://github.com/RocketChat/fuselage/compare/v0.9.0...v0.10.0) (2020-06-20)
### Bug Fixes
- lamejs problematic usage ([#239](https://github.com/RocketChat/fuselage/issues/239)) ([eb12eb4](https://github.com/RocketChat/fuselage/commit/eb12eb4))
### Features
- MP3 encoder ([#235](https://github.com/RocketChat/fuselage/issues/235)) ([a85eb1a](https://github.com/RocketChat/fuselage/commit/a85eb1a))

View File

@@ -0,0 +1,7 @@
import client from '@rocket.chat/jest-presets/client';
import type { Config } from 'jest';
export default {
preset: client.preset,
setupFilesAfterEnv: [...client.setupFilesAfterEnv],
} satisfies Config;

View File

@@ -0,0 +1,53 @@
{
"name": "@rocket.chat/mp3-encoder",
"version": "0.31.26",
"description": "A LAME encoder to be used in web workers",
"keywords": [
"rocket.chat",
"mp3",
"encoder"
],
"bugs": {
"url": "https://github.com/RocketChat/Rocket.Chat/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/RocketChat/Rocket.Chat.git",
"directory": "packages/mp3-encoder"
},
"license": "MIT",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"/dist"
],
"scripts": {
"build": "rollup -c",
"docs": "typedoc",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"start": "rollup -c -w",
"test": "jest",
"testunit": "jest",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@rocket.chat/jest-presets": "workspace:~",
"@rocket.chat/tsconfig": "workspace:~",
"@rollup/plugin-commonjs": "~29.0.3",
"@rollup/plugin-node-resolve": "~16.0.3",
"@rollup/plugin-typescript": "~12.3.0",
"eslint": "~9.39.5",
"jest": "~30.2.0",
"jest-environment-jsdom": "~30.2.0",
"rollup": "~4.62.5",
"ts-jest": "~29.4.12",
"typedoc": "~0.28.20",
"typescript": "~5.9.3"
}
}

View File

@@ -0,0 +1,33 @@
import { basename, dirname } from 'node:path';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';
import pkg from './package.json' with { type: 'json' };
export default defineConfig({
input: 'src/index.ts',
output: [
{
dir: dirname(pkg.main),
entryFileNames: basename(pkg.main),
format: 'cjs',
sourcemap: true,
strict: false,
},
{
file: pkg.module,
format: 'esm',
sourcemap: true,
},
],
plugins: [
typescript({
tsconfig: 'tsconfig.build.json',
}),
resolve(),
commonjs(),
],
});

View File

@@ -0,0 +1,42 @@
import './index';
declare const self: any;
describe('web worker messages', () => {
beforeEach(() => {
self.onmessage = jest.fn(self.onmessage);
self.postMessage = jest.fn();
});
it('handles init message', () => {
const event = new MessageEvent('message', {
data: {
command: 'init',
},
});
self.dispatchEvent(event);
expect(self.onmessage).toHaveBeenCalledTimes(1);
});
it('handles encode message', () => {
const event = new MessageEvent('message', {
data: {
command: 'encode',
buffer: new ArrayBuffer(0),
},
});
self.dispatchEvent(event);
expect(self.onmessage).toHaveBeenCalledTimes(1);
});
it('handles finish message', () => {
const event = new MessageEvent('message', {
data: {
command: 'finish',
},
});
self.dispatchEvent(event);
expect(self.onmessage).toHaveBeenCalledTimes(1);
expect(self.postMessage).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,66 @@
import { Mp3Encoder } from './lame';
type Config = {
numChannels?: number;
sampleRate?: number;
bitRate?: number;
};
let encoder: Mp3Encoder;
const maxSamples = 1152;
let samplesMono: Int16Array;
let dataBuffer: Int8Array[];
function convertBuffer(arrayBuffer: Float32Array): Int16Array {
const input = new Float32Array(arrayBuffer);
const output = new Int16Array(arrayBuffer.length);
for (let i = 0; i < input.length; i++) {
const s = Math.max(-1, Math.min(1, input[i]));
output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
}
return output;
}
function init(config: Config): void {
config = config || {};
encoder = new Mp3Encoder(config.numChannels || 1, config.sampleRate || 44100, config.bitRate || 32);
dataBuffer = [];
}
function encode(arrayBuffer: Float32Array): void {
samplesMono = convertBuffer(arrayBuffer);
let remaining = samplesMono.length;
for (let i = 0; remaining >= 0; i += maxSamples) {
const left = samplesMono.subarray(i, i + maxSamples);
const buffer = encoder.encodeBuffer(left);
dataBuffer.push(new Int8Array(buffer));
remaining -= maxSamples;
}
}
function finish(): void {
const buffer = encoder.flush();
dataBuffer.push(new Int8Array(buffer));
self.postMessage({ command: 'end', buffer: dataBuffer });
dataBuffer = [];
}
function onMessage(event: MessageEvent): void {
switch (event.data.command) {
case 'init':
init(event.data.config);
break;
case 'encode':
encode(event.data.buffer);
break;
case 'finish':
finish();
break;
}
}
self.onmessage = onMessage;

View File

@@ -0,0 +1,431 @@
import type { LameGlobalFlags } from './LameGlobalFlags';
import { VbrMode } from './VbrMode';
import type { Bitrate } from './bitrates';
import { equals } from './math';
interface ABRPreset {
readonly kbps: number;
readonly quant_comp: number;
readonly quant_comp_s: number;
readonly safejoint: number;
readonly nsmsfix: number;
readonly st_lrm: number;
readonly st_s: number;
readonly nsbass: number;
readonly scale: number;
readonly masking_adj: number;
readonly ath_lower: number;
readonly ath_curve: number;
readonly interch: number;
readonly sfscale: 0 | 1;
}
interface BandPass {
readonly bitrate: number;
readonly lowpass: number;
}
type FullBitrateIndex = number & { __brand: 'FullBitrateIndex' };
type PresetMap = Record<FullBitrateIndex, ABRPreset>;
export class ABRPresets {
private readonly fullBitrates = [
8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
] as const satisfies readonly Bitrate[];
private findNearestFullBitrateIndex(bitrate: number): FullBitrateIndex {
let lowerRange = this.fullBitrates.length - 1;
let upperRange = this.fullBitrates.length - 1;
for (let maybeLowerRange = 0; maybeLowerRange < this.fullBitrates.length - 1; maybeLowerRange++) {
const maybeUpperRange = maybeLowerRange + 1;
if (this.fullBitrates[maybeUpperRange] > bitrate) {
lowerRange = maybeLowerRange;
upperRange = maybeUpperRange;
break;
}
}
return bitrate - this.fullBitrates[lowerRange] < this.fullBitrates[upperRange] - bitrate
? (lowerRange as FullBitrateIndex)
: (upperRange as FullBitrateIndex);
}
private readonly presetMap = [
{
kbps: 8,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -30,
ath_curve: 11,
interch: 0.0012,
sfscale: 1,
},
{
kbps: 16,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -25,
ath_curve: 11,
interch: 0.001,
sfscale: 1,
},
{
kbps: 24,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -20,
ath_curve: 11,
interch: 0.001,
sfscale: 1,
},
{
kbps: 32,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -15,
ath_curve: 11,
interch: 0.001,
sfscale: 1,
},
{
kbps: 40,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -10,
ath_curve: 11,
interch: 0.0009,
sfscale: 1,
},
{
kbps: 48,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -10,
ath_curve: 11,
interch: 0.0009,
sfscale: 1,
},
{
kbps: 56,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -6,
ath_curve: 11,
interch: 0.0008,
sfscale: 1,
},
{
kbps: 64,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: -2,
ath_curve: 11,
interch: 0.0008,
sfscale: 1,
},
{
kbps: 80,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 0,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: 0,
ath_curve: 8,
interch: 0.0007,
sfscale: 1,
},
{
kbps: 96,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 2.5,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: 1,
ath_curve: 5.5,
interch: 0.0006,
sfscale: 1,
},
{
kbps: 112,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 2.25,
st_lrm: 6.6,
st_s: 145,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: 2,
ath_curve: 4.5,
interch: 0.0005,
sfscale: 1,
},
{
kbps: 128,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 0,
nsmsfix: 1.95,
st_lrm: 6.4,
st_s: 140,
nsbass: 0,
scale: 0.95,
masking_adj: 0,
ath_lower: 3,
ath_curve: 4,
interch: 0.0002,
sfscale: 1,
},
{
kbps: 160,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 1,
nsmsfix: 1.79,
st_lrm: 6,
st_s: 135,
nsbass: 0,
scale: 0.95,
masking_adj: -2,
ath_lower: 5,
ath_curve: 3.5,
interch: 0,
sfscale: 1,
},
{
kbps: 192,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 1,
nsmsfix: 1.49,
st_lrm: 5.6,
st_s: 125,
nsbass: 0,
scale: 0.97,
masking_adj: -4,
ath_lower: 7,
ath_curve: 3,
interch: 0,
sfscale: 0,
},
{
kbps: 224,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 1,
nsmsfix: 1.25,
st_lrm: 5.2,
st_s: 125,
nsbass: 0,
scale: 0.98,
masking_adj: -6,
ath_lower: 9,
ath_curve: 2,
interch: 0,
sfscale: 0,
},
{
kbps: 256,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 1,
nsmsfix: 0.97,
st_lrm: 5.2,
st_s: 125,
nsbass: 0,
scale: 1,
masking_adj: -8,
ath_lower: 10,
ath_curve: 1,
interch: 0,
sfscale: 0,
},
{
kbps: 320,
quant_comp: 9,
quant_comp_s: 9,
safejoint: 1,
nsmsfix: 0.9,
st_lrm: 5.2,
st_s: 125,
nsbass: 0,
scale: 1,
masking_adj: -10,
ath_lower: 12,
ath_curve: 0,
interch: 0,
sfscale: 0,
},
] as const satisfies PresetMap;
public apply(gfp: LameGlobalFlags, bitrate: number) {
const preset = this.presetMap[this.findNearestFullBitrateIndex(bitrate)];
gfp.VBR = VbrMode.vbr_abr;
gfp.VBR_mean_bitrate_kbps = bitrate;
gfp.VBR_mean_bitrate_kbps = Math.min(gfp.VBR_mean_bitrate_kbps, 320);
gfp.VBR_mean_bitrate_kbps = Math.max(gfp.VBR_mean_bitrate_kbps, 8);
gfp.brate = gfp.VBR_mean_bitrate_kbps;
if (preset.safejoint > 0) {
gfp.exp_nspsytune |= 2;
}
if (preset.sfscale > 0) {
gfp.internal_flags.noise_shaping = 2;
}
if (Math.abs(preset.nsbass) > 0) {
let k = Math.trunc(preset.nsbass * 4);
if (k < 0) {
k += 64;
}
gfp.exp_nspsytune |= k << 2;
}
if (equals(gfp.quant_comp, -1)) {
gfp.quant_comp = preset.quant_comp;
}
if (equals(gfp.quant_comp_short, -1)) {
gfp.quant_comp_short = preset.quant_comp_s;
}
if (equals(gfp.msfix, -1)) {
gfp.msfix = preset.nsmsfix;
}
if (equals(gfp.internal_flags.nsPsy.attackthre, -1)) {
gfp.internal_flags.nsPsy.attackthre = preset.st_lrm;
}
if (equals(gfp.internal_flags.nsPsy.attackthre_s, -1)) {
gfp.internal_flags.nsPsy.attackthre_s = preset.st_s;
}
if (equals(gfp.scale, -1)) {
gfp.scale = preset.scale;
}
if (equals(gfp.maskingadjust, 0)) {
gfp.maskingadjust = preset.masking_adj;
}
if (preset.masking_adj > 0) {
if (equals(gfp.maskingadjust_short, 0)) {
gfp.maskingadjust_short = preset.masking_adj * 0.9;
}
} else if (equals(gfp.maskingadjust_short, 0)) {
gfp.maskingadjust_short = preset.masking_adj * 1.1;
}
if (equals(-gfp.ATHlower * 10, 0)) {
gfp.ATHlower = -preset.ath_lower / 10;
}
if (equals(gfp.ATHcurve, -1)) {
gfp.ATHcurve = preset.ath_curve;
}
if (equals(gfp.interChRatio, -1)) {
gfp.interChRatio = preset.interch;
}
return bitrate;
}
private readonly optimumBandwidths = [
{ bitrate: 8, lowpass: 2000 },
{ bitrate: 16, lowpass: 3700 },
{ bitrate: 24, lowpass: 3900 },
{ bitrate: 32, lowpass: 5500 },
{ bitrate: 40, lowpass: 7000 },
{ bitrate: 48, lowpass: 7500 },
{ bitrate: 56, lowpass: 10000 },
{ bitrate: 64, lowpass: 11000 },
{ bitrate: 80, lowpass: 13500 },
{ bitrate: 96, lowpass: 15100 },
{ bitrate: 112, lowpass: 15600 },
{ bitrate: 128, lowpass: 17000 },
{ bitrate: 160, lowpass: 17500 },
{ bitrate: 192, lowpass: 18600 },
{ bitrate: 224, lowpass: 19400 },
{ bitrate: 256, lowpass: 19700 },
{ bitrate: 320, lowpass: 20500 },
] as const satisfies Record<FullBitrateIndex, BandPass>;
public getOptimumBandwidth(bitrate: number) {
const table_index = this.findNearestFullBitrateIndex(bitrate);
return this.optimumBandwidths[table_index].lowpass;
}
}

View File

@@ -0,0 +1,29 @@
import { BLKSIZE, CBANDS, PSFB12, PSFB21, SBMAX_l, SBMAX_s } from './constants';
export class ATH {
useAdjust = 0;
aaSensitivityP = 0;
adjust = 0;
adjustLimit = 0;
decay = 0;
floor = 0;
readonly l = new Float32Array(SBMAX_l);
readonly s = new Float32Array(SBMAX_s);
readonly psfb21 = new Float32Array(PSFB21);
readonly psfb12 = new Float32Array(PSFB12);
readonly cb_l = new Float32Array(CBANDS);
readonly cb_s = new Float32Array(CBANDS);
readonly eql_w = new Float32Array(BLKSIZE / 2);
}

View File

@@ -0,0 +1,693 @@
import { GainAnalysis } from './GainAnalysis';
import type { GrInfo } from './GrInfo';
import type { LameGlobalFlags } from './LameGlobalFlags';
import type { LameInternalFlags } from './LameInternalFlags';
import * as tables from './Tables';
import { Takehiro } from './Takehiro';
import { TotalBytes } from './TotalBytes';
import { VBRTag } from './VBRTag';
import { copyArray, fillArray } from './arrays';
import { assert } from './assert';
import { getBitrate } from './bitrates';
import { LAME_MAXMP3BUFFER, MAX_HEADER_BUF, NORM_TYPE, SHORT_TYPE } from './constants';
import { getLameShortVersion } from './getLameShortVersion';
import { isCloseToEachOther } from './math';
export class BitStream {
private static readonly MAX_LENGTH = 32;
readonly ga = new GainAnalysis();
private readonly vbr = new VBRTag();
private readonly buf = new Uint8Array(LAME_MAXMP3BUFFER);
private totbit = 0;
private bufByteIdx = -1;
private bufBitIdx = 0;
resetPointers(gfc: LameInternalFlags) {
gfc.w_ptr = 0;
gfc.h_ptr = 0;
gfc.header[gfc.h_ptr].write_timing = 0;
}
getframebits(gfp: LameGlobalFlags) {
const gfc = gfp.internal_flags;
let bit_rate;
if (gfc.bitrate_index !== 0) {
bit_rate = getBitrate(gfp.version, gfc.bitrate_index);
} else {
bit_rate = gfp.brate;
}
assert(bit_rate >= 8 && bit_rate <= 640);
const bytes = Math.trunc(((gfp.version + 1) * 72000 * bit_rate) / gfp.out_samplerate + gfc.padding);
return 8 * bytes;
}
private putheader_bits(gfc: LameInternalFlags) {
copyArray(gfc.header[gfc.w_ptr].buf, 0, this.buf, this.bufByteIdx, gfc.sideinfo_len);
this.bufByteIdx += gfc.sideinfo_len;
this.totbit += gfc.sideinfo_len * 8;
gfc.w_ptr = (gfc.w_ptr + 1) & (MAX_HEADER_BUF - 1);
}
private putbits2(gfc: LameInternalFlags, val: number, j: number) {
assert(j < BitStream.MAX_LENGTH - 2);
while (j > 0) {
if (this.bufBitIdx === 0) {
this.bufBitIdx = 8;
this.bufByteIdx++;
assert(this.bufByteIdx < LAME_MAXMP3BUFFER);
assert(gfc.header[gfc.w_ptr].write_timing >= this.totbit);
if (gfc.header[gfc.w_ptr].write_timing === this.totbit) {
this.putheader_bits(gfc);
}
this.buf[this.bufByteIdx] = 0;
}
const k = Math.min(j, this.bufBitIdx);
j -= k;
this.bufBitIdx -= k;
assert(j < BitStream.MAX_LENGTH);
assert(this.bufBitIdx < BitStream.MAX_LENGTH);
this.buf[this.bufByteIdx] |= (val >> j) << this.bufBitIdx;
this.totbit += k;
}
}
private drain_into_ancillary(gfp: LameGlobalFlags, remainingBits: number) {
const gfc = gfp.internal_flags;
let i;
assert(remainingBits >= 0);
if (remainingBits >= 8) {
this.putbits2(gfc, 0x4c, 8);
remainingBits -= 8;
}
if (remainingBits >= 8) {
this.putbits2(gfc, 0x41, 8);
remainingBits -= 8;
}
if (remainingBits >= 8) {
this.putbits2(gfc, 0x4d, 8);
remainingBits -= 8;
}
if (remainingBits >= 8) {
this.putbits2(gfc, 0x45, 8);
remainingBits -= 8;
}
if (remainingBits >= 32) {
const version = getLameShortVersion();
if (remainingBits >= 32)
for (i = 0; i < version.length && remainingBits >= 8; ++i) {
remainingBits -= 8;
this.putbits2(gfc, version.charCodeAt(i), 8);
}
}
for (; remainingBits >= 1; remainingBits -= 1) {
this.putbits2(gfc, 0, 1);
}
assert(remainingBits === 0);
}
private writeheader(gfc: LameInternalFlags, val: number, j: number) {
let { ptr } = gfc.header[gfc.h_ptr];
while (j > 0) {
const k = Math.min(j, 8 - (ptr & 7));
j -= k;
assert(j < BitStream.MAX_LENGTH);
gfc.header[gfc.h_ptr].buf[ptr >> 3] |= (val >> j) << (8 - (ptr & 7) - k);
ptr += k;
}
gfc.header[gfc.h_ptr].ptr = ptr;
}
private encodeSideInfo2(gfp: LameGlobalFlags, bitsPerFrame: number) {
const gfc = gfp.internal_flags;
let gr;
let ch;
const { l3_side } = gfc;
gfc.header[gfc.h_ptr].ptr = 0;
fillArray(gfc.header[gfc.h_ptr].buf, 0, gfc.sideinfo_len, 0);
if (gfp.out_samplerate < 16000) {
this.writeheader(gfc, 0xffe, 12);
} else {
this.writeheader(gfc, 0xfff, 12);
}
this.writeheader(gfc, gfp.version, 1);
this.writeheader(gfc, 4 - 3, 2);
this.writeheader(gfc, 1, 1);
this.writeheader(gfc, gfc.bitrate_index, 4);
this.writeheader(gfc, gfc.samplerate_index, 2);
this.writeheader(gfc, gfc.padding, 1);
this.writeheader(gfc, 0, 1);
this.writeheader(gfc, gfp.mode.ordinal, 2);
this.writeheader(gfc, gfc.mode_ext, 2);
this.writeheader(gfc, 0, 1);
this.writeheader(gfc, 1, 1);
this.writeheader(gfc, 0, 2);
if (gfp.version === 1) {
assert(l3_side.main_data_begin >= 0);
this.writeheader(gfc, l3_side.main_data_begin, 9);
if (gfc.channels_out === 2) this.writeheader(gfc, l3_side.private_bits, 3);
else this.writeheader(gfc, l3_side.private_bits, 5);
for (ch = 0; ch < gfc.channels_out; ch++) {
for (let band = 0; band < 4; band++) {
this.writeheader(gfc, l3_side.scfsi[ch][band], 1);
}
}
for (gr = 0; gr < 2; gr++) {
for (ch = 0; ch < gfc.channels_out; ch++) {
const gi = l3_side.tt[gr][ch];
this.writeheader(gfc, gi.part2_3_length + gi.part2_length, 12);
this.writeheader(gfc, gi.big_values / 2, 9);
this.writeheader(gfc, gi.global_gain, 8);
this.writeheader(gfc, gi.scalefac_compress, 4);
if (gi.block_type !== NORM_TYPE) {
this.writeheader(gfc, 1, 1);
this.writeheader(gfc, gi.block_type, 2);
this.writeheader(gfc, gi.mixed_block_flag, 1);
if (gi.table_select[0] === 14) gi.table_select[0] = 16;
this.writeheader(gfc, gi.table_select[0], 5);
if (gi.table_select[1] === 14) gi.table_select[1] = 16;
this.writeheader(gfc, gi.table_select[1], 5);
this.writeheader(gfc, gi.subblock_gain[0], 3);
this.writeheader(gfc, gi.subblock_gain[1], 3);
this.writeheader(gfc, gi.subblock_gain[2], 3);
} else {
this.writeheader(gfc, 0, 1);
if (gi.table_select[0] === 14) gi.table_select[0] = 16;
this.writeheader(gfc, gi.table_select[0], 5);
if (gi.table_select[1] === 14) gi.table_select[1] = 16;
this.writeheader(gfc, gi.table_select[1], 5);
if (gi.table_select[2] === 14) gi.table_select[2] = 16;
this.writeheader(gfc, gi.table_select[2], 5);
assert(gi.region0_count >= 0 && gi.region0_count < 16);
assert(gi.region1_count >= 0 && gi.region1_count < 8);
this.writeheader(gfc, gi.region0_count, 4);
this.writeheader(gfc, gi.region1_count, 3);
}
this.writeheader(gfc, gi.preflag, 1);
this.writeheader(gfc, gi.scalefac_scale, 1);
this.writeheader(gfc, gi.count1table_select, 1);
}
}
} else {
assert(l3_side.main_data_begin >= 0);
this.writeheader(gfc, l3_side.main_data_begin, 8);
this.writeheader(gfc, l3_side.private_bits, gfc.channels_out);
gr = 0;
for (ch = 0; ch < gfc.channels_out; ch++) {
const gi = l3_side.tt[gr][ch];
this.writeheader(gfc, gi.part2_3_length + gi.part2_length, 12);
this.writeheader(gfc, gi.big_values / 2, 9);
this.writeheader(gfc, gi.global_gain, 8);
this.writeheader(gfc, gi.scalefac_compress, 9);
if (gi.block_type !== NORM_TYPE) {
this.writeheader(gfc, 1, 1);
this.writeheader(gfc, gi.block_type, 2);
this.writeheader(gfc, gi.mixed_block_flag, 1);
if (gi.table_select[0] === 14) gi.table_select[0] = 16;
this.writeheader(gfc, gi.table_select[0], 5);
if (gi.table_select[1] === 14) gi.table_select[1] = 16;
this.writeheader(gfc, gi.table_select[1], 5);
this.writeheader(gfc, gi.subblock_gain[0], 3);
this.writeheader(gfc, gi.subblock_gain[1], 3);
this.writeheader(gfc, gi.subblock_gain[2], 3);
} else {
this.writeheader(gfc, 0, 1);
if (gi.table_select[0] === 14) gi.table_select[0] = 16;
this.writeheader(gfc, gi.table_select[0], 5);
if (gi.table_select[1] === 14) gi.table_select[1] = 16;
this.writeheader(gfc, gi.table_select[1], 5);
if (gi.table_select[2] === 14) gi.table_select[2] = 16;
this.writeheader(gfc, gi.table_select[2], 5);
assert(gi.region0_count >= 0 && gi.region0_count < 16);
assert(gi.region1_count >= 0 && gi.region1_count < 8);
this.writeheader(gfc, gi.region0_count, 4);
this.writeheader(gfc, gi.region1_count, 3);
}
this.writeheader(gfc, gi.scalefac_scale, 1);
this.writeheader(gfc, gi.count1table_select, 1);
}
}
const old = gfc.h_ptr;
assert(gfc.header[old].ptr === gfc.sideinfo_len * 8);
gfc.h_ptr = (old + 1) & (MAX_HEADER_BUF - 1);
gfc.header[gfc.h_ptr].write_timing = gfc.header[old].write_timing + bitsPerFrame;
if (gfc.h_ptr === gfc.w_ptr) {
console.warn('MAX_HEADER_BUF too small in bitstream.');
}
}
private huffman_coder_count1(gfc: LameInternalFlags, gi: GrInfo) {
const h = tables.ht[gi.count1table_select + 32];
let i;
let bits = 0;
let ix = gi.big_values;
let xr = gi.big_values;
assert(gi.count1table_select < 2);
for (i = (gi.count1 - gi.big_values) / 4; i > 0; --i) {
let huffbits = 0;
let p = 0;
let v;
v = gi.l3_enc[ix + 0];
if (v !== 0) {
p += 8;
if (gi.xr[xr + 0] < 0) huffbits++;
assert(v <= 1);
}
v = gi.l3_enc[ix + 1];
if (v !== 0) {
p += 4;
huffbits *= 2;
if (gi.xr[xr + 1] < 0) huffbits++;
assert(v <= 1);
}
v = gi.l3_enc[ix + 2];
if (v !== 0) {
p += 2;
huffbits *= 2;
if (gi.xr[xr + 2] < 0) huffbits++;
assert(v <= 1);
}
v = gi.l3_enc[ix + 3];
if (v !== 0) {
p++;
huffbits *= 2;
if (gi.xr[xr + 3] < 0) huffbits++;
assert(v <= 1);
}
assert(h.table !== undefined);
assert(h.hlen !== undefined);
ix += 4;
xr += 4;
this.putbits2(gfc, huffbits + h.table[p], h.hlen[p]);
bits += h.hlen[p];
}
return bits;
}
private huffmancode(gfc: LameInternalFlags, tableindex: number, start: number, end: number, gi: GrInfo) {
const h = tables.ht[tableindex];
let bits = 0;
assert(tableindex < 32);
if (tableindex === 0) return bits;
for (let i = start; i < end; i += 2) {
let cbits = 0;
let xbits = 0;
const linbits = h.xlen;
let { xlen } = h;
let ext = 0;
let x1 = gi.l3_enc[i];
let x2 = gi.l3_enc[i + 1];
if (x1 !== 0) {
if (gi.xr[i] < 0) ext++;
cbits--;
}
if (tableindex > 15) {
if (x1 > 14) {
const linbits_x1 = x1 - 15;
assert(linbits_x1 <= h.linmax);
ext |= linbits_x1 << 1;
xbits = linbits;
x1 = 15;
}
if (x2 > 14) {
const linbits_x2 = x2 - 15;
assert(linbits_x2 <= h.linmax);
ext <<= linbits;
ext |= linbits_x2;
xbits += linbits;
x2 = 15;
}
xlen = 16;
}
if (x2 !== 0) {
ext <<= 1;
if (gi.xr[i + 1] < 0) ext++;
cbits--;
}
assert((x1 | x2) < 16);
assert(h.hlen !== undefined);
assert(h.table !== undefined);
x1 = x1 * xlen + x2;
xbits -= cbits;
cbits += h.hlen[x1];
assert(cbits <= BitStream.MAX_LENGTH);
assert(xbits <= BitStream.MAX_LENGTH);
assert(h.table !== undefined);
this.putbits2(gfc, h.table[x1], cbits);
this.putbits2(gfc, ext, xbits);
bits += cbits + xbits;
}
return bits;
}
private shortHuffmancodebits(gfc: LameInternalFlags, gi: GrInfo) {
let region1Start = 3 * gfc.scalefac_band.s[3];
if (region1Start > gi.big_values) region1Start = gi.big_values;
let bits = this.huffmancode(gfc, gi.table_select[0], 0, region1Start, gi);
bits += this.huffmancode(gfc, gi.table_select[1], region1Start, gi.big_values, gi);
return bits;
}
private longHuffmancodebits(gfc: LameInternalFlags, gi: GrInfo) {
let bits;
let region1Start;
let region2Start;
const bigvalues = gi.big_values;
assert(bigvalues >= 0 && bigvalues <= 576);
let i = gi.region0_count + 1;
assert(i >= 0);
assert(i < gfc.scalefac_band.l.length);
region1Start = gfc.scalefac_band.l[i];
i += gi.region1_count + 1;
assert(i >= 0);
assert(i < gfc.scalefac_band.l.length);
region2Start = gfc.scalefac_band.l[i];
if (region1Start > bigvalues) {
region1Start = bigvalues;
}
if (region2Start > bigvalues) {
region2Start = bigvalues;
}
bits = this.huffmancode(gfc, gi.table_select[0], 0, region1Start, gi);
bits += this.huffmancode(gfc, gi.table_select[1], region1Start, region2Start, gi);
bits += this.huffmancode(gfc, gi.table_select[2], region2Start, bigvalues, gi);
return bits;
}
private writeMainData(gfp: LameGlobalFlags) {
let gr;
let ch;
let sfb;
let data_bits;
let tot_bits = 0;
const gfc = gfp.internal_flags;
const { l3_side } = gfc;
if (gfp.version === 1) {
for (gr = 0; gr < 2; gr++) {
for (ch = 0; ch < gfc.channels_out; ch++) {
const gi = l3_side.tt[gr][ch];
const slen1 = Takehiro.slen1_tab[gi.scalefac_compress];
const slen2 = Takehiro.slen2_tab[gi.scalefac_compress];
data_bits = 0;
for (sfb = 0; sfb < gi.sfbdivide; sfb++) {
if (gi.scalefac[sfb] === -1) {
continue;
}
this.putbits2(gfc, gi.scalefac[sfb], slen1);
data_bits += slen1;
}
for (; sfb < gi.sfbmax; sfb++) {
if (gi.scalefac[sfb] === -1) {
continue;
}
this.putbits2(gfc, gi.scalefac[sfb], slen2);
data_bits += slen2;
}
assert(data_bits === gi.part2_length);
if (gi.block_type === SHORT_TYPE) {
data_bits += this.shortHuffmancodebits(gfc, gi);
} else {
data_bits += this.longHuffmancodebits(gfc, gi);
}
data_bits += this.huffman_coder_count1(gfc, gi);
assert(data_bits === gi.part2_3_length + gi.part2_length);
tot_bits += data_bits;
}
}
return tot_bits;
}
gr = 0;
for (ch = 0; ch < gfc.channels_out; ch++) {
const gi = l3_side.tt[gr][ch];
let i;
let sfb_partition;
let scale_bits = 0;
assert(gi.sfb_partition_table !== null);
data_bits = 0;
sfb = 0;
sfb_partition = 0;
if (gi.block_type === SHORT_TYPE) {
for (; sfb_partition < 4; sfb_partition++) {
const sfbs = gi.sfb_partition_table[sfb_partition] / 3;
const slen = gi.slen[sfb_partition];
for (i = 0; i < sfbs; i++, sfb++) {
this.putbits2(gfc, Math.max(gi.scalefac[sfb * 3 + 0], 0), slen);
this.putbits2(gfc, Math.max(gi.scalefac[sfb * 3 + 1], 0), slen);
this.putbits2(gfc, Math.max(gi.scalefac[sfb * 3 + 2], 0), slen);
scale_bits += 3 * slen;
}
}
data_bits += this.shortHuffmancodebits(gfc, gi);
} else {
for (; sfb_partition < 4; sfb_partition++) {
const sfbs = gi.sfb_partition_table[sfb_partition];
const slen = gi.slen[sfb_partition];
for (i = 0; i < sfbs; i++, sfb++) {
this.putbits2(gfc, Math.max(gi.scalefac[sfb], 0), slen);
scale_bits += slen;
}
}
data_bits += this.longHuffmancodebits(gfc, gi);
}
data_bits += this.huffman_coder_count1(gfc, gi);
assert(data_bits === gi.part2_3_length);
assert(scale_bits === gi.part2_length);
tot_bits += scale_bits + data_bits;
}
return tot_bits;
}
private compute_flushbits(gfp: LameGlobalFlags, total_bytes_output: TotalBytes) {
const gfc = gfp.internal_flags;
let flushbits;
let remaining_headers;
let last_ptr;
const first_ptr = gfc.w_ptr;
last_ptr = gfc.h_ptr - 1;
if (last_ptr === -1) last_ptr = MAX_HEADER_BUF - 1;
flushbits = gfc.header[last_ptr].write_timing - this.totbit;
total_bytes_output.total = flushbits;
if (flushbits >= 0) {
remaining_headers = 1 + last_ptr - first_ptr;
if (last_ptr < first_ptr) {
remaining_headers = 1 + last_ptr - first_ptr + MAX_HEADER_BUF;
}
flushbits -= remaining_headers * 8 * gfc.sideinfo_len;
}
const bitsPerFrame = this.getframebits(gfp);
flushbits += bitsPerFrame;
total_bytes_output.total += bitsPerFrame;
if (total_bytes_output.total % 8 !== 0) total_bytes_output.total = 1 + total_bytes_output.total / 8;
else total_bytes_output.total /= 8;
total_bytes_output.total += this.bufByteIdx + 1;
if (flushbits < 0) {
console.warn('strange error flushing buffer ... ');
}
return flushbits;
}
flush_bitstream(gfp: LameGlobalFlags) {
const gfc = gfp.internal_flags;
let flushbits;
let last_ptr = gfc.h_ptr - 1;
if (last_ptr === -1) last_ptr = MAX_HEADER_BUF - 1;
const { l3_side } = gfc;
if ((flushbits = this.compute_flushbits(gfp, new TotalBytes())) < 0) return;
this.drain_into_ancillary(gfp, flushbits);
assert(gfc.header[last_ptr].write_timing + this.getframebits(gfp) === this.totbit);
gfc.ResvSize = 0;
l3_side.main_data_begin = 0;
if (gfc.findReplayGain) {
const radioGain = this.ga.getTitleGain(gfc.rgdata);
assert(!isCloseToEachOther(radioGain, GainAnalysis.GAIN_NOT_ENOUGH_SAMPLES));
gfc.RadioGain = Math.floor(radioGain * 10.0 + 0.5);
}
if (gfc.findPeakSample) {
gfc.noclipGainChange = Math.ceil(Math.log10(gfc.PeakSample / 32767.0) * 20.0 * 10.0);
if (gfc.noclipGainChange > 0) {
if (isCloseToEachOther(gfp.scale, 1.0) || isCloseToEachOther(gfp.scale, 0.0)) {
gfc.noclipScale = Math.floor((32767.0 / gfc.PeakSample) * 100.0) / 100.0;
} else {
gfc.noclipScale = -1;
}
} else {
gfc.noclipScale = -1;
}
}
}
format_bitstream(gfp: LameGlobalFlags) {
const gfc = gfp.internal_flags;
const { l3_side } = gfc;
const bitsPerFrame = this.getframebits(gfp);
this.drain_into_ancillary(gfp, l3_side.resvDrain_pre);
this.encodeSideInfo2(gfp, bitsPerFrame);
let bits = 8 * gfc.sideinfo_len;
bits += this.writeMainData(gfp);
this.drain_into_ancillary(gfp, l3_side.resvDrain_post);
bits += l3_side.resvDrain_post;
l3_side.main_data_begin += (bitsPerFrame - bits) / 8;
if (this.compute_flushbits(gfp, new TotalBytes()) !== gfc.ResvSize) {
console.warn('Internal buffer inconsistency. flushbits <> ResvSize');
}
if (l3_side.main_data_begin * 8 !== gfc.ResvSize) {
console.warn(
`bit reservoir error: \n` +
`l3_side.main_data_begin: ${8 * l3_side.main_data_begin} \n` +
`Resvoir size: ${gfc.ResvSize} \n` +
`resv drain (post) ${l3_side.resvDrain_post} \n` +
`resv drain (pre) ${l3_side.resvDrain_pre} \n` +
`header and sideinfo: ${8 * gfc.sideinfo_len} \n` +
`data bits: ${bits - l3_side.resvDrain_post - 8 * gfc.sideinfo_len} \n` +
`total bits: ${bits} (remainder: ${bits % 8}) \n` +
`bitsperframe: ${bitsPerFrame}`,
);
console.warn('This is a fatal error. It has several possible causes:');
console.warn('90%% LAME compiled with buggy version of gcc using advanced optimizations');
console.warn(' 9%% Your system is overclocked');
console.warn(' 1%% bug in LAME encoding library');
gfc.ResvSize = l3_side.main_data_begin * 8;
}
assert(this.totbit % 8 === 0);
if (this.totbit > 1000000000) {
for (let i = 0; i < MAX_HEADER_BUF; ++i) {
gfc.header[i].write_timing -= this.totbit;
}
this.totbit = 0;
}
return 0;
}
copyMetadata(gfc: LameInternalFlags, buffer: Uint8Array, bufferPos: number, size: number) {
return this.copyBuffer(gfc, buffer, bufferPos, size, false);
}
copyFrameData(gfc: LameInternalFlags, buffer: Uint8Array, bufferPos: number, size: number) {
return this.copyBuffer(gfc, buffer, bufferPos, size, true);
}
private copyBuffer(gfc: LameInternalFlags, buffer: Uint8Array, bufferPos: number, size: number, mp3data: boolean) {
const minimum = this.bufByteIdx + 1;
if (minimum <= 0) {
return 0;
}
if (size !== 0 && minimum > size) {
return -1;
}
copyArray(this.buf, 0, buffer, bufferPos, minimum);
this.bufByteIdx = -1;
this.bufBitIdx = 0;
if (mp3data) {
const crc = new Int32Array(1);
crc[0] = gfc.nMusicCRC;
this.vbr.updateMusicCRC(crc, buffer, bufferPos, minimum);
gfc.nMusicCRC = crc[0];
if (minimum > 0) {
gfc.VBR_seek_table.nBytesWritten += minimum;
}
}
return minimum;
}
}

View File

@@ -0,0 +1,7 @@
export class Bits {
public bits: number;
constructor(bits: number) {
this.bits = Math.trunc(bits);
}
}

View File

@@ -0,0 +1,115 @@
import type { III_psy_ratio } from './III_psy_ratio';
import type { LameGlobalFlags } from './LameGlobalFlags';
import { MeanBits } from './MeanBits';
import type { Quantize } from './Quantize';
import { assert } from './assert';
import { MAX_BITS_PER_CHANNEL, MAX_BITS_PER_GRANULE, MPG_MD_MS_LR, SFBMAX, SHORT_TYPE } from './constants';
export class CBRNewIterationLoop {
constructor(public quantize: Quantize) {}
iteration_loop(gfp: LameGlobalFlags, pe: number[][], ms_ener_ratio: number[], ratio: III_psy_ratio[][]) {
const gfc = gfp.internal_flags;
const l3_xmin = new Float32Array(SFBMAX);
const xrpow = new Float32Array(576);
const targ_bits = new Int32Array(2);
let mean_bits = 0;
let max_bits;
const { l3_side } = gfc;
const mb = new MeanBits(mean_bits);
this.quantize.rv.ResvFrameBegin(gfp, mb);
mean_bits = mb.bits;
for (let gr = 0; gr < gfc.mode_gr; gr++) {
max_bits = this.on_pe(gfp, pe, targ_bits, mean_bits, gr, gr);
if (gfc.mode_ext === MPG_MD_MS_LR) {
this.quantize.ms_convert(gfc.l3_side, gr);
this.quantize.qupvt.reduce_side(targ_bits, ms_ener_ratio[gr], mean_bits, max_bits);
}
for (let ch = 0; ch < gfc.channels_out; ch++) {
let adjust;
let masking_lower_db;
const cod_info = l3_side.tt[gr][ch];
if (cod_info.block_type !== SHORT_TYPE) {
// NORM, START or STOP type
adjust = 0;
masking_lower_db = gfc.PSY.mask_adjust - adjust;
} else {
adjust = 0;
masking_lower_db = gfc.PSY.mask_adjust_short - adjust;
}
gfc.masking_lower = Math.pow(10.0, masking_lower_db * 0.1);
this.quantize.init_outer_loop(gfc, cod_info);
if (this.quantize.init_xrpow(gfc, cod_info, xrpow)) {
this.quantize.qupvt.calc_xmin(gfp, ratio[gr][ch], cod_info, l3_xmin);
this.quantize.outer_loop(gfp, cod_info, l3_xmin, xrpow, ch, targ_bits[ch]);
}
this.quantize.iteration_finish_one(gfc, gr, ch);
assert(cod_info.part2_3_length <= MAX_BITS_PER_CHANNEL);
assert(cod_info.part2_3_length <= targ_bits[ch]);
}
}
this.quantize.rv.ResvFrameEnd(gfc, mean_bits);
}
private on_pe(gfp: LameGlobalFlags, pe: number[][], targ_bits: Int32Array, mean_bits: number, gr: number, cbr: number) {
const gfc = gfp.internal_flags;
let tbits = 0;
let bits;
const add_bits = new Int32Array(2);
let ch;
const mb = new MeanBits(tbits);
let extra_bits = this.quantize.rv.ResvMaxBits(gfp, mean_bits, mb, cbr);
tbits = mb.bits;
let max_bits = tbits + extra_bits;
if (max_bits > MAX_BITS_PER_GRANULE) {
max_bits = MAX_BITS_PER_GRANULE;
}
for (bits = 0, ch = 0; ch < gfc.channels_out; ++ch) {
targ_bits[ch] = Math.min(MAX_BITS_PER_CHANNEL, tbits / gfc.channels_out);
add_bits[ch] = Math.trunc((targ_bits[ch] * pe[gr][ch]) / 700.0 - targ_bits[ch]);
if (add_bits[ch] > (mean_bits * 3) / 4) add_bits[ch] = (mean_bits * 3) / 4;
if (add_bits[ch] < 0) add_bits[ch] = 0;
if (add_bits[ch] + targ_bits[ch] > MAX_BITS_PER_CHANNEL) add_bits[ch] = Math.max(0, MAX_BITS_PER_CHANNEL - targ_bits[ch]);
bits += add_bits[ch];
}
if (bits > extra_bits) {
for (ch = 0; ch < gfc.channels_out; ++ch) {
add_bits[ch] = (extra_bits * add_bits[ch]) / bits;
}
}
for (ch = 0; ch < gfc.channels_out; ++ch) {
targ_bits[ch] += add_bits[ch];
extra_bits -= add_bits[ch];
}
for (bits = 0, ch = 0; ch < gfc.channels_out; ++ch) {
bits += targ_bits[ch];
}
if (bits > MAX_BITS_PER_GRANULE) {
let sum = 0;
for (ch = 0; ch < gfc.channels_out; ++ch) {
targ_bits[ch] *= MAX_BITS_PER_GRANULE;
targ_bits[ch] /= bits;
sum += targ_bits[ch];
}
assert(sum <= MAX_BITS_PER_GRANULE);
}
return max_bits;
}
}

View File

@@ -0,0 +1,11 @@
export class CalcNoiseData {
global_gain = 0;
sfb_count1 = 0;
step = new Int32Array(39);
noise = new Float32Array(39);
noise_log = new Float32Array(39);
}

View File

@@ -0,0 +1,13 @@
export class CalcNoiseResult {
over_noise = 0;
tot_noise = 0;
max_noise = 0;
over_count = 0;
over_SSD = 0;
bits = 0;
}

View File

@@ -0,0 +1,305 @@
import type { BitStream } from './BitStream';
import { III_psy_ratio } from './III_psy_ratio';
import type { LameGlobalFlags } from './LameGlobalFlags';
import type { LameInternalFlags } from './LameInternalFlags';
import { MPEGMode } from './MPEGMode';
import { NewMDCT } from './NewMDCT';
import type { PsyModel } from './PsyModel';
import { VbrMode } from './VbrMode';
import { assert } from './assert';
import { BLKSIZE, FFTOFFSET, MPG_MD_LR_LR, MPG_MD_MS_LR, NORM_TYPE, SHORT_TYPE } from './constants';
export class Encoder {
private readonly bs: BitStream;
private readonly psy: PsyModel;
constructor(bs: BitStream, psy: PsyModel) {
this.bs = bs;
this.psy = psy;
}
private newMDCT = new NewMDCT();
private adjust_ATH(gfc: LameInternalFlags) {
if (gfc.ATH.useAdjust === 0) {
gfc.ATH.adjust = 1.0;
return;
}
let max_pow = gfc.loudness_sq[0][0];
let gr2_max = gfc.loudness_sq[1][0];
if (gfc.channels_out === 2) {
max_pow += gfc.loudness_sq[0][1];
gr2_max += gfc.loudness_sq[1][1];
} else {
max_pow += max_pow;
gr2_max += gr2_max;
}
if (gfc.mode_gr === 2) {
max_pow = Math.max(max_pow, gr2_max);
}
max_pow *= 0.5;
max_pow *= gfc.ATH.aaSensitivityP;
if (max_pow > 0.03125) {
if (gfc.ATH.adjust >= 1.0) {
gfc.ATH.adjust = 1.0;
} else if (gfc.ATH.adjust < gfc.ATH.adjustLimit) {
gfc.ATH.adjust = gfc.ATH.adjustLimit;
}
gfc.ATH.adjustLimit = 1.0;
} else {
const adj_lim_new = 31.98 * max_pow + 0.000625;
if (gfc.ATH.adjust >= adj_lim_new) {
gfc.ATH.adjust *= adj_lim_new * 0.075 + 0.925;
if (gfc.ATH.adjust < adj_lim_new) {
gfc.ATH.adjust = adj_lim_new;
}
} else if (gfc.ATH.adjustLimit >= adj_lim_new) {
gfc.ATH.adjust = adj_lim_new;
} else if (gfc.ATH.adjust < gfc.ATH.adjustLimit) {
gfc.ATH.adjust = gfc.ATH.adjustLimit;
}
gfc.ATH.adjustLimit = adj_lim_new;
}
}
private updateStats(gfc: LameInternalFlags) {
let gr;
let ch;
assert(gfc.bitrate_index >= 0 && gfc.bitrate_index < 16);
assert(gfc.mode_ext >= 0 && gfc.mode_ext < 4);
gfc.bitrate_stereoMode_Hist[gfc.bitrate_index][4]++;
gfc.bitrate_stereoMode_Hist[15][4]++;
if (gfc.channels_out === 2) {
gfc.bitrate_stereoMode_Hist[gfc.bitrate_index][gfc.mode_ext]++;
gfc.bitrate_stereoMode_Hist[15][gfc.mode_ext]++;
}
for (gr = 0; gr < gfc.mode_gr; ++gr) {
for (ch = 0; ch < gfc.channels_out; ++ch) {
let bt = Math.trunc(gfc.l3_side.tt[gr][ch].block_type);
if (gfc.l3_side.tt[gr][ch].mixed_block_flag !== 0) bt = 4;
gfc.bitrate_blockType_Hist[gfc.bitrate_index][bt]++;
gfc.bitrate_blockType_Hist[gfc.bitrate_index][5]++;
gfc.bitrate_blockType_Hist[15][bt]++;
gfc.bitrate_blockType_Hist[15][5]++;
}
}
}
private lame_encode_frame_init(gfp: LameGlobalFlags, inbuf: [Float32Array, Float32Array]) {
const gfc = gfp.internal_flags;
let ch;
let gr;
if (!gfc.lame_encode_frame_init) {
let i;
let j;
const primebuff0 = new Float32Array(286 + 1152 + 576);
const primebuff1 = new Float32Array(286 + 1152 + 576);
gfc.lame_encode_frame_init = true;
for (i = 0, j = 0; i < 286 + 576 * (1 + gfc.mode_gr); ++i) {
if (i < 576 * gfc.mode_gr) {
primebuff0[i] = 0;
if (gfc.channels_out === 2) primebuff1[i] = 0;
} else {
primebuff0[i] = inbuf[0][j];
if (gfc.channels_out === 2) primebuff1[i] = inbuf[1][j];
++j;
}
}
for (gr = 0; gr < gfc.mode_gr; gr++) {
for (ch = 0; ch < gfc.channels_out; ch++) {
gfc.l3_side.tt[gr][ch].block_type = SHORT_TYPE;
}
}
this.newMDCT.mdct_sub48(gfc, primebuff0, primebuff1);
assert(FFTOFFSET <= 576);
assert(gfc.mf_size >= BLKSIZE + gfp.framesize - FFTOFFSET);
assert(gfc.mf_size >= 512 + gfp.framesize - 32);
}
}
lame_encode_mp3_frame(
gfp: LameGlobalFlags,
inbuf_l: Float32Array,
inbuf_r: Float32Array,
mp3buf: Uint8Array,
mp3bufPos: number,
mp3buf_size: number,
) {
const masking_LR = Array.from({ length: 2 }, () => Array.from({ length: 2 }, () => new III_psy_ratio()));
const masking_MS = Array.from({ length: 2 }, () => Array.from({ length: 2 }, () => new III_psy_ratio()));
masking_MS[0][0] = new III_psy_ratio();
masking_MS[0][1] = new III_psy_ratio();
masking_MS[1][0] = new III_psy_ratio();
masking_MS[1][1] = new III_psy_ratio();
let masking;
const gfc = gfp.internal_flags;
const tot_ener = Array.from({ length: 2 }, () => new Float32Array(4));
const ms_ener_ratio = [0.5, 0.5];
const pe = [
[0, 0],
[0, 0],
];
const pe_MS = [
[0, 0],
[0, 0],
];
let pe_use;
let ch;
let gr;
const inbuf: [Float32Array, Float32Array] = [inbuf_l, inbuf_r];
if (!gfc.lame_encode_frame_init) {
this.lame_encode_frame_init(gfp, inbuf);
}
gfc.padding = 0;
if ((gfc.slot_lag -= gfc.frac_SpF) < 0) {
gfc.slot_lag += gfp.out_samplerate;
gfc.padding = 1;
}
if (gfc.psymodel !== 0) {
let ret;
let bufpPos = 0;
const blocktype = new Int32Array(2);
const bufp: Float32Array[] = [];
for (gr = 0; gr < gfc.mode_gr; gr++) {
for (ch = 0; ch < gfc.channels_out; ch++) {
bufp[ch] = inbuf[ch];
bufpPos = 576 + gr * 576 - FFTOFFSET;
}
if (gfp.VBR === VbrMode.vbr_mtrh || gfp.VBR === VbrMode.vbr_mt) {
ret = this.psy.L3psycho_anal_vbr(gfp, bufp, bufpPos, gr, masking_LR, masking_MS, pe[gr], pe_MS[gr], tot_ener[gr], blocktype);
} else {
ret = this.psy.L3psycho_anal_ns(gfp, bufp, bufpPos, gr, masking_LR, masking_MS, pe[gr], pe_MS[gr], tot_ener[gr], blocktype);
}
if (ret !== 0) return -4;
if (gfp.mode === MPEGMode.JOINT_STEREO) {
ms_ener_ratio[gr] = tot_ener[gr][2] + tot_ener[gr][3];
if (ms_ener_ratio[gr] > 0) ms_ener_ratio[gr] = tot_ener[gr][3] / ms_ener_ratio[gr];
}
for (ch = 0; ch < gfc.channels_out; ch++) {
const cod_info = gfc.l3_side.tt[gr][ch];
cod_info.block_type = blocktype[ch];
cod_info.mixed_block_flag = 0;
}
}
} else {
for (gr = 0; gr < gfc.mode_gr; gr++)
for (ch = 0; ch < gfc.channels_out; ch++) {
gfc.l3_side.tt[gr][ch].block_type = NORM_TYPE;
gfc.l3_side.tt[gr][ch].mixed_block_flag = 0;
pe_MS[gr][ch] = 700;
pe[gr][ch] = 700;
}
}
this.adjust_ATH(gfc);
this.newMDCT.mdct_sub48(gfc, inbuf[0], inbuf[1]);
gfc.mode_ext = MPG_MD_LR_LR;
if (gfp.mode === MPEGMode.JOINT_STEREO) {
let sum_pe_MS = 0;
let sum_pe_LR = 0;
for (gr = 0; gr < gfc.mode_gr; gr++) {
for (ch = 0; ch < gfc.channels_out; ch++) {
sum_pe_MS += pe_MS[gr][ch];
sum_pe_LR += pe[gr][ch];
}
}
if (sum_pe_MS <= 1.0 * sum_pe_LR) {
const gi0 = gfc.l3_side.tt[0];
const gi1 = gfc.l3_side.tt[gfc.mode_gr - 1];
if (gi0[0].block_type === gi0[1].block_type && gi1[0].block_type === gi1[1].block_type) {
gfc.mode_ext = MPG_MD_MS_LR;
}
}
}
if (gfc.mode_ext === MPG_MD_MS_LR) {
masking = masking_MS;
pe_use = pe_MS;
} else {
masking = masking_LR;
pe_use = pe;
}
if (gfp.VBR === VbrMode.vbr_off || gfp.VBR === VbrMode.vbr_abr) {
let i;
let f;
for (i = 0; i < 18; i++) gfc.nsPsy.pefirbuf[i] = gfc.nsPsy.pefirbuf[i + 1];
f = 0.0;
for (gr = 0; gr < gfc.mode_gr; gr++) for (ch = 0; ch < gfc.channels_out; ch++) f += pe_use[gr][ch];
gfc.nsPsy.pefirbuf[18] = f;
f = gfc.nsPsy.pefirbuf[9];
for (i = 0; i < 9; i++) f += (gfc.nsPsy.pefirbuf[i] + gfc.nsPsy.pefirbuf[18 - i]) * Encoder.fircoef[i];
f = (670 * 5 * gfc.mode_gr * gfc.channels_out) / f;
for (gr = 0; gr < gfc.mode_gr; gr++) {
for (ch = 0; ch < gfc.channels_out; ch++) {
pe_use[gr][ch] *= f;
}
}
}
assert(gfc.iteration_loop !== null);
gfc.iteration_loop.iteration_loop(gfp, pe_use, ms_ener_ratio, masking);
this.bs.format_bitstream(gfp);
const mp3count = this.bs.copyFrameData(gfc, mp3buf, mp3bufPos, mp3buf_size);
this.updateStats(gfc);
return mp3count;
}
static fircoef = [
-0.0207887 * 5,
-0.0378413 * 5,
-0.0432472 * 5,
-0.031183 * 5,
7.79609e-18 * 5,
0.0467745 * 5,
0.10091 * 5,
0.151365 * 5,
0.187098 * 5,
] as const;
}

View File

@@ -0,0 +1,213 @@
import type { LameInternalFlags } from './LameInternalFlags';
import { BLKSIZE, BLKSIZE_s } from './constants';
export class FFT {
private window: Float32Array = new Float32Array(BLKSIZE);
private window_s: Float32Array = new Float32Array(BLKSIZE_s / 2);
private readonly costab = [
9.238795325112867e-1, 3.826834323650898e-1, 9.951847266721969e-1, 9.80171403295606e-2, 9.996988186962042e-1, 2.454122852291229e-2,
9.999811752826011e-1, 6.135884649154475e-3,
] as const;
private fht(fz: Float32Array, fzPos: number, n: number) {
let tri = 0;
let k4: number;
let fi: number;
let gi: number;
n <<= 1;
const fn = fzPos + n;
k4 = 4;
do {
let s1;
let c1;
let i;
const kx = k4 >> 1;
const k1 = k4;
const k2 = k4 << 1;
const k3 = k2 + k1;
k4 = k2 << 1;
fi = fzPos;
gi = fi + kx;
do {
let f0;
let f1;
let f2;
let f3;
f1 = fz[fi + 0] - fz[fi + k1];
f0 = fz[fi + 0] + fz[fi + k1];
f3 = fz[fi + k2] - fz[fi + k3];
f2 = fz[fi + k2] + fz[fi + k3];
fz[fi + k2] = f0 - f2;
fz[fi + 0] = f0 + f2;
fz[fi + k3] = f1 - f3;
fz[fi + k1] = f1 + f3;
f1 = fz[gi + 0] - fz[gi + k1];
f0 = fz[gi + 0] + fz[gi + k1];
f3 = Math.SQRT2 * fz[gi + k3];
f2 = Math.SQRT2 * fz[gi + k2];
fz[gi + k2] = f0 - f2;
fz[gi + 0] = f0 + f2;
fz[gi + k3] = f1 - f3;
fz[gi + k1] = f1 + f3;
gi += k4;
fi += k4;
} while (fi < fn);
c1 = this.costab[tri + 0];
s1 = this.costab[tri + 1];
for (i = 1; i < kx; i++) {
let c2: number = 1 - 2 * s1 * s1;
const s2 = 2 * s1 * c1;
fi = fzPos + i;
gi = fzPos + k1 - i;
do {
let a: number;
let b: number;
b = s2 * fz[fi + k1] - c2 * fz[gi + k1];
a = c2 * fz[fi + k1] + s2 * fz[gi + k1];
const f1 = fz[fi + 0] - a;
const f0 = fz[fi + 0] + a;
const g1 = fz[gi + 0] - b;
const g0 = fz[gi + 0] + b;
b = s2 * fz[fi + k3] - c2 * fz[gi + k3];
a = c2 * fz[fi + k3] + s2 * fz[gi + k3];
const f3 = fz[fi + k2] - a;
const f2 = fz[fi + k2] + a;
const g3 = fz[gi + k2] - b;
const g2 = fz[gi + k2] + b;
b = s1 * f2 - c1 * g3;
a = c1 * f2 + s1 * g3;
fz[fi + k2] = f0 - a;
fz[fi + 0] = f0 + a;
fz[gi + k3] = g1 - b;
fz[gi + k1] = g1 + b;
b = c1 * g2 - s1 * f3;
a = s1 * g2 + c1 * f3;
fz[gi + k2] = g0 - a;
fz[gi + 0] = g0 + a;
fz[fi + k3] = f1 - b;
fz[fi + k1] = f1 + b;
gi += k4;
fi += k4;
} while (fi < fn);
c2 = c1;
c1 = c2 * this.costab[tri + 0] - s1 * this.costab[tri + 1];
s1 = c2 * this.costab[tri + 1] + s1 * this.costab[tri + 0];
}
tri += 2;
} while (k4 < n);
}
private readonly rv_tbl = [
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8,
0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4,
0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82,
0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6,
0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
] as const;
fft_short(_gfc: LameInternalFlags, x_real: Float32Array[], chn: number, buffer: Float32Array[], bufPos: number) {
for (let b = 0; b < 3; b++) {
let x = BLKSIZE_s / 2;
const k = 0xffff & ((576 / 3) * (b + 1));
let j = BLKSIZE_s / 8 - 1;
do {
let f0;
let f1;
let f2;
let f3;
let w;
const i = this.rv_tbl[j << 2] & 0xff;
f0 = this.window_s[i] * buffer[chn][bufPos + i + k];
w = this.window_s[0x7f - i] * buffer[chn][bufPos + i + k + 0x80];
f1 = f0 - w;
f0 += w;
f2 = this.window_s[i + 0x40] * buffer[chn][bufPos + i + k + 0x40];
w = this.window_s[0x3f - i] * buffer[chn][bufPos + i + k + 0xc0];
f3 = f2 - w;
f2 += w;
x -= 4;
x_real[b][x + 0] = f0 + f2;
x_real[b][x + 2] = f0 - f2;
x_real[b][x + 1] = f1 + f3;
x_real[b][x + 3] = f1 - f3;
f0 = this.window_s[i + 0x01] * buffer[chn][bufPos + i + k + 0x01];
w = this.window_s[0x7e - i] * buffer[chn][bufPos + i + k + 0x81];
f1 = f0 - w;
f0 += w;
f2 = this.window_s[i + 0x41] * buffer[chn][bufPos + i + k + 0x41];
w = this.window_s[0x3e - i] * buffer[chn][bufPos + i + k + 0xc1];
f3 = f2 - w;
f2 += w;
x_real[b][x + BLKSIZE_s / 2 + 0] = f0 + f2;
x_real[b][x + BLKSIZE_s / 2 + 2] = f0 - f2;
x_real[b][x + BLKSIZE_s / 2 + 1] = f1 + f3;
x_real[b][x + BLKSIZE_s / 2 + 3] = f1 - f3;
} while (--j >= 0);
this.fht(x_real[b], x, BLKSIZE_s / 2);
}
}
fft_long(_gfc: LameInternalFlags, y: Float32Array, chn: number, buffer: Float32Array[], bufPos: number) {
let jj = BLKSIZE / 8 - 1;
let x = BLKSIZE / 2;
do {
let f0;
let f1;
let f2;
let f3;
let w;
const i = this.rv_tbl[jj] & 0xff;
f0 = this.window[i] * buffer[chn][bufPos + i];
w = this.window[i + 0x200] * buffer[chn][bufPos + i + 0x200];
f1 = f0 - w;
f0 += w;
f2 = this.window[i + 0x100] * buffer[chn][bufPos + i + 0x100];
w = this.window[i + 0x300] * buffer[chn][bufPos + i + 0x300];
f3 = f2 - w;
f2 += w;
x -= 4;
y[x + 0] = f0 + f2;
y[x + 2] = f0 - f2;
y[x + 1] = f1 + f3;
y[x + 3] = f1 - f3;
f0 = this.window[i + 0x001] * buffer[chn][bufPos + i + 0x001];
w = this.window[i + 0x201] * buffer[chn][bufPos + i + 0x201];
f1 = f0 - w;
f0 += w;
f2 = this.window[i + 0x101] * buffer[chn][bufPos + i + 0x101];
w = this.window[i + 0x301] * buffer[chn][bufPos + i + 0x301];
f3 = f2 - w;
f2 += w;
y[x + BLKSIZE / 2 + 0] = f0 + f2;
y[x + BLKSIZE / 2 + 2] = f0 - f2;
y[x + BLKSIZE / 2 + 1] = f1 + f3;
y[x + BLKSIZE / 2 + 3] = f1 - f3;
} while (--jj >= 0);
this.fht(y, x, BLKSIZE / 2);
}
init() {
for (let i = 0; i < BLKSIZE; i++) {
this.window[i] = 0.42 - 0.5 * Math.cos((2 * Math.PI * (i + 0.5)) / BLKSIZE) + 0.08 * Math.cos((4 * Math.PI * (i + 0.5)) / BLKSIZE);
}
for (let i = 0; i < BLKSIZE_s / 2; i++) {
this.window_s[i] = 0.5 * (1.0 - Math.cos((2.0 * Math.PI * (i + 0.5)) / BLKSIZE_s));
}
}
}

View File

@@ -0,0 +1,402 @@
import type { ReplayGain } from './ReplayGain';
import { copyArray, fillArray } from './arrays';
import { fsqr } from './math';
export class GainAnalysis {
static readonly STEPS_per_dB = 100;
static readonly MAX_dB = 120;
static readonly GAIN_NOT_ENOUGH_SAMPLES = -24601;
static readonly GAIN_ANALYSIS_ERROR = 0;
static readonly GAIN_ANALYSIS_OK = 1;
static readonly INIT_GAIN_ANALYSIS_ERROR = 0;
static readonly INIT_GAIN_ANALYSIS_OK = 1;
static readonly YULE_ORDER = 10;
static readonly MAX_ORDER = GainAnalysis.YULE_ORDER;
static readonly MAX_SAMP_FREQ = 48000;
static readonly RMS_WINDOW_TIME_NUMERATOR = 1;
static readonly RMS_WINDOW_TIME_DENOMINATOR = 20;
static readonly MAX_SAMPLES_PER_WINDOW =
(GainAnalysis.MAX_SAMP_FREQ * GainAnalysis.RMS_WINDOW_TIME_NUMERATOR) / GainAnalysis.RMS_WINDOW_TIME_DENOMINATOR + 1;
private static readonly PINK_REF = 64.82;
private static readonly RMS_PERCENTILE = 0.95;
private ABYule = [
[
0.038575994352, -3.84664617118067, -0.02160367184185, 7.81501653005538, -0.00123395316851, -11.34170355132042, -0.00009291677959,
13.05504219327545, -0.01655260341619, -12.28759895145294, 0.02161526843274, 9.4829380631979, -0.02074045215285, -5.87257861775999,
0.00594298065125, 2.75465861874613, 0.00306428023191, -0.86984376593551, 0.00012025322027, 0.13919314567432, 0.00288463683916,
],
[
0.0541865640643, -3.47845948550071, -0.02911007808948, 6.36317777566148, -0.00848709379851, -8.54751527471874, -0.00851165645469,
9.4769360780128, -0.00834990904936, -8.81498681370155, 0.02245293253339, 6.85401540936998, -0.02596338512915, -4.39470996079559,
0.01624864962975, 2.19611684890774, -0.00240879051584, -0.75104302451432, 0.00674613682247, 0.13149317958808, -0.00187763777362,
],
[
0.15457299681924, -2.37898834973084, -0.09331049056315, 2.84868151156327, -0.06247880153653, -2.64577170229825, 0.02163541888798,
2.23697657451713, -0.05588393329856, -1.67148153367602, 0.04781476674921, 1.00595954808547, 0.00222312597743, -0.45953458054983,
0.03174092540049, 0.16378164858596, -0.01390589421898, -0.05032077717131, 0.00651420667831, 0.0234789740702, -0.00881362733839,
],
[
0.30296907319327, -1.61273165137247, -0.22613988682123, 1.0797749225997, -0.08587323730772, -0.2565625775407, 0.03282930172664,
-0.1627671912044, -0.00915702933434, -0.22638893773906, -0.02364141202522, 0.39120800788284, -0.00584456039913, -0.22138138954925,
0.06276101321749, 0.04500235387352, -0.00000828086748, 0.02005851806501, 0.00205861885564, 0.00302439095741, -0.02950134983287,
],
[
0.33642304856132, -1.49858979367799, -0.2557224142557, 0.87350271418188, -0.11828570177555, 0.12205022308084, 0.11921148675203,
-0.80774944671438, -0.07834489609479, 0.47854794562326, -0.0046997791438, -0.12453458140019, -0.0058950022444, -0.04067510197014,
0.05724228140351, 0.08333755284107, 0.00832043980773, -0.04237348025746, -0.0163538138454, 0.02977207319925, -0.0176017656815,
],
[
0.4491525660845, -0.62820619233671, -0.14351757464547, 0.29661783706366, -0.22784394429749, -0.372563729424, -0.01419140100551,
0.00213767857124, 0.04078262797139, -0.42029820170918, -0.12398163381748, 0.22199650564824, 0.04097565135648, 0.00613424350682,
0.10478503600251, 0.06747620744683, -0.01863887810927, 0.05784820375801, -0.03193428438915, 0.03222754072173, 0.00541907748707,
],
[
0.56619470757641, -1.04800335126349, -0.75464456939302, 0.29156311971249, 0.1624213774223, -0.26806001042947, 0.16744243493672,
0.00819999645858, -0.18901604199609, 0.45054734505008, 0.3093178284183, -0.33032403314006, -0.27562961986224, 0.0673936833311,
0.00647310677246, -0.04784254229033, 0.08647503780351, 0.01639907836189, -0.0378898455484, 0.01807364323573, -0.00588215443421,
],
[
0.58100494960553, -0.51035327095184, -0.53174909058578, -0.31863563325245, -0.14289799034253, -0.20256413484477, 0.17520704835522,
0.1472815413433, 0.02377945217615, 0.38952639978999, 0.15558449135573, -0.23313271880868, -0.25344790059353, -0.05246019024463,
0.01628462406333, -0.02505961724053, 0.06920467763959, 0.02442357316099, -0.03721611395801, 0.01818801111503, -0.00749618797172,
],
[
0.53648789255105, -0.2504987195602, -0.42163034350696, -0.43193942311114, -0.00275953611929, -0.03424681017675, 0.04267842219415,
-0.04678328784242, -0.10214864179676, 0.26408300200955, 0.14590772289388, 0.15113130533216, -0.02459864859345, -0.17556493366449,
-0.11202315195388, -0.18823009262115, -0.04060034127, 0.05477720428674, 0.0478866554818, 0.0470440968812, -0.02217936801134,
],
];
private ABButter = [
[0.98621192462708, -1.97223372919527, -1.97242384925416, 0.97261396931306, 0.98621192462708],
[0.98500175787242, -1.96977855582618, -1.97000351574484, 0.9702284756635, 0.98500175787242],
[0.97938932735214, -1.95835380975398, -1.95877865470428, 0.95920349965459, 0.97938932735214],
[0.97531843204928, -1.95002759149878, -1.95063686409857, 0.95124613669835, 0.97531843204928],
[0.97316523498161, -1.94561023566527, -1.94633046996323, 0.94705070426118, 0.97316523498161],
[0.96454515552826, -1.92783286977036, -1.92909031105652, 0.93034775234268, 0.96454515552826],
[0.96009142950541, -1.91858953033784, -1.92018285901082, 0.92177618768381, 0.96009142950541],
[0.95856916599601, -1.9154210807478, -1.91713833199203, 0.91885558323625, 0.95856916599601],
[0.94597685600279, -1.88903307939452, -1.89195371200558, 0.89487434461664, 0.94597685600279],
];
private filterYule(
input: Float32Array,
inputPos: number,
output: Float32Array,
outputPos: number,
nSamples: number,
kernel: number[],
): void {
while (nSamples-- !== 0) {
output[outputPos] =
1e-10 +
input[inputPos + 0] * kernel[0] -
output[outputPos - 1] * kernel[1] +
input[inputPos - 1] * kernel[2] -
output[outputPos - 2] * kernel[3] +
input[inputPos - 2] * kernel[4] -
output[outputPos - 3] * kernel[5] +
input[inputPos - 3] * kernel[6] -
output[outputPos - 4] * kernel[7] +
input[inputPos - 4] * kernel[8] -
output[outputPos - 5] * kernel[9] +
input[inputPos - 5] * kernel[10] -
output[outputPos - 6] * kernel[11] +
input[inputPos - 6] * kernel[12] -
output[outputPos - 7] * kernel[13] +
input[inputPos - 7] * kernel[14] -
output[outputPos - 8] * kernel[15] +
input[inputPos - 8] * kernel[16] -
output[outputPos - 9] * kernel[17] +
input[inputPos - 9] * kernel[18] -
output[outputPos - 10] * kernel[19] +
input[inputPos - 10] * kernel[20];
++outputPos;
++inputPos;
}
}
private filterButter(input: Float32Array, inputPos: number, output: Float32Array, outputPos: number, nSamples: number, kernel: number[]) {
while (nSamples-- !== 0) {
output[outputPos] =
input[inputPos + 0] * kernel[0] -
output[outputPos - 1] * kernel[1] +
input[inputPos - 1] * kernel[2] -
output[outputPos - 2] * kernel[3] +
input[inputPos - 2] * kernel[4];
++outputPos;
++inputPos;
}
}
private resetSampleFrequency(rgData: ReplayGain, samplefreq: number) {
for (let i = 0; i < GainAnalysis.MAX_ORDER; i++) {
rgData.linprebuf[i] = 0;
rgData.lstepbuf[i] = 0;
rgData.loutbuf[i] = 0;
rgData.rinprebuf[i] = 0;
rgData.rstepbuf[i] = 0;
rgData.routbuf[i] = 0;
}
switch (Math.trunc(samplefreq)) {
case 48000:
rgData.reqindex = 0;
break;
case 44100:
rgData.reqindex = 1;
break;
case 32000:
rgData.reqindex = 2;
break;
case 24000:
rgData.reqindex = 3;
break;
case 22050:
rgData.reqindex = 4;
break;
case 16000:
rgData.reqindex = 5;
break;
case 12000:
rgData.reqindex = 6;
break;
case 11025:
rgData.reqindex = 7;
break;
case 8000:
rgData.reqindex = 8;
break;
default:
return GainAnalysis.INIT_GAIN_ANALYSIS_ERROR;
}
rgData.sampleWindow = Math.trunc(
(samplefreq * GainAnalysis.RMS_WINDOW_TIME_NUMERATOR + GainAnalysis.RMS_WINDOW_TIME_DENOMINATOR - 1) /
GainAnalysis.RMS_WINDOW_TIME_DENOMINATOR,
);
rgData.lsum = 0;
rgData.rsum = 0;
rgData.totsamp = 0;
fillArray(rgData.A, 0);
return GainAnalysis.INIT_GAIN_ANALYSIS_OK;
}
initGainAnalysis(rgData: ReplayGain, samplefreq: number) {
if (this.resetSampleFrequency(rgData, samplefreq) !== GainAnalysis.INIT_GAIN_ANALYSIS_OK) {
return GainAnalysis.INIT_GAIN_ANALYSIS_ERROR;
}
rgData.linpre = GainAnalysis.MAX_ORDER;
rgData.rinpre = GainAnalysis.MAX_ORDER;
rgData.lstep = GainAnalysis.MAX_ORDER;
rgData.rstep = GainAnalysis.MAX_ORDER;
rgData.lout = GainAnalysis.MAX_ORDER;
rgData.rout = GainAnalysis.MAX_ORDER;
fillArray(rgData.B, 0);
return GainAnalysis.INIT_GAIN_ANALYSIS_OK;
}
analyzeSamples(
rgData: ReplayGain,
left_samples: Float32Array,
left_samplesPos: number,
right_samples: Float32Array,
right_samplesPos: number,
num_samples: number,
num_channels: number,
) {
let curleft;
let curleftBase;
let curright;
let currightBase;
let batchsamples;
let cursamples;
let cursamplepos;
if (num_samples === 0) return GainAnalysis.GAIN_ANALYSIS_OK;
cursamplepos = 0;
batchsamples = num_samples;
switch (num_channels) {
case 1:
right_samples = left_samples;
right_samplesPos = left_samplesPos;
break;
case 2:
break;
default:
return GainAnalysis.GAIN_ANALYSIS_ERROR;
}
if (num_samples < GainAnalysis.MAX_ORDER) {
copyArray(left_samples, left_samplesPos, rgData.linprebuf, GainAnalysis.MAX_ORDER, num_samples);
copyArray(right_samples, right_samplesPos, rgData.rinprebuf, GainAnalysis.MAX_ORDER, num_samples);
} else {
copyArray(left_samples, left_samplesPos, rgData.linprebuf, GainAnalysis.MAX_ORDER, GainAnalysis.MAX_ORDER);
copyArray(right_samples, right_samplesPos, rgData.rinprebuf, GainAnalysis.MAX_ORDER, GainAnalysis.MAX_ORDER);
}
while (batchsamples > 0) {
cursamples = batchsamples > rgData.sampleWindow - rgData.totsamp ? rgData.sampleWindow - rgData.totsamp : batchsamples;
if (cursamplepos < GainAnalysis.MAX_ORDER) {
curleft = rgData.linpre + cursamplepos;
curleftBase = rgData.linprebuf;
curright = rgData.rinpre + cursamplepos;
currightBase = rgData.rinprebuf;
if (cursamples > GainAnalysis.MAX_ORDER - cursamplepos) cursamples = GainAnalysis.MAX_ORDER - cursamplepos;
} else {
curleft = left_samplesPos + cursamplepos;
curleftBase = left_samples;
curright = right_samplesPos + cursamplepos;
currightBase = right_samples;
}
this.filterYule(curleftBase, curleft, rgData.lstepbuf, rgData.lstep + rgData.totsamp, cursamples, this.ABYule[rgData.reqindex]);
this.filterYule(currightBase, curright, rgData.rstepbuf, rgData.rstep + rgData.totsamp, cursamples, this.ABYule[rgData.reqindex]);
this.filterButter(
rgData.lstepbuf,
rgData.lstep + rgData.totsamp,
rgData.loutbuf,
rgData.lout + rgData.totsamp,
cursamples,
this.ABButter[rgData.reqindex],
);
this.filterButter(
rgData.rstepbuf,
rgData.rstep + rgData.totsamp,
rgData.routbuf,
rgData.rout + rgData.totsamp,
cursamples,
this.ABButter[rgData.reqindex],
);
curleft = rgData.lout + rgData.totsamp;
curleftBase = rgData.loutbuf;
curright = rgData.rout + rgData.totsamp;
currightBase = rgData.routbuf;
let i = cursamples % 8;
while (i-- !== 0) {
rgData.lsum += fsqr(curleftBase[curleft++]);
rgData.rsum += fsqr(currightBase[curright++]);
}
i = cursamples / 8;
while (i-- !== 0) {
rgData.lsum +=
fsqr(curleftBase[curleft + 0]) +
fsqr(curleftBase[curleft + 1]) +
fsqr(curleftBase[curleft + 2]) +
fsqr(curleftBase[curleft + 3]) +
fsqr(curleftBase[curleft + 4]) +
fsqr(curleftBase[curleft + 5]) +
fsqr(curleftBase[curleft + 6]) +
fsqr(curleftBase[curleft + 7]);
curleft += 8;
rgData.rsum +=
fsqr(currightBase[curright + 0]) +
fsqr(currightBase[curright + 1]) +
fsqr(currightBase[curright + 2]) +
fsqr(currightBase[curright + 3]) +
fsqr(currightBase[curright + 4]) +
fsqr(currightBase[curright + 5]) +
fsqr(currightBase[curright + 6]) +
fsqr(currightBase[curright + 7]);
curright += 8;
}
batchsamples -= cursamples;
cursamplepos += cursamples;
rgData.totsamp += cursamples;
if (rgData.totsamp === rgData.sampleWindow) {
const val = GainAnalysis.STEPS_per_dB * 10 * Math.log10(((rgData.lsum + rgData.rsum) / rgData.totsamp) * 0.5 + 1e-37);
let ival = val <= 0 ? 0 : Math.trunc(val);
if (ival >= rgData.A.length) ival = rgData.A.length - 1;
rgData.A[ival]++;
rgData.lsum = 0;
rgData.rsum = 0;
copyArray(rgData.loutbuf, rgData.totsamp, rgData.loutbuf, 0, GainAnalysis.MAX_ORDER);
copyArray(rgData.routbuf, rgData.totsamp, rgData.routbuf, 0, GainAnalysis.MAX_ORDER);
copyArray(rgData.lstepbuf, rgData.totsamp, rgData.lstepbuf, 0, GainAnalysis.MAX_ORDER);
copyArray(rgData.rstepbuf, rgData.totsamp, rgData.rstepbuf, 0, GainAnalysis.MAX_ORDER);
rgData.totsamp = 0;
}
if (rgData.totsamp > rgData.sampleWindow) {
return GainAnalysis.GAIN_ANALYSIS_ERROR;
}
}
if (num_samples < GainAnalysis.MAX_ORDER) {
copyArray(rgData.linprebuf, num_samples, rgData.linprebuf, 0, GainAnalysis.MAX_ORDER - num_samples);
copyArray(rgData.rinprebuf, num_samples, rgData.rinprebuf, 0, GainAnalysis.MAX_ORDER - num_samples);
copyArray(left_samples, left_samplesPos, rgData.linprebuf, GainAnalysis.MAX_ORDER - num_samples, num_samples);
copyArray(right_samples, right_samplesPos, rgData.rinprebuf, GainAnalysis.MAX_ORDER - num_samples, num_samples);
} else {
copyArray(left_samples, left_samplesPos + num_samples - GainAnalysis.MAX_ORDER, rgData.linprebuf, 0, GainAnalysis.MAX_ORDER);
copyArray(right_samples, right_samplesPos + num_samples - GainAnalysis.MAX_ORDER, rgData.rinprebuf, 0, GainAnalysis.MAX_ORDER);
}
return GainAnalysis.GAIN_ANALYSIS_OK;
}
private analyzeResult(Array: Int32Array, len: number) {
let i;
let elems = 0;
for (i = 0; i < len; i++) elems += Array[i];
if (elems === 0) return GainAnalysis.GAIN_NOT_ENOUGH_SAMPLES;
let upper = Math.ceil(elems * (1 - GainAnalysis.RMS_PERCENTILE));
for (i = len; i-- > 0;) {
if ((upper -= Array[i]) <= 0) break;
}
return GainAnalysis.PINK_REF - i / GainAnalysis.STEPS_per_dB;
}
getTitleGain(rgData: ReplayGain) {
const retval = this.analyzeResult(rgData.A, rgData.A.length);
for (let i = 0; i < rgData.A.length; i++) {
rgData.B[i] += rgData.A[i];
rgData.A[i] = 0;
}
for (let i = 0; i < GainAnalysis.MAX_ORDER; i++) {
rgData.linprebuf[i] = 0;
rgData.lstepbuf[i] = 0;
rgData.loutbuf[i] = 0;
rgData.rinprebuf[i] = 0;
rgData.rstepbuf[i] = 0;
rgData.routbuf[i] = 0;
}
rgData.totsamp = 0;
rgData.lsum = 0;
rgData.rsum = 0;
return retval;
}
}

View File

@@ -0,0 +1,102 @@
import { SFBMAX } from './constants';
export class GrInfo {
xr = new Float32Array(576);
l3_enc = new Int32Array(576);
scalefac = new Int32Array(SFBMAX);
xrpow_max = 0;
part2_3_length = 0;
big_values = 0;
count1 = 0;
global_gain = 0;
scalefac_compress = 0;
block_type = 0;
mixed_block_flag = 0;
table_select = new Int32Array(3);
subblock_gain = new Int32Array(3 + 1);
region0_count = 0;
region1_count = 0;
preflag = 0;
scalefac_scale = 0;
count1table_select = 0;
part2_length = 0;
sfb_lmax = 0;
sfb_smin = 0;
psy_lmax = 0;
sfbmax = 0;
psymax = 0;
sfbdivide = 0;
width = new Int32Array(SFBMAX);
window = new Int32Array(SFBMAX);
count1bits = 0;
sfb_partition_table: readonly number[] | null = null;
slen = new Int32Array(4);
max_nonzero_coeff = 0;
assign(other: GrInfo) {
this.xr = new Float32Array(other.xr);
this.l3_enc = new Int32Array(other.l3_enc);
this.scalefac = new Int32Array(other.scalefac);
this.xrpow_max = other.xrpow_max;
this.part2_3_length = other.part2_3_length;
this.big_values = other.big_values;
this.count1 = other.count1;
this.global_gain = other.global_gain;
this.scalefac_compress = other.scalefac_compress;
this.block_type = other.block_type;
this.mixed_block_flag = other.mixed_block_flag;
this.table_select = new Int32Array(other.table_select);
this.subblock_gain = new Int32Array(other.subblock_gain);
this.region0_count = other.region0_count;
this.region1_count = other.region1_count;
this.preflag = other.preflag;
this.scalefac_scale = other.scalefac_scale;
this.count1table_select = other.count1table_select;
this.part2_length = other.part2_length;
this.sfb_lmax = other.sfb_lmax;
this.sfb_smin = other.sfb_smin;
this.psy_lmax = other.psy_lmax;
this.sfbmax = other.sfbmax;
this.psymax = other.psymax;
this.sfbdivide = other.sfbdivide;
this.width = new Int32Array(other.width);
this.window = new Int32Array(other.window);
this.count1bits = other.count1bits;
this.sfb_partition_table = other.sfb_partition_table !== null ? Array.from(other.sfb_partition_table) : null;
this.slen = new Int32Array(other.slen);
this.max_nonzero_coeff = other.max_nonzero_coeff;
}
}

View File

@@ -0,0 +1,7 @@
export class Header {
write_timing = 0;
ptr = 0;
readonly buf = new Uint8Array(40);
}

View File

@@ -0,0 +1,9 @@
export interface HuffCodeTab {
xlen: number;
linmax: number;
table?: readonly number[];
hlen?: readonly number[];
}

View File

@@ -0,0 +1,15 @@
import { GrInfo } from './GrInfo';
export class IIISideInfo {
tt = Array.from({ length: 2 }, () => Array.from({ length: 2 }, () => new GrInfo()));
main_data_begin = 0;
private_bits = 0;
resvDrain_pre = 0;
resvDrain_post = 0;
scfsi = Array.from({ length: 2 }, () => new Int32Array(4));
}

View File

@@ -0,0 +1,7 @@
import { III_psy_xmin } from './III_psy_xmin';
export class III_psy_ratio {
thm = new III_psy_xmin();
en = new III_psy_xmin();
}

View File

@@ -0,0 +1,17 @@
import { copyArray } from './arrays';
import { SBMAX_l, SBMAX_s } from './constants';
export class III_psy_xmin {
l = new Float32Array(SBMAX_l);
s = Array.from({ length: SBMAX_s }, () => new Float32Array(3));
assign(iii_psy_xmin: Readonly<III_psy_xmin>) {
copyArray(iii_psy_xmin.l, 0, this.l, 0, SBMAX_l);
for (let i = 0; i < SBMAX_s; i++) {
for (let j = 0; j < 3; j++) {
this.s[i][j] = iii_psy_xmin.s[i][j];
}
}
}
}

View File

@@ -0,0 +1,5 @@
export class InOut {
n_in = 0;
n_out = 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
import { LameInternalFlags } from './LameInternalFlags';
import { MPEGMode } from './MPEGMode';
import type { Quality } from './Quality';
import type { ShortBlock } from './ShortBlock';
import { VbrMode } from './VbrMode';
import type { SampleRate } from './sampleRates';
export class LameGlobalFlags {
num_channels: number;
in_samplerate: number;
out_samplerate: SampleRate | 0 = 0;
scale = -1;
quality: Quality = 3;
mode = MPEGMode.STEREO;
brate: number;
compression_ratio = 0;
quant_comp = -1;
quant_comp_short = -1;
experimentalY = false;
exp_nspsytune = 0;
preset = 0;
VBR = VbrMode.vbr_off;
VBR_q: Quality = 4;
VBR_mean_bitrate_kbps = 128;
VBR_min_bitrate_kbps = 0;
VBR_max_bitrate_kbps = 0;
lowpassfreq = 0;
maskingadjust = 0;
maskingadjust_short = 0;
ATHtype = -1;
ATHcurve = -1;
ATHlower = 0;
athaa_loudapprox = -1;
athaa_sensitivity = 0;
short_blocks: ShortBlock | null = null;
interChRatio = -1;
msfix = -1;
version: 0 | 1 = 0;
framesize = 0;
frameNum = 0;
readonly internal_flags = new LameInternalFlags();
constructor(channels: number, samplerate: number, kbps: number) {
this.num_channels = channels;
this.in_samplerate = samplerate;
this.brate = kbps;
}
}

View File

@@ -0,0 +1,214 @@
import { ATH } from './ATH';
import type { CBRNewIterationLoop } from './CBRNewIterationLoop';
import { Header } from './Header';
import { IIISideInfo } from './IIISideInfo';
import { III_psy_xmin } from './III_psy_xmin';
import { NsPsy } from './NsPsy';
import { PSY } from './PSY';
import { ReplayGain } from './ReplayGain';
import { ScaleFac } from './ScaleFac';
import { BPC, CBANDS, ENCDELAY, MAX_HEADER_BUF, MDCTDELAY, MFSIZE, POSTDELAY, SBLIMIT, SBMAX_l, SBMAX_s, SFBMAX } from './constants';
export class LameInternalFlags {
Class_ID = 0;
lame_encode_frame_init = false;
iteration_init_init = false;
fill_buffer_resample_init = false;
readonly mfbuf = Array.from({ length: 2 }, () => new Float32Array(MFSIZE));
mode_gr = 0;
channels_in = 0;
channels_out = 0;
resample_ratio = 1;
mf_samples_to_encode = ENCDELAY + POSTDELAY;
mf_size = ENCDELAY - MDCTDELAY;
VBR_min_bitrate = 1;
VBR_max_bitrate = 13;
bitrate_index = 0;
samplerate_index = 0;
mode_ext = 0;
lowpass1 = 0;
lowpass2 = 0;
highpass1 = 0;
highpass2 = 0;
noise_shaping = 0;
noise_shaping_amp = 0;
substep_shaping = 0;
psymodel = 0;
noise_shaping_stop = 0;
subblock_gain = -1;
use_best_huffman = 0;
full_outer_loop = 0;
readonly l3_side = new IIISideInfo();
padding = 0;
frac_SpF = 0;
slot_lag = 0;
nMusicCRC = 0;
oldValue = new Int32Array([180, 180]);
currentStep = new Int32Array([4, 4]);
masking_lower = 1;
readonly bv_scf = new Int32Array(576);
readonly pseudohalf = new Int32Array(SFBMAX);
sfb21_extra = false;
readonly inbuf_old = Array.from({ length: 2 }, () => new Float32Array());
readonly blackfilt = Array.from({ length: 2 * BPC + 1 }, () => new Float32Array());
readonly itime = new Float64Array(2);
sideinfo_len = 0;
readonly sb_sample = Array.from({ length: 2 }, () =>
Array.from({ length: 2 }, () => Array.from({ length: 18 }, () => new Float32Array(SBLIMIT))),
);
readonly amp_filter = new Float32Array(32);
readonly header = Array.from({ length: MAX_HEADER_BUF }, () => new Header());
h_ptr = 0;
w_ptr = 0;
ResvSize = 0;
readonly scalefac_band = new ScaleFac();
readonly minval_l = new Float32Array(CBANDS);
readonly minval_s = new Float32Array(CBANDS);
readonly nb_1 = Array.from({ length: 4 }, () => new Float32Array(CBANDS));
readonly nb_2 = Array.from({ length: 4 }, () => new Float32Array(CBANDS));
readonly nb_s1 = Array.from({ length: 4 }, () => new Float32Array(CBANDS));
readonly nb_s2 = Array.from({ length: 4 }, () => new Float32Array(CBANDS));
s3_ss: Float32Array | null = null;
s3_ll: Float32Array | null = null;
decay = 0;
readonly thm = Array.from({ length: 4 }, () => new III_psy_xmin());
readonly en = Array.from({ length: 4 }, () => new III_psy_xmin());
readonly tot_ener = new Float32Array(4);
readonly loudness_sq = Array.from({ length: 2 }, () => new Float32Array(2));
readonly loudness_sq_save = new Float32Array(2);
readonly mld_l = new Float32Array(SBMAX_l);
readonly mld_s = new Float32Array(SBMAX_s);
readonly bm_l = new Int32Array(SBMAX_l);
readonly bo_l = new Int32Array(SBMAX_l);
readonly bm_s = new Int32Array(SBMAX_s);
readonly bo_s = new Int32Array(SBMAX_s);
npart_l = 0;
npart_s = 0;
readonly s3ind = Array.from({ length: CBANDS }, () => new Int32Array(2));
readonly s3ind_s = Array.from({ length: CBANDS }, () => new Int32Array(2));
readonly numlines_s = new Int32Array(CBANDS);
readonly numlines_l = new Int32Array(CBANDS);
readonly rnumlines_l = new Float32Array(CBANDS);
readonly mld_cb_l = new Float32Array(CBANDS);
readonly mld_cb_s = new Float32Array(CBANDS);
ms_ener_ratio_old = 0;
readonly blocktype_old = new Int32Array(2);
readonly nsPsy = new NsPsy();
readonly VBR_seek_table = {
nBytesWritten: 0,
};
readonly ATH = new ATH();
readonly PSY = new PSY();
findReplayGain = false;
findPeakSample = false;
PeakSample = 0;
RadioGain = 0;
AudiophileGain = 0;
readonly rgdata = new ReplayGain();
noclipGainChange = 0;
noclipScale = -1.0;
readonly bitrate_stereoMode_Hist = Array.from({ length: 16 }, () => new Int32Array(4 + 1));
readonly bitrate_blockType_Hist = Array.from({ length: 16 }, () => new Int32Array(4 + 1 + 1));
in_buffer_nsamples = 0;
in_buffer_0: Float32Array | null = null;
in_buffer_1: Float32Array | null = null;
iteration_loop: CBRNewIterationLoop | null = null;
}

View File

@@ -0,0 +1,13 @@
export class MPEGMode {
private constructor(public readonly ordinal: number) {}
static readonly STEREO = new MPEGMode(0);
static readonly JOINT_STEREO = new MPEGMode(1);
static readonly DUAL_CHANNEL = new MPEGMode(2);
static readonly MONO = new MPEGMode(3);
static readonly NOT_SET = new MPEGMode(4);
}

View File

@@ -0,0 +1,3 @@
export class MeanBits {
constructor(public bits: number) {}
}

View File

@@ -0,0 +1,90 @@
import { createHash } from 'crypto';
import { readFile } from 'fs/promises';
import { join } from 'path';
import { Mp3Encoder } from './Mp3Encoder';
import { WavHeader } from './WavHeader';
let leftSampleBuffer: ArrayBufferLike;
let rightSampleBuffer: ArrayBufferLike;
beforeAll(async () => {
const leftPath = join('testdata', 'Left44100.wav');
const rightPath = join('testdata', 'Right44100.wav');
leftSampleBuffer = new Uint8Array(await readFile(leftPath)).buffer;
rightSampleBuffer = new Uint8Array(await readFile(rightPath)).buffer;
});
test('mono', async () => {
const waveHeader = WavHeader.readHeader(new DataView(leftSampleBuffer));
const samples = new Int16Array(leftSampleBuffer, waveHeader.dataOffset, waveHeader.dataLen / 2);
const hash = createHash('sha1');
hash.setEncoding('hex');
let remainingSamples = samples.length;
const encoder = new Mp3Encoder();
const maxSamples = 1152;
for (let i = 0; remainingSamples >= maxSamples; i += maxSamples) {
const left = samples.subarray(i, i + maxSamples);
const right = samples.subarray(i, i + maxSamples);
const mp3buf = encoder.encodeBuffer(left, right);
if (mp3buf.length > 0) {
hash.write(Buffer.from(mp3buf));
}
remainingSamples -= maxSamples;
}
const mp3buf = encoder.flush();
if (mp3buf.length > 0) {
hash.write(Buffer.from(mp3buf));
}
hash.end();
expect(hash.read()).toBe('ca9292fc5fea3ba4cb07c4a0ba60cf0c267b783b');
});
test('stereo', async () => {
const leftWaveHeader = WavHeader.readHeader(new DataView(leftSampleBuffer));
const rightWaveHeader = WavHeader.readHeader(new DataView(rightSampleBuffer));
expect(leftWaveHeader.sampleRate).toBe(rightWaveHeader.sampleRate);
const leftSamples = new Int16Array(leftSampleBuffer, leftWaveHeader.dataOffset, leftWaveHeader.dataLen / 2);
const rightSamples = new Int16Array(rightSampleBuffer, rightWaveHeader.dataOffset, rightWaveHeader.dataLen / 2);
expect(leftSamples.length).toBe(rightSamples.length);
const hash = createHash('sha1');
hash.setEncoding('hex');
let remainingSamples = leftSamples.length;
const encoder = new Mp3Encoder(2, leftWaveHeader.sampleRate, 128);
const maxSamples = 1152;
for (let i = 0; remainingSamples >= maxSamples; i += maxSamples) {
const left = leftSamples.subarray(i, i + maxSamples);
const right = rightSamples.subarray(i, i + maxSamples);
const mp3buf = encoder.encodeBuffer(left, right);
if (mp3buf.length > 0) {
hash.write(Buffer.from(mp3buf));
}
remainingSamples -= maxSamples;
}
const mp3buf = encoder.flush();
if (mp3buf.length > 0) {
hash.write(Buffer.from(mp3buf));
}
hash.end();
expect(hash.read()).toBe('ab6daeb1c563389cafacc0ec4ed963ee8ae8e8d7');
});

View File

@@ -0,0 +1,44 @@
import { Lame } from './Lame';
import type { LameGlobalFlags } from './LameGlobalFlags';
export class Mp3Encoder {
private readonly lame: Lame;
private readonly gfp: LameGlobalFlags;
private mp3buf: Uint8Array;
private mp3buf_size: number;
private maxSamples: number;
constructor(channels = 1, samplerate = 44100, kbps = 128) {
this.lame = new Lame();
this.gfp = this.lame.lame_init(channels, samplerate, kbps);
this.maxSamples = 1152;
this.mp3buf_size = Math.trunc(1.25 * this.maxSamples + 7200);
this.mp3buf = new Uint8Array(this.mp3buf_size);
}
encodeBuffer(left: Int16Array, right: Int16Array = left) {
if (left.length !== right.length) {
throw new Error('left and right channel buffers must be the same length');
}
if (left.length > this.maxSamples) {
this.maxSamples = left.length;
this.mp3buf_size = Math.trunc(1.25 * this.maxSamples + 7200);
this.mp3buf = new Uint8Array(this.mp3buf_size);
}
const size = this.lame.lame_encode_buffer(this.gfp, left, right, left.length, this.mp3buf, 0, this.mp3buf_size);
return new Uint8Array(this.mp3buf.subarray(0, size));
}
flush() {
const size = this.lame.lame_encode_flush(this.gfp, this.mp3buf, 0, this.mp3buf_size);
return new Uint8Array(this.mp3buf.subarray(0, size));
}
}

View File

@@ -0,0 +1,950 @@
import type { LameInternalFlags } from './LameInternalFlags';
import { copyArray, fillArray } from './arrays';
import { SHORT_TYPE } from './constants';
export class NewMDCT {
private readonly enwindow = [
(-4.77e-7 * 0.740951125354959) / 2.384e-6,
(1.03951e-4 * 0.740951125354959) / 2.384e-6,
(9.53674e-4 * 0.740951125354959) / 2.384e-6,
(2.841473e-3 * 0.740951125354959) / 2.384e-6,
(3.5758972e-2 * 0.740951125354959) / 2.384e-6,
(3.401756e-3 * 0.740951125354959) / 2.384e-6,
(9.83715e-4 * 0.740951125354959) / 2.384e-6,
(9.9182e-5 * 0.740951125354959) / 2.384e-6,
(1.2398e-5 * 0.740951125354959) / 2.384e-6,
(1.91212e-4 * 0.740951125354959) / 2.384e-6,
(2.283096e-3 * 0.740951125354959) / 2.384e-6,
(1.6994476e-2 * 0.740951125354959) / 2.384e-6,
(-1.8756866e-2 * 0.740951125354959) / 2.384e-6,
(-2.630711e-3 * 0.740951125354959) / 2.384e-6,
(-2.47478e-4 * 0.740951125354959) / 2.384e-6,
(-1.4782e-5 * 0.740951125354959) / 2.384e-6,
9.063471690191471e-1,
1.960342806591213e-1,
(-4.77e-7 * 0.773010453362737) / 2.384e-6,
(1.05858e-4 * 0.773010453362737) / 2.384e-6,
(9.30786e-4 * 0.773010453362737) / 2.384e-6,
(2.521515e-3 * 0.773010453362737) / 2.384e-6,
(3.5694122e-2 * 0.773010453362737) / 2.384e-6,
(3.643036e-3 * 0.773010453362737) / 2.384e-6,
(9.91821e-4 * 0.773010453362737) / 2.384e-6,
(9.6321e-5 * 0.773010453362737) / 2.384e-6,
(1.1444e-5 * 0.773010453362737) / 2.384e-6,
(1.65462e-4 * 0.773010453362737) / 2.384e-6,
(2.110004e-3 * 0.773010453362737) / 2.384e-6,
(1.6112804e-2 * 0.773010453362737) / 2.384e-6,
(-1.9634247e-2 * 0.773010453362737) / 2.384e-6,
(-2.803326e-3 * 0.773010453362737) / 2.384e-6,
(-2.77042e-4 * 0.773010453362737) / 2.384e-6,
(-1.6689e-5 * 0.773010453362737) / 2.384e-6,
8.206787908286602e-1,
3.901806440322567e-1,
(-4.77e-7 * 0.803207531480645) / 2.384e-6,
(1.07288e-4 * 0.803207531480645) / 2.384e-6,
(9.02653e-4 * 0.803207531480645) / 2.384e-6,
(2.174854e-3 * 0.803207531480645) / 2.384e-6,
(3.5586357e-2 * 0.803207531480645) / 2.384e-6,
(3.858566e-3 * 0.803207531480645) / 2.384e-6,
(9.95159e-4 * 0.803207531480645) / 2.384e-6,
(9.346e-5 * 0.803207531480645) / 2.384e-6,
(1.0014e-5 * 0.803207531480645) / 2.384e-6,
(1.4019e-4 * 0.803207531480645) / 2.384e-6,
(1.937389e-3 * 0.803207531480645) / 2.384e-6,
(1.5233517e-2 * 0.803207531480645) / 2.384e-6,
(-2.0506859e-2 * 0.803207531480645) / 2.384e-6,
(-2.974033e-3 * 0.803207531480645) / 2.384e-6,
(-3.0756e-4 * 0.803207531480645) / 2.384e-6,
(-1.812e-5 * 0.803207531480645) / 2.384e-6,
7.416505462720353e-1,
5.805693545089249e-1,
(-4.77e-7 * 0.831469612302545) / 2.384e-6,
(1.08242e-4 * 0.831469612302545) / 2.384e-6,
(8.68797e-4 * 0.831469612302545) / 2.384e-6,
(1.800537e-3 * 0.831469612302545) / 2.384e-6,
(3.54352e-2 * 0.831469612302545) / 2.384e-6,
(4.049301e-3 * 0.831469612302545) / 2.384e-6,
(9.94205e-4 * 0.831469612302545) / 2.384e-6,
(9.0599e-5 * 0.831469612302545) / 2.384e-6,
(9.06e-6 * 0.831469612302545) / 2.384e-6,
(1.16348e-4 * 0.831469612302545) / 2.384e-6,
(1.766682e-3 * 0.831469612302545) / 2.384e-6,
(1.4358521e-2 * 0.831469612302545) / 2.384e-6,
(-2.1372318e-2 * 0.831469612302545) / 2.384e-6,
(-3.14188e-3 * 0.831469612302545) / 2.384e-6,
(-3.39031e-4 * 0.831469612302545) / 2.384e-6,
(-1.955e-5 * 0.831469612302545) / 2.384e-6,
6.681786379192989e-1,
7.653668647301797e-1,
(-4.77e-7 * 0.857728610000272) / 2.384e-6,
(1.08719e-4 * 0.857728610000272) / 2.384e-6,
(8.2922e-4 * 0.857728610000272) / 2.384e-6,
(1.399517e-3 * 0.857728610000272) / 2.384e-6,
(3.5242081e-2 * 0.857728610000272) / 2.384e-6,
(4.21524e-3 * 0.857728610000272) / 2.384e-6,
(9.89437e-4 * 0.857728610000272) / 2.384e-6,
(8.7261e-5 * 0.857728610000272) / 2.384e-6,
(8.106e-6 * 0.857728610000272) / 2.384e-6,
(9.3937e-5 * 0.857728610000272) / 2.384e-6,
(1.597881e-3 * 0.857728610000272) / 2.384e-6,
(1.3489246e-2 * 0.857728610000272) / 2.384e-6,
(-2.2228718e-2 * 0.857728610000272) / 2.384e-6,
(-3.306866e-3 * 0.857728610000272) / 2.384e-6,
(-3.71456e-4 * 0.857728610000272) / 2.384e-6,
(-2.1458e-5 * 0.857728610000272) / 2.384e-6,
5.993769336819237e-1,
9.427934736519954e-1,
(-4.77e-7 * 0.881921264348355) / 2.384e-6,
(1.08719e-4 * 0.881921264348355) / 2.384e-6,
(7.8392e-4 * 0.881921264348355) / 2.384e-6,
(9.71317e-4 * 0.881921264348355) / 2.384e-6,
(3.5007e-2 * 0.881921264348355) / 2.384e-6,
(4.357815e-3 * 0.881921264348355) / 2.384e-6,
(9.80854e-4 * 0.881921264348355) / 2.384e-6,
(8.3923e-5 * 0.881921264348355) / 2.384e-6,
(7.629e-6 * 0.881921264348355) / 2.384e-6,
(7.2956e-5 * 0.881921264348355) / 2.384e-6,
(1.432419e-3 * 0.881921264348355) / 2.384e-6,
(1.2627602e-2 * 0.881921264348355) / 2.384e-6,
(-2.307415e-2 * 0.881921264348355) / 2.384e-6,
(-3.467083e-3 * 0.881921264348355) / 2.384e-6,
(-4.04358e-4 * 0.881921264348355) / 2.384e-6,
(-2.3365e-5 * 0.881921264348355) / 2.384e-6,
5.345111359507916e-1,
1.111140466039205,
(-9.54e-7 * 0.903989293123443) / 2.384e-6,
(1.08242e-4 * 0.903989293123443) / 2.384e-6,
(7.31945e-4 * 0.903989293123443) / 2.384e-6,
(5.15938e-4 * 0.903989293123443) / 2.384e-6,
(3.4730434e-2 * 0.903989293123443) / 2.384e-6,
(4.477024e-3 * 0.903989293123443) / 2.384e-6,
(9.68933e-4 * 0.903989293123443) / 2.384e-6,
(8.0585e-5 * 0.903989293123443) / 2.384e-6,
(6.676e-6 * 0.903989293123443) / 2.384e-6,
(5.2929e-5 * 0.903989293123443) / 2.384e-6,
(1.269817e-3 * 0.903989293123443) / 2.384e-6,
(1.1775017e-2 * 0.903989293123443) / 2.384e-6,
(-2.3907185e-2 * 0.903989293123443) / 2.384e-6,
(-3.622532e-3 * 0.903989293123443) / 2.384e-6,
(-4.38213e-4 * 0.903989293123443) / 2.384e-6,
(-2.5272e-5 * 0.903989293123443) / 2.384e-6,
4.729647758913199e-1,
1.268786568327291,
(-9.54e-7 * 0.9238795325112867) / 2.384e-6,
(1.06812e-4 * 0.9238795325112867) / 2.384e-6,
(6.74248e-4 * 0.9238795325112867) / 2.384e-6,
(3.3379e-5 * 0.9238795325112867) / 2.384e-6,
(3.4412861e-2 * 0.9238795325112867) / 2.384e-6,
(4.573822e-3 * 0.9238795325112867) / 2.384e-6,
(9.54151e-4 * 0.9238795325112867) / 2.384e-6,
(7.6771e-5 * 0.9238795325112867) / 2.384e-6,
(6.199e-6 * 0.9238795325112867) / 2.384e-6,
(3.4332e-5 * 0.9238795325112867) / 2.384e-6,
(1.111031e-3 * 0.9238795325112867) / 2.384e-6,
(1.0933399e-2 * 0.9238795325112867) / 2.384e-6,
(-2.4725437e-2 * 0.9238795325112867) / 2.384e-6,
(-3.771782e-3 * 0.9238795325112867) / 2.384e-6,
(-4.72546e-4 * 0.9238795325112867) / 2.384e-6,
(-2.7657e-5 * 0.9238795325112867) / 2.384e-6,
0.41421356237309503,
1.414213562373095,
(-9.54e-7 * 0.941544065183021) / 2.384e-6,
(1.05381e-4 * 0.941544065183021) / 2.384e-6,
(6.10352e-4 * 0.941544065183021) / 2.384e-6,
(-4.75883e-4 * 0.941544065183021) / 2.384e-6,
(3.405571e-2 * 0.941544065183021) / 2.384e-6,
(4.649162e-3 * 0.941544065183021) / 2.384e-6,
(9.35555e-4 * 0.941544065183021) / 2.384e-6,
(7.3433e-5 * 0.941544065183021) / 2.384e-6,
(5.245e-6 * 0.941544065183021) / 2.384e-6,
(1.7166e-5 * 0.941544065183021) / 2.384e-6,
(9.56535e-4 * 0.941544065183021) / 2.384e-6,
(1.0103703e-2 * 0.941544065183021) / 2.384e-6,
(-2.5527e-2 * 0.941544065183021) / 2.384e-6,
(-3.914356e-3 * 0.941544065183021) / 2.384e-6,
(-5.07355e-4 * 0.941544065183021) / 2.384e-6,
(-3.0041e-5 * 0.941544065183021) / 2.384e-6,
3.578057213145241e-1,
1.546020906725474,
(-9.54e-7 * 0.956940335732209) / 2.384e-6,
(1.0252e-4 * 0.956940335732209) / 2.384e-6,
(5.39303e-4 * 0.956940335732209) / 2.384e-6,
(-1.011848e-3 * 0.956940335732209) / 2.384e-6,
(3.3659935e-2 * 0.956940335732209) / 2.384e-6,
(4.703045e-3 * 0.956940335732209) / 2.384e-6,
(9.15051e-4 * 0.956940335732209) / 2.384e-6,
(7.0095e-5 * 0.956940335732209) / 2.384e-6,
(4.768e-6 * 0.956940335732209) / 2.384e-6,
(9.54e-7 * 0.956940335732209) / 2.384e-6,
(8.06808e-4 * 0.956940335732209) / 2.384e-6,
(9.287834e-3 * 0.956940335732209) / 2.384e-6,
(-2.6310921e-2 * 0.956940335732209) / 2.384e-6,
(-4.048824e-3 * 0.956940335732209) / 2.384e-6,
(-5.42164e-4 * 0.956940335732209) / 2.384e-6,
(-3.2425e-5 * 0.956940335732209) / 2.384e-6,
3.033466836073424e-1,
1.66293922460509,
(-1.431e-6 * 0.970031253194544) / 2.384e-6,
(9.9182e-5 * 0.970031253194544) / 2.384e-6,
(4.62532e-4 * 0.970031253194544) / 2.384e-6,
(-1.573563e-3 * 0.970031253194544) / 2.384e-6,
(3.3225536e-2 * 0.970031253194544) / 2.384e-6,
(4.737377e-3 * 0.970031253194544) / 2.384e-6,
(8.91685e-4 * 0.970031253194544) / 2.384e-6,
(6.628e-5 * 0.970031253194544) / 2.384e-6,
(4.292e-6 * 0.970031253194544) / 2.384e-6,
(-1.3828e-5 * 0.970031253194544) / 2.384e-6,
(6.6185e-4 * 0.970031253194544) / 2.384e-6,
(8.487225e-3 * 0.970031253194544) / 2.384e-6,
(-2.707386e-2 * 0.970031253194544) / 2.384e-6,
(-4.174709e-3 * 0.970031253194544) / 2.384e-6,
(-5.76973e-4 * 0.970031253194544) / 2.384e-6,
(-3.4809e-5 * 0.970031253194544) / 2.384e-6,
2.504869601913055e-1,
1.76384252869671,
(-1.431e-6 * 0.98078528040323) / 2.384e-6,
(9.5367e-5 * 0.98078528040323) / 2.384e-6,
(3.78609e-4 * 0.98078528040323) / 2.384e-6,
(-2.161503e-3 * 0.98078528040323) / 2.384e-6,
(3.2754898e-2 * 0.98078528040323) / 2.384e-6,
(4.752159e-3 * 0.98078528040323) / 2.384e-6,
(8.66413e-4 * 0.98078528040323) / 2.384e-6,
(6.2943e-5 * 0.98078528040323) / 2.384e-6,
(3.815e-6 * 0.98078528040323) / 2.384e-6,
(-2.718e-5 * 0.98078528040323) / 2.384e-6,
(5.22137e-4 * 0.98078528040323) / 2.384e-6,
(7.703304e-3 * 0.98078528040323) / 2.384e-6,
(-2.7815342e-2 * 0.98078528040323) / 2.384e-6,
(-4.290581e-3 * 0.98078528040323) / 2.384e-6,
(-6.11782e-4 * 0.98078528040323) / 2.384e-6,
(-3.767e-5 * 0.98078528040323) / 2.384e-6,
1.98912367379658e-1,
1.847759065022573,
(-1.907e-6 * 0.989176509964781) / 2.384e-6,
(9.0122e-5 * 0.989176509964781) / 2.384e-6,
(2.88486e-4 * 0.989176509964781) / 2.384e-6,
(-2.774239e-3 * 0.989176509964781) / 2.384e-6,
(3.224802e-2 * 0.989176509964781) / 2.384e-6,
(4.748821e-3 * 0.989176509964781) / 2.384e-6,
(8.38757e-4 * 0.989176509964781) / 2.384e-6,
(5.9605e-5 * 0.989176509964781) / 2.384e-6,
(3.338e-6 * 0.989176509964781) / 2.384e-6,
(-3.9577e-5 * 0.989176509964781) / 2.384e-6,
(3.88145e-4 * 0.989176509964781) / 2.384e-6,
(6.937027e-3 * 0.989176509964781) / 2.384e-6,
(-2.8532982e-2 * 0.989176509964781) / 2.384e-6,
(-4.395962e-3 * 0.989176509964781) / 2.384e-6,
(-6.46591e-4 * 0.989176509964781) / 2.384e-6,
(-4.0531e-5 * 0.989176509964781) / 2.384e-6,
1.483359875383474e-1,
1.913880671464418,
(-1.907e-6 * 0.995184726672197) / 2.384e-6,
(8.44e-5 * 0.995184726672197) / 2.384e-6,
(1.91689e-4 * 0.995184726672197) / 2.384e-6,
(-3.411293e-3 * 0.995184726672197) / 2.384e-6,
(3.170681e-2 * 0.995184726672197) / 2.384e-6,
(4.728317e-3 * 0.995184726672197) / 2.384e-6,
(8.09669e-4 * 0.995184726672197) / 2.384e-6,
(5.579e-5 * 0.995184726672197) / 2.384e-6,
(3.338e-6 * 0.995184726672197) / 2.384e-6,
(-5.0545e-5 * 0.995184726672197) / 2.384e-6,
(2.59876e-4 * 0.995184726672197) / 2.384e-6,
(6.189346e-3 * 0.995184726672197) / 2.384e-6,
(-2.9224873e-2 * 0.995184726672197) / 2.384e-6,
(-4.489899e-3 * 0.995184726672197) / 2.384e-6,
(-6.80923e-4 * 0.995184726672197) / 2.384e-6,
(-4.3392e-5 * 0.995184726672197) / 2.384e-6,
9.849140335716425e-2,
1.961570560806461,
(-2.384e-6 * 0.998795456205172) / 2.384e-6,
(7.7724e-5 * 0.998795456205172) / 2.384e-6,
(8.8215e-5 * 0.998795456205172) / 2.384e-6,
(-4.072189e-3 * 0.998795456205172) / 2.384e-6,
(3.1132698e-2 * 0.998795456205172) / 2.384e-6,
(4.691124e-3 * 0.998795456205172) / 2.384e-6,
(7.79152e-4 * 0.998795456205172) / 2.384e-6,
(5.2929e-5 * 0.998795456205172) / 2.384e-6,
(2.861e-6 * 0.998795456205172) / 2.384e-6,
(-6.0558e-5 * 0.998795456205172) / 2.384e-6,
(1.37329e-4 * 0.998795456205172) / 2.384e-6,
(5.46217e-3 * 0.998795456205172) / 2.384e-6,
(-2.989006e-2 * 0.998795456205172) / 2.384e-6,
(-4.570484e-3 * 0.998795456205172) / 2.384e-6,
(-7.14302e-4 * 0.998795456205172) / 2.384e-6,
(-4.6253e-5 * 0.998795456205172) / 2.384e-6,
4.912684976946725e-2,
1.990369453344394,
(3.5780907e-2 * Math.SQRT2 * 0.5) / 2.384e-6,
(1.7876148e-2 * Math.SQRT2 * 0.5) / 2.384e-6,
(3.134727e-3 * Math.SQRT2 * 0.5) / 2.384e-6,
(2.457142e-3 * Math.SQRT2 * 0.5) / 2.384e-6,
(9.71317e-4 * Math.SQRT2 * 0.5) / 2.384e-6,
(2.18868e-4 * Math.SQRT2 * 0.5) / 2.384e-6,
(1.01566e-4 * Math.SQRT2 * 0.5) / 2.384e-6,
(1.3828e-5 * Math.SQRT2 * 0.5) / 2.384e-6,
3.0526638e-2 / 2.384e-6,
4.638195e-3 / 2.384e-6,
7.47204e-4 / 2.384e-6,
4.9591e-5 / 2.384e-6,
4.756451e-3 / 2.384e-6,
2.1458e-5 / 2.384e-6,
-6.9618e-5 / 2.384e-6,
] as const;
private static readonly NS = 12;
private static readonly NL = 36;
private readonly win = [
[
2.382191739347913e-13, 6.423305872147834e-13, 9.400849094049688e-13, 1.122435026096556e-12, 1.183840321267481e-12,
1.122435026096556e-12, 9.40084909404969e-13, 6.423305872147839e-13, 2.382191739347918e-13,
5.456116108943412e-12, 4.878985199565852e-12, 4.240448995017367e-12, 3.559909094758252e-12, 2.858043359288075e-12,
2.156177623817898e-12, 1.475637723558783e-12, 8.371015190102974e-13, 2.599706096327376e-13,
-5.456116108943412e-12, -4.878985199565852e-12, -4.240448995017367e-12, -3.559909094758252e-12, -2.858043359288076e-12,
-2.156177623817898e-12, -1.475637723558783e-12, -8.371015190102975e-13, -2.599706096327376e-13,
-2.382191739347923e-13, -6.423305872147843e-13, -9.400849094049696e-13, -1.122435026096556e-12, -1.183840321267481e-12,
-1.122435026096556e-12, -9.400849094049694e-13, -6.42330587214784e-13, -2.382191739347918e-13,
],
[
2.382191739347913e-13, 6.423305872147834e-13, 9.400849094049688e-13, 1.122435026096556e-12, 1.183840321267481e-12,
1.122435026096556e-12, 9.400849094049688e-13, 6.423305872147841e-13, 2.382191739347918e-13,
5.456116108943413e-12, 4.878985199565852e-12, 4.240448995017367e-12, 3.559909094758253e-12, 2.858043359288075e-12,
2.156177623817898e-12, 1.475637723558782e-12, 8.371015190102975e-13, 2.599706096327376e-13,
-5.461314069809755e-12, -4.921085770524055e-12, -4.343405037091838e-12, -3.732668368707687e-12, -3.093523840190885e-12,
-2.430835727329465e-12, -1.734679010007751e-12, -9.748253656609281e-13, -2.797435120168326e-13,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.283748241799531e-13, -4.037858874020686e-13, -2.146547464825323e-13,
],
[
1.316524975873958e-1, 4.14213562373095e-1, 7.673269879789602e-1,
1.091308501069271, 1.303225372841206, 1.56968557711749, 1.920982126971166, 2.414213562373094, 3.171594802363212, 4.510708503662055,
7.595754112725146, 2.290376554843115e1,
0.98480775301220802032, 0.64278760968653936292, 0.34202014332566882393, 0.93969262078590842791, -0.17364817766693030343,
-0.76604444311897790243, 0.86602540378443870761, 0.5,
-5.144957554275265e-1, -4.717319685649723e-1, -3.133774542039019e-1, -1.819131996109812e-1, -9.457419252642064e-2,
-4.096558288530405e-2, -1.419856857247115e-2, -3.699974673760037e-3,
8.574929257125442e-1, 8.817419973177052e-1, 9.496286491027329e-1, 9.833145924917901e-1, 9.955178160675857e-1, 9.991605581781475e-1,
9.99899195244447e-1, 9.999931550702802e-1,
],
[
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.283748241799531e-13, 4.037858874020686e-13, 2.146547464825323e-13,
5.461314069809755e-12, 4.921085770524055e-12, 4.343405037091838e-12, 3.732668368707687e-12, 3.093523840190885e-12,
2.430835727329466e-12, 1.734679010007751e-12, 9.748253656609281e-13, 2.797435120168326e-13,
-5.456116108943413e-12, -4.878985199565852e-12, -4.240448995017367e-12, -3.559909094758253e-12, -2.858043359288075e-12,
-2.156177623817898e-12, -1.475637723558782e-12, -8.371015190102975e-13, -2.599706096327376e-13,
-2.382191739347913e-13, -6.423305872147834e-13, -9.400849094049688e-13, -1.122435026096556e-12, -1.183840321267481e-12,
-1.122435026096556e-12, -9.400849094049688e-13, -6.423305872147841e-13, -2.382191739347918e-13,
],
] as const;
private readonly tantab_l = this.win[SHORT_TYPE];
private readonly cx = this.win[SHORT_TYPE];
private readonly ca = this.win[SHORT_TYPE];
private readonly cs = this.win[SHORT_TYPE];
private readonly order = [
0, 1, 16, 17, 8, 9, 24, 25, 4, 5, 20, 21, 12, 13, 28, 29, 2, 3, 18, 19, 10, 11, 26, 27, 6, 7, 22, 23, 14, 15, 30, 31,
] as const;
private window_subband(x1: Float32Array, x1Pos: number, a: Float32Array) {
let wp = 10;
let x2 = x1Pos + 238 - 14 - 286;
for (let i = -15; i < 0; i++) {
let w;
let s;
let t;
w = this.enwindow[wp + -10];
s = x1[x2 + -224] * w;
t = x1[x1Pos + 224] * w;
w = this.enwindow[wp + -9];
s += x1[x2 + -160] * w;
t += x1[x1Pos + 160] * w;
w = this.enwindow[wp + -8];
s += x1[x2 + -96] * w;
t += x1[x1Pos + 96] * w;
w = this.enwindow[wp + -7];
s += x1[x2 + -32] * w;
t += x1[x1Pos + 32] * w;
w = this.enwindow[wp + -6];
s += x1[x2 + 32] * w;
t += x1[x1Pos + -32] * w;
w = this.enwindow[wp + -5];
s += x1[x2 + 96] * w;
t += x1[x1Pos + -96] * w;
w = this.enwindow[wp + -4];
s += x1[x2 + 160] * w;
t += x1[x1Pos + -160] * w;
w = this.enwindow[wp + -3];
s += x1[x2 + 224] * w;
t += x1[x1Pos + -224] * w;
w = this.enwindow[wp + -2];
s += x1[x1Pos + -256] * w;
t -= x1[x2 + 256] * w;
w = this.enwindow[wp + -1];
s += x1[x1Pos + -192] * w;
t -= x1[x2 + 192] * w;
w = this.enwindow[wp + 0];
s += x1[x1Pos + -128] * w;
t -= x1[x2 + 128] * w;
w = this.enwindow[wp + 1];
s += x1[x1Pos + -64] * w;
t -= x1[x2 + 64] * w;
w = this.enwindow[wp + 2];
s += x1[x1Pos + 0] * w;
t -= x1[x2 + 0] * w;
w = this.enwindow[wp + 3];
s += x1[x1Pos + 64] * w;
t -= x1[x2 + -64] * w;
w = this.enwindow[wp + 4];
s += x1[x1Pos + 128] * w;
t -= x1[x2 + -128] * w;
w = this.enwindow[wp + 5];
s += x1[x1Pos + 192] * w;
t -= x1[x2 + -192] * w;
s *= this.enwindow[wp + 6];
w = t - s;
a[30 + i * 2] = t + s;
a[31 + i * 2] = this.enwindow[wp + 7] * w;
wp += 18;
x1Pos--;
x2++;
}
let s;
let t;
t = x1[x1Pos + -16] * this.enwindow[wp + -10];
s = x1[x1Pos + -32] * this.enwindow[wp + -2];
t += (x1[x1Pos + -48] - x1[x1Pos + 16]) * this.enwindow[wp + -9];
s += x1[x1Pos + -96] * this.enwindow[wp + -1];
t += (x1[x1Pos + -80] + x1[x1Pos + 48]) * this.enwindow[wp + -8];
s += x1[x1Pos + -160] * this.enwindow[wp + 0];
t += (x1[x1Pos + -112] - x1[x1Pos + 80]) * this.enwindow[wp + -7];
s += x1[x1Pos + -224] * this.enwindow[wp + 1];
t += (x1[x1Pos + -144] + x1[x1Pos + 112]) * this.enwindow[wp + -6];
s -= x1[x1Pos + 32] * this.enwindow[wp + 2];
t += (x1[x1Pos + -176] - x1[x1Pos + 144]) * this.enwindow[wp + -5];
s -= x1[x1Pos + 96] * this.enwindow[wp + 3];
t += (x1[x1Pos + -208] + x1[x1Pos + 176]) * this.enwindow[wp + -4];
s -= x1[x1Pos + 160] * this.enwindow[wp + 4];
t += (x1[x1Pos + -240] - x1[x1Pos + 208]) * this.enwindow[wp + -3];
s -= x1[x1Pos + 224];
const u = s - t;
const v = s + t;
t = a[14];
s = a[15] - t;
a[31] = v + t;
a[30] = u + s;
a[15] = u - s;
a[14] = v - t;
let xr;
xr = a[28] - a[0];
a[0] += a[28];
a[28] = xr * this.enwindow[wp + -2 * 18 + 7];
xr = a[29] - a[1];
a[1] += a[29];
a[29] = xr * this.enwindow[wp + -2 * 18 + 7];
xr = a[26] - a[2];
a[2] += a[26];
a[26] = xr * this.enwindow[wp + -4 * 18 + 7];
xr = a[27] - a[3];
a[3] += a[27];
a[27] = xr * this.enwindow[wp + -4 * 18 + 7];
xr = a[24] - a[4];
a[4] += a[24];
a[24] = xr * this.enwindow[wp + -6 * 18 + 7];
xr = a[25] - a[5];
a[5] += a[25];
a[25] = xr * this.enwindow[wp + -6 * 18 + 7];
xr = a[22] - a[6];
a[6] += a[22];
a[22] = xr * Math.SQRT2;
xr = a[23] - a[7];
a[7] += a[23];
a[23] = xr * Math.SQRT2 - a[7];
a[7] -= a[6];
a[22] -= a[7];
a[23] -= a[22];
xr = a[6];
a[6] = a[31] - xr;
a[31] += xr;
xr = a[7];
a[7] = a[30] - xr;
a[30] += xr;
xr = a[22];
a[22] = a[15] - xr;
a[15] += xr;
xr = a[23];
a[23] = a[14] - xr;
a[14] += xr;
xr = a[20] - a[8];
a[8] += a[20];
a[20] = xr * this.enwindow[wp + -10 * 18 + 7];
xr = a[21] - a[9];
a[9] += a[21];
a[21] = xr * this.enwindow[wp + -10 * 18 + 7];
xr = a[18] - a[10];
a[10] += a[18];
a[18] = xr * this.enwindow[wp + -12 * 18 + 7];
xr = a[19] - a[11];
a[11] += a[19];
a[19] = xr * this.enwindow[wp + -12 * 18 + 7];
xr = a[16] - a[12];
a[12] += a[16];
a[16] = xr * this.enwindow[wp + -14 * 18 + 7];
xr = a[17] - a[13];
a[13] += a[17];
a[17] = xr * this.enwindow[wp + -14 * 18 + 7];
xr = -a[20] + a[24];
a[20] += a[24];
a[24] = xr * this.enwindow[wp + -12 * 18 + 7];
xr = -a[21] + a[25];
a[21] += a[25];
a[25] = xr * this.enwindow[wp + -12 * 18 + 7];
xr = a[4] - a[8];
a[4] += a[8];
a[8] = xr * this.enwindow[wp + -12 * 18 + 7];
xr = a[5] - a[9];
a[5] += a[9];
a[9] = xr * this.enwindow[wp + -12 * 18 + 7];
xr = a[0] - a[12];
a[0] += a[12];
a[12] = xr * this.enwindow[wp + -4 * 18 + 7];
xr = a[1] - a[13];
a[1] += a[13];
a[13] = xr * this.enwindow[wp + -4 * 18 + 7];
xr = a[16] - a[28];
a[16] += a[28];
a[28] = xr * this.enwindow[wp + -4 * 18 + 7];
xr = -a[17] + a[29];
a[17] += a[29];
a[29] = xr * this.enwindow[wp + -4 * 18 + 7];
xr = Math.SQRT2 * (a[2] - a[10]);
a[2] += a[10];
a[10] = xr;
xr = Math.SQRT2 * (a[3] - a[11]);
a[3] += a[11];
a[11] = xr;
xr = Math.SQRT2 * (-a[18] + a[26]);
a[18] += a[26];
a[26] = xr - a[18];
xr = Math.SQRT2 * (-a[19] + a[27]);
a[19] += a[27];
a[27] = xr - a[19];
xr = a[2];
a[19] -= a[3];
a[3] -= xr;
a[2] = a[31] - xr;
a[31] += xr;
xr = a[3];
a[11] -= a[19];
a[18] -= xr;
a[3] = a[30] - xr;
a[30] += xr;
xr = a[18];
a[27] -= a[11];
a[19] -= xr;
a[18] = a[15] - xr;
a[15] += xr;
xr = a[19];
a[10] -= xr;
a[19] = a[14] - xr;
a[14] += xr;
xr = a[10];
a[11] -= xr;
a[10] = a[23] - xr;
a[23] += xr;
xr = a[11];
a[26] -= xr;
a[11] = a[22] - xr;
a[22] += xr;
xr = a[26];
a[27] -= xr;
a[26] = a[7] - xr;
a[7] += xr;
xr = a[27];
a[27] = a[6] - xr;
a[6] += xr;
xr = Math.SQRT2 * (a[0] - a[4]);
a[0] += a[4];
a[4] = xr;
xr = Math.SQRT2 * (a[1] - a[5]);
a[1] += a[5];
a[5] = xr;
xr = Math.SQRT2 * (a[16] - a[20]);
a[16] += a[20];
a[20] = xr;
xr = Math.SQRT2 * (a[17] - a[21]);
a[17] += a[21];
a[21] = xr;
xr = -Math.SQRT2 * (a[8] - a[12]);
a[8] += a[12];
a[12] = xr - a[8];
xr = -Math.SQRT2 * (a[9] - a[13]);
a[9] += a[13];
a[13] = xr - a[9];
xr = -Math.SQRT2 * (a[25] - a[29]);
a[25] += a[29];
a[29] = xr - a[25];
xr = -Math.SQRT2 * (a[24] + a[28]);
a[24] -= a[28];
a[28] = xr - a[24];
xr = a[24] - a[16];
a[24] = xr;
xr = a[20] - xr;
a[20] = xr;
xr = a[28] - xr;
a[28] = xr;
xr = a[25] - a[17];
a[25] = xr;
xr = a[21] - xr;
a[21] = xr;
xr = a[29] - xr;
a[29] = xr;
xr = a[17] - a[1];
a[17] = xr;
xr = a[9] - xr;
a[9] = xr;
xr = a[25] - xr;
a[25] = xr;
xr = a[5] - xr;
a[5] = xr;
xr = a[21] - xr;
a[21] = xr;
xr = a[13] - xr;
a[13] = xr;
xr = a[29] - xr;
a[29] = xr;
xr = a[1] - a[0];
a[1] = xr;
xr = a[16] - xr;
a[16] = xr;
xr = a[17] - xr;
a[17] = xr;
xr = a[8] - xr;
a[8] = xr;
xr = a[9] - xr;
a[9] = xr;
xr = a[24] - xr;
a[24] = xr;
xr = a[25] - xr;
a[25] = xr;
xr = a[4] - xr;
a[4] = xr;
xr = a[5] - xr;
a[5] = xr;
xr = a[20] - xr;
a[20] = xr;
xr = a[21] - xr;
a[21] = xr;
xr = a[12] - xr;
a[12] = xr;
xr = a[13] - xr;
a[13] = xr;
xr = a[28] - xr;
a[28] = xr;
xr = a[29] - xr;
a[29] = xr;
xr = a[0];
a[0] += a[31];
a[31] -= xr;
xr = a[1];
a[1] += a[30];
a[30] -= xr;
xr = a[16];
a[16] += a[15];
a[15] -= xr;
xr = a[17];
a[17] += a[14];
a[14] -= xr;
xr = a[8];
a[8] += a[23];
a[23] -= xr;
xr = a[9];
a[9] += a[22];
a[22] -= xr;
xr = a[24];
a[24] += a[7];
a[7] -= xr;
xr = a[25];
a[25] += a[6];
a[6] -= xr;
xr = a[4];
a[4] += a[27];
a[27] -= xr;
xr = a[5];
a[5] += a[26];
a[26] -= xr;
xr = a[20];
a[20] += a[11];
a[11] -= xr;
xr = a[21];
a[21] += a[10];
a[10] -= xr;
xr = a[12];
a[12] += a[19];
a[19] -= xr;
xr = a[13];
a[13] += a[18];
a[18] -= xr;
xr = a[28];
a[28] += a[3];
a[3] -= xr;
xr = a[29];
a[29] += a[2];
a[2] -= xr;
}
private mdct_short(inout: Float32Array, inoutPos: number) {
for (let l = 0; l < 3; l++) {
let tc0;
let tc1;
let tc2;
let ts0;
let ts1;
let ts2;
ts0 = inout[inoutPos + 2 * 3] * this.win[SHORT_TYPE][0] - inout[inoutPos + 5 * 3];
tc0 = inout[inoutPos + 0 * 3] * this.win[SHORT_TYPE][2] - inout[inoutPos + 3 * 3];
tc1 = ts0 + tc0;
tc2 = ts0 - tc0;
ts0 = inout[inoutPos + 5 * 3] * this.win[SHORT_TYPE][0] + inout[inoutPos + 2 * 3];
tc0 = inout[inoutPos + 3 * 3] * this.win[SHORT_TYPE][2] + inout[inoutPos + 0 * 3];
ts1 = ts0 + tc0;
ts2 = -ts0 + tc0;
tc0 = (inout[inoutPos + 1 * 3] * this.win[SHORT_TYPE][1] - inout[inoutPos + 4 * 3]) * 2.069978111953089e-11;
ts0 = (inout[inoutPos + 4 * 3] * this.win[SHORT_TYPE][1] + inout[inoutPos + 1 * 3]) * 2.069978111953089e-11;
inout[inoutPos + 3 * 0] = tc1 * 1.90752519173728e-11 + tc0;
inout[inoutPos + 3 * 5] = -ts1 * 1.90752519173728e-11 + ts0;
tc2 = tc2 * 0.86602540378443870761 * 1.907525191737281e-11;
ts1 = ts1 * 0.5 * 1.907525191737281e-11 + ts0;
inout[inoutPos + 3 * 1] = tc2 - ts1;
inout[inoutPos + 3 * 2] = tc2 + ts1;
tc1 = tc1 * 0.5 * 1.907525191737281e-11 - tc0;
ts2 = ts2 * 0.86602540378443870761 * 1.907525191737281e-11;
inout[inoutPos + 3 * 3] = tc1 + ts2;
inout[inoutPos + 3 * 4] = tc1 - ts2;
inoutPos++;
}
}
private mdct_long(out: Float32Array, outPos: number, _in: Float32Array) {
let ct;
let st;
{
const tc1 = _in[17] - _in[9];
const tc3 = _in[15] - _in[11];
const tc4 = _in[14] - _in[12];
const ts5 = _in[0] + _in[8];
let ts6 = _in[1] + _in[7];
const ts7 = _in[2] + _in[6];
const ts8 = _in[3] + _in[5];
out[outPos + 17] = ts5 + ts7 - ts8 - (ts6 - _in[4]);
st = (ts5 + ts7 - ts8) * this.cx[12 + 7] + (ts6 - _in[4]);
ct = (tc1 - tc3 - tc4) * this.cx[12 + 6];
out[outPos + 5] = ct + st;
out[outPos + 6] = ct - st;
const tc2 = (_in[16] - _in[10]) * this.cx[12 + 6];
ts6 = ts6 * this.cx[12 + 7] + _in[4];
ct = tc1 * this.cx[12 + 0] + tc2 + tc3 * this.cx[12 + 1] + tc4 * this.cx[12 + 2];
st = -ts5 * this.cx[12 + 4] + ts6 - ts7 * this.cx[12 + 5] + ts8 * this.cx[12 + 3];
out[outPos + 1] = ct + st;
out[outPos + 2] = ct - st;
ct = tc1 * this.cx[12 + 1] - tc2 - tc3 * this.cx[12 + 2] + tc4 * this.cx[12 + 0];
st = -ts5 * this.cx[12 + 5] + ts6 - ts7 * this.cx[12 + 3] + ts8 * this.cx[12 + 4];
out[outPos + 9] = ct + st;
out[outPos + 10] = ct - st;
ct = tc1 * this.cx[12 + 2] - tc2 + tc3 * this.cx[12 + 0] - tc4 * this.cx[12 + 1];
st = ts5 * this.cx[12 + 3] - ts6 + ts7 * this.cx[12 + 4] - ts8 * this.cx[12 + 5];
out[outPos + 13] = ct + st;
out[outPos + 14] = ct - st;
}
{
const ts1 = _in[8] - _in[0];
const ts3 = _in[6] - _in[2];
const ts4 = _in[5] - _in[3];
const tc5 = _in[17] + _in[9];
let tc6 = _in[16] + _in[10];
const tc7 = _in[15] + _in[11];
const tc8 = _in[14] + _in[12];
out[outPos + 0] = tc5 + tc7 + tc8 + (tc6 + _in[13]);
ct = (tc5 + tc7 + tc8) * this.cx[12 + 7] - (tc6 + _in[13]);
st = (ts1 - ts3 + ts4) * this.cx[12 + 6];
out[outPos + 11] = ct + st;
out[outPos + 12] = ct - st;
const ts2 = (_in[7] - _in[1]) * this.cx[12 + 6];
tc6 = _in[13] - tc6 * this.cx[12 + 7];
ct = tc5 * this.cx[12 + 3] - tc6 + tc7 * this.cx[12 + 4] + tc8 * this.cx[12 + 5];
st = ts1 * this.cx[12 + 2] + ts2 + ts3 * this.cx[12 + 0] + ts4 * this.cx[12 + 1];
out[outPos + 3] = ct + st;
out[outPos + 4] = ct - st;
ct = -tc5 * this.cx[12 + 5] + tc6 - tc7 * this.cx[12 + 3] - tc8 * this.cx[12 + 4];
st = ts1 * this.cx[12 + 1] + ts2 - ts3 * this.cx[12 + 2] - ts4 * this.cx[12 + 0];
out[outPos + 7] = ct + st;
out[outPos + 8] = ct - st;
ct = -tc5 * this.cx[12 + 4] + tc6 - tc7 * this.cx[12 + 5] - tc8 * this.cx[12 + 3];
st = ts1 * this.cx[12 + 0] - ts2 + ts3 * this.cx[12 + 1] - ts4 * this.cx[12 + 2];
out[outPos + 15] = ct + st;
out[outPos + 16] = ct - st;
}
}
mdct_sub48(gfc: LameInternalFlags, w0: Float32Array, w1: Float32Array) {
let wk = w0;
let wkPos = 286;
for (let ch = 0; ch < gfc.channels_out; ch++) {
for (let gr = 0; gr < gfc.mode_gr; gr++) {
let band;
const gi = gfc.l3_side.tt[gr][ch];
const mdct_enc = gi.xr;
let mdct_encPos = 0;
const samp = gfc.sb_sample[ch][1 - gr];
let sampPos = 0;
for (let k = 0; k < 18 / 2; k++) {
this.window_subband(wk, wkPos, samp[sampPos]);
this.window_subband(wk, wkPos + 32, samp[sampPos + 1]);
sampPos += 2;
wkPos += 64;
for (band = 1; band < 32; band += 2) {
samp[sampPos - 1][band] *= -1;
}
}
for (band = 0; band < 32; band++, mdct_encPos += 18) {
let type = gi.block_type;
const band0 = gfc.sb_sample[ch][gr];
const band1 = gfc.sb_sample[ch][1 - gr];
if (gi.mixed_block_flag !== 0 && band < 2) type = 0;
if (gfc.amp_filter[band] < 1e-12) {
fillArray(mdct_enc, mdct_encPos + 0, mdct_encPos + 18, 0);
} else {
if (gfc.amp_filter[band] < 1.0) {
for (let k = 0; k < 18; k++) band1[k][this.order[band]] *= gfc.amp_filter[band];
}
if (type === SHORT_TYPE) {
for (let k = -NewMDCT.NS / 4; k < 0; k++) {
const w = this.win[SHORT_TYPE][k + 3];
mdct_enc[mdct_encPos + k * 3 + 9] = band0[9 + k][this.order[band]] * w - band0[8 - k][this.order[band]];
mdct_enc[mdct_encPos + k * 3 + 18] = band0[14 - k][this.order[band]] * w + band0[15 + k][this.order[band]];
mdct_enc[mdct_encPos + k * 3 + 10] = band0[15 + k][this.order[band]] * w - band0[14 - k][this.order[band]];
mdct_enc[mdct_encPos + k * 3 + 19] = band1[2 - k][this.order[band]] * w + band1[3 + k][this.order[band]];
mdct_enc[mdct_encPos + k * 3 + 11] = band1[3 + k][this.order[band]] * w - band1[2 - k][this.order[band]];
mdct_enc[mdct_encPos + k * 3 + 20] = band1[8 - k][this.order[band]] * w + band1[9 + k][this.order[band]];
}
this.mdct_short(mdct_enc, mdct_encPos);
} else {
const work = new Float32Array(18);
for (let k = -NewMDCT.NL / 4; k < 0; k++) {
const a = this.win[type][k + 27] * band1[k + 9][this.order[band]] + this.win[type][k + 36] * band1[8 - k][this.order[band]];
const b = this.win[type][k + 9] * band0[k + 9][this.order[band]] - this.win[type][k + 18] * band0[8 - k][this.order[band]];
work[k + 9] = a - b * this.tantab_l[3 + k + 9];
work[k + 18] = a * this.tantab_l[3 + k + 9] + b;
}
this.mdct_long(mdct_enc, mdct_encPos, work);
}
}
if (type !== SHORT_TYPE && band !== 0) {
for (let k = 7; k >= 0; --k) {
const bu = mdct_enc[mdct_encPos + k] * this.ca[20 + k] + mdct_enc[mdct_encPos + -1 - k] * this.cs[28 + k];
const bd = mdct_enc[mdct_encPos + k] * this.cs[28 + k] - mdct_enc[mdct_encPos + -1 - k] * this.ca[20 + k];
mdct_enc[mdct_encPos + -1 - k] = bu;
mdct_enc[mdct_encPos + k] = bd;
}
}
}
}
wk = w1;
wkPos = 286;
if (gfc.mode_gr === 1) {
for (let i = 0; i < 18; i++) {
copyArray(gfc.sb_sample[ch][1][i], 0, gfc.sb_sample[ch][0][i], 0, 32);
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
import { SBMAX_l, SBMAX_s } from './constants';
export class NsPsy {
last_en_subshort = Array.from({ length: 4 }, () => new Float32Array(9));
lastAttacks = new Int32Array(4);
pefirbuf = new Float32Array(19);
longfact = new Float32Array(SBMAX_l);
shortfact = new Float32Array(SBMAX_s);
attackthre = -1;
attackthre_s = -1;
}

View File

@@ -0,0 +1,3 @@
export class NumUsed {
num_used = 0;
}

View File

@@ -0,0 +1,11 @@
import { SBMAX_l, SBMAX_s } from './constants';
export class PSY {
mask_adjust = 0;
mask_adjust_short = 0;
readonly bo_l_weight = new Float32Array(SBMAX_l);
readonly bo_s_weight = new Float32Array(SBMAX_s);
}

View File

@@ -0,0 +1,126 @@
import { ABRPresets } from './ABRPresets';
import type { LameGlobalFlags } from './LameGlobalFlags';
import type { Quality } from './Quality';
import { VBRPresets } from './VBRPresets';
import { VbrMode } from './VbrMode';
const enum Preset {
V9 = 410,
V8 = 420,
V7 = 430,
V6 = 440,
V5 = 450,
V4 = 460,
V3 = 470,
V2 = 480,
V1 = 490,
V0 = 500,
R3MIX = 1000,
STANDARD = 1001,
EXTREME = 1002,
INSANE = 1003,
STANDARD_FAST = 1004,
EXTREME_FAST = 1005,
MEDIUM = 1006,
MEDIUM_FAST = 1007,
}
export class Presets {
private readonly vbrPresets = new VBRPresets();
public readonly abrPresets = new ABRPresets();
apply(gfp: LameGlobalFlags, preset: Preset) {
switch (preset) {
case Preset.R3MIX: {
preset = Preset.V3;
gfp.VBR = VbrMode.vbr_mtrh;
break;
}
case Preset.MEDIUM: {
preset = Preset.V4;
gfp.VBR = VbrMode.vbr_rh;
break;
}
case Preset.MEDIUM_FAST: {
preset = Preset.V4;
gfp.VBR = VbrMode.vbr_mtrh;
break;
}
case Preset.STANDARD: {
preset = Preset.V2;
gfp.VBR = VbrMode.vbr_rh;
break;
}
case Preset.STANDARD_FAST: {
preset = Preset.V2;
gfp.VBR = VbrMode.vbr_mtrh;
break;
}
case Preset.EXTREME: {
preset = Preset.V0;
gfp.VBR = VbrMode.vbr_rh;
break;
}
case Preset.EXTREME_FAST: {
preset = Preset.V0;
gfp.VBR = VbrMode.vbr_mtrh;
break;
}
case Preset.INSANE: {
gfp.preset = 320;
gfp.VBR = VbrMode.vbr_off;
this.abrPresets.apply(gfp, 320);
return 320;
}
}
gfp.preset = preset;
switch (preset) {
case Preset.V9:
this.vbrPresets.apply(gfp, 9);
return preset;
case Preset.V8:
this.vbrPresets.apply(gfp, 8);
return preset;
case Preset.V7:
this.vbrPresets.apply(gfp, 7);
return preset;
case Preset.V6:
this.vbrPresets.apply(gfp, 6);
return preset;
case Preset.V5:
this.vbrPresets.apply(gfp, 5);
return preset;
case Preset.V4:
this.vbrPresets.apply(gfp, 4);
return preset;
case Preset.V3:
this.vbrPresets.apply(gfp, 3);
return preset;
case Preset.V2:
this.vbrPresets.apply(gfp, 2);
return preset;
case Preset.V1:
this.vbrPresets.apply(gfp, 1);
return preset;
case Preset.V0:
this.vbrPresets.apply(gfp, 0);
return preset;
default:
break;
}
if (preset >= 8 && preset <= 320) {
return this.abrPresets.apply(gfp, preset);
}
gfp.preset = 0;
return preset;
}
applyPresetFromQuality(gfp: LameGlobalFlags, quality: Quality) {
return this.apply(gfp, 500 - 10 * quality);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
export type Quality = 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0;

View File

@@ -0,0 +1,862 @@
import type { BitStream } from './BitStream';
import { CalcNoiseData } from './CalcNoiseData';
import { CalcNoiseResult } from './CalcNoiseResult';
import { GrInfo } from './GrInfo';
import type { IIISideInfo } from './IIISideInfo';
import type { LameGlobalFlags } from './LameGlobalFlags';
import type { LameInternalFlags } from './LameInternalFlags';
import { PsyModel } from './PsyModel';
import { QuantizePVT } from './QuantizePVT';
import { Reservoir } from './Reservoir';
import { StartLine } from './StartLine';
import { Takehiro } from './Takehiro';
import { VbrMode } from './VbrMode';
import { copyArray, fillArray, sortArray } from './arrays';
import { assert } from './assert';
import { PSFB12, PSFB21, SBMAX_l, SBMAX_s, SBPSY_l, SBPSY_s, SFBMAX, SHORT_TYPE } from './constants';
import { isCloseToEachOther } from './math';
export class Quantize {
readonly rv: Reservoir;
readonly qupvt: QuantizePVT;
readonly tak: Takehiro;
constructor(bs: BitStream) {
this.rv = new Reservoir(bs);
this.qupvt = new QuantizePVT(new PsyModel());
this.tak = new Takehiro(this.qupvt);
}
ms_convert(l3_side: IIISideInfo, gr: number) {
for (let i = 0; i < 576; ++i) {
const l = l3_side.tt[gr][0].xr[i];
const r = l3_side.tt[gr][1].xr[i];
l3_side.tt[gr][0].xr[i] = (l + r) * (Math.SQRT2 * 0.5);
l3_side.tt[gr][1].xr[i] = (l - r) * (Math.SQRT2 * 0.5);
}
}
private init_xrpow_core(cod_info: GrInfo, xrpow: Float32Array, upper: number, sum: number) {
sum = 0;
for (let i = 0; i <= upper; ++i) {
const tmp = Math.abs(cod_info.xr[i]);
sum += tmp;
xrpow[i] = Math.sqrt(tmp * Math.sqrt(tmp));
if (xrpow[i] > cod_info.xrpow_max) cod_info.xrpow_max = xrpow[i];
}
return sum;
}
init_xrpow(gfc: LameInternalFlags, cod_info: GrInfo, xrpow: Float32Array) {
let sum = 0;
const upper = Math.trunc(cod_info.max_nonzero_coeff);
assert(xrpow !== null);
cod_info.xrpow_max = 0;
assert(upper >= 0 && upper <= 575);
fillArray(xrpow, upper, 576, 0);
sum = this.init_xrpow_core(cod_info, xrpow, upper, sum);
if (sum > 1e-20) {
let j = 0;
if ((gfc.substep_shaping & 2) !== 0) j = 1;
for (let i = 0; i < cod_info.psymax; i++) gfc.pseudohalf[i] = j;
return true;
}
fillArray(cod_info.l3_enc, 0, 576, 0);
return false;
}
private psfb21_analogsilence(gfc: LameInternalFlags, cod_info: GrInfo) {
const ath = gfc.ATH;
const { xr } = cod_info;
if (cod_info.block_type !== SHORT_TYPE) {
let stop = false;
for (let gsfb = PSFB21 - 1; gsfb >= 0 && !stop; gsfb--) {
const start = gfc.scalefac_band.psfb21[gsfb];
const end = gfc.scalefac_band.psfb21[gsfb + 1];
let ath21 = this.qupvt.athAdjust(ath.adjust, ath.psfb21[gsfb], ath.floor);
if (gfc.nsPsy.longfact[21] > 1e-12) ath21 *= gfc.nsPsy.longfact[21];
for (let j = end - 1; j >= start; j--) {
if (Math.abs(xr[j]) < ath21) xr[j] = 0;
else {
stop = true;
break;
}
}
}
} else {
for (let block = 0; block < 3; block++) {
let stop = false;
for (let gsfb = PSFB12 - 1; gsfb >= 0 && !stop; gsfb--) {
const start =
gfc.scalefac_band.s[12] * 3 +
(gfc.scalefac_band.s[13] - gfc.scalefac_band.s[12]) * block +
(gfc.scalefac_band.psfb12[gsfb] - gfc.scalefac_band.psfb12[0]);
const end = start + (gfc.scalefac_band.psfb12[gsfb + 1] - gfc.scalefac_band.psfb12[gsfb]);
let ath12 = this.qupvt.athAdjust(ath.adjust, ath.psfb12[gsfb], ath.floor);
if (gfc.nsPsy.shortfact[12] > 1e-12) ath12 *= gfc.nsPsy.shortfact[12];
for (let j = end - 1; j >= start; j--) {
if (Math.abs(xr[j]) < ath12) xr[j] = 0;
else {
stop = true;
break;
}
}
}
}
}
}
init_outer_loop(gfc: LameInternalFlags, cod_info: GrInfo) {
cod_info.part2_3_length = 0;
cod_info.big_values = 0;
cod_info.count1 = 0;
cod_info.global_gain = 210;
cod_info.scalefac_compress = 0;
cod_info.table_select[0] = 0;
cod_info.table_select[1] = 0;
cod_info.table_select[2] = 0;
cod_info.subblock_gain[0] = 0;
cod_info.subblock_gain[1] = 0;
cod_info.subblock_gain[2] = 0;
cod_info.subblock_gain[3] = 0;
cod_info.region0_count = 0;
cod_info.region1_count = 0;
cod_info.preflag = 0;
cod_info.scalefac_scale = 0;
cod_info.count1table_select = 0;
cod_info.part2_length = 0;
cod_info.sfb_lmax = SBPSY_l;
cod_info.sfb_smin = SBPSY_s;
cod_info.psy_lmax = gfc.sfb21_extra ? SBMAX_l : SBPSY_l;
cod_info.psymax = cod_info.psy_lmax;
cod_info.sfbmax = cod_info.sfb_lmax;
cod_info.sfbdivide = 11;
for (let sfb = 0; sfb < SBMAX_l; sfb++) {
cod_info.width[sfb] = gfc.scalefac_band.l[sfb + 1] - gfc.scalefac_band.l[sfb];
cod_info.window[sfb] = 3;
}
if (cod_info.block_type === SHORT_TYPE) {
const ixwork = new Float32Array(576);
cod_info.sfb_smin = 0;
cod_info.sfb_lmax = 0;
if (cod_info.mixed_block_flag !== 0) {
cod_info.sfb_smin = 3;
cod_info.sfb_lmax = gfc.mode_gr * 2 + 4;
}
cod_info.psymax = cod_info.sfb_lmax + 3 * ((gfc.sfb21_extra ? SBMAX_s : SBPSY_s) - cod_info.sfb_smin);
cod_info.sfbmax = cod_info.sfb_lmax + 3 * (SBPSY_s - cod_info.sfb_smin);
cod_info.sfbdivide = cod_info.sfbmax - 18;
cod_info.psy_lmax = cod_info.sfb_lmax;
let ix = gfc.scalefac_band.l[cod_info.sfb_lmax];
copyArray(cod_info.xr, 0, ixwork, 0, 576);
for (let sfb = cod_info.sfb_smin; sfb < SBMAX_s; sfb++) {
const start = gfc.scalefac_band.s[sfb];
const end = gfc.scalefac_band.s[sfb + 1];
for (let window = 0; window < 3; window++) {
for (let l = start; l < end; l++) {
cod_info.xr[ix++] = ixwork[3 * l + window];
}
}
}
let j = cod_info.sfb_lmax;
for (let sfb = cod_info.sfb_smin; sfb < SBMAX_s; sfb++) {
const y = gfc.scalefac_band.s[sfb + 1] - gfc.scalefac_band.s[sfb];
cod_info.width[j] = y;
cod_info.width[j + 1] = y;
cod_info.width[j + 2] = y;
cod_info.window[j] = 0;
cod_info.window[j + 1] = 1;
cod_info.window[j + 2] = 2;
j += 3;
}
}
cod_info.count1bits = 0;
cod_info.sfb_partition_table = this.qupvt.nr_of_sfb_block[0][0];
cod_info.slen[0] = 0;
cod_info.slen[1] = 0;
cod_info.slen[2] = 0;
cod_info.slen[3] = 0;
cod_info.max_nonzero_coeff = 575;
fillArray(cod_info.scalefac, 0);
this.psfb21_analogsilence(gfc, cod_info);
}
private bin_search_StepSize(gfc: LameInternalFlags, cod_info: GrInfo, desired_rate: number, ch: number, xrpow: Float32Array) {
const enum BinSearchDirection {
BINSEARCH_NONE,
BINSEARCH_UP,
BINSEARCH_DOWN,
}
let nBits;
let CurrentStep = gfc.currentStep[ch];
let flagGoneOver = false;
const start = gfc.oldValue[ch];
let direction = BinSearchDirection.BINSEARCH_NONE;
cod_info.global_gain = start;
desired_rate -= cod_info.part2_length;
assert(CurrentStep !== 0);
for (;;) {
let step;
nBits = this.tak.count_bits(gfc, xrpow, cod_info, null);
if (CurrentStep === 1 || nBits === desired_rate) break;
if (nBits > desired_rate) {
if (direction === BinSearchDirection.BINSEARCH_DOWN) flagGoneOver = true;
if (flagGoneOver) CurrentStep /= 2;
direction = BinSearchDirection.BINSEARCH_UP;
step = CurrentStep;
} else {
if (direction === BinSearchDirection.BINSEARCH_UP) flagGoneOver = true;
if (flagGoneOver) CurrentStep /= 2;
direction = BinSearchDirection.BINSEARCH_DOWN;
step = -CurrentStep;
}
cod_info.global_gain += step;
if (cod_info.global_gain < 0) {
cod_info.global_gain = 0;
flagGoneOver = true;
}
if (cod_info.global_gain > 255) {
cod_info.global_gain = 255;
flagGoneOver = true;
}
}
assert(cod_info.global_gain >= 0);
assert(cod_info.global_gain < 256);
while (nBits > desired_rate && cod_info.global_gain < 255) {
cod_info.global_gain++;
nBits = this.tak.count_bits(gfc, xrpow, cod_info, null);
}
gfc.currentStep[ch] = start - cod_info.global_gain >= 4 ? 4 : 2;
gfc.oldValue[ch] = cod_info.global_gain;
cod_info.part2_3_length = nBits;
return nBits;
}
trancate_smallspectrums(gfc: LameInternalFlags, gi: GrInfo, l3_xmin: Float32Array, work: Float32Array) {
const distort = new Float32Array(SFBMAX);
if (((gfc.substep_shaping & 4) === 0 && gi.block_type === SHORT_TYPE) || (gfc.substep_shaping & 0x80) !== 0) return;
this.calc_noise(gi, l3_xmin, distort, new CalcNoiseResult(), null);
for (let j = 0; j < 576; j++) {
let xr = 0.0;
if (gi.l3_enc[j] !== 0) xr = Math.abs(gi.xr[j]);
work[j] = xr;
}
let j = 0;
let sfb = 8;
if (gi.block_type === SHORT_TYPE) sfb = 6;
do {
let allowedNoise;
let trancateThreshold;
let nsame;
let start;
let width = gi.width[sfb];
j += width;
if (distort[sfb] >= 1.0) continue;
sortArray(work, j - width, width);
if (isCloseToEachOther(work[j - 1], 0.0)) continue;
allowedNoise = (1.0 - distort[sfb]) * l3_xmin[sfb];
trancateThreshold = 0.0;
start = 0;
do {
for (nsame = 1; start + nsame < width; nsame++)
if (!isCloseToEachOther(work[start + j - width], work[start + j + nsame - width])) break;
const noise = work[start + j - width] * work[start + j - width] * nsame;
if (allowedNoise < noise) {
if (start !== 0) trancateThreshold = work[start + j - width - 1];
break;
}
allowedNoise -= noise;
start += nsame;
} while (start < width);
if (isCloseToEachOther(trancateThreshold, 0.0)) continue;
do {
if (Math.abs(gi.xr[j - width]) <= trancateThreshold) gi.l3_enc[j - width] = 0;
} while (--width > 0);
} while (++sfb < gi.psymax);
gi.part2_3_length = this.tak.noquant_count_bits(gfc, gi, null);
}
private loop_break(cod_info: GrInfo) {
for (let sfb = 0; sfb < cod_info.sfbmax; sfb++)
if (cod_info.scalefac[sfb] + cod_info.subblock_gain[cod_info.window[sfb]] === 0) return false;
return true;
}
private penalties(noise: number) {
return Math.log10(0.368 + 0.632 * noise * noise * noise);
}
private get_klemm_noise(distort: Float32Array, gi: GrInfo) {
let klemm_noise = 1e-37;
for (let sfb = 0; sfb < gi.psymax; sfb++) klemm_noise += this.penalties(distort[sfb]);
return Math.max(1e-20, klemm_noise);
}
// eslint-disable-next-line complexity
private quant_compare(quant_comp: number, best: CalcNoiseResult, calc: CalcNoiseResult, gi: GrInfo, distort: Float32Array) {
let better;
switch (quant_comp) {
default:
case 9: {
if (best.over_count > 0) {
better = calc.over_SSD <= best.over_SSD;
if (calc.over_SSD === best.over_SSD) better = calc.bits < best.bits;
} else {
better = calc.max_noise < 0 && calc.max_noise * 10 + calc.bits <= best.max_noise * 10 + best.bits;
}
break;
}
case 0:
better =
calc.over_count < best.over_count ||
(calc.over_count === best.over_count && calc.over_noise < best.over_noise) ||
(calc.over_count === best.over_count && isCloseToEachOther(calc.over_noise, best.over_noise) && calc.tot_noise < best.tot_noise);
break;
case 8:
calc.max_noise = this.get_klemm_noise(distort, gi);
// eslint-disable-next-line no-fallthrough
case 1:
better = calc.max_noise < best.max_noise;
break;
case 2:
better = calc.tot_noise < best.tot_noise;
break;
case 3:
better = calc.tot_noise < best.tot_noise && calc.max_noise < best.max_noise;
break;
case 4:
better =
(calc.max_noise <= 0.0 && best.max_noise > 0.2) ||
(calc.max_noise <= 0.0 && best.max_noise < 0.0 && best.max_noise > calc.max_noise - 0.2 && calc.tot_noise < best.tot_noise) ||
(calc.max_noise <= 0.0 &&
best.max_noise > 0.0 &&
best.max_noise > calc.max_noise - 0.2 &&
calc.tot_noise < best.tot_noise + best.over_noise) ||
(calc.max_noise > 0.0 &&
best.max_noise > -0.05 &&
best.max_noise > calc.max_noise - 0.1 &&
calc.tot_noise + calc.over_noise < best.tot_noise + best.over_noise) ||
(calc.max_noise > 0.0 &&
best.max_noise > -0.1 &&
best.max_noise > calc.max_noise - 0.15 &&
calc.tot_noise + calc.over_noise + calc.over_noise < best.tot_noise + best.over_noise + best.over_noise);
break;
case 5:
better =
calc.over_noise < best.over_noise || (isCloseToEachOther(calc.over_noise, best.over_noise) && calc.tot_noise < best.tot_noise);
break;
case 6:
better =
calc.over_noise < best.over_noise ||
(isCloseToEachOther(calc.over_noise, best.over_noise) &&
(calc.max_noise < best.max_noise || (isCloseToEachOther(calc.max_noise, best.max_noise) && calc.tot_noise <= best.tot_noise)));
break;
case 7:
better = calc.over_count < best.over_count || calc.over_noise < best.over_noise;
break;
}
if (best.over_count === 0) {
better = better && calc.bits < best.bits;
}
return better;
}
private amp_scalefac_bands(gfp: LameGlobalFlags, cod_info: GrInfo, distort: Float32Array, xrpow: Float32Array, bRefine: boolean) {
const gfc = gfp.internal_flags;
let ifqstep34;
if (cod_info.scalefac_scale === 0) {
ifqstep34 = 1.29683955465100964055;
} else {
ifqstep34 = 1.68179283050742922612;
}
let trigger = 0;
for (let sfb = 0; sfb < cod_info.sfbmax; sfb++) {
if (trigger < distort[sfb]) trigger = distort[sfb];
}
let { noise_shaping_amp } = gfc;
if (noise_shaping_amp === 3) {
if (bRefine) noise_shaping_amp = 2;
else noise_shaping_amp = 1;
}
switch (noise_shaping_amp) {
case 2:
break;
case 1:
if (trigger > 1.0) trigger = Math.pow(trigger, 0.5);
else trigger *= 0.95;
break;
case 0:
default:
if (trigger > 1.0) trigger = 1.0;
else trigger *= 0.95;
break;
}
let j = 0;
for (let sfb = 0; sfb < cod_info.sfbmax; sfb++) {
const width = cod_info.width[sfb];
let l;
j += width;
if (distort[sfb] < trigger) continue;
if ((gfc.substep_shaping & 2) !== 0) {
gfc.pseudohalf[sfb] = gfc.pseudohalf[sfb] === 0 ? 1 : 0;
if (gfc.pseudohalf[sfb] === 0 && gfc.noise_shaping_amp === 2) return;
}
cod_info.scalefac[sfb]++;
for (l = -width; l < 0; l++) {
xrpow[j + l] *= ifqstep34;
if (xrpow[j + l] > cod_info.xrpow_max) cod_info.xrpow_max = xrpow[j + l];
}
if (gfc.noise_shaping_amp === 2) return;
}
}
private inc_scalefac_scale(cod_info: GrInfo, xrpow: Float32Array) {
const ifqstep34 = 1.29683955465100964055;
let j = 0;
for (let sfb = 0; sfb < cod_info.sfbmax; sfb++) {
const width = cod_info.width[sfb];
let s = cod_info.scalefac[sfb];
if (cod_info.preflag !== 0) s += this.qupvt.pretab[sfb];
j += width;
if ((s & 1) !== 0) {
s++;
for (let l = -width; l < 0; l++) {
xrpow[j + l] *= ifqstep34;
if (xrpow[j + l] > cod_info.xrpow_max) cod_info.xrpow_max = xrpow[j + l];
}
}
cod_info.scalefac[sfb] = s >> 1;
}
cod_info.preflag = 0;
cod_info.scalefac_scale = 1;
}
private inc_subblock_gain(gfc: LameInternalFlags, cod_info: GrInfo, xrpow: Float32Array) {
let sfb;
const { scalefac } = cod_info;
for (sfb = 0; sfb < cod_info.sfb_lmax; sfb++) {
if (scalefac[sfb] >= 16) return true;
}
for (let window = 0; window < 3; window++) {
let s1 = 0;
let s2 = 0;
for (sfb = cod_info.sfb_lmax + window; sfb < cod_info.sfbdivide; sfb += 3) {
if (s1 < scalefac[sfb]) s1 = scalefac[sfb];
}
for (; sfb < cod_info.sfbmax; sfb += 3) {
if (s2 < scalefac[sfb]) s2 = scalefac[sfb];
}
if (s1 < 16 && s2 < 8) continue;
if (cod_info.subblock_gain[window] >= 7) return true;
cod_info.subblock_gain[window]++;
let j = gfc.scalefac_band.l[cod_info.sfb_lmax];
for (sfb = cod_info.sfb_lmax + window; sfb < cod_info.sfbmax; sfb += 3) {
let amp;
const width = cod_info.width[sfb];
let s = scalefac[sfb];
assert(s >= 0);
s -= 4 >> cod_info.scalefac_scale;
if (s >= 0) {
scalefac[sfb] = s;
j += width * 3;
continue;
}
scalefac[sfb] = 0;
{
const gain = 210 + (s << (cod_info.scalefac_scale + 1));
amp = this.qupvt.ipow20(gain);
}
j += width * (window + 1);
for (let l = -width; l < 0; l++) {
xrpow[j + l] *= amp;
if (xrpow[j + l] > cod_info.xrpow_max) cod_info.xrpow_max = xrpow[j + l];
}
j += width * (3 - window - 1);
}
const amp = this.qupvt.ipow20(202);
j += cod_info.width[sfb] * (window + 1);
for (let l = -cod_info.width[sfb]; l < 0; l++) {
xrpow[j + l] *= amp;
if (xrpow[j + l] > cod_info.xrpow_max) cod_info.xrpow_max = xrpow[j + l];
}
}
return false;
}
private balance_noise(gfp: LameGlobalFlags, cod_info: GrInfo, distort: Float32Array, xrpow: Float32Array, bRefine: boolean) {
const gfc = gfp.internal_flags;
this.amp_scalefac_bands(gfp, cod_info, distort, xrpow, bRefine);
let status = this.loop_break(cod_info);
if (status) return false;
if (gfc.mode_gr === 2) status = this.tak.scale_bitcount(cod_info);
else status = this.tak.scale_bitcount_lsf(gfc, cod_info);
if (!status) return true;
if (gfc.noise_shaping > 1) {
fillArray(gfc.pseudohalf, 0);
if (cod_info.scalefac_scale === 0) {
this.inc_scalefac_scale(cod_info, xrpow);
status = false;
} else if (cod_info.block_type === SHORT_TYPE && gfc.subblock_gain > 0) {
status = this.inc_subblock_gain(gfc, cod_info, xrpow) || this.loop_break(cod_info);
}
}
if (!status) {
if (gfc.mode_gr === 2) status = this.tak.scale_bitcount(cod_info);
else status = this.tak.scale_bitcount_lsf(gfc, cod_info);
}
return !status;
}
// eslint-disable-next-line complexity
outer_loop(gfp: LameGlobalFlags, cod_info: GrInfo, l3_xmin: Float32Array, xrpow: Float32Array, ch: number, targ_bits: number) {
const gfc = gfp.internal_flags;
const cod_info_w = new GrInfo();
const save_xrpow = new Float32Array(576);
const distort = new Float32Array(SFBMAX);
let best_noise_info = new CalcNoiseResult();
let better;
const prev_noise = new CalcNoiseData();
let best_part2_3_length = 9999999;
let bEndOfSearch = false;
let bRefine = false;
let best_ggain_pass1 = 0;
this.bin_search_StepSize(gfc, cod_info, targ_bits, ch, xrpow);
if (gfc.noise_shaping === 0) {
return 100;
}
this.calc_noise(cod_info, l3_xmin, distort, best_noise_info, prev_noise);
best_noise_info.bits = cod_info.part2_3_length;
cod_info_w.assign(cod_info);
let age = 0;
copyArray(xrpow, 0, save_xrpow, 0, 576);
while (!bEndOfSearch) {
do {
const noise_info = new CalcNoiseResult();
let search_limit;
let maxggain = 255;
if ((gfc.substep_shaping & 2) !== 0) {
search_limit = 20;
} else {
search_limit = 3;
}
if (gfc.sfb21_extra) {
if (distort[cod_info_w.sfbmax] > 1.0) break;
if (cod_info_w.block_type === SHORT_TYPE && (distort[cod_info_w.sfbmax + 1] > 1.0 || distort[cod_info_w.sfbmax + 2] > 1.0)) break;
}
if (!this.balance_noise(gfp, cod_info_w, distort, xrpow, bRefine)) break;
if (cod_info_w.scalefac_scale !== 0) maxggain = 254;
const huff_bits = targ_bits - cod_info_w.part2_length;
if (huff_bits <= 0) break;
while (
(cod_info_w.part2_3_length = this.tak.count_bits(gfc, xrpow, cod_info_w, prev_noise)) > huff_bits &&
cod_info_w.global_gain <= maxggain
)
cod_info_w.global_gain++;
if (cod_info_w.global_gain > maxggain) break;
if (best_noise_info.over_count === 0) {
while (
(cod_info_w.part2_3_length = this.tak.count_bits(gfc, xrpow, cod_info_w, prev_noise)) > best_part2_3_length &&
cod_info_w.global_gain <= maxggain
)
cod_info_w.global_gain++;
if (cod_info_w.global_gain > maxggain) break;
}
this.calc_noise(cod_info_w, l3_xmin, distort, noise_info, prev_noise);
noise_info.bits = cod_info_w.part2_3_length;
if (cod_info.block_type !== SHORT_TYPE) {
better = gfp.quant_comp;
} else better = gfp.quant_comp_short;
better = this.quant_compare(better, best_noise_info, noise_info, cod_info_w, distort) ? 1 : 0;
if (better !== 0) {
best_part2_3_length = cod_info.part2_3_length;
best_noise_info = noise_info;
cod_info.assign(cod_info_w);
age = 0;
copyArray(xrpow, 0, save_xrpow, 0, 576);
} else if (gfc.full_outer_loop === 0) {
if (++age > search_limit && best_noise_info.over_count === 0) break;
if (gfc.noise_shaping_amp === 3 && bRefine && age > 30) break;
if (gfc.noise_shaping_amp === 3 && bRefine && cod_info_w.global_gain - best_ggain_pass1 > 15) break;
}
} while (cod_info_w.global_gain + cod_info_w.scalefac_scale < 255);
if (gfc.noise_shaping_amp === 3) {
if (!bRefine) {
cod_info_w.assign(cod_info);
copyArray(save_xrpow, 0, xrpow, 0, 576);
age = 0;
best_ggain_pass1 = cod_info_w.global_gain;
bRefine = true;
} else {
bEndOfSearch = true;
}
} else {
bEndOfSearch = true;
}
}
assert(cod_info.global_gain + cod_info.scalefac_scale <= 255);
if (gfp.VBR === VbrMode.vbr_rh || gfp.VBR === VbrMode.vbr_mtrh) {
copyArray(save_xrpow, 0, xrpow, 0, 576);
} else if ((gfc.substep_shaping & 1) !== 0) {
this.trancate_smallspectrums(gfc, cod_info, l3_xmin, xrpow);
}
return best_noise_info.over_count;
}
iteration_finish_one(gfc: LameInternalFlags, gr: number, ch: number) {
const { l3_side } = gfc;
const cod_info = l3_side.tt[gr][ch];
this.tak.best_scalefac_store(gfc, gr, ch, l3_side);
if (gfc.use_best_huffman === 1) this.tak.best_huffman_divide(gfc, cod_info);
this.rv.ResvAdjust(gfc, cod_info);
}
private _pow20: Float32Array | undefined;
private pow20(x: number) {
assert(x + QuantizePVT.Q_MAX2 >= 0 && x < QuantizePVT.Q_MAX);
if (this._pow20 === undefined) {
this._pow20 = new Float32Array(QuantizePVT.Q_MAX + QuantizePVT.Q_MAX2 + 1);
for (let i = 0; i <= QuantizePVT.Q_MAX + QuantizePVT.Q_MAX2; i++) {
this._pow20[i] = Math.pow(2.0, (i - 210 - QuantizePVT.Q_MAX2) * 0.25);
}
}
return this._pow20[x + QuantizePVT.Q_MAX2];
}
private calc_noise_core(cod_info: GrInfo, startline: StartLine, l: number, step: number) {
let noise = 0;
let j = startline.s;
const ix = cod_info.l3_enc;
if (j > cod_info.count1) {
while (l-- !== 0) {
let temp;
temp = cod_info.xr[j];
j++;
noise += temp * temp;
temp = cod_info.xr[j];
j++;
noise += temp * temp;
}
} else if (j > cod_info.big_values) {
const ix01 = new Float32Array(2);
ix01[0] = 0;
ix01[1] = step;
while (l-- !== 0) {
let temp;
temp = Math.abs(cod_info.xr[j]) - ix01[ix[j]];
j++;
noise += temp * temp;
temp = Math.abs(cod_info.xr[j]) - ix01[ix[j]];
j++;
noise += temp * temp;
}
} else {
while (l-- !== 0) {
let temp;
temp = Math.abs(cod_info.xr[j]) - this.qupvt.pow43(ix[j]) * step;
j++;
noise += temp * temp;
temp = Math.abs(cod_info.xr[j]) - this.qupvt.pow43(ix[j]) * step;
j++;
noise += temp * temp;
}
}
startline.s = j;
return noise;
}
private calc_noise(
cod_info: GrInfo,
l3_xmin: Float32Array,
distort: Float32Array,
res: CalcNoiseResult,
prev_noise: CalcNoiseData | null,
) {
let distortPos = 0;
let l3_xminPos = 0;
let sfb;
let l;
let over = 0;
let over_noise_db = 0;
let tot_noise_db = 0;
let max_noise = -20.0;
let j = 0;
const { scalefac } = cod_info;
let scalefacPos = 0;
res.over_SSD = 0;
for (sfb = 0; sfb < cod_info.psymax; sfb++) {
const s =
cod_info.global_gain -
((scalefac[scalefacPos++] + (cod_info.preflag !== 0 ? this.qupvt.pretab[sfb] : 0)) << (cod_info.scalefac_scale + 1)) -
cod_info.subblock_gain[cod_info.window[sfb]] * 8;
let noise = 0.0;
if (prev_noise !== null && prev_noise.step[sfb] === s) {
noise = prev_noise.noise[sfb];
j += cod_info.width[sfb];
distort[distortPos++] = noise / l3_xmin[l3_xminPos++];
noise = prev_noise.noise_log[sfb];
} else {
const step = this.pow20(s);
l = cod_info.width[sfb] >> 1;
if (j + cod_info.width[sfb] > cod_info.max_nonzero_coeff) {
const usefullsize = cod_info.max_nonzero_coeff - j + 1;
if (usefullsize > 0) l = usefullsize >> 1;
else l = 0;
}
const sl = new StartLine(j);
noise = this.calc_noise_core(cod_info, sl, l, step);
j = sl.s;
if (prev_noise !== null) {
prev_noise.step[sfb] = s;
prev_noise.noise[sfb] = noise;
}
noise /= l3_xmin[l3_xminPos++];
distort[distortPos++] = noise;
noise = Math.log10(Math.max(noise, 1e-20));
if (prev_noise !== null) {
prev_noise.noise_log[sfb] = noise;
}
}
if (prev_noise !== null) {
prev_noise.global_gain = cod_info.global_gain;
}
tot_noise_db += noise;
if (noise > 0.0) {
const tmp = Math.max(Math.trunc(noise * 10 + 0.5), 1);
res.over_SSD += tmp * tmp;
over++;
over_noise_db += noise;
}
max_noise = Math.max(max_noise, noise);
}
res.over_count = over;
res.tot_noise = tot_noise_db;
res.over_noise = over_noise_db;
res.max_noise = max_noise;
return over;
}
}

View File

@@ -0,0 +1,414 @@
import type { GrInfo } from './GrInfo';
import type { III_psy_ratio } from './III_psy_ratio';
import type { LameGlobalFlags } from './LameGlobalFlags';
import type { PsyModel } from './PsyModel';
import type { Takehiro } from './Takehiro';
import { VbrMode } from './VbrMode';
import { assert } from './assert';
import {
MAX_BITS_PER_CHANNEL,
MAX_BITS_PER_GRANULE,
MAX_FLOAT32_VALUE,
PSFB12,
PSFB21,
SBMAX_l,
SBMAX_s,
SBPSY_l,
SBPSY_s,
SHORT_TYPE,
} from './constants';
import { isCloseToEachOther } from './math';
export class QuantizePVT {
static Q_MAX = 256 + 1;
static Q_MAX2 = 116;
static IXMAX_VAL = 8206;
readonly psy: PsyModel;
constructor(psy: PsyModel) {
this.psy = psy;
}
private static readonly DBL_EPSILON = 2.2204460492503131e-16;
static readonly PRECALC_SIZE = QuantizePVT.IXMAX_VAL + 2;
private static readonly NSATHSCALE = 100;
readonly nr_of_sfb_block = [
[
[6, 5, 5, 5],
[9, 9, 9, 9],
[6, 9, 9, 9],
],
[
[6, 5, 7, 3],
[9, 9, 12, 6],
[6, 9, 12, 6],
],
[
[11, 10, 0, 0],
[18, 18, 0, 0],
[15, 18, 0, 0],
],
[
[7, 7, 7, 0],
[12, 12, 12, 0],
[6, 15, 12, 0],
],
[
[6, 6, 6, 3],
[12, 9, 9, 6],
[6, 12, 9, 6],
],
[
[8, 8, 5, 0],
[15, 12, 9, 0],
[6, 18, 9, 0],
],
] as const;
private _ipow20: Float32Array | undefined;
ipow20(x: number) {
assert(x >= 0 && x < QuantizePVT.Q_MAX);
if (this._ipow20 === undefined) {
this._ipow20 = new Float32Array(QuantizePVT.Q_MAX);
for (let i = 0; i < QuantizePVT.Q_MAX; i++) {
this._ipow20[i] = Math.pow(2.0, (i - 210) * -0.1875);
}
}
return this._ipow20[x];
}
private _pow43: Float32Array | undefined;
pow43(k: number) {
if (this._pow43 === undefined) {
this._pow43 = new Float32Array(QuantizePVT.PRECALC_SIZE);
this._pow43[0] = 0.0;
for (let i = 1; i < QuantizePVT.PRECALC_SIZE; i++) {
this._pow43[i] = Math.pow(i, 4.0 / 3.0);
}
}
return this._pow43[k];
}
_adj43: Float32Array | undefined;
adj43(k: number) {
if (this._adj43 === undefined) {
this._adj43 = new Float32Array(QuantizePVT.PRECALC_SIZE);
let i;
for (i = 0; i < QuantizePVT.PRECALC_SIZE - 1; i++) {
this._adj43[i] = i + 1 - Math.pow(0.5 * (this.pow43(i) + this.pow43(i + 1)), 0.75);
}
this._adj43[i] = 0.5;
}
return this._adj43[k];
}
private ATHmdct(gfp: LameGlobalFlags, f: number) {
let ath = this.psy.ATHformula(f, gfp);
ath -= QuantizePVT.NSATHSCALE;
ath = Math.pow(10.0, ath / 10.0 + gfp.ATHlower);
return ath;
}
private compute_ath(gfp: LameGlobalFlags) {
const ATH_l = gfp.internal_flags.ATH.l;
const ATH_psfb21 = gfp.internal_flags.ATH.psfb21;
const ATH_s = gfp.internal_flags.ATH.s;
const ATH_psfb12 = gfp.internal_flags.ATH.psfb12;
const gfc = gfp.internal_flags;
const samp_freq = gfp.out_samplerate;
for (let sfb = 0; sfb < SBMAX_l; sfb++) {
const start = gfc.scalefac_band.l[sfb];
const end = gfc.scalefac_band.l[sfb + 1];
ATH_l[sfb] = MAX_FLOAT32_VALUE;
for (let i = start; i < end; i++) {
const freq = (i * samp_freq) / (2 * 576);
const ATH_f = this.ATHmdct(gfp, freq);
ATH_l[sfb] = Math.min(ATH_l[sfb], ATH_f);
}
}
for (let sfb = 0; sfb < PSFB21; sfb++) {
const start = gfc.scalefac_band.psfb21[sfb];
const end = gfc.scalefac_band.psfb21[sfb + 1];
ATH_psfb21[sfb] = MAX_FLOAT32_VALUE;
for (let i = start; i < end; i++) {
const freq = (i * samp_freq) / (2 * 576);
const ATH_f = this.ATHmdct(gfp, freq);
ATH_psfb21[sfb] = Math.min(ATH_psfb21[sfb], ATH_f);
}
}
for (let sfb = 0; sfb < SBMAX_s; sfb++) {
const start = gfc.scalefac_band.s[sfb];
const end = gfc.scalefac_band.s[sfb + 1];
ATH_s[sfb] = MAX_FLOAT32_VALUE;
for (let i = start; i < end; i++) {
const freq = (i * samp_freq) / (2 * 192);
const ATH_f = this.ATHmdct(gfp, freq);
ATH_s[sfb] = Math.min(ATH_s[sfb], ATH_f);
}
ATH_s[sfb] *= gfc.scalefac_band.s[sfb + 1] - gfc.scalefac_band.s[sfb];
}
for (let sfb = 0; sfb < PSFB12; sfb++) {
const start = gfc.scalefac_band.psfb12[sfb];
const end = gfc.scalefac_band.psfb12[sfb + 1];
ATH_psfb12[sfb] = MAX_FLOAT32_VALUE;
for (let i = start; i < end; i++) {
const freq = (i * samp_freq) / (2 * 192);
const ATH_f = this.ATHmdct(gfp, freq);
ATH_psfb12[sfb] = Math.min(ATH_psfb12[sfb], ATH_f);
}
ATH_psfb12[sfb] *= gfc.scalefac_band.s[13] - gfc.scalefac_band.s[12];
}
gfc.ATH.floor = 10 * Math.log10(this.ATHmdct(gfp, -1));
}
iteration_init(gfp: LameGlobalFlags, tak: Takehiro) {
const gfc = gfp.internal_flags;
const { l3_side } = gfc;
let i;
if (!gfc.iteration_init_init) {
gfc.iteration_init_init = true;
l3_side.main_data_begin = 0;
this.compute_ath(gfp);
tak.huffman_init(gfc);
i = (gfp.exp_nspsytune >> 2) & 63;
if (i >= 32) i -= 64;
const bass = Math.pow(10, i / 4.0 / 10.0);
i = (gfp.exp_nspsytune >> 8) & 63;
if (i >= 32) i -= 64;
const alto = Math.pow(10, i / 4.0 / 10.0);
i = (gfp.exp_nspsytune >> 14) & 63;
if (i >= 32) i -= 64;
const treble = Math.pow(10, i / 4.0 / 10.0);
i = (gfp.exp_nspsytune >> 20) & 63;
if (i >= 32) i -= 64;
const sfb21 = treble * Math.pow(10, i / 4.0 / 10.0);
for (i = 0; i < SBMAX_l; i++) {
let f;
if (i <= 6) f = bass;
else if (i <= 13) f = alto;
else if (i <= 20) f = treble;
else f = sfb21;
gfc.nsPsy.longfact[i] = f;
}
for (i = 0; i < SBMAX_s; i++) {
let f;
if (i <= 5) f = bass;
else if (i <= 10) f = alto;
else if (i <= 11) f = treble;
else f = sfb21;
gfc.nsPsy.shortfact[i] = f;
}
}
}
reduce_side(targ_bits: Int32Array, ms_ener_ratio: number, mean_bits: number, max_bits: number) {
assert(max_bits <= MAX_BITS_PER_GRANULE);
assert(targ_bits[0] + targ_bits[1] <= MAX_BITS_PER_GRANULE);
let fac = (0.33 * (0.5 - ms_ener_ratio)) / 0.5;
if (fac < 0) fac = 0;
if (fac > 0.5) fac = 0.5;
let move_bits = Math.trunc(fac * 0.5 * (targ_bits[0] + targ_bits[1]));
if (move_bits > MAX_BITS_PER_CHANNEL - targ_bits[0]) {
move_bits = MAX_BITS_PER_CHANNEL - targ_bits[0];
}
if (move_bits < 0) move_bits = 0;
if (targ_bits[1] >= 125) {
if (targ_bits[1] - move_bits > 125) {
if (targ_bits[0] < mean_bits) targ_bits[0] += move_bits;
targ_bits[1] -= move_bits;
} else {
targ_bits[0] += targ_bits[1] - 125;
targ_bits[1] = 125;
}
}
move_bits = targ_bits[0] + targ_bits[1];
if (move_bits > max_bits) {
targ_bits[0] = (max_bits * targ_bits[0]) / move_bits;
targ_bits[1] = (max_bits * targ_bits[1]) / move_bits;
}
assert(targ_bits[0] <= MAX_BITS_PER_CHANNEL);
assert(targ_bits[1] <= MAX_BITS_PER_CHANNEL);
assert(targ_bits[0] + targ_bits[1] <= MAX_BITS_PER_GRANULE);
}
athAdjust(a: number, x: number, athFloor: number) {
const o = 90.30873362;
const p = 94.82444863;
let u = Math.log10(x) * 10.0;
const v = a * a;
let w = 0.0;
u -= athFloor;
if (v > 1e-20) w = 1 + Math.log10(v) * (10.0 / o);
if (w < 0) w = 0;
u *= w;
u += athFloor + o - p;
return Math.pow(10, 0.1 * u);
}
// eslint-disable-next-line complexity
calc_xmin(gfp: LameGlobalFlags, ratio: III_psy_ratio, cod_info: GrInfo, pxmin: Float32Array) {
let pxminPos = 0;
const gfc = gfp.internal_flags;
let gsfb;
let j = 0;
let ath_over = 0;
const { ATH } = gfc;
const { xr } = cod_info;
const enable_athaa_fix = gfp.VBR === VbrMode.vbr_mtrh ? 1 : 0;
let { masking_lower } = gfc;
if (gfp.VBR === VbrMode.vbr_mtrh || gfp.VBR === VbrMode.vbr_mt) {
masking_lower = 1.0;
}
for (gsfb = 0; gsfb < cod_info.psy_lmax; gsfb++) {
let en0;
let xmin;
let rh2;
let l;
if (gfp.VBR === VbrMode.vbr_rh || gfp.VBR === VbrMode.vbr_mtrh) xmin = this.athAdjust(ATH.adjust, ATH.l[gsfb], ATH.floor);
else xmin = ATH.adjust * ATH.l[gsfb];
const width = cod_info.width[gsfb];
const rh1 = xmin / width;
rh2 = QuantizePVT.DBL_EPSILON;
l = width >> 1;
en0 = 0.0;
do {
const xa = xr[j] * xr[j];
en0 += xa;
rh2 += xa < rh1 ? xa : rh1;
j++;
const xb = xr[j] * xr[j];
en0 += xb;
rh2 += xb < rh1 ? xb : rh1;
j++;
} while (--l > 0);
if (en0 > xmin) ath_over++;
if (gsfb === SBPSY_l) {
const x = xmin * gfc.nsPsy.longfact[gsfb];
if (rh2 < x) {
rh2 = x;
}
}
if (enable_athaa_fix !== 0) {
xmin = rh2;
}
const e = ratio.en.l[gsfb];
if (e > 0.0) {
let x;
x = (en0 * ratio.thm.l[gsfb] * masking_lower) / e;
if (enable_athaa_fix !== 0) x *= gfc.nsPsy.longfact[gsfb];
if (xmin < x) xmin = x;
}
if (enable_athaa_fix !== 0) pxmin[pxminPos++] = xmin;
else pxmin[pxminPos++] = xmin * gfc.nsPsy.longfact[gsfb];
}
let max_nonzero = 575;
if (cod_info.block_type !== SHORT_TYPE) {
let k = 576;
while (k-- !== 0 && isCloseToEachOther(xr[k], 0)) {
max_nonzero = k;
}
}
cod_info.max_nonzero_coeff = max_nonzero;
for (let sfb = cod_info.sfb_smin; gsfb < cod_info.psymax; sfb++, gsfb += 3) {
let b;
let tmpATH;
if (gfp.VBR === VbrMode.vbr_rh || gfp.VBR === VbrMode.vbr_mtrh) tmpATH = this.athAdjust(ATH.adjust, ATH.s[sfb], ATH.floor);
else tmpATH = ATH.adjust * ATH.s[sfb];
const width = cod_info.width[gsfb];
for (b = 0; b < 3; b++) {
let en0 = 0.0;
let xmin;
let rh2;
let l = width >> 1;
const rh1 = tmpATH / width;
rh2 = QuantizePVT.DBL_EPSILON;
do {
const xa = xr[j] * xr[j];
en0 += xa;
rh2 += xa < rh1 ? xa : rh1;
j++;
const xb = xr[j] * xr[j];
en0 += xb;
rh2 += xb < rh1 ? xb : rh1;
j++;
} while (--l > 0);
if (en0 > tmpATH) ath_over++;
if (sfb === SBPSY_s) {
const x = tmpATH * gfc.nsPsy.shortfact[sfb];
if (rh2 < x) {
rh2 = x;
}
}
if (enable_athaa_fix !== 0) xmin = rh2;
else xmin = tmpATH;
const e = ratio.en.s[sfb][b];
if (e > 0.0) {
let x;
x = (en0 * ratio.thm.s[sfb][b] * masking_lower) / e;
if (enable_athaa_fix !== 0) x *= gfc.nsPsy.shortfact[sfb];
if (xmin < x) xmin = x;
}
if (enable_athaa_fix !== 0) pxmin[pxminPos++] = xmin;
else pxmin[pxminPos++] = xmin * gfc.nsPsy.shortfact[sfb];
}
}
return ath_over;
}
readonly pretab = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0] as const;
}

View File

@@ -0,0 +1,45 @@
import { GainAnalysis } from './GainAnalysis';
export class ReplayGain {
linprebuf = new Float32Array(GainAnalysis.MAX_ORDER * 2);
linpre = 0;
lstepbuf = new Float32Array(GainAnalysis.MAX_SAMPLES_PER_WINDOW + GainAnalysis.MAX_ORDER);
lstep = 0;
loutbuf = new Float32Array(GainAnalysis.MAX_SAMPLES_PER_WINDOW + GainAnalysis.MAX_ORDER);
lout = 0;
rinprebuf = new Float32Array(GainAnalysis.MAX_ORDER * 2);
rinpre = 0;
rstepbuf = new Float32Array(GainAnalysis.MAX_SAMPLES_PER_WINDOW + GainAnalysis.MAX_ORDER);
rstep = 0;
routbuf = new Float32Array(GainAnalysis.MAX_SAMPLES_PER_WINDOW + GainAnalysis.MAX_ORDER);
rout = 0;
sampleWindow = 0;
totsamp = 0;
lsum = 0;
rsum = 0;
freqindex = 0;
first = 0;
A = new Int32Array(GainAnalysis.STEPS_per_dB * GainAnalysis.MAX_dB);
B = new Int32Array(GainAnalysis.STEPS_per_dB * GainAnalysis.MAX_dB);
reqindex = 0;
}

View File

@@ -0,0 +1,94 @@
import type { BitStream } from './BitStream';
import type { GrInfo } from './GrInfo';
import type { LameGlobalFlags } from './LameGlobalFlags';
import type { LameInternalFlags } from './LameInternalFlags';
import type { MeanBits } from './MeanBits';
import { assert } from './assert';
export class Reservoir {
private bs: BitStream | undefined;
constructor(bs: BitStream) {
this.bs = bs;
}
ResvFrameBegin(gfp: LameGlobalFlags, mean_bits: MeanBits) {
const gfc = gfp.internal_flags;
let maxmp3buf;
const { l3_side } = gfc;
const frameLength = this.bs?.getframebits(gfp) ?? 0;
mean_bits.bits = (frameLength - gfc.sideinfo_len * 8) / gfc.mode_gr;
if (gfp.brate > 320) {
maxmp3buf = 8 * Math.trunc((gfp.brate * 1000) / (gfp.out_samplerate / 1152) / 8 + 0.5);
} else {
maxmp3buf = 8 * 1440;
}
let fullFrameBits = mean_bits.bits * gfc.mode_gr + Math.min(gfc.ResvSize, 0);
if (fullFrameBits > maxmp3buf) fullFrameBits = maxmp3buf;
l3_side.resvDrain_pre = 0;
return fullFrameBits;
}
ResvMaxBits(gfp: LameGlobalFlags, mean_bits: number, targ_bits: MeanBits, cbr: number) {
const gfc = gfp.internal_flags;
let add_bits;
let { ResvSize } = gfc;
if (cbr !== 0) ResvSize += mean_bits;
targ_bits.bits = mean_bits;
if (ResvSize * 10 > 0) {
add_bits = ResvSize;
targ_bits.bits += add_bits;
gfc.substep_shaping |= 0x80;
} else {
add_bits = 0;
gfc.substep_shaping &= 0x7f;
}
let extra_bits = ResvSize < 0 ? ResvSize : 0;
extra_bits -= add_bits;
if (extra_bits < 0) extra_bits = 0;
return extra_bits;
}
ResvAdjust(gfc: LameInternalFlags, gi: GrInfo) {
gfc.ResvSize -= gi.part2_3_length + gi.part2_length;
}
ResvFrameEnd(gfc: LameInternalFlags, mean_bits: number) {
let over_bits;
const { l3_side } = gfc;
gfc.ResvSize += mean_bits * gfc.mode_gr;
let stuffingBits = 0;
l3_side.resvDrain_post = 0;
l3_side.resvDrain_pre = 0;
if ((over_bits = gfc.ResvSize % 8) !== 0) stuffingBits += over_bits;
over_bits = gfc.ResvSize - stuffingBits;
if (over_bits > 0) {
assert(over_bits % 8 === 0);
assert(over_bits >= 0);
stuffingBits += over_bits;
}
const mdb_bytes = Math.min(l3_side.main_data_begin * 8, stuffingBits) / 8;
l3_side.resvDrain_pre += 8 * mdb_bytes;
stuffingBits -= 8 * mdb_bytes;
gfc.ResvSize -= 8 * mdb_bytes;
l3_side.main_data_begin -= mdb_bytes;
l3_side.resvDrain_post += stuffingBits;
gfc.ResvSize -= stuffingBits;
}
}

View File

@@ -0,0 +1,42 @@
import { copyArray } from './arrays';
import { PSFB12, PSFB21, SBMAX_l, SBMAX_s } from './constants';
export class ScaleFac {
private readonly arrL: number[] | undefined;
private readonly arrS: number[] | undefined;
private readonly arr21: number[] | undefined;
private readonly arr12: number[] | undefined;
readonly l: Int32Array;
readonly s: Int32Array;
readonly psfb21: Int32Array;
readonly psfb12: Int32Array;
constructor();
constructor(arrL: number[], arrS: number[], arr21: number[], arr12: number[]);
constructor(...args: [] | [arrL: number[], arrS: number[], arr21: number[], arr12: number[]]) {
this.l = new Int32Array(1 + SBMAX_l);
this.s = new Int32Array(1 + SBMAX_s);
this.psfb21 = new Int32Array(1 + PSFB21);
this.psfb12 = new Int32Array(1 + PSFB12);
const { l } = this;
const { s } = this;
if (args.length === 4) {
[this.arrL, this.arrS, this.arr21, this.arr12] = args;
copyArray(this.arrL, 0, l, 0, Math.min(this.arrL.length, this.l.length));
copyArray(this.arrS, 0, s, 0, Math.min(this.arrS.length, this.s.length));
copyArray(this.arr21, 0, this.psfb21, 0, Math.min(this.arr21.length, this.psfb21.length));
copyArray(this.arr12, 0, this.psfb12, 0, Math.min(this.arr12.length, this.psfb12.length));
}
}
}

View File

@@ -0,0 +1,6 @@
export const enum ShortBlock {
short_block_allowed,
short_block_coupled,
short_block_dispensed,
short_block_forced,
}

View File

@@ -0,0 +1,3 @@
export class StartLine {
constructor(public s: number) {}
}

View File

@@ -0,0 +1,253 @@
import type { HuffCodeTab } from './HuffCodeTab';
const t1HB = [1, 1, 1, 0] as const;
const t2HB = [1, 2, 1, 3, 1, 1, 3, 2, 0] as const;
const t3HB = [3, 2, 1, 1, 1, 1, 3, 2, 0] as const;
const t5HB = [1, 2, 6, 5, 3, 1, 4, 4, 7, 5, 7, 1, 6, 1, 1, 0] as const;
const t6HB = [7, 3, 5, 1, 6, 2, 3, 2, 5, 4, 4, 1, 3, 3, 2, 0] as const;
const t7HB = [
1, 2, 10, 19, 16, 10, 3, 3, 7, 10, 5, 3, 11, 4, 13, 17, 8, 4, 12, 11, 18, 15, 11, 2, 7, 6, 9, 14, 3, 1, 6, 4, 5, 3, 2, 0,
] as const;
const t8HB = [
3, 4, 6, 18, 12, 5, 5, 1, 2, 16, 9, 3, 7, 3, 5, 14, 7, 3, 19, 17, 15, 13, 10, 4, 13, 5, 8, 11, 5, 1, 12, 4, 4, 1, 1, 0,
] as const;
const t9HB = [7, 5, 9, 14, 15, 7, 6, 4, 5, 5, 6, 7, 7, 6, 8, 8, 8, 5, 15, 6, 9, 10, 5, 1, 11, 7, 9, 6, 4, 1, 14, 4, 6, 2, 6, 0] as const;
const t10HB = [
1, 2, 10, 23, 35, 30, 12, 17, 3, 3, 8, 12, 18, 21, 12, 7, 11, 9, 15, 21, 32, 40, 19, 6, 14, 13, 22, 34, 46, 23, 18, 7, 20, 19, 33, 47, 27,
22, 9, 3, 31, 22, 41, 26, 21, 20, 5, 3, 14, 13, 10, 11, 16, 6, 5, 1, 9, 8, 7, 8, 4, 4, 2, 0,
] as const;
const t11HB = [
3, 4, 10, 24, 34, 33, 21, 15, 5, 3, 4, 10, 32, 17, 11, 10, 11, 7, 13, 18, 30, 31, 20, 5, 25, 11, 19, 59, 27, 18, 12, 5, 35, 33, 31, 58,
30, 16, 7, 5, 28, 26, 32, 19, 17, 15, 8, 14, 14, 12, 9, 13, 14, 9, 4, 1, 11, 4, 6, 6, 6, 3, 2, 0,
] as const;
const t12HB = [
9, 6, 16, 33, 41, 39, 38, 26, 7, 5, 6, 9, 23, 16, 26, 11, 17, 7, 11, 14, 21, 30, 10, 7, 17, 10, 15, 12, 18, 28, 14, 5, 32, 13, 22, 19, 18,
16, 9, 5, 40, 17, 31, 29, 17, 13, 4, 2, 27, 12, 11, 15, 10, 7, 4, 1, 27, 12, 8, 12, 6, 3, 1, 0,
] as const;
const t13HB = [
1, 5, 14, 21, 34, 51, 46, 71, 42, 52, 68, 52, 67, 44, 43, 19, 3, 4, 12, 19, 31, 26, 44, 33, 31, 24, 32, 24, 31, 35, 22, 14, 15, 13, 23,
36, 59, 49, 77, 65, 29, 40, 30, 40, 27, 33, 42, 16, 22, 20, 37, 61, 56, 79, 73, 64, 43, 76, 56, 37, 26, 31, 25, 14, 35, 16, 60, 57, 97,
75, 114, 91, 54, 73, 55, 41, 48, 53, 23, 24, 58, 27, 50, 96, 76, 70, 93, 84, 77, 58, 79, 29, 74, 49, 41, 17, 47, 45, 78, 74, 115, 94, 90,
79, 69, 83, 71, 50, 59, 38, 36, 15, 72, 34, 56, 95, 92, 85, 91, 90, 86, 73, 77, 65, 51, 44, 43, 42, 43, 20, 30, 44, 55, 78, 72, 87, 78,
61, 46, 54, 37, 30, 20, 16, 53, 25, 41, 37, 44, 59, 54, 81, 66, 76, 57, 54, 37, 18, 39, 11, 35, 33, 31, 57, 42, 82, 72, 80, 47, 58, 55,
21, 22, 26, 38, 22, 53, 25, 23, 38, 70, 60, 51, 36, 55, 26, 34, 23, 27, 14, 9, 7, 34, 32, 28, 39, 49, 75, 30, 52, 48, 40, 52, 28, 18, 17,
9, 5, 45, 21, 34, 64, 56, 50, 49, 45, 31, 19, 12, 15, 10, 7, 6, 3, 48, 23, 20, 39, 36, 35, 53, 21, 16, 23, 13, 10, 6, 1, 4, 2, 16, 15, 17,
27, 25, 20, 29, 11, 17, 12, 16, 8, 1, 1, 0, 1,
] as const;
const t15HB = [
7, 12, 18, 53, 47, 76, 124, 108, 89, 123, 108, 119, 107, 81, 122, 63, 13, 5, 16, 27, 46, 36, 61, 51, 42, 70, 52, 83, 65, 41, 59, 36, 19,
17, 15, 24, 41, 34, 59, 48, 40, 64, 50, 78, 62, 80, 56, 33, 29, 28, 25, 43, 39, 63, 55, 93, 76, 59, 93, 72, 54, 75, 50, 29, 52, 22, 42,
40, 67, 57, 95, 79, 72, 57, 89, 69, 49, 66, 46, 27, 77, 37, 35, 66, 58, 52, 91, 74, 62, 48, 79, 63, 90, 62, 40, 38, 125, 32, 60, 56, 50,
92, 78, 65, 55, 87, 71, 51, 73, 51, 70, 30, 109, 53, 49, 94, 88, 75, 66, 122, 91, 73, 56, 42, 64, 44, 21, 25, 90, 43, 41, 77, 73, 63, 56,
92, 77, 66, 47, 67, 48, 53, 36, 20, 71, 34, 67, 60, 58, 49, 88, 76, 67, 106, 71, 54, 38, 39, 23, 15, 109, 53, 51, 47, 90, 82, 58, 57, 48,
72, 57, 41, 23, 27, 62, 9, 86, 42, 40, 37, 70, 64, 52, 43, 70, 55, 42, 25, 29, 18, 11, 11, 118, 68, 30, 55, 50, 46, 74, 65, 49, 39, 24,
16, 22, 13, 14, 7, 91, 44, 39, 38, 34, 63, 52, 45, 31, 52, 28, 19, 14, 8, 9, 3, 123, 60, 58, 53, 47, 43, 32, 22, 37, 24, 17, 12, 15, 10,
2, 1, 71, 37, 34, 30, 28, 20, 17, 26, 21, 16, 10, 6, 8, 6, 2, 0,
] as const;
const t16HB = [
1, 5, 14, 44, 74, 63, 110, 93, 172, 149, 138, 242, 225, 195, 376, 17, 3, 4, 12, 20, 35, 62, 53, 47, 83, 75, 68, 119, 201, 107, 207, 9, 15,
13, 23, 38, 67, 58, 103, 90, 161, 72, 127, 117, 110, 209, 206, 16, 45, 21, 39, 69, 64, 114, 99, 87, 158, 140, 252, 212, 199, 387, 365, 26,
75, 36, 68, 65, 115, 101, 179, 164, 155, 264, 246, 226, 395, 382, 362, 9, 66, 30, 59, 56, 102, 185, 173, 265, 142, 253, 232, 400, 388,
378, 445, 16, 111, 54, 52, 100, 184, 178, 160, 133, 257, 244, 228, 217, 385, 366, 715, 10, 98, 48, 91, 88, 165, 157, 148, 261, 248, 407,
397, 372, 380, 889, 884, 8, 85, 84, 81, 159, 156, 143, 260, 249, 427, 401, 392, 383, 727, 713, 708, 7, 154, 76, 73, 141, 131, 256, 245,
426, 406, 394, 384, 735, 359, 710, 352, 11, 139, 129, 67, 125, 247, 233, 229, 219, 393, 743, 737, 720, 885, 882, 439, 4, 243, 120, 118,
115, 227, 223, 396, 746, 742, 736, 721, 712, 706, 223, 436, 6, 202, 224, 222, 218, 216, 389, 386, 381, 364, 888, 443, 707, 440, 437, 1728,
4, 747, 211, 210, 208, 370, 379, 734, 723, 714, 1735, 883, 877, 876, 3459, 865, 2, 377, 369, 102, 187, 726, 722, 358, 711, 709, 866, 1734,
871, 3458, 870, 434, 0, 12, 10, 7, 11, 10, 17, 11, 9, 13, 12, 10, 7, 5, 3, 1, 3,
] as const;
const t24HB = [
15, 13, 46, 80, 146, 262, 248, 434, 426, 669, 653, 649, 621, 517, 1032, 88, 14, 12, 21, 38, 71, 130, 122, 216, 209, 198, 327, 345, 319,
297, 279, 42, 47, 22, 41, 74, 68, 128, 120, 221, 207, 194, 182, 340, 315, 295, 541, 18, 81, 39, 75, 70, 134, 125, 116, 220, 204, 190, 178,
325, 311, 293, 271, 16, 147, 72, 69, 135, 127, 118, 112, 210, 200, 188, 352, 323, 306, 285, 540, 14, 263, 66, 129, 126, 119, 114, 214,
202, 192, 180, 341, 317, 301, 281, 262, 12, 249, 123, 121, 117, 113, 215, 206, 195, 185, 347, 330, 308, 291, 272, 520, 10, 435, 115, 111,
109, 211, 203, 196, 187, 353, 332, 313, 298, 283, 531, 381, 17, 427, 212, 208, 205, 201, 193, 186, 177, 169, 320, 303, 286, 268, 514, 377,
16, 335, 199, 197, 191, 189, 181, 174, 333, 321, 305, 289, 275, 521, 379, 371, 11, 668, 184, 183, 179, 175, 344, 331, 314, 304, 290, 277,
530, 383, 373, 366, 10, 652, 346, 171, 168, 164, 318, 309, 299, 287, 276, 263, 513, 375, 368, 362, 6, 648, 322, 316, 312, 307, 302, 292,
284, 269, 261, 512, 376, 370, 364, 359, 4, 620, 300, 296, 294, 288, 282, 273, 266, 515, 380, 374, 369, 365, 361, 357, 2, 1033, 280, 278,
274, 267, 264, 259, 382, 378, 372, 367, 363, 360, 358, 356, 0, 43, 20, 19, 17, 15, 13, 11, 9, 7, 6, 4, 7, 5, 3, 1, 3,
] as const;
const t32HB = [
1 << 0,
5 << 1,
4 << 1,
5 << 2,
6 << 1,
5 << 2,
4 << 2,
4 << 3,
7 << 1,
3 << 2,
6 << 2,
0 << 3,
7 << 2,
2 << 3,
3 << 3,
1 << 4,
] as const;
const t33HB = [
15 << 0,
14 << 1,
13 << 1,
12 << 2,
11 << 1,
10 << 2,
9 << 2,
8 << 3,
7 << 1,
6 << 2,
5 << 2,
4 << 3,
3 << 2,
2 << 3,
1 << 3,
0 << 4,
] as const;
const t1l = [1, 4, 3, 5] as const;
const t2l = [1, 4, 7, 4, 5, 7, 6, 7, 8] as const;
const t3l = [2, 3, 7, 4, 4, 7, 6, 7, 8] as const;
const t5l = [1, 4, 7, 8, 4, 5, 8, 9, 7, 8, 9, 10, 8, 8, 9, 10] as const;
const t6l = [3, 4, 6, 8, 4, 4, 6, 7, 5, 6, 7, 8, 7, 7, 8, 9] as const;
const t7l = [
1, 4, 7, 9, 9, 10, 4, 6, 8, 9, 9, 10, 7, 7, 9, 10, 10, 11, 8, 9, 10, 11, 11, 11, 8, 9, 10, 11, 11, 12, 9, 10, 11, 12, 12, 12,
] as const;
const t8l = [
2, 4, 7, 9, 9, 10, 4, 4, 6, 10, 10, 10, 7, 6, 8, 10, 10, 11, 9, 10, 10, 11, 11, 12, 9, 9, 10, 11, 12, 12, 10, 10, 11, 11, 13, 13,
] as const;
const t9l = [3, 4, 6, 7, 9, 10, 4, 5, 6, 7, 8, 10, 5, 6, 7, 8, 9, 10, 7, 7, 8, 9, 9, 10, 8, 8, 9, 9, 10, 11, 9, 9, 10, 10, 11, 11] as const;
const t10l = [
1, 4, 7, 9, 10, 10, 10, 11, 4, 6, 8, 9, 10, 11, 10, 10, 7, 8, 9, 10, 11, 12, 11, 11, 8, 9, 10, 11, 12, 12, 11, 12, 9, 10, 11, 12, 12, 12,
12, 12, 10, 11, 12, 12, 13, 13, 12, 13, 9, 10, 11, 12, 12, 12, 13, 13, 10, 10, 11, 12, 12, 13, 13, 13,
] as const;
const t11l = [
2, 4, 6, 8, 9, 10, 9, 10, 4, 5, 6, 8, 10, 10, 9, 10, 6, 7, 8, 9, 10, 11, 10, 10, 8, 8, 9, 11, 10, 12, 10, 11, 9, 10, 10, 11, 11, 12, 11,
12, 9, 10, 11, 12, 12, 13, 12, 13, 9, 9, 9, 10, 11, 12, 12, 12, 9, 9, 10, 11, 12, 12, 12, 12,
] as const;
const t12l = [
4, 4, 6, 8, 9, 10, 10, 10, 4, 5, 6, 7, 9, 9, 10, 10, 6, 6, 7, 8, 9, 10, 9, 10, 7, 7, 8, 8, 9, 10, 10, 10, 8, 8, 9, 9, 10, 10, 10, 11, 9,
9, 10, 10, 10, 11, 10, 11, 9, 9, 9, 10, 10, 11, 11, 12, 10, 10, 10, 11, 11, 11, 11, 12,
] as const;
const t13l = [
1, 5, 7, 8, 9, 10, 10, 11, 10, 11, 12, 12, 13, 13, 14, 14, 4, 6, 8, 9, 10, 10, 11, 11, 11, 11, 12, 12, 13, 14, 14, 14, 7, 8, 9, 10, 11,
11, 12, 12, 11, 12, 12, 13, 13, 14, 15, 15, 8, 9, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 15, 15, 9, 9, 11, 11, 12, 12, 13, 13,
12, 13, 13, 14, 14, 15, 15, 16, 10, 10, 11, 12, 12, 12, 13, 13, 13, 13, 14, 13, 15, 15, 16, 16, 10, 11, 12, 12, 13, 13, 13, 13, 13, 14,
14, 14, 15, 15, 16, 16, 11, 11, 12, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 16, 18, 18, 10, 10, 11, 12, 12, 13, 13, 14, 14, 14, 14, 15,
15, 16, 17, 17, 11, 11, 12, 12, 13, 13, 13, 15, 14, 15, 15, 16, 16, 16, 18, 17, 11, 12, 12, 13, 13, 14, 14, 15, 14, 15, 16, 15, 16, 17,
18, 19, 12, 12, 12, 13, 14, 14, 14, 14, 15, 15, 15, 16, 17, 17, 17, 18, 12, 13, 13, 14, 14, 15, 14, 15, 16, 16, 17, 17, 17, 18, 18, 18,
13, 13, 14, 15, 15, 15, 16, 16, 16, 16, 16, 17, 18, 17, 18, 18, 14, 14, 14, 15, 15, 15, 17, 16, 16, 19, 17, 17, 17, 19, 18, 18, 13, 14,
15, 16, 16, 16, 17, 16, 17, 17, 18, 18, 21, 20, 21, 18,
] as const;
const t15l = [
3, 5, 6, 8, 8, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 14, 5, 5, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 6, 7, 7, 8, 9, 9, 10,
10, 10, 11, 11, 12, 12, 13, 13, 13, 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 8, 8, 9, 9, 10, 10, 11, 11, 11, 11, 12, 12,
12, 13, 13, 13, 9, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 13, 13, 13, 14, 10, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 14,
14, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 14, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 14, 14, 10,
10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 14, 15, 14, 11, 11, 11,
11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 12, 12, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 14, 14, 15, 15, 12, 12, 12, 12, 12,
13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 14, 15, 13, 13, 13, 13, 13, 13, 13,
14, 14, 14, 14, 14, 15, 15, 15, 15,
] as const;
const t16_5l = [
1, 5, 7, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 11, 4, 6, 8, 9, 10, 11, 11, 11, 12, 12, 12, 13, 14, 13, 14, 11, 7, 8, 9, 10, 11,
11, 12, 12, 13, 12, 13, 13, 13, 14, 14, 12, 9, 9, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14, 15, 15, 13, 10, 10, 11, 11, 12, 12, 13, 13,
13, 14, 14, 14, 15, 15, 15, 12, 10, 10, 11, 11, 12, 13, 13, 14, 13, 14, 14, 15, 15, 15, 16, 13, 11, 11, 11, 12, 13, 13, 13, 13, 14, 14,
14, 14, 15, 15, 16, 13, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15, 17, 17, 13, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15,
16, 16, 16, 13, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 15, 16, 15, 14, 12, 13, 12, 13, 14, 14, 14, 14, 15, 16, 16, 16, 17, 17,
16, 13, 13, 13, 13, 13, 14, 14, 15, 16, 16, 16, 16, 16, 16, 15, 16, 14, 13, 14, 14, 14, 14, 15, 15, 15, 15, 17, 16, 16, 16, 16, 18, 14,
15, 14, 14, 14, 15, 15, 16, 16, 16, 18, 17, 17, 17, 19, 17, 14, 14, 15, 13, 14, 16, 16, 15, 16, 16, 17, 18, 17, 19, 17, 16, 14, 11, 11,
11, 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 12,
] as const;
const t16l = [
1, 5, 7, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 10, 4, 6, 8, 9, 10, 11, 11, 11, 12, 12, 12, 13, 14, 13, 14, 10, 7, 8, 9, 10, 11,
11, 12, 12, 13, 12, 13, 13, 13, 14, 14, 11, 9, 9, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14, 15, 15, 12, 10, 10, 11, 11, 12, 12, 13, 13,
13, 14, 14, 14, 15, 15, 15, 11, 10, 10, 11, 11, 12, 13, 13, 14, 13, 14, 14, 15, 15, 15, 16, 12, 11, 11, 11, 12, 13, 13, 13, 13, 14, 14,
14, 14, 15, 15, 16, 12, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15, 17, 17, 12, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15,
16, 16, 16, 12, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 15, 16, 15, 13, 12, 13, 12, 13, 14, 14, 14, 14, 15, 16, 16, 16, 17, 17,
16, 12, 13, 13, 13, 13, 14, 14, 15, 16, 16, 16, 16, 16, 16, 15, 16, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 17, 16, 16, 16, 16, 18, 13,
15, 14, 14, 14, 15, 15, 16, 16, 16, 18, 17, 17, 17, 19, 17, 13, 14, 15, 13, 14, 16, 16, 15, 16, 16, 17, 18, 17, 19, 17, 16, 13, 10, 10,
10, 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 10,
] as const;
const t24l = [
4, 5, 7, 8, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 13, 10, 5, 6, 7, 8, 9, 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 10, 7, 7, 8, 9, 9, 10,
10, 11, 11, 11, 11, 12, 12, 12, 13, 9, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12,
12, 12, 12, 13, 9, 10, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12,
13, 9, 11, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 10, 11,
11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 10, 12, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 10, 12, 12, 11,
11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 10, 12, 12, 12, 12, 12,
12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 10, 13, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 10, 10, 10, 10, 6,
] as const;
const t32l = [1 + 0, 4 + 1, 4 + 1, 5 + 2, 4 + 1, 6 + 2, 5 + 2, 6 + 3, 4 + 1, 5 + 2, 5 + 2, 6 + 3, 5 + 2, 6 + 3, 6 + 3, 6 + 4] as const;
const t33l = [4 + 0, 4 + 1, 4 + 1, 4 + 2, 4 + 1, 4 + 2, 4 + 2, 4 + 3, 4 + 1, 4 + 2, 4 + 2, 4 + 3, 4 + 2, 4 + 3, 4 + 3, 4 + 4] as const;
const ht: readonly HuffCodeTab[] = [
{ xlen: 0, linmax: 0 },
{ xlen: 2, linmax: 0, table: t1HB, hlen: t1l },
{ xlen: 3, linmax: 0, table: t2HB, hlen: t2l },
{ xlen: 3, linmax: 0, table: t3HB, hlen: t3l },
{ xlen: 0, linmax: 0 },
{ xlen: 4, linmax: 0, table: t5HB, hlen: t5l },
{ xlen: 4, linmax: 0, table: t6HB, hlen: t6l },
{ xlen: 6, linmax: 0, table: t7HB, hlen: t7l },
{ xlen: 6, linmax: 0, table: t8HB, hlen: t8l },
{ xlen: 6, linmax: 0, table: t9HB, hlen: t9l },
{ xlen: 8, linmax: 0, table: t10HB, hlen: t10l },
{ xlen: 8, linmax: 0, table: t11HB, hlen: t11l },
{ xlen: 8, linmax: 0, table: t12HB, hlen: t12l },
{ xlen: 16, linmax: 0, table: t13HB, hlen: t13l },
{ xlen: 0, linmax: 0, hlen: t16_5l },
{ xlen: 16, linmax: 0, table: t15HB, hlen: t15l },
{ xlen: 1, linmax: 1, table: t16HB, hlen: t16l },
{ xlen: 2, linmax: 3, table: t16HB, hlen: t16l },
{ xlen: 3, linmax: 7, table: t16HB, hlen: t16l },
{ xlen: 4, linmax: 15, table: t16HB, hlen: t16l },
{ xlen: 6, linmax: 63, table: t16HB, hlen: t16l },
{ xlen: 8, linmax: 255, table: t16HB, hlen: t16l },
{ xlen: 10, linmax: 1023, table: t16HB, hlen: t16l },
{ xlen: 13, linmax: 8191, table: t16HB, hlen: t16l },
{ xlen: 4, linmax: 15, table: t24HB, hlen: t24l },
{ xlen: 5, linmax: 31, table: t24HB, hlen: t24l },
{ xlen: 6, linmax: 63, table: t24HB, hlen: t24l },
{ xlen: 7, linmax: 127, table: t24HB, hlen: t24l },
{ xlen: 8, linmax: 255, table: t24HB, hlen: t24l },
{ xlen: 9, linmax: 511, table: t24HB, hlen: t24l },
{ xlen: 11, linmax: 2047, table: t24HB, hlen: t24l },
{ xlen: 13, linmax: 8191, table: t24HB, hlen: t24l },
{ xlen: 0, linmax: 0, table: t32HB, hlen: t32l },
{ xlen: 0, linmax: 0, table: t33HB, hlen: t33l },
];
export { t32l, t33l, ht };

View File

@@ -0,0 +1,994 @@
import { Bits } from './Bits';
import type { CalcNoiseData } from './CalcNoiseData';
import { GrInfo } from './GrInfo';
import type { IIISideInfo } from './IIISideInfo';
import type { LameInternalFlags } from './LameInternalFlags';
import { QuantizePVT } from './QuantizePVT';
import * as tables from './Tables';
import { fillArray } from './arrays';
import { assert } from './assert';
import { NORM_TYPE, SBMAX_l, SBPSY_l, SHORT_TYPE } from './constants';
export class Takehiro {
private static readonly LARGE_BITS = 100000;
private readonly qupvt: QuantizePVT;
constructor(qupvt: QuantizePVT) {
this.qupvt = qupvt;
}
private readonly largetbl = [
0x010004, 0x050005, 0x070007, 0x090008, 0x0a0009, 0x0a000a, 0x0b000a, 0x0b000b, 0x0c000b, 0x0c000c, 0x0c000c, 0x0d000c, 0x0d000c,
0x0d000c, 0x0e000d, 0x0a000a, 0x040005, 0x060006, 0x080007, 0x090008, 0x0a0009, 0x0b000a, 0x0b000a, 0x0b000b, 0x0c000b, 0x0c000b,
0x0c000c, 0x0d000c, 0x0e000c, 0x0d000c, 0x0e000c, 0x0a000a, 0x070007, 0x080007, 0x090008, 0x0a0009, 0x0b0009, 0x0b000a, 0x0c000a,
0x0c000b, 0x0d000b, 0x0c000b, 0x0d000b, 0x0d000c, 0x0d000c, 0x0e000c, 0x0e000d, 0x0b0009, 0x090008, 0x090008, 0x0a0009, 0x0b0009,
0x0b000a, 0x0c000a, 0x0c000a, 0x0c000b, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x0c0009, 0x0a0009,
0x0a0009, 0x0b0009, 0x0b000a, 0x0c000a, 0x0c000a, 0x0d000a, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c,
0x0f000d, 0x0b0009, 0x0a000a, 0x0a0009, 0x0b000a, 0x0b000a, 0x0c000a, 0x0d000a, 0x0d000b, 0x0e000b, 0x0d000b, 0x0e000b, 0x0e000c,
0x0f000c, 0x0f000c, 0x0f000c, 0x10000c, 0x0c0009, 0x0b000a, 0x0b000a, 0x0b000a, 0x0c000a, 0x0d000a, 0x0d000b, 0x0d000b, 0x0d000b,
0x0e000b, 0x0e000c, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x10000d, 0x0c0009, 0x0b000b, 0x0b000a, 0x0c000a, 0x0c000a, 0x0d000b,
0x0d000b, 0x0d000b, 0x0e000b, 0x0e000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x11000d, 0x11000d, 0x0c000a, 0x0b000b, 0x0c000b,
0x0c000b, 0x0d000b, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000b, 0x0f000b, 0x0f000c, 0x0f000c, 0x0f000c, 0x10000c, 0x10000d, 0x10000d,
0x0c000a, 0x0c000b, 0x0c000b, 0x0c000b, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000b, 0x0f000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x10000c,
0x0f000d, 0x10000d, 0x0f000d, 0x0d000a, 0x0c000c, 0x0d000b, 0x0c000b, 0x0d000b, 0x0e000b, 0x0e000c, 0x0e000c, 0x0e000c, 0x0f000c,
0x10000c, 0x10000c, 0x10000d, 0x11000d, 0x11000d, 0x10000d, 0x0c000a, 0x0d000c, 0x0d000c, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000c,
0x0f000c, 0x10000c, 0x10000c, 0x10000c, 0x10000c, 0x10000d, 0x10000d, 0x0f000d, 0x10000d, 0x0d000a, 0x0d000c, 0x0e000c, 0x0e000c,
0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x11000c, 0x10000d, 0x10000d, 0x10000d, 0x10000d, 0x12000d, 0x0d000a,
0x0f000c, 0x0e000c, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x10000c, 0x10000c, 0x10000d, 0x12000d, 0x11000d, 0x11000d, 0x11000d,
0x13000d, 0x11000d, 0x0d000a, 0x0e000d, 0x0f000c, 0x0d000c, 0x0e000c, 0x10000c, 0x10000c, 0x0f000c, 0x10000d, 0x10000d, 0x11000d,
0x12000d, 0x11000d, 0x13000d, 0x11000d, 0x10000d, 0x0d000a, 0x0a0009, 0x0a0009, 0x0a0009, 0x0b0009, 0x0b0009, 0x0c0009, 0x0c0009,
0x0c0009, 0x0d0009, 0x0d0009, 0x0d0009, 0x0d000a, 0x0d000a, 0x0d000a, 0x0d000a, 0x0a0006,
] as const;
private readonly table23 = [0x010002, 0x040003, 0x070007, 0x040004, 0x050004, 0x070007, 0x060006, 0x070007, 0x080008] as const;
private readonly table56 = [
0x010003, 0x040004, 0x070006, 0x080008, 0x040004, 0x050004, 0x080006, 0x090007, 0x070005, 0x080006, 0x090007, 0x0a0008, 0x080007,
0x080007, 0x090008, 0x0a0009,
] as const;
private readonly scfsi_band = [0, 6, 11, 16, 21] as const;
private readonly subdv_table = [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 1],
[1, 1],
[1, 1],
[1, 2],
[2, 2],
[2, 3],
[2, 3],
[3, 4],
[3, 4],
[3, 4],
[4, 5],
[4, 5],
[4, 6],
[5, 6],
[5, 6],
[5, 7],
[6, 7],
[6, 7],
] as const;
private quantize_lines_xrpow_01(l: number, istep: number, xr: Float32Array, xrPos: number, ix: Int32Array, ixPos: number) {
const compareval0 = (1.0 - 0.4054) / istep;
assert(l > 0);
l >>= 1;
while (l-- !== 0) {
ix[ixPos++] = compareval0 > xr[xrPos++] ? 0 : 1;
ix[ixPos++] = compareval0 > xr[xrPos++] ? 0 : 1;
}
}
private quantize_lines_xrpow(l: number, istep: number, xr: Float32Array, xrPos: number, ix: Int32Array, ixPos: number) {
assert(l > 0);
l >>= 1;
const remaining = l % 2;
l >>= 1;
while (l-- !== 0) {
let x0 = xr[xrPos++] * istep;
let x1 = xr[xrPos++] * istep;
const rx0 = Math.trunc(x0);
let x2 = xr[xrPos++] * istep;
const rx1 = Math.trunc(x1);
let x3 = xr[xrPos++] * istep;
const rx2 = Math.trunc(x2);
x0 += this.qupvt.adj43(rx0);
const rx3 = Math.trunc(x3);
x1 += this.qupvt.adj43(rx1);
ix[ixPos++] = Math.trunc(x0);
x2 += this.qupvt.adj43(rx2);
ix[ixPos++] = Math.trunc(x1);
x3 += this.qupvt.adj43(rx3);
ix[ixPos++] = Math.trunc(x2);
ix[ixPos++] = Math.trunc(x3);
}
if (remaining !== 0) {
let x0 = xr[xrPos++] * istep;
let x1 = xr[xrPos++] * istep;
const rx0 = Math.trunc(x0);
const rx1 = Math.trunc(x1);
x0 += this.qupvt.adj43(rx0);
x1 += this.qupvt.adj43(rx1);
ix[ixPos++] = Math.trunc(x0);
ix[ixPos++] = Math.trunc(x1);
}
}
private quantize_xrpow(xp: Float32Array, pi: Int32Array, istep: number, codInfo: GrInfo, prevNoise: CalcNoiseData | null) {
let sfb;
let sfbmax;
let j = 0;
let accumulate = 0;
let accumulate01 = 0;
let xpPos = 0;
const iData = pi;
let iDataPos = 0;
let acc_iData = iData;
let acc_iDataPos = 0;
let acc_xp = xp;
let acc_xpPos = 0;
const prev_data_use = prevNoise !== null && codInfo.global_gain === prevNoise.global_gain;
if (codInfo.block_type === SHORT_TYPE) sfbmax = 38;
else sfbmax = 21;
for (sfb = 0; sfb <= sfbmax; sfb++) {
let step = -1;
if (prev_data_use || codInfo.block_type === NORM_TYPE) {
step =
codInfo.global_gain -
((codInfo.scalefac[sfb] + (codInfo.preflag !== 0 ? this.qupvt.pretab[sfb] : 0)) << (codInfo.scalefac_scale + 1)) -
codInfo.subblock_gain[codInfo.window[sfb]] * 8;
}
assert(codInfo.width[sfb] >= 0);
if (prev_data_use && prevNoise.step[sfb] === step) {
if (accumulate !== 0) {
this.quantize_lines_xrpow(accumulate, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
accumulate = 0;
}
if (accumulate01 !== 0) {
this.quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
accumulate01 = 0;
}
} else {
let l = codInfo.width[sfb];
if (j + codInfo.width[sfb] > codInfo.max_nonzero_coeff) {
const usefullsize = codInfo.max_nonzero_coeff - j + 1;
fillArray(pi, codInfo.max_nonzero_coeff, 576, 0);
l = usefullsize;
if (l < 0) {
l = 0;
}
sfb = sfbmax + 1;
}
if (accumulate === 0 && accumulate01 === 0) {
acc_iData = iData;
acc_iDataPos = iDataPos;
acc_xp = xp;
acc_xpPos = xpPos;
}
if (
prevNoise !== null &&
prevNoise.sfb_count1 > 0 &&
sfb >= prevNoise.sfb_count1 &&
prevNoise.step[sfb] > 0 &&
step >= prevNoise.step[sfb]
) {
if (accumulate !== 0) {
this.quantize_lines_xrpow(accumulate, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
accumulate = 0;
acc_iData = iData;
acc_iDataPos = iDataPos;
acc_xp = xp;
acc_xpPos = xpPos;
}
accumulate01 += l;
} else {
if (accumulate01 !== 0) {
this.quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
accumulate01 = 0;
acc_iData = iData;
acc_iDataPos = iDataPos;
acc_xp = xp;
acc_xpPos = xpPos;
}
accumulate += l;
}
if (l <= 0) {
if (accumulate01 !== 0) {
this.quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
accumulate01 = 0;
}
if (accumulate !== 0) {
this.quantize_lines_xrpow(accumulate, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
accumulate = 0;
}
break;
}
}
if (sfb <= sfbmax) {
iDataPos += codInfo.width[sfb];
xpPos += codInfo.width[sfb];
j += codInfo.width[sfb];
}
}
if (accumulate !== 0) {
this.quantize_lines_xrpow(accumulate, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
}
if (accumulate01 !== 0) {
this.quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_xpPos, acc_iData, acc_iDataPos);
}
}
private ix_max(ix: Int32Array, ixPos: number, endPos: number) {
let max1 = 0;
let max2 = 0;
do {
const x1 = ix[ixPos++];
const x2 = ix[ixPos++];
if (max1 < x1) max1 = x1;
if (max2 < x2) max2 = x2;
} while (ixPos < endPos);
if (max1 < max2) max1 = max2;
return max1;
}
private count_bit_ESC(ix: Int32Array, ixPos: number, end: number, t1: number, t2: number, s: Bits) {
const linbits = tables.ht[t1].xlen * 65536 + tables.ht[t2].xlen;
let sum = 0;
do {
let x = ix[ixPos++];
let y = ix[ixPos++];
if (x !== 0) {
if (x > 14) {
x = 15;
sum += linbits;
}
x *= 16;
}
if (y !== 0) {
if (y > 14) {
y = 15;
sum += linbits;
}
x += y;
}
sum += this.largetbl[x];
} while (ixPos < end);
const sum2 = sum & 0xffff;
sum >>= 16;
if (sum > sum2) {
sum = sum2;
t1 = t2;
}
s.bits += sum;
return t1;
}
private count_bit_noESC(ix: Int32Array, ixPos: number, end: number, s: Bits) {
let sum1 = 0;
const hlen1 = tables.ht[1].hlen;
assert(hlen1 !== undefined);
do {
const x = ix[ixPos + 0] * 2 + ix[ixPos + 1];
ixPos += 2;
sum1 += hlen1[x];
} while (ixPos < end);
s.bits += sum1;
return 1;
}
private count_bit_noESC_from2(ix: Int32Array, ixPos: number, end: number, t1: number, s: Bits) {
let sum = 0;
const { xlen } = tables.ht[t1];
let hlen;
if (t1 === 2) hlen = this.table23;
else hlen = this.table56;
do {
const x = ix[ixPos + 0] * xlen + ix[ixPos + 1];
ixPos += 2;
sum += hlen[x];
} while (ixPos < end);
const sum2 = sum & 0xffff;
sum >>= 16;
if (sum > sum2) {
sum = sum2;
t1++;
}
s.bits += sum;
return t1;
}
private count_bit_noESC_from3(ix: Int32Array, ixPos: number, end: number, t1: number, s: Bits) {
let sum1 = 0;
let sum2 = 0;
let sum3 = 0;
const { xlen } = tables.ht[t1];
const hlen1 = tables.ht[t1].hlen;
const hlen2 = tables.ht[t1 + 1].hlen;
const hlen3 = tables.ht[t1 + 2].hlen;
assert(hlen1 !== undefined);
assert(hlen2 !== undefined);
assert(hlen3 !== undefined);
do {
const x = ix[ixPos + 0] * xlen + ix[ixPos + 1];
ixPos += 2;
sum1 += hlen1[x];
sum2 += hlen2[x];
sum3 += hlen3[x];
} while (ixPos < end);
let t = t1;
if (sum1 > sum2) {
sum1 = sum2;
t++;
}
if (sum1 > sum3) {
sum1 = sum3;
t = t1 + 2;
}
s.bits += sum1;
return t;
}
private readonly huf_tbl_noESC = [1, 2, 5, 7, 7, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13] as const;
private choose_table(ix: Int32Array, ixPos: number, endPos: number, s: Bits) {
let max = this.ix_max(ix, ixPos, endPos);
switch (max) {
case 0:
return max;
case 1:
return this.count_bit_noESC(ix, ixPos, endPos, s);
case 2:
case 3:
return this.count_bit_noESC_from2(ix, ixPos, endPos, this.huf_tbl_noESC[max - 1], s);
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
return this.count_bit_noESC_from3(ix, ixPos, endPos, this.huf_tbl_noESC[max - 1], s);
default:
if (max > QuantizePVT.IXMAX_VAL) {
s.bits = Takehiro.LARGE_BITS;
return -1;
}
max -= 15;
let choice2;
for (choice2 = 24; choice2 < 32; choice2++) {
if (tables.ht[choice2].linmax >= max) {
break;
}
}
let choice;
for (choice = choice2 - 8; choice < 24; choice++) {
if (tables.ht[choice].linmax >= max) {
break;
}
}
return this.count_bit_ESC(ix, ixPos, endPos, choice, choice2, s);
}
}
noquant_count_bits(gfc: LameInternalFlags, gi: GrInfo, prev_noise: CalcNoiseData | null) {
const ix = gi.l3_enc;
let i = Math.min(576, ((gi.max_nonzero_coeff + 2) >> 1) << 1);
if (prev_noise !== null) prev_noise.sfb_count1 = 0;
for (; i > 1; i -= 2) if ((ix[i - 1] | ix[i - 2]) !== 0) break;
gi.count1 = i;
let a1 = 0;
let a2 = 0;
for (; i > 3; i -= 4) {
if (((ix[i - 1] | ix[i - 2] | ix[i - 3] | ix[i - 4]) & 0x7fffffff) > 1) {
break;
}
const p = ((ix[i - 4] * 2 + ix[i - 3]) * 2 + ix[i - 2]) * 2 + ix[i - 1];
a1 += tables.t32l[p];
a2 += tables.t33l[p];
}
let bits = a1;
gi.count1table_select = 0;
if (a1 > a2) {
bits = a2;
gi.count1table_select = 1;
}
gi.count1bits = bits;
gi.big_values = i;
if (i === 0) return bits;
if (gi.block_type === SHORT_TYPE) {
a1 = 3 * gfc.scalefac_band.s[3];
if (a1 > gi.big_values) a1 = gi.big_values;
a2 = gi.big_values;
} else if (gi.block_type === NORM_TYPE) {
assert(i <= 576);
a1 = gfc.bv_scf[i - 2];
gi.region0_count = gfc.bv_scf[i - 2];
a2 = gfc.bv_scf[i - 1];
gi.region1_count = gfc.bv_scf[i - 1];
assert(a1 + a2 + 2 < SBPSY_l);
a2 = gfc.scalefac_band.l[a1 + a2 + 2];
a1 = gfc.scalefac_band.l[a1 + 1];
if (a2 < i) {
const bi = new Bits(bits);
gi.table_select[2] = this.choose_table(ix, a2, i, bi);
bits = bi.bits;
}
} else {
gi.region0_count = 7;
gi.region1_count = SBMAX_l - 1 - 7 - 1;
a1 = gfc.scalefac_band.l[7 + 1];
a2 = i;
if (a1 > a2) {
a1 = a2;
}
}
a1 = Math.min(a1, i);
a2 = Math.min(a2, i);
assert(a1 >= 0);
assert(a2 >= 0);
if (a1 > 0) {
const bi = new Bits(bits);
gi.table_select[0] = this.choose_table(ix, 0, a1, bi);
bits = bi.bits;
}
if (a1 < a2) {
const bi = new Bits(bits);
gi.table_select[1] = this.choose_table(ix, a1, a2, bi);
bits = bi.bits;
}
if (gfc.use_best_huffman === 2) {
gi.part2_3_length = bits;
this.best_huffman_divide(gfc, gi);
bits = gi.part2_3_length;
}
if (prev_noise !== null) {
if (gi.block_type === NORM_TYPE) {
let sfb = 0;
while (gfc.scalefac_band.l[sfb] < gi.big_values) {
sfb++;
}
prev_noise.sfb_count1 = sfb;
}
}
return bits;
}
count_bits(gfc: LameInternalFlags, xr: Float32Array, gi: GrInfo, prev_noise: CalcNoiseData | null) {
const ix = gi.l3_enc;
const w = QuantizePVT.IXMAX_VAL / this.qupvt.ipow20(gi.global_gain);
if (gi.xrpow_max > w) return Takehiro.LARGE_BITS;
this.quantize_xrpow(xr, ix, this.qupvt.ipow20(gi.global_gain), gi, prev_noise);
if ((gfc.substep_shaping & 2) !== 0) {
let j = 0;
const gain = gi.global_gain + gi.scalefac_scale;
const roundfac = 0.634521682242439 / this.qupvt.ipow20(gain);
for (let sfb = 0; sfb < gi.sfbmax; sfb++) {
const width = gi.width[sfb];
assert(width >= 0);
if (gfc.pseudohalf[sfb] === 0) {
j += width;
} else {
let k;
for (k = j, j += width; k < j; ++k) {
ix[k] = xr[k] >= roundfac ? ix[k] : 0;
}
}
}
}
return this.noquant_count_bits(gfc, gi, prev_noise);
}
private recalc_divide_init(
gfc: LameInternalFlags,
cod_info: GrInfo,
ix: Int32Array,
r01_bits: Int32Array,
r01_div: Int32Array,
r0_tbl: Int32Array,
r1_tbl: Int32Array,
) {
const bigv = cod_info.big_values;
for (let r0 = 0; r0 <= 7 + 15; r0++) {
r01_bits[r0] = Takehiro.LARGE_BITS;
}
for (let r0 = 0; r0 < 16; r0++) {
const a1 = gfc.scalefac_band.l[r0 + 1];
if (a1 >= bigv) break;
let r0bits = 0;
let bi = new Bits(r0bits);
const r0t = this.choose_table(ix, 0, a1, bi);
r0bits = bi.bits;
for (let r1 = 0; r1 < 8; r1++) {
const a2 = gfc.scalefac_band.l[r0 + r1 + 2];
if (a2 >= bigv) break;
let bits = r0bits;
bi = new Bits(bits);
const r1t = this.choose_table(ix, a1, a2, bi);
bits = bi.bits;
if (r01_bits[r0 + r1] > bits) {
r01_bits[r0 + r1] = bits;
r01_div[r0 + r1] = r0;
r0_tbl[r0 + r1] = r0t;
r1_tbl[r0 + r1] = r1t;
}
}
}
}
private recalc_divide_sub(
gfc: LameInternalFlags,
cod_info2: GrInfo,
gi: GrInfo,
ix: Int32Array,
r01_bits: Int32Array,
r01_div: Int32Array,
r0_tbl: Int32Array,
r1_tbl: Int32Array,
) {
const bigv = cod_info2.big_values;
for (let r2 = 2; r2 < SBMAX_l + 1; r2++) {
const a2 = gfc.scalefac_band.l[r2];
if (a2 >= bigv) break;
let bits = r01_bits[r2 - 2] + cod_info2.count1bits;
if (gi.part2_3_length <= bits) break;
const bi = new Bits(bits);
const r2t = this.choose_table(ix, a2, bigv, bi);
bits = bi.bits;
if (gi.part2_3_length <= bits) continue;
gi.assign(cod_info2);
gi.part2_3_length = bits;
gi.region0_count = r01_div[r2 - 2];
gi.region1_count = r2 - 2 - r01_div[r2 - 2];
gi.table_select[0] = r0_tbl[r2 - 2];
gi.table_select[1] = r1_tbl[r2 - 2];
gi.table_select[2] = r2t;
}
}
best_huffman_divide(gfc: LameInternalFlags, gi: GrInfo) {
const cod_info2 = new GrInfo();
const ix = gi.l3_enc;
const r01_bits = new Int32Array(7 + 15 + 1);
const r01_div = new Int32Array(7 + 15 + 1);
const r0_tbl = new Int32Array(7 + 15 + 1);
const r1_tbl = new Int32Array(7 + 15 + 1);
if (gi.block_type === SHORT_TYPE && gfc.mode_gr === 1) return;
cod_info2.assign(gi);
if (gi.block_type === NORM_TYPE) {
this.recalc_divide_init(gfc, gi, ix, r01_bits, r01_div, r0_tbl, r1_tbl);
this.recalc_divide_sub(gfc, cod_info2, gi, ix, r01_bits, r01_div, r0_tbl, r1_tbl);
}
let i = cod_info2.big_values;
if (i === 0 || (ix[i - 2] | ix[i - 1]) > 1) return;
i = gi.count1 + 2;
if (i > 576) return;
cod_info2.assign(gi);
cod_info2.count1 = i;
let a1 = 0;
let a2 = 0;
assert(i <= 576);
for (; i > cod_info2.big_values; i -= 4) {
const p = ((ix[i - 4] * 2 + ix[i - 3]) * 2 + ix[i - 2]) * 2 + ix[i - 1];
a1 += tables.t32l[p];
a2 += tables.t33l[p];
}
cod_info2.big_values = i;
cod_info2.count1table_select = 0;
if (a1 > a2) {
a1 = a2;
cod_info2.count1table_select = 1;
}
cod_info2.count1bits = a1;
if (cod_info2.block_type === NORM_TYPE) {
this.recalc_divide_sub(gfc, cod_info2, gi, ix, r01_bits, r01_div, r0_tbl, r1_tbl);
} else {
cod_info2.part2_3_length = a1;
a1 = gfc.scalefac_band.l[7 + 1];
if (a1 > i) {
a1 = i;
}
if (a1 > 0) {
const bi = new Bits(cod_info2.part2_3_length);
cod_info2.table_select[0] = this.choose_table(ix, 0, a1, bi);
cod_info2.part2_3_length = bi.bits;
}
if (i > a1) {
const bi = new Bits(cod_info2.part2_3_length);
cod_info2.table_select[1] = this.choose_table(ix, a1, i, bi);
cod_info2.part2_3_length = bi.bits;
}
if (gi.part2_3_length > cod_info2.part2_3_length) gi.assign(cod_info2);
}
}
static readonly slen1_tab = [0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4] as const;
static readonly slen2_tab = [0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3] as const;
private readonly slen1_n = [1, 1, 1, 1, 8, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, 16] as const;
private readonly slen2_n = [1, 2, 4, 8, 1, 2, 4, 8, 2, 4, 8, 2, 4, 8, 4, 8] as const;
private scfsi_calc(ch: number, l3_side: IIISideInfo) {
let sfb;
const gi = l3_side.tt[1][ch];
const g0 = l3_side.tt[0][ch];
for (let i = 0; i < this.scfsi_band.length - 1; i++) {
for (sfb = this.scfsi_band[i]; sfb < this.scfsi_band[i + 1]; sfb++) {
if (g0.scalefac[sfb] !== gi.scalefac[sfb] && gi.scalefac[sfb] >= 0) break;
}
if (sfb === this.scfsi_band[i + 1]) {
for (sfb = this.scfsi_band[i]; sfb < this.scfsi_band[i + 1]; sfb++) {
gi.scalefac[sfb] = -1;
}
l3_side.scfsi[ch][i] = 1;
}
}
let s1 = 0;
let c1 = 0;
for (sfb = 0; sfb < 11; sfb++) {
if (gi.scalefac[sfb] === -1) continue;
c1++;
if (s1 < gi.scalefac[sfb]) s1 = gi.scalefac[sfb];
}
let s2 = 0;
let c2 = 0;
for (; sfb < SBPSY_l; sfb++) {
if (gi.scalefac[sfb] === -1) continue;
c2++;
if (s2 < gi.scalefac[sfb]) s2 = gi.scalefac[sfb];
}
for (let i = 0; i < 16; i++) {
if (s1 < this.slen1_n[i] && s2 < this.slen2_n[i]) {
const c = Takehiro.slen1_tab[i] * c1 + Takehiro.slen2_tab[i] * c2;
if (gi.part2_length > c) {
gi.part2_length = c;
gi.scalefac_compress = i;
}
}
}
}
best_scalefac_store(gfc: LameInternalFlags, gr: number, ch: number, l3_side: IIISideInfo) {
const gi = l3_side.tt[gr][ch];
let sfb;
let i;
let j;
let l;
let recalc = 0;
j = 0;
for (sfb = 0; sfb < gi.sfbmax; sfb++) {
const width = gi.width[sfb];
assert(width >= 0);
j += width;
for (l = -width; l < 0; l++) {
if (gi.l3_enc[l + j] !== 0) break;
}
if (l === 0) {
gi.scalefac[sfb] = -2;
recalc = -2;
}
}
if (gi.scalefac_scale === 0 && gi.preflag === 0) {
let s = 0;
for (sfb = 0; sfb < gi.sfbmax; sfb++) if (gi.scalefac[sfb] > 0) s |= gi.scalefac[sfb];
if ((s & 1) === 0 && s !== 0) {
for (sfb = 0; sfb < gi.sfbmax; sfb++) {
if (gi.scalefac[sfb] > 0) {
gi.scalefac[sfb] >>= 1;
}
}
gi.scalefac_scale = 1;
recalc = 1;
}
}
if (gi.preflag === 0 && gi.block_type !== SHORT_TYPE && gfc.mode_gr === 2) {
for (sfb = 11; sfb < SBPSY_l; sfb++) if (gi.scalefac[sfb] < this.qupvt.pretab[sfb] && gi.scalefac[sfb] !== -2) break;
if (sfb === SBPSY_l) {
for (sfb = 11; sfb < SBPSY_l; sfb++) if (gi.scalefac[sfb] > 0) gi.scalefac[sfb] -= this.qupvt.pretab[sfb];
gi.preflag = 1;
recalc = 1;
}
}
for (i = 0; i < 4; i++) {
l3_side.scfsi[ch][i] = 0;
}
if (gfc.mode_gr === 2 && gr === 1 && l3_side.tt[0][ch].block_type !== SHORT_TYPE && l3_side.tt[1][ch].block_type !== SHORT_TYPE) {
this.scfsi_calc(ch, l3_side);
recalc = 0;
}
for (sfb = 0; sfb < gi.sfbmax; sfb++) {
if (gi.scalefac[sfb] === -2) {
gi.scalefac[sfb] = 0;
}
}
if (recalc !== 0) {
if (gfc.mode_gr === 2) {
this.scale_bitcount(gi);
} else {
this.scale_bitcount_lsf(gfc, gi);
}
}
}
private all_scalefactors_not_negative(scalefac: Int32Array, n: number) {
for (let i = 0; i < n; ++i) {
if (scalefac[i] < 0) return false;
}
return true;
}
private readonly scale_short = [0, 18, 36, 54, 54, 36, 54, 72, 54, 72, 90, 72, 90, 108, 108, 126] as const;
private readonly scale_mixed = [0, 18, 36, 54, 51, 35, 53, 71, 52, 70, 88, 69, 87, 105, 104, 122] as const;
private readonly scale_long = [0, 10, 20, 30, 33, 21, 31, 41, 32, 42, 52, 43, 53, 63, 64, 74] as const;
scale_bitcount(cod_info: GrInfo) {
let k;
let sfb;
let max_slen1 = 0;
let max_slen2 = 0;
let tab;
const { scalefac } = cod_info;
assert(this.all_scalefactors_not_negative(scalefac, cod_info.sfbmax));
if (cod_info.block_type === SHORT_TYPE) {
tab = this.scale_short;
if (cod_info.mixed_block_flag !== 0) tab = this.scale_mixed;
} else {
tab = this.scale_long;
if (cod_info.preflag === 0) {
for (sfb = 11; sfb < SBPSY_l; sfb++) if (scalefac[sfb] < this.qupvt.pretab[sfb]) break;
if (sfb === SBPSY_l) {
cod_info.preflag = 1;
for (sfb = 11; sfb < SBPSY_l; sfb++) scalefac[sfb] -= this.qupvt.pretab[sfb];
}
}
}
for (sfb = 0; sfb < cod_info.sfbdivide; sfb++) {
if (max_slen1 < scalefac[sfb]) {
max_slen1 = scalefac[sfb];
}
}
for (; sfb < cod_info.sfbmax; sfb++) {
if (max_slen2 < scalefac[sfb]) {
max_slen2 = scalefac[sfb];
}
}
cod_info.part2_length = Takehiro.LARGE_BITS;
for (k = 0; k < 16; k++) {
if (max_slen1 < this.slen1_n[k] && max_slen2 < this.slen2_n[k] && cod_info.part2_length > tab[k]) {
cod_info.part2_length = tab[k];
cod_info.scalefac_compress = k;
}
}
return cod_info.part2_length === Takehiro.LARGE_BITS;
}
private readonly max_range_sfac_tab = [
[15, 15, 7, 7],
[15, 15, 7, 0],
[7, 3, 0, 0],
[15, 31, 31, 0],
[7, 7, 7, 0],
[3, 3, 0, 0],
] as const;
private readonly log2tab = [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4] as const;
scale_bitcount_lsf(_gfc: LameInternalFlags, cod_info: GrInfo) {
let table_number;
let row_in_table;
let partition;
let nr_sfb;
let window;
let over;
let i;
let sfb;
const max_sfac = new Int32Array(4);
const { scalefac } = cod_info;
if (cod_info.preflag !== 0) {
table_number = 2;
} else {
table_number = 0;
}
for (i = 0; i < 4; i++) {
max_sfac[i] = 0;
}
if (cod_info.block_type === SHORT_TYPE) {
row_in_table = 1;
const partition_table = this.qupvt.nr_of_sfb_block[table_number][row_in_table];
for (sfb = 0, partition = 0; partition < 4; partition++) {
nr_sfb = partition_table[partition] / 3;
for (i = 0; i < nr_sfb; i++, sfb++) {
for (window = 0; window < 3; window++) {
if (scalefac[sfb * 3 + window] > max_sfac[partition]) {
max_sfac[partition] = scalefac[sfb * 3 + window];
}
}
}
}
} else {
row_in_table = 0;
const partition_table = this.qupvt.nr_of_sfb_block[table_number][row_in_table];
for (sfb = 0, partition = 0; partition < 4; partition++) {
nr_sfb = partition_table[partition];
for (i = 0; i < nr_sfb; i++, sfb++) {
if (scalefac[sfb] > max_sfac[partition]) {
max_sfac[partition] = scalefac[sfb];
}
}
}
}
for (over = false, partition = 0; partition < 4; partition++) {
if (max_sfac[partition] > this.max_range_sfac_tab[table_number][partition]) {
over = true;
}
}
if (!over) {
cod_info.sfb_partition_table = this.qupvt.nr_of_sfb_block[table_number][row_in_table];
for (partition = 0; partition < 4; partition++) {
cod_info.slen[partition] = this.log2tab[max_sfac[partition]];
}
const slen1 = cod_info.slen[0];
const slen2 = cod_info.slen[1];
const slen3 = cod_info.slen[2];
const slen4 = cod_info.slen[3];
switch (table_number) {
case 0:
cod_info.scalefac_compress = ((slen1 * 5 + slen2) << 4) + (slen3 << 2) + slen4;
break;
case 1:
cod_info.scalefac_compress = 400 + ((slen1 * 5 + slen2) << 2) + slen3;
break;
case 2:
cod_info.scalefac_compress = 500 + slen1 * 3 + slen2;
break;
default:
console.warn('intensity stereo not implemented yet');
break;
}
}
if (!over) {
assert(cod_info.sfb_partition_table !== null);
cod_info.part2_length = 0;
for (partition = 0; partition < 4; partition++)
cod_info.part2_length += cod_info.slen[partition] * cod_info.sfb_partition_table[partition];
}
return over;
}
huffman_init(gfc: LameInternalFlags) {
for (let i = 2; i <= 576; i += 2) {
let scfb_anz = 0;
let bv_index;
while (gfc.scalefac_band.l[++scfb_anz] < i);
bv_index = this.subdv_table[scfb_anz][0];
while (gfc.scalefac_band.l[bv_index + 1] > i) bv_index--;
if (bv_index < 0) {
bv_index = this.subdv_table[scfb_anz][0];
}
gfc.bv_scf[i - 2] = bv_index;
bv_index = this.subdv_table[scfb_anz][1];
while (gfc.scalefac_band.l[bv_index + gfc.bv_scf[i - 2] + 2] > i) bv_index--;
if (bv_index < 0) {
bv_index = this.subdv_table[scfb_anz][1];
}
gfc.bv_scf[i - 1] = bv_index;
}
}
}

View File

@@ -0,0 +1,3 @@
export class TotalBytes {
total = 0;
}

View File

@@ -0,0 +1,450 @@
import type { LameGlobalFlags } from './LameGlobalFlags';
import type { Quality } from './Quality';
import { VbrMode } from './VbrMode';
import { equals } from './math';
interface VBRPreset {
readonly vbr_q: Quality;
readonly quant_comp: 9;
readonly quant_comp_s: 9;
readonly expY: 0 | 1;
readonly st_lrm: number;
readonly st_s: number;
readonly masking_adj: number;
readonly masking_adj_short: number;
readonly ath_lower: number;
readonly ath_curve: number;
readonly ath_sensitivity: number;
readonly interch: number;
readonly safejoint: number;
readonly sfb21mod: number;
readonly msfix: number;
}
type PresetMap = Record<Quality, VBRPreset>;
export class VBRPresets {
private readonly oldSwitchMap = [
{
vbr_q: 0,
quant_comp: 9,
quant_comp_s: 9,
expY: 0,
st_lrm: 5.2,
st_s: 125,
masking_adj: -4.2,
masking_adj_short: -6.3,
ath_lower: 4.8,
ath_curve: 1,
ath_sensitivity: 0,
interch: 0,
safejoint: 2,
sfb21mod: 21,
msfix: 0.97,
},
{
vbr_q: 1,
quant_comp: 9,
quant_comp_s: 9,
expY: 0,
st_lrm: 5.3,
st_s: 125,
masking_adj: -3.6,
masking_adj_short: -5.6,
ath_lower: 4.5,
ath_curve: 1.5,
ath_sensitivity: 0,
interch: 0,
safejoint: 2,
sfb21mod: 21,
msfix: 1.35,
},
{
vbr_q: 2,
quant_comp: 9,
quant_comp_s: 9,
expY: 0,
st_lrm: 5.6,
st_s: 125,
masking_adj: -2.2,
masking_adj_short: -3.5,
ath_lower: 2.8,
ath_curve: 2,
ath_sensitivity: 0,
interch: 0,
safejoint: 2,
sfb21mod: 21,
msfix: 1.49,
},
{
vbr_q: 3,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 5.8,
st_s: 130,
masking_adj: -1.8,
masking_adj_short: -2.8,
ath_lower: 2.6,
ath_curve: 3,
ath_sensitivity: -4,
interch: 0,
safejoint: 2,
sfb21mod: 20,
msfix: 1.64,
},
{
vbr_q: 4,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 6,
st_s: 135,
masking_adj: -0.7,
masking_adj_short: -1.1,
ath_lower: 1.1,
ath_curve: 3.5,
ath_sensitivity: -8,
interch: 0,
safejoint: 2,
sfb21mod: 0,
msfix: 1.79,
},
{
vbr_q: 5,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 6.4,
st_s: 140,
masking_adj: 0.5,
masking_adj_short: 0.4,
ath_lower: -7.5,
ath_curve: 4,
ath_sensitivity: -12,
interch: 0.0002,
safejoint: 0,
sfb21mod: 0,
msfix: 1.95,
},
{
vbr_q: 6,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 6.6,
st_s: 145,
masking_adj: 0.67,
masking_adj_short: 0.65,
ath_lower: -14.7,
ath_curve: 6.5,
ath_sensitivity: -19,
interch: 0.0004,
safejoint: 0,
sfb21mod: 0,
msfix: 2.3,
},
{
vbr_q: 7,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 6.6,
st_s: 145,
masking_adj: 0.8,
masking_adj_short: 0.75,
ath_lower: -19.7,
ath_curve: 8,
ath_sensitivity: -22,
interch: 0.0006,
safejoint: 0,
sfb21mod: 0,
msfix: 2.7,
},
{
vbr_q: 8,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 6.6,
st_s: 145,
masking_adj: 1.2,
masking_adj_short: 1.15,
ath_lower: -27.5,
ath_curve: 10,
ath_sensitivity: -23,
interch: 0.0007,
safejoint: 0,
sfb21mod: 0,
msfix: 0,
},
{
vbr_q: 9,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 6.6,
st_s: 145,
masking_adj: 1.6,
masking_adj_short: 1.6,
ath_lower: -36,
ath_curve: 11,
ath_sensitivity: -25,
interch: 0.0008,
safejoint: 0,
sfb21mod: 0,
msfix: 0,
},
] as const satisfies PresetMap;
private readonly switchMap = [
{
vbr_q: 0,
quant_comp: 9,
quant_comp_s: 9,
expY: 0,
st_lrm: 4.2,
st_s: 25,
masking_adj: -7,
masking_adj_short: -4,
ath_lower: 7.5,
ath_curve: 1,
ath_sensitivity: 0,
interch: 0,
safejoint: 2,
sfb21mod: 26,
msfix: 0.97,
},
{
vbr_q: 1,
quant_comp: 9,
quant_comp_s: 9,
expY: 0,
st_lrm: 4.2,
st_s: 25,
masking_adj: -5.6,
masking_adj_short: -3.6,
ath_lower: 4.5,
ath_curve: 1.5,
ath_sensitivity: 0,
interch: 0,
safejoint: 2,
sfb21mod: 21,
msfix: 1.35,
},
{
vbr_q: 2,
quant_comp: 9,
quant_comp_s: 9,
expY: 0,
st_lrm: 4.2,
st_s: 25,
masking_adj: -4.4,
masking_adj_short: -1.8,
ath_lower: 2,
ath_curve: 2,
ath_sensitivity: 0,
interch: 0,
safejoint: 2,
sfb21mod: 18,
msfix: 1.49,
},
{
vbr_q: 3,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 4.2,
st_s: 25,
masking_adj: -3.4,
masking_adj_short: -1.25,
ath_lower: 1.1,
ath_curve: 3,
ath_sensitivity: -4,
interch: 0,
safejoint: 2,
sfb21mod: 15,
msfix: 1.64,
},
{
vbr_q: 4,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 4.2,
st_s: 25,
masking_adj: -2.2,
masking_adj_short: 0.1,
ath_lower: 0,
ath_curve: 3.5,
ath_sensitivity: -8,
interch: 0,
safejoint: 2,
sfb21mod: 0,
msfix: 1.79,
},
{
vbr_q: 5,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 4.2,
st_s: 25,
masking_adj: -1,
masking_adj_short: 1.65,
ath_lower: -7.7,
ath_curve: 4,
ath_sensitivity: -12,
interch: 0.0002,
safejoint: 0,
sfb21mod: 0,
msfix: 1.95,
},
{
vbr_q: 6,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 4.2,
st_s: 25,
masking_adj: -0,
masking_adj_short: 2.47,
ath_lower: -7.7,
ath_curve: 6.5,
ath_sensitivity: -19,
interch: 0.0004,
safejoint: 0,
sfb21mod: 0,
msfix: 2,
},
{
vbr_q: 7,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 4.2,
st_s: 25,
masking_adj: 0.5,
masking_adj_short: 2,
ath_lower: -14.5,
ath_curve: 8,
ath_sensitivity: -22,
interch: 0.0006,
safejoint: 0,
sfb21mod: 0,
msfix: 2,
},
{
vbr_q: 8,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 4.2,
st_s: 25,
masking_adj: 1,
masking_adj_short: 2.4,
ath_lower: -22,
ath_curve: 10,
ath_sensitivity: -23,
interch: 0.0007,
safejoint: 0,
sfb21mod: 0,
msfix: 2,
},
{
vbr_q: 9,
quant_comp: 9,
quant_comp_s: 9,
expY: 1,
st_lrm: 4.2,
st_s: 25,
masking_adj: 1.5,
masking_adj_short: 2.95,
ath_lower: -30,
ath_curve: 11,
ath_sensitivity: -25,
interch: 0.0008,
safejoint: 0,
sfb21mod: 0,
msfix: 2,
},
] as const satisfies PresetMap;
private setVBRQuality(gfp: LameGlobalFlags, VBR_q: Quality): void {
if (VBR_q < 0) {
VBR_q = 0;
}
if (VBR_q > 9) {
VBR_q = 9;
}
gfp.VBR_q = VBR_q;
}
public apply(gfp: LameGlobalFlags, a: Quality) {
const presetMap = gfp.VBR === VbrMode.vbr_rh ? this.oldSwitchMap : this.switchMap;
const set: VBRPreset = presetMap[a];
this.setVBRQuality(gfp, set.vbr_q);
if (equals(gfp.quant_comp, -1)) {
gfp.quant_comp = set.quant_comp;
}
if (equals(gfp.quant_comp_short, -1)) {
gfp.quant_comp_short = set.quant_comp_s;
}
if (set.expY !== 0) {
gfp.experimentalY = true;
}
if (equals(gfp.internal_flags.nsPsy.attackthre, -1)) {
gfp.internal_flags.nsPsy.attackthre = set.st_lrm;
}
if (equals(gfp.internal_flags.nsPsy.attackthre_s, -1)) {
gfp.internal_flags.nsPsy.attackthre_s = set.st_s;
}
if (equals(gfp.maskingadjust, 0)) {
gfp.maskingadjust = set.masking_adj;
}
if (equals(gfp.maskingadjust_short, 0)) {
gfp.maskingadjust_short = set.masking_adj_short;
}
if (equals(-gfp.ATHlower * 10.0, 0)) {
gfp.ATHlower = -set.ath_lower / 10.0;
}
if (equals(gfp.ATHcurve, -1)) {
gfp.ATHcurve = set.ath_curve;
}
if (equals(gfp.athaa_sensitivity, -1)) {
gfp.athaa_sensitivity = set.ath_sensitivity;
}
if (set.interch > 0 && equals(gfp.interChRatio, -1)) {
gfp.interChRatio = set.interch;
}
if (set.safejoint > 0) {
gfp.exp_nspsytune |= set.safejoint;
}
if (set.sfb21mod > 0) {
gfp.exp_nspsytune |= set.sfb21mod << 20;
}
if (equals(gfp.msfix, -1)) {
gfp.msfix = set.msfix;
}
gfp.VBR_q = a;
}
}

View File

@@ -0,0 +1,32 @@
export class VBRTag {
private readonly crc16Lookup = [
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01,
0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0,
0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581,
0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141,
0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01,
0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0,
0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681,
0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01,
0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0,
0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381,
0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741,
0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901,
0x59c0, 0x5880, 0x9841, 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0,
0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081,
0x4040,
] as const;
private crcUpdateLookup(value: number, crc: number) {
const tmp = crc ^ value;
crc = (crc >> 8) ^ this.crc16Lookup[tmp & 0xff];
return crc;
}
updateMusicCRC(crc: Int32Array, buffer: Uint8Array, bufferPos: number, size: number) {
for (let i = 0; i < size; ++i) {
crc[0] = this.crcUpdateLookup(buffer[bufferPos + i], crc[0]);
}
}
}

View File

@@ -0,0 +1,8 @@
export const enum VbrMode {
vbr_off,
vbr_mt,
vbr_rh,
vbr_abr,
vbr_mtrh,
vbr_default = VbrMode.vbr_mtrh,
}

View File

@@ -0,0 +1,60 @@
export class WavHeader {
private constructor(
public readonly dataOffset: number,
public readonly dataLen: number,
public readonly channels: number,
public readonly sampleRate: number,
) {}
private static fourccToInt(fourcc: string) {
return (fourcc.charCodeAt(0) << 24) | (fourcc.charCodeAt(1) << 16) | (fourcc.charCodeAt(2) << 8) | fourcc.charCodeAt(3);
}
private static readonly RIFF = WavHeader.fourccToInt('RIFF');
private static readonly WAVE = WavHeader.fourccToInt('WAVE');
private static readonly fmt_ = WavHeader.fourccToInt('fmt ');
private static readonly data = WavHeader.fourccToInt('data');
static readHeader(dataView: DataView) {
let header = dataView.getUint32(0, false);
if (WavHeader.RIFF !== header) {
throw new Error('Invalid WAV file');
}
dataView.getUint32(4, true);
if (WavHeader.WAVE !== dataView.getUint32(8, false)) {
throw new Error('Invalid WAV file');
}
if (WavHeader.fmt_ !== dataView.getUint32(12, false)) {
throw new Error('Invalid WAV file');
}
const fmtLen = dataView.getUint32(16, true);
let pos = 16 + 4;
let channels = 0;
let sampleRate = 0;
switch (fmtLen) {
case 16:
case 18:
channels = dataView.getUint16(pos + 2, true);
sampleRate = dataView.getUint32(pos + 4, true);
break;
default:
throw new Error('extended fmt chunk not implemented');
}
pos += fmtLen;
let len = 0;
while (WavHeader.data !== header) {
header = dataView.getUint32(pos, false);
len = dataView.getUint32(pos + 4, true);
if (WavHeader.data === header) {
break;
}
pos += len + 8;
}
return new WavHeader(pos + 8, len, channels, sampleRate);
}
}

View File

@@ -0,0 +1,40 @@
type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
const hasSubarray = (a: unknown): a is Pick<TypedArray, 'subarray'> =>
typeof a === 'object' && a !== null && 'subarray' in a && typeof a.subarray === 'function';
const hasSet = (a: unknown): a is Pick<TypedArray, 'set'> =>
typeof a === 'object' && a !== null && 'set' in a && typeof a.set === 'function';
export function copyArray<T>(src: ArrayLike<T>, srcPos: number, dest: { [index: number]: T }, destPos: number, length: number) {
if (hasSubarray(src) && hasSet(dest)) {
dest.set(src.subarray(srcPos, srcPos + length), destPos);
return;
}
const srcEnd = srcPos + length;
while (srcPos < srcEnd) dest[destPos++] = src[srcPos++];
}
export function sortArray(a: TypedArray, fromIndex: number, toIndex: number) {
const sorted = Array.from(a).slice(fromIndex, toIndex).sort();
for (let i = fromIndex; i < toIndex; i++) {
a[i] = sorted[i - fromIndex];
}
}
export function fillArray(a: TypedArray, val: number): void;
export function fillArray(a: TypedArray, fromIndex: number, toIndex: number, val: number): void;
export function fillArray(...args: [a: TypedArray, val: number] | [a: TypedArray, fromIndex: number, toIndex: number, val: number]) {
if (args.length === 2) {
const [a, val] = args;
for (let i = 0; i < a.length; i++) {
a[i] = val;
}
} else {
const [a, fromIndex, toIndex, val] = args;
for (let i = fromIndex; i < toIndex; i++) {
a[i] = val;
}
}
}

View File

@@ -0,0 +1,8 @@
export function assert(condition: boolean, _message?: string): asserts condition {
if (!condition) {
// TODO: There is a condition generating multiple NaN values that was never
// addressed in the original code and is not clear how to handle it.
// Originally this assertion was commented out, probably because of it.
// throw new Error(message);
}
}

View File

@@ -0,0 +1,59 @@
export type Bitrate = 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 144 | 160 | 192 | 224 | 256 | 320;
const bitratesMap = {
mpeg2: [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1],
mpeg1: [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1],
mpeg2_5: [0, 8, 16, 24, 32, 40, 48, 56, 64, -1, -1, -1, -1, -1, -1, -1],
} as const satisfies Record<string, readonly (Bitrate | 0 | -1)[]>;
const LOW_SAMPLE_RATE_THRESHOLD = 16000;
const getBitrates = (version: 0 | 1, samplerate = LOW_SAMPLE_RATE_THRESHOLD) => {
if (samplerate < 16000) {
return bitratesMap.mpeg2_5;
}
if (version === 0) {
return bitratesMap.mpeg2;
}
return bitratesMap.mpeg1;
};
export function findNearestBitrate(bRate: number, version: 0 | 1, samplerate: number): Bitrate {
const bitrates = getBitrates(version, samplerate);
let matchingBitrate: Bitrate = bitrates[1];
for (let i = 2; i <= 14; i++) {
const bitrate = bitrates[i];
if (bitrate !== 0 && bitrate !== -1) {
if (Math.abs(bitrate - bRate) < Math.abs(matchingBitrate - bRate)) {
matchingBitrate = bitrate;
}
}
}
return matchingBitrate;
}
export type BitrateIndex = number & { __brand: 'BitrateIndex' };
export function findBitrateIndex(bRate: number, version: 0 | 1, samplerate: number): BitrateIndex {
const bitrates = getBitrates(version, samplerate);
for (let i = 1; i <= 14; i++) {
const bitrate = bitrates[i];
if (bitrate > 0) {
if (bitrate === bRate) {
return i as BitrateIndex;
}
}
}
throw new Error(`Invalid bitrate ${bRate}`);
}
export function getBitrate(version: 0 | 1, index: number): Bitrate | 0 | -1 {
const bitrates = getBitrates(version);
return bitrates[index];
}

View File

@@ -0,0 +1,65 @@
export const MAX_FLOAT32_VALUE = 3.4028235e38;
export const LOG10 = 2.302585092994046;
export const SBMAX_l = 22;
export const SBMAX_s = 13;
export const PSFB21 = 6;
export const PSFB12 = 6;
export const CBANDS = 64;
export const NORM_TYPE = 0;
export const START_TYPE = 1;
export const SHORT_TYPE = 2;
export const STOP_TYPE = 3;
export const LAME_MAXALBUMART = 128 * 1024;
export const LAME_MAXMP3BUFFER = 16384 + LAME_MAXALBUMART;
export const BLKSIZE = 1024;
export const HBLKSIZE = BLKSIZE / 2 + 1;
export const BLKSIZE_s = 256;
export const HBLKSIZE_s = BLKSIZE_s / 2 + 1;
export const SFBMAX = SBMAX_s * 3;
export const MPG_MD_LR_LR = 0;
export const MPG_MD_MS_LR = 2;
export const SBPSY_l = 21;
export const SBPSY_s = 12;
export const SBLIMIT = 32;
export const ENCDELAY = 576;
export const POSTDELAY = 1152;
export const MDCTDELAY = 48;
export const FFTOFFSET = 224 + MDCTDELAY;
export const LAME_ID = 0xfff88e3b;
export const BPC = 320;
export const MAX_BITS_PER_GRANULE = 7680;
export const MAX_BITS_PER_CHANNEL = 4095;
export const MAX_HEADER_BUF = 256;
export const MFSIZE = 3 * 1152 + ENCDELAY - MDCTDELAY;

View File

@@ -0,0 +1,9 @@
const LAME_MAJOR_VERSION = 3;
const LAME_MINOR_VERSION = 98;
const LAME_PATCH_VERSION = 4;
export function getLameShortVersion() {
return `${LAME_MAJOR_VERSION}.${LAME_MINOR_VERSION}.${LAME_PATCH_VERSION}` as const;
}

View File

@@ -0,0 +1 @@
export { Mp3Encoder } from './Mp3Encoder';

View File

@@ -0,0 +1,28 @@
export function blackmanWindow(x: number, fcn: number, l: number) {
const wcn = Math.PI * fcn;
x /= l;
if (x < 0) x = 0;
if (x > 1) x = 1;
const x2 = x - 0.5;
const bkwn = 0.42 - 0.5 * Math.cos(2 * x * Math.PI) + 0.08 * Math.cos(4 * x * Math.PI);
if (Math.abs(x2) < 1e-9) return wcn / Math.PI;
return (bkwn * Math.sin(l * wcn * x2)) / (Math.PI * l * x2);
}
export function gcd(i: number, j: number): number {
return j !== 0 ? gcd(j, i % j) : i;
}
export function isCloseToEachOther(a: number, b: number) {
return Math.abs(a) > Math.abs(b) ? Math.abs(a - b) <= Math.abs(a) * 1e-6 : Math.abs(a - b) <= Math.abs(b) * 1e-6;
}
export function fsqr(d: number) {
return d * d;
}
export function equals(a: number, b: number) {
return !(Math.abs(a - b) > 0);
}

View File

@@ -0,0 +1,14 @@
export type SampleRate = 44100 | 48000 | 32000 | 24000 | 22050 | 16000 | 12000 | 11025 | 8000;
export function findNearestSampleRate(freq: number): SampleRate {
if (freq <= 8000) return 8000;
if (freq <= 11025) return 11025;
if (freq <= 12000) return 12000;
if (freq <= 16000) return 16000;
if (freq <= 22050) return 22050;
if (freq <= 24000) return 24000;
if (freq <= 32000) return 32000;
if (freq <= 44100) return 44100;
return 48000;
}

View File

@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["**/*.spec.*", "jest.config.*"]
}

View File

@@ -0,0 +1,19 @@
{
"extends": "@rocket.chat/tsconfig/client.json",
"compilerOptions": {
"rootDir": "./src",
"target": "es2024",
"lib": ["WebWorker"],
"outDir": "./dist",
"erasableSyntaxOnly": false,
"isolatedModules": true,
"noFallthroughCasesInSwitch": false
},
"include": ["src/**/*"],
"typedocOptions": {
"entryPoints": ["src/index.ts"],
"out": "../../static/mp3-encoder",
"skipErrorChecking": true
},
"exclude": ["./dist", "./jest.config.ts"]
}