

# Technical Specification: Grid Snapping System (Grid Snapping Specification)

This document defines the graphical user interface design and coordinate/signal processing algorithms required to build a synchronized grid snapping feature across both the Web Frontend and the Dockerized Python Desktop Backend.

---

## 1. Toolbar UI Design Upgrade

The toolbar layout has been expanded to double its physical vertical height. This increase in interactive space allows for larger navigation buttons and the integration of a dedicated Snap controller.

```text
+───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────+
| [Pro Toolbar - Height: 64px]                                                                                          |
| +──────────+ +──────────+ +──────────+ +──────────+ +───────────────────────────────────────────────────────────────+ |
| |  Cut     | |  Copy    | |  Paste   | |  Snap:   | | Transport Monitor                                             | |
| |  [Ctrl+X]| |  [Ctrl+C]| |  [Ctrl+V]| | [1/4  ▼] | | [Tempo: 120 BPM]  [Time Signature: 4/4]  [Bar:Beat 1.3.00]      | |
| +──────────+ +──────────+ +──────────+ +──────────+ +───────────────────────────────────────────────────────────────+ |
+───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────+

```

### 1.1. Snap Dropdown Configuration

* **Placement:** Located immediately following the *Paste* button on the primary toolbar row.
* **UI Syntax:** `Snap: <Dropdown_Widget>`
* **Dropdown Option Matrix:**
* `free`: Disables snapping; allows unrestricted pixel-by-pixel dragging.
* `1`: Snaps to the beginning of each complete measure (Whole Bar / 1/1).
* `1/2`: Divides the bar into 2 subdivisions (Half Note).
* `1/4`: Divides the bar into 4 subdivisions (Quarter Note / 1 Beat).
* `1/8`: Divides the bar into 8 subdivisions (Eighth Note).
* `1/16`: Divides the bar into 16 subdivisions (Sixteenth Note).
* `1/32`: Divides the bar into 32 subdivisions (Thirty-second Note).



---

## 2. DSP Grid Math: Grid Subdivision & Time Interval Calculations

To calculate the absolute temporal duration between individual grid lanes, the time metrics must be derived dynamically from the project's master tempo.

Let:

* $B$ be the project tempo (Beats Per Minute, e.g., $120\text{ BPM}$).
* $T_{\text{beat}}$ be the duration of a single beat (seconds).
* $T_{\text{bar}}$ be the duration of a full measure/bar (seconds)—assuming a standard $4/4$ time signature (4 beats per bar).

The foundational constants are established as follows:


$$T_{\text{beat}} = \frac{60}{B} \quad (\text{seconds})$$

$$T_{\text{bar}} = 4 \times T_{\text{beat}} = \frac{240}{B} \quad (\text{seconds})$$

*Example:* At a tempo of $120\text{ BPM}$, a single $1\text{ Bar}$ measure spans exactly $2.0\text{ seconds}$.

### 2.1. Determining Grid Time Interval Modifiers ($\Delta t$)

Based on the user's active choice inside the snap dropdown selection ($S \in \{\text{free}, 1, 1/2, 1/4, 1/8, 1/16, 1/32\}$), the exact grid time step $\Delta t$ (seconds) is mapped as follows:

$$\Delta t = \begin{cases} 0 & S = \text{free} \\ T_{\text{bar}} & S = 1 \\ \frac{T_{\text{bar}}}{2} & S = 1/2 \\ \frac{T_{\text{bar}}}{4} = T_{\text{beat}} & S = 1/4 \\ \frac{T_{\text{bar}}}{8} & S = 1/8 \\ \frac{T_{\text{bar}}}{16} & S = 1/16 \\ \frac{T_{\text{bar}}}{32} & S = 1/32  \end{cases}$$

---

## 3. Snapping Coordinate Calculation

When a user executes a drag-and-drop event on an audio clip, or updates a timeline marker position, the system continuously converts raw cursor values into aligned coordinates.

```text
                      Grid Line 1 (k * dt)          Grid Line 2 ((k+1) * dt)
                             │                             │
                             ├───────────────○─────────────┤
                                             ▲
                                             │ [ Cursor dragging action ]
                                        Raw Time (t_raw)
                                             │
                                             ▼ [ Apply Snap round() function ]
                             ────────────────┼─────────────►
                                        Snapped Time (t_snapped)

```

### 3.1. Pixel to Snap-Time Translation Workflow

1. Intercept the actual client-side horizontal cursor position $X_{\text{raw}}$ (pixels).
2. Convert it into a raw timeline duration metric $t_{\text{raw}}$ (seconds) utilizing the current scaling zoom factor $Z$ (pixels/second):

$$t_{\text{raw}} = \frac{X_{\text{raw}}}{Z}$$


3. Apply the rounding constraint formula to lock the raw timestamp to the absolute nearest grid marker:

$$t_{\text{snapped}} = \begin{cases}    t_{\text{raw}} & S = \text{free} \\    \text{round}\left( \frac{t_{\text{raw}}}{\Delta t} \right) \times \Delta t & S \neq \text{free}    \end{cases}$$


4. Map the snapped timeline index $t_{\text{snapped}}$ back to the layout canvas system coordinates to paint the element at its snapped visual boundary:

$$X_{\text{snapped}} = t_{\text{snapped}} \times Z$$



---

## 4. Python Porting Blueprint

When translating this architectural logic into a desktop Python core environment using frameworks like PyQt6, the snapping evaluations are tied directly into the tracking loop inside the `mouseMoveEvent` handler.

```python
# [PYTHON PORTING BLUEPRINT] - Integrating the Snap algorithm into Python UI layer
import numpy as np

class AudioSnapEngine:
    def __init__(self, bpm: float = 120.0):
        self.bpm = bpm
        
    def calculate_grid_step(self, snap_option: str) -> float:
        """
        Calculates the grid's target duration step (seconds) based on Tempo and Snap selection.
        """
        if snap_option == "free":
            return 0.0
            
        # 1 Bar in a standard 4/4 signature equals 240 / BPM seconds
        t_bar = 240.0 / self.bpm
        
        fraction_map = {
            "1": 1.0,
            "1/2": 2.0,
            "1/4": 4.0,
            "1/8": 8.0,
            "1/16": 16.0,
            "1/32": 32.0
        }
        
        division = fraction_map.get(snap_option, 4.0)
        return float(t_bar / division)

    def snap_time(self, raw_time_seconds: float, snap_option: str) -> float:
        """
        Hard-clamps a raw timestamp to the nearest grid milestone. Prevents negative index overflows.
        """
        dt = self.calculate_grid_step(snap_option)
        if dt == 0.0:
            return max(0.0, raw_time_seconds)
            
        # Find the nearest integer index k of the target grid lane: raw_time / dt
        k = round(raw_time_seconds / dt)
        snapped_time = k * dt
        
        return max(0.0, snapped_time)

```

---

## 5. Visual Grid Alignment

To maintain an intuitive environment for multi-channel editing, whenever a snapping constraint value other than `free` is engaged:

* The rendering engine overlays thin, low-contrast vertical grid lines (`rgba(255, 255, 255, 0.05)`) over the background profile of every active Waveform Lane.
* These marker lines are projected onto every timeline axis point that satisfies a whole multiple increment of $\Delta t$.
* Displaying these alignment indicators ensures that users can visually anticipate bounding snapping positions before releasing their mouse track buttons.