diff --git a/src/auracast/auracast_config.py b/src/auracast/auracast_config.py index a732393..aa8de68 100644 --- a/src/auracast/auracast_config.py +++ b/src/auracast/auracast_config.py @@ -58,25 +58,26 @@ class AuracastBigConfig(BaseModel): class AuracastBigConfigDeu(AuracastBigConfig): id: int = 12 random_address: str = 'F1:F1:F2:F3:F4:F5' - name: str = 'Broadcast0' + name: str = 'Hörsaal A' language: str ='deu' - program_info: str = 'Announcements German' + program_info: str = 'Vorlesung DE' audio_source: str = 'file:./testdata/announcement_de.wav' class AuracastBigConfigEng(AuracastBigConfig): id: int = 123 random_address: str = 'F2:F1:F2:F3:F4:F5' - name: str = 'Broadcast1' + name: str = 'Lecture Hall A' language: str ='eng' - program_info: str = 'Announcements English' + program_info: str = 'Lecture EN' audio_source: str = 'file:./testdata/announcement_en.wav' class AuracastBigConfigFra(AuracastBigConfig): id: int = 1234 random_address: str = 'F3:F1:F2:F3:F4:F5' - name: str = 'Broadcast2' + # French + name: str = 'Auditoire A' language: str ='fra' - program_info: str = 'Announcements French' + program_info: str = 'Auditoire FR' audio_source: str = 'file:./testdata/announcement_fr.wav' class AuracastBigConfigSpa(AuracastBigConfig): diff --git a/src/auracast/multicast.py b/src/auracast/multicast.py index c9aa32b..0cbee2a 100644 --- a/src/auracast/multicast.py +++ b/src/auracast/multicast.py @@ -395,6 +395,7 @@ class Streamer(): # precoded lc3 from ram elif isinstance(big_config[i].audio_source, bytes): big['precoded'] = True + big['lc3_bytes_per_frame'] = global_config.octets_per_frame lc3_frames = iter(big_config[i].audio_source) @@ -405,6 +406,7 @@ class Streamer(): # precoded lc3 file elif big_config[i].audio_source.endswith('.lc3'): big['precoded'] = True + big['lc3_bytes_per_frame'] = global_config.octets_per_frame filename = big_config[i].audio_source.replace('file:', '') lc3_bytes = read_lc3_file(filename) @@ -417,6 +419,7 @@ class Streamer(): # use wav files and code them entirely before streaming elif big_config[i].precode_wav and big_config[i].audio_source.endswith('.wav'): big['precoded'] = True + big['lc3_bytes_per_frame'] = global_config.octets_per_frame audio_input = await audio_io.create_audio_input(audio_source, input_format) audio_input.rewind = False @@ -540,52 +543,52 @@ class Streamer(): big['precoded'] = False - logging.info("Streaming audio...") - bigs = self.bigs - self.is_streaming = True - # One streamer fits all - while self.is_streaming: - stream_finished = [False for _ in range(len(bigs))] - for i, big in enumerate(bigs.values()): + logging.info("Streaming audio...") + bigs = self.bigs + self.is_streaming = True + # One streamer fits all + while self.is_streaming: + stream_finished = [False for _ in range(len(bigs))] + for i, big in enumerate(bigs.values()): - if big['precoded']:# everything was already lc3 coded beforehand - lc3_frame = bytes( - itertools.islice(big['lc3_frames'], big['lc3_bytes_per_frame']) - ) - - if lc3_frame == b'': # Not all streams may stop at the same time - stream_finished[i] = True - continue - else: # code lc3 on the fly - 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 - - # Down-mix multi-channel PCM to mono for LC3 encoder if needed - if big.get('channels', 1) > 1: - if isinstance(pcm_frame, np.ndarray): - if pcm_frame.ndim > 1: - mono = pcm_frame.mean(axis=1).astype(pcm_frame.dtype) - pcm_frame = mono - else: - # Convert raw bytes to numpy, average channels, convert back - dtype = np.int16 if big['pcm_bit_depth'] == 16 else np.float32 - samples = np.frombuffer(pcm_frame, dtype=dtype) - samples = samples.reshape(-1, big['channels']).mean(axis=1) - pcm_frame = samples.astype(dtype).tobytes() - - lc3_frame = big['encoder'].encode( - pcm_frame, num_bytes=big['lc3_bytes_per_frame'], bit_depth=big['pcm_bit_depth'] + if big['precoded']:# everything was already lc3 coded beforehand + lc3_frame = bytes( + itertools.islice(big['lc3_frames'], big['lc3_bytes_per_frame']) ) - await big['iso_queue'].write(lc3_frame) + if lc3_frame == b'': # Not all streams may stop at the same time + stream_finished[i] = True + continue + else: # code lc3 on the fly + pcm_frame = await anext(big['audio_input'].frames(big['lc3_frame_samples']), None) - if all(stream_finished): # Take into account that multiple files have different lengths - logging.info('All streams finished, stopping streamer') - self.is_streaming = False - break + if pcm_frame is None: # Not all streams may stop at the same time + stream_finished[i] = True + continue + + # Down-mix multi-channel PCM to mono for LC3 encoder if needed + if big.get('channels', 1) > 1: + if isinstance(pcm_frame, np.ndarray): + if pcm_frame.ndim > 1: + mono = pcm_frame.mean(axis=1).astype(pcm_frame.dtype) + pcm_frame = mono + else: + # Convert raw bytes to numpy, average channels, convert back + dtype = np.int16 if big['pcm_bit_depth'] == 16 else np.float32 + samples = np.frombuffer(pcm_frame, dtype=dtype) + samples = samples.reshape(-1, big['channels']).mean(axis=1) + pcm_frame = samples.astype(dtype).tobytes() + + lc3_frame = big['encoder'].encode( + pcm_frame, num_bytes=big['lc3_bytes_per_frame'], bit_depth=big['pcm_bit_depth'] + ) + + await big['iso_queue'].write(lc3_frame) + + if all(stream_finished): # Take into account that multiple files have different lengths + logging.info('All streams finished, stopping streamer') + self.is_streaming = False + break # ----------------------------------------------------------------------------- @@ -634,12 +637,11 @@ if __name__ == "__main__": ) os.chdir(os.path.dirname(__file__)) - config = auracast_config.AuracastConfigGroup( bigs = [ auracast_config.AuracastBigConfigDeu(), - #auracast_config.AuracastBigConfigEng(), - #auracast_config.AuracastBigConfigFra(), + auracast_config.AuracastBigConfigEng(), + auracast_config.AuracastBigConfigFra(), #auracast_config.AuracastBigConfigEs(), #auracast_config.AuracastBigConfigIt(), ] @@ -653,8 +655,8 @@ if __name__ == "__main__": #config.transport='serial:/dev/serial/by-id/usb-SEGGER_J-Link_001057705357-if02,1000000,rtscts' # transport for nrf54l15dk #config.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_95A087EADB030B24-if00,115200,rtscts' #nrf52dongle hci_uart usb cdc #config.transport='usb:2fe3:000b' #nrf52dongle hci_usb # TODO: iso packet over usb not supported - config.transport= 'auto' - #config.transport='serial:/dev/ttyAMA2,1000000,rtscts' # transport for raspberry pi + # config.transport= 'auto' + config.transport='serial:/dev/ttyAMA3,1000000,rtscts' # transport for raspberry pi for big in config.bigs: # TODO: encrypted streams are not working