Skip to content

Commit 5a4cc6e

Browse files
authored
Merge pull request #13 from matejdro/music
Music control packets
2 parents 0e1102b + 671df66 commit 5a4cc6e

File tree

12 files changed

+421
-15
lines changed

12 files changed

+421
-15
lines changed

src/androidMain/kotlin/util/DataBuffer.kt

+15
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,33 @@ actual class DataBuffer {
99
actual constructor(size: Int) {
1010
actualBuf = ByteBuffer.allocate(size)
1111
}
12+
1213
actual constructor(bytes: UByteArray) {
1314
actualBuf = ByteBuffer.wrap(bytes.toByteArray())
1415
}
1516

17+
/**
18+
* Total length of the buffer
19+
*/
20+
actual val length: Int
21+
get() = actualBuf.capacity()
22+
23+
/**
24+
* Current position in the buffer
25+
*/
26+
actual val readPosition: Int
27+
get() = actualBuf.position()
28+
1629
actual fun putUShort(short: UShort) {
1730
actualBuf.putShort(short.toShort())
1831
}
32+
1933
actual fun getUShort(): UShort = actualBuf.short.toUShort()
2034

2135
actual fun putShort(short: Short) {
2236
actualBuf.putShort(short)
2337
}
38+
2439
actual fun getShort(): Short = actualBuf.short
2540

2641
actual fun putUByte(byte: UByte) {
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
package io.rebble.libpebblecommon.exceptions
22

3-
class PacketEncodeException(message: String?): Exception(message)
4-
class PacketDecodeException(message: String?): Exception(message)
3+
class PacketEncodeException(message: String?) : Exception(message)
4+
class PacketDecodeException(message: String?, cause: Exception? = null) : Exception(message, cause)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package io.rebble.libpebblecommon.packets
2+
3+
import io.rebble.libpebblecommon.protocolhelpers.PacketRegistry
4+
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket
5+
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint
6+
import io.rebble.libpebblecommon.structmapper.*
7+
8+
open class MusicControl(val message: Message) : PebblePacket(ProtocolEndpoint.MUSIC_CONTROL) {
9+
val command = SUByte(m, message.value)
10+
11+
init {
12+
type = command.get()
13+
}
14+
15+
enum class Message(val value: UByte) {
16+
PlayPause(0x01u),
17+
Pause(0x02u),
18+
Play(0x03u),
19+
NextTrack(0x04u),
20+
PreviousTrack(0x05u),
21+
VolumeUp(0x06u),
22+
VolumeDown(0x07u),
23+
GetCurrentTrack(0x08u),
24+
UpdateCurrentTrack(0x10u),
25+
UpdatePlayStateInfo(0x11u),
26+
UpdateVolumeInfo(0x12u),
27+
UpdatePlayerInfo(0x13u)
28+
}
29+
30+
class UpdateCurrentTrack(
31+
artist: String = "",
32+
album: String = "",
33+
title: String = "",
34+
trackLength: Int? = null,
35+
trackCount: Int? = null,
36+
currentTrack: Int? = null
37+
) : MusicControl(Message.UpdateCurrentTrack) {
38+
val artist = SString(m, artist)
39+
val album = SString(m, album)
40+
val title = SString(m, title)
41+
val trackLength = SOptional(
42+
m,
43+
SUInt(StructMapper(), trackLength?.toUInt() ?: 0u, '<'),
44+
trackLength != null
45+
)
46+
val trackCount = SOptional(
47+
m,
48+
SUInt(StructMapper(), trackCount?.toUInt() ?: 0u, '<'),
49+
trackCount != null
50+
)
51+
val currentTrack = SOptional(
52+
m,
53+
SUInt(StructMapper(), currentTrack?.toUInt() ?: 0u, '<'),
54+
currentTrack != null
55+
)
56+
}
57+
58+
class UpdatePlayStateInfo(
59+
playbackState: PlaybackState = PlaybackState.Unknown,
60+
trackPosition: UInt = 0u,
61+
playRate: UInt = 0u,
62+
shuffle: ShuffleState = ShuffleState.Unknown,
63+
repeat: RepeatState = RepeatState.Unknown
64+
) : MusicControl(Message.UpdatePlayStateInfo) {
65+
val state = SUByte(m, playbackState.value)
66+
val trackPosition = SUInt(m, trackPosition, '<')
67+
val playRate = SUInt(m, playRate, '<')
68+
val shuffle = SUByte(m, shuffle.value)
69+
val repeat = SUByte(m, repeat.value)
70+
}
71+
72+
class UpdateVolumeInfo(
73+
volumePercent: UByte = 0u,
74+
) : MusicControl(Message.UpdateVolumeInfo) {
75+
val volumePercent = SUByte(m, volumePercent)
76+
}
77+
78+
class UpdatePlayerInfo(
79+
pkg: String = "",
80+
name: String = ""
81+
) : MusicControl(Message.UpdatePlayerInfo) {
82+
val pkg = SString(m, pkg)
83+
val name = SString(m, name)
84+
}
85+
86+
enum class PlaybackState(val value: UByte) {
87+
Paused(0x00u),
88+
Playing(0x01u),
89+
Rewinding(0x02u),
90+
FastForwarding(0x03u),
91+
Unknown(0x04u),
92+
}
93+
94+
enum class ShuffleState(val value: UByte) {
95+
Unknown(0x00u),
96+
Off(0x01u),
97+
On(0x02u),
98+
}
99+
100+
enum class RepeatState(val value: UByte) {
101+
Unknown(0x00u),
102+
Off(0x01u),
103+
One(0x02u),
104+
All(0x03u),
105+
}
106+
}
107+
108+
fun musicPacketsRegister() {
109+
PacketRegistry.register(
110+
ProtocolEndpoint.MUSIC_CONTROL,
111+
MusicControl.Message.PlayPause.value
112+
) { MusicControl(MusicControl.Message.PlayPause) }
113+
114+
PacketRegistry.register(
115+
ProtocolEndpoint.MUSIC_CONTROL,
116+
MusicControl.Message.Pause.value
117+
) { MusicControl(MusicControl.Message.Pause) }
118+
119+
PacketRegistry.register(
120+
ProtocolEndpoint.MUSIC_CONTROL,
121+
MusicControl.Message.Play.value
122+
) { MusicControl(MusicControl.Message.Play) }
123+
124+
PacketRegistry.register(
125+
ProtocolEndpoint.MUSIC_CONTROL,
126+
MusicControl.Message.NextTrack.value
127+
) { MusicControl(MusicControl.Message.NextTrack) }
128+
129+
PacketRegistry.register(
130+
ProtocolEndpoint.MUSIC_CONTROL,
131+
MusicControl.Message.PreviousTrack.value
132+
) { MusicControl(MusicControl.Message.PreviousTrack) }
133+
134+
PacketRegistry.register(
135+
ProtocolEndpoint.MUSIC_CONTROL,
136+
MusicControl.Message.VolumeUp.value
137+
) { MusicControl(MusicControl.Message.VolumeUp) }
138+
139+
PacketRegistry.register(
140+
ProtocolEndpoint.MUSIC_CONTROL,
141+
MusicControl.Message.VolumeDown.value
142+
) { MusicControl(MusicControl.Message.VolumeDown) }
143+
144+
PacketRegistry.register(
145+
ProtocolEndpoint.MUSIC_CONTROL,
146+
MusicControl.Message.GetCurrentTrack.value
147+
) { MusicControl(MusicControl.Message.GetCurrentTrack) }
148+
149+
PacketRegistry.register(
150+
ProtocolEndpoint.MUSIC_CONTROL,
151+
MusicControl.Message.UpdateCurrentTrack.value
152+
) { MusicControl.UpdateCurrentTrack() }
153+
154+
PacketRegistry.register(
155+
ProtocolEndpoint.MUSIC_CONTROL,
156+
MusicControl.Message.UpdatePlayStateInfo.value
157+
) { MusicControl.UpdatePlayStateInfo() }
158+
159+
PacketRegistry.register(
160+
ProtocolEndpoint.MUSIC_CONTROL,
161+
MusicControl.Message.UpdateVolumeInfo.value
162+
) { MusicControl.UpdateVolumeInfo() }
163+
164+
PacketRegistry.register(
165+
ProtocolEndpoint.MUSIC_CONTROL,
166+
MusicControl.Message.UpdatePlayerInfo.value
167+
) { MusicControl.UpdatePlayerInfo() }
168+
}

src/commonMain/kotlin/io/rebble/libpebblecommon/protocolhelpers/PacketRegistry.kt

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package io.rebble.libpebblecommon.protocolhelpers
22

33
import io.rebble.libpebblecommon.exceptions.PacketDecodeException
4-
import io.rebble.libpebblecommon.packets.appRunStatePacketsRegister
5-
import io.rebble.libpebblecommon.packets.appmessagePacketsRegister
4+
import io.rebble.libpebblecommon.packets.*
65
import io.rebble.libpebblecommon.packets.blobdb.blobDBPacketsRegister
76
import io.rebble.libpebblecommon.packets.blobdb.timelinePacketsRegister
8-
import io.rebble.libpebblecommon.packets.systemPacketsRegister
9-
import io.rebble.libpebblecommon.packets.timePacketsRegister
107

118
/**
129
* Singleton to track endpoint / type discriminators for deserialization
@@ -22,6 +19,7 @@ object PacketRegistry {
2219
blobDBPacketsRegister()
2320
appmessagePacketsRegister()
2421
appRunStatePacketsRegister()
22+
musicPacketsRegister()
2523
}
2624

2725
/**
@@ -46,7 +44,7 @@ object PacketRegistry {
4644

4745
val typeOffset = if (typeOffsets[endpoint] != null) typeOffsets[endpoint]!! else 4
4846
val decoder = epdecoders[packet[typeOffset]]
49-
?: throw PacketDecodeException("No packet class registered for endpoint $endpoint")
47+
?: throw PacketDecodeException("No packet class registered for type ${packet[typeOffset]} of $endpoint")
5048
return decoder(packet)
5149
}
5250
}

src/commonMain/kotlin/io/rebble/libpebblecommon/protocolhelpers/PebblePacket.kt

+7-3
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,19 @@ open class PebblePacket{
5858
val length = SUShort(meta)
5959
val ep = SUShort(meta)
6060
meta.fromBytes(buf)
61-
if (packet.size <= (2*UShort.SIZE_BYTES))
61+
if (packet.size <= (2 * UShort.SIZE_BYTES))
6262
throw PacketDecodeException("Malformed packet: contents empty")
63-
if (length.get().toInt() != (packet.size - (2*UShort.SIZE_BYTES)))
63+
if (length.get().toInt() != (packet.size - (2 * UShort.SIZE_BYTES)))
6464
throw PacketDecodeException("Malformed packet: bad length")
6565
val ret = PacketRegistry.get(
6666
ProtocolEndpoint.getByValue(ep.get()),
6767
packet
6868
)
69-
ret.m.fromBytes(buf)
69+
try {
70+
ret.m.fromBytes(buf)
71+
} catch (e: Exception) {
72+
throw PacketDecodeException("Failed to decode packet $ret", e)
73+
}
7074
return ret
7175
}
7276
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.rebble.libpebblecommon.services
2+
3+
import io.rebble.libpebblecommon.ProtocolHandler
4+
import io.rebble.libpebblecommon.packets.MusicControl
5+
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket
6+
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint
7+
import kotlinx.coroutines.channels.Channel
8+
9+
class MusicService(private val protocolHandler: ProtocolHandler) : ProtocolService {
10+
val receivedMessages = Channel<MusicControl>(Channel.BUFFERED)
11+
12+
init {
13+
protocolHandler.registerReceiveCallback(ProtocolEndpoint.MUSIC_CONTROL, this::receive)
14+
}
15+
16+
suspend fun send(packet: MusicControl) {
17+
protocolHandler.send(packet)
18+
}
19+
20+
fun receive(packet: PebblePacket) {
21+
if (packet !is MusicControl) {
22+
throw IllegalStateException("Received invalid packet type: $packet")
23+
}
24+
25+
receivedMessages.offer(packet)
26+
}
27+
28+
}

src/commonMain/kotlin/io/rebble/libpebblecommon/services/SystemService.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ class SystemService(private val protocolHandler: ProtocolHandler) : ProtocolServ
8686
2u,
8787
ProtocolCapsFlag.makeFlags(
8888
listOf(
89-
ProtocolCapsFlag.Supports8kAppMessage
89+
ProtocolCapsFlag.Supports8kAppMessage,
90+
ProtocolCapsFlag.SupportsExtendedMusicProtocol
9091
)
9192
)
9293

src/commonMain/kotlin/io/rebble/libpebblecommon/structmapper/types.kt

+37
Original file line numberDiff line numberDiff line change
@@ -377,4 +377,41 @@ class SFixedList<T : Mappable>(
377377
override fun hashCode(): Int {
378378
return list.hashCode()
379379
}
380+
}
381+
382+
class SOptional<T>(
383+
mapper: StructMapper,
384+
val value: StructElement<T>,
385+
var present: Boolean
386+
) : Mappable {
387+
init {
388+
mapper.register(this)
389+
}
390+
391+
override fun toBytes(): UByteArray {
392+
return if (present) value.toBytes() else UByteArray(0)
393+
}
394+
395+
override fun fromBytes(bytes: DataBuffer) {
396+
val leftBytes = bytes.length - bytes.readPosition
397+
if (leftBytes < value.size) {
398+
present = false
399+
} else {
400+
present = true
401+
value.fromBytes(bytes)
402+
}
403+
}
404+
405+
fun get(): T? {
406+
return if (present) value.get() else null
407+
}
408+
409+
fun set(value: T?) {
410+
if (value != null) {
411+
present = true
412+
this.value.set(value)
413+
} else {
414+
present = false
415+
}
416+
}
380417
}

src/commonMain/kotlin/io/rebble/libpebblecommon/util/DataBuffer.kt

+10
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,14 @@ expect class DataBuffer {
3434
fun array(): UByteArray
3535

3636
fun setEndian(endian: Char)
37+
38+
/**
39+
* Total length of the buffer
40+
*/
41+
val length: Int
42+
43+
/**
44+
* Current position in the buffer
45+
*/
46+
val readPosition: Int
3747
}

0 commit comments

Comments
 (0)