diff --git a/src/auracast/multicast.py b/src/auracast/multicast.py index 4041092..5352ec3 100644 --- a/src/auracast/multicast.py +++ b/src/auracast/multicast.py @@ -277,7 +277,7 @@ async def init_broadcast( logging.debug(f'big{i} parameters are:') logging.debug('%s', pprint.pformat(vars(big))) - logging.debug(f'Finished setup of big{i}.') + logging.info(f'Finished setup of big{i}.') await asyncio.sleep(i+1) # Wait for advertising to set up diff --git a/src/auracast/server/multicast_frontend.py b/src/auracast/server/multicast_frontend.py index 124a088..bde7690 100644 --- a/src/auracast/server/multicast_frontend.py +++ b/src/auracast/server/multicast_frontend.py @@ -1,5 +1,6 @@ # frontend/app.py import os +import time import streamlit as st import requests from auracast import auracast_config @@ -12,6 +13,13 @@ if 'stream_started' not in st.session_state: # Global: desired packetization time in ms for Opus (should match backend) PTIME = 40 BACKEND_URL = "http://localhost:5000" +TRANSPORT1 = "auto" #'serial:/dev/ttyAMA3,1000000,rtscts', # transport for raspberry pi gpio header +QUALITY_MAP = { + "High (48kHz)": {"rate": 48000, "octets": 120}, + "Good (32kHz)": {"rate": 32000, "octets": 80}, + "Medium (24kHz)": {"rate": 24000, "octets": 60}, + "Fair (16kHz)": {"rate": 16000, "octets": 40}, +} # Try loading persisted settings from backend saved_settings = {} @@ -25,7 +33,7 @@ except Exception: st.title("🎙️ Auracast Audio Mode Control") # Audio mode selection with persisted default -options = ["Webapp", "USB"] +options = ["Webapp", "USB", "Demo"] saved_audio_mode = saved_settings.get("audio_mode", "Webapp") if saved_audio_mode not in options: saved_audio_mode = "Webapp" @@ -34,18 +42,108 @@ audio_mode = st.selectbox( "Audio Mode", options, index=options.index(saved_audio_mode), - help="Select the audio input source. Choose 'Webapp' for browser microphone or 'USB' for a connected hardware device." + help="Select the audio input source. Choose 'Webapp' for browser microphone, 'USB' for a connected hardware device, or 'Demo' for a simulated stream." ) -if audio_mode in ["Webapp", "USB"]: - # Stream quality selection (now enabled) - quality_map = { - "High (48kHz)": {"rate": 48000, "octets": 120}, - "Good (32kHz)": {"rate": 32000, "octets": 80}, - "Medium (24kHz)": {"rate": 24000, "octets": 60}, - "Fair (16kHz)": {"rate": 16000, "octets": 40}, +if audio_mode == "Demo": + demo_stream_map = { + "1 × 48kHz": {"quality": "High (48kHz)", "streams": 1,}, + "2 × 24kHz": {"quality": "Medium (24kHz)", "streams": 2,}, + "3 × 16kHz": {"quality": "Fair (16kHz)", "streams": 3,}, } - quality_options = list(quality_map.keys()) + demo_options = list(demo_stream_map.keys()) + default_demo = demo_options[0] + demo_selected = st.selectbox( + "Demo Stream Type", + demo_options, + index=0, + help="Select the demo stream configuration." + ) + #st.info(f"Demo mode selected: {demo_selected} (Streams: {demo_stream_map[demo_selected]['streams']}, Rate: {demo_stream_map[demo_selected]['rate']} Hz)") + # Start/Stop buttons for demo mode + if 'demo_stream_started' not in st.session_state: + st.session_state['demo_stream_started'] = False + col1, col2 = st.columns(2) + with col1: + start_demo = st.button("Start Demo Stream") + with col2: + stop_demo = st.button("Stop Demo Stream") + if start_demo: + # Always stop any running stream for clean state + try: + requests.post(f"{BACKEND_URL}/stop_audio").json() + except Exception: + pass + time.sleep(1) + demo_cfg = demo_stream_map[demo_selected] + # Octets per frame logic matches quality_map + q = QUALITY_MAP[demo_cfg['quality']] + + if demo_cfg['streams'] >= 1: + bigs = [ + auracast_config.AuracastBigConfigDeu( + audio_source=f'file:../testdata/wave_particle_5min_de_{int(q["rate"]/1000)}kHz_mono.wav', + iso_que_len=32, + sampling_frequency=q['rate'], + octets_per_frame=q['octets'], + ) + ] + if demo_cfg['streams'] >= 2: + bigs += [ + auracast_config.AuracastBigConfigEng( + audio_source=f'file:../testdata/wave_particle_5min_en_{int(q["rate"]/1000)}kHz_mono.wav', + iso_que_len=32, + sampling_frequency=q['rate'], + octets_per_frame=q['octets'], + ), + ] + if demo_cfg['streams'] >= 3: + bigs += [ + auracast_config.AuracastBigConfigFra( + audio_source=f'file:../testdata/wave_particle_5min_fr_{int(q["rate"]/1000)}kHz_mono.wav', + iso_que_len=32, + sampling_frequency=q['rate'], + octets_per_frame=q['octets'], + ), + ] + + config = auracast_config.AuracastConfigGroup( + auracast_sampling_rate_hz=q['rate'], + octets_per_frame=q['octets'], + transport=TRANSPORT1, # transport for raspberry pi gpio header + bigs = bigs + ) + + try: + r = requests.post(f"{BACKEND_URL}/init", json=config.model_dump()) + if r.status_code == 200: + st.session_state['demo_stream_started'] = True + st.success(f"Demo stream started: {demo_selected}") + else: + st.session_state['demo_stream_started'] = False + st.error(f"Failed to initialize demo: {r.text}") + except Exception as e: + st.session_state['demo_stream_started'] = False + st.error(f"Error: {e}") + elif stop_demo: + try: + r = requests.post(f"{BACKEND_URL}/stop_audio").json() + st.session_state['demo_stream_started'] = False + if r.get('was_running'): + st.info("Demo stream stopped.") + else: + st.info("Demo stream was not running.") + except Exception as e: + st.error(f"Error: {e}") + elif st.session_state['demo_stream_started']: + st.success(f"Demo stream running: {demo_selected}") + else: + st.info("Demo stream not running.") + quality = None # Not used in demo mode +else: + # Stream quality selection (now enabled) + + quality_options = list(QUALITY_MAP.keys()) default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] quality = st.selectbox( "Stream Quality (Sampling Rate)", @@ -119,7 +217,7 @@ if audio_mode in ["Webapp", "USB"]: input_device = selected_option.split(":", 1)[0] if ":" in selected_option else selected_option else: input_device = None - import time + start_stream = st.button("Start Auracast") stop_stream = st.button("Stop Auracast") @@ -164,13 +262,13 @@ if audio_mode in ["Webapp", "USB"]: st.success("Stream Stopped!") # Small pause lets backend fully release audio devices before re-init - import time; time.sleep(1) + time.sleep(1) # Prepare config using the model (do NOT send qos_config, only relevant fields) - q = quality_map[quality] + q = QUALITY_MAP[quality] config = auracast_config.AuracastConfigGroup( auracast_sampling_rate_hz=q['rate'], octets_per_frame=q['octets'], - transport='serial:/dev/ttyAMA3,1000000,rtscts', # transport for raspberry pi gpio header + transport=TRANSPORT1, # transport for raspberry pi gpio header bigs = [ auracast_config.AuracastBigConfig( name=stream_name, @@ -254,9 +352,9 @@ if audio_mode in ["Webapp", "USB"]: """ st.components.v1.html(component, height=0) st.session_state['stream_started'] = True -else: - st.header("Advertised Streams (Cloud Announcements)") - st.info("This feature requires backend support to list advertised streams.") +#else: +# st.header("Advertised Streams (Cloud Announcements)") +# st.info("This feature requires backend support to list advertised streams.") # Placeholder for future implementation # Example: r = requests.get(f"{BACKEND_URL}/advertised_streams") # if r.status_code == 200: diff --git a/src/auracast/server/multicast_server.py b/src/auracast/server/multicast_server.py index 0a53324..80be758 100644 --- a/src/auracast/server/multicast_server.py +++ b/src/auracast/server/multicast_server.py @@ -96,6 +96,9 @@ async def initialize(conf: auracast_config.AuracastConfigGroup): elif first_source == 'webrtc': audio_mode_persist = 'Webapp' input_device = None + elif first_source.startswith('file:'): + audio_mode_persist = 'Demo' + input_device = None else: audio_mode_persist = 'Network' input_device = None @@ -129,7 +132,9 @@ async def initialize(conf: auracast_config.AuracastConfigGroup): # Auto-start streaming only when using a local USB audio device. For Webapp mode the # streamer is started by the /offer handler once the WebRTC track arrives so we know # the peer connection is established. - if any(big.audio_source.startswith("device:") for big in conf.bigs): + # TODO rather do a if not webrtc in the future + if any(big.audio_source.startswith("device:") or big.audio_source.startswith("file:") for big in conf.bigs): + log.info("Auto-starting streaming") await multicaster.start_streaming() except Exception as e: log.error("Exception in /init: %s", traceback.format_exc()) @@ -362,6 +367,8 @@ async def shutdown(): raise HTTPException(status_code=500, detail=str(e)) if __name__ == '__main__': + import os + os.chdir(os.path.dirname(__file__)) import uvicorn log.basicConfig( # for debug log level export LOG_LEVEL=DEBUG level=os.environ.get('LOG_LEVEL', log.INFO), diff --git a/src/auracast/testdata/wave_particle_5min_de_16kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_de_16kHz_mono.wav new file mode 100644 index 0000000..d4cbb61 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_de_16kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_de_24kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_de_24kHz_mono.wav new file mode 100644 index 0000000..3a1ab31 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_de_24kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_de_48kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_de_48kHz_mono.wav new file mode 100644 index 0000000..ae7d35e Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_de_48kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_en_16kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_en_16kHz_mono.wav new file mode 100644 index 0000000..592bd42 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_en_16kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_en_24kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_en_24kHz_mono.wav new file mode 100644 index 0000000..8ebacf5 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_en_24kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_en_48kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_en_48kHz_mono.wav new file mode 100644 index 0000000..b203240 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_en_48kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_es_16kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_es_16kHz_mono.wav new file mode 100644 index 0000000..22905a5 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_es_16kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_es_24kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_es_24kHz_mono.wav new file mode 100644 index 0000000..acad61a Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_es_24kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_es_48kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_es_48kHz_mono.wav new file mode 100644 index 0000000..9d1773f Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_es_48kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_fr_16kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_fr_16kHz_mono.wav new file mode 100644 index 0000000..7f16d51 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_fr_16kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_fr_24kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_fr_24kHz_mono.wav new file mode 100644 index 0000000..dcfe9ca Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_fr_24kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_fr_48kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_fr_48kHz_mono.wav new file mode 100644 index 0000000..c558003 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_fr_48kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_it_16kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_it_16kHz_mono.wav new file mode 100644 index 0000000..a6f6db3 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_it_16kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_it_24kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_it_24kHz_mono.wav new file mode 100644 index 0000000..605fd17 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_it_24kHz_mono.wav differ diff --git a/src/auracast/testdata/wave_particle_5min_it_48kHz_mono.wav b/src/auracast/testdata/wave_particle_5min_it_48kHz_mono.wav new file mode 100644 index 0000000..3b100d1 Binary files /dev/null and b/src/auracast/testdata/wave_particle_5min_it_48kHz_mono.wav differ