/* * Windows Media Audio 1 and 2 decoder, translated to Kotlin from FFmpeg's libavcodec/wmadec.c and * wma.c (Copyright (c) 2002 The FFmpeg project). Licensed under the GNU Lesser General Public * License 2.1 or later, like the original; see the open-source licenses page in the app. * * This is the "Windows Media Audio 9 Standard" format (and its older version 1) that Windows Media * Player used when ripping CDs. WMA Pro, WMA Lossless and WMA Voice are different codecs and are * not handled here. */ package com.turntablemedic.auralrest.wma import kotlin.math.PI import kotlin.math.cos import kotlin.math.pow import kotlin.math.sin import kotlin.math.sqrt /** MSB-first bit reader over part of a byte array, like FFmpeg's GetBitContext. */ internal class BitReader(private val buf: ByteArray, private val start: Int, private val bitLen: Int) { var pos = 0 private set fun left(): Int = bitLen - pos fun bit(): Int { val p = pos++ if (p >= bitLen) return 0 return (buf[start + (p ushr 3)].toInt() ushr (7 - (p and 7))) and 1 } fun bits(n: Int): Int { var v = 0 for (i in 0 until n) v = (v shl 1) or bit() return v } fun skip(n: Int) { pos += n } fun align() { pos = (pos + 7) and 7.inv() } } /** Prefix-code (Huffman) decoder built as a binary tree from code values and lengths. */ internal class Vlc(codes: IntArray, lens: IntArray, symbols: IntArray? = null) { // node i: child[2i] for bit 0, child[2i+1] for bit 1. Positive = next node, negative = -(symbol+1), 0 = none. private var child = IntArray(1024) private var nodes = 1 init { for (i in codes.indices) { val len = lens[i] if (len == 0) continue val sym = symbols?.get(i) ?: i var node = 0 for (b in len - 1 downTo 0) { val bit = (codes[i] ushr b) and 1 val slot = node * 2 + bit if (b == 0) { child[slot] = -(sym + 1) } else { if (child[slot] <= 0) { if ((nodes + 1) * 2 > child.size) child = child.copyOf(child.size * 2) child[slot] = nodes++ } node = child[slot] } } } } /** Reads one code; returns its symbol, or Int.MIN_VALUE for a code that isn't in the table. */ fun read(r: BitReader): Int { var node = 0 while (true) { val v = child[node * 2 + r.bit()] if (v < 0) return -v - 1 if (v == 0) return Int.MIN_VALUE node = v } } companion object { /** Codes assigned in the given order from lengths alone, as FFmpeg's ff_vlc_init_from_lengths does. */ fun fromLengths(lens: IntArray, symbols: IntArray, offset: Int): Vlc { val codes = IntArray(lens.size) var code = 0L for (i in lens.indices) { codes[i] = (code ushr (32 - lens[i])).toInt() code += 1L shl (32 - lens[i]) } return Vlc(codes, lens, IntArray(symbols.size) { symbols[it] + offset }) } } } /** * Inverse MDCT producing the full 2N output for N coefficients, scaled, using an N/2-point * complex FFT. Matches FFmpeg's AV_TX_FULL_IMDCT. */ internal class Imdct(private val n: Int, private val scale: Float) { private val n2 = n / 2 private val n4 = n / 4 private val tcos = FloatArray(n2) private val tsin = FloatArray(n2) private val re = FloatArray(n2) private val im = FloatArray(n2) private val rev = IntArray(n2) private val fcos = FloatArray(n2 / 2) private val fsin = FloatArray(n2 / 2) private val half = FloatArray(n) init { // Twiddles for the pre/post rotation (N = coefficient count, full length 2N). val theta = 1.0 / 8.0 + (if (scale < 0) n2 else 0) val sc = sqrt(kotlin.math.abs(scale.toDouble())) for (i in 0 until n2) { val a = 2 * PI * (i + theta) / (2 * n) tcos[i] = (-cos(a) * sc).toFloat() tsin[i] = (-sin(a) * sc).toFloat() } var bitsN = 0 while ((1 shl bitsN) < n2) bitsN++ for (i in 0 until n2) { var x = i; var y = 0 repeat(bitsN) { y = (y shl 1) or (x and 1); x = x ushr 1 } rev[i] = y } for (i in 0 until n2 / 2) { fcos[i] = cos(2 * PI * i / n2).toFloat() fsin[i] = sin(2 * PI * i / n2).toFloat() } } private fun fft() { var size = 2 while (size <= n2) { val h = size / 2 val step = n2 / size var s = 0 while (s < n2) { var t = 0 for (k in s until s + h) { val wr = fcos[t]; val wi = fsin[t] val xr = re[k + h] * wr - im[k + h] * wi val xi = re[k + h] * wi + im[k + h] * wr re[k + h] = re[k] - xr; im[k + h] = im[k] - xi re[k] += xr; im[k] += xi t += step } s += size } size *= 2 } } /** Half IMDCT (the N middle samples), the classic FFmpeg ff_imdct_half algorithm. */ private fun imdctHalf(input: FloatArray, out: FloatArray) { // pre rotation for (k in 0 until n2) { val j = rev[k] val a = input[n - 1 - 2 * k] val b = input[2 * k] re[j] = a * tcos[k] - b * tsin[k] im[j] = a * tsin[k] + b * tcos[k] } fft() // post rotation + reordering for (k in 0 until n4) { val k1 = n4 - k - 1 val k2 = n4 + k val r0 = im[k1] * tsin[k1] - re[k1] * tcos[k1] val i1 = im[k1] * tcos[k1] + re[k1] * tsin[k1] val r1 = im[k2] * tsin[k2] - re[k2] * tcos[k2] val i0 = im[k2] * tcos[k2] + re[k2] * tsin[k2] out[2 * k1] = r0; out[2 * k1 + 1] = i0 out[2 * k2] = r1; out[2 * k2 + 1] = i1 } } /** Full IMDCT: [out] gets 2N samples from N coefficients in [input] (ff_imdct_calc). */ fun inverse(input: FloatArray, out: FloatArray) { imdctHalf(input, half) System.arraycopy(half, 0, out, n2, n) for (k in 0 until n2) { out[k] = -out[n - k - 1] out[2 * n - k - 1] = out[n + k] } } } /** * Decodes WMA 1/2 "superframes" (one block_align-sized chunk of a media object at a time) into * planar float samples. */ class WmaDecoder( val version: Int, // 1 = WMA 1 (0x160), 2 = WMA 2 (0x161) val channels: Int, val sampleRate: Int, bitRate: Int, private val blockAlign: Int, extradata: ByteArray, ) { private companion object { const val BLOCK_MIN_BITS = 7 const val BLOCK_MAX_BITS = 11 const val BLOCK_MAX_SIZE = 1 shl BLOCK_MAX_BITS const val BLOCK_NB_SIZES = BLOCK_MAX_BITS - BLOCK_MIN_BITS + 1 const val HIGH_BAND_MAX_SIZE = 16 const val NB_LSP_COEFS = 10 const val MAX_CODED_SUPERFRAME_SIZE = 32768 const val NOISE_TAB_SIZE = 8192 const val LSP_POW_BITS = 7 /** pow(10, i / 16.0) for i in -60..95. */ val POW_TAB = FloatArray(156) { 10.0.pow((it - 60) / 16.0).toFloat() } } private val useExpVlc: Boolean val useBitReservoir: Boolean private var useVariableBlockLen: Boolean private var useNoiseCoding = true private val byteOffsetBits: Int val frameLenBits: Int val frameLen: Int private val nbBlockSizes: Int private val exponentSizes = IntArray(BLOCK_NB_SIZES) private val exponentBands = Array(BLOCK_NB_SIZES) { IntArray(25) } private val highBandStart = IntArray(BLOCK_NB_SIZES) private val coefsStart: Int private val coefsEnd = IntArray(BLOCK_NB_SIZES) private val exponentHighSizes = IntArray(BLOCK_NB_SIZES) private val exponentHighBands = Array(BLOCK_NB_SIZES) { IntArray(HIGH_BAND_MAX_SIZE) } private val highBandCoded = Array(2) { IntArray(HIGH_BAND_MAX_SIZE) } private val highBandValues = Array(2) { IntArray(HIGH_BAND_MAX_SIZE) } private val coefVlc = arrayOfNulls(2) private val runTable = arrayOfNulls(2) private val levelTable = arrayOfNulls(2) private var expVlc: Vlc? = null private var hgainVlc: Vlc? = null private var resetBlockLengths = true private var blockLenBits = 0 private var nextBlockLenBits = 0 private var prevBlockLenBits = 0 private var blockLen = 0 private var blockPos = 0 private var msStereo = false private val channelCoded = BooleanArray(2) private val exponentsBsize = IntArray(2) private val exponents = Array(2) { FloatArray(BLOCK_MAX_SIZE) } private val maxExponent = floatArrayOf(1f, 1f) private val coefs1 = Array(2) { FloatArray(BLOCK_MAX_SIZE) } private val coefs = Array(2) { FloatArray(BLOCK_MAX_SIZE) } private val output = FloatArray(BLOCK_MAX_SIZE * 2) private val imdct = arrayOfNulls(BLOCK_NB_SIZES) private val windows = arrayOfNulls(BLOCK_NB_SIZES) private val frameOut = Array(2) { FloatArray(BLOCK_MAX_SIZE * 2) } private val lastSuperframe = ByteArray(MAX_CODED_SUPERFRAME_SIZE + 64) private var lastBitoffset = 0 private var lastSuperframeLen = 0 private val exponentsInitialized = BooleanArray(2) private val noiseTable = FloatArray(NOISE_TAB_SIZE) private var noiseIndex = 0 private var noiseMult = 0f private val lspCosTable = FloatArray(BLOCK_MAX_SIZE) private val lspPowETable = FloatArray(256) private val lspPowMTable1 = FloatArray(1 shl LSP_POW_BITS) private val lspPowMTable2 = FloatArray(1 shl LSP_POW_BITS) private lateinit var gb: BitReader init { require(channels in 1..2 && sampleRate in 1..50000 && bitRate > 0 && blockAlign > 0) { "Unsupported WMA stream" } var flags2 = 0 if (version == 1 && extradata.size >= 4) flags2 = le16(extradata, 2) else if (version == 2 && extradata.size >= 6) flags2 = le16(extradata, 4) useExpVlc = flags2 and 1 != 0 useBitReservoir = flags2 and 2 != 0 useVariableBlockLen = flags2 and 4 != 0 if (version == 2 && extradata.size >= 8 && le16(extradata, 4) == 0xd && useVariableBlockLen) useVariableBlockLen = false // ---- ff_wma_init ---- frameLenBits = when { sampleRate <= 16000 -> 9 sampleRate <= 22050 || (sampleRate <= 32000 && version == 1) -> 10 else -> 11 } nextBlockLenBits = frameLenBits; prevBlockLenBits = frameLenBits; blockLenBits = frameLenBits frameLen = 1 shl frameLenBits nbBlockSizes = if (useVariableBlockLen) { var nb = ((flags2 shr 3) and 3) + 1 if (bitRate / channels >= 32000) nb += 2 val nbMax = frameLenBits - BLOCK_MIN_BITS if (nb > nbMax) nb = nbMax nb + 1 } else 1 var highFreq = sampleRate * 0.5f var sampleRate1 = sampleRate if (version == 2) { sampleRate1 = when { sampleRate1 >= 44100 -> 44100 sampleRate1 >= 22050 -> 22050 sampleRate1 >= 16000 -> 16000 sampleRate1 >= 11025 -> 11025 sampleRate1 >= 8000 -> 8000 else -> sampleRate1 } } val bps = bitRate.toFloat() / (channels * sampleRate).toFloat() byteOffsetBits = log2i((bps * frameLen / 8.0 + 0.5).toInt()) + 2 require(byteOffsetBits + 3 <= 25) { "byte_offset_bits too large" } var bps1 = bps if (channels == 2) bps1 = bps * 1.6f when (sampleRate1) { 44100 -> if (bps1 >= 0.61f) useNoiseCoding = false else highFreq *= 0.4f 22050 -> if (bps1 >= 1.16f) useNoiseCoding = false else if (bps1 >= 0.72f) highFreq *= 0.7f else highFreq *= 0.6f 16000 -> if (bps > 0.5f) highFreq *= 0.5f else highFreq *= 0.3f 11025 -> highFreq *= 0.7f 8000 -> if (bps <= 0.625f) highFreq *= 0.5f else if (bps > 0.75f) useNoiseCoding = false else highFreq *= 0.65f else -> if (bps >= 0.8f) highFreq *= 0.75f else if (bps >= 0.6f) highFreq *= 0.6f else highFreq *= 0.5f } coefsStart = if (version == 1) 3 else 0 for (k in 0 until nbBlockSizes) { val blockLenK = frameLen shr k if (version == 1) { var lpos = 0 var i = 0 while (i < 25) { val a = WmaTables.CRITICAL_FREQS[i] var pos = ((blockLenK * 2 * a) + (sampleRate shr 1)) / sampleRate if (pos > blockLenK) pos = blockLenK exponentBands[0][i] = pos - lpos if (pos >= blockLenK) { i++; break } lpos = pos i++ } exponentSizes[0] = i } else { val a = frameLenBits - BLOCK_MIN_BITS - k val table: IntArray? = if (a < 3) when { sampleRate >= 44100 -> WmaTables.EXPONENT_BAND_44100[a] sampleRate >= 32000 -> WmaTables.EXPONENT_BAND_32000[a] sampleRate >= 22050 -> WmaTables.EXPONENT_BAND_22050[a] else -> null } else null if (table != null) { val n = table[0] for (i in 0 until n) exponentBands[k][i] = table[1 + i] exponentSizes[k] = n } else { var j = 0 var lpos = 0 for (i in 0 until 25) { val a2 = WmaTables.CRITICAL_FREQS[i] var pos = ((blockLenK * 2 * a2) + (sampleRate shl 1)) / (4 * sampleRate) pos = pos shl 2 if (pos > blockLenK) pos = blockLenK if (pos > lpos) exponentBands[k][j++] = pos - lpos if (pos >= blockLenK) break lpos = pos } exponentSizes[k] = j } } coefsEnd[k] = (frameLen - ((frameLen * 9) / 100)) shr k highBandStart[k] = ((blockLenK * 2 * highFreq) / sampleRate + 0.5f).toInt() val n = exponentSizes[k] var j = 0 var pos = 0 for (i in 0 until n) { var startB = pos pos += exponentBands[k][i] var endB = pos if (startB < highBandStart[k]) startB = highBandStart[k] if (endB > coefsEnd[k]) endB = coefsEnd[k] if (endB > startB) exponentHighBands[k][j++] = endB - startB } exponentHighSizes[k] = j } for (i in 0 until nbBlockSizes) { val n = 1 shl (frameLenBits - i) windows[i] = FloatArray(n) { sin((it + 0.5) * (PI / (2.0 * n))).toFloat() } } if (useNoiseCoding) { noiseMult = if (useExpVlc) 0.02f else 0.04f var seed = 1 val norm = ((1.0 / (1L shl 31).toDouble()) * sqrt(3.0) * noiseMult).toFloat() for (i in 0 until NOISE_TAB_SIZE) { seed = seed * 314159 + 1 noiseTable[i] = seed.toFloat() * norm } } var coefVlcTable = 2 if (sampleRate >= 32000) { if (bps1 < 0.72f) coefVlcTable = 0 else if (bps1 < 1.16f) coefVlcTable = 1 } for (t in 0..1) initCoefVlc(t, coefVlcTable * 2 + t) // ---- wma_decode_init ---- for (i in 0 until nbBlockSizes) imdct[i] = Imdct(1 shl (frameLenBits - i), 1f / 32768f) if (useNoiseCoding) hgainVlc = Vlc.fromLengths(WmaTables.HGAIN_LEN, WmaTables.HGAIN_SYM, -18) if (useExpVlc) expVlc = Vlc(WmaTables.SCALEFACTOR_CODES, WmaTables.SCALEFACTOR_BITS) else lspToCurveInit(frameLen) } /** Samples to drop at the very start (and after a seek), as FFmpeg does: the decoder's delay. */ val skipSamples: Int get() = frameLen * 2 private fun initCoefVlc(slot: Int, table: Int) { val (codes, bits, levels) = when (table) { 0 -> Triple(WmaTables.COEF0_CODES, WmaTables.COEF0_BITS, WmaTables.LEVELS0) 1 -> Triple(WmaTables.COEF1_CODES, WmaTables.COEF1_BITS, WmaTables.LEVELS1) 2 -> Triple(WmaTables.COEF2_CODES, WmaTables.COEF2_BITS, WmaTables.LEVELS2) 3 -> Triple(WmaTables.COEF3_CODES, WmaTables.COEF3_BITS, WmaTables.LEVELS3) 4 -> Triple(WmaTables.COEF4_CODES, WmaTables.COEF4_BITS, WmaTables.LEVELS4) else -> Triple(WmaTables.COEF5_CODES, WmaTables.COEF5_BITS, WmaTables.LEVELS5) } val n = bits.size coefVlc[slot] = Vlc(codes, bits) val run = IntArray(n) val lev = FloatArray(n) var i = 2 var level = 1 var k = 0 while (i < n) { val l = levels[k++] for (j in 0 until l) { run[i] = j lev[i] = level.toFloat() i++ } level++ } runTable[slot] = run levelTable[slot] = lev } private fun lspToCurveInit(frameLen: Int) { val wdel = PI / frameLen for (i in 0 until frameLen) lspCosTable[i] = (2.0 * cos(wdel * i)).toFloat() for (i in 0 until 256) lspPowETable[i] = 2.0.pow((i - 126) * -0.25).toFloat() var b = 1.0f for (i in (1 shl LSP_POW_BITS) - 1 downTo 0) { val m = (1 shl LSP_POW_BITS) + i var a = (m * (0.5 / (1 shl LSP_POW_BITS))).toFloat() a = (1.0 / sqrt(sqrt(a.toDouble()))).toFloat() lspPowMTable1[i] = 2 * a - b lspPowMTable2[i] = b - a b = a } } private fun powM14(x: Float): Float { val u = java.lang.Float.floatToRawIntBits(x) val e = u ushr 23 val m = (u ushr (23 - LSP_POW_BITS)) and ((1 shl LSP_POW_BITS) - 1) val t = java.lang.Float.intBitsToFloat(((u shl LSP_POW_BITS) and ((1 shl 23) - 1)) or (127 shl 23)) val a = lspPowMTable1[m] val b = lspPowMTable2[m] return lspPowETable[e and 255] * (a + b * t) } private fun lspToCurve(out: FloatArray, ch: Int, n: Int, lsp: FloatArray) { var valMax = 0f for (i in 0 until n) { var p = 0.5f var q = 0.5f val w = lspCosTable[i] var j = 1 while (j < NB_LSP_COEFS) { q *= w - lsp[j - 1] p *= w - lsp[j] j += 2 } p *= p * (2.0f - w) q *= q * (2.0f + w) var v = p + q v = powM14(v) if (v > valMax) valMax = v out[i] = v } maxExponent[ch] = valMax } private fun decodeExpLsp(ch: Int) { val lsp = FloatArray(NB_LSP_COEFS) for (i in 0 until NB_LSP_COEFS) { val v = if (i == 0 || i >= 8) gb.bits(3) else gb.bits(4) lsp[i] = WmaTables.LSP_CODEBOOK[i][v] } lspToCurve(exponents[ch], ch, blockLen, lsp) } private fun decodeExpVlc(ch: Int): Boolean { val bands = exponentBands[frameLenBits - blockLenBits] var bi = 0 val q = exponents[ch] var qi = 0 val qEnd = blockLen var maxScale = 0f var lastExp: Int if (version == 1) { lastExp = gb.bits(5) + 10 val v = POW_TAB[lastExp + 60] maxScale = v val n = bands[bi++] for (i in 0 until n) q[qi++] = v } else lastExp = 36 while (qi < qEnd) { val code = expVlc!!.read(gb) if (code == Int.MIN_VALUE) return false lastExp += code - 60 if (lastExp + 60 < 0 || lastExp + 60 >= POW_TAB.size) return false val v = POW_TAB[lastExp + 60] if (v > maxScale) maxScale = v val n = bands[bi++] for (i in 0 until n) { if (qi < q.size) q[qi] = v; qi++ } } maxExponent[ch] = maxScale return true } private fun window(outArr: FloatArray, outStart: Int) { val inArr = output var out = outStart var inn = 0 if (blockLenBits <= prevBlockLenBits) { val bl = blockLen val w = windows[frameLenBits - blockLenBits]!! for (i in 0 until bl) outArr[out + i] = inArr[inn + i] * w[i] + outArr[out + i] } else { val bl = 1 shl prevBlockLenBits val n = (blockLen - bl) / 2 val w = windows[frameLenBits - prevBlockLenBits]!! for (i in 0 until bl) outArr[out + n + i] = inArr[inn + n + i] * w[i] + outArr[out + n + i] System.arraycopy(inArr, inn + n + bl, outArr, out + n + bl, n) } out += blockLen inn += blockLen if (blockLenBits <= nextBlockLenBits) { val bl = blockLen val w = windows[frameLenBits - blockLenBits]!! for (i in 0 until bl) outArr[out + i] = inArr[inn + i] * w[bl - 1 - i] } else { val bl = 1 shl nextBlockLenBits val n = (blockLen - bl) / 2 val w = windows[frameLenBits - nextBlockLenBits]!! System.arraycopy(inArr, inn, outArr, out, n) for (i in 0 until bl) outArr[out + n + i] = inArr[inn + n + i] * w[bl - 1 - i] java.util.Arrays.fill(outArr, out + n + bl, out + n + bl + n, 0f) } } private fun runLevelDecode(slot: Int, ptr: FloatArray, numCoefs: Int, coefNbBits: Int): Boolean { val vlc = coefVlc[slot]!! val levels = levelTable[slot]!! val runs = runTable[slot]!! val mask = blockLen - 1 var offset = 0 while (offset < numCoefs) { val code = vlc.read(gb) if (code == Int.MIN_VALUE) return false if (code > 1) { offset += runs[code] val positive = gb.bit() == 1 ptr[offset and mask] = if (positive) levels[code] else -levels[code] } else if (code == 1) { break } else { val level = gb.bits(coefNbBits) offset += gb.bits(frameLenBits) val positive = gb.bit() == 1 ptr[offset and mask] = if (positive) level.toFloat() else -level.toFloat() } offset++ } return offset <= numCoefs } /** Returns 0 if OK, 1 if last block of frame, -1 on error. */ private fun decodeBlock(): Int { if (useVariableBlockLen) { val n = log2i(nbBlockSizes - 1) + 1 if (resetBlockLengths) { resetBlockLengths = false var v = gb.bits(n) if (v >= nbBlockSizes) return -1 prevBlockLenBits = frameLenBits - v v = gb.bits(n) if (v >= nbBlockSizes) return -1 blockLenBits = frameLenBits - v } else { prevBlockLenBits = blockLenBits blockLenBits = nextBlockLenBits } val v = gb.bits(n) if (v >= nbBlockSizes) return -1 nextBlockLenBits = frameLenBits - v } else { nextBlockLenBits = frameLenBits; prevBlockLenBits = frameLenBits; blockLenBits = frameLenBits } if (frameLenBits - blockLenBits >= nbBlockSizes) return -1 blockLen = 1 shl blockLenBits if (blockPos + blockLen > frameLen) return -1 if (channels == 2) msStereo = gb.bit() == 1 var any = false for (ch in 0 until channels) { val a = gb.bit() == 1 channelCoded[ch] = a any = any or a } val bsize = frameLenBits - blockLenBits if (any) { var totalGain = 1 while (true) { if (gb.left() < 7) return -1 val a = gb.bits(7) totalGain += a if (a != 127) break } val coefNbBits = totalGainToBits(totalGain) val n = coefsEnd[bsize] - coefsStart val nbCoefs = IntArray(channels) { n } if (useNoiseCoding) { for (ch in 0 until channels) { if (channelCoded[ch]) { val nh = exponentHighSizes[bsize] for (i in 0 until nh) { val a = gb.bit() highBandCoded[ch][i] = a if (a != 0) nbCoefs[ch] -= exponentHighBands[bsize][i] } } } for (ch in 0 until channels) { if (channelCoded[ch]) { val nh = exponentHighSizes[bsize] var v = Int.MIN_VALUE for (i in 0 until nh) { if (highBandCoded[ch][i] != 0) { if (v == Int.MIN_VALUE) v = gb.bits(7) - 19 else { val d = hgainVlc!!.read(gb) if (d == Int.MIN_VALUE) return -1 v += d } highBandValues[ch][i] = v } } } } } if (blockLenBits == frameLenBits || gb.bit() == 1) { for (ch in 0 until channels) { if (channelCoded[ch]) { if (useExpVlc) { if (!decodeExpVlc(ch)) return -1 } else decodeExpLsp(ch) exponentsBsize[ch] = bsize exponentsInitialized[ch] = true } } } for (ch in 0 until channels) if (channelCoded[ch] && !exponentsInitialized[ch]) return -1 for (ch in 0 until channels) { if (channelCoded[ch]) { val tindex = if (ch == 1 && msStereo) 1 else 0 val ptr = coefs1[ch] java.util.Arrays.fill(ptr, 0, blockLen, 0f) if (!runLevelDecode(tindex, ptr, nbCoefs[ch], coefNbBits)) return -1 } if (version == 1 && channels >= 2) gb.align() } var mdctNorm = 1.0f / (blockLen / 2).toFloat() if (version == 1) mdctNorm *= sqrt((blockLen / 2).toFloat()) for (ch in 0 until channels) { if (!channelCoded[ch]) continue val c1 = coefs1[ch] var c1i = 0 val ex = exponents[ch] val esize = exponentsBsize[ch] var mult = (10.0.pow(totalGain * 0.05) / maxExponent[ch]).toFloat() mult *= mdctNorm val out = coefs[ch] var oi = 0 if (useNoiseCoding) { var mult1 = mult for (i in 0 until coefsStart) { out[oi++] = noiseTable[noiseIndex] * ex[(i shl bsize) shr esize] * mult1 noiseIndex = (noiseIndex + 1) and (NOISE_TAB_SIZE - 1) } val n1 = exponentHighSizes[bsize] val expPower = FloatArray(HIGH_BAND_MAX_SIZE) var exIdx = (highBandStart[bsize] shl bsize) shr esize var lastHighBand = 0 for (j in 0 until n1) { val nn = exponentHighBands[frameLenBits - blockLenBits][j] if (highBandCoded[ch][j] != 0) { var e2 = 0f for (i in 0 until nn) { val v = ex[exIdx + ((i shl bsize) shr esize)] e2 += v * v } expPower[j] = e2 / nn lastHighBand = j } exIdx += (nn shl bsize) shr esize } exIdx = (coefsStart shl bsize) shr esize for (j in -1 until n1) { val nn = if (j < 0) highBandStart[bsize] - coefsStart else exponentHighBands[frameLenBits - blockLenBits][j] if (j >= 0 && highBandCoded[ch][j] != 0) { mult1 = sqrt(expPower[j] / expPower[lastHighBand]) mult1 *= 10.0.pow(highBandValues[ch][j] * 0.05).toFloat() mult1 /= (maxExponent[ch] * noiseMult) mult1 *= mdctNorm for (i in 0 until nn) { val noise = noiseTable[noiseIndex] noiseIndex = (noiseIndex + 1) and (NOISE_TAB_SIZE - 1) out[oi++] = noise * ex[exIdx + ((i shl bsize) shr esize)] * mult1 } exIdx += (nn shl bsize) shr esize } else { for (i in 0 until nn) { val noise = noiseTable[noiseIndex] noiseIndex = (noiseIndex + 1) and (NOISE_TAB_SIZE - 1) out[oi++] = (c1[c1i++] + noise) * ex[exIdx + ((i shl bsize) shr esize)] * mult } exIdx += (nn shl bsize) shr esize } } val nn = blockLen - coefsEnd[bsize] mult1 = mult * ex[exIdx + ((-(1 shl bsize)) shr esize)] for (i in 0 until nn) { out[oi++] = noiseTable[noiseIndex] * mult1 noiseIndex = (noiseIndex + 1) and (NOISE_TAB_SIZE - 1) } } else { for (i in 0 until coefsStart) out[oi++] = 0f val nn = nbCoefs[ch] for (i in 0 until nn) out[oi++] = c1[i] * ex[(i shl bsize) shr esize] * mult val rest = blockLen - coefsEnd[bsize] for (i in 0 until rest) out[oi++] = 0f } } if (msStereo && channelCoded[1]) { if (!channelCoded[0]) { java.util.Arrays.fill(coefs[0], 0, blockLen, 0f) channelCoded[0] = true } val a = coefs[0]; val b = coefs[1] for (i in 0 until blockLen) { val t = a[i] - b[i] a[i] += b[i] b[i] = t } } } // next: val md = imdct[bsize]!! for (ch in 0 until channels) { val n4 = blockLen / 2 if (channelCoded[ch]) md.inverse(coefs[ch], output) else if (!(msStereo && ch == 1)) java.util.Arrays.fill(output, 0f) val index = (frameLen / 2) + blockPos - n4 window(frameOut[ch], index) } blockPos += blockLen return if (blockPos >= frameLen) 1 else 0 } private fun decodeFrame(out: Array, offset: Int): Boolean { blockPos = 0 while (true) { val r = decodeBlock() if (r < 0) return false if (r != 0) break } for (ch in 0 until channels) { System.arraycopy(frameOut[ch], 0, out[ch], offset, frameLen) System.arraycopy(frameOut[ch], frameLen, frameOut[ch], 0, frameLen) } return true } /** * Decodes one superframe ([blockAlign] bytes starting at [off] in [buf]). Returns planar samples * (one array per channel, all the same length), an empty result when the superframe only * carried data for the next one, or null on a decoding error (the next superframe resyncs). */ fun decodeSuperframe(buf: ByteArray, off: Int): Array? { gb = BitReader(buf, off, blockAlign * 8) var nbFrames: Int if (useBitReservoir) { gb.skip(4) nbFrames = gb.bits(4) - (if (lastSuperframeLen <= 0) 1 else 0) if (nbFrames <= 0) { if (nbFrames < 0 || gb.left() <= 8) { lastSuperframeLen = 0; return null } if (lastSuperframeLen + blockAlign - 1 > MAX_CODED_SUPERFRAME_SIZE) { lastSuperframeLen = 0; return null } var q = lastSuperframeLen for (i in 0 until blockAlign - 1) lastSuperframe[q++] = gb.bits(8).toByte() lastSuperframeLen += 8 * blockAlign - 8 return Array(channels) { FloatArray(0) } } } else nbFrames = 1 val total = nbFrames * frameLen val samples = Array(channels) { FloatArray(total) } var sampleOffset = 0 if (useBitReservoir) { val bitOffset = gb.bits(byteOffsetBits + 3) if (bitOffset > gb.left()) { lastSuperframeLen = 0; return null } if (lastSuperframeLen > 0) { if (lastSuperframeLen + ((bitOffset + 7) shr 3) > MAX_CODED_SUPERFRAME_SIZE) { lastSuperframeLen = 0; return null } var q = lastSuperframeLen var len = bitOffset while (len > 7) { lastSuperframe[q++] = gb.bits(8).toByte(); len -= 8 } if (len > 0) lastSuperframe[q++] = (gb.bits(len) shl (8 - len)).toByte() java.util.Arrays.fill(lastSuperframe, q, minOf(q + 8, lastSuperframe.size), 0) val saved = gb gb = BitReader(lastSuperframe, 0, lastSuperframeLen * 8 + bitOffset) if (lastBitoffset > 0) gb.skip(lastBitoffset) if (!decodeFrame(samples, sampleOffset)) { lastSuperframeLen = 0; return null } sampleOffset += frameLen nbFrames-- gb = saved } val pos = bitOffset + 4 + 4 + byteOffsetBits + 3 if (pos >= MAX_CODED_SUPERFRAME_SIZE * 8 || pos > blockAlign * 8) { lastSuperframeLen = 0; return null } gb = BitReader(buf, off + (pos shr 3), (blockAlign - (pos shr 3)) * 8) val skip = pos and 7 if (skip > 0) gb.skip(skip) resetBlockLengths = true for (i in 0 until nbFrames) { if (!decodeFrame(samples, sampleOffset)) { lastSuperframeLen = 0; return null } sampleOffset += frameLen } val pos2 = gb.pos + ((bitOffset + 4 + 4 + byteOffsetBits + 3) and 7.inv()) lastBitoffset = pos2 and 7 val p = pos2 shr 3 val len = blockAlign - p if (len > MAX_CODED_SUPERFRAME_SIZE || len < 0) { lastSuperframeLen = 0; return null } lastSuperframeLen = len System.arraycopy(buf, off + p, lastSuperframe, 0, len) } else { if (!decodeFrame(samples, sampleOffset)) return null sampleOffset += frameLen } return if (sampleOffset == total) samples else Array(channels) { samples[it].copyOf(sampleOffset) } } /** The samples still in the overlap buffer at the end of the stream. */ fun drain(): Array = Array(channels) { frameOut[it].copyOf(frameLen) } /** Forget all state, for a seek. */ fun flush() { lastBitoffset = 0 lastSuperframeLen = 0 resetBlockLengths = true for (ch in 0 until 2) java.util.Arrays.fill(frameOut[ch], 0f) } private fun totalGainToBits(g: Int) = when { g < 15 -> 13 g < 32 -> 12 g < 40 -> 11 g < 45 -> 10 else -> 9 } private fun log2i(v: Int): Int = if (v <= 0) 0 else 31 - Integer.numberOfLeadingZeros(v) private fun le16(b: ByteArray, o: Int) = (b[o].toInt() and 255) or ((b[o + 1].toInt() and 255) shl 8) }