A byte-exact Kotlin implementation of the CMSDK DataMessage wire format, so Kotlin programs (the Mimir phone app, JVM tools) can talk to Psyclone services and CMSDK programs without linking the C++ library. Pure JVM, no Android imports. Package cmlabs.cmsdk.
This is a permanent part of CMSDK and must be kept in step with include/DataMessage.h.
import cmlabs.cmsdk.DataMessage
val m = DataMessage()
.setString("PsyPost", "AudioFrame") // a Psyclone service post NAME, never a type
.setString("PsyDevice", "phone-s25")
.setInt("Serial", 1)
.setData("pcm", pcmBytes)
udpSocket.send(DatagramPacket(m.toBytes(), ...)) // one DataMessage per datagram
val reply = DataMessage.fromBytes(packet.data, 0, packet.length)
reply.getString("PsyTrigger") // e.g. "Playout"
Entries: string, int64, double, time (PsyTime = µs since year 0, see Functions dealing with time), binary data and attached DataMessages. Other content (arrays and maps, which C++ stores under composite keys) is kept as Entry.Raw and written back unchanged. All header fields are exposed. Little-endian hosts only (as CMSDK).
Build and test
make kotlin # from CMSDK/: regenerate the C++ reference data, then ./gradlew test
cd kotlin && ./gradlew test # tests only (needs JDK 17+)
cd kotlin && ./gradlew jar # build/libs/cmsdk-kotlin-<version>.jar
An Android app can use the jar, or add the sources directly: sourceSets["main"].java.srcDir("<path>/CMSDK/kotlin/src/main/kotlin").
How it stays correct
- tools/layout.cpp dumps the C++ DataMessageHeader layout with offsetof() (padding included) to fixtures/layout.json. DataMessageLayout.kt holds the same offsets; a test compares every field.
- tools/gen_fixtures.cpp writes golden messages with the C++ library to fixtures/*.bin plus the values in fixtures/expected.json. Every header field carries a distinct non-zero value somewhere, so a wrong offset reads a wrong value. The tests decode every fixture, re-encode the compact ones and require byte-for-byte equality, and build two messages from scratch that must equal the C++ bytes.
- Proven to catch drift: moving one header offset by four bytes fails four of the nine tests.
When DataMessage changes in C++ (the maintenance rule)
- Change DataMessage.h / DataMessage.cpp as usual (bump CURRENTDATAMESSAGEVERSION on a format change).
- make kotlin from CMSDK/ — regenerates fixtures/ from the new C++ and runs the Kotlin tests.
- If they fail, update DataMessageLayout.kt / DataMessage.kt until they pass; add a fixture for any new entry kind to tools/gen_fixtures.cpp.
- Commit the regenerated fixtures together with the C++ and Kotlin changes.