diff options
Diffstat (limited to 'src/main/kotlin/space/autistic/radio/opus')
-rw-r--r-- | src/main/kotlin/space/autistic/radio/opus/OpusDecoder.kt | 77 | ||||
-rw-r--r-- | src/main/kotlin/space/autistic/radio/opus/OpusFactory.kt | 26 |
2 files changed, 103 insertions, 0 deletions
diff --git a/src/main/kotlin/space/autistic/radio/opus/OpusDecoder.kt b/src/main/kotlin/space/autistic/radio/opus/OpusDecoder.kt new file mode 100644 index 0000000..56fce2b --- /dev/null +++ b/src/main/kotlin/space/autistic/radio/opus/OpusDecoder.kt @@ -0,0 +1,77 @@ +package space.autistic.radio.opus + +import com.dylibso.chicory.runtime.ByteBufferMemory +import space.autistic.radio.reflection.getBuffer +import java.nio.ByteOrder + +class OpusDecoder(sampleRate: Int, private val channels: Int) { + private val instance = OpusFactory() + + init { + instance.export("_initialize").apply() + } + + private val errorPtr = instance.export("malloc").apply(4)[0] + + init { + if (errorPtr == 0L) { + throw IllegalStateException() + } + instance.memory().writeI32(errorPtr.toInt(), 0) + } + + private val decoder = + instance.export("opus_decoder_create").apply(sampleRate.toLong(), channels.toLong(), errorPtr)[0] + + init { + val error = instance.memory().readI32(errorPtr.toInt()) + if (error < 0) { + throw IllegalStateException( + instance.memory().readCString(instance.export("opus_strerror").apply(error)[0].toInt()) + ) + } + } + + private val opusDecodeFloat = instance.export("opus_decode_float") + + private val outBuf = instance.export("malloc").apply((4 * MAX_FRAME_SIZE * channels).toLong())[0] + + init { + if (outBuf == 0L) { + throw IllegalStateException() + } + } + + private val cbits = instance.export("malloc").apply(MAX_PACKET_SIZE.toLong())[0] + + init { + if (cbits == 0L) { + throw IllegalStateException() + } + } + + private val memory = instance.memory() as ByteBufferMemory + + fun decode(packet: ByteArray): FloatArray { + if (packet.size > MAX_PACKET_SIZE) { + throw IllegalArgumentException("packet too big") + } + memory.getBuffer().put(cbits.toInt(), packet) + val decoded = + opusDecodeFloat.apply(decoder, cbits, packet.size.toLong(), outBuf, MAX_FRAME_SIZE.toLong(), 0L)[0] + if (decoded < 0L) { + throw IllegalStateException( + instance.memory().readCString(instance.export("opus_strerror").apply(decoded)[0].toInt()) + ) + } + val out = FloatArray(decoded.toInt()) + memory.getBuffer().slice(outBuf.toInt(), outBuf.toInt() + 4 * channels * decoded.toInt()) + .order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer().get(out) + return out + } + + companion object { + const val MAX_FRAME_SIZE = 6 * 960 + const val MAX_PACKET_SIZE = 3 * 1276 + } +} \ No newline at end of file diff --git a/src/main/kotlin/space/autistic/radio/opus/OpusFactory.kt b/src/main/kotlin/space/autistic/radio/opus/OpusFactory.kt new file mode 100644 index 0000000..70e0c3c --- /dev/null +++ b/src/main/kotlin/space/autistic/radio/opus/OpusFactory.kt @@ -0,0 +1,26 @@ +package space.autistic.radio.opus + +import com.dylibso.chicory.experimental.aot.AotMachineFactory +import com.dylibso.chicory.runtime.ImportValues +import com.dylibso.chicory.runtime.Instance +import com.dylibso.chicory.wasm.Parser +import net.fabricmc.loader.api.FabricLoader +import java.io.InputStream + +object OpusFactory : () -> Instance { + private val defaultImports = ImportValues.builder().build() + private val module = Parser.parse(getModuleInputStream()) + private val instanceBuilder = + Instance.builder(module) + .withMachineFactory(AotMachineFactory(module)) + .withImportValues(defaultImports) + + override fun invoke(): Instance = instanceBuilder.build() + + private fun getModuleInputStream(): InputStream { + return FabricLoader.getInstance().getModContainer("pirate-radio").flatMap { it.findPath("opus.wasm") } + .map<InputStream?> { it.toFile().inputStream() }.orElseGet { + this.javaClass.getResourceAsStream("/opus.wasm") + } + } +} \ No newline at end of file |