-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
voice/audio endpoint support, improved debugging of packet decode
- Loading branch information
Showing
8 changed files
with
239 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
src/commonMain/kotlin/io/rebble/libpebblecommon/packets/Audio.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package io.rebble.libpebblecommon.packets | ||
|
||
import io.rebble.libpebblecommon.protocolhelpers.PacketRegistry | ||
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket | ||
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint | ||
import io.rebble.libpebblecommon.structmapper.* | ||
import io.rebble.libpebblecommon.util.Endian | ||
|
||
/** | ||
* Audio streaming packet. Little endian. | ||
*/ | ||
sealed class AudioStream(command: Command, sessionId: UShort = 0u) : PebblePacket(ProtocolEndpoint.AUDIO_STREAMING) { | ||
val command = SUByte(m, command.value) | ||
val sessionId = SUShort(m, sessionId, endianness = Endian.Little) | ||
|
||
class EncoderFrame : StructMappable() { | ||
val data = SUnboundBytes(m) | ||
} | ||
|
||
class DataTransfer : AudioStream(AudioStream.Command.DataTransfer) { | ||
val frameCount = SUByte(m) | ||
val frames = SFixedList(m, 0) { | ||
EncoderFrame() | ||
} | ||
init { | ||
frames.linkWithCount(frameCount) | ||
} | ||
} | ||
|
||
class StopTransfer(sessionId: UShort = 0u) : AudioStream(AudioStream.Command.StopTransfer, sessionId) | ||
|
||
enum class Command(val value: UByte) { | ||
DataTransfer(0x02u), | ||
StopTransfer(0x03u) | ||
} | ||
} | ||
|
||
fun audioStreamPacketsRegister() { | ||
PacketRegistry.register(ProtocolEndpoint.AUDIO_STREAMING, AudioStream.Command.DataTransfer.value) { | ||
AudioStream.DataTransfer() | ||
} | ||
PacketRegistry.register(ProtocolEndpoint.AUDIO_STREAMING, AudioStream.Command.StopTransfer.value) { | ||
AudioStream.StopTransfer() | ||
} | ||
} |
134 changes: 134 additions & 0 deletions
134
src/commonMain/kotlin/io/rebble/libpebblecommon/packets/Voice.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package io.rebble.libpebblecommon.packets | ||
|
||
import io.rebble.libpebblecommon.protocolhelpers.PacketRegistry | ||
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket | ||
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint | ||
import io.rebble.libpebblecommon.structmapper.* | ||
import io.rebble.libpebblecommon.util.DataBuffer | ||
import io.rebble.libpebblecommon.util.Endian | ||
|
||
|
||
sealed class IncomingVoicePacket() : PebblePacket(ProtocolEndpoint.VOICE_CONTROL) { | ||
/** | ||
* Voice command. See [VoiceCommand]. | ||
*/ | ||
val command = SUByte(m) | ||
val flags = SUInt(m, endianness = Endian.Little) | ||
} | ||
sealed class OutgoingVoicePacket(command: VoiceCommand) : | ||
PebblePacket(ProtocolEndpoint.VOICE_CONTROL) { | ||
/** | ||
* Voice command. See [VoiceCommand]. | ||
*/ | ||
val command = SUByte(m, command.value) | ||
val flags = SUInt(m, endianness = Endian.Little) | ||
} | ||
|
||
enum class VoiceCommand(val value: UByte) { | ||
SessionSetup(0x01u), | ||
DictationResult(0x02u), | ||
} | ||
|
||
class Word(confidence: UByte = 0u, data: String = "") : StructMappable() { | ||
val confidence = SUByte(m, confidence) | ||
val length = SUShort(m, data.length.toUShort(), endianness = Endian.Little) | ||
val data = SFixedString(m, data.length, data) | ||
init { | ||
this.data.linkWithSize(length) | ||
} | ||
} | ||
|
||
class Sentence(words: List<Word> = emptyList()) : StructMappable() { | ||
val wordCount = SUShort(m, words.size.toUShort(), endianness = Endian.Little) | ||
val words = SFixedList(m, words.size, words) { Word() } | ||
init { | ||
this.words.linkWithCount(wordCount) | ||
} | ||
} | ||
|
||
enum class VoiceAttributeType(val value: UByte) { | ||
SpeexEncoderInfo(0x01u), | ||
Transcription(0x02u), | ||
AppUuid(0x03u), | ||
} | ||
|
||
open class VoiceAttribute(id: UByte = 0u, content: StructMappable? = null) : StructMappable() { | ||
val id = SUByte(m, id) | ||
val length = SUShort(m, content?.size?.toUShort() ?: 0u, endianness = Endian.Little) | ||
val content = SBytes(m, content?.size ?: 0, content?.toBytes() ?: ubyteArrayOf()) | ||
init { | ||
this.content.linkWithSize(length) | ||
} | ||
|
||
class SpeexEncoderInfo : StructMappable() { | ||
val version = SFixedString(m, 20) | ||
val sampleRate = SUInt(m, endianness = Endian.Little) | ||
val bitRate = SUShort(m, endianness = Endian.Little) | ||
val bitstreamVersion = SUByte(m) | ||
val frameSize = SUShort(m, endianness = Endian.Little) | ||
} | ||
|
||
class Transcription( | ||
type: UByte = 0x1u, | ||
sentences: List<Sentence> = emptyList() | ||
) : StructMappable() { | ||
val type = SUByte(m, type) // always 0x1? (sentence list) | ||
val count = SUByte(m, sentences.size.toUByte()) | ||
val sentences = SFixedList(m, sentences.size, sentences) { Sentence() } | ||
init { | ||
this.sentences.linkWithCount(count) | ||
} | ||
} | ||
|
||
class AppUuid : StructMappable() { | ||
val uuid = SUUID(m) | ||
} | ||
} | ||
|
||
/** | ||
* Voice session setup command. Little endian. | ||
*/ | ||
class SessionSetupCommand : IncomingVoicePacket() { | ||
val sessionType = SUByte(m) | ||
val sessionId = SUShort(m, endianness = Endian.Little) | ||
val attributeCount = SUByte(m) | ||
val attributes = SFixedList(m, 0) { | ||
VoiceAttribute() | ||
} | ||
init { | ||
attributes.linkWithCount(attributeCount) | ||
} | ||
} | ||
|
||
enum class SessionType(val value: UByte) { | ||
Dictation(0x01u), | ||
Command(0x02u), | ||
} | ||
|
||
enum class Result(val value: UByte) { | ||
Success(0x0u), | ||
FailServiceUnavailable(0x1u), | ||
FailTimeout(0x2u), | ||
FailRecognizerError(0x3u), | ||
FailInvalidRecognizerResponse(0x4u), | ||
FailDisabled(0x5u), | ||
FailInvalidMessage(0x6u), | ||
} | ||
|
||
class SessionSetupResult(sessionType: SessionType, result: Result) : OutgoingVoicePacket(VoiceCommand.SessionSetup) { | ||
val sessionType = SUByte(m, sessionType.value) | ||
val result = SUByte(m, result.value) | ||
} | ||
|
||
class DictationResult(sessionId: UShort, result: Result, attributes: List<StructMappable>) : OutgoingVoicePacket(VoiceCommand.DictationResult) { | ||
val sessionId = SUShort(m, sessionId, endianness = Endian.Little) | ||
val result = SUByte(m, result.value) | ||
val attributeCount = SUByte(m, attributes.size.toUByte()) | ||
val attributes = SFixedList(m, attributes.size, attributes) { VoiceAttribute() } | ||
} | ||
|
||
fun voicePacketsRegister() { | ||
PacketRegistry.register(ProtocolEndpoint.VOICE_CONTROL, VoiceCommand.SessionSetup.value) { | ||
SessionSetupCommand() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
src/commonMain/kotlin/io/rebble/libpebblecommon/services/AudioStreamService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package io.rebble.libpebblecommon.services | ||
|
||
import io.rebble.libpebblecommon.ProtocolHandler | ||
import io.rebble.libpebblecommon.packets.AudioStream | ||
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket | ||
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint | ||
import kotlinx.coroutines.channels.Channel | ||
|
||
class AudioStreamService(private val protocolHandler: ProtocolHandler) : ProtocolService { | ||
val receivedMessages = Channel<AudioStream>(Channel.BUFFERED) | ||
|
||
init { | ||
protocolHandler.registerReceiveCallback(ProtocolEndpoint.AUDIO_STREAMING, this::receive) | ||
} | ||
|
||
suspend fun send(packet: AudioStream) { | ||
protocolHandler.send(packet) | ||
} | ||
|
||
fun receive(packet: PebblePacket) { | ||
if (packet !is AudioStream) { | ||
throw IllegalStateException("Received invalid packet type: $packet") | ||
} | ||
|
||
receivedMessages.trySend(packet) | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/commonMain/kotlin/io/rebble/libpebblecommon/services/VoiceService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package io.rebble.libpebblecommon.services | ||
|
||
import io.rebble.libpebblecommon.ProtocolHandler | ||
import io.rebble.libpebblecommon.packets.* | ||
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket | ||
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint | ||
import kotlinx.coroutines.channels.Channel | ||
|
||
class VoiceService(private val protocolHandler: ProtocolHandler) : ProtocolService { | ||
val receivedMessages = Channel<IncomingVoicePacket>(Channel.BUFFERED) | ||
|
||
init { | ||
protocolHandler.registerReceiveCallback(ProtocolEndpoint.VOICE_CONTROL, this::receive) | ||
} | ||
|
||
suspend fun send(packet: OutgoingVoicePacket) { | ||
protocolHandler.send(packet) | ||
} | ||
|
||
fun receive(packet: PebblePacket) { | ||
if (packet !is IncomingVoicePacket) { | ||
throw IllegalStateException("Received invalid packet type: $packet") | ||
} | ||
|
||
receivedMessages.trySend(packet) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters