diff --git a/src/web/ui-v2-dist/index.html b/src/web/ui-v2-dist/index.html
index 6da216d..03be420 100644
--- a/src/web/ui-v2-dist/index.html
+++ b/src/web/ui-v2-dist/index.html
@@ -137,14 +137,6 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
.wifi-connect-pw input{flex:1;padding:6px 8px;border-radius:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-family:var(--font);font-size:11px;outline:none;min-height:36px}
.wifi-connect-pw input:focus{border-color:var(--amber)}
.wifi-connect-pw button{min-width:52px;min-height:36px;border-radius:6px;border:1px solid var(--amber);background:var(--amber);color:#000;font-family:var(--font);font-size:11px;font-weight:700;cursor:pointer}
-.audio-profile-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px}
-.ap-card{background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 6px;cursor:pointer;transition:all .12s;text-align:center}
-.ap-card:active{transform:scale(.95)}
-.ap-card.active{border-color:var(--amber);background:rgba(232,160,48,.08)}
-.ap-card .ap-key{font-size:9px;font-weight:600;text-transform:capitalize;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
-.ap-card .ap-desc{font-size:8px;color:var(--text2)}
-.ap-card.loading{opacity:.5;pointer-events:none}
-.ap-card.loading .ap-key::after{content:' ⟳';animation:spin .6s linear infinite;display:inline-block}
/* Loading overlay */
.loading-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:300;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px}
@@ -268,8 +260,24 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
Audio
-
🎛 Profile—
-
+
🎛 Sample Rate
+
+
+
📦 Buffer Size
+
+
🎸 InstrumentGuitar
🔄 Channel ModeDual Mono
🔗 Routing4CM
@@ -379,7 +387,11 @@ const API = {
btMidiDisable:()=>API._fetch('/api/bluetooth/midi-disable',{method:'POST'}),
btDiscoverableSet:(on)=>API._fetch('/api/bluetooth/discoverable',{method:'POST',body:JSON.stringify({on})}),
listAudioProfiles:()=>API._fetch('/api/audio/profiles'),
- setAudioProfile:(p)=>API._fetch('/api/audio/profile',{method:'POST',body:JSON.stringify({profile:p})}),
+ getAudioProfile:()=>API._fetch('/api/audio/profile'),
+ setAudioProfile:(p)=>{
+ if(typeof p==='string') return API._fetch('/api/audio/profile',{method:'POST',body:JSON.stringify({profile:p})});
+ return API._fetch('/api/audio/profile',{method:'POST',body:JSON.stringify(p)});
+ },
getChannelMode:()=>API._fetch('/api/channel-mode'),
setChannelMode:(m)=>API._fetch('/api/channel-mode',{method:'POST',body:JSON.stringify({channel_mode:m})}),
getRouting:(ch)=>API._fetch('/api/routing?channel='+(ch||'guitar')),
@@ -404,7 +416,6 @@ let currentChannel='guitar';
let channels=['guitar','bass','keys','vocals','backing_tracks'];
let presets=[];
let snapshots={};
-let audioProfiles=[];
let currentBank=0;
let pollTimer=null;
let tunerPollTimer=null;
@@ -671,27 +682,13 @@ async function loadSystemInfo(){
}catch(e){}
}
-async function loadAudioProfiles(){
+async function loadAudioParams(){
try{
- audioProfiles=await API.listAudioProfiles();
- const grid=document.getElementById('audioProfileGrid');
- const cp=audioProfiles.current||'standard';
- grid.innerHTML='';
- (audioProfiles.profiles||[]).forEach(p=>{
- const d=document.createElement('div');
- d.className='ap-card'+(p.key===cp?' active':'');
- d.innerHTML='
'+p.key.replace(/_/g,' ')+'
'+p.period+'fr / '+p.rate+'Hz
'+((p.key===cp)?'
● Active
':'');
- d.onclick=async()=>{
- d.classList.add('loading');
- try{
- await API.setAudioProfile(p.key);
- toast('Profile: '+p.key);
- loadAudioProfiles();
- }catch(e){toast(e.message);d.classList.remove('loading');}
- };
- grid.appendChild(d);
- });
- document.getElementById('audioProfileLabel').textContent=cp;
+ const p=await API.getAudioProfile();
+ const rate=p.rate||48000;
+ const period=p.period||256;
+ document.getElementById('audioSampleRate').value=String(rate);
+ document.getElementById('audioBufferSize').value=String(period);
const cm=await API.getChannelMode();
if(cm.channel_mode)document.getElementById('channelModeLabel').textContent=cm.channel_mode.replace(/-/g,' ').replace(/\b\w/g,l=>l.toUpperCase());
const rt=await API.getRouting(currentChannel);
@@ -699,6 +696,18 @@ async function loadAudioProfiles(){
}catch(e){}
}
+async function setAudioParams(){
+ const rate=parseInt(document.getElementById('audioSampleRate').value);
+ const period=parseInt(document.getElementById('audioBufferSize').value);
+ const btn=document.querySelector('.sn-btn[data-section="audio"]');
+ if(btn)btn.textContent='⏳ Applying...';
+ try{
+ await API.setAudioProfile({period,rate});
+ toast('Audio: '+rate+'Hz / '+period+'fr');
+ }catch(e){toast('Failed: '+e.message);}
+ if(btn)btn.textContent='🎛 Audio';
+}
+
async function loadBtStatus(){
try{
const st=await API.btStatus();
@@ -809,7 +818,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();loadAudioProfiles();loadHotspotStatus();loadBtStatus();loadBtMidi();return;}
+ else if(view==='settings'){openOverlay('Settings');loadNetworkInfo();loadSystemInfo();loadAudioParams();loadHotspotStatus();loadBtStatus();loadBtMidi();return;}
else if(view==='presets'){openOverlay('Presets');loadPresets();}
else if(view==='snapshots'){openOverlay('Snapshots');loadSnapshots();}
else if(view==='downloads')openOverlay('Downloads');
@@ -821,7 +830,7 @@ function init(){
openOverlay('Settings');
loadNetworkInfo();
loadSystemInfo();
- loadAudioProfiles();
+ loadAudioParams();
loadHotspotStatus();
loadBtStatus();
loadBtMidi();
@@ -844,11 +853,11 @@ function init(){
document.getElementById('channelModeToggle').onclick=async()=>{
const cur=document.getElementById('channelModeLabel').textContent.toLowerCase();
const next=cur.includes('dual')?'stereo':'dual-mono';
- try{await API.setChannelMode(next);loadAudioProfiles();toast('Mode: '+next);}catch(e){toast(e.message);}
+ try{await API.setChannelMode(next);loadAudioParams();toast('Mode: '+next);}catch(e){toast(e.message);}
};
document.getElementById('routingModeToggle').onclick=async()=>{
const cur=document.getElementById('routingModeLabel').textContent;
- try{await API.setRouting(currentChannel,{routing_mode:cur==='MONO'?'4cm':'mono'});loadAudioProfiles();}catch(e){toast(e.message);}
+ try{await API.setRouting(currentChannel,{routing_mode:cur==='MONO'?'4cm':'mono'});loadAudioParams();}catch(e){toast(e.message);}
};
document.getElementById('netModeToggle').onclick=async()=>{
const lbl=document.getElementById('netDhcpStatus');
@@ -883,7 +892,7 @@ function init(){
await API.saveSettings({channel_mode:'dual-mono',brightness:8,tap_tempo:120});
await API.setRouting(currentChannel,{routing_mode:'mono'}).catch(()=>{});
toast('Defaults restored');
- setTimeout(()=>{closeOverlay('Settings');loadAudioProfiles();},1000);
+ setTimeout(()=>{closeOverlay('Settings');loadAudioParams();},1000);
}catch(e){toast('Failed: '+e.message);}
};
document.getElementById('btnRestartSvc').onclick=()=>{toast('Restarting service...');fetch('/api/settings',{method:'POST',body:JSON.stringify({_restart:true})}).catch(()=>{});};