# Technical Specification: Sandbox Isolation & Sub-Tab DSP Editing Algorithms This document defines the processing workflow design and digital signal processing (DSP) algorithms dedicated to localized clip editing within a temporary isolated document workspace (Sub-tab). --- ## 1. Sandbox Splicing Workflow When a user highlights a time region on the Main Tab and triggers "Edit in Sub-tab" or presses the edit keyboard shortcut: ```text [ MAIN TAB - MULTITRACK ] Track 01: ───[█████ Selected Segment █████]─── │ ▼ (Copy to Clipboard Buffer) [ KHỔI TẠO TAB TẠM THỜI (SUB-TAB) ] - Instantiates a single Track (Height bounds: 48px - 200px via ns-resize) - Timeline Ruler axis resets to t = 0.0s │ ▼ (Automated Insertion - Auto-Paste) Track 01 (Sub-tab): [█████ Isolated Segment █████] at t = 0s ``` * **Extract Buffer:** The underlying engine extracts the binary sample array (`Float32Array`) of the highlighted region from the active track, caching it securely into the application's clipboard buffer memory. * **Sandbox Environment Initialization:** * Appends a temporary document window onto the global Tab Bar (e.g., `Tab: sẤit tiá...n` or `Sub_Edit_1`). * Focuses the viewport down into the sandboxed tab. Here, a single standalone track lane is drawn, mapping the timeline ruler scale to start at $t = 0.0\text{ s}$ up to the absolute duration limit ($T_{\text{clip}}$) of the extracted audio asset. * **Track Height Resizing:** * Hovering the cursor over the lower layout bounding path of the track lane changes the style configuration to `ns-resize`. * Dragging downward expands the vertical height ceiling (up to an upper bound of $200\text{ px}$), maximizing the waveform amplitude drawing path for precision clip editing. Dragging upward compresses the physical row dimensions (down to a lower constraint of $48\text{ px}$) to protect screen space. * **Auto-Paste Routine:** The framework automates the insertion sequence, dropping the cached array block onto the root index milestone ($t = 0.0\text{ s}$) inside the isolated single-track layer. --- ## 2. Apply & Sync-Back Workflow When an editor finishes processing steps inside the sandbox workspace and engages the *Apply* action: ```text [ SUB-TAB - AUDIO SANDBOX ] y_sub = [█████ Edited Waveform █████] │ ▼ (Click "Apply" - Trigger Overwrite) [ MAIN TAB - ORIGINAL TRACK ] Track 01: ───[█████ Overwritten Segment █████]─── at t = t_start │ (Sub-tab remains open, Undo Stack kept) ▼ [User presses Undo (Ctrl+Z) inside Sub-tab to iterate] y_sub = [█████ Rollbacked Waveform █████] │ (Click "Apply" again) ▼ Track 01: ───[█████ Corrected Segment █████]──── at t = t_start ``` ### 2.1. Target Mapping & Metadata Linkage Throughout its lifecycle, each sub-tab persistently locks standard metadata records linking back to the origin source elements: * `parent_track_id`: Unique identifier referencing the primary source track on the Main Tab. * `parent_clip_id`: Unique identifier tracking the original source audio clip. * `t_start` (seconds): The exact historical start time position of the sliced block on the Main Tab timeline view. * `original_duration` (seconds): The baseline temporal duration of the region prior to modification. ### 2.2. In-place Overwrite & Splicing * **Edited Buffer Extraction:** The system reads the active sample sequence from the sub-tab ($y_{\text{sub}}$) along with its updated duration boundary $T_{\text{sub}}$ (which fluctuates if time-stretching or rate scaling actions have occurred). * **Main Session Integration:** 1. The core route mapper checks for the matching `parent_track_id` parameter on the Main Tab. 2. Purges the legacy audio segment stretching from $t_{\text{start}}$ through $t_{\text{start}} + T_{\text{original}}$. 3. Splices the updated signal array $y_{\text{sub}}$ precisely at the historical insertion index $t_{\text{start}}$. 4. **Micro-crossfade:** Executes a ultra-fast crossfade envelope ($10\text{ ms}$) across both the initial and terminating splice boundaries. Blending adjacent files prevents phase cancellation or signal breakage that manifests as transient clicks/pops. * **Visual Update Tracking:** Commands the canvas engine to redraw the waveform visualization grid for the origin track lane inside the Main Tab view. ### 2.3. Persistence for Iterative Editing * **Tab Lifetime:** Engaging the *Apply* trigger propagates data back to the primary environment but does **not** close down the active sub-tab view. * **Undo Stack Isolation:** The tracking loop containing the localized *Undo/Redo History Stack* inside the sub-tab sandbox remains entirely preserved. * **Iterative Loop Workflow:** 1. If monitoring the Main Tab arrangement uncovers an audio anomaly, the user switches focus back to the Sub-tab workspace. 2. Pressing `Ctrl + Z` (Undo) rollbacks the localized signal to its earlier state. 3. The editor runs separate DSP actions. 4. Hitting *Apply* overwrites the updated audio slice over the same target coordinates on the Main Tab. * **Explicit Destruction Hook:** The sandboxed tab structure is only unmapped when the user clicks the explicit close icon ($\times$) on the horizontal tab bar. --- ## 3. Sub-Tab DSP Algorithm Specification Editing operations executed inside the sub-tab environment calculate discrete changes over the amplitude sample arrays ($x[n]$). These map to Web Audio API routines on the client layer and standard NumPy/SciPy audio arrays on the Dockerized backend. ### 3.1. Time-Stretching & Speed Math Alters the duration bounds of the audio clip with optional pitch-shifting linking logic: * **Pitch-preserving Time-stretching:** Utilizes the Phase Vocoder method to analyze the Short-Time Fourier Transform (STFT) of the signal, shifts spectral frames across the frequency domain, and reconstructs the audio via the Inverse Short-Time Fourier Transform (ISTFT) to align with a new playback velocity ratio $S$: $$S = \frac{D}{D'} \times 100\%$$ *Where:* $D$ corresponds to the legacy unscaled duration (seconds), and $D'$ maps to the updated value post-resizing (executed by holding down the `Alt` key and dragging the right boundary handle). * **Resampling (Pitch-shifting Speed Scale):** Runs a standard linear interpolation algorithm to resample the core data array size: $$x_{\text{new}}[m] = x\left[ \frac{m \cdot D}{D'} \right]$$ ### 3.2. Peak Normalization Amplifies the signal scale uniformly across the active block until the single maximum absolute sample peak reaches a specified ceiling parameter $A_{\text{target}}$ (typically locked at $1.0$ or $0\text{ dBFS}$): 1. Evaluate the absolute maximum peak within the array bounds: $$A_{\text{max}} = \max_{n=0}^{N-1} \vert x[n] \vert$$ 2. Compute the static gain multiplier constant $G$: $$G = \frac{A_{\text{target}}}{A_{\text{max}}}$$ 3. Multiply the entire audio array values by $G$: $$x_{\text{norm}}[n] = x[n] \cdot G$$ ### 3.3. Volume Gain Adjustment (dB Scaling) 1. Capture the decibel variance target ($\Delta \text{dB}$). 2. Translate the logarithmic value into a standard linear scalar multiplier variable $G_{\text{linear}}$: $$G_{\text{linear}} = 10^{\frac{\Delta \text{dB}}{20}}$$ 3. Apply the gain multiplier directly into the sample values: $$x_{\text{gained}}[n] = x[n] \cdot G_{\text{linear}}$$ ### 3.4. Pitch Shifting Shifts the fundamental frequencies of the signal up or down by a specific number of semitones ($n$) while keeping the temporal duration value completely intact. * **Frequency Transposition Ratio ($F_{\text{ratio}}$):** $$F_{\text{ratio}} = 2^{\frac{n}{12}}$$ * **DSP Processing Pipeline:** Employs either a Pitch Synchronous Overlap and Add (PSOLA) routine or a spectral Phase Vocoder to expand/compress the frequency components, then passes the array into a time-stretching step to return the physical track length to its source metric $T_{\text{clip}}$. ### 3.5. Linear Fade-In & Fade-Out Curves Applies a linear fading envelope over the boundaries of the audio data block. * **Linear Fade-In Envelope** (Across a duration bound of $L_{\text{fade}}$ samples): $$x_{\text{fade}}[n] = x[n] \cdot \left( \frac{n}{L_{\text{fade}}} \right) \quad \text{for } 0 \le n < L_{\text{fade}}$$ * **Linear Fade-Out Envelope** (Across the final trailing $L_{\text{fade}}$ samples): $$x_{\text{fade}}[N - 1 - n] = x[N - 1 - n] \cdot \left( \frac{n}{L_{\text{fade}}} \right) \quad \text{for } 0 \le n < L_{\text{fade}}$$ ### 3.6. Array Splitting & Merging * **Split at Position ($n_{\text{cut}}$):** Unlinks a single sample block $x[n]$ of size $N$ into two separate independent sub-arrays: $$x_1[n] = x[n] \quad (0 \le n < n_{\text{cut}})$$ $$x_2[n] = x[n + n_{\text{cut}}] \quad (0 \le n < N - n_{\text{cut}})$$ * **Merge Segments:** Concatenates separate sample sequences end-to-end. The stitching logic runs a $10\text{ ms}$ micro-crossfade overlay envelope at the junction to smooth out phase gaps that prompt click artifacts. --- ## 4. Python Backend Implementation Manual This prototype Python class (`core/sub_tab_dsp.py`) handles the sandboxed operations and includes the crossfaded structural splicing algorithm designed to run inside the Docker engine: ```python import numpy as np import scipy.signal as signal import librosa class SubTabDSPEngine: @staticmethod def change_speed(y: np.ndarray, sr: int, speed_ratio: float, preserve_pitch: bool = True) -> np.ndarray: """ Alters the playback velocity (Time-Stretching) of a NumPy signal array. """ if speed_ratio == 1.0: return y if preserve_pitch: return librosa.effects.time_stretch(y, rate=speed_ratio) else: num_samples_new = int(len(y) / speed_ratio) return signal.resample(y, num_samples_new) @staticmethod def normalize(y: np.ndarray, target_db: float = 0.0) -> np.ndarray: """ Performs Peak Normalization on an array to scale it to the target decibel value. """ target_amplitude = 10.0 ** (target_db / 20.0) max_amplitude = np.max(np.abs(y)) if max_amplitude == 0: return y gain = target_amplitude / max_amplitude return y * gain @staticmethod def merge_back_to_parent( parent_track_audio: np.ndarray, sr: int, edited_sub_audio: np.ndarray, start_seconds: float, original_duration_seconds: float ) -> np.ndarray: """ Splices the modified audio segment from the Sub-tab back into the parent track array. Applies a 10ms micro-crossfade at the boundaries to eliminate pop/click noise. """ start_sample = int(start_seconds * sr) original_samples_len = int(original_duration_seconds * sr) edited_samples_len = len(edited_sub_audio) crossfade_samples = int(0.01 * sr) # 10ms crossfade window # 1. Allocate the target output array dimension bounds new_total_len = len(parent_track_audio) - original_samples_len + edited_samples_len output_audio = np.zeros(new_total_len, dtype=np.float32) # 2. Extract leading unedited block output_audio[:start_sample] = parent_track_audio[:start_sample] # 3. Stitch the modified audio payload output_audio[start_sample:start_sample + edited_samples_len] = edited_sub_audio # 4. Extract trailing unedited block post_start_original = start_sample + original_samples_len post_start_new = start_sample + edited_samples_len output_audio[post_start_new:] = parent_track_audio[post_start_original:] # 5. Execute micro-crossfade across the initial splice junction if start_sample > crossfade_samples: fade_in_ramp = np.linspace(0.0, 1.0, crossfade_samples) fade_out_ramp = np.linspace(1.0, 0.0, crossfade_samples) # Smooth 10ms interpolation overlay output_audio[start_sample : start_sample + crossfade_samples] = ( edited_sub_audio[:crossfade_samples] * fade_in_ramp + parent_track_audio[start_sample : start_sample + crossfade_samples] * fade_out_ramp ) # 6. Execute micro-crossfade across the trailing splice junction if post_start_new + crossfade_samples < len(output_audio): fade_in_ramp = np.linspace(0.0, 1.0, crossfade_samples) fade_out_ramp = np.linspace(1.0, 0.0, crossfade_samples) output_audio[post_start_new : post_start_new + crossfade_samples] = ( parent_track_audio[post_start_original : post_start_original + crossfade_samples] * fade_in_ramp + edited_sub_audio[-crossfade_samples:] * fade_out_ramp ) return output_audio ```