diff --git a/packages/app/studio/README.md b/packages/app/studio/README.md
index 5b8e66a9f..c5abc1fb7 100644
--- a/packages/app/studio/README.md
+++ b/packages/app/studio/README.md
@@ -9,6 +9,11 @@ For a guided overview of the interface, see the [UI tour](../../docs/docs-user/u
Guidance on saving and importing projects lives in the [file management guide](../../docs/docs-user/features/file-management.md). The [notepad feature](../../docs/docs-user/features/notepad.md) lets you store project notes using Markdown.
Developer details about project storage and sessions can be found in the [projects documentation](../../docs/docs-dev/projects/overview.md).
+The capture subsystem for recording audio and MIDI is described in the
+[capture architecture docs](../../docs/docs-dev/architecture/capture/overview.md).
+Users can follow the [recording workflow guide](../../docs/docs-user/workflows/recording.md)
+to learn how to capture takes.
+
Quickly launch commands using the Spotlight search palette with Shift+Enter. Read the [user guide](../../docs/docs-user/features/search.md) or see the [developer docs](../../docs/docs-dev/ui/spotlight/overview.md).
Exchange projects with other DAWs via the `.dawproject` format using the
[DAWproject workflow](../../docs/docs-user/workflows/dawproject.md). Implementation
diff --git a/packages/app/studio/src/project/ProjectDialogs.tsx b/packages/app/studio/src/project/ProjectDialogs.tsx
index f5ec37b99..ce6fec647 100644
--- a/packages/app/studio/src/project/ProjectDialogs.tsx
+++ b/packages/app/studio/src/project/ProjectDialogs.tsx
@@ -1,5 +1,7 @@
/**
* Collection of dialogs used for common project management operations.
+ * See the capture architecture docs for details on recording:
+ * {@link ../../../../docs/docs-dev/architecture/capture/overview.md}.
*/
import {Dialog} from "@/ui/components/Dialog"
import {ExportStemsConfiguration, IconSymbol} from "@opendaw/studio-adapters"
diff --git a/packages/docs/docs-dev/architecture/capture/audio.md b/packages/docs/docs-dev/architecture/capture/audio.md
new file mode 100644
index 000000000..053f95482
--- /dev/null
+++ b/packages/docs/docs-dev/architecture/capture/audio.md
@@ -0,0 +1,8 @@
+# Audio Capture
+
+`CaptureAudio` wraps a browser `MediaStream` to provide microphone and line
+input. When armed, it requests a stream matching the desired device and channel
+configuration and feeds the data into a [`RecordingWorklet`](./worklet.md).
+
+Levels can be adjusted via a software gain stage before samples are written to
+the recording buffer.
diff --git a/packages/docs/docs-dev/architecture/capture/midi.md b/packages/docs/docs-dev/architecture/capture/midi.md
new file mode 100644
index 000000000..2425ae97f
--- /dev/null
+++ b/packages/docs/docs-dev/architecture/capture/midi.md
@@ -0,0 +1,6 @@
+# MIDI Capture
+
+`CaptureMidi` listens to Web MIDI input devices and forwards filtered note
+messages to the recording subsystem. Each capture can restrict events to a
+specific channel and normalises note on/off pairs before they are persisted in
+the project timeline.
diff --git a/packages/docs/docs-dev/architecture/capture/overview.md b/packages/docs/docs-dev/architecture/capture/overview.md
new file mode 100644
index 000000000..42b5a1bde
--- /dev/null
+++ b/packages/docs/docs-dev/architecture/capture/overview.md
@@ -0,0 +1,14 @@
+# Capture Architecture
+
+The capture layer manages audio and MIDI inputs that feed the recording system.
+It instantiates `Capture` objects for audio units and coordinates their lifecycles
+through the `CaptureManager`.
+
+- [Audio capture](./audio.md) describes how microphone and line inputs are
+ obtained from the browser.
+- [MIDI capture](./midi.md) covers incoming note events.
+- [Recording worklet](./worklet.md) explains how raw samples are buffered and
+ converted into peak data.
+
+This subsystem works in tandem with the [`Recording` API](../../../../studio/core/src/capture/Recording.ts)
+which orchestrates the actual recording session.
diff --git a/packages/docs/docs-dev/architecture/capture/worklet.md b/packages/docs/docs-dev/architecture/capture/worklet.md
new file mode 100644
index 000000000..1ab12b89f
--- /dev/null
+++ b/packages/docs/docs-dev/architecture/capture/worklet.md
@@ -0,0 +1,6 @@
+# Recording Worklet
+
+The `RecordingWorklet` is an `AudioWorkletNode` that receives frames from a
+`MediaStream` and writes them into a ring buffer. It also computes peak data so
+waveforms can be displayed immediately after recording. Once finalized, the
+worklet stores audio and peak information through the sample storage service.
diff --git a/packages/docs/docs-user/workflows/recording.md b/packages/docs/docs-user/workflows/recording.md
new file mode 100644
index 000000000..df4af4877
--- /dev/null
+++ b/packages/docs/docs-user/workflows/recording.md
@@ -0,0 +1,18 @@
+# Recording Workflow
+
+Capture audio or MIDI directly onto the timeline.
+
+1. **Choose a track type.** Add an audio or instrument track depending on the
+ source you want to record.
+2. **Arm the track.** Click the record icon on the track header to enable
+ input monitoring.
+3. **Select the device.** Use the track's capture panel to pick the desired
+ microphone or MIDI device.
+4. **Check levels.** Speak or play to ensure the meter responds without
+ clipping.
+5. **Press record.** Hit the transport record button and perform.
+6. **Stop when finished.** The take is placed on the timeline where it can be
+ edited like any other clip.
+
+For details on the underlying system see the
+[developer capture docs](../../docs-dev/architecture/capture/overview.md).
diff --git a/packages/docs/sidebarsDev.js b/packages/docs/sidebarsDev.js
index d6b07f559..98f08bad5 100644
--- a/packages/docs/sidebarsDev.js
+++ b/packages/docs/sidebarsDev.js
@@ -59,6 +59,16 @@ module.exports = {
"architecture/headless-vs-studio",
"architecture/persistence",
"architecture/opfs-samples",
+ {
+ type: "category",
+ label: "Capture",
+ items: [
+ "architecture/capture/overview",
+ "architecture/capture/audio",
+ "architecture/capture/midi",
+ "architecture/capture/worklet",
+ ],
+ },
],
},
{
diff --git a/packages/studio/core/src/RecordingWorklet.ts b/packages/studio/core/src/RecordingWorklet.ts
index d6086c6f2..97fc62c93 100644
--- a/packages/studio/core/src/RecordingWorklet.ts
+++ b/packages/studio/core/src/RecordingWorklet.ts
@@ -70,7 +70,9 @@ class PeaksWriter implements Peaks, Peaks.Stage {
/**
* Captures audio from its input and exposes recorded data along with peak
- * information.
+ * information. Recording is performed in the audio worklet thread while the
+ * class provides a loader style interface for retrieving the resulting
+ * {@link AudioData} and peak information.
*/
export class RecordingWorklet extends AudioWorkletNode implements Terminable, SampleLoader {
readonly uuid: UUID.Format = UUID.generate()
@@ -118,13 +120,22 @@ export class RecordingWorklet extends AudioWorkletNode implements Terminable, Sa
})
}
+ /** Total number of frames recorded so far. */
get numberOfFrames(): int {return this.#output.length * RenderQuantum}
+ /** Recorded audio data once {@link finalize} has been called. */
get data(): Option {return this.#data}
+ /** Peak information for the recorded data if available. */
get peaks(): Option {return this.#peaks}
+ /** Loading state for consumers implementing {@link SampleLoader}. */
get state(): SampleLoaderState {return this.#state}
+ /** Part of {@link SampleLoader}; no-op for recordings. */
invalidate(): void {}
+ /**
+ * Observe loader state changes. Once loaded, the observer is invoked
+ * immediately and no subscription is kept.
+ */
subscribe(observer: Observer): Subscription {
if (this.#state.type === "loaded") {
observer(this.#state)
@@ -162,6 +173,7 @@ export class RecordingWorklet extends AudioWorkletNode implements Terminable, Sa
this.#setState({type: "loaded"})
}
+ /** Stop recording and release any buffered data. */
terminate(): void {
this.#reader.stop()
this.#isRecording = false
diff --git a/packages/studio/core/src/capture/Capture.ts b/packages/studio/core/src/capture/Capture.ts
index ffde91b3a..f00f4a3d2 100644
--- a/packages/studio/core/src/capture/Capture.ts
+++ b/packages/studio/core/src/capture/Capture.ts
@@ -13,6 +13,11 @@ import {CaptureBox} from "@opendaw/studio-adapters"
import {RecordingContext} from "./RecordingContext"
import {CaptureManager} from "./CaptureManager"
+/**
+ * Base class for audio or MIDI capture units. A {@link Capture} wraps a
+ * {@link CaptureBox} and manages device selection and arming state for the
+ * associated {@link AudioUnitBox}.
+ */
export abstract class Capture implements Terminable {
readonly #terminator = new Terminator()
@@ -23,6 +28,11 @@ export abstract class Capture implements Te
readonly #deviceId: MutableObservableValue