refractoring
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -4,7 +4,6 @@ coverage/ # Coverage results after running tests with coverage tools
|
||||
.dist-info/ # Wheel metadata (use poetry build to handle this)
|
||||
*.egg-info/ # Egg info directory (automatically created by pip)
|
||||
auracast.egg-info/
|
||||
.vscode/ # IDE configuration (edit in VS Code)
|
||||
|
||||
# Ignore these file types and extensions
|
||||
*.pyc # Compiled Python files (.pyc, .pyo are automatically ignored by git)
|
||||
@@ -16,11 +15,11 @@ venv/
|
||||
env/
|
||||
|
||||
# Ignore any IDE configurations or project-specific metadata
|
||||
.vscode/**
|
||||
.pycharm/**
|
||||
*.iml
|
||||
.project
|
||||
.settings
|
||||
.vscode/settings.json
|
||||
|
||||
# Ignore test results and logs (adjust to your specific testing framework)
|
||||
/testresults/**
|
||||
@@ -35,4 +34,5 @@ env/
|
||||
__pycache__/
|
||||
|
||||
# Exclude .env file from all platforms
|
||||
*/.env
|
||||
*/.env
|
||||
|
||||
|
||||
19
.vscode/launch.json
vendored
Normal file
19
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python Debugger: current file",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": [
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
12
.vscode/tasks.json
vendored
Normal file
12
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "pip install -e bumble",
|
||||
"type": "shell",
|
||||
"command": "pip install -e ../bumble --config-settings editable_mode=compat"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -53,6 +53,7 @@ class AuracastBigConfig:
|
||||
name: str = 'Broadcast0'
|
||||
program_info: str = 'Some Announcements'
|
||||
audio_source: str = 'file:./auracast/announcement_48_10_96000_en.wav'
|
||||
iso_que_len: int = 64
|
||||
loop_wav: bool = True
|
||||
|
||||
|
||||
|
||||
@@ -97,228 +97,243 @@ def run_async(async_command: Coroutine) -> None:
|
||||
)
|
||||
|
||||
async def setup_broadcast(
|
||||
device,
|
||||
global_config : auracast_config.AuracastGlobalConfig,
|
||||
big_config: List[auracast_config.AuracastBigConfig]
|
||||
) -> dict:
|
||||
|
||||
) -> None:
|
||||
async with create_device(global_config) as device:
|
||||
if not device.supports_le_periodic_advertising:
|
||||
logger.error(color('Periodic advertising not supported', 'red'))
|
||||
return
|
||||
|
||||
bap_sampling_freq = getattr(bap.SamplingFrequency, f"FREQ_{global_config.auracast_sampling_rate_hz}")
|
||||
bigs = {}
|
||||
for i, conf in enumerate(big_config):
|
||||
bigs[f'big{i}'] = {}
|
||||
# Config advertising set
|
||||
bigs[f'big{i}']['basic_audio_announcement'] = bap.BasicAudioAnnouncement(
|
||||
presentation_delay=global_config.presentation_delay_us,
|
||||
subgroups=[
|
||||
bap.BasicAudioAnnouncement.Subgroup(
|
||||
codec_id=hci.CodingFormat(codec_id=hci.CodecID.LC3),
|
||||
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
||||
sampling_frequency=bap_sampling_freq,
|
||||
frame_duration=bap.FrameDuration.DURATION_10000_US,
|
||||
octets_per_codec_frame=global_config.octets_per_frame,
|
||||
),
|
||||
metadata=le_audio.Metadata(
|
||||
[
|
||||
le_audio.Metadata.Entry(
|
||||
tag=le_audio.Metadata.Tag.LANGUAGE, data=conf.language.encode()
|
||||
),
|
||||
le_audio.Metadata.Entry(
|
||||
tag=le_audio.Metadata.Tag.PROGRAM_INFO, data=conf.program_info.encode()
|
||||
),
|
||||
le_audio.Metadata.Entry(
|
||||
tag=le_audio.Metadata.Tag.BROADCAST_NAME, data=conf.name.encode()
|
||||
),
|
||||
]
|
||||
),
|
||||
bis=[
|
||||
bap.BasicAudioAnnouncement.BIS(
|
||||
index=1,
|
||||
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
||||
audio_channel_allocation=bap.AudioLocation.FRONT_LEFT
|
||||
),
|
||||
bap_sampling_freq = getattr(bap.SamplingFrequency, f"FREQ_{global_config.auracast_sampling_rate_hz}")
|
||||
bigs = {}
|
||||
for i, conf in enumerate(big_config):
|
||||
bigs[f'big{i}'] = {}
|
||||
# Config advertising set
|
||||
bigs[f'big{i}']['basic_audio_announcement'] = bap.BasicAudioAnnouncement(
|
||||
presentation_delay=global_config.presentation_delay_us,
|
||||
subgroups=[
|
||||
bap.BasicAudioAnnouncement.Subgroup(
|
||||
codec_id=hci.CodingFormat(codec_id=hci.CodecID.LC3),
|
||||
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
||||
sampling_frequency=bap_sampling_freq,
|
||||
frame_duration=bap.FrameDuration.DURATION_10000_US,
|
||||
octets_per_codec_frame=global_config.octets_per_frame,
|
||||
),
|
||||
metadata=le_audio.Metadata(
|
||||
[
|
||||
le_audio.Metadata.Entry(
|
||||
tag=le_audio.Metadata.Tag.LANGUAGE, data=conf.language.encode()
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
logger.info('Setup Advertising')
|
||||
advertising_manufacturer_data = (
|
||||
b''
|
||||
if global_config.manufacturer_data is None
|
||||
else bytes(
|
||||
core.AdvertisingData(
|
||||
[
|
||||
(
|
||||
core.AdvertisingData.MANUFACTURER_SPECIFIC_DATA,
|
||||
struct.pack('<H', global_config.manufacturer_data[0])
|
||||
+ global_config.manufacturer_data[1],
|
||||
)
|
||||
]
|
||||
le_audio.Metadata.Entry(
|
||||
tag=le_audio.Metadata.Tag.PROGRAM_INFO, data=conf.program_info.encode()
|
||||
),
|
||||
le_audio.Metadata.Entry(
|
||||
tag=le_audio.Metadata.Tag.BROADCAST_NAME, data=conf.name.encode()
|
||||
),
|
||||
]
|
||||
),
|
||||
bis=[
|
||||
bap.BasicAudioAnnouncement.BIS(
|
||||
index=1,
|
||||
codec_specific_configuration=bap.CodecSpecificConfiguration(
|
||||
audio_channel_allocation=bap.AudioLocation.FRONT_LEFT
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
logger.info('Setup Advertising')
|
||||
advertising_manufacturer_data = (
|
||||
b''
|
||||
if global_config.manufacturer_data is None
|
||||
else bytes(
|
||||
core.AdvertisingData(
|
||||
[
|
||||
(
|
||||
core.AdvertisingData.MANUFACTURER_SPECIFIC_DATA,
|
||||
struct.pack('<H', global_config.manufacturer_data[0])
|
||||
+ global_config.manufacturer_data[1],
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
bigs[f'big{i}']['broadcast_audio_announcement'] = bap.BroadcastAudioAnnouncement(conf.id)
|
||||
advertising_set = await device.create_advertising_set(
|
||||
random_address=conf.random_address,
|
||||
advertising_parameters=bumble.device.AdvertisingParameters(
|
||||
advertising_event_properties=bumble.device.AdvertisingEventProperties(
|
||||
is_connectable=False
|
||||
),
|
||||
primary_advertising_interval_min=round(100),
|
||||
primary_advertising_interval_max=round(200),
|
||||
advertising_sid=i,
|
||||
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,
|
||||
)
|
||||
bigs[f'big{i}']['broadcast_audio_announcement'] = bap.BroadcastAudioAnnouncement(conf.id)
|
||||
advertising_set = await device.create_advertising_set(
|
||||
random_address=conf.random_address,
|
||||
advertising_parameters=bumble.device.AdvertisingParameters(
|
||||
advertising_event_properties=bumble.device.AdvertisingEventProperties(
|
||||
is_connectable=False
|
||||
),
|
||||
advertising_data=(
|
||||
bigs[f'big{i}']['broadcast_audio_announcement'].get_advertising_data()
|
||||
+ bytes(
|
||||
core.AdvertisingData(
|
||||
[(core.AdvertisingData.BROADCAST_NAME, conf.name.encode())]
|
||||
)
|
||||
primary_advertising_interval_min=round(100),
|
||||
primary_advertising_interval_max=round(200),
|
||||
advertising_sid=i,
|
||||
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()
|
||||
+ bytes(
|
||||
core.AdvertisingData(
|
||||
[(core.AdvertisingData.BROADCAST_NAME, conf.name.encode())]
|
||||
)
|
||||
+ advertising_manufacturer_data
|
||||
),
|
||||
periodic_advertising_parameters=bumble.device.PeriodicAdvertisingParameters(
|
||||
periodic_advertising_interval_min=round(80),
|
||||
periodic_advertising_interval_max=round(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 advertising_set.start_periodic()
|
||||
|
||||
logging.info('Setup BIG')
|
||||
if global_config.qos_config.iso_int_multiple_10ms == 1:
|
||||
frame_enable = 0
|
||||
else:
|
||||
frame_enable = 1
|
||||
|
||||
big = await device.create_big(
|
||||
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
|
||||
max_sdu=global_config.octets_per_frame,
|
||||
max_transport_latency=global_config.qos_config.max_transport_latency_ms,
|
||||
rtn=global_config.qos_config.number_of_retransmissions,
|
||||
broadcast_code=(
|
||||
bytes.fromhex(conf.code) if conf.code else None
|
||||
),
|
||||
framing=frame_enable # needed if iso interval is not frame interval of codedc
|
||||
),
|
||||
)
|
||||
bigs[f'big{i}']['big'] = big
|
||||
|
||||
for bis_link in big.bis_links:
|
||||
await bis_link.setup_data_path(
|
||||
direction=bis_link.Direction.HOST_TO_CONTROLLER
|
||||
)
|
||||
+ advertising_manufacturer_data
|
||||
),
|
||||
periodic_advertising_parameters=bumble.device.PeriodicAdvertisingParameters(
|
||||
periodic_advertising_interval_min=round(80),
|
||||
periodic_advertising_interval_max=round(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
|
||||
|
||||
iso_queue = bumble.device.IsoPacketStream(big.bis_links[0], 64)
|
||||
logging.info('Start Periodic Advertising')
|
||||
await advertising_set.start_periodic()
|
||||
|
||||
logging.info('Setup ISO Data Path')
|
||||
logging.info('Setup BIG')
|
||||
if global_config.qos_config.iso_int_multiple_10ms == 1:
|
||||
frame_enable = 0
|
||||
else:
|
||||
frame_enable = 1
|
||||
|
||||
bigs[f'big{i}']['iso_queue'] = iso_queue
|
||||
big = await device.create_big(
|
||||
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
|
||||
max_sdu=global_config.octets_per_frame,
|
||||
max_transport_latency=global_config.qos_config.max_transport_latency_ms,
|
||||
rtn=global_config.qos_config.number_of_retransmissions,
|
||||
broadcast_code=(
|
||||
bytes.fromhex(conf.code) if conf.code else None
|
||||
),
|
||||
framing=frame_enable # needed if iso interval is not frame interval of codedc
|
||||
),
|
||||
)
|
||||
bigs[f'big{i}']['big'] = big
|
||||
|
||||
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(i+1) # Wait for advertising to set up
|
||||
|
||||
logging.info("Broadcasting...")
|
||||
|
||||
def on_flow():
|
||||
data_packet_queue = iso_queue.data_packet_queue
|
||||
print(
|
||||
f'\rPACKETS: pending={data_packet_queue.pending}, '
|
||||
f'queued={data_packet_queue.queued}, '
|
||||
f'completed={data_packet_queue.completed}',
|
||||
end='',
|
||||
for bis_link in big.bis_links:
|
||||
await bis_link.setup_data_path(
|
||||
direction=bis_link.Direction.HOST_TO_CONTROLLER
|
||||
)
|
||||
|
||||
if global_conf.debug:
|
||||
bigs[f'big{0}']['iso_queue'][0].data_packet_queue.on('flow', on_flow)
|
||||
|
||||
iso_queue = bumble.device.IsoPacketStream(big.bis_links[0], conf.iso_que_len)
|
||||
|
||||
logging.info('Setup ISO Data Path')
|
||||
|
||||
bigs[f'big{i}']['iso_queue'] = iso_queue
|
||||
|
||||
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(i+1) # Wait for advertising to set up
|
||||
|
||||
def on_flow():
|
||||
data_packet_queue = iso_queue.data_packet_queue
|
||||
print(
|
||||
f'\rPACKETS: pending={data_packet_queue.pending}, '
|
||||
f'queued={data_packet_queue.queued}, '
|
||||
f'completed={data_packet_queue.completed}',
|
||||
end='',
|
||||
)
|
||||
|
||||
if global_conf.debug:
|
||||
bigs[f'big{0}']['iso_queue'].data_packet_queue.on('flow', on_flow)
|
||||
|
||||
return bigs
|
||||
|
||||
async def setup_audio(
|
||||
bigs,
|
||||
global_config : auracast_config.AuracastGlobalConfig,
|
||||
big_config: List[auracast_config.AuracastBigConfig]
|
||||
):
|
||||
for i, big in enumerate(bigs.values()):
|
||||
audio_source = big_config[i].audio_source
|
||||
input_format = 'auto'
|
||||
audio_input = await audio_io.create_audio_input(audio_source, input_format)
|
||||
audio_input.rewind = big_config[i].loop_wav
|
||||
pcm_format = await audio_input.open()
|
||||
|
||||
#try:
|
||||
if pcm_format.channels != 1:
|
||||
print("Only 1 channels PCM configurations are supported")
|
||||
return
|
||||
if pcm_format.sample_type == audio_io.PcmFormat.SampleType.INT16:
|
||||
pcm_bit_depth = 16
|
||||
elif pcm_format.sample_type == audio_io.PcmFormat.SampleType.FLOAT32:
|
||||
pcm_bit_depth = None
|
||||
else:
|
||||
print("Only INT16 and FLOAT32 sample types are supported")
|
||||
return
|
||||
encoder = lc3.Encoder(
|
||||
frame_duration_us=global_config.frame_duration_us,
|
||||
sample_rate_hz=global_config.auracast_sampling_rate_hz,
|
||||
num_channels=1,
|
||||
input_sample_rate_hz=pcm_format.sample_rate,
|
||||
)
|
||||
lc3_frame_samples = encoder.get_frame_samples() # number of the pcm samples per lc3 frame
|
||||
lc3_frame_size = global_config.octets_per_frame #encoder.get_frame_bytes(bitrate)
|
||||
lc3_bytes_per_frame = lc3_frame_size #* 2 #multiplied by number of channels
|
||||
|
||||
big['pcm_bit_depth'] = pcm_bit_depth
|
||||
big['lc3_bytes_per_frame'] = lc3_bytes_per_frame
|
||||
big['lc3_frame_samples'] = lc3_frame_samples
|
||||
big['audio_input'] = audio_input
|
||||
big['encoder'] = encoder
|
||||
|
||||
async def streamer(bigs):
|
||||
# TODO: do some pre buffering so the stream is stable from the beginning. One half iso queue would be appropriate
|
||||
logging.info("Streaming audio...")
|
||||
while True:
|
||||
stream_finished = [False for _ in range(len(bigs))]
|
||||
for i, big in enumerate(bigs.values()):
|
||||
audio_source = big_config[i].audio_source
|
||||
input_format = 'auto'
|
||||
audio_input = await audio_io.create_audio_input(audio_source, input_format)
|
||||
audio_input.rewind = big_config[i].loop_wav
|
||||
pcm_format = await audio_input.open()
|
||||
pcm_frame = await anext(big['audio_input'].frames(big['lc3_frame_samples']), None)
|
||||
if pcm_frame is None: # Not all streams may stop at the same time
|
||||
stream_finished[i] = True
|
||||
continue
|
||||
|
||||
#try:
|
||||
if pcm_format.channels != 1:
|
||||
print("Only 1 channels PCM configurations are supported")
|
||||
return
|
||||
if pcm_format.sample_type == audio_io.PcmFormat.SampleType.INT16:
|
||||
pcm_bit_depth = 16
|
||||
elif pcm_format.sample_type == audio_io.PcmFormat.SampleType.FLOAT32:
|
||||
pcm_bit_depth = None
|
||||
else:
|
||||
print("Only INT16 and FLOAT32 sample types are supported")
|
||||
return
|
||||
encoder = lc3.Encoder(
|
||||
frame_duration_us=global_config.frame_duration_us,
|
||||
sample_rate_hz=global_config.auracast_sampling_rate_hz,
|
||||
num_channels=1,
|
||||
input_sample_rate_hz=pcm_format.sample_rate,
|
||||
lc3_frame = big['encoder'].encode(
|
||||
pcm_frame, num_bytes=big['lc3_bytes_per_frame'], bit_depth=big['pcm_bit_depth']
|
||||
)
|
||||
lc3_frame_samples = encoder.get_frame_samples() # number of the pcm samples per lc3 frame
|
||||
lc3_frame_size = global_config.octets_per_frame #encoder.get_frame_bytes(bitrate)
|
||||
lc3_bytes_per_frame = lc3_frame_size #* 2 #multiplied by number of channels
|
||||
await big['iso_queue'].write(lc3_frame)
|
||||
|
||||
bigs[f'big{i}']['lc3_frame_samples'] = lc3_frame_samples
|
||||
bigs[f'big{i}']['audio_input'] = audio_input
|
||||
bigs[f'big{i}']['encoder'] = encoder
|
||||
if all(stream_finished): # Take into account that multiple files have different lengths
|
||||
print('All streams finished, stopping streamer')
|
||||
break
|
||||
|
||||
async def streamer(bigs):
|
||||
# TODO: do some pre buffering so the stream is stable from the beginning. One half iso queue would be appropriate
|
||||
while True:
|
||||
stream_finished = [False for _ in range(len(bigs))]
|
||||
for i, big in enumerate(bigs.values()):
|
||||
pcm_frame = await anext(big['audio_input'].frames(big['lc3_frame_samples']), None)
|
||||
if pcm_frame is None: # Not all streams may stop at the same time
|
||||
stream_finished[i] = True
|
||||
continue
|
||||
|
||||
lc3_frame = big['encoder'].encode(
|
||||
pcm_frame, num_bytes=lc3_bytes_per_frame, bit_depth=pcm_bit_depth
|
||||
)
|
||||
await big['iso_queue'].write(lc3_frame) # iso_queue.write(lc3_frame)
|
||||
|
||||
|
||||
if all(stream_finished): # TODO: Take into account that multiple files have different lengths
|
||||
print('All streams finished, stopping streamer')
|
||||
break
|
||||
|
||||
stream = streamer(bigs)
|
||||
|
||||
await stream # running until stream ends
|
||||
|
||||
return bigs
|
||||
#return streamer(bigs)
|
||||
#await stream # running until stream ends
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
async def broadcast(global_conf: auracast_config.AuracastGlobalConfig, big_conf: List[auracast_config.AuracastBigConfig]):
|
||||
"""Start a broadcast as a source."""
|
||||
ret = await setup_broadcast(
|
||||
global_conf,
|
||||
big_conf
|
||||
)
|
||||
"""Start a broadcast."""
|
||||
async with create_device(global_conf) as device:
|
||||
if not device.supports_le_periodic_advertising:
|
||||
logger.error(color('Periodic advertising not supported', 'red'))
|
||||
return
|
||||
|
||||
bigs = await setup_broadcast( # the bigs dictionary contains all the global configurations
|
||||
device,
|
||||
global_conf,
|
||||
big_conf
|
||||
)
|
||||
await setup_audio(
|
||||
bigs,
|
||||
global_conf,
|
||||
big_conf
|
||||
)
|
||||
|
||||
await streamer(bigs)
|
||||
|
||||
# make a second coroutine to run the streaming - maybe even use the streamer coroutine
|
||||
# start it without await and go into a infinite loop were further instrucations via a ui can be given ?
|
||||
|
||||
@@ -342,7 +357,7 @@ if __name__ == "__main__":
|
||||
|
||||
# global_conf.transport='usb:2fe3:000b' #nrf52dongle hci_usb # TODO: iso packet over usb not 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
|
||||
@@ -366,7 +381,7 @@ if __name__ == "__main__":
|
||||
global_conf.auracast_sampling_rate_hz = 16000
|
||||
global_conf.octets_per_frame = 40 # 32kbps@16kHz
|
||||
#global_conf.debug = True
|
||||
|
||||
|
||||
run_async(
|
||||
broadcast(
|
||||
global_conf,
|
||||
@@ -382,4 +397,3 @@ if __name__ == "__main__":
|
||||
# (realtime audio network uncoded)
|
||||
|
||||
# TODO: add support for playing new files will keeping the advertising running
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.0.1"
|
||||
requires-python = ">=3.8"
|
||||
|
||||
dependencies = [
|
||||
"bumble @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble.git@e027bcb57a0f29c82e3c02c8bb8691dcb91eac62",
|
||||
"bumble @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble_mirror.git@e027bcb57a0f29c82e3c02c8bb8691dcb91eac62",
|
||||
"lc3 @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/liblc3.git@7558637303106c7ea971e7bb8cedf379d3e08bcc",
|
||||
"sounddevice",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user