summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2025-03-15 22:47:10 -0300
committerSoniEx2 <endermoneymod@gmail.com>2025-03-15 22:47:10 -0300
commit1b4e672a4580e2d38edcd06510dabb45359f162b (patch)
treee8991c94e57c61fd1287f31a71261230c71b4f18
parent2aa1dea5126290ee6dadc0884a3d8e2791be04ef (diff)
Make flite work
-rw-r--r--.gitignore3
-rw-r--r--build.gradle3
-rwxr-xr-xflite/build_script.sh14
-rw-r--r--flite/extrafuncs.c20
-rw-r--r--src/client/kotlin/space/autistic/radio/client/cli/Funny.kt11
-rw-r--r--src/client/kotlin/space/autistic/radio/client/cli/OfflineSimulator.kt28
-rw-r--r--src/client/kotlin/space/autistic/radio/client/flite/FliteFactory.kt58
-rw-r--r--src/client/kotlin/space/autistic/radio/client/flite/FliteWrapper.kt35
-rw-r--r--src/client/kotlin/space/autistic/radio/client/opus/OpusDecoder.kt2
-rw-r--r--src/main/kotlin/space/autistic/radio/reflection/MemoryReflection.kt (renamed from src/client/kotlin/space/autistic/radio/client/reflection/MemoryReflection.kt)2
-rw-r--r--src/main/kotlin/space/autistic/radio/wasm/Bindings.kt5
-rw-r--r--src/main/kotlin/space/autistic/radio/wasm/WasmExitException.kt4
12 files changed, 167 insertions, 18 deletions
diff --git a/.gitignore b/.gitignore
index 863eead..ea0871f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,6 @@ replay_*.log
 # wasm
 
 *.wasm
+
+# flite
+/src/main/resources/flite/
diff --git a/build.gradle b/build.gradle
index 2c2c885..95aa747 100644
--- a/build.gradle
+++ b/build.gradle
@@ -52,6 +52,9 @@ dependencies {
 	include implementation("com.dylibso.chicory:aot-experimental:${project.chicory_version}")
 	include implementation("com.github.wendykierp:JTransforms:3.1:with-dependencies")
 
+	// cli-only
+	implementation("com.github.ooxi:jdatauri:1.2.1")
+
 	testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
 	testImplementation "org.jetbrains.kotlin:kotlin-test:${project.kotlin_test_version}"
 }
diff --git a/flite/build_script.sh b/flite/build_script.sh
index 4e18760..2748331 100755
--- a/flite/build_script.sh
+++ b/flite/build_script.sh
@@ -5,6 +5,9 @@ set -e
 cd flite-6c9f20dc915b17f5619340069889db0aa007fcdc
 
 s_EXPORTED_FUNCTIONS="-s EXPORTED_FUNCTIONS=$(printf '_%s,' \
+flite_wrapper_init \
+flite_text_to_wave \
+delete_val \
 malloc
 )_free"
 
@@ -12,14 +15,13 @@ shared_flags="-Oz -flto -fno-unroll-loops -fno-inline -mnontrapping-fptoint"
 compile_flags="-DNDEBUG -DNO_STDIO $shared_flags"
 link_flags="$shared_flags -s MALLOC=emmalloc -s ASSERTIONS=0 -s INITIAL_MEMORY=$((20*1024*1024)) -s TOTAL_STACK=$((2*1024*1024)) -s STANDALONE_WASM=1 -s PURE_WASI=1 -s STACK_OVERFLOW_CHECK=0 -s WASM_BIGINT=1 -s ABORTING_MALLOC=0 -s MEMORY_GROWTH_GEOMETRIC_STEP=0 -s ALLOW_MEMORY_GROWTH=1"
 
-emconfigure ./configure STRIP='emstrip' CFLAGS="$compile_flags" LDFLAGS="$link_flags" --host=wasm32 --disable-sockets --disable-shared --without-pic --with-mmap=none --with-audio=none
+emconfigure ./configure STRIP='emstrip' CFLAGS="$compile_flags" LDFLAGS="$link_flags" --host=wasm32 --disable-sockets --disable-shared --without-pic --with-mmap=none --with-audio=none --with-vox=cmu_us_kal
 emmake make
 
-#emcc --no-entry $s_EXPORTED_FUNCTIONS $link_flags libmp3lame/.libs/libmp3lame.a
-#em++ --no-entry $s_EXPORTED_FUNCTIONS $link_flags $compile_flags -o pocketfft.wasm impl.cc
+emcc --no-entry $s_EXPORTED_FUNCTIONS $link_flags -o flite.wasm -Lbuild/wasm32-none/lib -Iinclude/ -lflite -lflite_usenglish -lflite_cmulex -lflite_cmu_us_kal ../extrafuncs.c
 
-#mkdir -p ../../src/main/resources/flite/
-#cp a.out.wasm ../../src/main/resources/flite.wasm
-#cp COPYING ../../src/main/resources/flite/
+mkdir -p ../../src/main/resources/flite/
+cp flite.wasm ../../src/main/resources/flite.wasm
+cp COPYING ../../src/main/resources/flite/
 
 exit 0
diff --git a/flite/extrafuncs.c b/flite/extrafuncs.c
new file mode 100644
index 0000000..2831120
--- /dev/null
+++ b/flite/extrafuncs.c
@@ -0,0 +1,20 @@
+#include "flite.h"
+
+cst_voice *register_cmu_us_kal(const char *voxdir);
+
+void usenglish_init(cst_voice *v);
+cst_lexicon *cmulex_init(void);
+
+void flite_set_lang_list(void)
+{
+   flite_add_lang("eng",usenglish_init,cmulex_init);
+   flite_add_lang("usenglish",usenglish_init,cmulex_init);
+}
+
+cst_voice *flite_wrapper_init() {
+	flite_init();
+	flite_set_lang_list();
+	flite_voice_list = cons_val(voice_val(register_cmu_us_kal(NULL)),flite_voice_list);
+	flite_voice_list = val_reverse(flite_voice_list);
+        return flite_voice_select(NULL);
+}
diff --git a/src/client/kotlin/space/autistic/radio/client/cli/Funny.kt b/src/client/kotlin/space/autistic/radio/client/cli/Funny.kt
index ebf6b06..2e64d36 100644
--- a/src/client/kotlin/space/autistic/radio/client/cli/Funny.kt
+++ b/src/client/kotlin/space/autistic/radio/client/cli/Funny.kt
@@ -1,16 +1,7 @@
 package space.autistic.radio.client.cli
 
-import space.autistic.radio.wasm.Bindings
-import java.lang.invoke.MethodHandles
-import kotlin.reflect.jvm.javaMethod
+import space.autistic.radio.client.flite.FliteWrapper
 
-object Funny {
-    val lookup = MethodHandles.lookup()
-
-    fun test() {
-    }
-}
 
 fun main() {
-    Bindings.bindFunc("", "", Funny.lookup, Funny::test.javaMethod!!, Funny)
 }
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/cli/OfflineSimulator.kt b/src/client/kotlin/space/autistic/radio/client/cli/OfflineSimulator.kt
index 68c4719..2646ed2 100644
--- a/src/client/kotlin/space/autistic/radio/client/cli/OfflineSimulator.kt
+++ b/src/client/kotlin/space/autistic/radio/client/cli/OfflineSimulator.kt
@@ -1,10 +1,15 @@
 package space.autistic.radio.client.cli
 
+import com.github.ooxi.jdatauri.DataUri
 import org.joml.Vector2f
 import space.autistic.radio.client.complex.cmul
+import space.autistic.radio.client.flite.FliteWrapper
 import space.autistic.radio.client.fmsim.FmFullConstants
 import space.autistic.radio.client.fmsim.FmFullDemodulator
 import space.autistic.radio.client.fmsim.FmFullModulator
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.DataOutputStream
 import java.io.FileInputStream
 import java.io.FileOutputStream
 import java.io.InputStream
@@ -126,6 +131,29 @@ fun main(args: Array<String>) {
             if (inputFile.filename != "file:///dev/zero") {
                 if (inputFile.filename.startsWith("./")) {
                     inputFile.stream = FileInputStream(inputFile.filename)
+                } else if (inputFile.filename.startsWith("data:")) {
+                    val uri = try {
+                        DataUri.parse(inputFile.filename, Charsets.UTF_8)
+                    } catch (e: IllegalArgumentException) {
+                        println("error parsing data URI")
+                        exitProcess(1)
+                    }
+                    if (!uri.mime.startsWith("text/")) {
+                        println("unsupported data URI format")
+                        exitProcess(1)
+                    }
+                    FliteWrapper.textToWave(uri.data.toString(uri.charset ?: Charsets.UTF_8)) {
+                        // 8k mono -> 48k stereo float
+                        val bbuf = ByteBuffer.allocate(it.capacity() * 2 * 6 * 4)
+                        val fbuf = bbuf.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer()
+                        while (it.hasRemaining()) {
+                            val sample = (it.get().toFloat() + 0.5f) / 32767.5f
+                            for (i in 0 until 2 * 6) {
+                                fbuf.put(sample)
+                            }
+                        }
+                        inputFile.stream = ByteArrayInputStream(bbuf.array())
+                    }
                 } else {
                     inputFile.stream = URI(inputFile.filename).toPath().inputStream()
                 }
diff --git a/src/client/kotlin/space/autistic/radio/client/flite/FliteFactory.kt b/src/client/kotlin/space/autistic/radio/client/flite/FliteFactory.kt
new file mode 100644
index 0000000..993cbe9
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/flite/FliteFactory.kt
@@ -0,0 +1,58 @@
+package space.autistic.radio.client.flite
+
+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 space.autistic.radio.wasm.Bindings
+import space.autistic.radio.wasm.WasmExitException
+import java.io.InputStream
+import java.lang.invoke.MethodHandles
+import kotlin.reflect.jvm.javaMethod
+
+object FliteFactory : () -> Instance {
+    private fun fd_read(a: Int, b: Int, c: Int, d: Int): Int {
+        throw UnsupportedOperationException()
+    }
+
+    private fun fd_seek(a: Int, b: Long, c: Int, d: Int): Int {
+        throw UnsupportedOperationException()
+    }
+
+    private fun fd_close(a: Int): Int {
+        throw UnsupportedOperationException()
+    }
+
+    private fun fd_write(a: Int, b: Int, c: Int, d: Int): Int {
+        throw UnsupportedOperationException()
+    }
+
+    private fun proc_exit(status: Int): Nothing {
+        throw WasmExitException(status)
+    }
+
+    private val lookup = MethodHandles.lookup()
+    private val defaultImports = ImportValues.builder()
+        .addFunction(
+            Bindings.bindFunc("wasi_snapshot_preview1", "fd_close", lookup, ::fd_close.javaMethod!!, this),
+            Bindings.bindFunc("wasi_snapshot_preview1", "fd_read", lookup, ::fd_read.javaMethod!!, this),
+            Bindings.bindFunc("wasi_snapshot_preview1", "fd_write", lookup, ::fd_write.javaMethod!!, this),
+            Bindings.bindFunc("wasi_snapshot_preview1", "fd_seek", lookup, ::fd_seek.javaMethod!!, this),
+            Bindings.bindFunc("wasi_snapshot_preview1", "proc_exit", lookup, ::proc_exit.javaMethod!!, this),
+        ).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("flite.wasm") }
+            .map<InputStream?> { it.toFile().inputStream() }.orElseGet {
+                this.javaClass.getResourceAsStream("/flite.wasm")
+            }
+    }
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/flite/FliteWrapper.kt b/src/client/kotlin/space/autistic/radio/client/flite/FliteWrapper.kt
new file mode 100644
index 0000000..e73d4d0
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/flite/FliteWrapper.kt
@@ -0,0 +1,35 @@
+package space.autistic.radio.client.flite
+
+import com.dylibso.chicory.runtime.ByteBufferMemory
+import space.autistic.radio.reflection.getBuffer
+import java.nio.ByteOrder
+import java.nio.ShortBuffer
+import java.util.function.Consumer
+
+object FliteWrapper {
+    // Produces audio at 8kHz mono
+    fun textToWave(s: String, consumer: Consumer<ShortBuffer>) {
+        val instance = FliteFactory.invoke()
+        instance.export("_initialize").apply()
+
+        val voice = instance.export("flite_wrapper_init").apply()[0]
+
+        val textToWaveImpl = instance.export("flite_text_to_wave")
+        val mallocImpl = instance.export("malloc")
+        val memory = instance.memory() as ByteBufferMemory
+
+        val bytes = s.toByteArray()
+        val space = mallocImpl.apply((bytes.size + 1).toLong())[0].toInt()
+        memory.getBuffer().run {
+            put(space, bytes)
+            put(space + bytes.size, 0)
+        }
+        val wavedata = textToWaveImpl.apply(space.toLong(), voice)[0].toInt()
+        val numSamples = memory.readInt(wavedata + 8)
+        val samplesPtr = memory.readInt(wavedata + 16)
+        consumer.accept(
+            memory.getBuffer().slice(samplesPtr, numSamples * 2).order(ByteOrder.LITTLE_ENDIAN)
+                .asShortBuffer()
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/opus/OpusDecoder.kt b/src/client/kotlin/space/autistic/radio/client/opus/OpusDecoder.kt
index fe78393..98e80d4 100644
--- a/src/client/kotlin/space/autistic/radio/client/opus/OpusDecoder.kt
+++ b/src/client/kotlin/space/autistic/radio/client/opus/OpusDecoder.kt
@@ -1,7 +1,7 @@
 package space.autistic.radio.client.opus
 
 import com.dylibso.chicory.runtime.ByteBufferMemory
-import space.autistic.radio.client.reflection.getBuffer
+import space.autistic.radio.reflection.getBuffer
 import java.nio.ByteOrder
 
 class OpusDecoder(sampleRate: Int, private val channels: Int) {
diff --git a/src/client/kotlin/space/autistic/radio/client/reflection/MemoryReflection.kt b/src/main/kotlin/space/autistic/radio/reflection/MemoryReflection.kt
index b1da224..78961da 100644
--- a/src/client/kotlin/space/autistic/radio/client/reflection/MemoryReflection.kt
+++ b/src/main/kotlin/space/autistic/radio/reflection/MemoryReflection.kt
@@ -1,4 +1,4 @@
-package space.autistic.radio.client.reflection
+package space.autistic.radio.reflection
 
 import com.dylibso.chicory.runtime.ByteBufferMemory
 import java.lang.invoke.MethodHandles
diff --git a/src/main/kotlin/space/autistic/radio/wasm/Bindings.kt b/src/main/kotlin/space/autistic/radio/wasm/Bindings.kt
index cc123a1..5ff4102 100644
--- a/src/main/kotlin/space/autistic/radio/wasm/Bindings.kt
+++ b/src/main/kotlin/space/autistic/radio/wasm/Bindings.kt
@@ -141,6 +141,11 @@ class Bindings {
                     Value.EMPTY_VALUES
                 }
 
+                Void::class.java -> HostFunction(module, name, wasmParameters, emptyList()) { instance, args ->
+                    handle.invokeExact(instance, args)
+                    throw IllegalStateException("unreachable")
+                }
+
                 Int::class.java -> HostFunction(module, name, wasmParameters, listOf(ValueType.I32)) { instance, args ->
                     val result: Int = handle.invokeExact(instance, args) as Int
                     longArrayOf(result.toLong())
diff --git a/src/main/kotlin/space/autistic/radio/wasm/WasmExitException.kt b/src/main/kotlin/space/autistic/radio/wasm/WasmExitException.kt
new file mode 100644
index 0000000..43c08be
--- /dev/null
+++ b/src/main/kotlin/space/autistic/radio/wasm/WasmExitException.kt
@@ -0,0 +1,4 @@
+package space.autistic.radio.wasm
+
+class WasmExitException(val status: Int) : Exception() {
+}