Implement basic support for multiple broadcasters
This commit is contained in:
@@ -121,115 +121,124 @@ async def run_broadcast(
|
|||||||
logger.error(color('Periodic advertising not supported', 'red'))
|
logger.error(color('Periodic advertising not supported', 'red'))
|
||||||
return
|
return
|
||||||
|
|
||||||
with wave.open(big_config[0].broacast_wav_file_path, 'rb') as wav:
|
|
||||||
logger.info('Encoding wav file into lc3...')
|
|
||||||
logger.info('Frame rate of .wav file is: %s', wav.getframerate())
|
|
||||||
encoder = lc3.Encoder(
|
|
||||||
frame_duration_us=global_config.frame_duration_us,
|
|
||||||
sample_rate_hz=global_config.auracast_sampling_rate_khz,
|
|
||||||
num_channels=1,
|
|
||||||
input_sample_rate_hz=wav.getframerate(),
|
|
||||||
)
|
|
||||||
frames = list[bytes]()
|
|
||||||
while pcm := wav.readframes(encoder.get_frame_samples()):
|
|
||||||
frames.append(
|
|
||||||
encoder.encode(pcm, num_bytes=global_config.octets_per_frame, bit_depth=wav.getsampwidth() * 8)
|
|
||||||
)
|
|
||||||
del encoder
|
|
||||||
print('Encoding complete.')
|
|
||||||
|
|
||||||
# Config advertising set
|
|
||||||
bap_sampling_freq = getattr(bap.SamplingFrequency, f"FREQ_{global_config.auracast_sampling_rate_khz}")
|
bap_sampling_freq = getattr(bap.SamplingFrequency, f"FREQ_{global_config.auracast_sampling_rate_khz}")
|
||||||
basic_audio_announcement = bap.BasicAudioAnnouncement(
|
bigs = {}
|
||||||
presentation_delay=global_config.presentation_delay_us,
|
for i, conf in enumerate(big_config):
|
||||||
subgroups=[
|
bigs[f'big{i}'] = {}
|
||||||
bap.BasicAudioAnnouncement.Subgroup(
|
with wave.open(conf.broacast_wav_file_path, 'rb') as wav:
|
||||||
codec_id=hci.CodingFormat(codec_id=hci.CodecID.LC3),
|
logger.info('Encoding wav file into lc3...')
|
||||||
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
logger.info('Frame rate of .wav file is: %s', wav.getframerate())
|
||||||
sampling_frequency=bap_sampling_freq,
|
encoder = lc3.Encoder(
|
||||||
frame_duration=bap.FrameDuration.DURATION_10000_US,
|
frame_duration_us=global_config.frame_duration_us,
|
||||||
octets_per_codec_frame=global_config.octets_per_frame,
|
sample_rate_hz=global_config.auracast_sampling_rate_khz,
|
||||||
),
|
num_channels=1,
|
||||||
metadata=le_audio.Metadata(
|
input_sample_rate_hz=wav.getframerate(),
|
||||||
[
|
|
||||||
le_audio.Metadata.Entry(
|
|
||||||
tag=le_audio.Metadata.Tag.LANGUAGE, data=big_config[0].broadcast_language.encode()
|
|
||||||
),
|
|
||||||
le_audio.Metadata.Entry(
|
|
||||||
tag=le_audio.Metadata.Tag.PROGRAM_INFO, data=big_config[0].broadcast_program_info.encode()
|
|
||||||
),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
bis=[
|
|
||||||
bap.BasicAudioAnnouncement.BIS(
|
|
||||||
index=1,
|
|
||||||
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
|
||||||
audio_channel_allocation=bap.AudioLocation.FRONT_LEFT
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
],
|
frames = list[bytes]()
|
||||||
)
|
while pcm := wav.readframes(encoder.get_frame_samples()):
|
||||||
logging.info('Setup Advertising')
|
frames.append(
|
||||||
broadcast_audio_announcement = bap.BroadcastAudioAnnouncement(big_config[0].broadcast_id)
|
encoder.encode(pcm, num_bytes=global_config.octets_per_frame, bit_depth=wav.getsampwidth() * 8)
|
||||||
advertising_set0 = await device.create_advertising_set(
|
|
||||||
advertising_parameters=bumble.device.AdvertisingParameters(
|
|
||||||
advertising_event_properties=bumble.device.AdvertisingEventProperties(
|
|
||||||
is_connectable=False
|
|
||||||
),
|
|
||||||
primary_advertising_interval_min=100,
|
|
||||||
primary_advertising_interval_max=200,
|
|
||||||
advertising_sid=0
|
|
||||||
# TODO: use 2mbit phy
|
|
||||||
),
|
|
||||||
advertising_data=(
|
|
||||||
broadcast_audio_announcement.get_advertising_data()
|
|
||||||
+ bytes(
|
|
||||||
core.AdvertisingData(
|
|
||||||
[(core.AdvertisingData.BROADCAST_NAME, big_config[0].broadcast_name.encode())]
|
|
||||||
)
|
)
|
||||||
)
|
del encoder
|
||||||
),
|
logger.info('Encoding complete.')
|
||||||
periodic_advertising_parameters=bumble.device.PeriodicAdvertisingParameters(
|
|
||||||
periodic_advertising_interval_min=80,
|
|
||||||
periodic_advertising_interval_max=160,
|
|
||||||
),
|
|
||||||
periodic_advertising_data=basic_audio_announcement.get_advertising_data(),
|
|
||||||
auto_restart=True,
|
|
||||||
auto_start=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
logging.info('Start Periodic Advertising')
|
bigs.update()
|
||||||
await advertising_set0.start_periodic()
|
bigs[f'big{i}']['frames'] = frames
|
||||||
|
|
||||||
logging.info('Setup BIG')
|
# Config advertising set
|
||||||
big0 = await device.create_big(
|
bigs[f'big{i}']['basic_audio_announcement'] = bap.BasicAudioAnnouncement(
|
||||||
advertising_set0,
|
presentation_delay=global_config.presentation_delay_us,
|
||||||
parameters=bumble.device.BigParameters(
|
subgroups=[
|
||||||
num_bis=1,
|
bap.BasicAudioAnnouncement.Subgroup(
|
||||||
sdu_interval=global_config.frame_duration_us,
|
codec_id=hci.CodingFormat(codec_id=hci.CodecID.LC3),
|
||||||
max_sdu=global_config.octets_per_frame, # is this octets per frame ?
|
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
||||||
max_transport_latency=65,
|
sampling_frequency=bap_sampling_freq,
|
||||||
rtn=4,
|
frame_duration=bap.FrameDuration.DURATION_10000_US,
|
||||||
broadcast_code=(
|
octets_per_codec_frame=global_config.octets_per_frame,
|
||||||
bytes.fromhex(big_config[0].broadcast_code) if big_config[0].broadcast_code else None
|
),
|
||||||
),
|
metadata=le_audio.Metadata(
|
||||||
),
|
[
|
||||||
)
|
le_audio.Metadata.Entry(
|
||||||
|
tag=le_audio.Metadata.Tag.LANGUAGE, data=conf.broadcast_language.encode()
|
||||||
logging.info('Setup ISO Data Path')
|
),
|
||||||
for bis_link in big0.bis_links:
|
le_audio.Metadata.Entry(
|
||||||
await bis_link.setup_data_path(
|
tag=le_audio.Metadata.Tag.PROGRAM_INFO, data=conf.broadcast_program_info.encode()
|
||||||
direction=bis_link.Direction.HOST_TO_CONTROLLER
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
bis=[
|
||||||
|
bap.BasicAudioAnnouncement.BIS(
|
||||||
|
index=1,
|
||||||
|
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
||||||
|
audio_channel_allocation=bap.AudioLocation.FRONT_LEFT
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
logging.info('Setup Advertising')
|
||||||
|
bigs[f'big{i}']['broadcast_audio_announcement'] = bap.BroadcastAudioAnnouncement(conf.broadcast_id)
|
||||||
|
advertising_set = await device.create_advertising_set(
|
||||||
|
advertising_parameters=bumble.device.AdvertisingParameters(
|
||||||
|
advertising_event_properties=bumble.device.AdvertisingEventProperties(
|
||||||
|
is_connectable=False
|
||||||
|
),
|
||||||
|
primary_advertising_interval_min=100,
|
||||||
|
primary_advertising_interval_max=200,
|
||||||
|
advertising_sid=0
|
||||||
|
# TODO: use 2mbit phy
|
||||||
|
),
|
||||||
|
advertising_data=(
|
||||||
|
bigs[f'big{i}']['broadcast_audio_announcement'].get_advertising_data()
|
||||||
|
+ bytes(
|
||||||
|
core.AdvertisingData(
|
||||||
|
[(core.AdvertisingData.BROADCAST_NAME, conf.broadcast_name.encode())]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
periodic_advertising_parameters=bumble.device.PeriodicAdvertisingParameters(
|
||||||
|
periodic_advertising_interval_min=80,
|
||||||
|
periodic_advertising_interval_max=160,
|
||||||
|
),
|
||||||
|
periodic_advertising_data=bigs[f'big{i}']['basic_audio_announcement'].get_advertising_data(),
|
||||||
|
auto_restart=True,
|
||||||
|
auto_start=True,
|
||||||
|
)
|
||||||
|
bigs[f'big{i}']['advertising_set'] = advertising_set
|
||||||
|
|
||||||
|
logging.info('Start Periodic Advertising')
|
||||||
|
await bigs[f'big{i}']['advertising_set'].start_periodic()
|
||||||
|
|
||||||
|
logging.info('Setup BIG')
|
||||||
|
big = await device.create_big(
|
||||||
|
bigs[f'big{i}']['advertising_set'] ,
|
||||||
|
parameters=bumble.device.BigParameters(
|
||||||
|
num_bis=1,
|
||||||
|
sdu_interval=global_config.frame_duration_us,
|
||||||
|
max_sdu=global_config.octets_per_frame, # is this octets per frame ?
|
||||||
|
max_transport_latency=65,
|
||||||
|
rtn=4,
|
||||||
|
broadcast_code=(
|
||||||
|
bytes.fromhex(conf.broadcast_code) if conf.broadcast_code else None
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
bigs[f'big{i}']['big'] = big
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
bigs[f'big{i}']['frames_iterator'] = itertools.cycle(frames)
|
||||||
|
|
||||||
frames_iterator = itertools.cycle(frames)
|
|
||||||
logging.info("Broadcasting...")
|
logging.info("Broadcasting...")
|
||||||
|
|
||||||
def on_packet_complete(event):
|
def on_packet_complete(event):
|
||||||
frame = next(frames_iterator)
|
for val in bigs.values():
|
||||||
big0.bis_links[0].write(frame)
|
frame = next(val['frames_iterator'] )
|
||||||
|
val['big'].bis_links[0].write(frame)
|
||||||
|
|
||||||
device.host.on('hci_number_of_completed_packets_event', on_packet_complete)
|
device.host.on('hci_number_of_completed_packets_event', on_packet_complete)
|
||||||
|
|
||||||
@@ -259,7 +268,8 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
global_conf = auracast_config.global_base_config
|
global_conf = auracast_config.global_base_config
|
||||||
bigs = [
|
bigs = [
|
||||||
auracast_config.broadcast_de
|
#auracast_config.broadcast_de,
|
||||||
|
auracast_config.broadcast_en
|
||||||
]
|
]
|
||||||
global_conf.octets_per_frame=60# 48kbps@24kHz
|
global_conf.octets_per_frame=60# 48kbps@24kHz
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user