A CLAP plugin that plays a video file in sync with a DAW transport. Includes a companion Bitwig Controller Extension for full two-way sync with Bitwig Studio.
Note
This project was created for personal needs and has only been tested on Windows 11 with Bitwig Studio 6. Other configurations may work but I have not tested them.
Made by a Bitwig user for Bitwig users. Not affiliated with Bitwig in any way. Mostly made with Antigravity, Claude and Gemini.
Get the latest version from the Releases page.
| Platform | Status |
|---|---|
| Windows | ✅ Tested |
| macOS | |
| Linux |
macOS and Linux builds are provided as-is. If you try them, please open an issue to report whether they work!
- DAW → video sync — reads
clap_event_transporton every audio block; corrects drift > 50 ms with a hard seek. - Video → DAW seek — scrubbing the video or stepping frames moves the DAW playhead via the companion controller extension (see below).
- Stopped-playhead sync — when Bitwig moves the caret while stopped (a quirk where CLAP plugins receive no notification), the companion extension detects the change and pushes it to the plugin so the video follows.
- FFmpeg decoding — MP4, MKV, MOV, AVI, and anything FFmpeg handles.
- OpenGL 3.3 rendering with correct letterboxing / pillarboxing.
- Resizable embedded window via the CLAP GUI extension.
- State persistence — the host saves and restores the open file path and position.
| Dependency | Version |
|---|---|
| CMake | ≥ 3.21 |
| C++ compiler | C++20 (GCC 12+, Clang 15+, MSVC 2022) |
| FFmpeg libraries | ≥ 5.0 (libavcodec, libavformat, libavutil, libswscale) |
| OpenGL | ≥ 3.3 |
| Linux: X11, GLX | system packages |
| macOS: Cocoa, OpenGL.framework | Xcode / CommandLineTools |
| Windows: WGL, WinSock2 | Windows SDK |
The UDP IPC code in ipc_bridge.cpp is cross-platform (WinSock2 on Windows, POSIX sockets on Linux/macOS).
# Ubuntu / Debian
sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev
# Arch
sudo pacman -S ffmpeg
# macOS (Homebrew)
brew install ffmpeg
# Windows — download a dev build from https://www.gyan.dev/ffmpeg/builds/
# Then pass -DFFMPEG_ROOT=<path> to CMake (see Build section).git clone <this-repo>
cd DAWvid
# Linux / macOS
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
# Windows (set FFMPEG_ROOT to your extracted FFmpeg dev package)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DFFMPEG_ROOT=C:/ffmpeg
cmake --build build --parallelDo not commit FFmpeg SDK binaries or headers into the repo. Keep FFmpeg as an external dependency and point CMake at it with
FFMPEG_ROOT.Optionally you can use a local workspace symlink if you want a stable path:
mklink /D deps\ffmpeg C:\path\to\ffmpeg cmake -B build -DCMAKE_BUILD_TYPE=Release -DFFMPEG_ROOT=%CD%/deps/ffmpeg
Install to your CLAP folder:
cmake --install build
# or:
cmake --build build --target install-plugincmake --build build --target distThis produces build/dist/DAWvid-install/:
DAWvid-install/
DAWvid.clap
avcodec-62.dll
avformat-62.dll
avutil-60.dll
swscale-9.dll
install.bat ← double-click to install
install.ps1
Double-click install.bat — it copies everything to C:\Program Files\Common Files\CLAP\ (the standard CLAP scan path). The FFmpeg DLLs go next to DAWvid.clap so the DAW finds them without PATH changes.
C:\Program Files\Common Files\CLAP\
DAWvid.clap
avcodec-62.dll
avformat-62.dll
avutil-60.dll
swscale-9.dll
Copy DAWvid.clap (a bundle) to:
~/Library/Audio/Plug-Ins/CLAP/
FFmpeg must be installed (brew install ffmpeg).
Copy DAWvid.clap to ~/.clap/ or /usr/lib/clap/.
Why is this needed? CLAP plugins are sandboxed — they can read the transport but cannot command the host to seek, play, or stop. Bitwig also has a quirk where it does not notify CLAP plugins when the user moves the playhead while stopped. The companion extension runs inside Bitwig with full Transport API access and bridges both limitations over a local UDP socket.
-
Copy
bitwig/DAWvid.control.jsinto Bitwig's controller scripts folder:OS Path Windows %USERPROFILE%\Documents\Bitwig Studio\Controller Scripts\DAWvid\macOS ~/Documents/Bitwig Studio/Controller Scripts/DAWvid/Linux ~/Bitwig Studio/Controller Scripts/DAWvid/ -
In Bitwig Studio:
- Open Settings (gear icon, top-right)
- Go to Controllers
- Click Add controller
- In the controller list, find DAWvid under the manufacturer column
- Select DAWvid Bridge
- Click Add
The entry appears as DAWvid — DAWvid Bridge in your controller list once added.
-
The extension and plugin discover each other automatically on
localhost. No further configuration needed.
The controller extension requires Bitwig 5 or later (GraalVM JS engine). It uses two loopback UDP ports: 47491 (controller listens) and 47492 (plugin listens). Both are loopback-only and not exposed to the network.
┌──────────────────────────────────────────────────────────────────────┐
│ Bitwig Studio │
│ │
│ ┌─────────────────────────────┐ ┌──────────────────────────────┐ │
│ │ CLAP plugin (DAWvid.clap) │ │ Controller Extension │ │
│ │ │ │ (DAWvid.control.js) │ │
│ │ audio thread │ │ │ │
│ │ process() │ │ transport.playStartPosition │ │
│ │ TransportSync::update()│ │ .addValueObserver() ─────┼─┼─► UDP 47492
│ │ ▼ │ │ │ │ "POS_BEATS n"
│ │ syncVideoToTransport() │◄───┼─── UDP 47492 │ │
│ │ ▼ │ │ "POS_BEATS n" │ │
│ │ VideoDecoder::seekTo() │ │ │ │
│ │ │ │ transport.isPlaying() │ │
│ │ GUI thread │ │ .addValueObserver() ─────┼─┼─► UDP 47492
│ │ scrub / step / play ─────┼────┼──► UDP 47491 │ │ "PLAYING/STOPPED"
│ │ │ │ "SEEK_BEATS n" │ │
│ │ │ │ ▼ │ │
│ │ │ │ transport.setPosition() │ │
│ └─────────────────────────────┘ └──────────────────────────────┘ │
│ │
│ VideoDecoder (background thread) │
│ FFmpeg decode loop → DecodedFrame (RGBA) → shared_ptr │
│ │
│ GLRenderer (GUI timer, 60 Hz) │
│ uploads frame → GL texture → draws with aspect-ratio scaling │
└──────────────────────────────────────────────────────────────────────┘
DAW → Video (every audio block, audio thread):
TransportSync::update()readsclap_process_t::transport- Detects play/pause changes and position jumps > 50 ms
- Calls
VideoDecoder::play()/pause()/seekTo()
Stopped-playhead fix (IPC receive thread → audio thread):
- User drags the Bitwig caret while transport is stopped
- Controller extension sees
playStartPosition()change, sendsPOS_BEATS <n>to the plugin IPCBridgereceive thread callsTransportSync::injectPositionBeats(), converts beats → seconds using the last known tempo, sets an atomic flag- Next
process()call: flag is cleared,syncVideoToTransport()seeks the video
Video → DAW (GUI thread → controller extension):
- User scrubs the video or clicks play/stop in the plugin window
IPCBridge::sendSeekBeats()/sendPlay()/sendStop()sends a UDP packet to the controller extension- Extension calls
transport.setPosition()/transport.play()/transport.stop()— Bitwig's playhead moves
All messages are ASCII, newline-terminated, over loopback UDP.
| Direction | Message | Meaning |
|---|---|---|
| Controller → Plugin | POS_BEATS <double> |
Caret moved while stopped |
| Controller → Plugin | PLAYING |
Transport started |
| Controller → Plugin | STOPPED |
Transport stopped |
| Plugin → Controller | SEEK_BEATS <double> |
Video scrubbed to this beat |
| Plugin → Controller | PLAY |
User clicked play in plugin |
| Plugin → Controller | STOP |
User clicked stop in plugin |
Position values are in beats (quarter notes from the start of the arrangement), matching Bitwig's native timeline unit. The plugin converts to seconds using the last tempo reported by the CLAP transport.
| Platform | Status |
|---|---|
| Windows | ✅ Tested |
| macOS | |
| Linux |
macOS and Linux builds are provided as-is. If you try them, please open an issue to report whether they work!
The UDP IPC code in ipc_bridge.cpp is cross-platform (WinSock2 on Windows, POSIX sockets on Linux/macOS).
src/
plugin_entry.cpp CLAP factory + DLL entry point
video_player_plugin.cpp Lifecycle, extensions, wires everything together
video_decoder.cpp FFmpeg decode loop (background thread)
transport_sync.cpp DAW↔video sync + IPC position injection
gl_renderer.cpp OpenGL 3.3 rendering, letterbox/pillarbox
gui_window.cpp Platform-native window (Win32/Cocoa/X11)
ipc_bridge.cpp UDP IPC bridge to companion controller extension
include/ Matching headers
bitwig/
DAWvid.control.js Bitwig Controller Extension (install separately)
This project is licensed under the MIT License.
