audio interface: separate input/output device selectors

- Added Output Device dropdown alongside Input Device
- loadAudioDevices() now populates both dropdowns from the API and
  selects current values
- Separated setAudioInputDevice / setAudioOutputDevice functions
  (each calls API with only its respective field)
- Added input_device / output_device to GET /api/audio/profile response
- Removed the old combined setAudioDevice function
This commit is contained in:
2026-06-16 19:13:25 -04:00
parent 1530aa266f
commit 93610e4d98
2 changed files with 36 additions and 12 deletions
+2
View File
@@ -1353,6 +1353,8 @@ class WebServer:
"rate": profile["rate"],
"jack_running": True,
"xrun_count": xruns,
"input_device": self.deps.audio_system.config.input_device,
"output_device": self.deps.audio_system.config.output_device,
}
@app.post("/api/audio/profile")
+34 -12
View File
@@ -283,7 +283,8 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
<div class="setting-row"><span class="sr-label">🔗 Routing</span><span class="sr-value" id="routingModeLabel">4CM</span><button class="sr-action" id="routingModeToggle">Toggle</button></div>
</div>
<div class="setting-group" id="section-interface"><div class="setting-group-title">🎚 Audio Interface</div>
<div class="setting-row"><span class="sr-label">🔌 Input Device</span><select id="audioInputDevice" style="flex:1;min-width:0;font-size:10px;padding:4px 6px;min-height:30px" onchange="setAudioDevice(this.value)"></select></div>
<div class="setting-row"><span class="sr-label">🔌 Input Device</span><select id="audioInputDevice" onchange="setAudioInputDevice(this.value)" style="flex:1;min-width:0;font-size:10px;padding:4px 6px;min-height:30px"><option value="">Loading...</option></select></div>
<div class="setting-row"><span class="sr-label">🔊 Output Device</span><select id="audioOutputDevice" onchange="setAudioOutputDevice(this.value)" style="flex:1;min-width:0;font-size:10px;padding:4px 6px;min-height:30px"><option value="">Loading...</option></select></div>
<div class="ch-card" id="ch1Card" style="background:var(--card2);border-radius:8px;padding:6px 8px;margin:4px 0;border:2px solid rgba(232,160,48,.25)">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
<span style="font-size:11px;font-weight:700;color:var(--amber)">🔴 CH 1 — Input 1/L</span>
@@ -450,10 +451,36 @@ function hideLoading(){
// Audio Interface helpers
async function loadAudioDevices(){
const sel=document.getElementById('audioInputDevice');
if(!sel)return;
const inSel=document.getElementById('audioInputDevice');
const outSel=document.getElementById('audioOutputDevice');
if(!inSel||!outSel)return;
const devs=await API.listAudioDevices();
sel.innerHTML=(devs||[]).map(d=>'<option value="'+d.id+'">'+d.name+'</option>').join('')||'<option value="">No devices</option>';
const opts=(devs||[]).map(d=>{
const id='hw:'+d.card+','+d.device;
return '<option value="'+id+'">'+d.name+' ('+d.desc+')</option>';
}).join('')||'<option value="">No devices</option>';
inSel.innerHTML=opts;
outSel.innerHTML=opts;
// Load current input/output from audio profile
try{
const p=await API.getAudioProfile();
if(p.input_device)inSel.value=p.input_device;
if(p.output_device)outSel.value=p.output_device;
}catch(e){}
}
async function setAudioInputDevice(id){
if(!id)return;
showLoading('Switching input...');
await API.saveSettings({input_device:id}).catch(()=>{});
hideLoading();
toast('Input: '+id);
}
async function setAudioOutputDevice(id){
if(!id)return;
showLoading('Switching output...');
await API.saveSettings({output_device:id}).catch(()=>{});
hideLoading();
toast('Output: '+id);
}
function setChInstrument(idx,val){
const arr=JSON.parse(localStorage.getItem('chInstruments')||'["guitar","bass"]');
@@ -470,13 +497,7 @@ function toggleChPower(idx){
btn.style.color=on?'var(--text3)':'var(--green)';
API.bypassToggle(channels[idx]||'guitar').catch(()=>{});
}
async function setAudioDevice(devId){
if(!devId)return;
showLoading('Switching audio device...');
await API.saveSettings({input_device:devId,output_device:devId}).catch(()=>{});
hideLoading();
toast('Device: '+devId);
}
function loadChInstrumentState(){
const arr=JSON.parse(localStorage.getItem('chInstruments')||'["guitar","bass"]');
['ch1Instrument','ch2Instrument'].forEach((id,i)=>{
@@ -818,7 +839,7 @@ function init(){
btn.onclick=()=>{
const view=btn.dataset.view;
if(view==='main'){document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));btn.classList.add('active');}
else if(view==='settings'){openOverlay('Settings');loadNetworkInfo();loadSystemInfo();loadAudioParams();loadHotspotStatus();loadBtStatus();loadBtMidi();return;}
else if(view==='settings'){openOverlay('Settings');loadNetworkInfo();loadSystemInfo();loadAudioParams();loadAudioDevices();loadHotspotStatus();loadBtStatus();loadBtMidi();return;}
else if(view==='presets'){openOverlay('Presets');loadPresets();}
else if(view==='snapshots'){openOverlay('Snapshots');loadSnapshots();}
else if(view==='downloads')openOverlay('Downloads');
@@ -831,6 +852,7 @@ function init(){
loadNetworkInfo();
loadSystemInfo();
loadAudioParams();
loadAudioDevices();
loadHotspotStatus();
loadBtStatus();
loadBtMidi();