# 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 (