UNPKG

reaper-osc

Version:
263 lines 9.88 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ReaperConfiguration = exports.Reaper = void 0; /** * Contains classes for controlling Reaper via OSC * @module */ const Messages_1 = require("./Messages"); const Tracks_1 = require("./Tracks"); const Transport_1 = require("./Transport"); const osc = __importStar(require("osc")); const Handlers_1 = require("./Handlers"); const Notify_1 = require("./Notify"); /** * Allows control of an instance of Reaper via OSC. * * @example * ```typescript * // Create an instance of Reaper using default settings * const reaper = new Reaper(); * // Start OSC * reaper.startOsc(); * // Give the port a chance to open, then tell Reaper to start playback * setTimeout(() => {reaper.transport.play();}, 100); *``` */ let Reaper = class Reaper { constructor(config = new ReaperConfiguration()) { this._isMetronomeEnabled = false; this._handlers = []; this._isReady = false; this._tracks = []; this._transport = new Transport_1.Transport(message => this.sendOscMessage(message)); this._osc = new osc.UDPPort({ localAddress: config.localAddress, localPort: config.localPort, remoteAddress: config.remoteAddress, remotePort: config.remotePort, broadcast: true, metadata: true, }); this._afterMessageReceived = config.afterMessageReceived; this._log = config.log; this.initOsc(); this.initHandlers(); this._masterTrack = new Tracks_1.Track(0, config.numberOfFx, message => this.sendOscMessage(message)); for (let i = 0; i < config.numberOfTracks; i++) { this._tracks[i] = new Tracks_1.Track(i + 1, config.numberOfFx, message => this.sendOscMessage(message)); } } /** Indicates whether the metronome is enabled */ get isMetronomeEnabled() { return this._isMetronomeEnabled; } /** Indicates whether OSC is ready to send and receive messages */ get isReady() { return this._isReady; } /** The master track */ get master() { return this._masterTrack; } // eslint-disable-next-line @typescript-eslint/no-unused-vars onPropertyChanged(property, callback) { throw new Error('not implemented'); } /** * Triggers the action `Control surface: Refresh all surfaces` (Command ID: 41743) */ refreshControlSurfaces() { this.triggerAction(41743); } /** * Send a message to Reaper via OSC. Messages may not be sent while {@link Reaper.isReady} is false. * @param message The OSC message to be sent */ sendOscMessage(message) { if (!this._isReady) { throw new Error("Can't send while OSC is not ready"); } this._osc.send(message); this._log('debug', 'OSC Message sent', message); } /** Open the OSC port and start listening for messages */ async start() { if (this.isReady) { return; } let errorCallback; let readyCallback; const promise = new Promise((resolve, reject) => { errorCallback = (err) => { this._log('debug', 'Error opening OSC connection', err); reject(err); }; readyCallback = () => { this._log('debug', 'OSC listener ready'); this._isReady = true; resolve(); }; this._osc.once('ready', readyCallback); this._osc.once('error', errorCallback); }); const removeListeners = () => { this._osc.removeListener('ready', readyCallback); this._osc.removeListener('error', errorCallback); }; this._osc.open(); try { await promise; } finally { removeListeners(); } } /** Stop listening for OSC messages */ async stop() { if (!this._isReady) { return; } const promise = new Promise(resolve => { this._osc.once('close', () => { this._log('debug', 'OSC listener stopped'); resolve(); }); }); this._isReady = false; this._osc.close(); return promise; } /** Toggle the metronome on or off */ toggleMetronome() { this.sendOscMessage(new Messages_1.OscMessage('/click')); } /** The current bank of tracks */ get tracks() { return this._tracks; } /** Transport controls */ get transport() { return this._transport; } /** * Trigger a Reaper action * @param commandId The Command ID of the action to be triggered. * @param value The value to send for the action. Note that some actions expect the CC value (0-127) while others expect a decimal value between 0 and 1. * @example * ```typescript * // Trigger action 'Track: Toggle mute for master track' * reaper.triggerAction(14); * // Trigger SWS Extension action 'SWS/S&M: Live Config #1 - Apply config (MIDI/OSC only)' with a CC value of 3, selects config #3 * reaper.triggerAction('_S&M_LIVECFG_APPLY1', 3); * // Trigger action 'Track: Set volume for track 01 (MIDI CC/OSC only)' with a value of 0.75, sets volume of track 1 to +0.0dB * reaper.triggerAction(20, 0.7156) * ``` */ triggerAction(commandId, value = null) { if (value !== null && (value < 0 || value > 127)) { throw new RangeError('Values must be between 0 and 127 inclusive'); } this.sendOscMessage(new Messages_1.ActionMessage(commandId, value)); } initHandlers() { this._handlers.push(new Handlers_1.TrackMessageHandler(trackNumber => { if (trackNumber == 0) { return this._masterTrack; } return this._tracks[trackNumber - 1] !== undefined ? this._tracks[trackNumber - 1] : null; }), new Handlers_1.TransportMessageHandler(this._transport), new Handlers_1.BooleanMessageHandler('/click', value => (this._isMetronomeEnabled = value))); } initOsc() { this._osc.on('error', (err) => { this._log('error', 'OSC error received', err); }); this._osc.on('message', (rawMessage) => { const message = new Messages_1.OscMessage(rawMessage.address, rawMessage.args); let handled = false; for (const handler of this._handlers) { if (handler.handle(message)) { handled = true; break; } } if (this._afterMessageReceived !== null) { this._afterMessageReceived(message, handled); } }); } }; __decorate([ Notify_1.notify('isMetronomeEnabled') ], Reaper.prototype, "_isMetronomeEnabled", void 0); Reaper = __decorate([ Notify_1.notifyOnPropertyChanged ], Reaper); exports.Reaper = Reaper; class ReaperConfiguration { constructor() { this.afterMessageReceived = null; /** The address to listen for Reaper OSC messages on */ this.localAddress = '127.0.0.1'; /** The port to listen for Reaper OSC messages on */ this.localPort = 9000; /** Function for logging messages. Defaults to logging to console */ this.log = ConsoleLogger; /** Number of FX per track */ this.numberOfFx = 8; /** Number of tracks per bank */ this.numberOfTracks = 8; /** The address to send Reaper OSC messages to */ this.remoteAddress = '127.0.0.1'; /** The port to send Reaper OSC messages to */ this.remotePort = 8000; } } exports.ReaperConfiguration = ReaperConfiguration; // eslint-disable-next-line @typescript-eslint/no-explicit-any function ConsoleLogger(level, message, ...optionalParams) { switch (level) { case 'debug': { console.debug(message, optionalParams); break; } case 'info': { console.log(message, optionalParams); break; } case 'warn': { console.warn(message, optionalParams); break; } case 'error': { console.error(message, optionalParams); break; } } } //# sourceMappingURL=Reaper.js.map