diff --git a/auracast/announcement_48_10_96000_en_stereo.wav b/auracast/announcement_48_10_96000_en_stereo.wav new file mode 100644 index 0000000..7304827 Binary files /dev/null and b/auracast/announcement_48_10_96000_en_stereo.wav differ diff --git a/auracast/multicast.py b/auracast/multicast.py index 0fed9ee..be02359 100644 --- a/auracast/multicast.py +++ b/auracast/multicast.py @@ -43,31 +43,14 @@ from bumble.profiles import bass import bumble.device import bumble.transport import bumble.utils -from bumble.device import Host, BIGInfoAdvertisement +from bumble.device import Host, BIGInfoAdvertisement, AdvertisingChannelMap import auracast_config -def modified_on_hci_number_of_completed_packets_event(self, event): - for connection_handle, num_completed_packets in zip( - event.connection_handles, event.num_completed_packets - ): - if connection := self.connections.get(connection_handle): - connection.acl_packet_queue.on_packets_completed(num_completed_packets) - elif connection_handle not in itertools.chain( - self.cis_links.keys(), - self.sco_links.keys(), - itertools.chain.from_iterable(self.bigs.values()), - ): - logger.warning( - 'received packet completion event for unknown handle ' - f'0x{connection_handle:04X}' - ) - self.emit('hci_number_of_completed_packets_event', event) - -Host.on_hci_number_of_completed_packets_event = modified_on_hci_number_of_completed_packets_event - - +ADVERTISING_CHANNELS = ( + AdvertisingChannelMap.CHANNEL_37 ,AdvertisingChannelMap.CHANNEL_38 ,AdvertisingChannelMap.CHANNEL_39 + ) # ----------------------------------------------------------------------------- # Logging @@ -113,8 +96,6 @@ def run_async(async_command: Coroutine) -> None: ) ADVERTISING_SLOWDOWN_FACTOR=1 -PKGS_COUNT = 0 - async def run_broadcast( global_config : auracast_config.AuracastGlobalConfig, big_config: List[auracast_config.AuracastBigConfig] @@ -151,6 +132,7 @@ async def run_broadcast( bigs.update() bigs[f'big{i}']['frames'] = frames + bigs[f'big{i}']['frames_iterator'] = itertools.cycle(frames) # Config advertising set bigs[f'big{i}']['basic_audio_announcement'] = bap.BasicAudioAnnouncement( @@ -201,6 +183,7 @@ async def run_broadcast( primary_advertising_phy=hci.Phy.LE_1M, # 2m phy config throws error - because for primary advertising channels, 1mbit is only supported secondary_advertising_phy=hci.Phy.LE_2M, # this is the secondary advertising beeing send on non advertising channels (extendend advertising) #advertising_tx_power= # tx power in dbm (max 20) + secondary_advertising_max_skip=10, ), advertising_data=( bigs[f'big{i}']['broadcast_audio_announcement'].get_advertising_data() @@ -230,7 +213,7 @@ async def run_broadcast( frame_enable = 1 big = await device.create_big( - bigs[f'big{i}']['advertising_set'] , + bigs[f'big{i}']['advertising_set'], parameters=bumble.device.BigParameters( num_bis=1, sdu_interval=global_config.qos_config.iso_int_multiple_10ms*10000, # Is the same as iso interval @@ -245,54 +228,54 @@ async def run_broadcast( ) bigs[f'big{i}']['big'] = big + iso_queues = [ + bumble.device.IsoPacketStream(big.bis_links[0], 64), + #bumble.device.IsoPacketStream(big.bis_links[1], 64), # right channel + ] + logging.info('Setup ISO Data Path') - for bis_link in big.bis_links: - await bis_link.setup_data_path( - direction=bis_link.Direction.HOST_TO_CONTROLLER + for queue in iso_queues: + await queue.iso_link.setup_data_path( + direction=queue.iso_link.Direction.HOST_TO_CONTROLLER ) - bigs[f'big{i}']['frames_iterator'] = itertools.cycle(frames) + bigs[f'big{i}']['iso_queues'] = iso_queues logging.debug(f'big{i} parameters are:') logging.debug('%s', pprint.pformat(vars(big))) logging.debug(f'Finished setup of big{i}.') - await asyncio.sleep(3) # Wait for advertising to set up - - LOOP_FINITE_PKGS = -1 # set -1 to loop infinitely - - def on_packet_complete(event): - global PKGS_COUNT - PKGS_COUNT = PKGS_COUNT + 1 - - event_handle = event.connection_handles[0] - - if PKGS_COUNT < LOOP_FINITE_PKGS or LOOP_FINITE_PKGS == -1: - event_found = False - for big in bigs.values(): - if big['big'].bis_links[0].handle == event_handle: - frame = b'' - for _ in range(0, global_config.qos_config.iso_int_multiple_10ms): - # loop is nececarry if iso interval is not frame interval of codec - frame += next(big['frames_iterator']) - big['big'].bis_links[0].write(frame) - - event_found = True - if not event_found: - logging.warning('unknown event on packet complete with handle: %s', event_handle) - else: - logging.warning("Stopping iso loop") + await asyncio.sleep(i+1) # Wait for advertising to set up logging.info("Broadcasting...") - device.host.on('hci_number_of_completed_packets_event', on_packet_complete) - # Send the first packet for each big, to get the event loop running + def on_flow(): + data_packet_queue = iso_queues[0].data_packet_queue + print( + f'\rPACKETS: pending={data_packet_queue.pending}, ' + f'queued={data_packet_queue.queued}, ' + f'completed={data_packet_queue.completed}', + end='', + ) + + bigs[f'big{0}']['iso_queues'][0].data_packet_queue.on('flow', on_flow) + async def streamer(queue, iterator): + while True: + frame = next(iterator) + await queue[0].write(frame) + + streams = [] for big in bigs.values(): - frame = next(big['frames_iterator']) - big['big'].bis_links[0].write(frame) + streams.append( + asyncio.create_task( + streamer(big['iso_queues'], big['frames_iterator']) + ) + ) + + await asyncio.wait(streams) + print("Done.") + - while True: - await asyncio.sleep(1) # ----------------------------------------------------------------------------- # Main @@ -312,7 +295,7 @@ if __name__ == "__main__": logging.basicConfig( level=logging.INFO, format='%(module)s.py:%(lineno)d %(levelname)s: %(message)s' -) + ) global_conf = auracast_config.global_base_config @@ -322,22 +305,24 @@ if __name__ == "__main__": #global_conf.transport='serial:/dev/serial/by-id/usb-SEGGER_J-Link_001057705357-if02,1000000,rtscts' # transport for nrf54l15dk - global_conf.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if02,115200,rtscts' #nrf52dongle usb cdc uart + global_conf.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,115200,rtscts' #nrf52dongle hci_uart usb cdc - # TODO use nrf52dongle with hci over usb + # global_conf.transport='usb:2fe3:000b' #nrf52dongle hci_usb #TODO: iso packet over usb seems not to be supported - # TODO: How can we use other iso interval than 10ms ?(medium or low rel) ? - nrf53audio receiver repports I2S tx underrun #global_conf.qos_config = auracast_config.qos_config_mono_medium_rel - global_conf.qos_config = auracast_config.qos_config_mono_high_rel bigs = [ auracast_config.broadcast_de, auracast_config.broadcast_en, auracast_config.broadcast_fr, - auracast_config.broadcast_es, - # auracast_config.broadcast_it, #TODO: with more than three broadcasts, not advertising at all is present, regardless the adv. interval - ] # TODO: more than three broadcasters lead to some buffer overflow on hci side- try to increase some buffer lengths + #auracast_config.broadcast_es, + #auracast_config.broadcast_it, + ] + # TODO: with more than three broadcasters no advertising (no primary channels is present anymore) - is this a number of advertising sets constraint? + # TODO: solve the advertising problem and 5 broadcaster may be possible + + global_conf.auracast_sampling_rate_hz = 16000 global_conf.octets_per_frame = 40 # 32kbps@16kHz @@ -345,8 +330,8 @@ if __name__ == "__main__": # 16kHz works reliably with 3 streams # use thread usage debugger on controller to check actual cpu load - not much load + # TODO: How can we use other iso interval than 10ms ?(medium or low rel) ? - nrf53audio receiver repports I2S tx underrun # TODO; I dont think hci is really the bottleneck. probably limited airtime is the problem. Analyze this somehow. - # TODO: upgrade to latests bumble version broadcast( global_conf, diff --git a/auracast/run_btmon.sh b/auracast/run_btmon_rtt.sh similarity index 100% rename from auracast/run_btmon.sh rename to auracast/run_btmon_rtt.sh diff --git a/pyproject.toml b/pyproject.toml index 52bd2e9..4681c2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ version = "0.0.1" requires-python = ">=3.8" dependencies = [ - "bumble @ git+ssh://git@hinterwaldner.duckdns.org:222/auracaster/bumble.git@af466c29704d4d47b291e697b1990621c0b7d86b", - "lc3 @ git+https://github.com/google/liblc3.git@7558637303106c7ea971e7bb8cedf379d3e08bcc", + "bumble @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble.git@3f6f0362704f51cba7c8f4f66bcd6117d2fd7fc1", + "lc3 @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/liblc3.git@7558637303106c7ea971e7bb8cedf379d3e08bcc", ] [project.optional-dependencies]