bumble 0.0.198__py3-none-any.whl → 0.0.200__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/a2dp.py +502 -202
- bumble/apps/controller_info.py +60 -0
- bumble/apps/pair.py +32 -5
- bumble/apps/player/player.py +608 -0
- bumble/apps/speaker/speaker.py +25 -27
- bumble/att.py +57 -41
- bumble/avc.py +1 -2
- bumble/avdtp.py +56 -99
- bumble/avrcp.py +48 -29
- bumble/codecs.py +214 -68
- bumble/decoder.py +14 -10
- bumble/device.py +19 -11
- bumble/drivers/rtk.py +19 -5
- bumble/gatt.py +24 -19
- bumble/gatt_client.py +5 -25
- bumble/gatt_server.py +14 -6
- bumble/hci.py +298 -7
- bumble/hfp.py +52 -48
- bumble/host.py +28 -6
- bumble/pandora/__init__.py +3 -0
- bumble/pandora/l2cap.py +310 -0
- bumble/profiles/aics.py +520 -0
- bumble/profiles/asha.py +295 -0
- bumble/profiles/hap.py +674 -0
- bumble/profiles/vcp.py +5 -3
- bumble/rtp.py +110 -0
- bumble/smp.py +23 -4
- bumble/transport/android_netsim.py +3 -0
- bumble/transport/pyusb.py +20 -2
- {bumble-0.0.198.dist-info → bumble-0.0.200.dist-info}/METADATA +2 -2
- {bumble-0.0.198.dist-info → bumble-0.0.200.dist-info}/RECORD +36 -31
- {bumble-0.0.198.dist-info → bumble-0.0.200.dist-info}/WHEEL +1 -1
- {bumble-0.0.198.dist-info → bumble-0.0.200.dist-info}/entry_points.txt +1 -0
- bumble/profiles/asha_service.py +0 -193
- {bumble-0.0.198.dist-info → bumble-0.0.200.dist-info}/LICENSE +0 -0
- {bumble-0.0.198.dist-info → bumble-0.0.200.dist-info}/top_level.txt +0 -0
bumble/codecs.py
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
from dataclasses import dataclass
|
|
20
|
+
from typing_extensions import Self
|
|
20
21
|
|
|
21
22
|
from bumble import core
|
|
22
23
|
|
|
@@ -101,12 +102,40 @@ class BitReader:
|
|
|
101
102
|
break
|
|
102
103
|
|
|
103
104
|
|
|
105
|
+
# -----------------------------------------------------------------------------
|
|
106
|
+
class BitWriter:
|
|
107
|
+
"""Simple but not optimized bit stream writer."""
|
|
108
|
+
|
|
109
|
+
data: int
|
|
110
|
+
bit_count: int
|
|
111
|
+
|
|
112
|
+
def __init__(self) -> None:
|
|
113
|
+
self.data = 0
|
|
114
|
+
self.bit_count = 0
|
|
115
|
+
|
|
116
|
+
def write(self, value: int, bit_count: int) -> None:
|
|
117
|
+
self.data = (self.data << bit_count) | value
|
|
118
|
+
self.bit_count += bit_count
|
|
119
|
+
|
|
120
|
+
def write_bytes(self, data: bytes) -> None:
|
|
121
|
+
bit_count = 8 * len(data)
|
|
122
|
+
self.data = (self.data << bit_count) | int.from_bytes(data, 'big')
|
|
123
|
+
self.bit_count += bit_count
|
|
124
|
+
|
|
125
|
+
def __bytes__(self) -> bytes:
|
|
126
|
+
return (self.data << ((8 - (self.bit_count % 8)) % 8)).to_bytes(
|
|
127
|
+
(self.bit_count + 7) // 8, 'big'
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
104
131
|
# -----------------------------------------------------------------------------
|
|
105
132
|
class AacAudioRtpPacket:
|
|
106
133
|
"""AAC payload encapsulated in an RTP packet payload"""
|
|
107
134
|
|
|
135
|
+
audio_mux_element: AudioMuxElement
|
|
136
|
+
|
|
108
137
|
@staticmethod
|
|
109
|
-
def
|
|
138
|
+
def read_latm_value(reader: BitReader) -> int:
|
|
110
139
|
bytes_for_value = reader.read(2)
|
|
111
140
|
value = 0
|
|
112
141
|
for _ in range(bytes_for_value + 1):
|
|
@@ -114,24 +143,33 @@ class AacAudioRtpPacket:
|
|
|
114
143
|
return value
|
|
115
144
|
|
|
116
145
|
@staticmethod
|
|
117
|
-
def
|
|
118
|
-
|
|
146
|
+
def read_audio_object_type(reader: BitReader):
|
|
147
|
+
# GetAudioObjectType - ISO/EIC 14496-3 Table 1.16
|
|
148
|
+
audio_object_type = reader.read(5)
|
|
149
|
+
if audio_object_type == 31:
|
|
150
|
+
audio_object_type = 32 + reader.read(6)
|
|
151
|
+
|
|
152
|
+
return audio_object_type
|
|
119
153
|
|
|
120
154
|
@dataclass
|
|
121
155
|
class GASpecificConfig:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
156
|
+
audio_object_type: int
|
|
157
|
+
# NOTE: other fields not supported
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def from_bits(
|
|
161
|
+
cls, reader: BitReader, channel_configuration: int, audio_object_type: int
|
|
162
|
+
) -> Self:
|
|
125
163
|
# GASpecificConfig - ISO/EIC 14496-3 Table 4.1
|
|
126
164
|
frame_length_flag = reader.read(1)
|
|
127
165
|
depends_on_core_coder = reader.read(1)
|
|
128
166
|
if depends_on_core_coder:
|
|
129
|
-
|
|
167
|
+
core_coder_delay = reader.read(14)
|
|
130
168
|
extension_flag = reader.read(1)
|
|
131
169
|
if not channel_configuration:
|
|
132
|
-
|
|
170
|
+
raise core.InvalidPacketError('program_config_element not supported')
|
|
133
171
|
if audio_object_type in (6, 20):
|
|
134
|
-
|
|
172
|
+
layer_nr = reader.read(3)
|
|
135
173
|
if extension_flag:
|
|
136
174
|
if audio_object_type == 22:
|
|
137
175
|
num_of_sub_frame = reader.read(5)
|
|
@@ -144,14 +182,13 @@ class AacAudioRtpPacket:
|
|
|
144
182
|
if extension_flag_3 == 1:
|
|
145
183
|
raise core.InvalidPacketError('extensionFlag3 == 1 not supported')
|
|
146
184
|
|
|
147
|
-
|
|
148
|
-
def audio_object_type(reader: BitReader):
|
|
149
|
-
# GetAudioObjectType - ISO/EIC 14496-3 Table 1.16
|
|
150
|
-
audio_object_type = reader.read(5)
|
|
151
|
-
if audio_object_type == 31:
|
|
152
|
-
audio_object_type = 32 + reader.read(6)
|
|
185
|
+
return cls(audio_object_type)
|
|
153
186
|
|
|
154
|
-
|
|
187
|
+
def to_bits(self, writer: BitWriter) -> None:
|
|
188
|
+
assert self.audio_object_type in (1, 2)
|
|
189
|
+
writer.write(0, 1) # frame_length_flag = 0
|
|
190
|
+
writer.write(0, 1) # depends_on_core_coder = 0
|
|
191
|
+
writer.write(0, 1) # extension_flag = 0
|
|
155
192
|
|
|
156
193
|
@dataclass
|
|
157
194
|
class AudioSpecificConfig:
|
|
@@ -159,6 +196,7 @@ class AacAudioRtpPacket:
|
|
|
159
196
|
sampling_frequency_index: int
|
|
160
197
|
sampling_frequency: int
|
|
161
198
|
channel_configuration: int
|
|
199
|
+
ga_specific_config: AacAudioRtpPacket.GASpecificConfig
|
|
162
200
|
sbr_present_flag: int
|
|
163
201
|
ps_present_flag: int
|
|
164
202
|
extension_audio_object_type: int
|
|
@@ -182,44 +220,73 @@ class AacAudioRtpPacket:
|
|
|
182
220
|
7350,
|
|
183
221
|
]
|
|
184
222
|
|
|
185
|
-
|
|
223
|
+
@classmethod
|
|
224
|
+
def for_simple_aac(
|
|
225
|
+
cls,
|
|
226
|
+
audio_object_type: int,
|
|
227
|
+
sampling_frequency: int,
|
|
228
|
+
channel_configuration: int,
|
|
229
|
+
) -> Self:
|
|
230
|
+
if sampling_frequency not in cls.SAMPLING_FREQUENCIES:
|
|
231
|
+
raise ValueError(f'invalid sampling frequency {sampling_frequency}')
|
|
232
|
+
|
|
233
|
+
ga_specific_config = AacAudioRtpPacket.GASpecificConfig(audio_object_type)
|
|
234
|
+
|
|
235
|
+
return cls(
|
|
236
|
+
audio_object_type=audio_object_type,
|
|
237
|
+
sampling_frequency_index=cls.SAMPLING_FREQUENCIES.index(
|
|
238
|
+
sampling_frequency
|
|
239
|
+
),
|
|
240
|
+
sampling_frequency=sampling_frequency,
|
|
241
|
+
channel_configuration=channel_configuration,
|
|
242
|
+
ga_specific_config=ga_specific_config,
|
|
243
|
+
sbr_present_flag=0,
|
|
244
|
+
ps_present_flag=0,
|
|
245
|
+
extension_audio_object_type=0,
|
|
246
|
+
extension_sampling_frequency_index=0,
|
|
247
|
+
extension_sampling_frequency=0,
|
|
248
|
+
extension_channel_configuration=0,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
@classmethod
|
|
252
|
+
def from_bits(cls, reader: BitReader) -> Self:
|
|
186
253
|
# AudioSpecificConfig - ISO/EIC 14496-3 Table 1.15
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if
|
|
190
|
-
|
|
254
|
+
audio_object_type = AacAudioRtpPacket.read_audio_object_type(reader)
|
|
255
|
+
sampling_frequency_index = reader.read(4)
|
|
256
|
+
if sampling_frequency_index == 0xF:
|
|
257
|
+
sampling_frequency = reader.read(24)
|
|
191
258
|
else:
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
259
|
+
sampling_frequency = cls.SAMPLING_FREQUENCIES[sampling_frequency_index]
|
|
260
|
+
channel_configuration = reader.read(4)
|
|
261
|
+
sbr_present_flag = 0
|
|
262
|
+
ps_present_flag = 0
|
|
263
|
+
extension_sampling_frequency_index = 0
|
|
264
|
+
extension_sampling_frequency = 0
|
|
265
|
+
extension_channel_configuration = 0
|
|
266
|
+
extension_audio_object_type = 0
|
|
267
|
+
if audio_object_type in (5, 29):
|
|
268
|
+
extension_audio_object_type = 5
|
|
269
|
+
sbr_present_flag = 1
|
|
270
|
+
if audio_object_type == 29:
|
|
271
|
+
ps_present_flag = 1
|
|
272
|
+
extension_sampling_frequency_index = reader.read(4)
|
|
273
|
+
if extension_sampling_frequency_index == 0xF:
|
|
274
|
+
extension_sampling_frequency = reader.read(24)
|
|
206
275
|
else:
|
|
207
|
-
|
|
208
|
-
|
|
276
|
+
extension_sampling_frequency = cls.SAMPLING_FREQUENCIES[
|
|
277
|
+
extension_sampling_frequency_index
|
|
209
278
|
]
|
|
210
|
-
|
|
211
|
-
if
|
|
212
|
-
|
|
213
|
-
else:
|
|
214
|
-
self.extension_audio_object_type = 0
|
|
279
|
+
audio_object_type = AacAudioRtpPacket.read_audio_object_type(reader)
|
|
280
|
+
if audio_object_type == 22:
|
|
281
|
+
extension_channel_configuration = reader.read(4)
|
|
215
282
|
|
|
216
|
-
if
|
|
217
|
-
ga_specific_config = AacAudioRtpPacket.GASpecificConfig(
|
|
218
|
-
reader,
|
|
283
|
+
if audio_object_type in (1, 2, 3, 4, 6, 7, 17, 19, 20, 21, 22, 23):
|
|
284
|
+
ga_specific_config = AacAudioRtpPacket.GASpecificConfig.from_bits(
|
|
285
|
+
reader, channel_configuration, audio_object_type
|
|
219
286
|
)
|
|
220
287
|
else:
|
|
221
288
|
raise core.InvalidPacketError(
|
|
222
|
-
f'audioObjectType {
|
|
289
|
+
f'audioObjectType {audio_object_type} not supported'
|
|
223
290
|
)
|
|
224
291
|
|
|
225
292
|
# if self.extension_audio_object_type != 5 and bits_to_decode >= 16:
|
|
@@ -248,13 +315,44 @@ class AacAudioRtpPacket:
|
|
|
248
315
|
# self.extension_sampling_frequency = self.SAMPLING_FREQUENCIES[self.extension_sampling_frequency_index]
|
|
249
316
|
# self.extension_channel_configuration = reader.read(4)
|
|
250
317
|
|
|
318
|
+
return cls(
|
|
319
|
+
audio_object_type,
|
|
320
|
+
sampling_frequency_index,
|
|
321
|
+
sampling_frequency,
|
|
322
|
+
channel_configuration,
|
|
323
|
+
ga_specific_config,
|
|
324
|
+
sbr_present_flag,
|
|
325
|
+
ps_present_flag,
|
|
326
|
+
extension_audio_object_type,
|
|
327
|
+
extension_sampling_frequency_index,
|
|
328
|
+
extension_sampling_frequency,
|
|
329
|
+
extension_channel_configuration,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def to_bits(self, writer: BitWriter) -> None:
|
|
333
|
+
if self.sampling_frequency_index >= 15:
|
|
334
|
+
raise ValueError(
|
|
335
|
+
f"unsupported sampling frequency index {self.sampling_frequency_index}"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
if self.audio_object_type not in (1, 2):
|
|
339
|
+
raise ValueError(
|
|
340
|
+
f"unsupported audio object type {self.audio_object_type} "
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
writer.write(self.audio_object_type, 5)
|
|
344
|
+
writer.write(self.sampling_frequency_index, 4)
|
|
345
|
+
writer.write(self.channel_configuration, 4)
|
|
346
|
+
self.ga_specific_config.to_bits(writer)
|
|
347
|
+
|
|
251
348
|
@dataclass
|
|
252
349
|
class StreamMuxConfig:
|
|
253
350
|
other_data_present: int
|
|
254
351
|
other_data_len_bits: int
|
|
255
352
|
audio_specific_config: AacAudioRtpPacket.AudioSpecificConfig
|
|
256
353
|
|
|
257
|
-
|
|
354
|
+
@classmethod
|
|
355
|
+
def from_bits(cls, reader: BitReader) -> Self:
|
|
258
356
|
# StreamMuxConfig - ISO/EIC 14496-3 Table 1.42
|
|
259
357
|
audio_mux_version = reader.read(1)
|
|
260
358
|
if audio_mux_version == 1:
|
|
@@ -264,7 +362,7 @@ class AacAudioRtpPacket:
|
|
|
264
362
|
if audio_mux_version_a != 0:
|
|
265
363
|
raise core.InvalidPacketError('audioMuxVersionA != 0 not supported')
|
|
266
364
|
if audio_mux_version == 1:
|
|
267
|
-
tara_buffer_fullness = AacAudioRtpPacket.
|
|
365
|
+
tara_buffer_fullness = AacAudioRtpPacket.read_latm_value(reader)
|
|
268
366
|
stream_cnt = 0
|
|
269
367
|
all_streams_same_time_framing = reader.read(1)
|
|
270
368
|
num_sub_frames = reader.read(6)
|
|
@@ -275,13 +373,13 @@ class AacAudioRtpPacket:
|
|
|
275
373
|
if num_layer != 0:
|
|
276
374
|
raise core.InvalidPacketError('num_layer != 0 not supported')
|
|
277
375
|
if audio_mux_version == 0:
|
|
278
|
-
|
|
376
|
+
audio_specific_config = AacAudioRtpPacket.AudioSpecificConfig.from_bits(
|
|
279
377
|
reader
|
|
280
378
|
)
|
|
281
379
|
else:
|
|
282
|
-
asc_len = AacAudioRtpPacket.
|
|
380
|
+
asc_len = AacAudioRtpPacket.read_latm_value(reader)
|
|
283
381
|
marker = reader.bit_position
|
|
284
|
-
|
|
382
|
+
audio_specific_config = AacAudioRtpPacket.AudioSpecificConfig.from_bits(
|
|
285
383
|
reader
|
|
286
384
|
)
|
|
287
385
|
audio_specific_config_len = reader.bit_position - marker
|
|
@@ -299,36 +397,49 @@ class AacAudioRtpPacket:
|
|
|
299
397
|
f'frame_length_type {frame_length_type} not supported'
|
|
300
398
|
)
|
|
301
399
|
|
|
302
|
-
|
|
303
|
-
|
|
400
|
+
other_data_present = reader.read(1)
|
|
401
|
+
other_data_len_bits = 0
|
|
402
|
+
if other_data_present:
|
|
304
403
|
if audio_mux_version == 1:
|
|
305
|
-
|
|
404
|
+
other_data_len_bits = AacAudioRtpPacket.read_latm_value(reader)
|
|
306
405
|
else:
|
|
307
|
-
self.other_data_len_bits = 0
|
|
308
406
|
while True:
|
|
309
|
-
|
|
407
|
+
other_data_len_bits *= 256
|
|
310
408
|
other_data_len_esc = reader.read(1)
|
|
311
|
-
|
|
409
|
+
other_data_len_bits += reader.read(8)
|
|
312
410
|
if other_data_len_esc == 0:
|
|
313
411
|
break
|
|
314
412
|
crc_check_present = reader.read(1)
|
|
315
413
|
if crc_check_present:
|
|
316
414
|
crc_checksum = reader.read(8)
|
|
317
415
|
|
|
416
|
+
return cls(other_data_present, other_data_len_bits, audio_specific_config)
|
|
417
|
+
|
|
418
|
+
def to_bits(self, writer: BitWriter) -> None:
|
|
419
|
+
writer.write(0, 1) # audioMuxVersion = 0
|
|
420
|
+
writer.write(1, 1) # allStreamsSameTimeFraming = 1
|
|
421
|
+
writer.write(0, 6) # numSubFrames = 0
|
|
422
|
+
writer.write(0, 4) # numProgram = 0
|
|
423
|
+
writer.write(0, 3) # numLayer = 0
|
|
424
|
+
self.audio_specific_config.to_bits(writer)
|
|
425
|
+
writer.write(0, 3) # frameLengthType = 0
|
|
426
|
+
writer.write(0, 8) # latmBufferFullness = 0
|
|
427
|
+
writer.write(0, 1) # otherDataPresent = 0
|
|
428
|
+
writer.write(0, 1) # crcCheckPresent = 0
|
|
429
|
+
|
|
318
430
|
@dataclass
|
|
319
431
|
class AudioMuxElement:
|
|
320
|
-
payload: bytes
|
|
321
432
|
stream_mux_config: AacAudioRtpPacket.StreamMuxConfig
|
|
433
|
+
payload: bytes
|
|
322
434
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
raise core.InvalidPacketError('muxConfigPresent == 0 not supported')
|
|
326
|
-
|
|
435
|
+
@classmethod
|
|
436
|
+
def from_bits(cls, reader: BitReader) -> Self:
|
|
327
437
|
# AudioMuxElement - ISO/EIC 14496-3 Table 1.41
|
|
438
|
+
# (only supports mux_config_present=1)
|
|
328
439
|
use_same_stream_mux = reader.read(1)
|
|
329
440
|
if use_same_stream_mux:
|
|
330
441
|
raise core.InvalidPacketError('useSameStreamMux == 1 not supported')
|
|
331
|
-
|
|
442
|
+
stream_mux_config = AacAudioRtpPacket.StreamMuxConfig.from_bits(reader)
|
|
332
443
|
|
|
333
444
|
# We only support:
|
|
334
445
|
# allStreamsSameTimeFraming == 1
|
|
@@ -344,19 +455,46 @@ class AacAudioRtpPacket:
|
|
|
344
455
|
if tmp != 255:
|
|
345
456
|
break
|
|
346
457
|
|
|
347
|
-
|
|
458
|
+
payload = reader.read_bytes(mux_slot_length_bytes)
|
|
348
459
|
|
|
349
|
-
if
|
|
350
|
-
reader.skip(
|
|
460
|
+
if stream_mux_config.other_data_present:
|
|
461
|
+
reader.skip(stream_mux_config.other_data_len_bits)
|
|
351
462
|
|
|
352
463
|
# ByteAlign
|
|
353
464
|
while reader.bit_position % 8:
|
|
354
465
|
reader.read(1)
|
|
355
466
|
|
|
356
|
-
|
|
467
|
+
return cls(stream_mux_config, payload)
|
|
468
|
+
|
|
469
|
+
def to_bits(self, writer: BitWriter) -> None:
|
|
470
|
+
writer.write(0, 1) # useSameStreamMux = 0
|
|
471
|
+
self.stream_mux_config.to_bits(writer)
|
|
472
|
+
mux_slot_length_bytes = len(self.payload)
|
|
473
|
+
while mux_slot_length_bytes > 255:
|
|
474
|
+
writer.write(255, 8)
|
|
475
|
+
mux_slot_length_bytes -= 255
|
|
476
|
+
writer.write(mux_slot_length_bytes, 8)
|
|
477
|
+
if mux_slot_length_bytes == 255:
|
|
478
|
+
writer.write(0, 8)
|
|
479
|
+
writer.write_bytes(self.payload)
|
|
480
|
+
|
|
481
|
+
@classmethod
|
|
482
|
+
def from_bytes(cls, data: bytes) -> Self:
|
|
357
483
|
# Parse the bit stream
|
|
358
484
|
reader = BitReader(data)
|
|
359
|
-
|
|
485
|
+
return cls(cls.AudioMuxElement.from_bits(reader))
|
|
486
|
+
|
|
487
|
+
@classmethod
|
|
488
|
+
def for_simple_aac(
|
|
489
|
+
cls, sampling_frequency: int, channel_configuration: int, payload: bytes
|
|
490
|
+
) -> Self:
|
|
491
|
+
audio_specific_config = cls.AudioSpecificConfig.for_simple_aac(
|
|
492
|
+
2, sampling_frequency, channel_configuration
|
|
493
|
+
)
|
|
494
|
+
stream_mux_config = cls.StreamMuxConfig(0, 0, audio_specific_config)
|
|
495
|
+
audio_mux_element = cls.AudioMuxElement(stream_mux_config, payload)
|
|
496
|
+
|
|
497
|
+
return cls(audio_mux_element)
|
|
360
498
|
|
|
361
499
|
def to_adts(self):
|
|
362
500
|
# pylint: disable=line-too-long
|
|
@@ -383,3 +521,11 @@ class AacAudioRtpPacket:
|
|
|
383
521
|
)
|
|
384
522
|
+ self.audio_mux_element.payload
|
|
385
523
|
)
|
|
524
|
+
|
|
525
|
+
def __init__(self, audio_mux_element: AudioMuxElement) -> None:
|
|
526
|
+
self.audio_mux_element = audio_mux_element
|
|
527
|
+
|
|
528
|
+
def __bytes__(self) -> bytes:
|
|
529
|
+
writer = BitWriter()
|
|
530
|
+
self.audio_mux_element.to_bits(writer)
|
|
531
|
+
return bytes(writer)
|
bumble/decoder.py
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
from typing import Union
|
|
16
|
+
|
|
15
17
|
# -----------------------------------------------------------------------------
|
|
16
18
|
# Constants
|
|
17
19
|
# -----------------------------------------------------------------------------
|
|
@@ -149,7 +151,7 @@ QMF_COEFFS = [3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11]
|
|
|
149
151
|
# -----------------------------------------------------------------------------
|
|
150
152
|
# Classes
|
|
151
153
|
# -----------------------------------------------------------------------------
|
|
152
|
-
class G722Decoder
|
|
154
|
+
class G722Decoder:
|
|
153
155
|
"""G.722 decoder with bitrate 64kbit/s.
|
|
154
156
|
|
|
155
157
|
For the Blocks in the sub-band decoders, please refer to the G.722
|
|
@@ -157,7 +159,7 @@ class G722Decoder(object):
|
|
|
157
159
|
https://www.itu.int/rec/T-REC-G.722-201209-I
|
|
158
160
|
"""
|
|
159
161
|
|
|
160
|
-
def __init__(self):
|
|
162
|
+
def __init__(self) -> None:
|
|
161
163
|
self._x = [0] * 24
|
|
162
164
|
self._band = [Band(), Band()]
|
|
163
165
|
# The initial value in BLOCK 3L
|
|
@@ -165,12 +167,12 @@ class G722Decoder(object):
|
|
|
165
167
|
# The initial value in BLOCK 3H
|
|
166
168
|
self._band[1].det = 8
|
|
167
169
|
|
|
168
|
-
def decode_frame(self, encoded_data) -> bytearray:
|
|
170
|
+
def decode_frame(self, encoded_data: Union[bytes, bytearray]) -> bytearray:
|
|
169
171
|
result_array = bytearray(len(encoded_data) * 4)
|
|
170
172
|
self.g722_decode(result_array, encoded_data)
|
|
171
173
|
return result_array
|
|
172
174
|
|
|
173
|
-
def g722_decode(self, result_array, encoded_data) -> int:
|
|
175
|
+
def g722_decode(self, result_array, encoded_data: Union[bytes, bytearray]) -> int:
|
|
174
176
|
"""Decode the data frame using g722 decoder."""
|
|
175
177
|
result_length = 0
|
|
176
178
|
|
|
@@ -198,14 +200,16 @@ class G722Decoder(object):
|
|
|
198
200
|
|
|
199
201
|
return result_length
|
|
200
202
|
|
|
201
|
-
def update_decoded_result(
|
|
203
|
+
def update_decoded_result(
|
|
204
|
+
self, xout: int, byte_length: int, byte_array: bytearray
|
|
205
|
+
) -> int:
|
|
202
206
|
result = (int)(xout >> 11)
|
|
203
207
|
bytes_result = result.to_bytes(2, 'little', signed=True)
|
|
204
208
|
byte_array[byte_length] = bytes_result[0]
|
|
205
209
|
byte_array[byte_length + 1] = bytes_result[1]
|
|
206
210
|
return byte_length + 2
|
|
207
211
|
|
|
208
|
-
def lower_sub_band_decoder(self, lower_bits) -> int:
|
|
212
|
+
def lower_sub_band_decoder(self, lower_bits: int) -> int:
|
|
209
213
|
"""Lower sub-band decoder for last six bits."""
|
|
210
214
|
|
|
211
215
|
# Block 5L
|
|
@@ -258,7 +262,7 @@ class G722Decoder(object):
|
|
|
258
262
|
|
|
259
263
|
return rlow
|
|
260
264
|
|
|
261
|
-
def higher_sub_band_decoder(self, higher_bits) -> int:
|
|
265
|
+
def higher_sub_band_decoder(self, higher_bits: int) -> int:
|
|
262
266
|
"""Higher sub-band decoder for first two bits."""
|
|
263
267
|
|
|
264
268
|
# Block 2H
|
|
@@ -306,14 +310,14 @@ class G722Decoder(object):
|
|
|
306
310
|
|
|
307
311
|
|
|
308
312
|
# -----------------------------------------------------------------------------
|
|
309
|
-
class Band
|
|
310
|
-
"""Structure for G722 decode
|
|
313
|
+
class Band:
|
|
314
|
+
"""Structure for G722 decode processing."""
|
|
311
315
|
|
|
312
316
|
s: int = 0
|
|
313
317
|
nb: int = 0
|
|
314
318
|
det: int = 0
|
|
315
319
|
|
|
316
|
-
def __init__(self):
|
|
320
|
+
def __init__(self) -> None:
|
|
317
321
|
self._sp = 0
|
|
318
322
|
self._sz = 0
|
|
319
323
|
self._r = [0] * 3
|
bumble/device.py
CHANGED
|
@@ -1571,14 +1571,22 @@ class Connection(CompositeEventEmitter):
|
|
|
1571
1571
|
raise
|
|
1572
1572
|
|
|
1573
1573
|
def __str__(self):
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1574
|
+
if self.transport == BT_LE_TRANSPORT:
|
|
1575
|
+
return (
|
|
1576
|
+
f'Connection(transport=LE, handle=0x{self.handle:04X}, '
|
|
1577
|
+
f'role={self.role_name}, '
|
|
1578
|
+
f'self_address={self.self_address}, '
|
|
1579
|
+
f'self_resolvable_address={self.self_resolvable_address}, '
|
|
1580
|
+
f'peer_address={self.peer_address}, '
|
|
1581
|
+
f'peer_resolvable_address={self.peer_resolvable_address})'
|
|
1582
|
+
)
|
|
1583
|
+
else:
|
|
1584
|
+
return (
|
|
1585
|
+
f'Connection(transport=BR/EDR, handle=0x{self.handle:04X}, '
|
|
1586
|
+
f'role={self.role_name}, '
|
|
1587
|
+
f'self_address={self.self_address}, '
|
|
1588
|
+
f'peer_address={self.peer_address})'
|
|
1589
|
+
)
|
|
1582
1590
|
|
|
1583
1591
|
|
|
1584
1592
|
# -----------------------------------------------------------------------------
|
|
@@ -1766,9 +1774,9 @@ device_host_event_handlers: List[str] = []
|
|
|
1766
1774
|
# -----------------------------------------------------------------------------
|
|
1767
1775
|
class Device(CompositeEventEmitter):
|
|
1768
1776
|
# Incomplete list of fields.
|
|
1769
|
-
random_address: Address # Random address that may change
|
|
1770
|
-
public_address: Address # Public address (
|
|
1771
|
-
static_address: Address # Random address that
|
|
1777
|
+
random_address: Address # Random private address that may change periodically
|
|
1778
|
+
public_address: Address # Public address that is globally unique (from controller)
|
|
1779
|
+
static_address: Address # Random static address that does not change once set
|
|
1772
1780
|
classic_enabled: bool
|
|
1773
1781
|
name: str
|
|
1774
1782
|
class_of_device: int
|
bumble/drivers/rtk.py
CHANGED
|
@@ -301,6 +301,8 @@ class Driver(common.Driver):
|
|
|
301
301
|
fw_name: str = ""
|
|
302
302
|
config_name: str = ""
|
|
303
303
|
|
|
304
|
+
POST_RESET_DELAY: float = 0.2
|
|
305
|
+
|
|
304
306
|
DRIVER_INFOS = [
|
|
305
307
|
# 8723A
|
|
306
308
|
DriverInfo(
|
|
@@ -495,12 +497,24 @@ class Driver(common.Driver):
|
|
|
495
497
|
|
|
496
498
|
@classmethod
|
|
497
499
|
async def driver_info_for_host(cls, host):
|
|
498
|
-
|
|
499
|
-
|
|
500
|
+
try:
|
|
501
|
+
await host.send_command(
|
|
502
|
+
HCI_Reset_Command(),
|
|
503
|
+
check_result=True,
|
|
504
|
+
response_timeout=cls.POST_RESET_DELAY,
|
|
505
|
+
)
|
|
506
|
+
host.ready = True # Needed to let the host know the controller is ready.
|
|
507
|
+
except asyncio.exceptions.TimeoutError:
|
|
508
|
+
logger.warning("timeout waiting for hci reset, retrying")
|
|
509
|
+
await host.send_command(HCI_Reset_Command(), check_result=True)
|
|
510
|
+
host.ready = True
|
|
511
|
+
|
|
512
|
+
command = HCI_Read_Local_Version_Information_Command()
|
|
513
|
+
response = await host.send_command(command, check_result=True)
|
|
514
|
+
if response.command_opcode != command.op_code:
|
|
515
|
+
logger.error("failed to probe local version information")
|
|
516
|
+
return None
|
|
500
517
|
|
|
501
|
-
response = await host.send_command(
|
|
502
|
-
HCI_Read_Local_Version_Information_Command(), check_result=True
|
|
503
|
-
)
|
|
504
518
|
local_version = response.return_parameters
|
|
505
519
|
|
|
506
520
|
logger.debug(
|
bumble/gatt.py
CHANGED
|
@@ -238,22 +238,22 @@ GATT_SEARCH_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x
|
|
|
238
238
|
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Content Control Id')
|
|
239
239
|
|
|
240
240
|
# Telephone Bearer Service (TBS)
|
|
241
|
-
GATT_BEARER_PROVIDER_NAME_CHARACTERISTIC = UUID.from_16_bits(
|
|
242
|
-
GATT_BEARER_UCI_CHARACTERISTIC = UUID.from_16_bits(
|
|
243
|
-
GATT_BEARER_TECHNOLOGY_CHARACTERISTIC = UUID.from_16_bits(
|
|
244
|
-
GATT_BEARER_URI_SCHEMES_SUPPORTED_LIST_CHARACTERISTIC = UUID.from_16_bits(
|
|
245
|
-
GATT_BEARER_SIGNAL_STRENGTH_CHARACTERISTIC = UUID.from_16_bits(
|
|
246
|
-
GATT_BEARER_SIGNAL_STRENGTH_REPORTING_INTERVAL_CHARACTERISTIC = UUID.from_16_bits(
|
|
247
|
-
GATT_BEARER_LIST_CURRENT_CALLS_CHARACTERISTIC = UUID.from_16_bits(
|
|
248
|
-
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(
|
|
249
|
-
GATT_STATUS_FLAGS_CHARACTERISTIC = UUID.from_16_bits(
|
|
250
|
-
GATT_INCOMING_CALL_TARGET_BEARER_URI_CHARACTERISTIC = UUID.from_16_bits(
|
|
251
|
-
GATT_CALL_STATE_CHARACTERISTIC = UUID.from_16_bits(
|
|
252
|
-
GATT_CALL_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(
|
|
253
|
-
GATT_CALL_CONTROL_POINT_OPTIONAL_OPCODES_CHARACTERISTIC = UUID.from_16_bits(
|
|
254
|
-
GATT_TERMINATION_REASON_CHARACTERISTIC = UUID.from_16_bits(
|
|
255
|
-
GATT_INCOMING_CALL_CHARACTERISTIC = UUID.from_16_bits(
|
|
256
|
-
GATT_CALL_FRIENDLY_NAME_CHARACTERISTIC = UUID.from_16_bits(
|
|
241
|
+
GATT_BEARER_PROVIDER_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BB3, 'Bearer Provider Name')
|
|
242
|
+
GATT_BEARER_UCI_CHARACTERISTIC = UUID.from_16_bits(0x2BB4, 'Bearer UCI')
|
|
243
|
+
GATT_BEARER_TECHNOLOGY_CHARACTERISTIC = UUID.from_16_bits(0x2BB5, 'Bearer Technology')
|
|
244
|
+
GATT_BEARER_URI_SCHEMES_SUPPORTED_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2BB6, 'Bearer URI Schemes Supported List')
|
|
245
|
+
GATT_BEARER_SIGNAL_STRENGTH_CHARACTERISTIC = UUID.from_16_bits(0x2BB7, 'Bearer Signal Strength')
|
|
246
|
+
GATT_BEARER_SIGNAL_STRENGTH_REPORTING_INTERVAL_CHARACTERISTIC = UUID.from_16_bits(0x2BB8, 'Bearer Signal Strength Reporting Interval')
|
|
247
|
+
GATT_BEARER_LIST_CURRENT_CALLS_CHARACTERISTIC = UUID.from_16_bits(0x2BB9, 'Bearer List Current Calls')
|
|
248
|
+
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Content Control ID')
|
|
249
|
+
GATT_STATUS_FLAGS_CHARACTERISTIC = UUID.from_16_bits(0x2BBB, 'Status Flags')
|
|
250
|
+
GATT_INCOMING_CALL_TARGET_BEARER_URI_CHARACTERISTIC = UUID.from_16_bits(0x2BBC, 'Incoming Call Target Bearer URI')
|
|
251
|
+
GATT_CALL_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BBD, 'Call State')
|
|
252
|
+
GATT_CALL_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BBE, 'Call Control Point')
|
|
253
|
+
GATT_CALL_CONTROL_POINT_OPTIONAL_OPCODES_CHARACTERISTIC = UUID.from_16_bits(0x2BBF, 'Call Control Point Optional Opcodes')
|
|
254
|
+
GATT_TERMINATION_REASON_CHARACTERISTIC = UUID.from_16_bits(0x2BC0, 'Termination Reason')
|
|
255
|
+
GATT_INCOMING_CALL_CHARACTERISTIC = UUID.from_16_bits(0x2BC1, 'Incoming Call')
|
|
256
|
+
GATT_CALL_FRIENDLY_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BC2, 'Call Friendly Name')
|
|
257
257
|
|
|
258
258
|
# Microphone Control Service (MICS)
|
|
259
259
|
GATT_MUTE_CHARACTERISTIC = UUID.from_16_bits(0x2BC3, 'Mute')
|
|
@@ -275,6 +275,11 @@ GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCC, 'Sou
|
|
|
275
275
|
GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCD, 'Available Audio Contexts')
|
|
276
276
|
GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCE, 'Supported Audio Contexts')
|
|
277
277
|
|
|
278
|
+
# Hearing Access Service
|
|
279
|
+
GATT_HEARING_AID_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2BDA, 'Hearing Aid Features')
|
|
280
|
+
GATT_HEARING_AID_PRESET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BDB, 'Hearing Aid Preset Control Point')
|
|
281
|
+
GATT_ACTIVE_PRESET_INDEX_CHARACTERISTIC = UUID.from_16_bits(0x2BDC, 'Active Preset Index')
|
|
282
|
+
|
|
278
283
|
# ASHA Service
|
|
279
284
|
GATT_ASHA_SERVICE = UUID.from_16_bits(0xFDF0, 'Audio Streaming for Hearing Aid')
|
|
280
285
|
GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC = UUID('6333651e-c481-4a3e-9169-7c902aad37bb', 'ReadOnlyProperties')
|
|
@@ -340,7 +345,7 @@ class Service(Attribute):
|
|
|
340
345
|
uuid: Union[str, UUID],
|
|
341
346
|
characteristics: List[Characteristic],
|
|
342
347
|
primary=True,
|
|
343
|
-
included_services:
|
|
348
|
+
included_services: Iterable[Service] = (),
|
|
344
349
|
) -> None:
|
|
345
350
|
# Convert the uuid to a UUID object if it isn't already
|
|
346
351
|
if isinstance(uuid, str):
|
|
@@ -356,7 +361,7 @@ class Service(Attribute):
|
|
|
356
361
|
uuid.to_pdu_bytes(),
|
|
357
362
|
)
|
|
358
363
|
self.uuid = uuid
|
|
359
|
-
self.included_services = included_services
|
|
364
|
+
self.included_services = list(included_services)
|
|
360
365
|
self.characteristics = characteristics[:]
|
|
361
366
|
self.primary = primary
|
|
362
367
|
|
|
@@ -390,7 +395,7 @@ class TemplateService(Service):
|
|
|
390
395
|
self,
|
|
391
396
|
characteristics: List[Characteristic],
|
|
392
397
|
primary: bool = True,
|
|
393
|
-
included_services:
|
|
398
|
+
included_services: Iterable[Service] = (),
|
|
394
399
|
) -> None:
|
|
395
400
|
super().__init__(self.UUID, characteristics, primary, included_services)
|
|
396
401
|
|