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
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:
143
packages/mp3-encoder/CHANGELOG.md
Normal file
143
packages/mp3-encoder/CHANGELOG.md
Normal 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))
|
||||
7
packages/mp3-encoder/jest.config.ts
Normal file
7
packages/mp3-encoder/jest.config.ts
Normal 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;
|
||||
53
packages/mp3-encoder/package.json
Normal file
53
packages/mp3-encoder/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
33
packages/mp3-encoder/rollup.config.mjs
Normal file
33
packages/mp3-encoder/rollup.config.mjs
Normal 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(),
|
||||
],
|
||||
});
|
||||
42
packages/mp3-encoder/src/index.spec.ts
Normal file
42
packages/mp3-encoder/src/index.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
66
packages/mp3-encoder/src/index.ts
Normal file
66
packages/mp3-encoder/src/index.ts
Normal 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;
|
||||
431
packages/mp3-encoder/src/lame/ABRPresets.ts
Normal file
431
packages/mp3-encoder/src/lame/ABRPresets.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
29
packages/mp3-encoder/src/lame/ATH.ts
Normal file
29
packages/mp3-encoder/src/lame/ATH.ts
Normal 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);
|
||||
}
|
||||
693
packages/mp3-encoder/src/lame/BitStream.ts
Normal file
693
packages/mp3-encoder/src/lame/BitStream.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
7
packages/mp3-encoder/src/lame/Bits.ts
Normal file
7
packages/mp3-encoder/src/lame/Bits.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export class Bits {
|
||||
public bits: number;
|
||||
|
||||
constructor(bits: number) {
|
||||
this.bits = Math.trunc(bits);
|
||||
}
|
||||
}
|
||||
115
packages/mp3-encoder/src/lame/CBRNewIterationLoop.ts
Normal file
115
packages/mp3-encoder/src/lame/CBRNewIterationLoop.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
11
packages/mp3-encoder/src/lame/CalcNoiseData.ts
Normal file
11
packages/mp3-encoder/src/lame/CalcNoiseData.ts
Normal 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);
|
||||
}
|
||||
13
packages/mp3-encoder/src/lame/CalcNoiseResult.ts
Normal file
13
packages/mp3-encoder/src/lame/CalcNoiseResult.ts
Normal 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;
|
||||
}
|
||||
305
packages/mp3-encoder/src/lame/Encoder.ts
Normal file
305
packages/mp3-encoder/src/lame/Encoder.ts
Normal 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;
|
||||
}
|
||||
213
packages/mp3-encoder/src/lame/FFT.ts
Normal file
213
packages/mp3-encoder/src/lame/FFT.ts
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
402
packages/mp3-encoder/src/lame/GainAnalysis.ts
Normal file
402
packages/mp3-encoder/src/lame/GainAnalysis.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
102
packages/mp3-encoder/src/lame/GrInfo.ts
Normal file
102
packages/mp3-encoder/src/lame/GrInfo.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
7
packages/mp3-encoder/src/lame/Header.ts
Normal file
7
packages/mp3-encoder/src/lame/Header.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export class Header {
|
||||
write_timing = 0;
|
||||
|
||||
ptr = 0;
|
||||
|
||||
readonly buf = new Uint8Array(40);
|
||||
}
|
||||
9
packages/mp3-encoder/src/lame/HuffCodeTab.ts
Normal file
9
packages/mp3-encoder/src/lame/HuffCodeTab.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface HuffCodeTab {
|
||||
xlen: number;
|
||||
|
||||
linmax: number;
|
||||
|
||||
table?: readonly number[];
|
||||
|
||||
hlen?: readonly number[];
|
||||
}
|
||||
15
packages/mp3-encoder/src/lame/IIISideInfo.ts
Normal file
15
packages/mp3-encoder/src/lame/IIISideInfo.ts
Normal 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));
|
||||
}
|
||||
7
packages/mp3-encoder/src/lame/III_psy_ratio.ts
Normal file
7
packages/mp3-encoder/src/lame/III_psy_ratio.ts
Normal 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();
|
||||
}
|
||||
17
packages/mp3-encoder/src/lame/III_psy_xmin.ts
Normal file
17
packages/mp3-encoder/src/lame/III_psy_xmin.ts
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
packages/mp3-encoder/src/lame/InOut.ts
Normal file
5
packages/mp3-encoder/src/lame/InOut.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export class InOut {
|
||||
n_in = 0;
|
||||
|
||||
n_out = 0;
|
||||
}
|
||||
1103
packages/mp3-encoder/src/lame/Lame.ts
Normal file
1103
packages/mp3-encoder/src/lame/Lame.ts
Normal file
File diff suppressed because it is too large
Load Diff
80
packages/mp3-encoder/src/lame/LameGlobalFlags.ts
Normal file
80
packages/mp3-encoder/src/lame/LameGlobalFlags.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
214
packages/mp3-encoder/src/lame/LameInternalFlags.ts
Normal file
214
packages/mp3-encoder/src/lame/LameInternalFlags.ts
Normal 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;
|
||||
}
|
||||
13
packages/mp3-encoder/src/lame/MPEGMode.ts
Normal file
13
packages/mp3-encoder/src/lame/MPEGMode.ts
Normal 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);
|
||||
}
|
||||
3
packages/mp3-encoder/src/lame/MeanBits.ts
Normal file
3
packages/mp3-encoder/src/lame/MeanBits.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export class MeanBits {
|
||||
constructor(public bits: number) {}
|
||||
}
|
||||
90
packages/mp3-encoder/src/lame/Mp3Encoder.spec.ts
Normal file
90
packages/mp3-encoder/src/lame/Mp3Encoder.spec.ts
Normal 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');
|
||||
});
|
||||
44
packages/mp3-encoder/src/lame/Mp3Encoder.ts
Normal file
44
packages/mp3-encoder/src/lame/Mp3Encoder.ts
Normal 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));
|
||||
}
|
||||
}
|
||||
950
packages/mp3-encoder/src/lame/NewMDCT.ts
Normal file
950
packages/mp3-encoder/src/lame/NewMDCT.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
packages/mp3-encoder/src/lame/NsPsy.ts
Normal file
17
packages/mp3-encoder/src/lame/NsPsy.ts
Normal 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;
|
||||
}
|
||||
3
packages/mp3-encoder/src/lame/NumUsed.ts
Normal file
3
packages/mp3-encoder/src/lame/NumUsed.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export class NumUsed {
|
||||
num_used = 0;
|
||||
}
|
||||
11
packages/mp3-encoder/src/lame/PSY.ts
Normal file
11
packages/mp3-encoder/src/lame/PSY.ts
Normal 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);
|
||||
}
|
||||
126
packages/mp3-encoder/src/lame/Presets.ts
Normal file
126
packages/mp3-encoder/src/lame/Presets.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
2287
packages/mp3-encoder/src/lame/PsyModel.ts
Normal file
2287
packages/mp3-encoder/src/lame/PsyModel.ts
Normal file
File diff suppressed because it is too large
Load Diff
1
packages/mp3-encoder/src/lame/Quality.ts
Normal file
1
packages/mp3-encoder/src/lame/Quality.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type Quality = 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0;
|
||||
862
packages/mp3-encoder/src/lame/Quantize.ts
Normal file
862
packages/mp3-encoder/src/lame/Quantize.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
414
packages/mp3-encoder/src/lame/QuantizePVT.ts
Normal file
414
packages/mp3-encoder/src/lame/QuantizePVT.ts
Normal 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;
|
||||
}
|
||||
45
packages/mp3-encoder/src/lame/ReplayGain.ts
Normal file
45
packages/mp3-encoder/src/lame/ReplayGain.ts
Normal 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;
|
||||
}
|
||||
94
packages/mp3-encoder/src/lame/Reservoir.ts
Normal file
94
packages/mp3-encoder/src/lame/Reservoir.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
42
packages/mp3-encoder/src/lame/ScaleFac.ts
Normal file
42
packages/mp3-encoder/src/lame/ScaleFac.ts
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/mp3-encoder/src/lame/ShortBlock.ts
Normal file
6
packages/mp3-encoder/src/lame/ShortBlock.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const enum ShortBlock {
|
||||
short_block_allowed,
|
||||
short_block_coupled,
|
||||
short_block_dispensed,
|
||||
short_block_forced,
|
||||
}
|
||||
3
packages/mp3-encoder/src/lame/StartLine.ts
Normal file
3
packages/mp3-encoder/src/lame/StartLine.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export class StartLine {
|
||||
constructor(public s: number) {}
|
||||
}
|
||||
253
packages/mp3-encoder/src/lame/Tables.ts
Normal file
253
packages/mp3-encoder/src/lame/Tables.ts
Normal 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 };
|
||||
994
packages/mp3-encoder/src/lame/Takehiro.ts
Normal file
994
packages/mp3-encoder/src/lame/Takehiro.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
packages/mp3-encoder/src/lame/TotalBytes.ts
Normal file
3
packages/mp3-encoder/src/lame/TotalBytes.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export class TotalBytes {
|
||||
total = 0;
|
||||
}
|
||||
450
packages/mp3-encoder/src/lame/VBRPresets.ts
Normal file
450
packages/mp3-encoder/src/lame/VBRPresets.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
32
packages/mp3-encoder/src/lame/VBRTag.ts
Normal file
32
packages/mp3-encoder/src/lame/VBRTag.ts
Normal 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
packages/mp3-encoder/src/lame/VbrMode.ts
Normal file
8
packages/mp3-encoder/src/lame/VbrMode.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const enum VbrMode {
|
||||
vbr_off,
|
||||
vbr_mt,
|
||||
vbr_rh,
|
||||
vbr_abr,
|
||||
vbr_mtrh,
|
||||
vbr_default = VbrMode.vbr_mtrh,
|
||||
}
|
||||
60
packages/mp3-encoder/src/lame/WavHeader.ts
Normal file
60
packages/mp3-encoder/src/lame/WavHeader.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
40
packages/mp3-encoder/src/lame/arrays.ts
Normal file
40
packages/mp3-encoder/src/lame/arrays.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
packages/mp3-encoder/src/lame/assert.ts
Normal file
8
packages/mp3-encoder/src/lame/assert.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
59
packages/mp3-encoder/src/lame/bitrates.ts
Normal file
59
packages/mp3-encoder/src/lame/bitrates.ts
Normal 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];
|
||||
}
|
||||
65
packages/mp3-encoder/src/lame/constants.ts
Normal file
65
packages/mp3-encoder/src/lame/constants.ts
Normal 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;
|
||||
9
packages/mp3-encoder/src/lame/getLameShortVersion.ts
Normal file
9
packages/mp3-encoder/src/lame/getLameShortVersion.ts
Normal 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;
|
||||
}
|
||||
1
packages/mp3-encoder/src/lame/index.ts
Normal file
1
packages/mp3-encoder/src/lame/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { Mp3Encoder } from './Mp3Encoder';
|
||||
28
packages/mp3-encoder/src/lame/math.ts
Normal file
28
packages/mp3-encoder/src/lame/math.ts
Normal 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);
|
||||
}
|
||||
14
packages/mp3-encoder/src/lame/sampleRates.ts
Normal file
14
packages/mp3-encoder/src/lame/sampleRates.ts
Normal 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;
|
||||
}
|
||||
4
packages/mp3-encoder/tsconfig.build.json
Normal file
4
packages/mp3-encoder/tsconfig.build.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["**/*.spec.*", "jest.config.*"]
|
||||
}
|
||||
19
packages/mp3-encoder/tsconfig.json
Normal file
19
packages/mp3-encoder/tsconfig.json
Normal 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"]
|
||||
}
|
||||
Reference in New Issue
Block a user