Implement a demo mode
This commit is contained in:
@@ -277,7 +277,7 @@ async def init_broadcast(
|
|||||||
|
|
||||||
logging.debug(f'big{i} parameters are:')
|
logging.debug(f'big{i} parameters are:')
|
||||||
logging.debug('%s', pprint.pformat(vars(big)))
|
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
|
await asyncio.sleep(i+1) # Wait for advertising to set up
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# frontend/app.py
|
# frontend/app.py
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
import requests
|
import requests
|
||||||
from auracast import auracast_config
|
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)
|
# Global: desired packetization time in ms for Opus (should match backend)
|
||||||
PTIME = 40
|
PTIME = 40
|
||||||
BACKEND_URL = "http://localhost:5000"
|
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
|
# Try loading persisted settings from backend
|
||||||
saved_settings = {}
|
saved_settings = {}
|
||||||
@@ -25,7 +33,7 @@ except Exception:
|
|||||||
st.title("🎙️ Auracast Audio Mode Control")
|
st.title("🎙️ Auracast Audio Mode Control")
|
||||||
|
|
||||||
# Audio mode selection with persisted default
|
# Audio mode selection with persisted default
|
||||||
options = ["Webapp", "USB"]
|
options = ["Webapp", "USB", "Demo"]
|
||||||
saved_audio_mode = saved_settings.get("audio_mode", "Webapp")
|
saved_audio_mode = saved_settings.get("audio_mode", "Webapp")
|
||||||
if saved_audio_mode not in options:
|
if saved_audio_mode not in options:
|
||||||
saved_audio_mode = "Webapp"
|
saved_audio_mode = "Webapp"
|
||||||
@@ -34,18 +42,108 @@ audio_mode = st.selectbox(
|
|||||||
"Audio Mode",
|
"Audio Mode",
|
||||||
options,
|
options,
|
||||||
index=options.index(saved_audio_mode),
|
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"]:
|
if audio_mode == "Demo":
|
||||||
# Stream quality selection (now enabled)
|
demo_stream_map = {
|
||||||
quality_map = {
|
"1 × 48kHz": {"quality": "High (48kHz)", "streams": 1,},
|
||||||
"High (48kHz)": {"rate": 48000, "octets": 120},
|
"2 × 24kHz": {"quality": "Medium (24kHz)", "streams": 2,},
|
||||||
"Good (32kHz)": {"rate": 32000, "octets": 80},
|
"3 × 16kHz": {"quality": "Fair (16kHz)", "streams": 3,},
|
||||||
"Medium (24kHz)": {"rate": 24000, "octets": 60},
|
|
||||||
"Fair (16kHz)": {"rate": 16000, "octets": 40},
|
|
||||||
}
|
}
|
||||||
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]
|
default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0]
|
||||||
quality = st.selectbox(
|
quality = st.selectbox(
|
||||||
"Stream Quality (Sampling Rate)",
|
"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
|
input_device = selected_option.split(":", 1)[0] if ":" in selected_option else selected_option
|
||||||
else:
|
else:
|
||||||
input_device = None
|
input_device = None
|
||||||
import time
|
|
||||||
start_stream = st.button("Start Auracast")
|
start_stream = st.button("Start Auracast")
|
||||||
stop_stream = st.button("Stop Auracast")
|
stop_stream = st.button("Stop Auracast")
|
||||||
|
|
||||||
@@ -164,13 +262,13 @@ if audio_mode in ["Webapp", "USB"]:
|
|||||||
st.success("Stream Stopped!")
|
st.success("Stream Stopped!")
|
||||||
|
|
||||||
# Small pause lets backend fully release audio devices before re-init
|
# 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)
|
# 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(
|
config = auracast_config.AuracastConfigGroup(
|
||||||
auracast_sampling_rate_hz=q['rate'],
|
auracast_sampling_rate_hz=q['rate'],
|
||||||
octets_per_frame=q['octets'],
|
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 = [
|
bigs = [
|
||||||
auracast_config.AuracastBigConfig(
|
auracast_config.AuracastBigConfig(
|
||||||
name=stream_name,
|
name=stream_name,
|
||||||
@@ -254,9 +352,9 @@ if audio_mode in ["Webapp", "USB"]:
|
|||||||
"""
|
"""
|
||||||
st.components.v1.html(component, height=0)
|
st.components.v1.html(component, height=0)
|
||||||
st.session_state['stream_started'] = True
|
st.session_state['stream_started'] = True
|
||||||
else:
|
#else:
|
||||||
st.header("Advertised Streams (Cloud Announcements)")
|
# st.header("Advertised Streams (Cloud Announcements)")
|
||||||
st.info("This feature requires backend support to list advertised streams.")
|
# st.info("This feature requires backend support to list advertised streams.")
|
||||||
# Placeholder for future implementation
|
# Placeholder for future implementation
|
||||||
# Example: r = requests.get(f"{BACKEND_URL}/advertised_streams")
|
# Example: r = requests.get(f"{BACKEND_URL}/advertised_streams")
|
||||||
# if r.status_code == 200:
|
# if r.status_code == 200:
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ async def initialize(conf: auracast_config.AuracastConfigGroup):
|
|||||||
elif first_source == 'webrtc':
|
elif first_source == 'webrtc':
|
||||||
audio_mode_persist = 'Webapp'
|
audio_mode_persist = 'Webapp'
|
||||||
input_device = None
|
input_device = None
|
||||||
|
elif first_source.startswith('file:'):
|
||||||
|
audio_mode_persist = 'Demo'
|
||||||
|
input_device = None
|
||||||
else:
|
else:
|
||||||
audio_mode_persist = 'Network'
|
audio_mode_persist = 'Network'
|
||||||
input_device = None
|
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
|
# 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
|
# streamer is started by the /offer handler once the WebRTC track arrives so we know
|
||||||
# the peer connection is established.
|
# 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()
|
await multicaster.start_streaming()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("Exception in /init: %s", traceback.format_exc())
|
log.error("Exception in /init: %s", traceback.format_exc())
|
||||||
@@ -362,6 +367,8 @@ async def shutdown():
|
|||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
import os
|
||||||
|
os.chdir(os.path.dirname(__file__))
|
||||||
import uvicorn
|
import uvicorn
|
||||||
log.basicConfig( # for debug log level export LOG_LEVEL=DEBUG
|
log.basicConfig( # for debug log level export LOG_LEVEL=DEBUG
|
||||||
level=os.environ.get('LOG_LEVEL', log.INFO),
|
level=os.environ.get('LOG_LEVEL', log.INFO),
|
||||||
|
|||||||
BIN
src/auracast/testdata/wave_particle_5min_de_16kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_de_16kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_de_24kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_de_24kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_de_48kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_de_48kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_en_16kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_en_16kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_en_24kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_en_24kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_en_48kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_en_48kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_es_16kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_es_16kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_es_24kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_es_24kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_es_48kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_es_48kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_fr_16kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_fr_16kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_fr_24kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_fr_24kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_fr_48kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_fr_48kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_it_16kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_it_16kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_it_24kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_it_24kHz_mono.wav
vendored
Normal file
Binary file not shown.
BIN
src/auracast/testdata/wave_particle_5min_it_48kHz_mono.wav
vendored
Normal file
BIN
src/auracast/testdata/wave_particle_5min_it_48kHz_mono.wav
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user