fix: AI Suggestion for MIDI Item rearrange
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,532 @@
|
||||
# TECHNICAL SPECIFICATION & SYSTEM ARCHITECTURE FOR MAIN SESSION AND SECTION-TAB MIXER
|
||||
|
||||
This document details the implementation of a docked Mixer Console (Mixer Control Panel - MCP) positioned at the bottom of the screen (similar to REAPER), alongside the hierarchical audio signal routing architecture when SECTION-TABs are nested within MAIN SESSION tracks.
|
||||
|
||||
---
|
||||
|
||||
## 1. HIERARCHICAL AUDIO SIGNAL ROUTING ARCHITECTURE
|
||||
|
||||
To allow a SECTION-TAB to maintain an independent mix while assigning final master control of its output to a single track in the MAIN SESSION, the system employs a **Sub-Mix Bus Piping** model via the Web Audio API.
|
||||
|
||||
### Audio Signal Graph Tree
|
||||
|
||||
```text
|
||||
[ SECTION-TAB (Sub-Session) ]
|
||||
├── Track S1 (Audio/MIDI) ──► Gain ──► Pan ──┐
|
||||
├── Track S2 (Audio/MIDI) ──► Gain ──► Pan ──┼──► [ SECTION SUB-MIX BUS ]
|
||||
└── Track S3 (Audio/MIDI) ──► Gain ──► Pan ──┘ │ (Section Master Out)
|
||||
│
|
||||
▼ (Piped to host track)
|
||||
[ MAIN SESSION (Root Session) ] │
|
||||
├── Track 1 (Normal Audio) ──► Gain ──► Pan ─────────────┼──┐
|
||||
├── Track 2 (SECTION ITEM Track) ◄───────────────────────┘ │
|
||||
│ └─ Gain Node ──► FX Chain ──► Pan Node ───────────────┼──► [ MAIN MASTER BUS ]
|
||||
└── Track 3 (Normal MIDI) ──► Gain ──► Pan ─────────────┘ │
|
||||
▼
|
||||
[ AudioContext Destination ]
|
||||
(Speakers / Soundcard)
|
||||
|
||||
```
|
||||
|
||||
### Audio Routing Principles:
|
||||
|
||||
* **Inside SECTION-TAB:** Internal tracks ($S1, S2, S3$) are balanced via volume, pan, and Mute/Solo controls on the SECTION-TAB's dedicated mixer. Output signals from these tracks are summed at the `Section Sub-Mix Bus Node`.
|
||||
* **Routing to MAIN SESSION:** The output of the `Section Sub-Mix Bus Node` does not connect directly to `audioCtx.destination`. Instead, it routes into the Input Node of Track 2 on the MAIN SESSION (the track hosting the `SECTION_ITEM`).
|
||||
* **MAIN SESSION Management:**
|
||||
* The Volume Fader on Track 2 of the MAIN SESSION controls the overall volume of the entire SECTION-TAB.
|
||||
* Mute, Solo, and FX Chain controls on Track 2 of the MAIN SESSION directly process all audio originating from that SECTION-TAB before it reaches the Main Master Bus.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 2. DOCKED MIXER CONSOLE (MCP) UI STRUCTURE
|
||||
|
||||
The Mixer Console is designed as a docked panel underneath the Timeline, split into two main sections: **Master Channel Strip** (Left) and **Track Channel Strips** (Right).
|
||||
|
||||
```text
|
||||
+------------------------------------------------------------------------------------------------+
|
||||
| DOCKED MIXER CONSOLE |
|
||||
+-------------------+----------------------------------------------------------------------------+
|
||||
| MASTER STRIP | TRACK CHANNEL STRIPS (Track 1, Track 2 [SECTION], Track 3...) |
|
||||
| | |
|
||||
| (Knob) Pan | (Knob) Pan (Knob) Pan (Knob) Pan (Knob) Pan |
|
||||
| [MONO] | -inf dB -inf dB -inf dB -inf dB |
|
||||
| [M] [S] | [M] [S] [M] [S] [M] [S] [M] [S] |
|
||||
| [ROUTE] | [|||] [|||] Peak [|||] [|||] Peak [|||] [|||] Peak [|||] [|||] Peak|
|
||||
| [FX] [Power] | [===] Fader [===] Fader [===] Fader [===] Fader |
|
||||
| [TRIM] | [FX] [Power] [FX] [Power] [FX] [Power] [FX] [Power] |
|
||||
| [FADER] (VU) | [🔴] [ROUTE] [🔴] [ROUTE] [🔴] [ROUTE] [🔴] [ROUTE] |
|
||||
| | ------------------ ------------------ ------------------ ----------------|
|
||||
| RMS/Peak Meter | Track 1 (Kick) Track 2 (SECTION) Track 3 (Guitars) Track 4 (Lead) |
|
||||
| MASTER | [🎵 SF2 Synth] [📦 Sec: Chorus] [🎸 Pedalboard] [🎹 Vital] |
|
||||
+-------------------+----------------------------------------------------------------------------+
|
||||
|
||||
```
|
||||
|
||||
### Channel Strip Components:
|
||||
|
||||
* **Top Pan Knob:** Rotatable control for Left/Right stereo balance (Pan Left/Right, $-1.0$ to $+1.0$).
|
||||
* **Numeric dB Readout:** Displays current volume level in numeric dB (e.g., $0.0\text{ dB}$, $-6.5\text{ dB}$, $-inf$).
|
||||
* **Mute [M] & Solo [S] Buttons:**
|
||||
* Orange **[M]** button: Mutes track output.
|
||||
* Yellow **[S]** button: Solos track output (mutes all non-soloed tracks).
|
||||
|
||||
|
||||
* **Vertical Level Meter (VU Peak Meter):** Real-time LED signal amplitude meter (Green $\rightarrow$ Yellow $\rightarrow$ Red clipping indicator above $0\text{ dBFS}$).
|
||||
* **Volume Fader Slider:** Vertical linear slider for gain control.
|
||||
* **FX & Power Toggle:**
|
||||
* **[FX]** button: Opens the Effects Plugin management window (Reverb, Delay, EQ, Compressor).
|
||||
* **[Power]** button: Toggles bypass state for the track's entire FX chain.
|
||||
|
||||
|
||||
* **Record Arm [🔴] & Routing/Trim Buttons:**
|
||||
* Red circle **[🔴]**: Arms track for recording.
|
||||
* **[ROUTE]** button: Opens the Input/Output Matrix configuration panel.
|
||||
|
||||
|
||||
* **Track Identifier Footer:** Displays track index ($1, 2, 3...$) and track name (e.g., *Track 2 - Chorus Section*).
|
||||
|
||||
---
|
||||
|
||||
## 3. STATE STORE SCHEMAS
|
||||
|
||||
### 3.1 Main Session State (`main_session_mixer_state`)
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "main_session_root",
|
||||
"master_bus": {
|
||||
"volume_db": 0.0,
|
||||
"pan": 0.0,
|
||||
"mute": false,
|
||||
"is_mono": false,
|
||||
"fx_chain": []
|
||||
},
|
||||
"tracks": [
|
||||
{
|
||||
"id": "track_01",
|
||||
"name": "Track 1 - Drums",
|
||||
"type": "AUDIO",
|
||||
"volume_db": -2.5,
|
||||
"pan": 0.0,
|
||||
"mute": false,
|
||||
"solo": false,
|
||||
"is_armed": false
|
||||
},
|
||||
{
|
||||
"id": "track_02_section_host",
|
||||
"name": "Track 2 - Chorus Section",
|
||||
"type": "SECTION",
|
||||
"volume_db": 0.0,
|
||||
"pan": 0.0,
|
||||
"mute": false,
|
||||
"solo": false,
|
||||
"referenced_section_id": "section_chorus_001"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 3.2 Section-Tab State (`section_store`)
|
||||
|
||||
```json
|
||||
{
|
||||
"section_store": {
|
||||
"section_chorus_001": {
|
||||
"id": "section_chorus_001",
|
||||
"name": "Chorus Section Tab",
|
||||
"is_root": false,
|
||||
"parent_track_id": "track_02_section_host",
|
||||
"sub_mix_bus": {
|
||||
"volume_db": 0.0,
|
||||
"pan": 0.0,
|
||||
"mute": false,
|
||||
"fx_chain": []
|
||||
},
|
||||
"tracks": [
|
||||
{
|
||||
"id": "sec_track_1",
|
||||
"name": "Sec Guitar 1",
|
||||
"volume_db": -1.0,
|
||||
"pan": -0.5,
|
||||
"mute": false,
|
||||
"solo": false
|
||||
},
|
||||
{
|
||||
"id": "sec_track_2",
|
||||
"name": "Sec Vocal Lead",
|
||||
"volume_db": +1.5,
|
||||
"pan": 0.0,
|
||||
"mute": false,
|
||||
"solo": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. AUDIO GRAPH NODE ENGINE (WEB AUDIO API)
|
||||
|
||||
To execute audio summing according to the architecture above, the application uses a manager class to maintain Web Audio nodes (`AudioNode Graph`):
|
||||
|
||||
```javascript
|
||||
// app/static/js/services/audioMixerGraphManager.js
|
||||
|
||||
export class AudioMixerGraphManager {
|
||||
constructor(audioCtx) {
|
||||
this.audioCtx = audioCtx;
|
||||
|
||||
// Main Session Master Nodes
|
||||
this.mainMasterGain = this.audioCtx.createGain();
|
||||
this.mainMasterPan = this.audioCtx.createStereoPanner();
|
||||
this.mainMasterAnalyser = this.audioCtx.createAnalyser();
|
||||
|
||||
// Connect Master Graph -> Speakers
|
||||
this.mainMasterGain
|
||||
.connect(this.mainMasterPan)
|
||||
.connect(this.mainMasterAnalyser)
|
||||
.connect(this.audioCtx.destination);
|
||||
|
||||
// Map storing track AudioNodes
|
||||
this.trackNodesMap = new Map(); // trackId -> { inputGain, faderGain, panNode, analyserNode }
|
||||
this.sectionBusesMap = new Map(); // sectionId -> { subMixGain, subMixPan, outputNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes Audio Nodes for a track on the Main Session
|
||||
*/
|
||||
createMainTrackNodes(track) {
|
||||
const inputGain = this.audioCtx.createGain();
|
||||
const faderGain = this.audioCtx.createGain();
|
||||
const panNode = this.audioCtx.createStereoPanner();
|
||||
const analyserNode = this.audioCtx.createAnalyser();
|
||||
analyserNode.fftSize = 64;
|
||||
|
||||
// Track internal signal chain connection:
|
||||
// InputGain -> FaderGain -> PanNode -> Analyser -> Main Master Gain
|
||||
inputGain
|
||||
.connect(faderGain)
|
||||
.connect(panNode)
|
||||
.connect(analyserNode)
|
||||
.connect(this.mainMasterGain);
|
||||
|
||||
const nodeBundle = { inputGain, faderGain, panNode, analyserNode, track };
|
||||
this.trackNodesMap.set(track.id, nodeBundle);
|
||||
|
||||
// Update initial Gain/Pan values
|
||||
this.updateTrackVolumePan(track.id, track.volume_db, track.pan, track.mute);
|
||||
return nodeBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a Sub-Mix Bus for a SECTION-TAB and PIPES it to a Main Session track
|
||||
* @param {string} sectionId - Section ID
|
||||
* @param {string} hostTrackId - Main Session Track ID hosting this section
|
||||
*/
|
||||
createSectionSubMixBus(sectionId, hostTrackId) {
|
||||
const subMixGain = this.audioCtx.createGain();
|
||||
const subMixPan = this.audioCtx.createStereoPanner();
|
||||
|
||||
// Section Sub-Mix Bus internal connection
|
||||
subMixGain.connect(subMixPan);
|
||||
|
||||
// CRITICAL STEP: Locate host track on Main Session to pipe signal
|
||||
const hostTrackNodes = this.trackNodesMap.get(hostTrackId);
|
||||
|
||||
if (hostTrackNodes) {
|
||||
// Pipe Output of Section Bus directly into InputGain of Main Track
|
||||
subMixPan.connect(hostTrackNodes.inputGain);
|
||||
console.log(`[MixerRouter] Sub-Mix Bus of Section "${sectionId}" successfully PIPED into Main Track "${hostTrackId}"`);
|
||||
} else {
|
||||
console.warn(`[MixerRouter] Host Track "${hostTrackId}" not found. Section bus falling back to Master.`);
|
||||
subMixPan.connect(this.mainMasterGain);
|
||||
}
|
||||
|
||||
const sectionBundle = { subMixGain, subMixPan, hostTrackId };
|
||||
this.sectionBusesMap.set(sectionId, sectionBundle);
|
||||
return sectionBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an INTERNAL track inside a SECTION-TAB
|
||||
*/
|
||||
createSectionTrackNodes(sectionId, secTrack) {
|
||||
const sectionBundle = this.sectionBusesMap.get(sectionId);
|
||||
if (!sectionBundle) {
|
||||
throw new Error(`Section Sub-Mix Bus for "${sectionId}" has not been initialized!`);
|
||||
}
|
||||
|
||||
const inputGain = this.audioCtx.createGain();
|
||||
const faderGain = this.audioCtx.createGain();
|
||||
const panNode = this.audioCtx.createStereoPanner();
|
||||
const analyserNode = this.audioCtx.createAnalyser();
|
||||
|
||||
// Connect Track in Section to the Section's Sub-Mix Bus
|
||||
inputGain
|
||||
.connect(faderGain)
|
||||
.connect(panNode)
|
||||
.connect(analyserNode)
|
||||
.connect(sectionBundle.subMixGain); // Direct to Section Sub-Mix Bus!
|
||||
|
||||
const nodeBundle = { inputGain, faderGain, panNode, analyserNode, secTrack };
|
||||
this.trackNodesMap.set(secTrack.id, nodeBundle);
|
||||
|
||||
this.updateTrackVolumePan(secTrack.id, secTrack.volume_db, secTrack.pan, secTrack.mute);
|
||||
return nodeBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates Volume (dB) & Pan using standard audio formulas
|
||||
*/
|
||||
updateTrackVolumePan(trackId, volumeDb, panValue, isMuted) {
|
||||
const bundle = this.trackNodesMap.get(trackId);
|
||||
if (!bundle) return;
|
||||
|
||||
// Convert dB to Linear Gain Factor: Gain = 10 ^ (dB / 20)
|
||||
const linearGain = isMuted ? 0.0 : Math.pow(10, volumeDb / 20.0);
|
||||
|
||||
bundle.faderGain.gain.setTargetAtTime(linearGain, this.audioCtx.currentTime, 0.01);
|
||||
bundle.panNode.pan.setTargetAtTime(panValue, this.audioCtx.currentTime, 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. NESTED MUTE/SOLO LOGIC MATRIX
|
||||
|
||||
When muting or soloing a track on the Main Session or within a Section Tab, the system applies hierarchical rules:
|
||||
|
||||
| User Action | Main Session Impact | Internal Section Tracks Impact |
|
||||
| --- | --- | --- |
|
||||
| **Mute Track 2 (Section Host)** on Main Session | Track 2 is muted. Other tracks play normally. | Entire Section is muted (signal is blocked at Track 2's Fader Gain). |
|
||||
| **Mute Section Track 1** inside Section Tab | No impact on other Main Tracks. | Only Section Track 1 is silenced. Other Section Tracks ($S2, S3$) continue routing to Track 2. |
|
||||
| **Solo Section Track 1** inside Section Tab | No impact on Main Session. | Mutes $S2, S3$ within the Section. Only $S1$ outputs to Track 2 of the Main Session. |
|
||||
| **Solo Track 2** on Main Session | Mutes Track 1 and Track 3 on Main Session. | The Section audio stream plays normally to the Main Master Bus. |
|
||||
|
||||
---
|
||||
|
||||
## 6. STATE-DRIVEN REACT/CANVAS COMPONENT
|
||||
|
||||
Below is the React implementation for the Mixer Console supporting dynamic switching between MAIN SESSION and SECTION-TAB mixers:
|
||||
|
||||
```jsx
|
||||
// app/static/js/components/MixerConsole.jsx
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
export function MixerConsole({ sessionState, activeTabContext, mixerGraphMgr }) {
|
||||
// Determine if Mixer is rendering for MAIN SESSION or SECTION-TAB
|
||||
const isSectionTab = activeTabContext?.type === 'SECTION_TAB';
|
||||
|
||||
// Retrieve target tracks list for Mixer Console display
|
||||
const currentTracks = isSectionTab
|
||||
? sessionState.section_store[activeTabContext.referenced_section_id]?.tracks || []
|
||||
: sessionState.main_session.tracks;
|
||||
|
||||
const currentTitle = isSectionTab
|
||||
? `MIXER: SECTION TAB (${activeTabContext.title})`
|
||||
: "MIXER: MAIN SESSION";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-64 bg-slate-900 border-t border-slate-700 text-slate-200 select-none">
|
||||
{/* Mixer Header / Tab Indicator */}
|
||||
<div className="flex items-center justify-between px-3 py-1 bg-slate-950 border-b border-slate-800 text-xs font-semibold">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`w-2.5 h-2.5 rounded-full ${isSectionTab ? "bg-amber-400 animate-pulse" : "bg-indigo-500"}`} />
|
||||
<span className="text-white tracking-wide">{currentTitle}</span>
|
||||
</div>
|
||||
{isSectionTab && (
|
||||
<span className="text-[10px] text-amber-300/80 bg-amber-950/60 px-2 py-0.5 rounded border border-amber-800/50">
|
||||
Routed to Main Track: {activeTabContext.parent_track_name || "Track Host"}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Mixer Strips Container */}
|
||||
<div className="flex-1 flex overflow-x-auto p-2 gap-1.5 custom-scrollbar bg-slate-900/90">
|
||||
{/* 1. MASTER STRIP (Far Left) */}
|
||||
<MasterChannelStrip
|
||||
isSectionBus={isSectionTab}
|
||||
mixerGraphMgr={mixerGraphMgr}
|
||||
/>
|
||||
|
||||
<div className="w-[1px] bg-slate-700/60 mx-1 my-1" />
|
||||
|
||||
{/* 2. TRACK STRIPS (Audio Channels) */}
|
||||
{currentTracks.map((track, idx) => (
|
||||
<TrackChannelStrip
|
||||
key={track.id}
|
||||
track={track}
|
||||
index={idx + 1}
|
||||
mixerGraphMgr={mixerGraphMgr}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel Strip Component representing a single Track
|
||||
*/
|
||||
function TrackChannelStrip({ track, index, mixerGraphMgr }) {
|
||||
const [volumeDb, setVolumeDb] = useState(track.volume_db || 0);
|
||||
const [pan, setPan] = useState(track.pan || 0);
|
||||
const [isMuted, setIsMuted] = useState(Boolean(track.mute));
|
||||
const [isSoloed, setIsSoloed] = useState(Boolean(track.solo));
|
||||
|
||||
const handleVolumeChange = (e) => {
|
||||
const newDb = parseFloat(e.target.value);
|
||||
setVolumeDb(newDb);
|
||||
track.volume_db = newDb;
|
||||
mixerGraphMgr?.updateTrackVolumePan(track.id, newDb, pan, isMuted);
|
||||
};
|
||||
|
||||
const handleToggleMute = () => {
|
||||
const newMute = !isMuted;
|
||||
setIsMuted(newMute);
|
||||
track.mute = newMute;
|
||||
mixerGraphMgr?.updateTrackVolumePan(track.id, volumeDb, pan, newMute);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-24 bg-slate-950 border border-slate-800 rounded flex flex-col p-1.5 items-center justify-between shadow-lg">
|
||||
{/* 1. Pan Knob */}
|
||||
<div className="flex flex-col items-center w-full">
|
||||
<span className="text-[10px] text-slate-400 font-mono">
|
||||
{pan === 0 ? "center" : pan < 0 ? `L${Math.abs(Math.round(pan * 100))}` : `R${Math.round(pan * 100)}`}
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="-1"
|
||||
max="1"
|
||||
step="0.05"
|
||||
value={pan}
|
||||
onChange={(e) => {
|
||||
const p = parseFloat(e.target.value);
|
||||
setPan(p);
|
||||
track.pan = p;
|
||||
mixerGraphMgr?.updateTrackVolumePan(track.id, volumeDb, p, isMuted);
|
||||
}}
|
||||
className="w-12 h-1 accent-indigo-500 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 2. dB Readout */}
|
||||
<div className="text-[11px] font-mono text-slate-300 font-semibold my-0.5">
|
||||
{volumeDb <= -60 ? "-inf" : `${volumeDb > 0 ? "+" : ""}${volumeDb.toFixed(1)}dB`}
|
||||
</div>
|
||||
|
||||
{/* 3. Mute / Solo Buttons */}
|
||||
<div className="flex gap-1 w-full my-1">
|
||||
<button
|
||||
onClick={handleToggleMute}
|
||||
className={`flex-1 text-[10px] font-bold py-0.5 rounded border transition-colors ${
|
||||
isMuted ? "bg-amber-600 text-white border-amber-500" : "bg-slate-800 text-slate-400 border-slate-700 hover:bg-slate-700"
|
||||
}`}
|
||||
>
|
||||
M
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const s = !isSoloed;
|
||||
setIsSoloed(s);
|
||||
track.solo = s;
|
||||
}}
|
||||
className={`flex-1 text-[10px] font-bold py-0.5 rounded border transition-colors ${
|
||||
isSoloed ? "bg-yellow-500 text-black border-yellow-400" : "bg-slate-800 text-slate-400 border-slate-700 hover:bg-slate-700"
|
||||
}`}
|
||||
>
|
||||
S
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 4. Fader & Meter Section */}
|
||||
<div className="flex-1 flex items-center justify-center gap-1.5 w-full my-1 relative">
|
||||
{/* Fader Slider */}
|
||||
<input
|
||||
type="range"
|
||||
min="-60"
|
||||
max="12"
|
||||
step="0.5"
|
||||
value={volumeDb}
|
||||
onChange={handleVolumeChange}
|
||||
className="h-28 w-2 appearance-none bg-slate-800 rounded outline-none cursor-pointer [writing-mode:vertical-lr] [direction:rtl]"
|
||||
/>
|
||||
|
||||
{/* VU Peak Meter Bar */}
|
||||
<div className="w-2.5 h-28 bg-slate-900 border border-slate-800 rounded overflow-hidden flex flex-col justify-end p-0.5">
|
||||
<div className="w-full bg-gradient-to-t from-emerald-500 via-yellow-400 to-red-500 h-[40%]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 5. FX & Power Buttons */}
|
||||
<div className="flex gap-1 w-full my-1">
|
||||
<button className="flex-1 bg-slate-800 hover:bg-slate-700 text-slate-300 text-[9px] py-0.5 rounded border border-slate-700 font-semibold">
|
||||
FX
|
||||
</button>
|
||||
<button className="px-1.5 bg-slate-800 hover:bg-slate-700 text-slate-400 text-[9px] py-0.5 rounded border border-slate-700">
|
||||
⑈
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 6. Footer Name */}
|
||||
<div className="w-full text-center bg-slate-900 py-1 rounded border border-slate-800/80 mt-1">
|
||||
<div className="text-[10px] font-bold text-white truncate px-1">{index}. {track.name}</div>
|
||||
<div className="text-[8px] text-slate-500 uppercase tracking-tighter truncate">{track.type}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Master Channel Strip Component
|
||||
*/
|
||||
function MasterChannelStrip({ isSectionBus, mixerGraphMgr }) {
|
||||
return (
|
||||
<div className="w-28 bg-slate-950 border-2 border-indigo-900/60 rounded flex flex-col p-1.5 items-center justify-between shadow-2xl">
|
||||
<div className="text-[10px] font-bold text-indigo-400 uppercase tracking-wider">
|
||||
{isSectionBus ? "SEC BUS" : "MASTER"}
|
||||
</div>
|
||||
|
||||
{/* Peak / RMS Canvas Meter */}
|
||||
<div className="w-full h-32 bg-black border border-indigo-950 rounded relative my-2 overflow-hidden flex items-center justify-center">
|
||||
<span className="text-[9px] font-mono text-emerald-400/80 font-semibold">-inf dB</span>
|
||||
</div>
|
||||
|
||||
<div className="w-full text-center bg-indigo-950/80 border border-indigo-800/60 py-1 rounded mt-auto">
|
||||
<div className="text-[10px] font-bold text-indigo-200 uppercase">
|
||||
{isSectionBus ? "SUB-MIX" : "MAIN OUT"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. OPERATIONAL VERIFICATION CHECKLIST
|
||||
|
||||
* [ ] **Main Session Mixer Operations:**
|
||||
* Adjust Track 1 Fader $\rightarrow$ Track 1 volume increases/decreases as expected.
|
||||
* Mute Track 1 $\rightarrow$ Track 1 output is silenced.
|
||||
|
||||
|
||||
* [ ] **SECTION-TAB Mixer & Routing Operations:**
|
||||
* Open a SECTION-TAB into a sub-tab $\rightarrow$ Mixer Console dynamically switches header to `MIXER: SECTION TAB` displaying internal section tracks.
|
||||
* Adjust Section Track 1 Fader $\rightarrow$ Guitar volume within the Section increases/decreases without affecting other Main Tracks.
|
||||
|
||||
|
||||
* [ ] **Bus Routing Point Verification:**
|
||||
* Switch back to MAIN SESSION tab.
|
||||
* Adjust Track 2 Fader (Track hosting Section Item) $\rightarrow$ Entire SECTION-TAB volume adjusts synchronously.
|
||||
* Mute Track 2 $\rightarrow$ Entire SECTION-TAB output is silenced completely.
|
||||
Reference in New Issue
Block a user