Do korzystania z OmniDAW API nie potrzebujesz biblioteki: to linie JSON przez gniazdo TCP. Ta strona daje gotowego klienta dla Pythona oraz trzy małe w czystym Pythonie, Node.js i Ruście, które możesz skopiować i dopasować. Każdy jest kompletny: uruchom go przy otwartym Omni, a wypisze Twoją sesję.
Pobierz wszystko: omnidaw-api-examples.zip albo pojedyncze pliki z linków poniżej.
Klient Pythona
omnidaw.py to pojedynczy plik bez zależności poza biblioteką standardową Pythona. Pobierz go i połóż obok swojego skryptu. Czyta plik endpointu, wita się, wczytuje katalog i zamienia każdą metodę działającego Omni w wywołanie Pythona:
from omnidaw import Omni
with Omni() as omni:
print(omni.capabilities) # ['jobs', 'analysis', 'render', 'plugins', 'audio']
track = omni.tracks.create(kind="midi", name="Keys", instrument="builtin://piano-synth")
omni.clips.write_progression(track=track["id"], slot=0, progression={
"key": {"root": "F", "mode": "minor"},
"chords": "i VI III VII",
"perform": {"pattern": "pad"},
})
job = omni.analysis.capture(source="master", seconds=4)["job"]
print(omni.wait(job)["loudness"])
Ścieżka atrybutów to nazwa metody: omni.clips.write_progression(...) wywołuje clips.write_progression. Parametry to argumenty nazwane i odpowiadają jeden do jednego referencji. Ponieważ metody pochodzą z katalogu działającego Omni, nowsze Omni przynosi nowe metody do Twojego skryptu bez aktualizacji klienta.
| Element | Robi |
|---|---|
Omni(client_name="omnidaw.py", timeout=600.0) | Łączy się, wita, wczytuje katalog. Zgłasza FileNotFoundError lub ConnectionRefusedError, gdy Omni nie działa z włączonym API |
omni.domena.metoda(**params) | Wywołuje domena.metoda i zwraca wynik |
omni.call("domena.metoda", {...}) | To samo z nazwą metody i słownikiem |
omni.wait(job) | Czeka na koniec zadania i zwraca jego wynik; zgłasza OmniError, jeśli zadanie się nie powiodło |
omni.methods | Katalog: słownik od nazwy metody do jej summary, schematu params, example, effect |
omni.capabilities, omni.api_version | Z uścisku dłoni |
OmniError | Zgłaszany, gdy Omni odpowie błędem. Ma .code (kod JSON-RPC) i .data |
Warto wiedzieć:
- Metoda nazwana jak słowo kluczowe Pythona dostaje końcowy podkreślnik:
omni.audio.import_(...)wywołujeaudio.import. omni.domena.metoda.__doc__to opis metody z katalogu, więchelp()i edytor go pokazują.- Na kablu jest naraz jedno żądanie, chronione blokadą, więc klienta można bezpiecznie wywoływać z wielu wątków.
- Aby pominąć plik endpointu, ustaw
OMNI_API_ENDPOINT=host:portiOMNI_API_TOKEN. wait(job)wywołuje w pętlijobs.wait(9 sekund na rundę), aż zadanie się skończy.
Błędy czyta się naturalnie:
from omnidaw import Omni, OmniError
with Omni() as omni:
try:
omni.tracks.update(track=0, volumee=3)
except OmniError as e:
print(e.code, e) # -32602 tracks.update: -32602 unknown field `volumee`, expected one of ...
Minimalny klient w czystym Pythonie
Jeśli wolisz nie kopiować pliku, oto całość w około 30 liniach (minimal_client.py). Znajduje Omni, wita się i wykonuje wywołania:
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Tomasz Głuc
"""Smallest useful OmniDAW API client: no dependencies, just sockets and JSON."""
import json, os, socket, sys
from pathlib import Path
if sys.platform == "win32":
config = Path(os.environ["APPDATA"]) / "omni"
elif sys.platform == "darwin":
config = Path.home() / "Library" / "Application Support" / "omni"
else:
config = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "omni"
endpoint = json.loads((config / "api" / "endpoint.json").read_text())
sock = socket.create_connection((endpoint["host"], endpoint["port"]))
stream = sock.makefile("rw", encoding="utf-8", newline="\n")
next_id = 0
def call(method, params=None):
global next_id
next_id += 1
stream.write(json.dumps({"jsonrpc": "2.0", "id": next_id, "method": method, "params": params or {}}) + "\n")
stream.flush()
reply = json.loads(stream.readline())
if "error" in reply:
raise RuntimeError(f"{method}: {reply['error']['code']} {reply['error']['message']}")
return reply["result"]
call("api.hello", {"token": endpoint["token"], "api_version": "0.21.0",
"client": {"name": "raw-example", "version": "1.0"}})
print(call("session.info"))
print([t["name"] for t in call("tracks.list")])
Minimalny klient w Node.js
To samo w JavaScript, jako moduł ES (minimal_client.mjs, przetestowane na Node.js 24), tylko na wbudowanych modułach:
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Tomasz Głuc
// Smallest useful OmniDAW API client for Node.js: built-in modules only.
import fs from 'node:fs';
import net from 'node:net';
import os from 'node:os';
import path from 'node:path';
import readline from 'node:readline';
function configDir() {
if (process.platform === 'win32') return path.join(process.env.APPDATA, 'omni');
if (process.platform === 'darwin') return path.join(os.homedir(), 'Library', 'Application Support', 'omni');
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config'), 'omni');
}
const endpoint = JSON.parse(fs.readFileSync(path.join(configDir(), 'api', 'endpoint.json'), 'utf8'));
const socket = net.createConnection(endpoint.port, endpoint.host);
const pending = new Map();
let nextId = 0;
readline.createInterface({ input: socket }).on('line', (line) => {
const reply = JSON.parse(line);
const { resolve, reject } = pending.get(reply.id);
pending.delete(reply.id);
if (reply.error) reject(new Error(`${reply.error.code} ${reply.error.message}`));
else resolve(reply.result);
});
const call = (method, params = {}) =>
new Promise((resolve, reject) => {
const id = ++nextId;
pending.set(id, { resolve, reject });
socket.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
});
await call('api.hello', {
token: endpoint.token,
api_version: '0.21.0',
client: { name: 'node-example', version: '1.0' },
});
console.log(await call('session.info'));
console.log((await call('tracks.list')).map((t) => t.name));
socket.end();
Odpowiedzi przychodzą w kolejności żądań, ale ten klient dopasowuje je po id, co pozwala też mieć kilka wywołań naraz.
Minimalny klient w Ruście
Ten klient potrzebuje tylko crate'a serde_json (cargo add serde_json). To ten sam plik co session_info.rs w paczce do pobrania:
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Tomasz Głuc
//
// A complete OmniDAW API client in Rust. It needs only `serde_json`
// (`cargo add serde_json`): find Omni, one TCP connection, one JSON line per
// call. Omni must be running with the API on (Settings, AI Create).
use serde_json::{json, Value};
use std::error::Error;
use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
use std::path::PathBuf;
/// The API version this client was written against.
const API_VERSION: &str = "0.21.0";
/// Omni's settings folder.
fn config_dir() -> Option<PathBuf> {
let var = |name: &str| std::env::var_os(name).map(PathBuf::from);
if cfg!(windows) {
var("APPDATA").map(|p| p.join("omni"))
} else if cfg!(target_os = "macos") {
var("HOME").map(|h| h.join("Library/Application Support/omni"))
} else {
var("XDG_CONFIG_HOME")
.or_else(|| var("HOME").map(|h| h.join(".config")))
.map(|p| p.join("omni"))
}
}
struct Omni {
reader: BufReader<TcpStream>,
writer: TcpStream,
next_id: u64,
}
impl Omni {
/// Find Omni, connect and shake hands; returns the connection and Omni's welcome.
fn connect() -> Result<(Self, Value), Box<dyn Error>> {
let file = config_dir()
.ok_or("cannot find Omni's settings folder")?
.join("api")
.join("endpoint.json");
let text = std::fs::read_to_string(&file).map_err(|e| {
format!(
"Omni is not running with the API on ({}: {e})",
file.display()
)
})?;
let endpoint: Value = serde_json::from_str(&text)?;
let host = endpoint["host"]
.as_str()
.ok_or("endpoint.json has no host")?;
let port = endpoint["port"]
.as_u64()
.ok_or("endpoint.json has no port")?;
let stream = TcpStream::connect((host, u16::try_from(port)?))?;
let mut omni = Omni {
writer: stream.try_clone()?,
reader: BufReader::new(stream),
next_id: 0,
};
let welcome = omni.call(
"api.hello",
json!({
"token": endpoint["token"],
"api_version": API_VERSION,
"client": { "name": "session_info example", "version": "1.0" },
}),
)?;
Ok((omni, welcome))
}
/// Send one request and read its response.
fn call(&mut self, method: &str, params: Value) -> Result<Value, Box<dyn Error>> {
self.next_id += 1;
let request =
json!({ "jsonrpc": "2.0", "id": self.next_id, "method": method, "params": params });
self.writer.write_all(format!("{request}\n").as_bytes())?;
let mut line = String::new();
if self.reader.read_line(&mut line)? == 0 {
return Err("Omni closed the connection".into());
}
let mut reply: Value = serde_json::from_str(&line)?;
if let Some(error) = reply.get("error") {
return Err(format!("{method}: {} {}", error["code"], error["message"]).into());
}
Ok(reply["result"].take())
}
}
fn main() -> Result<(), Box<dyn Error>> {
let (mut omni, welcome) = Omni::connect()?;
println!(
"Omni speaks OmniDAW API {} ({} methods, capabilities {})",
welcome["api_version"], welcome["method_count"], welcome["capabilities"]
);
let info = omni.call("session.info", json!({}))?;
println!("{}", serde_json::to_string_pretty(&info)?);
Ok(())
}
Wstaw go do src/main.rs nowego projektu Cargo i uruchom przez cargo run.
Dowolny inny język
Przepis jest wszędzie ten sam:
- Ustal folder ustawień (tabela na stronie o protokole) i przeczytaj
api/endpoint.json. - Otwórz połączenie TCP z
host:port. - Wyślij
api.helloz tokenem jako pierwszą linię. - Przy każdym wywołaniu zapisz jedną linię JSON i odczytaj jedną linię JSON.
- Odpowiedź z
errortraktuj jako błąd, a pola wyniku, których nie znasz, ignoruj.
Jeśli chcesz typów dla swojego języka, wygeneruj je z katalogu: api.methods (albo omni-mcp --catalog, gdy Omni nie działa) daje schemat JSON parametrów każdej metody.
Write your own Model Context Protocol server for Omni, in any language: how the catalog becomes tools, a complete working server in Python, and how to test and ship it.
Working examples for the OmniDAW API: compose a loop, build a song, automate, mix, master for streaming, export stems, chop a break, use plugins.
