diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6700898 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +node_modules +.git +.env* +!.env.production +!.env.local.template +dist +build +.output +target +*.log +.turbo +coverage +.nyc_output diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9a0e142 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +# syntax=docker/dockerfile:1 + +FROM oven/bun:1 AS base +WORKDIR /app + +# Build +FROM base AS builder +COPY package.json bun.lock* ./ +RUN bun install --frozen-lockfile +COPY . . +RUN bun run build + +# TODO: Switch back to Bun runtime once module resolution is fixed +# Bun doesn't properly resolve externalized Nitro packages (srvx, react-dom/server) +# Error: Cannot find package 'srvx' from '/app/.output/server/chunks/virtual/entry.mjs' +FROM node:22-slim AS runner +WORKDIR /app +ENV NODE_ENV=production + +# Nitro bundles most deps but externalizes some (react-dom/server, srvx). +# Copy both .output and node_modules to ensure all SSR deps are available. +COPY --from=builder /app/.output ./.output +COPY --from=builder /app/node_modules ./node_modules +# Nitro's bundled node_modules may have Bun-specific entries missing Node variants. +# Replace them with the full copies from the top-level node_modules. +RUN rm -rf .output/server/node_modules/react-dom .output/server/node_modules/react \ + && cp -r node_modules/react-dom .output/server/node_modules/react-dom \ + && cp -r node_modules/react .output/server/node_modules/react + +EXPOSE 3000 +CMD ["node", ".output/server/index.mjs"] diff --git a/bun.lock b/bun.lock index 529aac7..54ea44d 100644 --- a/bun.lock +++ b/bun.lock @@ -22,7 +22,7 @@ "react": "^19.2.3", "react-dom": "^19.2.3", "react-icons": "^5.5.0", - "satori": "^0.19.1", + "satori": "^0.26.0", "tailwind-merge": "^3.4.0", "ts-pattern": "^5.9.0", "vite": "^7.3.0", @@ -1182,7 +1182,7 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "satori": ["satori@0.19.1", "", { "dependencies": { "@shuding/opentype.js": "1.4.0-beta.0", "css-background-parser": "^0.1.0", "css-box-shadow": "1.0.0-3", "css-gradient-parser": "^0.0.17", "css-to-react-native": "^3.0.0", "emoji-regex-xs": "^2.0.1", "escape-html": "^1.0.3", "linebreak": "^1.1.0", "parse-css-color": "^0.2.1", "postcss-value-parser": "^4.2.0", "yoga-layout": "^3.2.1" } }, "sha512-/XaT/JiWLfNlgjlQdde4wXB1/6F+FEze9c3OW2QIH0ywsfOrY57YOetgESWyOFHW3JfEQ6dJAo2U9Xwb7+DDAw=="], + "satori": ["satori@0.26.0", "", { "dependencies": { "@shuding/opentype.js": "1.4.0-beta.0", "css-background-parser": "^0.1.0", "css-box-shadow": "1.0.0-3", "css-gradient-parser": "^0.0.17", "css-to-react-native": "^3.0.0", "emoji-regex-xs": "^2.0.1", "escape-html": "^1.0.3", "linebreak": "^1.1.0", "parse-css-color": "^0.2.1", "postcss-value-parser": "^4.2.0", "yoga-layout": "^3.2.1" } }, "sha512-tkMFrfIs3l2mQ2JEcyW0ADTy3zGggFRFzi6Ef8YozQSFsFKEqaSO1Y8F9wJg4//PJGQauMalHGTUEkPrFwhVPA=="], "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], diff --git a/content/docs/community/contributing/index.mdx b/content/docs/community/contributing/index.mdx index 30c0513..c3bbf6f 100644 --- a/content/docs/community/contributing/index.mdx +++ b/content/docs/community/contributing/index.mdx @@ -34,7 +34,7 @@ fix(api): handle null response documentation(readme): update install steps ``` -Write **atomic commits**: each commit should represent a single logical change. +Write **atomic commits**: each commit should represent a single logical change. Do not use `!` in commit type prefixes (e.g., `feat!:`). Breaking changes are documented in PR descriptions and changesets. ### Pull Requests @@ -53,6 +53,25 @@ All contributions go through code review. Reviewers will check for: - Alignment with [quality pillars](/community/contributing/pillars) - Test coverage and documentation +### AI-Assisted Development + +Omni maintains shared conventions for AI coding tools in [golden/AGENTS.md](https://github.com/omnidotdev/golden/blob/master/AGENTS.md). These cover Git, TypeScript, Rust, Drizzle, infrastructure, and more. + +#### Setup + +1. Clone the [golden](https://github.com/omnidotdev/golden) repo +2. Configure your AI tool to load the rules: + +**Claude Code**: Add this line to your `~/.claude/CLAUDE.md` (create the file if it doesn't exist): + +```markdown +@/path/to/golden/CLAUDE.md +``` + +Replace the path with wherever you cloned golden. The rules load automatically at the start of every session. + +**Other tools**: Point your AI tool's system prompt or rules file at `AGENTS.md` in the golden repo. + ## What's Next? - [Changesets](/community/contributing/changesets): *How do I version my changes?* diff --git a/content/docs/fabric/resonance/audio-engine.mdx b/content/docs/fabric/resonance/audio-engine.mdx new file mode 100644 index 0000000..447c184 --- /dev/null +++ b/content/docs/fabric/resonance/audio-engine.mdx @@ -0,0 +1,105 @@ +--- +title: Audio Engine +description: Rust-native audio processing with shared-state threading +--- + +The Resonance audio engine is written in Rust and built around a pull-based audio graph. It prioritizes deterministic, glitch-free playback with sample-accurate timing. + +## Three-Layer Architecture + +The engine is organized into two threads: + +``` +┌──────────────────────────────────────────────────┐ +│ Audio Callback (real-time, highest priority) │ +│ Pulls samples through the audio graph │ +│ Shares AudioGraph/Transport/Metronome via │ +│ Arc> with the control thread │ +└───────────────────────┬──────────────────────────┘ + │ Arc> +┌───────────────────────┴──────────────────────────┐ +│ Control Thread (near-real-time) │ +│ Transport state, parameter changes, MIDI routing │ +│ Bridges UI events to audio-safe commands │ +└──────────────────────────────────────────────────┘ +``` + +### Audio Callback + +The audio callback runs at the OS audio callback priority. It processes one buffer at a time (typically 128 or 256 samples) by walking the audio graph from outputs back to inputs. The callback acquires a shared `Arc>` to access the `AudioGraph`, `Transport`, and `Metronome`. If the lock is contended (e.g., the control thread is updating state), the callback outputs silence for that buffer to avoid blocking. Lock-free communication via ring buffers is planned for a future release to reduce latency. + +### Control Thread + +The control thread receives commands from the UI (play, stop, seek, parameter change, track arm) and translates them into audio-safe messages. It also handles MIDI routing, automation playback, and transport state management. Commands are batched per audio buffer to minimize cross-thread traffic. + +### I/O Thread (Planned) + +A separate I/O thread for disk streaming, audio file decode/encode, and project save/load is planned but not yet implemented. Currently, file I/O is handled on the control thread. + +## Audio Graph + +The audio graph is a directed acyclic graph (DAG) of processor nodes. Each node has typed input and output ports. The graph is evaluated by pulling from the master output, which recursively pulls from upstream nodes. + +``` +[Audio File Reader] ──► [Gain] ──► [EQ] ──► [Bus Input] + │ +[MIDI Source] ──► [Synth Plugin] ──► [Reverb] ──► [Bus Input] + │ + [Bus Sum] ──► [Master] +``` + +Node types include: + +| Node | Purpose | +|---|---| +| `AudioFileReader` | Streams samples from disk | +| `AudioInput` | Captures from hardware input | +| `PluginProcessor` | Wraps a CLAP plugin instance | +| `BuiltinEffect` | Gain, EQ, compressor, reverb, delay | +| `MidiSynth` | Routes MIDI events to instrument plugins | +| `BusSum` | Mixes multiple inputs together | +| `MasterOutput` | Final output to hardware or bounce | + +Graph modifications (adding/removing nodes, changing connections) are performed on the control thread and swapped into the audio thread atomically using a triple-buffer scheme. + +## Key Data Structures + +### TransportState + +Tracks playback position, tempo, time signature, loop region, and record state. Shared between the control and audio threads via an atomic snapshot. + +```rust +struct TransportState { + playing: bool, + recording: bool, + position_samples: u64, + tempo_bpm: f64, + time_signature: (u32, u32), + loop_enabled: bool, + loop_start: u64, + loop_end: u64, + sample_rate: u32, +} +``` + +### MixerState + +Per-channel state: volume, pan, mute, solo, and the ordered list of plugin slots. + +### AutomationLane + +A sorted list of breakpoints with interpolation mode (linear or bezier). The control thread evaluates lanes each buffer and sends parameter updates to the audio thread. + +### MidiRouter + +Maps MIDI input ports and channels to instrument plugin nodes. Supports channel filtering, transposition, and velocity curves. + +## Sample-Accurate Timing + +All events (note on/off, parameter changes, transport jumps) are tagged with a sample offset within the current buffer. The audio thread applies events at the exact sample position, not at buffer boundaries. This ensures timing precision of 1/sample_rate seconds (approximately 23 microseconds at 44.1 kHz). + +## Thread Communication + +Currently, the audio callback and control thread share state (`AudioGraph`, `Transport`, `Metronome`) via `Arc>`. If the mutex is contended when the audio callback tries to acquire it, the callback outputs silence for that buffer rather than blocking. + +Lock-free threading via ring buffers is planned for a future release to reduce latency. The intended design uses SPSC ring buffers for parameter changes and transport commands, and triple-buffering for graph topology swaps. diff --git a/content/docs/fabric/resonance/automation.mdx b/content/docs/fabric/resonance/automation.mdx new file mode 100644 index 0000000..1ad0c3b --- /dev/null +++ b/content/docs/fabric/resonance/automation.mdx @@ -0,0 +1,86 @@ +--- +title: Automation +description: Breakpoint envelopes with sample-accurate parameter control +--- + +Automation allows any parameter in the signal chain to change over time. Volume fades, filter sweeps, effect wet/dry, tempo changes, and plugin parameters can all be automated. + +## Automation Lanes + +Each track can have multiple automation lanes, one per parameter. Lanes appear below the track in the arrangement view. + +To add an automation lane: + +1. Click the automation disclosure triangle on a track header +2. Click **Add Lane** +3. Select the parameter to automate (volume, pan, or any plugin parameter) + +## Breakpoint Envelopes + +Automation data is stored as a series of breakpoints (time-value pairs) with an interpolation mode between each pair. + +```toml +[[automation]] +target = "volume" +points = [ + { time = 0, value = -6.0, curve = "linear" }, + { time = 96000, value = 0.0, curve = "linear" }, + { time = 480000, value = 0.0, curve = "bezier" }, + { time = 528000, value = -96.0, curve = "bezier" }, +] +``` + +### Interpolation Modes + +| Mode | Behavior | +|---|---| +| **Linear** | Straight line between points | +| **Bezier** | Smooth curve with adjustable curvature (drag the curve handle) | +| **Step** | Instant jump at the second point (hold previous value until the transition) | + +## Drawing and Editing + +### Drawing + +1. Select the **Pencil** tool (or hold **B**) +2. Click and drag in an automation lane to draw a freehand envelope +3. Breakpoints are placed at the grid resolution (adjustable via snap settings) + +### Editing + +1. Select the **Pointer** tool +2. Click a breakpoint to select it, drag to move +3. Drag the curve between two points to adjust bezier curvature +4. Double-click to add a new breakpoint +5. Select and press **Delete** to remove breakpoints +6. Box-select multiple points and drag to move them together + +### Range Operations + +- **Copy/Paste**: Select a range of automation, copy, and paste at another position +- **Scale**: Select points and drag the top or bottom edge to scale values proportionally +- **Thin**: Reduce point density while preserving the shape (useful after freehand drawing) + +## Automatable Parameters + +Any parameter on any node in the audio graph can be automated: + +- **Track controls**: Volume, pan, mute +- **Plugin parameters**: Every exposed parameter from CLAP plugins +- **Built-in effects**: All parameters on gain, EQ, compressor, reverb, delay +- **Sends**: Send level per bus +- **Transport**: Tempo (for tempo automation) + +The parameter browser shows all available targets in a tree structure grouped by track and plugin. + +## Sample-Accurate Automation + +Automation values are evaluated per sample, not per buffer. The control thread pre-computes automation values for each buffer and sends them to the audio thread as a list of (sample_offset, value) pairs. The audio thread applies parameter changes at the exact sample position. + +This means: + +- A volume fade is perfectly smooth, with no stepping artifacts +- A filter cutoff sweep is continuous, not quantized to buffer boundaries +- Multiple parameters can change simultaneously at different rates + +For parameters that plugins process per-buffer (not per-sample), the audio thread sends the parameter value at the start of each processing block. CLAP plugins that support per-sample parameter events receive the full event list. diff --git a/content/docs/fabric/resonance/browser-version.mdx b/content/docs/fabric/resonance/browser-version.mdx new file mode 100644 index 0000000..074c2f3 --- /dev/null +++ b/content/docs/fabric/resonance/browser-version.mdx @@ -0,0 +1,103 @@ +--- +title: Browser Version +description: WASM-compiled audio engine with AudioWorklet bridge and offline support +--- + +> **Planned**: The browser version is not yet implemented. This page describes the intended design. + +The browser version of Resonance will run at [resonance.omni.dev](https://resonance.omni.dev). It will provide a subset of the desktop functionality, compiled to WebAssembly and running in an AudioWorklet. + +## What's Included + +- Audio graph engine (WASM-compiled `resonance-core`) +- All built-in effects (EQ, compressor, reverb, delay) +- MIDI input via WebMIDI +- Automation lanes with full editing +- Piano roll and arrangement views +- WASM-compiled CLAP plugins (planned) +- Project export (WAV, FLAC currently; MP3, OGG planned) +- Offline support via service worker + +## What's Excluded + +- **Native CLAP/LV2 plugins**: No access to local plugin binaries from the browser +- **Low-latency audio**: Browser audio typically has 10-50ms latency (vs 3-10ms on desktop) +- **Filesystem access**: Projects are stored in IndexedDB, not on disk +- **Hardware multi-channel I/O**: Browser audio is typically stereo only +- **ASIO/JACK support**: No direct audio driver access + +## WASM Compilation + +The `resonance-web` crate compiles a subset of `resonance-core` to `wasm32-unknown-unknown`. The compilation excludes: + +- Plugin host code (replaced by WASM plugin host) +- Native audio I/O (replaced by AudioWorklet bridge) +- File I/O (replaced by IndexedDB adapter) +- Thread spawning (single-threaded in WASM) + +The resulting `.wasm` binary is approximately 2 MB (gzipped) and loads in under 2 seconds on a modern connection. + +## AudioWorklet Bridge + +The audio engine runs inside an `AudioWorkletProcessor`: + +``` +┌─────────────────────────────────────────────┐ +│ Main Thread │ +│ UI (React), project state, MIDI handling │ +│ │ +│ ┌─────────────────────────────────────┐ │ +│ │ SharedArrayBuffer / MessagePort │ │ +│ └──────────────────┬──────────────────┘ │ +└─────────────────────┼───────────────────────┘ + │ +┌─────────────────────┼───────────────────────┐ +│ AudioWorklet Thread │ +│ ┌──────────────────┴──────────────────┐ │ +│ │ resonance-core (WASM) │ │ +│ │ Audio graph evaluation │ │ +│ │ Built-in DSP │ │ +│ │ WASM plugin instances │ │ +│ └─────────────────────────────────────┘ │ +└─────────────────────────────────────────────┘ +``` + +Communication between the main thread and the AudioWorklet uses: + +- **SharedArrayBuffer**: For audio data transfer (when available, with COOP/COEP headers) +- **MessagePort**: For commands and parameter changes (fallback if SAB unavailable) + +The AudioWorklet processes 128 samples per callback (the Web Audio API default). The WASM engine is called once per callback to fill the output buffer. + +## CLAP-in-WASM + +CLAP plugins that are compiled to WASM can run in the browser. The workflow: + +1. Plugin developer compiles their CLAP plugin to `wasm32-wasi` +2. The `.wasm` bundle is uploaded to the Resonance plugin registry +3. Users browse and install WASM plugins from within the browser version +4. The plugin runs inside the AudioWorklet alongside the engine + +WASM plugins share the same AudioWorklet thread. CPU-heavy plugins may cause audio dropouts since there is no real-time thread priority in the browser. + +## Offline Support + +The browser version works offline after the initial load: + +- **Service Worker**: Caches the app shell, WASM binary, and static assets +- **IndexedDB**: Stores projects, audio files, and plugin presets locally +- **Background Sync**: Queues cloud sync operations for when connectivity returns + +The app registers a service worker on first visit. Subsequent visits load from cache, with updates applied in the background. + +## Use Cases + +The browser version is best suited for: + +- **Sketching**: Quickly capture an idea without launching the desktop app +- **Review**: Open a shared project to listen and leave feedback +- **Collaboration**: Join a live session from any device +- **Education**: Learn music production without installing software +- **Mobile**: Basic editing on tablets (limited by screen size and touch input) + +For production work (recording, mixing, mastering), the desktop app is recommended. diff --git a/content/docs/fabric/resonance/collaboration.mdx b/content/docs/fabric/resonance/collaboration.mdx new file mode 100644 index 0000000..dbb263b --- /dev/null +++ b/content/docs/fabric/resonance/collaboration.mdx @@ -0,0 +1,83 @@ +--- +title: Collaboration +description: Live session sharing via WebRTC with host authority +--- + +> **Planned**: Real-time collaboration is not yet implemented. This page describes the intended design. + +Resonance supports real-time collaboration, allowing multiple users to work on the same project simultaneously. + +## Live Sessions + +A live session connects multiple Resonance instances (desktop or browser) over WebRTC: + +1. The host opens a project and clicks **Share > Start Live Session** +2. Resonance generates a session link +3. Collaborators open the link (desktop app or browser) +4. All participants see the same arrangement, mixer, and transport state + +Live sessions require an Omni account for all participants. Sign in via HIDRA (Omni's identity service). + +## Host Authority Model + +The session host has full authority over the project: + +| Action | Host | Collaborator | +|---|---|---| +| Play/stop/seek | Yes | Yes (forwarded to host) | +| Add/delete tracks | Yes | No | +| Edit clips | Yes | Delegated tracks only | +| Change routing | Yes | No | +| Adjust mixer | Yes | Delegated tracks only | +| Add/remove plugins | Yes | No | +| Export | Yes | No | +| Save | Yes | No | + +Collaborators can always listen to the full mix and view the arrangement. Editing permissions are granted per track by the host. + +## Track Delegation + +The host can delegate control of individual tracks to collaborators: + +1. Right-click a track header +2. Select **Delegate to...** and choose a participant +3. The delegated user can now edit clips, adjust volume/pan, and modify plugins on that track + +Delegation can be revoked at any time. A track can only be delegated to one collaborator at a time. The host always retains the ability to override. + +## Real-Time State Sync + +Session state is synchronized using a combination of: + +- **State diffs**: Track, mixer, and arrangement changes are transmitted as compact diffs over a WebRTC data channel +- **Transport sync**: Play/stop/seek commands are broadcast with timestamp correction for latency +- **Parameter updates**: Plugin and mixer parameter changes stream in real-time + +The host is the source of truth. If the host and a collaborator make conflicting edits, the host's state wins. + +## Audio Monitoring + +Collaborators hear the mix as rendered by the host: + +1. The host's audio engine renders the full mix +2. A compressed audio stream (Opus codec) is sent via WebRTC to each collaborator +3. Collaborators hear the mix with a small delay (typically 50-200ms depending on network) + +This approach means collaborators do not need to load plugins or audio files locally. The host bears the full processing load. + +### Low-Latency Mode + +For recording together, the host can enable low-latency mode: + +- Each participant sends their raw audio input via WebRTC +- The host's engine mixes all inputs in real-time +- Latency depends on network quality (typically 20-80ms on good connections) + +Low-latency mode requires a stable connection and works best on local networks. + +## Requirements + +- All participants need an Omni account (free tier is sufficient) +- Modern browser or Resonance desktop app +- WebRTC-capable network (most networks work, some corporate firewalls may block peer-to-peer) +- The Omni signaling server brokers the initial connection, after which traffic is peer-to-peer diff --git a/content/docs/fabric/resonance/getting-started.mdx b/content/docs/fabric/resonance/getting-started.mdx new file mode 100644 index 0000000..5f05494 --- /dev/null +++ b/content/docs/fabric/resonance/getting-started.mdx @@ -0,0 +1,79 @@ +--- +title: Getting Started +description: Install Resonance and create your first project +--- + +## Installation + +### Desktop App (Recommended) + +Download the Resonance desktop app for your platform: + +| Platform | Format | Notes | +|---|---|---| +| **macOS** | `.dmg` | macOS 12+ (Intel and Apple Silicon) | +| **Windows** | `.msi` | Windows 10+ | +| **Linux** | `.AppImage`, `.deb` | Ubuntu 22.04+, Fedora 38+, Arch | + +Download links are available at [resonance.omni.dev/download](https://resonance.omni.dev/download). + +The desktop app provides the full feature set: native audio I/O, CLAP/LV2 plugin hosting, low-latency recording, and filesystem-based project storage. + +### Browser Version + +The browser version is not yet available. When released, it will be accessible at [resonance.omni.dev](https://resonance.omni.dev) with no installation or account required. + +The browser version will include the audio graph, built-in effects, MIDI support, and automation. It will not support native plugins or low-latency audio. See [Browser Version](/fabric/resonance/browser-version) for details on the intended design. + +## Creating a Project + +1. Open Resonance (desktop or browser) +2. Click **New Project** from the start screen +3. Enter a project name and choose a save location (desktop) or accept the default (browser) +4. The editor opens with an empty arrangement and a default master bus + +On desktop, the project is saved as a `.resonance` directory. In the browser, the project is stored in IndexedDB. + +## Basic Workflow + +### 1. Create Tracks + +Click the **+** button in the track header area to add tracks: + +- **Audio Track**: Record or import audio files +- **MIDI Track**: Sequence notes via the piano roll +- **Bus Track**: Group and process multiple tracks together + +### 2. Record Audio + +1. Select an audio track +2. Choose your input device from the track's input selector +3. Arm the track for recording (click the red circle) +4. Press the record button in the transport bar +5. Press stop when finished + +The recorded audio is saved as a WAV file in the project directory and displayed as a waveform clip on the track. + +### 3. Add Effects + +1. Open the mixer (press **M** or click the mixer tab) +2. Click an empty plugin slot on any channel strip +3. Browse built-in effects or installed plugins +4. Adjust parameters in the plugin window + +Built-in effects include EQ, compressor, reverb, and delay. On desktop, CLAP plugins are also available (LV2 planned). + +### 4. Export + +1. Select **File > Export** (or press **Ctrl+Shift+E**) +2. Choose a format: WAV or FLAC +3. Set sample rate and bit depth +4. Optionally enable stem export (planned) to render each track as a separate file +5. Click **Export** + +## Next Steps + +- [Audio Engine](/fabric/resonance/audio-engine): Understand how audio flows through the system +- [Tracks and Routing](/fabric/resonance/tracks-and-routing): Learn about track types and signal routing +- [Mixing](/fabric/resonance/mixing): Explore the mixer console and effects +- [MIDI](/fabric/resonance/midi): Connect MIDI devices and use the piano roll diff --git a/content/docs/fabric/resonance/index.mdx b/content/docs/fabric/resonance/index.mdx new file mode 100644 index 0000000..954ba66 --- /dev/null +++ b/content/docs/fabric/resonance/index.mdx @@ -0,0 +1,142 @@ +--- +title: Resonance +description: Professional digital audio workstation, desktop-native with a browser fallback +--- + +import { ProductOverview } from "@/components/docs"; +import app from "@/lib/config/app.config"; +import { FaMusic, FaPlug, FaWaveSquare } from "react-icons/fa"; + +, + className: "bg-purple-100 text-purple-800 dark:bg-purple-900/20 dark:text-purple-300", + }, + { + label: "Plugins", + icon: , + className: "bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-300", + }, + { + label: "Audio", + icon: , + className: "bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-300", + }, + ]} + links={[ + { + href: "https://resonance.omni.dev", + label: "Visit App", + type: "website", + }, + { + href: `${app.socials.github}/resonance`, + label: "Visit Repository", + type: "repository", + }, + ]} + alerts={[ + { + title: "Under Construction", + description: + "Resonance is in early development. Expect this documentation to change rapidly.", + }, + { + title: "Big Idea", + description: + "Professional music production on the desktop and in the browser, free and open to all", + }, + { + title: "Problem Solved", + description: + "Resonance removes the barriers to music production. No expensive software, no platform lock-in. A full-featured Tauri desktop app with a browser fallback for lightweight workflows.", + }, + ]} +/> + +**Omni Resonance** is a professional digital audio workstation (DAW). The primary experience is a Tauri-based desktop application for macOS, Windows, and Linux. A browser version at [resonance.omni.dev](https://resonance.omni.dev) provides a lightweight fallback for sketching, review, and collaboration. + +## Key Features + +- **Desktop-Native Performance**: Tauri app with Rust audio engine for low-latency recording and playback +- **Browser Fallback**: Subset of functionality available in any modern browser via WASM +- **Multi-Track Recording**: Record audio and MIDI with unlimited tracks +- **Audio Editing**: Cut, splice, stretch, and manipulate audio clips +- **MIDI Sequencing**: Piano roll editor with quantization and automation +- **Mixer**: Full mixing console with faders, panning, and flexible routing +- **Effects Processing**: Built-in effects (EQ, compression, reverb, delay) +- **Plugin Support**: CLAP plugin hosting (LV2 planned) +- **Collaboration**: Real-time session sharing via WebRTC (planned) +- **Export Options**: Render to WAV and FLAC (MP3, OGG, stems planned) + +## Plugin Formats + +Resonance supports open and free plugin standards only: + +| Format | Role | Rationale | +|---|---|---| +| **CLAP** | Primary | Open-source, modern API, clean Rust FFI via `clack` | +| **LV2** | Planned | Linux audio standard, large open-source ecosystem | +| **No VST3** | Excluded | Steinberg license conflicts with Apache 2.0 | + +CLAP plugins can also run in the browser via WASM compilation. See [Plugin System](/fabric/resonance/plugin-system) for details. + +## Architecture + +Resonance is split into several crates and packages: + +``` +resonance-core (Rust) Audio engine, graph, transport, automation +resonance-plugins (Rust) CLAP/LV2 host, plugin scanning, parameter bridge (planned) +resonance-app (Tauri) Desktop shell, native GUI, file I/O +resonance-web (WASM) Browser build of resonance-core subset (planned) +resonance-ui (TypeScript) Shared UI (tracks, mixer, piano roll, automation) +``` + +The audio engine runs a real-time audio thread that pulls samples through a directed acyclic graph of processor nodes. A separate control thread handles UI state, transport commands, and plugin parameter changes. The control thread and audio callback share state via `Arc>`. Lock-free communication is planned for a future release. + +## Use Cases + +- **Music Production**: Create complete songs from idea to master +- **Podcasting**: Record and edit podcast episodes +- **Sound Design**: Design sound effects and ambient textures +- **Education**: Learn music production without expensive software +- **Collaboration**: Work on projects with musicians anywhere in the world + + + + Install the desktop app or open the browser version + + + Three-layer architecture, pull-based audio graph, lock-free threading + + + CLAP and LV2 hosting, plugin scanning, WASM plugins for browser + + + Directory structure, TOML manifests, non-destructive editing + + + Track types, flexible routing graph, sends and buses + + + Mixer console, channel strips, built-in effects, export + + + Device connection, piano roll, recording, quantization + + + Breakpoint envelopes, bezier curves, sample-accurate parameter control + + + WASM build, AudioWorklet bridge, offline support + + + Live session sharing, host authority, real-time monitoring + + + HIDRA auth, cloud sync, Rushes, Vortex events, Aether billing + + diff --git a/content/docs/fabric/resonance/meta.json b/content/docs/fabric/resonance/meta.json new file mode 100644 index 0000000..11af39c --- /dev/null +++ b/content/docs/fabric/resonance/meta.json @@ -0,0 +1,19 @@ +{ + "title": "Resonance", + "pages": [ + "getting-started", + "---Architecture---", + "audio-engine", + "plugin-system", + "project-format", + "---User Guide---", + "tracks-and-routing", + "mixing", + "midi", + "automation", + "---Advanced---", + "browser-version", + "collaboration", + "platform-integration" + ] +} diff --git a/content/docs/fabric/resonance/midi.mdx b/content/docs/fabric/resonance/midi.mdx new file mode 100644 index 0000000..9dc8591 --- /dev/null +++ b/content/docs/fabric/resonance/midi.mdx @@ -0,0 +1,96 @@ +--- +title: MIDI +description: MIDI device connection, piano roll, recording, and routing +--- + +Resonance supports MIDI input and output for sequencing instrument plugins, controlling parameters, and recording performances. + +## Device Connection + +### Desktop + +On the desktop app, MIDI devices are connected via the `midir` Rust crate. Resonance detects all connected MIDI devices on startup and monitors for hot-plug events. + +To select a MIDI input for a track: + +1. Create or select a MIDI track +2. Open the track's input selector +3. Choose a MIDI device and channel (or "All Channels") + +### Browser + +In the browser version, MIDI devices are accessed via the [WebMIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API). The browser will prompt for permission on first use. + +WebMIDI support varies by browser. Chromium-based browsers have the best support. Firefox requires a flag. Safari does not support WebMIDI. + +## Piano Roll Editor + +The piano roll is the primary MIDI editing view. Open it by double-clicking a MIDI clip or selecting a MIDI track and pressing **P**. + +Features: + +- **Note entry**: Click to place notes, drag to set length +- **Selection**: Click and drag to select groups of notes +- **Velocity**: Adjust note velocity via the velocity lane at the bottom +- **Snap**: Quantize note positions to a grid (1/4, 1/8, 1/16, triplets, dotted) +- **Keyboard input**: Type A-G to enter notes, Shift for sharps (#), 1-7 to set octave +- **Cursor navigation**: Arrow left/right to move cursor by grid step +- **Delete**: Right-click a note or select and press Delete/Backspace +- **Tools**: Pointer, pencil, eraser, slice +- **Zoom**: Scroll wheel to zoom, Ctrl+scroll for horizontal zoom + +## Recording + +MIDI recording captures incoming MIDI events to a clip on the armed track: + +1. Arm the MIDI track for recording +2. Press record in the transport +3. Play your MIDI controller +4. Press stop when finished + +Recorded notes appear in the piano roll. Multiple takes can be recorded as separate clips on the same track. + +### Quantization + +After recording, apply quantization to snap notes to the grid: + +- **Quantize strength**: 0% (no change) to 100% (fully on grid) +- **Grid size**: 1/4, 1/8, 1/16, 1/32, triplets +- **Swing**: Add swing feel by delaying off-beat notes (0-100%) + +Select notes and press **Q** to quantize with the current settings. Use **Ctrl+Q** to open the quantize dialog for fine-tuning. + +## Channel Mapping + +MIDI tracks can filter and remap channels: + +```toml +[midi] +input_channel = 1 # listen to channel 1 only (0 = all) +output_channel = 1 # send to instrument on channel 1 +transpose = 0 # semitones +velocity_curve = "linear" # linear, soft, hard, fixed +fixed_velocity = 100 # used when velocity_curve = "fixed" +``` + +## MIDI to Instrument Plugins + +MIDI tracks produce audio by routing MIDI events to an instrument plugin: + +1. Insert an instrument plugin (synth, sampler) as the first plugin on the MIDI track +2. MIDI events from the piano roll or live input are sent to the plugin +3. The plugin generates audio, which flows through any subsequent effect plugins +4. The audio output is routed like any other track (to a bus or master) + +CLAP instrument plugins are supported on desktop (LV2 planned). In the browser (planned), only WASM-compiled CLAP instruments and the built-in simple synth will be available. + +## MIDI CC Mapping + +MIDI continuous controller (CC) messages can be mapped to any automatable parameter: + +1. Right-click a parameter (plugin knob, fader, pan) +2. Select **MIDI Learn** +3. Move a knob or fader on your MIDI controller +4. The CC message is bound to that parameter + +Multiple parameters can be mapped to the same CC. Mappings are saved per project in `project.toml`. diff --git a/content/docs/fabric/resonance/mixing.mdx b/content/docs/fabric/resonance/mixing.mdx new file mode 100644 index 0000000..158db33 --- /dev/null +++ b/content/docs/fabric/resonance/mixing.mdx @@ -0,0 +1,90 @@ +--- +title: Mixing +description: Mixer console, channel strips, built-in effects, and export options +--- + +The mixer provides a console view of all tracks and buses with faders, pan knobs, plugin slots, and metering. + +## Mixer Console + +Open the mixer by pressing **M** or clicking the mixer tab. The console displays one channel strip per track and bus, arranged left to right in track order. The master bus is always on the far right. + +Each channel strip shows: + +- Track name and color +- Input/output routing indicators +- Plugin slots (click to add/edit effects) +- Pan knob +- Volume fader with dB scale +- Peak meter (stereo) +- Mute, solo, and record arm buttons + +## Channel Strip Controls + +### Volume + +The fader controls the track's output level in dB. Range is -inf to +12 dB. Double-click the fader to type a precise value. The fader position is automatable. + +### Pan + +The pan knob controls stereo placement from hard left (-1.0) to hard right (+1.0). Center is 0.0. Pan law is equal-power (-3 dB at center). The pan position is automatable. + +### Mute and Solo + +- **Mute**: Silences the track. Muted tracks still process plugins (to avoid pops) but output silence +- **Solo**: Solos the track, muting all non-soloed tracks. Multiple tracks can be soloed simultaneously. Solo is implemented as an implicit mute on all other tracks + +## Built-in Effects + +Resonance includes several built-in effects that are always available, including in the browser version: + +| Effect | Description | +|---|---| +| **Gain** | Simple gain stage for level adjustment | +| **Parametric EQ** | 4-band parametric EQ with low/high shelf and bell filters | +| **Compressor** | Dynamics compressor with threshold, ratio, attack, release, and makeup gain | +| **Reverb** | Algorithmic reverb with room size, damping, pre-delay, and mix controls | +| **Delay** | Stereo delay with tempo sync, feedback, and filtering | + +Built-in effects are implemented as Rust DSP processors, compiled to WASM for the browser version. They appear in the plugin browser alongside installed CLAP plugins. + +## Plugin Slots + +Each channel strip has an ordered list of plugin slots. Signal flows through plugins top to bottom: + +1. Click an empty slot to open the plugin browser +2. Select a plugin (built-in or CLAP) +3. The plugin loads and its GUI opens +4. Drag plugins to reorder, right-click to bypass or remove + +Each slot can be individually bypassed without removing the plugin. Bypassed slots pass audio through unprocessed. + +## Metering + +Each channel strip displays a stereo peak meter. The meter shows: + +- Real-time peak level (fast attack, slow release) +- Peak hold indicator (resets on click) +- Clip indicator (lights red when signal exceeds 0 dBFS) + +The master bus additionally shows LUFS loudness metering (integrated, short-term, and momentary). + +## Export + +Export renders the arrangement to an audio file: + +| Format | Extension | Notes | +|---|---|---| +| **WAV** | `.wav` | Uncompressed, 16/24/32-bit | +| **FLAC** | `.flac` | Lossless compression | +| **MP3** | `.mp3` | Lossy, 128-320 kbps (planned) | +| **OGG** | `.ogg` | Lossy, Vorbis codec (planned) | + +### Export Options + +- **Range**: Entire arrangement, loop region, or custom selection +- **Sample rate**: Match project or resample (44.1, 48, 88.2, 96 kHz) +- **Bit depth**: 16, 24, or 32-bit (WAV and FLAC) +- **Normalization**: Off, peak, or LUFS target +- **Dithering**: Off, triangular, or noise-shaped (when reducing bit depth) +- **Stem export** (planned): Render each track as a separate file, useful for collaboration or further mixing in another DAW diff --git a/content/docs/fabric/resonance/platform-integration.mdx b/content/docs/fabric/resonance/platform-integration.mdx new file mode 100644 index 0000000..1eb0307 --- /dev/null +++ b/content/docs/fabric/resonance/platform-integration.mdx @@ -0,0 +1,68 @@ +--- +title: Platform Integration +description: Optional Omni platform integrations for auth, sync, and cross-product workflows +--- + +Resonance works fully offline as a standalone DAW. All Omni platform integrations are optional and degrade gracefully when unavailable. + +## HIDRA Authentication + +Sign in with your Omni account via HIDRA (Omni's identity provider): + +- Enables cloud sync, collaboration, and cross-product features +- No feature gates: all DAW functionality works without an account +- Desktop app uses OAuth PKCE flow via system browser +- Browser version uses standard redirect flow + +If `HIDRA_URL` is not configured or the user is not signed in, Resonance logs a warning at startup and disables cloud features. + +## Cloud Project Sync + +Signed-in users can sync projects to Omni cloud storage: + +- **TOML files**: Synced as-is, enabling server-side diff and merge +- **Audio blobs**: Content-addressed and deduplicated (SHA-256 hash) +- **Incremental sync**: Only changed files are uploaded +- **Conflict resolution**: Last-write-wins for TOML, with local backup of the overwritten version + +Sync is manual (click **Sync** or press **Ctrl+Shift+S**). There is no automatic background sync to avoid unexpected bandwidth usage. + +### Storage + +Projects are stored in the user's Omni storage quota. Audio files are deduplicated across projects, so shared samples only count once toward the quota. + +## Rushes Integration + +[Rushes](/fabric/rushes) is Omni's video production tool. Resonance integrates with Rushes for audio-in-video workflows: + +- **Export stems to Rushes**: Send individual track stems or the final mixdown directly to a Rushes project +- **Import video from Rushes**: Load a video file as a reference track in Resonance for scoring or sound design +- **Timecode sync**: When working with video, Resonance displays SMPTE timecode alongside bars/beats + +Integration is available when both products are linked to the same Omni account. If Rushes is not available, these menu items are hidden. + +## Vortex CloudEvents + +Resonance publishes events to the Omni event stream via [Vortex](/grid/vortex): + +| Event | When | +|---|---| +| `resonance.project.created` | User creates a new project | +| `resonance.project.exported` | User exports a mixdown or stems | +| `resonance.session.started` | Host starts a live collaboration session | +| `resonance.session.ended` | Host ends a live session | + +Events use CloudEvents format with source `omni.resonance`. They are published only when the user is signed in. Other Omni products and Vortex workflows can subscribe to these events for automation (e.g., triggering a Rushes render when a mixdown is exported). + +## Aether Billing + +The DAW itself is free. Cloud features are billed through [Aether](/grid/aether): + +| Feature | Free Tier | Pro Tier | +|---|---|---| +| **DAW (all features)** | Included | Included | +| **Cloud storage** | 1 GB | 100 GB | +| **Collaboration sessions** | 2 participants | 10 participants | +| **Cloud sync** | Manual only | Manual only | + +Billing state is fetched from Aether at sign-in. If Aether is unreachable, the free tier limits apply as a fallback. diff --git a/content/docs/fabric/resonance/plugin-system.mdx b/content/docs/fabric/resonance/plugin-system.mdx new file mode 100644 index 0000000..edb54ca --- /dev/null +++ b/content/docs/fabric/resonance/plugin-system.mdx @@ -0,0 +1,99 @@ +--- +title: Plugin System +description: CLAP and LV2 plugin hosting with WASM support for the browser +--- + +Resonance hosts audio plugins using open, royalty-free formats. The plugin system handles scanning, loading, GUI hosting, and parameter bridging. + +## Supported Formats + +### CLAP (Primary) + +[CLAP](https://cleveraudio.org/) (CLever Audio Plug-in) is the primary plugin format. Resonance uses the `clack` Rust crate for host-side FFI. + +Why CLAP is the primary format: + +- **Open-source** specification and SDK, Apache 2.0 compatible +- **Modern API** designed for stability, thread safety, and parameter negotiation +- **Clean Rust FFI** via `clack-host`, no C++ interop required +- **Growing ecosystem** of free and commercial plugins +- **WASM-compilable** subset for browser use + +### LV2 (Planned) + +LV2 support is planned but not yet implemented. + +[LV2](https://lv2plug.in/) will be supported as a secondary format, primarily for Linux users. + +Why LV2 is included: + +- **Linux audio standard** with a large library of open-source plugins +- **Open specification** with no licensing restrictions +- **Extensible** via LV2 extensions (atom ports, worker threads, state) + +### No VST3 + +Resonance does not support VST3. The Steinberg VST3 SDK license includes clauses that conflict with Apache 2.0 distribution. Since both CLAP and LV2 provide comparable functionality with fully open licenses, VST3 is excluded. + +## Plugin Scanning + +On startup (and on demand via the plugin manager), Resonance scans standard plugin directories: + +| Format | macOS | Windows | Linux | +|---|---|---|---| +| **CLAP** | `~/Library/Audio/Plug-Ins/CLAP`, `/Library/Audio/Plug-Ins/CLAP` | `%COMMONPROGRAMFILES%\CLAP`, `%LOCALAPPDATA%\Programs\Common\CLAP` | `~/.clap`, `/usr/lib/clap` | +| **LV2** (planned) | `~/Library/Audio/Plug-Ins/LV2`, `/Library/Audio/Plug-Ins/LV2` | `%COMMONPROGRAMFILES%\LV2` | `~/.lv2`, `/usr/lib/lv2` | + +Scan results are cached in `session.db` (SQLite). The cache stores plugin ID, name, vendor, category, port configuration, and file modification time. Rescanning only reloads plugins whose files have changed. + +## Plugin Loading + +When a plugin is inserted into a track's effect chain: + +1. The control thread loads the plugin binary and creates an instance +2. Port configuration is negotiated (audio I/O channel count, parameter list) +3. The instance is wrapped in a `PluginProcessor` graph node +4. The node is inserted into the audio graph via triple-buffer swap +5. The audio thread begins pulling samples through the plugin + +Plugin instances are always created and destroyed on the control thread, never on the audio thread. + +## Native GUI Hosting + +Native plugin GUI hosting is not yet implemented. A generic parameter editor with auto-generated knobs is available. + +CLAP and LV2 plugins can provide their own GUI. The intended design embeds plugin GUIs as child windows: + +| Platform | Embedding | +|---|---| +| **macOS** | `NSView` child of Tauri `WebviewWindow` | +| **Windows** | `HWND` child window | +| **Linux** | X11 embed via `XReparentWindow` | + +If a plugin does not provide a GUI (or the user prefers), Resonance displays a generic parameter UI with sliders for each exposed parameter. + +## Parameter Bridge + +Plugin parameters are exposed to the rest of the system through a unified `ParamBridge`: + +- **Automation**: Any plugin parameter can be an automation target +- **MIDI CC**: Parameters can be mapped to MIDI CC messages +- **UI**: The generic parameter view reflects real-time values +- **Preset**: Parameter values are saved/restored with presets and projects + +Parameter changes from the UI or automation are sent to the audio thread as sample-accurate events. Parameter changes from the plugin (e.g., internal modulation) are reported back to the control thread for UI updates. + +## WASM Plugins (Browser) (Planned) + +WASM plugin support is planned but not yet implemented. + +In the browser version, a subset of CLAP plugins will be able to run as WASM modules. This will require plugins to be compiled to `wasm32-wasi` and bundled as `.wasm` files. + +The browser plugin host: + +1. Loads the `.wasm` file into a `WebAssembly.Instance` +2. Provides a CLAP host shim (memory allocation, parameter negotiation) +3. Bridges audio buffers between the `AudioWorklet` and the WASM instance +4. Renders a generic parameter UI (no native GUI in browser) + +Only plugins that avoid platform-specific code (filesystem, threads, GPU) can compile to WASM. The Resonance plugin registry tags WASM-compatible plugins. diff --git a/content/docs/fabric/resonance/project-format.mdx b/content/docs/fabric/resonance/project-format.mdx new file mode 100644 index 0000000..810694b --- /dev/null +++ b/content/docs/fabric/resonance/project-format.mdx @@ -0,0 +1,155 @@ +--- +title: Project Format +description: Directory-based project structure with TOML manifests and non-destructive editing +--- + +Resonance projects are stored as directories with a `.resonance` extension. The format is designed to be human-readable, version-control friendly, and non-destructive. + +## Directory Structure + +``` +my-song.resonance/ +├── project.toml # Project manifest +├── tracks/ +│ ├── lead-vocal/ +│ │ ├── track.toml # Track configuration +│ │ └── clips/ +│ │ ├── take-01.flac +│ │ └── take-02.flac +│ ├── drums/ +│ │ ├── track.toml +│ │ └── clips/ +│ │ └── beat-loop.flac +│ └── synth-pad/ +│ ├── track.toml +│ └── midi/ +│ └── chords.mid +├── buses/ +│ ├── reverb-send.toml +│ └── master.toml +├── plugins/ +│ └── presets/ +│ ├── lead-eq.json +│ └── pad-reverb.json +├── automation/ +│ ├── lead-vocal-volume.toml +│ └── synth-pad-filter.toml +└── session.db # Ephemeral state (SQLite) +``` + +## project.toml + +The project manifest contains global settings and metadata: + +```toml +[project] +name = "My Song" +created = "2026-03-15T10:30:00Z" +modified = "2026-03-29T14:20:00Z" +sample_rate = 48000 +bit_depth = 32 +tempo = 120.0 +time_signature = [4, 4] + +[transport] +loop_enabled = false +loop_start = 0 +loop_end = 0 + +[master] +volume = 0.0 # dB +pan = 0.0 # -1.0 to 1.0 +``` + +## track.toml + +Each track directory contains a `track.toml` with the track's configuration: + +```toml +[track] +id = "lead-vocal" +name = "Lead Vocal" +type = "audio" # audio, midi, bus +color = "#e74c3c" +armed = false +muted = false +solo = false + +[mixer] +volume = -3.0 # dB +pan = 0.1 + +[input] +device = "default" +channels = [1] # mono, channel 1 + +[routing] +output = "master" +sends = [ + { bus = "reverb-send", level = -12.0 } +] + +[[plugins]] +id = "com.example.eq" +format = "clap" +preset = "plugins/presets/lead-eq.json" +enabled = true + +[[plugins]] +id = "com.example.compressor" +format = "clap" +enabled = true + +[[clips]] +file = "clips/take-02.flac" +position = 48000 # sample offset in arrangement +length = 480000 +offset = 0 # start offset within the source file +gain = 0.0 # dB +fade_in = 1024 # samples +fade_out = 2048 +``` + +## session.db + +`session.db` is a SQLite database for ephemeral state that does not belong in version control: + +- Current playback position +- Scroll and zoom state per view +- Undo/redo history +- Plugin scan cache +- Window positions and sizes + +This file is listed in `.gitignore` by default. Deleting it resets the session state without affecting the project content. + +## Audio Storage + +| Context | Format | Rationale | +|---|---|---| +| **Stored clips** | FLAC | Lossless compression, typically 50-60% of WAV size | +| **Recording buffer** | WAV | No encoding overhead during real-time capture | +| **On save** | WAV converted to FLAC | Automatic background conversion after recording stops | + +Audio files are referenced by relative path from the track directory. Moving a `.resonance` folder preserves all references. + +## Non-Destructive Editing + +All edits are non-destructive. The original audio files are never modified: + +- **Clip trimming**: Adjusts `offset` and `length` in `track.toml`, source file is unchanged +- **Gain changes**: Stored as metadata, applied at playback +- **Fades**: Defined as sample counts, computed in real-time +- **Time stretching**: Stores the stretch ratio, original file preserved +- **Effects**: Applied in the audio graph, never baked into files + +To produce a final file, use the export function which renders the entire arrangement (or individual stems) to a new file. + +## Version Control + +The project format is designed to work well with Git: + +- TOML files produce clean, line-based diffs +- Track directories isolate changes per track +- `session.db` is excluded via `.gitignore` +- Audio files can be stored with Git LFS +- Merging is straightforward since tracks are independent directories diff --git a/content/docs/fabric/resonance/tracks-and-routing.mdx b/content/docs/fabric/resonance/tracks-and-routing.mdx new file mode 100644 index 0000000..f203e57 --- /dev/null +++ b/content/docs/fabric/resonance/tracks-and-routing.mdx @@ -0,0 +1,90 @@ +--- +title: Tracks and Routing +description: Track types, flexible routing graph, sends and buses +--- + +Resonance provides a flexible track and routing system. Any output can be routed to any input, enabling complex signal chains without limitations. + +## Track Types + +| Type | Purpose | Audio I/O | +|---|---|---| +| **Audio** | Record and play back audio clips | Hardware input, audio output | +| **MIDI** | Sequence notes via piano roll, drive instrument plugins | MIDI input, audio output (via instrument) | +| **Bus** | Sum multiple inputs, apply shared processing | Audio input (from sends/routes), audio output | +| **Master** | Final output, always present | Audio input (from buses/tracks), hardware output | + +### Audio Tracks + +Audio tracks host audio clips on a timeline. Each track has: + +- An input selector (hardware input device and channel) +- A record arm button +- A plugin chain (insert effects) +- Volume, pan, mute, and solo controls +- Output routing (default: master bus) + +### MIDI Tracks + +MIDI tracks contain MIDI clips (note sequences). A MIDI track must be routed to an instrument plugin to produce audio. The instrument can live on the same track (as the first plugin in the chain) or on a separate bus. + +### Bus Tracks + +Bus tracks receive audio from other tracks via sends or direct routing. Common uses: + +- **Submix bus**: Group drums, vocals, or guitars for shared processing +- **Effect bus**: Shared reverb or delay (tracks send to the bus at adjustable levels) +- **Parallel compression**: Blend dry and compressed signals + +### Master Bus + +Every project has one master bus. All unrouted track outputs default to the master. The master bus has its own plugin chain and volume control, and outputs to the hardware audio device (or to a file during export). + +## Routing + +### Direct Routing + +Any track or bus output can be routed to any bus input. Set the output destination in the track's routing section: + +```toml +[routing] +output = "drum-bus" +``` + +### Sends + +Sends tap the signal at a configurable point (pre-fader or post-fader) and route a copy to a bus at an adjustable level: + +```toml +[routing] +output = "master" +sends = [ + { bus = "reverb-send", level = -12.0, pre_fader = false }, + { bus = "delay-send", level = -18.0, pre_fader = false }, +] +``` + +A track can have unlimited sends. Each send has independent level and pre/post-fader selection. + +### Sidechain Routing + +Some plugins (compressors, gates, vocoders) accept a sidechain input. Resonance supports sidechain routing by connecting a track's output to a plugin's sidechain port: + +1. Open the plugin that accepts sidechain input +2. In the sidechain input selector, choose the source track or bus +3. The source signal is routed to the plugin's sidechain port without affecting the main signal path + +### Routing Graph + +Under the hood, all routing is represented as edges in the audio graph DAG. The UI provides a simplified view (output selector, sends, sidechain), but the underlying model supports arbitrary connections between any audio ports. + +Feedback loops are detected and prevented. If a routing change would create a cycle, the connection is rejected with an error message. + +## Track Management + +- **Create**: Click **+** in the track header, or press **Ctrl+T** +- **Duplicate**: Right-click a track header and select **Duplicate** +- **Delete**: Right-click and select **Delete**, or press **Delete** with the track selected +- **Reorder**: Drag track headers to rearrange +- **Color**: Right-click and choose a color for visual grouping +- **Folder**: Group tracks into collapsible folders for organization (folders do not affect routing) diff --git a/content/docs/fabric/trellis.mdx b/content/docs/fabric/trellis.mdx new file mode 100644 index 0000000..8b7af4c --- /dev/null +++ b/content/docs/fabric/trellis.mdx @@ -0,0 +1,147 @@ +--- +title: Trellis +--- + +import { ProductOverview } from "@/components/docs"; +import app from "@/lib/config/app.config"; +import { PiGraph, PiBooks, PiGitBranch } from "react-icons/pi"; + +, + className: "bg-purple-100 text-purple-800 dark:bg-purple-900/20 dark:text-purple-300", + }, + { + label: "Local-First", + icon: , + className: "bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-300", + }, + { + label: "Git-Backed", + icon: , + className: "bg-orange-100 text-orange-800 dark:bg-orange-900/20 dark:text-orange-300", + }, + ]} + links={[ + { + href: "https://trellis.omni.dev", + label: "Visit Website", + type: "website", + }, + { + href: `${app.socials.github}/trellis`, + label: "Visit Repository", + type: "repository", + }, + ]} + alerts={[ + { + title: "Local-First", + description: + "Trellis works fully offline as a desktop app. Notes are plain .md files on disk. Cloud sync and collaboration are opt-in.", + }, + { + title: "Obsidian Compatible", + description: + "Open existing Obsidian vaults directly. Same wikilink syntax, same frontmatter, same file structure.", + }, + ]} +/> + +**Omni Trellis** is a local-first knowledge garden and Obsidian alternative. Notes are plain markdown files on disk. The desktop app (Tauri) works offline with no account required. Cloud sync, real-time collaboration, and publishing are opt-in via trellis.omni.dev. + +## Key Features + +### Editor +- **Rich markdown editing** with Lexical, including slash commands, markdown shortcuts, and live preview +- **Wikilinks** (`[[note]]`) with autocomplete, resolution, and aliased display text +- **Backlinks panel** showing all notes that link to the current note +- **Transclusion** (`![[note]]`) to embed note content inline, with section targeting +- **Images** via paste, drag-and-drop, or `/image` slash command, stored in `assets/` directory +- **Templates** with create, browse, and instantiate flow +- **Daily notes** with automatic date-based creation +- **Full-text search** powered by a Rust backend + +### Knowledge Graph +- **Interactive force-directed graph** visualizing connections between notes +- **Ghost nodes** for wikilinks to nonexistent notes +- **Cross-vault links** for multi-vault setups +- **Unlinked mentions** detection with one-click conversion to wikilinks + +### Canvas +- **Spatial note organization** with draggable elements (text, images, shapes, note cards) +- **Freehand drawing** with pressure-sensitive strokes +- **Edge connections** between elements with labels +- **Note cards** that reference vault notes and navigate on click +- **Canvas persistence** as `.canvas` JSON files alongside notes + +### Query Blocks +- **Dataview-lite** query syntax in fenced code blocks +- Filter notes by `tag`, `link`, `backlink`, or `path` +- Results render as clickable note lists in preview mode +- `/query` slash command for quick insertion + +### Themes & Customization +- **4 built-in editor themes**: Default, Sakura, Minimal, Paper +- **Dark/light mode** with system preference detection +- **Custom CSS snippets** loaded from `.trellis/snippets/` +- **Plugin themes** via the plugin system +- **5 preview themes** for markdown rendering + +### Plugin System +- **Script loading** via ES module dynamic import +- **Slash command plugins** that appear in the editor menu +- **Theme plugins** with CSS injection +- Plugin discovery from `.trellis/plugins/` with manifest-based registration +- Enable/disable per plugin in vault settings + +### Collaboration & Publishing +- **Real-time multiplayer editing** via Hocuspocus (WebSocket-based CRDT sync) +- **Vault publishing** with one toggle -- generates a shareable public URL +- **Public viewer** with full markdown rendering, wikilink navigation, and SEO metadata +- **Cloud-to-local export** to download a cloud vault as local files + +### Local-First Architecture +- **No server required** for the desktop app +- **Plain `.md` files** on disk, editable in any editor +- **Obsidian vault import** -- open an `.obsidian/` directory directly +- **File watcher** detects external edits (vim, VS Code, `git pull`) +- **Graph cache** for fast backlink/tag resolution without scanning files on every operation + +## Two Modes, Same Codebase + +| | Desktop (Tauri) | Web (trellis.omni.dev) | +|---|---|---| +| Storage | Local filesystem | Cloud (GraphQL API) | +| Auth | Not required | Required | +| Collaboration | Not available | Real-time multiplayer | +| Publishing | Not available | Vault-level public URLs | +| Offline | Full offline support | Cached via IndexedDB | + +## Comparison with Obsidian + +| Feature | Trellis | Obsidian | +|---|---|---| +| Local-first files | Yes | Yes | +| Wikilinks & backlinks | Yes | Yes | +| Knowledge graph | Yes | Yes | +| Canvas | Yes | Yes | +| Templates | Yes | Yes (via plugin) | +| Real-time collaboration | Yes (built-in) | Paid add-on | +| Publishing | Yes (built-in) | Paid add-on ($8/mo) | +| Cloud sync | Yes (opt-in) | Paid add-on ($4/mo) | +| Query blocks | Yes (built-in) | Via Dataview plugin | +| Plugin system | Yes (early) | Yes (1000+ plugins) | +| Mobile app | PWA | Native iOS/Android | +| Open source | Yes (Apache 2.0) | No | + +## Use Cases + +- **Personal knowledge management** with local files and no vendor lock-in +- **Team knowledge bases** with real-time collaboration and shared vaults +- **Development documentation** alongside code, editable in any editor +- **Digital gardens** published to the web with a shareable URL +- **Research** with connected thinking, backlinks, and query blocks +- **Obsidian migration** with direct vault import and compatible file format diff --git a/package.json b/package.json index d212832..9951661 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "react": "^19.2.3", "react-dom": "^19.2.3", "react-icons": "^5.5.0", - "satori": "^0.19.1", + "satori": "^0.26.0", "tailwind-merge": "^3.4.0", "ts-pattern": "^5.9.0", "vite": "^7.3.0", diff --git a/vite.config.ts b/vite.config.ts index 5186a4f..223749d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -47,7 +47,8 @@ const viteConfig = defineConfig({ }), react(), // see https://tanstack.com/start/latest/docs/framework/react/guide/hosting for hosting config - nitro(), + // Force node-server preset so the build output runs on Node, not Bun + nitro({ preset: "node-server" }), ], });