carta1 1.0.21 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codec/browser/interface.js +0 -1
- package/codec/coding/bitallocation.js +205 -303
- package/codec/coding/quantization.js +79 -78
- package/codec/core/constants.js +53 -153
- package/codec/core/options.js +0 -13
- package/codec/pipeline/decoder.js +23 -3
- package/codec/pipeline/encoder.js +37 -39
- package/dist/carta1-worker-interface.min.js.map +1 -1
- package/dist/carta1-worker.min.js +1 -1
- package/dist/carta1-worker.min.js.map +1 -1
- package/dist/carta1.min.js +1 -1
- package/dist/carta1.min.js.map +1 -1
- package/package.json +1 -1
- package/codec/analysis/psychoacoustics.js +0 -453
|
@@ -71,7 +71,6 @@ class Carta1Worker {
|
|
|
71
71
|
* @param {number} [options.transientThresholdLow] - Low band transient threshold
|
|
72
72
|
* @param {number} [options.transientThresholdMid] - Mid band transient threshold
|
|
73
73
|
* @param {number} [options.transientThresholdHigh] - High band transient threshold
|
|
74
|
-
* @param {number} [options.normalizationDb] - Normalization level in dB
|
|
75
74
|
* @returns {Promise<Object>} Encoding result
|
|
76
75
|
* @returns {Blob} returns.aeaBlob - ATRAC1 encoded audio blob
|
|
77
76
|
* @returns {Object} returns.shortBlockData - Short block frame indices
|
|
@@ -1,373 +1,275 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Carta1 Audio Codec - Bit Allocation Module
|
|
3
3
|
*
|
|
4
|
-
* This module implements the bit allocation
|
|
5
|
-
* distributing available bits
|
|
6
|
-
*
|
|
4
|
+
* This module implements the bit allocation system for the Carta1 audio codec,
|
|
5
|
+
* responsible for distributing available bits across frequency bands to minimize
|
|
6
|
+
* perceptual distortion while maintaining the target bitrate.
|
|
7
|
+
*
|
|
8
|
+
* The module uses Rate-Distortion Optimization (RDO) with a greedy algorithm
|
|
9
|
+
* that allocates bits to Band Frequency Units (BFUs) based on the perceptual
|
|
10
|
+
* benefit per bit spent. It employs psychoacoustic modeling to determine
|
|
11
|
+
* scale factors and uses a priority queue to efficiently find the optimal
|
|
12
|
+
* bit distribution.
|
|
13
|
+
*
|
|
14
|
+
* Key components:
|
|
15
|
+
* - RDO-based bit allocation using exponential distortion modeling
|
|
16
|
+
* - Scale factor computation for each frequency band
|
|
17
|
+
* - Max-heap priority queue for greedy optimization
|
|
18
|
+
* - Support for variable word lengths per BFU
|
|
19
|
+
*
|
|
20
|
+
* The algorithm ensures that bits are allocated where they provide the
|
|
21
|
+
* greatest reduction in audible distortion, resulting in high-quality
|
|
22
|
+
* audio compression at the target bitrate.
|
|
7
23
|
*/
|
|
8
24
|
|
|
9
25
|
import {
|
|
26
|
+
NUM_BFUS,
|
|
10
27
|
FRAME_BITS,
|
|
11
28
|
FRAME_OVERHEAD_BITS,
|
|
12
29
|
BITS_PER_BFU_METADATA,
|
|
13
|
-
BFU_AMOUNTS,
|
|
14
30
|
MAX_WORD_LENGTH_INDEX,
|
|
15
31
|
WORD_LENGTH_BITS,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
PSYMODEL_MIN_POWER_DB,
|
|
22
|
-
PSYMODEL_CB_FREQ_INDICES,
|
|
23
|
-
INTERPOLATION_COMPENSATION_FACTOR,
|
|
24
|
-
PSYMODEL_FFT_SIZE,
|
|
32
|
+
LP_DISTORTION_CONSTANT,
|
|
33
|
+
SF_POW_P,
|
|
34
|
+
POW2_POS_P,
|
|
35
|
+
POW2_NEG_P,
|
|
36
|
+
SCALE_FACTORS,
|
|
25
37
|
} from '../core/constants.js'
|
|
26
38
|
|
|
27
39
|
/**
|
|
28
|
-
* Allocate bits to BFUs
|
|
29
|
-
* @param {
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
32
|
-
* @
|
|
33
|
-
* @returns {Object} Allocation results with BFU count and bit distribution
|
|
40
|
+
* Allocate bits to BFUs using optimized greedy Lp-RDO
|
|
41
|
+
* @param {Array<Float32Array>} bfuData
|
|
42
|
+
* @param {Int32Array} bfuSizes
|
|
43
|
+
* @param {number} maxBfuCount
|
|
44
|
+
* @returns {{bfuCount:number, allocation:Int32Array, scaleFactorIndices:Int32Array}}
|
|
34
45
|
*/
|
|
35
|
-
export function allocateBits(
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
// Interpolate critical band thresholds to BFUs
|
|
40
|
-
const maskThresholdDb = interpolateCriticalBandsToBfus(
|
|
41
|
-
psychoResults.criticalBandThresholds
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
// Calculate Signal-to-Mask Ratios
|
|
45
|
-
const smrValues = calculateSMR(signalEnergyDb, maskThresholdDb, maxBfuCount)
|
|
46
|
-
|
|
47
|
-
// Determine optimal number of BFUs to use
|
|
48
|
-
const actualBfuCount = selectOptimalBfuCount(
|
|
49
|
-
smrValues,
|
|
50
|
-
signalEnergyDb,
|
|
51
|
-
maxBfuCount
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
// Calculate available bits for allocation
|
|
55
|
-
const availableBits =
|
|
56
|
-
FRAME_BITS - FRAME_OVERHEAD_BITS - actualBfuCount * BITS_PER_BFU_METADATA
|
|
46
|
+
export function allocateBits(bfuData, bfuSizes, maxBfuCount) {
|
|
47
|
+
const usedBFU = maxBfuCount
|
|
48
|
+
const avail =
|
|
49
|
+
FRAME_BITS - FRAME_OVERHEAD_BITS - usedBFU * BITS_PER_BFU_METADATA
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
const allocation = performBitAllocation(
|
|
60
|
-
actualBfuCount,
|
|
61
|
-
smrValues,
|
|
62
|
-
bfuSizes,
|
|
63
|
-
availableBits
|
|
64
|
-
)
|
|
51
|
+
const rdoResult = distributeBitsRDO(usedBFU, bfuData, bfuSizes, avail)
|
|
65
52
|
|
|
66
53
|
return {
|
|
67
|
-
bfuCount:
|
|
68
|
-
allocation,
|
|
54
|
+
bfuCount: usedBFU,
|
|
55
|
+
allocation: rdoResult.wordLengths,
|
|
56
|
+
scaleFactorIndices: rdoResult.scaleFactorIndices,
|
|
69
57
|
}
|
|
70
58
|
}
|
|
71
59
|
|
|
72
60
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
61
|
+
* Distributes available bits across Band Frequency Units (BFUs) using Rate-Distortion Optimization.
|
|
62
|
+
*
|
|
63
|
+
* This function implements an optimized greedy RDO allocator that minimizes perceptual distortion
|
|
64
|
+
* while staying within the bit budget. It uses a priority queue (max-heap) to greedily allocate
|
|
65
|
+
* bits to the BFUs that provide the greatest distortion reduction per bit spent.
|
|
66
|
+
*
|
|
67
|
+
* Mathematical Model:
|
|
68
|
+
* Distortion for b bits is modeled as:
|
|
69
|
+
* D(b) = C * 2^{-p * b} for b >= 1
|
|
70
|
+
* D(0) = C * 2^{ p } (finite large step for "mute" case)
|
|
71
|
+
* where C = N * (sf^p) * k, with N = BFU length, sf = scale factor, p and k constants.
|
|
72
|
+
*
|
|
73
|
+
* Hence, for an upgrade from b1 -> b2 (b2 > b1), ΔD = D(b1) - D(b2) can be computed with 2 table lookups.
|
|
74
|
+
*
|
|
75
|
+
* Precomputed tables:
|
|
76
|
+
* POW2_NEG_P[b] = 2^{-p*b} for b=0..maxBits
|
|
77
|
+
* POW2_POS_P = 2^{ p}
|
|
78
|
+
* SF_POW_P[sfIndex] = (SCALE_FACTORS[sfIndex])^p
|
|
79
|
+
*
|
|
80
|
+
* @param {number} activeBfuCount
|
|
81
|
+
* @param {Array<Float32Array>} bfuData
|
|
82
|
+
* @param {Int32Array} bfuSizes
|
|
83
|
+
* @param {number} remainingBits
|
|
84
|
+
* @returns {{wordLengths: Int32Array, scaleFactorIndices: Int32Array}}
|
|
78
85
|
*/
|
|
79
|
-
function
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
for (let j = 0; j < size; j++) {
|
|
94
|
-
energy += data[j] * data[j]
|
|
86
|
+
function distributeBitsRDO(activeBfuCount, bfuData, bfuSizes, remainingBits) {
|
|
87
|
+
const scaleFactorTable = new Int32Array(NUM_BFUS)
|
|
88
|
+
const wordLengths = new Int32Array(activeBfuCount)
|
|
89
|
+
|
|
90
|
+
const baseWeight = new Float64Array(activeBfuCount) // C above
|
|
91
|
+
for (let bfuIndex = 0; bfuIndex < activeBfuCount; bfuIndex++) {
|
|
92
|
+
const sz = bfuSizes[bfuIndex] | 0
|
|
93
|
+
if (sz > 0) {
|
|
94
|
+
const sfi = findScaleFactor(bfuData[bfuIndex].subarray(0, sz))
|
|
95
|
+
scaleFactorTable[bfuIndex] = sfi
|
|
96
|
+
baseWeight[bfuIndex] = sz * SF_POW_P[sfi] * LP_DISTORTION_CONSTANT
|
|
97
|
+
} else {
|
|
98
|
+
scaleFactorTable[bfuIndex] = 0
|
|
99
|
+
baseWeight[bfuIndex] = 0
|
|
95
100
|
}
|
|
96
|
-
|
|
97
|
-
// Convert to dB correctly using log10
|
|
98
|
-
energyPerBfu[i] =
|
|
99
|
-
energy > 0 ? 10 * Math.log10(energy / size) : PSYMODEL_MIN_POWER_DB
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
const heapIndices = []
|
|
104
|
+
const heapPriorities = []
|
|
105
|
+
const deltaDist = (bfuIndex, currentWl, nextWl) => {
|
|
106
|
+
const base = baseWeight[bfuIndex]
|
|
107
|
+
if (base === 0) return 0
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
* @param {Float32Array} criticalBandThresholds - Critical band thresholds
|
|
108
|
-
* @returns {Float32Array} Masking threshold per BFU
|
|
109
|
-
*/
|
|
110
|
-
function interpolateCriticalBandsToBfus(criticalBandThresholds) {
|
|
111
|
-
const maskThresholdPerBfu = new Float32Array(NUM_BFUS)
|
|
112
|
-
const freqPerBin = SAMPLE_RATE / PSYMODEL_FFT_SIZE
|
|
113
|
-
|
|
114
|
-
for (let i = 0; i < NUM_BFUS; i++) {
|
|
115
|
-
const bfuFreq = BFU_FREQUENCIES[i]
|
|
116
|
-
const fftBinIndex = Math.round(bfuFreq / freqPerBin)
|
|
117
|
-
|
|
118
|
-
// Find which critical bands surround this FFT bin
|
|
119
|
-
let bandIndex = 0
|
|
120
|
-
while (
|
|
121
|
-
bandIndex < PSYMODEL_CB_FREQ_INDICES.length - 1 &&
|
|
122
|
-
PSYMODEL_CB_FREQ_INDICES[bandIndex + 1] < fftBinIndex
|
|
123
|
-
) {
|
|
124
|
-
bandIndex++
|
|
125
|
-
}
|
|
109
|
+
const bits1 = WORD_LENGTH_BITS[currentWl] | 0
|
|
110
|
+
const bits2 = WORD_LENGTH_BITS[nextWl] | 0
|
|
126
111
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
} else if (
|
|
130
|
-
fftBinIndex >=
|
|
131
|
-
PSYMODEL_CB_FREQ_INDICES[PSYMODEL_CB_FREQ_INDICES.length - 1]
|
|
132
|
-
) {
|
|
133
|
-
maskThresholdPerBfu[i] =
|
|
134
|
-
criticalBandThresholds[criticalBandThresholds.length - 1]
|
|
135
|
-
} else {
|
|
136
|
-
const x0 = PSYMODEL_CB_FREQ_INDICES[bandIndex]
|
|
137
|
-
const x1 = PSYMODEL_CB_FREQ_INDICES[bandIndex + 1]
|
|
138
|
-
const y0 = criticalBandThresholds[bandIndex]
|
|
139
|
-
const y1 = criticalBandThresholds[bandIndex + 1]
|
|
140
|
-
const bandWidth = x1 - x0
|
|
112
|
+
const f1 = bits1 === 0 ? POW2_POS_P : POW2_NEG_P[bits1]
|
|
113
|
+
const f2 = POW2_NEG_P[bits2]
|
|
141
114
|
|
|
142
|
-
|
|
143
|
-
const t = (fftBinIndex - x0) / bandWidth
|
|
144
|
-
const interpolated = y0 + (y1 - y0) * t
|
|
145
|
-
const compensation =
|
|
146
|
-
Math.abs(y1 - y0) * INTERPOLATION_COMPENSATION_FACTOR
|
|
147
|
-
maskThresholdPerBfu[i] = interpolated + compensation
|
|
148
|
-
} else {
|
|
149
|
-
maskThresholdPerBfu[i] = y0
|
|
150
|
-
}
|
|
151
|
-
}
|
|
115
|
+
return base * (f1 - f2)
|
|
152
116
|
}
|
|
153
117
|
|
|
154
|
-
|
|
155
|
-
|
|
118
|
+
for (let bfuIndex = 0; bfuIndex < activeBfuCount; bfuIndex++) {
|
|
119
|
+
const sz = bfuSizes[bfuIndex] | 0
|
|
120
|
+
if (sz === 0) continue
|
|
121
|
+
if (scaleFactorTable[bfuIndex] === 0) continue
|
|
156
122
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
* @returns {Float32Array} SMR values in dB
|
|
163
|
-
*/
|
|
164
|
-
function calculateSMR(signalEnergyDb, maskThresholdDb, maxBfuCount) {
|
|
165
|
-
const smrValues = new Float32Array(maxBfuCount)
|
|
123
|
+
const currentWl = 0
|
|
124
|
+
const nextWl = 1
|
|
125
|
+
const deltaBits =
|
|
126
|
+
(WORD_LENGTH_BITS[nextWl] - WORD_LENGTH_BITS[currentWl]) | 0
|
|
127
|
+
if (deltaBits <= 0) continue
|
|
166
128
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
signalEnergyDb[bfu] <= PSYMODEL_MIN_POWER_DB
|
|
170
|
-
? -Infinity
|
|
171
|
-
: signalEnergyDb[bfu] - maskThresholdDb[bfu]
|
|
172
|
-
}
|
|
129
|
+
const deltaCost = deltaBits * sz
|
|
130
|
+
const dd = deltaDist(bfuIndex, currentWl, nextWl)
|
|
173
131
|
|
|
174
|
-
|
|
175
|
-
|
|
132
|
+
heapIndices.push(bfuIndex)
|
|
133
|
+
heapPriorities.push(dd / deltaCost) // priority = ΔD / ΔR
|
|
134
|
+
}
|
|
176
135
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
* @param {Float32Array} signalEnergyDb - Signal energy per BFU
|
|
181
|
-
* @param {number} maxBfuCount - Maximum available BFUs
|
|
182
|
-
* @returns {number} Optimal BFU count
|
|
183
|
-
*/
|
|
184
|
-
function selectOptimalBfuCount(smrValues, signalEnergyDb, maxBfuCount) {
|
|
185
|
-
// Check if we have any audible content (positive SMR)
|
|
186
|
-
const hasAudibleContent = smrValues.some((smr) => smr > 0)
|
|
136
|
+
// Heapify
|
|
137
|
+
for (let i = (heapIndices.length >> 1) - 1; i >= 0; i--)
|
|
138
|
+
siftDown(heapIndices, heapPriorities, i)
|
|
187
139
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
contributions.push({ index: i, smr: smrValues[i] })
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
contributions.sort((a, b) => b.smr - a.smr)
|
|
140
|
+
// Greedy spending
|
|
141
|
+
while (remainingBits > 0 && heapIndices.length > 0) {
|
|
142
|
+
const bfu = heapIndices[0]
|
|
143
|
+
const cur = wordLengths[bfu] | 0
|
|
144
|
+
const nxt = cur + 1
|
|
197
145
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
146
|
+
const sz = bfuSizes[bfu] | 0
|
|
147
|
+
const deltaBits = (WORD_LENGTH_BITS[nxt] - WORD_LENGTH_BITS[cur]) | 0
|
|
148
|
+
const cost = deltaBits * sz
|
|
201
149
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
for (let i = 0; i < contributions.length; i++) {
|
|
206
|
-
sum += contributions[i].smr
|
|
207
|
-
cumulativeSMR.push(sum)
|
|
150
|
+
if (cost > remainingBits || cost <= 0) {
|
|
151
|
+
popRoot(heapIndices, heapPriorities)
|
|
152
|
+
continue
|
|
208
153
|
}
|
|
209
154
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (candidateCount > maxBfuCount) continue
|
|
213
|
-
|
|
214
|
-
// Check if all significant contributions fit within this count
|
|
215
|
-
const requiredBfus = contributions
|
|
216
|
-
.slice(0, Math.min(contributions.length, candidateCount))
|
|
217
|
-
.map((c) => c.index)
|
|
218
|
-
|
|
219
|
-
const maxRequiredIndex = Math.max(...requiredBfus, 0)
|
|
155
|
+
remainingBits -= cost
|
|
156
|
+
wordLengths[bfu] = nxt
|
|
220
157
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Use gradient to detect diminishing returns
|
|
232
|
-
const remainingIndices = contributions.length - capturedIndex - 1
|
|
233
|
-
if (remainingIndices > 0) {
|
|
234
|
-
const avgRemaining =
|
|
235
|
-
(cumulativeSMR[cumulativeSMR.length - 1] -
|
|
236
|
-
cumulativeSMR[capturedIndex]) /
|
|
237
|
-
remainingIndices
|
|
238
|
-
const avgCaptured = cumulativeSMR[capturedIndex] / (capturedIndex + 1)
|
|
239
|
-
|
|
240
|
-
// If remaining BFUs contribute much less on average, we've found our cutoff
|
|
241
|
-
if (avgRemaining < avgCaptured * 0.1) {
|
|
242
|
-
return candidateCount
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
// Find the highest BFU index that contains actual signal
|
|
249
|
-
let highestSignalBfu = -1
|
|
250
|
-
for (let i = maxBfuCount - 1; i >= 0; i--) {
|
|
251
|
-
if (
|
|
252
|
-
signalEnergyDb[i] > PSYMODEL_MIN_POWER_DB &&
|
|
253
|
-
smrValues[i] > -Infinity
|
|
254
|
-
) {
|
|
255
|
-
highestSignalBfu = i
|
|
256
|
-
break
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Find smallest BFU count that includes all signal
|
|
261
|
-
for (const candidateCount of BFU_AMOUNTS) {
|
|
262
|
-
if (candidateCount > highestSignalBfu) {
|
|
263
|
-
return candidateCount
|
|
158
|
+
if (nxt < MAX_WORD_LENGTH_INDEX) {
|
|
159
|
+
const nxt2 = nxt + 1
|
|
160
|
+
const deltaBits2 = (WORD_LENGTH_BITS[nxt2] - WORD_LENGTH_BITS[nxt]) | 0
|
|
161
|
+
if (deltaBits2 > 0) {
|
|
162
|
+
heapPriorities[0] = deltaDist(bfu, nxt, nxt2) / (deltaBits2 * sz)
|
|
163
|
+
siftDown(heapIndices, heapPriorities, 0)
|
|
164
|
+
} else {
|
|
165
|
+
popRoot(heapIndices, heapPriorities)
|
|
264
166
|
}
|
|
167
|
+
} else {
|
|
168
|
+
popRoot(heapIndices, heapPriorities)
|
|
265
169
|
}
|
|
266
170
|
}
|
|
267
171
|
|
|
268
|
-
return
|
|
172
|
+
return { wordLengths, scaleFactorIndices: scaleFactorTable }
|
|
269
173
|
}
|
|
270
174
|
|
|
271
175
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* @param {Float32Array}
|
|
275
|
-
* @
|
|
276
|
-
* @param {number} availableBits - Total bits available for allocation
|
|
277
|
-
* @returns {Int32Array} Word length index allocation per BFU
|
|
176
|
+
* Find the optimal scale factor index for a set of coefficients.
|
|
177
|
+
* Same semantics as the original.
|
|
178
|
+
* @param {Float32Array} coefficients
|
|
179
|
+
* @returns {number}
|
|
278
180
|
*/
|
|
279
|
-
function
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
|
|
292
|
-
|
|
181
|
+
export function findScaleFactor(coefficients) {
|
|
182
|
+
let maxAmplitude = 0.0
|
|
183
|
+
const n = coefficients.length | 0
|
|
184
|
+
let i = 0
|
|
185
|
+
const n4 = n & ~3
|
|
186
|
+
for (; i < n4; i += 4) {
|
|
187
|
+
const a0 = Math.abs(coefficients[i])
|
|
188
|
+
const a1 = Math.abs(coefficients[i + 1])
|
|
189
|
+
const a2 = Math.abs(coefficients[i + 2])
|
|
190
|
+
const a3 = Math.abs(coefficients[i + 3])
|
|
191
|
+
if (a0 > maxAmplitude) maxAmplitude = a0
|
|
192
|
+
if (a1 > maxAmplitude) maxAmplitude = a1
|
|
193
|
+
if (a2 > maxAmplitude) maxAmplitude = a2
|
|
194
|
+
if (a3 > maxAmplitude) maxAmplitude = a3
|
|
293
195
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
heapifyDown(heap, i)
|
|
196
|
+
for (; i < n; i++) {
|
|
197
|
+
const a = Math.abs(coefficients[i])
|
|
198
|
+
if (a > maxAmplitude) maxAmplitude = a
|
|
298
199
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (heap.length > 0) heapifyDown(heap, 0)
|
|
309
|
-
continue
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const upgradeCost = calculateUpgradeCost(bestBfu.bfu, currentWl, bfuSizes)
|
|
313
|
-
|
|
314
|
-
if (upgradeCost <= availableBits) {
|
|
315
|
-
availableBits -= upgradeCost
|
|
316
|
-
allocation[bestBfu.bfu]++
|
|
317
|
-
bestBfu.priority += DISTORTION_DELTA_DB[currentWl]
|
|
318
|
-
heapifyDown(heap, 0)
|
|
200
|
+
if (maxAmplitude === 0) return 0
|
|
201
|
+
|
|
202
|
+
// Binary search
|
|
203
|
+
let low = 0
|
|
204
|
+
let high = SCALE_FACTORS.length - 1
|
|
205
|
+
while (low < high) {
|
|
206
|
+
const mid = (low + high) >> 1
|
|
207
|
+
if (maxAmplitude <= SCALE_FACTORS[mid]) {
|
|
208
|
+
high = mid
|
|
319
209
|
} else {
|
|
320
|
-
|
|
321
|
-
heap.pop()
|
|
322
|
-
if (heap.length > 0) heapifyDown(heap, 0)
|
|
210
|
+
low = mid + 1
|
|
323
211
|
}
|
|
324
212
|
}
|
|
325
|
-
|
|
326
|
-
return allocation
|
|
213
|
+
return low
|
|
327
214
|
}
|
|
328
215
|
|
|
329
216
|
/**
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
217
|
+
* Removes the root element from a max-heap and maintains heap property.
|
|
218
|
+
*
|
|
219
|
+
* This function efficiently removes the maximum priority element from the heap
|
|
220
|
+
* by replacing it with the last element and then restoring the heap property
|
|
221
|
+
* through sifting down.
|
|
222
|
+
*
|
|
223
|
+
* @param {Array<number>} heapIndices
|
|
224
|
+
* @param {Array<number>} heapPriorities
|
|
335
225
|
*/
|
|
336
|
-
function
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
226
|
+
function popRoot(heapIndices, heapPriorities) {
|
|
227
|
+
const last = heapIndices.length - 1
|
|
228
|
+
heapIndices[0] = heapIndices[last]
|
|
229
|
+
heapPriorities[0] = heapPriorities[last]
|
|
230
|
+
heapIndices.pop()
|
|
231
|
+
heapPriorities.pop()
|
|
232
|
+
if (heapIndices.length) siftDown(heapIndices, heapPriorities, 0)
|
|
340
233
|
}
|
|
341
234
|
|
|
342
235
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
236
|
+
* Restores max-heap property by sifting an element down from a given position.
|
|
237
|
+
*
|
|
238
|
+
* This function maintains the heap invariant where parent nodes have higher
|
|
239
|
+
* priority values than their children. It compares the element at startIndex
|
|
240
|
+
* with its children and swaps with the larger child until the heap property
|
|
241
|
+
* is satisfied, or it reaches a leaf position.
|
|
242
|
+
*
|
|
243
|
+
* @param {Array<number>} heapIndices
|
|
244
|
+
* @param {Array<number>} heapPriorities
|
|
245
|
+
* @param {number} startIndex
|
|
346
246
|
*/
|
|
347
|
-
function
|
|
348
|
-
const
|
|
349
|
-
|
|
247
|
+
function siftDown(heapIndices, heapPriorities, startIndex) {
|
|
248
|
+
const heapSize = heapIndices.length
|
|
249
|
+
let i = startIndex
|
|
250
|
+
const idxVal = heapIndices[i]
|
|
251
|
+
const prVal = heapPriorities[i]
|
|
350
252
|
|
|
351
253
|
while (true) {
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
let
|
|
355
|
-
let
|
|
356
|
-
|
|
357
|
-
if (
|
|
358
|
-
|
|
359
|
-
|
|
254
|
+
const l = (i << 1) + 1
|
|
255
|
+
const r = l + 1
|
|
256
|
+
let maxI = i
|
|
257
|
+
let maxP = prVal
|
|
258
|
+
|
|
259
|
+
if (l < heapSize && heapPriorities[l] > maxP) {
|
|
260
|
+
maxI = l
|
|
261
|
+
maxP = heapPriorities[l]
|
|
360
262
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
largest = rightChild
|
|
263
|
+
if (r < heapSize && heapPriorities[r] > maxP) {
|
|
264
|
+
maxI = r
|
|
364
265
|
}
|
|
266
|
+
if (maxI === i) break
|
|
365
267
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
startIdx = largest
|
|
268
|
+
heapIndices[i] = heapIndices[maxI]
|
|
269
|
+
heapPriorities[i] = heapPriorities[maxI]
|
|
270
|
+
i = maxI
|
|
370
271
|
}
|
|
371
272
|
|
|
372
|
-
|
|
273
|
+
heapIndices[i] = idxVal
|
|
274
|
+
heapPriorities[i] = prVal
|
|
373
275
|
}
|