mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
"hardware" based mp2 decoding; dma and memcpy can now move data into the "hardware"
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
const SND_BASE_ADDR = audio.getBaseAddr()
|
||||
|
||||
if (!SND_BASE_ADDR) return 10
|
||||
|
||||
const pcm = require("pcm")
|
||||
const interactive = exec_args[2] && exec_args[2].toLowerCase() == "/i"
|
||||
function printdbg(s) { if (0) serial.println(s) }
|
||||
@@ -75,7 +79,7 @@ let bytes_left = FILE_SIZE
|
||||
let decodedLength = 0
|
||||
|
||||
|
||||
//serial.println(`Frame size: ${FRAME_SIZE}`)
|
||||
serial.println(`Frame size: ${FRAME_SIZE}`)
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +106,8 @@ function decodeEvent(frameSize, len) {
|
||||
}
|
||||
|
||||
|
||||
decodeAndResample(samplePtrL, samplePtrR, decodePtr, len)
|
||||
// decodeAndResample(samplePtrL, samplePtrR, decodePtr, len)
|
||||
|
||||
audio.putPcmDataByPtr(decodePtr, len, 0)
|
||||
audio.setSampleUploadLength(0, len)
|
||||
audio.startSampleUpload(0)
|
||||
@@ -168,13 +173,10 @@ audio.setMasterVolume(0, 255)
|
||||
audio.play(0)
|
||||
|
||||
|
||||
let mp2context = audio.mp2Init()
|
||||
//let mp2context = audio.mp2Init()
|
||||
audio.mp2Init()
|
||||
|
||||
// decode frame
|
||||
let frame = sys.malloc(FRAME_SIZE)
|
||||
let samplePtrL = sys.malloc(2304) // 16b samples
|
||||
let samplePtrR = sys.malloc(2304) // 16b samples
|
||||
let decodePtr = sys.malloc(2304) // 8b samples
|
||||
let t1 = sys.nanoTime()
|
||||
let bufRealTimeLen = 36
|
||||
let stopPlay = false
|
||||
@@ -191,14 +193,23 @@ try {
|
||||
|
||||
printPlayBar()
|
||||
|
||||
filebuf.readBytes(FRAME_SIZE, frame)
|
||||
let [frameSize, samples] = audio.mp2DecodeFrame(mp2context, frame, true, samplePtrL, samplePtrR)
|
||||
if (frameSize) {
|
||||
// println(samples)
|
||||
// play using decodedLR
|
||||
decodeEvent(frameSize, samples)
|
||||
FRAME_SIZE = frameSize // JUST IN CASE when a vbr mp2 is not filtered and played thru
|
||||
|
||||
filebuf.readBytes(FRAME_SIZE, SND_BASE_ADDR - 2368)
|
||||
audio.mp2Decode()
|
||||
sys.waitForMemChg(SND_BASE_ADDR - 41, 255, 255)
|
||||
|
||||
if (audio.getPosition(0) >= QUEUE_MAX) {
|
||||
while (audio.getPosition(0) >= (QUEUE_MAX >>> 1)) {
|
||||
printdbg(`Queue full, waiting until the queue has some space (${audio.getPosition(0)}/${QUEUE_MAX})`)
|
||||
sys.sleep(bufRealTimeLen)
|
||||
}
|
||||
}
|
||||
audio.putPcmDataByPtr(SND_BASE_ADDR - 64, 2304, 0)
|
||||
audio.setSampleUploadLength(0, 2304)
|
||||
audio.startSampleUpload(0)
|
||||
sys.sleep(10)
|
||||
|
||||
|
||||
|
||||
bytes_left -= FRAME_SIZE
|
||||
decodedLength += FRAME_SIZE
|
||||
@@ -209,10 +220,6 @@ catch (e) {
|
||||
errorlevel = 1
|
||||
}
|
||||
finally {
|
||||
sys.free(frame)
|
||||
sys.free(decodePtr)
|
||||
sys.free(samplePtrL)
|
||||
sys.free(samplePtrR)
|
||||
}
|
||||
|
||||
return errorlevel
|
||||
@@ -49,6 +49,8 @@ function readBytes(length, ptrToDecode) {
|
||||
let ptr = (ptrToDecode === undefined) ? sys.malloc(length) : ptrToDecode
|
||||
let requiredBlocks = Math.floor((readCount + length) / 4096) - Math.floor(readCount / 4096)
|
||||
|
||||
let destVector = (ptrToDecode >= 0) ? 1 : -1
|
||||
|
||||
let completedReads = 0
|
||||
|
||||
// serial.println(`readBytes(${length}); readCount = ${readCount}`)
|
||||
@@ -74,7 +76,7 @@ function readBytes(length, ptrToDecode) {
|
||||
// serial.println(`Pulled a block (${thisBlockLen}); readCount = ${readCount}, completedReads = ${completedReads}, remaining = ${remaining}`)
|
||||
|
||||
// copy from read buffer to designated position
|
||||
sys.memcpy(-4097 - port*4096, ptr + completedReads, remaining)
|
||||
sys.memcpy(-4097 - port*4096, ptr + completedReads*destVector, remaining)
|
||||
|
||||
// increment readCount properly
|
||||
readCount += remaining
|
||||
@@ -90,7 +92,7 @@ function readBytes(length, ptrToDecode) {
|
||||
// serial.println(`Reusing a block (${thisBlockLen}); readCount = ${readCount}, completedReads = ${completedReads}`)
|
||||
|
||||
// copy from read buffer to designated position
|
||||
sys.memcpy(-4097 - port*4096 - padding, ptr + completedReads, thisBlockLen)
|
||||
sys.memcpy(-4097 - port*4096 - padding, ptr + completedReads*destVector, thisBlockLen)
|
||||
|
||||
// increment readCount properly
|
||||
readCount += thisBlockLen
|
||||
|
||||
@@ -419,7 +419,7 @@ Packet Types -
|
||||
32 | 0 | 1
|
||||
48 | 2 | 3
|
||||
56 | 4 | 5
|
||||
64 | 6 | 7
|
||||
64 | 6 | 7 (libtwolame does not allow bitrate lower than this on 32 kHz)
|
||||
80 | 8 | 9
|
||||
96 | 10 | 11
|
||||
112 | 12 | 13
|
||||
@@ -612,7 +612,17 @@ Sound Adapter MMIO
|
||||
|
||||
... auto-fill to Play head #4
|
||||
|
||||
32 ??: ???
|
||||
40 WO: Media Decoder Control
|
||||
Write 16 to initialise the MP2 context (call this before the decoding of NEW music)
|
||||
Write 1 to decode the frame as MP2
|
||||
|
||||
When called with byte 17, initialisation will precede before the decoding
|
||||
|
||||
41 RO: Media Decoder Status
|
||||
Non-zero value indicates the decoder is busy
|
||||
|
||||
64..2367 RW: MP2 Decoded Samples (unsigned 8-bit stereo)
|
||||
2368..4095 RW: MP2 Frame to be decoded
|
||||
|
||||
Sound Hardware Info
|
||||
- Sampling rate: 32000 Hz
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package net.torvald.tsvm
|
||||
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||
import net.torvald.tsvm.peripheral.AudioAdapter
|
||||
import org.graalvm.polyglot.Value
|
||||
import kotlin.math.cos
|
||||
import net.torvald.tsvm.peripheral.MP2Env
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2022-12-31.
|
||||
@@ -78,6 +76,15 @@ class AudioJSR223Delegate(private val vm: VM) {
|
||||
|
||||
|
||||
|
||||
// fun mp2Init() = getFirstSnd()?.mp2Env?.initialise()
|
||||
fun mp2GetInitialFrameSize(bytes: IntArray) = getFirstSnd()?.mp2Env?.getInitialFrameSize(bytes)
|
||||
// fun mp2DecodeFrame(mp2: MP2Env.MP2, framePtr: Long?, pcm: Boolean, outL: Long, outR: Long) = getFirstSnd()?.mp2Env?.decodeFrame(mp2, framePtr, pcm, outL, outR)
|
||||
|
||||
fun getBaseAddr(): Int? = getFirstSnd()?.let { return it.vm.findPeriSlotNum(it)?.times(-131072)?.minus(1) }
|
||||
fun mp2Init() = getFirstSnd()?.mmio_write(40L, 16)
|
||||
fun mp2Decode() = getFirstSnd()?.mmio_write(40L, 1)
|
||||
fun mp2InitThenDecode() = getFirstSnd()?.mmio_write(40L, 17)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -105,7 +112,7 @@ class AudioJSR223Delegate(private val vm: VM) {
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
private val synthNWin = Array(64) { i -> FloatArray(32) { j -> cos(((16 + i) * (2 * j + 1)) * (Math.PI / 64.0)).toFloat() } }
|
||||
/*private val synthNWin = Array(64) { i -> FloatArray(32) { j -> cos(((16 + i) * (2 * j + 1)) * (Math.PI / 64.0)).toFloat() } }
|
||||
private val synthDtbl = floatArrayOf(
|
||||
0.000000000f, -0.000015259f, -0.000015259f, -0.000015259f,
|
||||
-0.000015259f, -0.000015259f, -0.000015259f, -0.000030518f,
|
||||
@@ -425,551 +432,7 @@ class AudioJSR223Delegate(private val vm: VM) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
mp2dec.js JavaScript MPEG-1 Audio Layer II decoder
|
||||
Copyright (C) 2011 Liam Wilson
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
/* Note this is a port of kjmp2 by Martin J. Fiedler: */
|
||||
|
||||
/******************************************************************************
|
||||
** kjmp2 -- a minimal MPEG-1 Audio Layer II decoder library **
|
||||
*******************************************************************************
|
||||
** Copyright (C) 2006 Martin J. Fiedler martin.fiedler@gmx.net **
|
||||
** **
|
||||
** This software is provided 'as-is', without any express or implied **
|
||||
** warranty. In no event will the authors be held liable for any damages **
|
||||
** arising from the use of this software. **
|
||||
** **
|
||||
** Permission is granted to anyone to use this software for any purpose, **
|
||||
** including commercial applications, and to alter it and redistribute it **
|
||||
** freely, subject to the following restrictions: **
|
||||
** 1. The origin of this software must not be misrepresented; you must not **
|
||||
** claim that you wrote the original software. If you use this software **
|
||||
** in a product, an acknowledgment in the product documentation would **
|
||||
** be appreciated but is not required. **
|
||||
** 2. Altered source versions must be plainly marked as such, and must not **
|
||||
** be misrepresented as being the original software. **
|
||||
** 3. This notice may not be removed or altered from any source **
|
||||
** distribution. **
|
||||
******************************************************************************/
|
||||
|
||||
private var mp2_frame: Long? = null; // ptr
|
||||
private var STEREO=0;
|
||||
// #define JOINT_STEREO 1
|
||||
private var JOINT_STEREO=1;
|
||||
// #define DUAL_CHANNEL 2
|
||||
private var DUAL_CHANNEL=2;
|
||||
// #define MONO 3
|
||||
private var MONO=3;
|
||||
private val mp2_sample_rates = arrayOf(44100, 48000, 32000, 0);
|
||||
private val mp2_bitrates = arrayOf(32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384);
|
||||
private val mp2_scf_value = arrayOf(
|
||||
0x02000000, 0x01965FEA, 0x01428A30, 0x01000000,
|
||||
0x00CB2FF5, 0x00A14518, 0x00800000, 0x006597FB,
|
||||
0x0050A28C, 0x00400000, 0x0032CBFD, 0x00285146,
|
||||
0x00200000, 0x001965FF, 0x001428A3, 0x00100000,
|
||||
0x000CB2FF, 0x000A1451, 0x00080000, 0x00065980,
|
||||
0x00050A29, 0x00040000, 0x00032CC0, 0x00028514,
|
||||
0x00020000, 0x00019660, 0x0001428A, 0x00010000,
|
||||
0x0000CB30, 0x0000A145, 0x00008000, 0x00006598,
|
||||
0x000050A3, 0x00004000, 0x000032CC, 0x00002851,
|
||||
0x00002000, 0x00001966, 0x00001429, 0x00001000,
|
||||
0x00000CB3, 0x00000A14, 0x00000800, 0x00000659,
|
||||
0x0000050A, 0x00000400, 0x0000032D, 0x00000285,
|
||||
0x00000200, 0x00000196, 0x00000143, 0x00000100,
|
||||
0x000000CB, 0x000000A1, 0x00000080, 0x00000066,
|
||||
0x00000051, 0x00000040, 0x00000033, 0x00000028,
|
||||
0x00000020, 0x00000019, 0x00000014, 0);
|
||||
private val mp2_N = Array(64) { i -> IntArray(32) { j ->
|
||||
Math.floor(256.0 * Math.cos((16 + i) * ((j shl 1) + 1) * 0.0490873852123405)).toInt()
|
||||
} }
|
||||
private val mp2_U = IntArray(512)
|
||||
private val mp2_D = arrayOf(
|
||||
0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,-0x00001,
|
||||
-0x00001,-0x00001,-0x00001,-0x00002,-0x00002,-0x00003,-0x00003,-0x00004,
|
||||
-0x00004,-0x00005,-0x00006,-0x00006,-0x00007,-0x00008,-0x00009,-0x0000A,
|
||||
-0x0000C,-0x0000D,-0x0000F,-0x00010,-0x00012,-0x00014,-0x00017,-0x00019,
|
||||
-0x0001C,-0x0001E,-0x00022,-0x00025,-0x00028,-0x0002C,-0x00030,-0x00034,
|
||||
-0x00039,-0x0003E,-0x00043,-0x00048,-0x0004E,-0x00054,-0x0005A,-0x00060,
|
||||
-0x00067,-0x0006E,-0x00074,-0x0007C,-0x00083,-0x0008A,-0x00092,-0x00099,
|
||||
-0x000A0,-0x000A8,-0x000AF,-0x000B6,-0x000BD,-0x000C3,-0x000C9,-0x000CF,
|
||||
0x000D5, 0x000DA, 0x000DE, 0x000E1, 0x000E3, 0x000E4, 0x000E4, 0x000E3,
|
||||
0x000E0, 0x000DD, 0x000D7, 0x000D0, 0x000C8, 0x000BD, 0x000B1, 0x000A3,
|
||||
0x00092, 0x0007F, 0x0006A, 0x00053, 0x00039, 0x0001D,-0x00001,-0x00023,
|
||||
-0x00047,-0x0006E,-0x00098,-0x000C4,-0x000F3,-0x00125,-0x0015A,-0x00190,
|
||||
-0x001CA,-0x00206,-0x00244,-0x00284,-0x002C6,-0x0030A,-0x0034F,-0x00396,
|
||||
-0x003DE,-0x00427,-0x00470,-0x004B9,-0x00502,-0x0054B,-0x00593,-0x005D9,
|
||||
-0x0061E,-0x00661,-0x006A1,-0x006DE,-0x00718,-0x0074D,-0x0077E,-0x007A9,
|
||||
-0x007D0,-0x007EF,-0x00808,-0x0081A,-0x00824,-0x00826,-0x0081F,-0x0080E,
|
||||
0x007F5, 0x007D0, 0x007A0, 0x00765, 0x0071E, 0x006CB, 0x0066C, 0x005FF,
|
||||
0x00586, 0x00500, 0x0046B, 0x003CA, 0x0031A, 0x0025D, 0x00192, 0x000B9,
|
||||
-0x0002C,-0x0011F,-0x00220,-0x0032D,-0x00446,-0x0056B,-0x0069B,-0x007D5,
|
||||
-0x00919,-0x00A66,-0x00BBB,-0x00D16,-0x00E78,-0x00FDE,-0x01148,-0x012B3,
|
||||
-0x01420,-0x0158C,-0x016F6,-0x0185C,-0x019BC,-0x01B16,-0x01C66,-0x01DAC,
|
||||
-0x01EE5,-0x02010,-0x0212A,-0x02232,-0x02325,-0x02402,-0x024C7,-0x02570,
|
||||
-0x025FE,-0x0266D,-0x026BB,-0x026E6,-0x026ED,-0x026CE,-0x02686,-0x02615,
|
||||
-0x02577,-0x024AC,-0x023B2,-0x02287,-0x0212B,-0x01F9B,-0x01DD7,-0x01BDD,
|
||||
0x019AE, 0x01747, 0x014A8, 0x011D1, 0x00EC0, 0x00B77, 0x007F5, 0x0043A,
|
||||
0x00046,-0x003E5,-0x00849,-0x00CE3,-0x011B4,-0x016B9,-0x01BF1,-0x0215B,
|
||||
-0x026F6,-0x02CBE,-0x032B3,-0x038D3,-0x03F1A,-0x04586,-0x04C15,-0x052C4,
|
||||
-0x05990,-0x06075,-0x06771,-0x06E80,-0x0759F,-0x07CCA,-0x083FE,-0x08B37,
|
||||
-0x09270,-0x099A7,-0x0A0D7,-0x0A7FD,-0x0AF14,-0x0B618,-0x0BD05,-0x0C3D8,
|
||||
-0x0CA8C,-0x0D11D,-0x0D789,-0x0DDC9,-0x0E3DC,-0x0E9BD,-0x0EF68,-0x0F4DB,
|
||||
-0x0FA12,-0x0FF09,-0x103BD,-0x1082C,-0x10C53,-0x1102E,-0x113BD,-0x116FB,
|
||||
-0x119E8,-0x11C82,-0x11EC6,-0x120B3,-0x12248,-0x12385,-0x12467,-0x124EF,
|
||||
0x1251E, 0x124F0, 0x12468, 0x12386, 0x12249, 0x120B4, 0x11EC7, 0x11C83,
|
||||
0x119E9, 0x116FC, 0x113BE, 0x1102F, 0x10C54, 0x1082D, 0x103BE, 0x0FF0A,
|
||||
0x0FA13, 0x0F4DC, 0x0EF69, 0x0E9BE, 0x0E3DD, 0x0DDCA, 0x0D78A, 0x0D11E,
|
||||
0x0CA8D, 0x0C3D9, 0x0BD06, 0x0B619, 0x0AF15, 0x0A7FE, 0x0A0D8, 0x099A8,
|
||||
0x09271, 0x08B38, 0x083FF, 0x07CCB, 0x075A0, 0x06E81, 0x06772, 0x06076,
|
||||
0x05991, 0x052C5, 0x04C16, 0x04587, 0x03F1B, 0x038D4, 0x032B4, 0x02CBF,
|
||||
0x026F7, 0x0215C, 0x01BF2, 0x016BA, 0x011B5, 0x00CE4, 0x0084A, 0x003E6,
|
||||
-0x00045,-0x00439,-0x007F4,-0x00B76,-0x00EBF,-0x011D0,-0x014A7,-0x01746,
|
||||
0x019AE, 0x01BDE, 0x01DD8, 0x01F9C, 0x0212C, 0x02288, 0x023B3, 0x024AD,
|
||||
0x02578, 0x02616, 0x02687, 0x026CF, 0x026EE, 0x026E7, 0x026BC, 0x0266E,
|
||||
0x025FF, 0x02571, 0x024C8, 0x02403, 0x02326, 0x02233, 0x0212B, 0x02011,
|
||||
0x01EE6, 0x01DAD, 0x01C67, 0x01B17, 0x019BD, 0x0185D, 0x016F7, 0x0158D,
|
||||
0x01421, 0x012B4, 0x01149, 0x00FDF, 0x00E79, 0x00D17, 0x00BBC, 0x00A67,
|
||||
0x0091A, 0x007D6, 0x0069C, 0x0056C, 0x00447, 0x0032E, 0x00221, 0x00120,
|
||||
0x0002D,-0x000B8,-0x00191,-0x0025C,-0x00319,-0x003C9,-0x0046A,-0x004FF,
|
||||
-0x00585,-0x005FE,-0x0066B,-0x006CA,-0x0071D,-0x00764,-0x0079F,-0x007CF,
|
||||
0x007F5, 0x0080F, 0x00820, 0x00827, 0x00825, 0x0081B, 0x00809, 0x007F0,
|
||||
0x007D1, 0x007AA, 0x0077F, 0x0074E, 0x00719, 0x006DF, 0x006A2, 0x00662,
|
||||
0x0061F, 0x005DA, 0x00594, 0x0054C, 0x00503, 0x004BA, 0x00471, 0x00428,
|
||||
0x003DF, 0x00397, 0x00350, 0x0030B, 0x002C7, 0x00285, 0x00245, 0x00207,
|
||||
0x001CB, 0x00191, 0x0015B, 0x00126, 0x000F4, 0x000C5, 0x00099, 0x0006F,
|
||||
0x00048, 0x00024, 0x00002,-0x0001C,-0x00038,-0x00052,-0x00069,-0x0007E,
|
||||
-0x00091,-0x000A2,-0x000B0,-0x000BC,-0x000C7,-0x000CF,-0x000D6,-0x000DC,
|
||||
-0x000DF,-0x000E2,-0x000E3,-0x000E3,-0x000E2,-0x000E0,-0x000DD,-0x000D9,
|
||||
0x000D5, 0x000D0, 0x000CA, 0x000C4, 0x000BE, 0x000B7, 0x000B0, 0x000A9,
|
||||
0x000A1, 0x0009A, 0x00093, 0x0008B, 0x00084, 0x0007D, 0x00075, 0x0006F,
|
||||
0x00068, 0x00061, 0x0005B, 0x00055, 0x0004F, 0x00049, 0x00044, 0x0003F,
|
||||
0x0003A, 0x00035, 0x00031, 0x0002D, 0x00029, 0x00026, 0x00023, 0x0001F,
|
||||
0x0001D, 0x0001A, 0x00018, 0x00015, 0x00013, 0x00011, 0x00010, 0x0000E,
|
||||
0x0000D, 0x0000B, 0x0000A, 0x00009, 0x00008, 0x00007, 0x00007, 0x00006,
|
||||
0x00005, 0x00005, 0x00004, 0x00004, 0x00003, 0x00003, 0x00002, 0x00002,
|
||||
0x00002, 0x00002, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001);
|
||||
private val mp2_quant_lut_step1= arrayOf(
|
||||
arrayOf(0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 ),
|
||||
arrayOf(0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 ));
|
||||
private val mp2_QUANT_TAB_A = 27 or 64 // Table 3-B.2a: high-rate, sblimit = 27
|
||||
private val mp2_QUANT_TAB_B = 30 or 64 // Table 3-B.2b: high-rate, sblimit = 30
|
||||
private val mp2_QUANT_TAB_C = 8 // Table 3-B.2c: low-rate, sblimit = 8
|
||||
private val mp2_QUANT_TAB_D = 12 // Table 3-B.2d: low-rate, sblimit = 12
|
||||
private val mp2_quant_lut_step2 = arrayOf(
|
||||
arrayOf(mp2_QUANT_TAB_C, mp2_QUANT_TAB_C, mp2_QUANT_TAB_D),
|
||||
arrayOf(mp2_QUANT_TAB_A, mp2_QUANT_TAB_A, mp2_QUANT_TAB_A),
|
||||
arrayOf(mp2_QUANT_TAB_B, mp2_QUANT_TAB_A, mp2_QUANT_TAB_B));
|
||||
private val mp2_quant_lut_step3 = arrayOf(
|
||||
arrayOf(0x44,0x44,
|
||||
0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34
|
||||
),
|
||||
arrayOf(0x43,0x43,0x43,
|
||||
0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
|
||||
0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20));
|
||||
private val mp2_quant_lut_step4 = arrayOf(
|
||||
arrayOf(0, 1, 2, 17),
|
||||
arrayOf(0, 1, 2, 3, 4, 5, 6, 17),
|
||||
arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17),
|
||||
arrayOf(0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
||||
arrayOf(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17));
|
||||
private data class mp2_quantizer_spec(var nlevels: Int, var grouping: Boolean, var cw_bits: Int, var Smul: Int, var Sdiv: Int)
|
||||
private val mp2_quantizer_table =arrayOf(
|
||||
mp2_quantizer_spec ( 3, true, 5, 0x7FFF, 0xFFFF ),
|
||||
mp2_quantizer_spec ( 5, true, 7, 0x3FFF, 0x0002 ),
|
||||
mp2_quantizer_spec ( 7, false, 3, 0x2AAA, 0x0003 ),
|
||||
mp2_quantizer_spec ( 9, true, 10, 0x1FFF, 0x0002 ),
|
||||
mp2_quantizer_spec ( 15, false, 4, 0x1249, 0xFFFF ),
|
||||
mp2_quantizer_spec ( 31, false, 5, 0x0888, 0x0003 ),
|
||||
mp2_quantizer_spec ( 63, false, 6, 0x0421, 0xFFFF ),
|
||||
mp2_quantizer_spec ( 127, false, 7, 0x0208, 0x0009 ),
|
||||
mp2_quantizer_spec ( 255, false, 8, 0x0102, 0x007F ),
|
||||
mp2_quantizer_spec ( 511, false, 9, 0x0080, 0x0002 ),
|
||||
mp2_quantizer_spec ( 1023, false, 10, 0x0040, 0x0009 ),
|
||||
mp2_quantizer_spec ( 2047, false, 11, 0x0020, 0x0021 ),
|
||||
mp2_quantizer_spec ( 4095, false, 12, 0x0010, 0x0089 ),
|
||||
mp2_quantizer_spec ( 8191, false, 13, 0x0008, 0x0249 ),
|
||||
mp2_quantizer_spec ( 16383, false, 14, 0x0004, 0x0AAB ),
|
||||
mp2_quantizer_spec ( 32767, false, 15, 0x0002, 0x3FFF ),
|
||||
mp2_quantizer_spec ( 65535, false, 16, 0x0001, 0xFFFF ));
|
||||
|
||||
val KJMP2_MAGIC= 0x32706D;
|
||||
private var mp2_initialized = false;
|
||||
private var mp2_bit_window = 0;
|
||||
private var mp2_bits_in_window = 0;
|
||||
private var mp2_frame_pos = 0;
|
||||
|
||||
private fun syspeek(ptr: Long) = vm.peek(ptr)!!.toUint()
|
||||
|
||||
data class MP2(
|
||||
var Voffs: Int = 0,
|
||||
var id: Int = 0,
|
||||
var V: Array<IntArray> = Array(2) { IntArray(1024) }
|
||||
)
|
||||
|
||||
|
||||
private fun show_bits(bit_count: Int) = (mp2_bit_window shr (24 - (bit_count)));
|
||||
private fun get_bits(bit_count: Int): Int {
|
||||
var result = show_bits(bit_count);
|
||||
mp2_bit_window = (mp2_bit_window shl bit_count) and 0xFFFFFF;
|
||||
mp2_bits_in_window -= bit_count;
|
||||
while (mp2_bits_in_window < 16) {
|
||||
mp2_bit_window = mp2_bit_window or (syspeek(mp2_frame!! + mp2_frame_pos++) shl (16 - mp2_bits_in_window));
|
||||
mp2_bits_in_window += 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fun mp2Init(): MP2 {
|
||||
val mp2 = MP2()
|
||||
|
||||
// check if global initialization is required
|
||||
if (!mp2_initialized) {
|
||||
mp2_initialized = true;
|
||||
}
|
||||
|
||||
// perform local initialization: clean the context and put the magic in it
|
||||
for (i in 0 until 2){
|
||||
for (j in 1023 downTo 0){
|
||||
mp2.V[i][j] = 0;
|
||||
};
|
||||
};
|
||||
mp2.Voffs = 0;
|
||||
mp2.id = KJMP2_MAGIC;
|
||||
|
||||
return mp2
|
||||
};
|
||||
|
||||
private fun kjmp2_get_sample_rate(frame: Long?): Int {
|
||||
if (frame == null){
|
||||
return 0;};
|
||||
if ((syspeek(frame) != 0xFF) || (syspeek(frame +1) != 0xFD) || ((syspeek(frame +2) - 0x10) >= 0xE0)) {
|
||||
return 0;};
|
||||
return mp2_sample_rates[(syspeek(frame +2) shr 2) and 3];
|
||||
};
|
||||
|
||||
private fun read_allocation(sb: Int, b2_table: Int): mp2_quantizer_spec? {
|
||||
var table_idx = mp2_quant_lut_step3[b2_table][sb];
|
||||
table_idx = mp2_quant_lut_step4[table_idx and 15][get_bits(table_idx shr 4)];
|
||||
return if (table_idx != 0) (mp2_quantizer_table[table_idx - 1]) else null
|
||||
}
|
||||
|
||||
private fun read_samples(q: mp2_quantizer_spec?, scalefactor: Int, sample: IntArray) {
|
||||
var adj = 0;
|
||||
var value = 0;
|
||||
if (q == null) {
|
||||
// no bits allocated for this subband
|
||||
sample[0] = 0
|
||||
sample[1] = 0
|
||||
sample[2] = 0;
|
||||
return;
|
||||
}
|
||||
// resolve scalefactor
|
||||
var scalefactor = mp2_scf_value[scalefactor];
|
||||
|
||||
// decode samples
|
||||
adj = q.nlevels;
|
||||
if (q.grouping) {
|
||||
// decode grouped samples
|
||||
value = get_bits(q.cw_bits);
|
||||
sample[0] = value % adj;
|
||||
value = Math.floor(value.toDouble() / adj).toInt();
|
||||
sample[1] = value % adj;
|
||||
sample[2] = Math.floor(value.toDouble() / adj).toInt();
|
||||
} else {
|
||||
// decode direct samples
|
||||
for(idx in 0 until 3)
|
||||
sample[idx] = get_bits(q.cw_bits);
|
||||
}
|
||||
|
||||
// postmultiply samples
|
||||
adj = ((adj + 1) shr 1) - 1;
|
||||
for (idx in 0 until 3) {
|
||||
// step 1: renormalization to [-1..1]
|
||||
value = adj - sample[idx];
|
||||
value = (value * q.Smul) + Math.floor(value.toDouble() / q.Sdiv).toInt();
|
||||
// step 2: apply scalefactor
|
||||
sample[idx] = ( value * (scalefactor shr 12) + ((value * (scalefactor and 4095) + 2048) shr 12)) shr 12; // scale adjust
|
||||
}
|
||||
}
|
||||
|
||||
private var mp2_allocation: Array<Array<mp2_quantizer_spec?>> = Array(2) { Array(32) { null } }
|
||||
private var mp2_scfsi = Array(2) { IntArray(32) }
|
||||
private var mp2_scalefactor = Array(2) { Array(32) { IntArray(3) } }
|
||||
private var mp2_sample = Array(2) { Array(32) { IntArray(3) } }
|
||||
|
||||
|
||||
fun mp2GetInitialFrameSize(bytes: IntArray): Int {
|
||||
val b0 = bytes[0]
|
||||
val b1 = bytes[1]
|
||||
val b2 = bytes[2]
|
||||
|
||||
// check sync pattern
|
||||
if ((b0 != 0xFF) || (b1 != 0xFD) || ((b2 - 0x10) >= 0xE0)) {
|
||||
throw Error("Not a MP2 Frame Head: ${listOf(b0, b1, b2).map { it.toString(16).padStart(2,'0') }.joinToString(" ")}")
|
||||
}
|
||||
|
||||
val sampling_frequency = (b2 shr 2) and 3
|
||||
val bit_rate_index_minus1 = ((b2 shr 4) and 15) - 1
|
||||
if (bit_rate_index_minus1 > 13){
|
||||
throw Error("Invalid bit rate") // invalid bit rate or 'free format'
|
||||
}
|
||||
val padding_bit = b2.shr(1) and 1
|
||||
return Math.floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit
|
||||
}
|
||||
|
||||
fun mp2DecodeFrame(mp2: MP2, framePtr: Long?, pcm: Boolean, outL: Long, outR: Long): IntArray {
|
||||
|
||||
var pushSizeL = 0
|
||||
var pushSizeR = 0
|
||||
fun pushL(sampleL: Int) {
|
||||
vm.poke(outL + pushSizeL + 0, (sampleL and 255).toByte())
|
||||
vm.poke(outL + pushSizeL + 1, (sampleL shr 8).toByte())
|
||||
pushSizeL += 2
|
||||
}
|
||||
fun pushR(sampleR: Int) {
|
||||
vm.poke(outR + pushSizeR + 0, (sampleR and 255).toByte())
|
||||
vm.poke(outR + pushSizeR + 1, (sampleR shr 8).toByte())
|
||||
pushSizeR += 2
|
||||
}
|
||||
|
||||
|
||||
if (framePtr == null) {
|
||||
throw Error("Frame is null")
|
||||
}
|
||||
mp2_frame = framePtr;
|
||||
val bit_rate_index_minus1: Int;
|
||||
val sampling_frequency: Int;
|
||||
val padding_bit: Int;
|
||||
val mode: Int;
|
||||
val frame_size: Int;
|
||||
var bound: Int
|
||||
val sblimit: Int;
|
||||
val nch: Int;
|
||||
var sum: Int;
|
||||
var table_idx: Int;
|
||||
// general sanity check
|
||||
if (!mp2_initialized || (mp2.id != KJMP2_MAGIC)){
|
||||
throw Error("MP2 not initialised")
|
||||
};
|
||||
// check for valid header: syncword OK, MPEG-Audio Layer 2
|
||||
if ((syspeek(mp2_frame!!) != 0xFF) || ((syspeek(mp2_frame!! +1) and 0xFE) != 0xFC)){
|
||||
throw Error("Invalid header at $mp2_frame: ${syspeek(mp2_frame!!).toString(16)} ${syspeek(mp2_frame!! +1).toString(16)}")
|
||||
};
|
||||
|
||||
// set up the bitstream reader
|
||||
mp2_bit_window = syspeek(mp2_frame!! +2) shl 16;
|
||||
mp2_bits_in_window = 8;
|
||||
mp2_frame_pos = 3;
|
||||
|
||||
// read the rest of the header
|
||||
bit_rate_index_minus1 = get_bits(4) - 1;
|
||||
if (bit_rate_index_minus1 > 13){
|
||||
throw Error("Invalid bit rate") // invalid bit rate or 'free format'
|
||||
};
|
||||
sampling_frequency = get_bits(2);
|
||||
if (sampling_frequency == 3){
|
||||
throw Error("Invalid sampling frequency")
|
||||
};
|
||||
padding_bit = get_bits(1);
|
||||
get_bits(1); // discard private_bit
|
||||
mode = get_bits(2);
|
||||
|
||||
// parse the mode_extension, set up the stereo bound
|
||||
if (mode == JOINT_STEREO) {
|
||||
bound = (get_bits(2) + 1) shl 2;
|
||||
} else {
|
||||
get_bits(2);
|
||||
bound = if (mode == MONO) 0 else 32;
|
||||
}
|
||||
|
||||
// discard the last 4 bits of the header and the CRC value, if present
|
||||
get_bits(4);
|
||||
if ((syspeek(mp2_frame!! +1) and 1) == 0)
|
||||
get_bits(16);
|
||||
|
||||
// compute the frame size
|
||||
frame_size = Math.floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit;
|
||||
if (!pcm){
|
||||
return intArrayOf(frame_size, pushSizeL); // no decoding
|
||||
};
|
||||
|
||||
// prepare the quantizer table lookups
|
||||
table_idx = if (mode == MONO) 0 else 1;
|
||||
table_idx = mp2_quant_lut_step1[table_idx][bit_rate_index_minus1];
|
||||
table_idx = mp2_quant_lut_step2[table_idx][sampling_frequency];
|
||||
sblimit = table_idx and 63;
|
||||
table_idx = table_idx shr 6;
|
||||
if (bound > sblimit){
|
||||
bound = sblimit;
|
||||
};
|
||||
|
||||
// read the allocation information
|
||||
for (sb in 0 until bound){
|
||||
for (ch in 0 until 2){
|
||||
mp2_allocation[ch][sb] = read_allocation(sb, table_idx)
|
||||
};
|
||||
};
|
||||
|
||||
for (sb in bound until sblimit){
|
||||
val tmp = read_allocation(sb, table_idx)
|
||||
mp2_allocation[0][sb] = tmp
|
||||
mp2_allocation[1][sb] = tmp
|
||||
};
|
||||
|
||||
|
||||
// read scale factor selector information
|
||||
nch = if (mode == MONO) 1 else 2;
|
||||
for (sb in 0 until sblimit) {
|
||||
for (ch in 0 until nch){
|
||||
if (mp2_allocation[ch][sb] != null){
|
||||
mp2_scfsi[ch][sb] = get_bits(2);
|
||||
};
|
||||
}
|
||||
if (mode == MONO){
|
||||
mp2_scfsi[1][sb] = mp2_scfsi[0][sb];
|
||||
};
|
||||
};
|
||||
// read scale factors
|
||||
for (sb in 0 until sblimit) {
|
||||
for (ch in 0 until nch) {
|
||||
if (mp2_allocation[ch][sb] != null) {
|
||||
when (mp2_scfsi[ch][sb]) {
|
||||
0 -> {
|
||||
mp2_scalefactor[ch][sb][0] = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][1] = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][2] = get_bits(6);
|
||||
}
|
||||
1 -> {
|
||||
val tmp = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][0] = tmp
|
||||
mp2_scalefactor[ch][sb][1] = tmp
|
||||
mp2_scalefactor[ch][sb][2] = get_bits(6);
|
||||
}
|
||||
2 -> {
|
||||
val tmp = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][0] = tmp
|
||||
mp2_scalefactor[ch][sb][1] = tmp
|
||||
mp2_scalefactor[ch][sb][2] = tmp
|
||||
}
|
||||
3 -> {
|
||||
mp2_scalefactor[ch][sb][0] = get_bits(6);
|
||||
val tmp = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][1] = tmp
|
||||
mp2_scalefactor[ch][sb][2] = tmp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode == MONO){
|
||||
for (part in 0 until 3){
|
||||
mp2_scalefactor[1][sb][part] = mp2_scalefactor[0][sb][part];
|
||||
};
|
||||
};
|
||||
}
|
||||
// let ppcm=0;
|
||||
// coefficient input and reconstruction
|
||||
for (part in 0 until 3){
|
||||
for (gr in 0 until 4) {
|
||||
|
||||
// read the samples
|
||||
for (sb in 0 until bound){
|
||||
for (ch in 0 until 2){
|
||||
read_samples(mp2_allocation[ch][sb], mp2_scalefactor[ch][sb][part], mp2_sample[ch][sb]);
|
||||
};
|
||||
};
|
||||
for (sb in bound until sblimit) {
|
||||
read_samples(mp2_allocation[0][sb], mp2_scalefactor[0][sb][part], mp2_sample[0][sb]);
|
||||
|
||||
for (idx in 0 until 3){
|
||||
mp2_sample[1][sb][idx] = mp2_sample[0][sb][idx];
|
||||
};
|
||||
};
|
||||
for (ch in 0 until 2){
|
||||
for (sb in sblimit until 32){
|
||||
for (idx in 0 until 3){
|
||||
mp2_sample[ch][sb][idx] = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// synthesis loop
|
||||
for (idx in 0 until 3) {
|
||||
// shifting step
|
||||
val tmp = (mp2.Voffs - 64) and 1023
|
||||
mp2.Voffs = tmp
|
||||
table_idx = tmp
|
||||
|
||||
for (ch in 0 until 2) {
|
||||
// matrixing
|
||||
for (i in 0 until 64) {
|
||||
sum = 0;
|
||||
for (j in 0 until 32)
|
||||
sum += mp2_N[i][j] * mp2_sample[ch][j][idx]; // 8b*15b=23b
|
||||
// intermediate value is 28 bit (23 + 5), clamp to 14b
|
||||
mp2.V[ch][table_idx + i] = (sum + 8192) shr 14;
|
||||
}
|
||||
|
||||
// construction of U
|
||||
for (i in 0 until 8){
|
||||
for (j in 0 until 32) {
|
||||
mp2_U[(i shl 6) + j] = mp2.V[ch][(table_idx + (i shl 7) + j ) and 1023];
|
||||
mp2_U[(i shl 6) + j + 32] = mp2.V[ch][(table_idx + (i shl 7) + j + 96) and 1023];
|
||||
};
|
||||
};
|
||||
// apply window
|
||||
for (i in 0 until 512){
|
||||
mp2_U[i] = (mp2_U[i] * mp2_D[i] + 32) shr 6;
|
||||
};
|
||||
// output samples
|
||||
for (j in 0 until 32) {
|
||||
sum = 0;
|
||||
for (i in 0 until 16){
|
||||
sum -= mp2_U[(i shl 5) + j];
|
||||
};
|
||||
sum = (sum + 8) shr 4;
|
||||
sum = sum.coerceIn(-32768, 32767)
|
||||
if (ch == 0) { pushL(sum) }
|
||||
if (ch == 1) { pushR(sum) }
|
||||
}
|
||||
} // end of synthesis channel loop
|
||||
} // end of synthesis sub-block loop
|
||||
|
||||
// adjust PCM output pointer: decoded 3 * 32 = 96 stereo samples
|
||||
// ppcm += 192;
|
||||
|
||||
} // decoding of the granule finished
|
||||
}
|
||||
|
||||
if (pushSizeL != pushSizeR && pushSizeR > 0) {
|
||||
throw Error("Push size mismatch -- U${pushSizeL} != R${pushSizeR}")
|
||||
}
|
||||
return intArrayOf(frame_size, pushSizeL);
|
||||
// return intArrayOf(frame_size, 2304);
|
||||
};
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -64,7 +64,17 @@ class DMADelegate(private val vm: VM) {
|
||||
val response = SerialHelper.getStatusCode(vm, portNo)
|
||||
if (response == 0) {
|
||||
val file = SerialHelper.pullMessage(vm, portNo)
|
||||
UnsafeHelper.memcpyRaw(file, UnsafeHelper.getArrayOffset(file) + srcOff.toLong(), null, vm.usermem.ptr + destOff, length.toLong())
|
||||
|
||||
// to user mem
|
||||
if (destOff >= 0)
|
||||
UnsafeHelper.memcpyRaw(file, UnsafeHelper.getArrayOffset(file) + srcOff.toLong(), null, vm.usermem.ptr + destOff, length.toLong())
|
||||
// to hardware
|
||||
else {
|
||||
val destL = destOff.toLong()
|
||||
for (i in 0 until length) {
|
||||
vm.poke(destL - i, file[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +83,16 @@ class DMADelegate(private val vm: VM) {
|
||||
val response = SerialHelper.getStatusCode(vm, portNo)
|
||||
if (response == 0) {
|
||||
val msg = ByteArray(length)
|
||||
UnsafeHelper.memcpyRaw(null, vm.usermem.ptr + srcOff, msg, UnsafeHelper.getArrayOffset(msg), length.toLong())
|
||||
// from user mem
|
||||
if (srcOff >= 0)
|
||||
UnsafeHelper.memcpyRaw(null, vm.usermem.ptr + srcOff, msg, UnsafeHelper.getArrayOffset(msg), length.toLong())
|
||||
// from hardware
|
||||
else {
|
||||
val srcL = srcOff.toLong()
|
||||
for (i in 0 until length) {
|
||||
msg[i] = vm.peek(srcL - i)!!
|
||||
}
|
||||
}
|
||||
SerialHelper.sendMessage(vm, portNo, msg)
|
||||
SerialHelper.sendMessage(vm, portNo, FLUSH)
|
||||
SerialHelper.sendMessage(vm, portNo, CLOSE)
|
||||
|
||||
@@ -119,7 +119,7 @@ object SerialHelper {
|
||||
private fun setBlockTransferStatus(vm: VM, portNo: Int, blockSize: Int, moreToSend: Boolean = false) {
|
||||
vm.getIO().mmio_write(4084L + (portNo * 2), (blockSize and 255).toByte())
|
||||
vm.getIO().mmio_write(4085L + (portNo * 2),
|
||||
((blockSize ushr 8).and(15) or (moreToSend.toInt() shl 7)).toByte()
|
||||
((blockSize ushr 8).and(15) or (moreToSend.toInt(7))).toByte()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -131,9 +131,6 @@ object SerialHelper {
|
||||
return (if (rawcnt == 0) BLOCK_SIZE else rawcnt) to gotMore
|
||||
}
|
||||
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
|
||||
data class DeviceStatus(val code: Int, val message: String)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,4 +3,7 @@ package net.torvald.tsvm
|
||||
/**
|
||||
* Created by minjaesong on 2023-01-04.
|
||||
*/
|
||||
fun getHashStr(length: Int = 5) = (0 until length).map { "YBNDRFG8EJKMCPQXOTLVWIS2A345H769"[Math.random().times(32).toInt()] }.joinToString("")
|
||||
fun getHashStr(length: Int = 5) = (0 until length).map { "YBNDRFG8EJKMCPQXOTLVWIS2A345H769"[Math.random().times(32).toInt()] }.joinToString("")
|
||||
|
||||
fun Boolean.toInt(shift: Int = 0) = if (this) 1 shl shift else 0
|
||||
fun Byte.isNonZero() = this != 0.toByte()
|
||||
|
||||
@@ -119,6 +119,13 @@ class VM(
|
||||
return null
|
||||
}
|
||||
|
||||
fun findPeriSlotNum(peri: PeriBase): Int? {
|
||||
for (i in 0 until peripheralSlots) {
|
||||
if (peripheralTable[i].peripheral == peri) return i
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun update(delta: Float) {
|
||||
getIO().update(delta)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ class VMJSR223Delegate(private val vm: VM) {
|
||||
fun malloc(size: Int) = vm.malloc(size)
|
||||
fun free(ptr: Int) = vm.free(ptr)
|
||||
fun memcpy(from: Int, to: Int, len: Int) {
|
||||
val fromVector = if (from >= 0) 1 else -1
|
||||
val toVector = if (to >= 0) 1 else -1
|
||||
val len = len.toLong()
|
||||
// some special cases for native memcpy
|
||||
val ioSpace = vm.peripheralTable[0].peripheral!! as IOSpace
|
||||
@@ -35,7 +37,8 @@ class VMJSR223Delegate(private val vm: VM) {
|
||||
UnsafeHelper.memcpy(vm.usermem.ptr + from, ioSpace.blockTransferTx[0].ptr + (-4097 - to), len)
|
||||
else
|
||||
for (i in 0 until len) {
|
||||
vm.poke(to + i, vm.peek(from + i)!!)
|
||||
// println("vm.memcpy($from, $to, $len) = mem[${to + i*toVector}] <- mem[${from + i*fromVector}]")
|
||||
vm.poke(to + i*toVector, vm.peek(from + i*fromVector)!!)
|
||||
}
|
||||
}
|
||||
fun mapRom(slot: Int) {
|
||||
|
||||
@@ -10,8 +10,7 @@ import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||
import net.torvald.tsvm.ThreeFiveMiniUfloat
|
||||
import net.torvald.tsvm.VM
|
||||
import net.torvald.tsvm.getHashStr
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
import net.torvald.tsvm.toInt
|
||||
|
||||
private class RenderRunnable(val playhead: AudioAdapter.Playhead) : Runnable {
|
||||
private fun printdbg(msg: Any) {
|
||||
@@ -98,7 +97,7 @@ private class WriteQueueingRunnable(val playhead: AudioAdapter.Playhead, val pcm
|
||||
/**
|
||||
* Created by minjaesong on 2022-12-30.
|
||||
*/
|
||||
class AudioAdapter(val vm: VM) : PeriBase {
|
||||
class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
|
||||
private fun printdbg(msg: Any) {
|
||||
if (DBGPRN) println("[AudioAdapter] $msg")
|
||||
@@ -116,6 +115,11 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
internal val cueSheet = Array(2048) { PlayCue() }
|
||||
internal val pcmBin = UnsafeHelper.allocate(65536L, this)
|
||||
|
||||
internal val mediaFrameBin = UnsafeHelper.allocate(1728, this)
|
||||
internal val mediaDecodedBin = UnsafeHelper.allocate(2304, this)
|
||||
|
||||
@Volatile private var mp2Busy = false
|
||||
|
||||
private val renderRunnables: Array<RenderRunnable>
|
||||
private val renderThreads: Array<Thread>
|
||||
private val writeQueueingRunnables: Array<WriteQueueingRunnable>
|
||||
@@ -125,7 +129,7 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
throwable.printStackTrace()
|
||||
}
|
||||
|
||||
val hash = getHashStr()
|
||||
internal val mp2Env = MP2Env(vm)
|
||||
|
||||
override fun toString() = "AudioAdapter!$hash"
|
||||
|
||||
@@ -225,6 +229,10 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
in 10..19 -> playheads[1].read(adi - 10)
|
||||
in 20..29 -> playheads[2].read(adi - 20)
|
||||
in 30..39 -> playheads[3].read(adi - 30)
|
||||
40 -> -1
|
||||
41 -> mp2Busy.toInt().toByte()
|
||||
in 64..2367 -> mediaDecodedBin[addr - 64]
|
||||
in 2368..4095 -> mediaFrameBin[addr - 2368]
|
||||
in 32768..65535 -> (adi - 32768).let {
|
||||
cueSheet[it / 16].read(it % 15)
|
||||
}
|
||||
@@ -241,6 +249,12 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
in 10..19 -> { playheads[1].write(adi - 10, bi) }
|
||||
in 20..29 -> { playheads[2].write(adi - 20, bi) }
|
||||
in 30..39 -> { playheads[3].write(adi - 30, bi) }
|
||||
40 -> {
|
||||
if (bi and 16 != 0) { mp2Context = mp2Env.initialise() }
|
||||
if (bi and 1 != 0) decodeMp2()
|
||||
}
|
||||
in 64..2367 -> { mediaDecodedBin[addr - 64] = byte }
|
||||
in 2368..4095 -> { mediaFrameBin[addr - 2368] = byte }
|
||||
in 32768..65535 -> { (adi - 32768).let {
|
||||
cueSheet[it / 16].write(it % 15, bi)
|
||||
} }
|
||||
@@ -254,13 +268,20 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
playheads.forEach { it.dispose() }
|
||||
sampleBin.destroy()
|
||||
pcmBin.destroy()
|
||||
mediaFrameBin.destroy()
|
||||
mediaDecodedBin.destroy()
|
||||
}
|
||||
|
||||
override fun getVM(): VM {
|
||||
return vm
|
||||
}
|
||||
|
||||
override val typestring = VM.PERITYPE_SOUND
|
||||
private var mp2Context = mp2Env.initialise()
|
||||
|
||||
private fun decodeMp2() {
|
||||
val periMmioBase = vm.findPeriSlotNum(this)!! * -131072 - 1L
|
||||
mp2Env.decodeFrameU8(mp2Context, periMmioBase - 2368, true, periMmioBase - 64)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -346,7 +367,7 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
3 -> pcmUploadLength.ushr(8).toByte()
|
||||
4 -> masterVolume.toByte()
|
||||
5 -> masterPan.toByte()
|
||||
6 -> (isPcmMode.toInt().shl(7) or isPlaying.toInt().shl(4) or pcmQueueSizeIndex.and(15)).toByte()
|
||||
6 -> (isPcmMode.toInt(7) or isPlaying.toInt(4) or pcmQueueSizeIndex.and(15)).toByte()
|
||||
7 -> 0
|
||||
8 -> (bpm - 24).toByte()
|
||||
9 -> tickRate.toByte()
|
||||
|
||||
@@ -13,14 +13,12 @@ import net.torvald.tsvm.VM
|
||||
*
|
||||
* Created by minjaesong on 2021-12-01.
|
||||
*/
|
||||
class ExtDisp(val vm: VM, val width: Int, val height: Int) : PeriBase {
|
||||
class ExtDisp(val vm: VM, val width: Int, val height: Int) : PeriBase("oled") {
|
||||
|
||||
constructor(vm: VM, w: java.lang.Integer, h: java.lang.Integer) : this(
|
||||
vm, w.toInt(), h.toInt()
|
||||
)
|
||||
|
||||
override val typestring = "oled"
|
||||
|
||||
override fun getVM(): VM {
|
||||
return vm
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.*
|
||||
* A paper tty must be able to implemented by extending this class (and butchering some of the features), of which it
|
||||
* sets limits on some of the functions (notably 'setCursorPos')
|
||||
*/
|
||||
abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
|
||||
abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int): PeriBase("tty") {
|
||||
|
||||
/**
|
||||
* (x, y)
|
||||
|
||||
@@ -12,9 +12,9 @@ import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.UnsafeHelper
|
||||
import net.torvald.UnsafePtr
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||
import net.torvald.tsvm.*
|
||||
import net.torvald.tsvm.FBM
|
||||
import net.torvald.tsvm.LoadShader
|
||||
import net.torvald.tsvm.VM
|
||||
import net.torvald.tsvm.kB
|
||||
import net.torvald.tsvm.peripheral.GraphicsAdapter.Companion.DRAW_SHADER_FRAG
|
||||
import java.io.InputStream
|
||||
@@ -51,7 +51,7 @@ class ReferenceLikeLCD(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot,
|
||||
* NOTE: if TTY size is greater than 80*32, SEGFAULT will occur because text buffer is fixed in size
|
||||
*/
|
||||
open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val config: AdapterConfig, val sgr: SuperGraphicsAddonConfig = SuperGraphicsAddonConfig()) :
|
||||
GlassTty(config.textRows, config.textCols), PeriBase {
|
||||
GlassTty(config.textRows, config.textCols) {
|
||||
|
||||
override val typestring = VM.PERITYPE_GPU_AND_TERM
|
||||
|
||||
@@ -255,7 +255,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
}
|
||||
|
||||
private fun getTextmodeAttirbutes(): Byte = (currentChrRom.and(15).shl(4) or
|
||||
ttyRawMode.toInt().shl(1) or
|
||||
ttyRawMode.toInt(1) or
|
||||
blinkCursor.toInt()).toByte()
|
||||
|
||||
private fun getGraphicsAttributes(): Byte = graphicsUseSprites.toInt().toByte()
|
||||
@@ -291,7 +291,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
16L -> framebufferScrollY.toByte()
|
||||
17L -> framebufferScrollY.ushr(8).toByte()
|
||||
|
||||
18L -> (drawCallBusy.toInt() or codecBusy.toInt().shl(1)).toByte()
|
||||
18L -> (drawCallBusy.toInt() or codecBusy.toInt(1)).toByte()
|
||||
19L -> -1
|
||||
20L -> drawCallProgramCounter.and(255).toByte()
|
||||
21L -> drawCallProgramCounter.ushr(8).and(255).toByte()
|
||||
@@ -363,11 +363,11 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
}
|
||||
}
|
||||
|
||||
private var codecBusy = false
|
||||
@Volatile private var codecBusy = false
|
||||
|
||||
private var drawCallSize = 0
|
||||
private val drawCallBuffer = Array<DrawCall>(3640) { DrawCallEnd }
|
||||
private var drawCallBusy = false
|
||||
@Volatile private var drawCallBusy = false
|
||||
internal var drawCallProgramCounter = 0
|
||||
internal var rScanline = 0
|
||||
|
||||
@@ -1362,8 +1362,6 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
return vm.peek(-38)!!.toInt().and(255)
|
||||
}
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
|
||||
companion object {
|
||||
val VRAM_SIZE = 256.kB()
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ import net.torvald.DanglingPointerException
|
||||
import net.torvald.UnsafeHelper
|
||||
import net.torvald.tsvm.CircularArray
|
||||
import net.torvald.tsvm.VM
|
||||
import net.torvald.tsvm.isNonZero
|
||||
import net.torvald.tsvm.toInt
|
||||
import kotlin.experimental.and
|
||||
|
||||
class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
|
||||
override val typestring = "io"
|
||||
class IOSpace(val vm: VM) : PeriBase("io"), InputProcessor {
|
||||
|
||||
override fun getVM(): VM {
|
||||
return vm
|
||||
@@ -60,11 +60,11 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
}
|
||||
|
||||
private fun composeBlockTransferStatus(portno: Int): Int {
|
||||
return blockTransferPorts[portno].isMaster.toInt().shl(5) or
|
||||
blockTransferPorts[portno].isSlave.toInt().shl(4) or
|
||||
blockTransferPorts[portno].getMode().toInt().shl(3) or
|
||||
blockTransferPorts[portno].busy.get().toInt().shl(2) or
|
||||
blockTransferPorts[portno].areYouReady().toInt().shl(1) or
|
||||
return blockTransferPorts[portno].isMaster.toInt(5) or
|
||||
blockTransferPorts[portno].isSlave.toInt(4) or
|
||||
blockTransferPorts[portno].getMode().toInt(3) or
|
||||
blockTransferPorts[portno].busy.get().toInt(2) or
|
||||
blockTransferPorts[portno].areYouReady().toInt(1) or
|
||||
blockTransferPorts[portno].cableConnected().toInt()
|
||||
}
|
||||
|
||||
@@ -103,17 +103,17 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
38L -> keyboardInputRequested.toInt().toByte()
|
||||
39L -> rawInputFunctionLatched.toInt().toByte()
|
||||
in 40..47 -> keyEventBuffers[adi - 40]
|
||||
48L -> ((vm.resetDown.toInt() shl 7) or (vm.stopDown.toInt())).toByte()
|
||||
48L -> ((vm.resetDown.toInt(7)) or (vm.stopDown.toInt())).toByte()
|
||||
|
||||
in 64..67 -> vm.memsize.shr((adi - 64) * 8).toByte()
|
||||
68L -> (uptimeCounterLatched.toInt() or RTClatched.toInt().shl(1)).toByte()
|
||||
68L -> (uptimeCounterLatched.toInt() or RTClatched.toInt(1)).toByte()
|
||||
|
||||
in 72..79 -> systemUptime.ushr((adi - 72) * 8).and(255).toByte()
|
||||
in 80..87 -> rtc.ushr((adi - 80) * 8).and(255).toByte()
|
||||
|
||||
88L -> vm.romMapping.toByte()
|
||||
|
||||
89L -> ((acpiShutoff.toInt() shl 7) or (bmsIsBatteryOperated.toInt() shl 3) or (bmsHasBattery.toInt() shl 1)
|
||||
89L -> ((acpiShutoff.toInt(7)) or (bmsIsBatteryOperated.toInt(3)) or (bmsHasBattery.toInt(1))
|
||||
or bmsIsCharging.toInt()).toByte()
|
||||
|
||||
in 1024..2047 -> peripheralFast[addr - 1024]
|
||||
@@ -129,13 +129,13 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
4083L -> blockTransferPorts[3].getYourStatusCode().toByte()
|
||||
|
||||
4084L -> (blockTransferPorts[0].yourBlockSize().toByte())
|
||||
4085L -> (blockTransferPorts[0].doYouHaveNext().toInt().shl(7) or blockTransferPorts[0].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4085L -> (blockTransferPorts[0].doYouHaveNext().toInt(7) or blockTransferPorts[0].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4086L -> (blockTransferPorts[1].yourBlockSize().toByte())
|
||||
4087L -> (blockTransferPorts[1].doYouHaveNext().toInt().shl(7) or blockTransferPorts[1].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4087L -> (blockTransferPorts[1].doYouHaveNext().toInt(7) or blockTransferPorts[1].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4088L -> (blockTransferPorts[2].yourBlockSize().toByte())
|
||||
4089L -> (blockTransferPorts[2].doYouHaveNext().toInt().shl(7) or blockTransferPorts[2].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4089L -> (blockTransferPorts[2].doYouHaveNext().toInt(7) or blockTransferPorts[2].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4090L -> (blockTransferPorts[3].yourBlockSize().toByte())
|
||||
4091L -> (blockTransferPorts[3].doYouHaveNext().toInt().shl(7) or blockTransferPorts[3].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4091L -> (blockTransferPorts[3].doYouHaveNext().toInt(7) or blockTransferPorts[3].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
|
||||
in 4092..4095 -> composeBlockTransferStatus(adi - 4092).toByte()
|
||||
|
||||
@@ -389,6 +389,4 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
private fun Byte.isNonZero() = this != 0.toByte()
|
||||
}
|
||||
|
||||
588
tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt
Normal file
588
tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt
Normal file
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
mp2dec.js JavaScript MPEG-1 Audio Layer II decoder
|
||||
Copyright (C) 2011 Liam Wilson
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
/* Note this is a port of kjmp2 by Martin J. Fiedler: */
|
||||
|
||||
/******************************************************************************
|
||||
** kjmp2 -- a minimal MPEG-1 Audio Layer II decoder library **
|
||||
*******************************************************************************
|
||||
** Copyright (C) 2006 Martin J. Fiedler martin.fiedler@gmx.net **
|
||||
** **
|
||||
** This software is provided 'as-is', without any express or implied **
|
||||
** warranty. In no event will the authors be held liable for any damages **
|
||||
** arising from the use of this software. **
|
||||
** **
|
||||
** Permission is granted to anyone to use this software for any purpose, **
|
||||
** including commercial applications, and to alter it and redistribute it **
|
||||
** freely, subject to the following restrictions: **
|
||||
** 1. The origin of this software must not be misrepresented; you must not **
|
||||
** claim that you wrote the original software. If you use this software **
|
||||
** in a product, an acknowledgment in the product documentation would **
|
||||
** be appreciated but is not required. **
|
||||
** 2. Altered source versions must be plainly marked as such, and must not **
|
||||
** be misrepresented as being the original software. **
|
||||
** 3. This notice may not be removed or altered from any source **
|
||||
** distribution. **
|
||||
******************************************************************************/
|
||||
|
||||
package net.torvald.tsvm.peripheral
|
||||
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||
import net.torvald.tsvm.VM
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
|
||||
class MP2Env(val vm: VM) {
|
||||
private var mp2_frame: Long? = null; // ptr
|
||||
private var mp2_frameIncr = 1
|
||||
private var STEREO=0;
|
||||
// #define JOINT_STEREO 1
|
||||
private var JOINT_STEREO=1;
|
||||
// #define DUAL_CHANNEL 2
|
||||
private var DUAL_CHANNEL=2;
|
||||
// #define MONO 3
|
||||
private var MONO=3;
|
||||
private val mp2_sample_rates = arrayOf(44100, 48000, 32000, 0);
|
||||
private val mp2_bitrates = arrayOf(32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384);
|
||||
private val mp2_scf_value = arrayOf(
|
||||
0x02000000, 0x01965FEA, 0x01428A30, 0x01000000,
|
||||
0x00CB2FF5, 0x00A14518, 0x00800000, 0x006597FB,
|
||||
0x0050A28C, 0x00400000, 0x0032CBFD, 0x00285146,
|
||||
0x00200000, 0x001965FF, 0x001428A3, 0x00100000,
|
||||
0x000CB2FF, 0x000A1451, 0x00080000, 0x00065980,
|
||||
0x00050A29, 0x00040000, 0x00032CC0, 0x00028514,
|
||||
0x00020000, 0x00019660, 0x0001428A, 0x00010000,
|
||||
0x0000CB30, 0x0000A145, 0x00008000, 0x00006598,
|
||||
0x000050A3, 0x00004000, 0x000032CC, 0x00002851,
|
||||
0x00002000, 0x00001966, 0x00001429, 0x00001000,
|
||||
0x00000CB3, 0x00000A14, 0x00000800, 0x00000659,
|
||||
0x0000050A, 0x00000400, 0x0000032D, 0x00000285,
|
||||
0x00000200, 0x00000196, 0x00000143, 0x00000100,
|
||||
0x000000CB, 0x000000A1, 0x00000080, 0x00000066,
|
||||
0x00000051, 0x00000040, 0x00000033, 0x00000028,
|
||||
0x00000020, 0x00000019, 0x00000014, 0);
|
||||
private val mp2_N = Array(64) { i -> IntArray(32) { j ->
|
||||
Math.floor(256.0 * Math.cos((16 + i) * ((j shl 1) + 1) * 0.0490873852123405)).toInt()
|
||||
} }
|
||||
private val mp2_U = IntArray(512)
|
||||
private val mp2_D = arrayOf(
|
||||
0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,-0x00001,
|
||||
-0x00001,-0x00001,-0x00001,-0x00002,-0x00002,-0x00003,-0x00003,-0x00004,
|
||||
-0x00004,-0x00005,-0x00006,-0x00006,-0x00007,-0x00008,-0x00009,-0x0000A,
|
||||
-0x0000C,-0x0000D,-0x0000F,-0x00010,-0x00012,-0x00014,-0x00017,-0x00019,
|
||||
-0x0001C,-0x0001E,-0x00022,-0x00025,-0x00028,-0x0002C,-0x00030,-0x00034,
|
||||
-0x00039,-0x0003E,-0x00043,-0x00048,-0x0004E,-0x00054,-0x0005A,-0x00060,
|
||||
-0x00067,-0x0006E,-0x00074,-0x0007C,-0x00083,-0x0008A,-0x00092,-0x00099,
|
||||
-0x000A0,-0x000A8,-0x000AF,-0x000B6,-0x000BD,-0x000C3,-0x000C9,-0x000CF,
|
||||
0x000D5, 0x000DA, 0x000DE, 0x000E1, 0x000E3, 0x000E4, 0x000E4, 0x000E3,
|
||||
0x000E0, 0x000DD, 0x000D7, 0x000D0, 0x000C8, 0x000BD, 0x000B1, 0x000A3,
|
||||
0x00092, 0x0007F, 0x0006A, 0x00053, 0x00039, 0x0001D,-0x00001,-0x00023,
|
||||
-0x00047,-0x0006E,-0x00098,-0x000C4,-0x000F3,-0x00125,-0x0015A,-0x00190,
|
||||
-0x001CA,-0x00206,-0x00244,-0x00284,-0x002C6,-0x0030A,-0x0034F,-0x00396,
|
||||
-0x003DE,-0x00427,-0x00470,-0x004B9,-0x00502,-0x0054B,-0x00593,-0x005D9,
|
||||
-0x0061E,-0x00661,-0x006A1,-0x006DE,-0x00718,-0x0074D,-0x0077E,-0x007A9,
|
||||
-0x007D0,-0x007EF,-0x00808,-0x0081A,-0x00824,-0x00826,-0x0081F,-0x0080E,
|
||||
0x007F5, 0x007D0, 0x007A0, 0x00765, 0x0071E, 0x006CB, 0x0066C, 0x005FF,
|
||||
0x00586, 0x00500, 0x0046B, 0x003CA, 0x0031A, 0x0025D, 0x00192, 0x000B9,
|
||||
-0x0002C,-0x0011F,-0x00220,-0x0032D,-0x00446,-0x0056B,-0x0069B,-0x007D5,
|
||||
-0x00919,-0x00A66,-0x00BBB,-0x00D16,-0x00E78,-0x00FDE,-0x01148,-0x012B3,
|
||||
-0x01420,-0x0158C,-0x016F6,-0x0185C,-0x019BC,-0x01B16,-0x01C66,-0x01DAC,
|
||||
-0x01EE5,-0x02010,-0x0212A,-0x02232,-0x02325,-0x02402,-0x024C7,-0x02570,
|
||||
-0x025FE,-0x0266D,-0x026BB,-0x026E6,-0x026ED,-0x026CE,-0x02686,-0x02615,
|
||||
-0x02577,-0x024AC,-0x023B2,-0x02287,-0x0212B,-0x01F9B,-0x01DD7,-0x01BDD,
|
||||
0x019AE, 0x01747, 0x014A8, 0x011D1, 0x00EC0, 0x00B77, 0x007F5, 0x0043A,
|
||||
0x00046,-0x003E5,-0x00849,-0x00CE3,-0x011B4,-0x016B9,-0x01BF1,-0x0215B,
|
||||
-0x026F6,-0x02CBE,-0x032B3,-0x038D3,-0x03F1A,-0x04586,-0x04C15,-0x052C4,
|
||||
-0x05990,-0x06075,-0x06771,-0x06E80,-0x0759F,-0x07CCA,-0x083FE,-0x08B37,
|
||||
-0x09270,-0x099A7,-0x0A0D7,-0x0A7FD,-0x0AF14,-0x0B618,-0x0BD05,-0x0C3D8,
|
||||
-0x0CA8C,-0x0D11D,-0x0D789,-0x0DDC9,-0x0E3DC,-0x0E9BD,-0x0EF68,-0x0F4DB,
|
||||
-0x0FA12,-0x0FF09,-0x103BD,-0x1082C,-0x10C53,-0x1102E,-0x113BD,-0x116FB,
|
||||
-0x119E8,-0x11C82,-0x11EC6,-0x120B3,-0x12248,-0x12385,-0x12467,-0x124EF,
|
||||
0x1251E, 0x124F0, 0x12468, 0x12386, 0x12249, 0x120B4, 0x11EC7, 0x11C83,
|
||||
0x119E9, 0x116FC, 0x113BE, 0x1102F, 0x10C54, 0x1082D, 0x103BE, 0x0FF0A,
|
||||
0x0FA13, 0x0F4DC, 0x0EF69, 0x0E9BE, 0x0E3DD, 0x0DDCA, 0x0D78A, 0x0D11E,
|
||||
0x0CA8D, 0x0C3D9, 0x0BD06, 0x0B619, 0x0AF15, 0x0A7FE, 0x0A0D8, 0x099A8,
|
||||
0x09271, 0x08B38, 0x083FF, 0x07CCB, 0x075A0, 0x06E81, 0x06772, 0x06076,
|
||||
0x05991, 0x052C5, 0x04C16, 0x04587, 0x03F1B, 0x038D4, 0x032B4, 0x02CBF,
|
||||
0x026F7, 0x0215C, 0x01BF2, 0x016BA, 0x011B5, 0x00CE4, 0x0084A, 0x003E6,
|
||||
-0x00045,-0x00439,-0x007F4,-0x00B76,-0x00EBF,-0x011D0,-0x014A7,-0x01746,
|
||||
0x019AE, 0x01BDE, 0x01DD8, 0x01F9C, 0x0212C, 0x02288, 0x023B3, 0x024AD,
|
||||
0x02578, 0x02616, 0x02687, 0x026CF, 0x026EE, 0x026E7, 0x026BC, 0x0266E,
|
||||
0x025FF, 0x02571, 0x024C8, 0x02403, 0x02326, 0x02233, 0x0212B, 0x02011,
|
||||
0x01EE6, 0x01DAD, 0x01C67, 0x01B17, 0x019BD, 0x0185D, 0x016F7, 0x0158D,
|
||||
0x01421, 0x012B4, 0x01149, 0x00FDF, 0x00E79, 0x00D17, 0x00BBC, 0x00A67,
|
||||
0x0091A, 0x007D6, 0x0069C, 0x0056C, 0x00447, 0x0032E, 0x00221, 0x00120,
|
||||
0x0002D,-0x000B8,-0x00191,-0x0025C,-0x00319,-0x003C9,-0x0046A,-0x004FF,
|
||||
-0x00585,-0x005FE,-0x0066B,-0x006CA,-0x0071D,-0x00764,-0x0079F,-0x007CF,
|
||||
0x007F5, 0x0080F, 0x00820, 0x00827, 0x00825, 0x0081B, 0x00809, 0x007F0,
|
||||
0x007D1, 0x007AA, 0x0077F, 0x0074E, 0x00719, 0x006DF, 0x006A2, 0x00662,
|
||||
0x0061F, 0x005DA, 0x00594, 0x0054C, 0x00503, 0x004BA, 0x00471, 0x00428,
|
||||
0x003DF, 0x00397, 0x00350, 0x0030B, 0x002C7, 0x00285, 0x00245, 0x00207,
|
||||
0x001CB, 0x00191, 0x0015B, 0x00126, 0x000F4, 0x000C5, 0x00099, 0x0006F,
|
||||
0x00048, 0x00024, 0x00002,-0x0001C,-0x00038,-0x00052,-0x00069,-0x0007E,
|
||||
-0x00091,-0x000A2,-0x000B0,-0x000BC,-0x000C7,-0x000CF,-0x000D6,-0x000DC,
|
||||
-0x000DF,-0x000E2,-0x000E3,-0x000E3,-0x000E2,-0x000E0,-0x000DD,-0x000D9,
|
||||
0x000D5, 0x000D0, 0x000CA, 0x000C4, 0x000BE, 0x000B7, 0x000B0, 0x000A9,
|
||||
0x000A1, 0x0009A, 0x00093, 0x0008B, 0x00084, 0x0007D, 0x00075, 0x0006F,
|
||||
0x00068, 0x00061, 0x0005B, 0x00055, 0x0004F, 0x00049, 0x00044, 0x0003F,
|
||||
0x0003A, 0x00035, 0x00031, 0x0002D, 0x00029, 0x00026, 0x00023, 0x0001F,
|
||||
0x0001D, 0x0001A, 0x00018, 0x00015, 0x00013, 0x00011, 0x00010, 0x0000E,
|
||||
0x0000D, 0x0000B, 0x0000A, 0x00009, 0x00008, 0x00007, 0x00007, 0x00006,
|
||||
0x00005, 0x00005, 0x00004, 0x00004, 0x00003, 0x00003, 0x00002, 0x00002,
|
||||
0x00002, 0x00002, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001);
|
||||
private val mp2_quant_lut_step1= arrayOf(
|
||||
arrayOf(0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 ),
|
||||
arrayOf(0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 ));
|
||||
private val mp2_QUANT_TAB_A = 27 or 64 // Table 3-B.2a: high-rate, sblimit = 27
|
||||
private val mp2_QUANT_TAB_B = 30 or 64 // Table 3-B.2b: high-rate, sblimit = 30
|
||||
private val mp2_QUANT_TAB_C = 8 // Table 3-B.2c: low-rate, sblimit = 8
|
||||
private val mp2_QUANT_TAB_D = 12 // Table 3-B.2d: low-rate, sblimit = 12
|
||||
private val mp2_quant_lut_step2 = arrayOf(
|
||||
arrayOf(mp2_QUANT_TAB_C, mp2_QUANT_TAB_C, mp2_QUANT_TAB_D),
|
||||
arrayOf(mp2_QUANT_TAB_A, mp2_QUANT_TAB_A, mp2_QUANT_TAB_A),
|
||||
arrayOf(mp2_QUANT_TAB_B, mp2_QUANT_TAB_A, mp2_QUANT_TAB_B));
|
||||
private val mp2_quant_lut_step3 = arrayOf(
|
||||
arrayOf(0x44,0x44,
|
||||
0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34
|
||||
),
|
||||
arrayOf(0x43,0x43,0x43,
|
||||
0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
|
||||
0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20));
|
||||
private val mp2_quant_lut_step4 = arrayOf(
|
||||
arrayOf(0, 1, 2, 17),
|
||||
arrayOf(0, 1, 2, 3, 4, 5, 6, 17),
|
||||
arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17),
|
||||
arrayOf(0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
||||
arrayOf(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17));
|
||||
private data class mp2_quantizer_spec(var nlevels: Int, var grouping: Boolean, var cw_bits: Int, var Smul: Int, var Sdiv: Int)
|
||||
private val mp2_quantizer_table =arrayOf(
|
||||
mp2_quantizer_spec ( 3, true, 5, 0x7FFF, 0xFFFF ),
|
||||
mp2_quantizer_spec ( 5, true, 7, 0x3FFF, 0x0002 ),
|
||||
mp2_quantizer_spec ( 7, false, 3, 0x2AAA, 0x0003 ),
|
||||
mp2_quantizer_spec ( 9, true, 10, 0x1FFF, 0x0002 ),
|
||||
mp2_quantizer_spec ( 15, false, 4, 0x1249, 0xFFFF ),
|
||||
mp2_quantizer_spec ( 31, false, 5, 0x0888, 0x0003 ),
|
||||
mp2_quantizer_spec ( 63, false, 6, 0x0421, 0xFFFF ),
|
||||
mp2_quantizer_spec ( 127, false, 7, 0x0208, 0x0009 ),
|
||||
mp2_quantizer_spec ( 255, false, 8, 0x0102, 0x007F ),
|
||||
mp2_quantizer_spec ( 511, false, 9, 0x0080, 0x0002 ),
|
||||
mp2_quantizer_spec ( 1023, false, 10, 0x0040, 0x0009 ),
|
||||
mp2_quantizer_spec ( 2047, false, 11, 0x0020, 0x0021 ),
|
||||
mp2_quantizer_spec ( 4095, false, 12, 0x0010, 0x0089 ),
|
||||
mp2_quantizer_spec ( 8191, false, 13, 0x0008, 0x0249 ),
|
||||
mp2_quantizer_spec ( 16383, false, 14, 0x0004, 0x0AAB ),
|
||||
mp2_quantizer_spec ( 32767, false, 15, 0x0002, 0x3FFF ),
|
||||
mp2_quantizer_spec ( 65535, false, 16, 0x0001, 0xFFFF ));
|
||||
|
||||
val KJMP2_MAGIC= 0x32706D;
|
||||
private var mp2_initialized = false;
|
||||
private var mp2_bit_window = 0;
|
||||
private var mp2_bits_in_window = 0;
|
||||
private var mp2_frame_pos = 0;
|
||||
|
||||
private fun syspeek(ptr: Long) = vm.peek(ptr)!!.toUint()
|
||||
|
||||
data class MP2(
|
||||
var Voffs: Int = 0,
|
||||
var id: Int = 0,
|
||||
var V: Array<IntArray> = Array(2) { IntArray(1024) }
|
||||
)
|
||||
|
||||
|
||||
private fun show_bits(bit_count: Int) = (mp2_bit_window shr (24 - (bit_count)));
|
||||
private fun get_bits(bit_count: Int): Int {
|
||||
var result = show_bits(bit_count);
|
||||
mp2_bit_window = (mp2_bit_window shl bit_count) and 0xFFFFFF;
|
||||
mp2_bits_in_window -= bit_count;
|
||||
while (mp2_bits_in_window < 16) {
|
||||
mp2_bit_window = mp2_bit_window or (syspeek(mp2_frame!! + mp2_frame_pos * mp2_frameIncr) shl (16 - mp2_bits_in_window));
|
||||
mp2_frame_pos += 1
|
||||
mp2_bits_in_window += 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fun initialise(): MP2 {
|
||||
val mp2 = MP2()
|
||||
|
||||
// check if global initialization is required
|
||||
if (!mp2_initialized) {
|
||||
mp2_initialized = true;
|
||||
}
|
||||
|
||||
// perform local initialization: clean the context and put the magic in it
|
||||
for (i in 0 until 2){
|
||||
for (j in 1023 downTo 0){
|
||||
mp2.V[i][j] = 0;
|
||||
};
|
||||
};
|
||||
mp2.Voffs = 0;
|
||||
mp2.id = KJMP2_MAGIC;
|
||||
|
||||
return mp2
|
||||
};
|
||||
|
||||
private fun read_allocation(sb: Int, b2_table: Int): mp2_quantizer_spec? {
|
||||
var table_idx = mp2_quant_lut_step3[b2_table][sb];
|
||||
table_idx = mp2_quant_lut_step4[table_idx and 15][get_bits(table_idx shr 4)];
|
||||
return if (table_idx != 0) (mp2_quantizer_table[table_idx - 1]) else null
|
||||
}
|
||||
|
||||
private fun read_samples(q: mp2_quantizer_spec?, scalefactor: Int, sample: IntArray) {
|
||||
var adj = 0;
|
||||
var value = 0;
|
||||
if (q == null) {
|
||||
// no bits allocated for this subband
|
||||
sample[0] = 0
|
||||
sample[1] = 0
|
||||
sample[2] = 0;
|
||||
return;
|
||||
}
|
||||
// resolve scalefactor
|
||||
var scalefactor = mp2_scf_value[scalefactor];
|
||||
|
||||
// decode samples
|
||||
adj = q.nlevels;
|
||||
if (q.grouping) {
|
||||
// decode grouped samples
|
||||
value = get_bits(q.cw_bits);
|
||||
sample[0] = value % adj;
|
||||
value = Math.floor(value.toDouble() / adj).toInt();
|
||||
sample[1] = value % adj;
|
||||
sample[2] = Math.floor(value.toDouble() / adj).toInt();
|
||||
} else {
|
||||
// decode direct samples
|
||||
for(idx in 0 until 3)
|
||||
sample[idx] = get_bits(q.cw_bits);
|
||||
}
|
||||
|
||||
// postmultiply samples
|
||||
adj = ((adj + 1) shr 1) - 1;
|
||||
for (idx in 0 until 3) {
|
||||
// step 1: renormalization to [-1..1]
|
||||
value = adj - sample[idx];
|
||||
value = (value * q.Smul) + Math.floor(value.toDouble() / q.Sdiv).toInt();
|
||||
// step 2: apply scalefactor
|
||||
sample[idx] = ( value * (scalefactor shr 12) + ((value * (scalefactor and 4095) + 2048) shr 12)) shr 12; // scale adjust
|
||||
}
|
||||
}
|
||||
|
||||
private var mp2_allocation: Array<Array<mp2_quantizer_spec?>> = Array(2) { Array(32) { null } }
|
||||
private var mp2_scfsi = Array(2) { IntArray(32) }
|
||||
private var mp2_scalefactor = Array(2) { Array(32) { IntArray(3) } }
|
||||
private var mp2_sample = Array(2) { Array(32) { IntArray(3) } }
|
||||
|
||||
|
||||
fun getInitialFrameSize(bytes: IntArray): Int {
|
||||
val b0 = bytes[0]
|
||||
val b1 = bytes[1]
|
||||
val b2 = bytes[2]
|
||||
|
||||
// check sync pattern
|
||||
if ((b0 != 0xFF) || (b1 != 0xFD) || ((b2 - 0x10) >= 0xE0)) {
|
||||
throw Error("Not a MP2 Frame Head: ${listOf(b0, b1, b2).map { it.toString(16).padStart(2,'0') }.joinToString(" ")}")
|
||||
}
|
||||
|
||||
val sampling_frequency = (b2 shr 2) and 3
|
||||
val bit_rate_index_minus1 = ((b2 shr 4) and 15) - 1
|
||||
if (bit_rate_index_minus1 > 13){
|
||||
throw Error("Invalid bit rate") // invalid bit rate or 'free format'
|
||||
}
|
||||
val padding_bit = b2.shr(1) and 1
|
||||
return floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit
|
||||
}
|
||||
|
||||
private fun randomRound(k: Double): Double {
|
||||
val rnd = (Math.random() + Math.random()) / 2.0 // this produces triangular distribution
|
||||
return if (rnd < (k - (k.toInt()))) ceil(k) else floor(k)
|
||||
}
|
||||
|
||||
private fun s16Tou8(i: Int): Byte {
|
||||
// apply dithering
|
||||
val ufval = (i.toDouble() / 65536.0) + 0.5
|
||||
val ival = randomRound(ufval * 255.0)
|
||||
return ival.toInt().toByte()
|
||||
}
|
||||
|
||||
private val samplesL = IntArray(1152) // should contain 1152 samples
|
||||
private val samplesR = IntArray(1152) // should contain 1152 samples
|
||||
|
||||
internal fun decodeFrameU8(mp2: MP2, framePtr: Long?, pcm: Boolean, out: Long): IntArray {
|
||||
val outVector = if (out >= 0) 1 else -1
|
||||
|
||||
var pushSizeL = 0
|
||||
var pushSizeR = 0
|
||||
|
||||
val pushL: (Int) -> Unit = { sampleL: Int ->
|
||||
samplesL[pushSizeL++] = sampleL
|
||||
}
|
||||
val pushR: (Int) -> Unit = { sampleR: Int ->
|
||||
samplesR[pushSizeR++] = sampleR
|
||||
}
|
||||
|
||||
val ret = _decodeFrame(mp2, framePtr, pcm, pushL, pushR)
|
||||
|
||||
// dither samples and store them to the given "out" pointer
|
||||
var outPos = out
|
||||
for (i in 0..1151) {
|
||||
vm.poke(outPos, s16Tou8(samplesL[i]))
|
||||
vm.poke(outPos + outVector, s16Tou8(samplesR[i]))
|
||||
outPos += 2*outVector
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
fun decodeFrame(mp2: MP2, framePtr: Long?, pcm: Boolean, outL: Long, outR: Long): IntArray {
|
||||
var pushSizeL = 0
|
||||
var pushSizeR = 0
|
||||
|
||||
val pushL = { sampleL: Int ->
|
||||
vm.poke(outL + pushSizeL + 0, (sampleL and 255).toByte())
|
||||
vm.poke(outL + pushSizeL + 1, (sampleL shr 8).toByte())
|
||||
pushSizeL += 2
|
||||
}
|
||||
val pushR = { sampleR: Int ->
|
||||
vm.poke(outR + pushSizeR + 0, (sampleR and 255).toByte())
|
||||
vm.poke(outR + pushSizeR + 1, (sampleR shr 8).toByte())
|
||||
pushSizeR += 2
|
||||
}
|
||||
|
||||
return _decodeFrame(mp2, framePtr, pcm, pushL, pushR)
|
||||
}
|
||||
|
||||
private fun _decodeFrame(mp2: MP2, framePtr: Long?, pcm: Boolean, pushL: (Int) -> Unit, pushR: (Int) -> Unit): IntArray {
|
||||
if (framePtr == null) {
|
||||
throw Error("Frame is null")
|
||||
}
|
||||
val incr = if (framePtr >= 0) 1 else -1
|
||||
mp2_frameIncr = incr
|
||||
mp2_frame = framePtr;
|
||||
val bit_rate_index_minus1: Int;
|
||||
val sampling_frequency: Int;
|
||||
val padding_bit: Int;
|
||||
val mode: Int;
|
||||
val frame_size: Int;
|
||||
var bound: Int
|
||||
val sblimit: Int;
|
||||
val nch: Int;
|
||||
var sum: Int;
|
||||
var table_idx: Int;
|
||||
// general sanity check
|
||||
if (!mp2_initialized || (mp2.id != KJMP2_MAGIC)){
|
||||
throw Error("MP2 not initialised")
|
||||
};
|
||||
// check for valid header: syncword OK, MPEG-Audio Layer 2
|
||||
if ((syspeek(mp2_frame!!) != 0xFF) || ((syspeek(mp2_frame!! + 1*incr) and 0xFE) != 0xFC)){
|
||||
throw Error("Invalid MP2 header at $mp2_frame: ${syspeek(mp2_frame!!).toString(16)} ${syspeek(mp2_frame!! + 1*incr).toString(16)}")
|
||||
};
|
||||
|
||||
// set up the bitstream reader
|
||||
mp2_bit_window = syspeek(mp2_frame!! + 2*incr) shl 16;
|
||||
mp2_bits_in_window = 8;
|
||||
mp2_frame_pos = 3;
|
||||
|
||||
// read the rest of the header
|
||||
bit_rate_index_minus1 = get_bits(4) - 1;
|
||||
if (bit_rate_index_minus1 > 13){
|
||||
throw Error("Invalid bit rate") // invalid bit rate or 'free format'
|
||||
};
|
||||
sampling_frequency = get_bits(2);
|
||||
if (sampling_frequency == 3){
|
||||
throw Error("Invalid sampling frequency")
|
||||
};
|
||||
padding_bit = get_bits(1);
|
||||
get_bits(1); // discard private_bit
|
||||
mode = get_bits(2);
|
||||
|
||||
// parse the mode_extension, set up the stereo bound
|
||||
if (mode == JOINT_STEREO) {
|
||||
bound = (get_bits(2) + 1) shl 2;
|
||||
} else {
|
||||
get_bits(2);
|
||||
bound = if (mode == MONO) 0 else 32;
|
||||
}
|
||||
|
||||
// discard the last 4 bits of the header and the CRC value, if present
|
||||
get_bits(4);
|
||||
if ((syspeek(mp2_frame!! + 1*incr) and 1) == 0)
|
||||
get_bits(16);
|
||||
|
||||
// compute the frame size
|
||||
frame_size = Math.floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit;
|
||||
if (!pcm){
|
||||
return intArrayOf(frame_size, 0); // no decoding
|
||||
};
|
||||
|
||||
// prepare the quantizer table lookups
|
||||
table_idx = if (mode == MONO) 0 else 1;
|
||||
table_idx = mp2_quant_lut_step1[table_idx][bit_rate_index_minus1];
|
||||
table_idx = mp2_quant_lut_step2[table_idx][sampling_frequency];
|
||||
sblimit = table_idx and 63;
|
||||
table_idx = table_idx shr 6;
|
||||
if (bound > sblimit){
|
||||
bound = sblimit;
|
||||
};
|
||||
|
||||
// read the allocation information
|
||||
for (sb in 0 until bound){
|
||||
for (ch in 0 until 2){
|
||||
mp2_allocation[ch][sb] = read_allocation(sb, table_idx)
|
||||
};
|
||||
};
|
||||
|
||||
for (sb in bound until sblimit){
|
||||
val tmp = read_allocation(sb, table_idx)
|
||||
mp2_allocation[0][sb] = tmp
|
||||
mp2_allocation[1][sb] = tmp
|
||||
};
|
||||
|
||||
|
||||
// read scale factor selector information
|
||||
nch = if (mode == MONO) 1 else 2;
|
||||
for (sb in 0 until sblimit) {
|
||||
for (ch in 0 until nch){
|
||||
if (mp2_allocation[ch][sb] != null){
|
||||
mp2_scfsi[ch][sb] = get_bits(2);
|
||||
};
|
||||
}
|
||||
if (mode == MONO){
|
||||
mp2_scfsi[1][sb] = mp2_scfsi[0][sb];
|
||||
};
|
||||
};
|
||||
// read scale factors
|
||||
for (sb in 0 until sblimit) {
|
||||
for (ch in 0 until nch) {
|
||||
if (mp2_allocation[ch][sb] != null) {
|
||||
when (mp2_scfsi[ch][sb]) {
|
||||
0 -> {
|
||||
mp2_scalefactor[ch][sb][0] = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][1] = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][2] = get_bits(6);
|
||||
}
|
||||
1 -> {
|
||||
val tmp = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][0] = tmp
|
||||
mp2_scalefactor[ch][sb][1] = tmp
|
||||
mp2_scalefactor[ch][sb][2] = get_bits(6);
|
||||
}
|
||||
2 -> {
|
||||
val tmp = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][0] = tmp
|
||||
mp2_scalefactor[ch][sb][1] = tmp
|
||||
mp2_scalefactor[ch][sb][2] = tmp
|
||||
}
|
||||
3 -> {
|
||||
mp2_scalefactor[ch][sb][0] = get_bits(6);
|
||||
val tmp = get_bits(6);
|
||||
mp2_scalefactor[ch][sb][1] = tmp
|
||||
mp2_scalefactor[ch][sb][2] = tmp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode == MONO){
|
||||
for (part in 0 until 3){
|
||||
mp2_scalefactor[1][sb][part] = mp2_scalefactor[0][sb][part];
|
||||
};
|
||||
};
|
||||
}
|
||||
// let ppcm=0;
|
||||
// coefficient input and reconstruction
|
||||
for (part in 0 until 3){
|
||||
for (gr in 0 until 4) {
|
||||
|
||||
// read the samples
|
||||
for (sb in 0 until bound){
|
||||
for (ch in 0 until 2){
|
||||
read_samples(mp2_allocation[ch][sb], mp2_scalefactor[ch][sb][part], mp2_sample[ch][sb]);
|
||||
};
|
||||
};
|
||||
for (sb in bound until sblimit) {
|
||||
read_samples(mp2_allocation[0][sb], mp2_scalefactor[0][sb][part], mp2_sample[0][sb]);
|
||||
|
||||
for (idx in 0 until 3){
|
||||
mp2_sample[1][sb][idx] = mp2_sample[0][sb][idx];
|
||||
};
|
||||
};
|
||||
for (ch in 0 until 2){
|
||||
for (sb in sblimit until 32){
|
||||
for (idx in 0 until 3){
|
||||
mp2_sample[ch][sb][idx] = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// synthesis loop
|
||||
for (idx in 0 until 3) {
|
||||
// shifting step
|
||||
val tmp = (mp2.Voffs - 64) and 1023
|
||||
mp2.Voffs = tmp
|
||||
table_idx = tmp
|
||||
|
||||
for (ch in 0 until 2) {
|
||||
// matrixing
|
||||
for (i in 0 until 64) {
|
||||
sum = 0;
|
||||
for (j in 0 until 32)
|
||||
sum += mp2_N[i][j] * mp2_sample[ch][j][idx]; // 8b*15b=23b
|
||||
// intermediate value is 28 bit (23 + 5), clamp to 14b
|
||||
mp2.V[ch][table_idx + i] = (sum + 8192) shr 14;
|
||||
}
|
||||
|
||||
// construction of U
|
||||
for (i in 0 until 8){
|
||||
for (j in 0 until 32) {
|
||||
mp2_U[(i shl 6) + j] = mp2.V[ch][(table_idx + (i shl 7) + j ) and 1023];
|
||||
mp2_U[(i shl 6) + j + 32] = mp2.V[ch][(table_idx + (i shl 7) + j + 96) and 1023];
|
||||
};
|
||||
};
|
||||
// apply window
|
||||
for (i in 0 until 512){
|
||||
mp2_U[i] = (mp2_U[i] * mp2_D[i] + 32) shr 6;
|
||||
};
|
||||
// output samples
|
||||
for (j in 0 until 32) {
|
||||
sum = 0;
|
||||
for (i in 0 until 16){
|
||||
sum -= mp2_U[(i shl 5) + j];
|
||||
};
|
||||
sum = (sum + 8) shr 4;
|
||||
sum = sum.coerceIn(-32768, 32767)
|
||||
if (ch == 0) { pushL(sum) }
|
||||
if (ch == 1) { pushR(sum) }
|
||||
}
|
||||
} // end of synthesis channel loop
|
||||
} // end of synthesis sub-block loop
|
||||
|
||||
// adjust PCM output pointer: decoded 3 * 32 = 96 stereo samples
|
||||
// ppcm += 192;
|
||||
|
||||
} // decoding of the granule finished
|
||||
}
|
||||
|
||||
// if (pushSizeL != pushSizeR && pushSizeR > 0) {
|
||||
// throw Error("Push size mismatch -- U${pushSizeL} != R${pushSizeR}")
|
||||
// }
|
||||
// return intArrayOf(frame_size, pushSizeL);
|
||||
return intArrayOf(frame_size, 2304);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,25 +1,30 @@
|
||||
package net.torvald.tsvm.peripheral
|
||||
|
||||
import net.torvald.tsvm.VM
|
||||
import net.torvald.tsvm.getHashStr
|
||||
|
||||
interface PeriBase {
|
||||
abstract class PeriBase(open val typestring: String) {
|
||||
|
||||
val hash = getHashStr()
|
||||
|
||||
/**
|
||||
* Addr is not an offset; they can be "wired" into any other "chip" in the card other than its RAM
|
||||
*/
|
||||
fun peek(addr: Long): Byte?
|
||||
abstract fun peek(addr: Long): Byte?
|
||||
|
||||
/**
|
||||
* Addr is not an offset; they can be "wired" into any other "chip" in the card other than its RAM
|
||||
*/
|
||||
fun poke(addr: Long, byte: Byte)
|
||||
abstract fun poke(addr: Long, byte: Byte)
|
||||
|
||||
fun mmio_read(addr: Long): Byte?
|
||||
fun mmio_write(addr: Long, byte: Byte)
|
||||
abstract fun mmio_read(addr: Long): Byte?
|
||||
abstract fun mmio_write(addr: Long, byte: Byte)
|
||||
|
||||
fun dispose()
|
||||
abstract fun dispose()
|
||||
|
||||
fun getVM(): VM
|
||||
abstract fun getVM(): VM
|
||||
|
||||
val typestring: String
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return (this.hash == (other as PeriBase).hash)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import java.io.File
|
||||
/**
|
||||
* Created by minjaesong on 2022-07-20.
|
||||
*/
|
||||
open class RamBank(val vm: VM, bankCount: Int) : PeriBase {
|
||||
open class RamBank(val vm: VM, bankCount: Int) : PeriBase("ramb") {
|
||||
|
||||
val bankSize = 524288L
|
||||
|
||||
@@ -60,8 +60,6 @@ open class RamBank(val vm: VM, bankCount: Int) : PeriBase {
|
||||
}
|
||||
|
||||
override fun getVM() = vm
|
||||
|
||||
override val typestring = "RAMB"
|
||||
}
|
||||
|
||||
open class RomBank(vm: VM, romfile: File, bankCount: Int) : RamBank(vm, bankCount) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import net.torvald.tsvm.VM
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
class TTY(assetsRoot: String, val vm: VM) : GlassTty(TEXT_ROWS, TEXT_COLS), PeriBase {
|
||||
class TTY(assetsRoot: String, val vm: VM) : GlassTty(TEXT_ROWS, TEXT_COLS) {
|
||||
|
||||
override val typestring = VM.PERITYPE_GPU_AND_TERM
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.torvald.tsvm.vdc
|
||||
|
||||
import net.torvald.UnsafeHelper
|
||||
import net.torvald.tsvm.peripheral.GraphicsAdapter
|
||||
import net.torvald.tsvm.toInt
|
||||
import net.torvald.tsvm.vdc.Command.instSet
|
||||
import java.lang.NumberFormatException
|
||||
import java.util.*
|
||||
@@ -358,8 +359,6 @@ class Videotron2K(var gpu: GraphicsAdapter?) {
|
||||
regs.destroy()
|
||||
}
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
|
||||
private fun <T> Array<T>.linearSearch(selector: (T) -> Boolean): Int? {
|
||||
this.forEachIndexed { index, it ->
|
||||
if (selector.invoke(it)) return index
|
||||
|
||||
Reference in New Issue
Block a user