# 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 (
{/* Mixer Header / Tab Indicator */}
{currentTitle}
{isSectionTab && ( Routed to Main Track: {activeTabContext.parent_track_name || "Track Host"} )}
{/* Mixer Strips Container */}
{/* 1. MASTER STRIP (Far Left) */}
{/* 2. TRACK STRIPS (Audio Channels) */} {currentTracks.map((track, idx) => ( ))}
); } /** * 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 (
{/* 1. Pan Knob */}
{pan === 0 ? "center" : pan < 0 ? `L${Math.abs(Math.round(pan * 100))}` : `R${Math.round(pan * 100)}`} { 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" />
{/* 2. dB Readout */}
{volumeDb <= -60 ? "-inf" : `${volumeDb > 0 ? "+" : ""}${volumeDb.toFixed(1)}dB`}
{/* 3. Mute / Solo Buttons */}
{/* 4. Fader & Meter Section */}
{/* Fader Slider */} {/* VU Peak Meter Bar */}
{/* 5. FX & Power Buttons */}
{/* 6. Footer Name */}
{index}. {track.name}
{track.type}
); } /** * Master Channel Strip Component */ function MasterChannelStrip({ isSectionBus, mixerGraphMgr }) { return (
{isSectionBus ? "SEC BUS" : "MASTER"}
{/* Peak / RMS Canvas Meter */}
-inf dB
{isSectionBus ? "SUB-MIX" : "MAIN OUT"}
); } ``` --- ## 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.