reaper-osc
Version:
Controls Cockos Reaper using OSC
238 lines • 10 kB
JavaScript
"use strict";
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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Reaper = exports.ConsoleLogger = exports.ReaperConfiguration = void 0;
const Tracks_1 = require("./Tracks");
const Transport_1 = require("./Transport");
const ViewPort_1 = require("./ViewPort");
const Device_1 = require("./Device");
const SelectedTrack_1 = require("./SelectedTrack");
const Marker_1 = require("./Marker");
const Region_1 = require("./Region");
const Client_1 = require("./Client/Client");
const Commands_1 = require("./Client/Commands");
const Config_1 = require("./Config");
const Notify_1 = require("./Notify");
// Re-export for backwards compatibility
var Config_2 = require("./Config");
Object.defineProperty(exports, "ReaperConfiguration", { enumerable: true, get: function () { return Config_2.ReaperConfiguration; } });
Object.defineProperty(exports, "ConsoleLogger", { enumerable: true, get: function () { return Config_2.ConsoleLogger; } });
/**
* 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(clientOrConfig, config) {
this._isAutoRecordArmEnabled = false;
this._isAnySoloed = false;
this._isMetronomeEnabled = false;
this._markers = [];
this._regions = [];
this._tracks = [];
let resolvedConfig;
if (clientOrConfig instanceof Client_1.ReaperOscClient) {
this._client = clientOrConfig;
resolvedConfig = config !== null && config !== void 0 ? config : new Config_1.ReaperConfiguration();
}
else {
resolvedConfig = clientOrConfig !== null && clientOrConfig !== void 0 ? clientOrConfig : new Config_1.ReaperConfiguration();
this._client = new Client_1.ReaperOscClient(resolvedConfig);
}
this._afterMessageReceived = resolvedConfig.afterMessageReceived;
const send = (command) => this._client.send(command);
this._device = new Device_1.DeviceState(send);
this._transport = new Transport_1.Transport(send);
this._viewport = new ViewPort_1.ViewPort(send);
this._selectedTrack = new SelectedTrack_1.SelectedTrack(resolvedConfig.numberOfFx, resolvedConfig.numberOfSends, resolvedConfig.numberOfReceives, send);
this._masterTrack = new Tracks_1.Track(0, resolvedConfig.numberOfFx, resolvedConfig.numberOfSends, resolvedConfig.numberOfReceives, send);
for (let i = 0; i < resolvedConfig.numberOfTracks; i++) {
this._tracks[i] = new Tracks_1.Track(i + 1, resolvedConfig.numberOfFx, resolvedConfig.numberOfSends, resolvedConfig.numberOfReceives, send);
}
for (let i = 0; i < resolvedConfig.numberOfMarkers; i++) {
this._markers[i] = new Marker_1.Marker(i + 1, send);
}
for (let i = 0; i < resolvedConfig.numberOfRegions; i++) {
this._regions[i] = new Region_1.Region(i + 1, send);
}
// Track whether the last parsed event was known (for afterMessageReceived)
let lastEventWasKnown = false;
this._client.on('message', event => {
var _a, _b, _c;
lastEventWasKnown = event.type !== 'unknown';
switch (event.type) {
case 'metronome':
this._isMetronomeEnabled = event.enabled;
break;
case 'autoRecordArm':
this._isAutoRecordArmEnabled = event.enabled;
break;
case 'anySolo':
this._isAnySoloed = event.active;
break;
}
this._transport.handleEvent(event);
this._selectedTrack.handleEvent(event);
if ('trackNumber' in event) {
const trackNumber = event.trackNumber;
if (trackNumber === 0) {
this._masterTrack.handleEvent(event);
}
else {
(_a = this._tracks[trackNumber - 1]) === null || _a === void 0 ? void 0 : _a.handleEvent(event);
}
}
if (event.type === 'marker:name' || event.type === 'marker:number' || event.type === 'marker:time') {
(_b = this._markers[event.slotIndex - 1]) === null || _b === void 0 ? void 0 : _b.handleEvent(event);
}
if (event.type === 'region:name' || event.type === 'region:number' || event.type === 'region:time' || event.type === 'region:length') {
(_c = this._regions[event.slotIndex - 1]) === null || _c === void 0 ? void 0 : _c.handleEvent(event);
}
});
this._client.on('rawMessage', message => {
if (this._afterMessageReceived !== null) {
this._afterMessageReceived(message, lastEventWasKnown);
}
});
}
/** The underlying stateless OSC client */
get client() {
return this._client;
}
/** Controls the OSC device's navigation state (track/bank/FX selection) */
get device() {
return this._device;
}
/** Indicates whether auto-record-arm is enabled */
get isAutoRecordArmEnabled() {
return this._isAutoRecordArmEnabled;
}
/** Indicates whether any track is soloed */
get isAnySoloed() {
return this._isAnySoloed;
}
/** 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._client.isReady;
}
/** The master track */
get master() {
return this._masterTrack;
}
/** The current bank of markers */
get markers() {
return this._markers;
}
/** The current bank of regions */
get regions() {
return this._regions;
}
// 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) {
this._client.sendRaw(message);
}
/** Open the OSC port and start listening for messages */
async start() {
return this._client.start();
}
/** Stop listening for OSC messages */
async stop() {
return this._client.stop();
}
/** Toggle auto-record-arm on or off */
toggleAutoRecordArm() {
this._client.send(Commands_1.ToggleAutoRecordArm());
}
/** Toggle the metronome on or off */
toggleMetronome() {
this._client.send(Commands_1.ToggleMetronome());
}
/** Reset all solos */
soloReset() {
this._client.send(Commands_1.ResetSolos());
}
/**
* The OSC device's currently focused track.
* State is populated by the sync burst Reaper sends when the focused track changes.
* @see {@link DeviceState.selectTrack}
*/
get selectedTrack() {
return this._selectedTrack;
}
/** The current bank of tracks */
get tracks() {
return this._tracks;
}
/** Transport controls */
get transport() {
return this._transport;
}
/** Arrange-view scroll and zoom controls */
get viewport() {
return this._viewport;
}
/**
* 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._client.send(Commands_1.TriggerAction(commandId, value !== null && value !== void 0 ? value : undefined));
}
};
__decorate([
Notify_1.notify('isAutoRecordArmEnabled')
], Reaper.prototype, "_isAutoRecordArmEnabled", void 0);
__decorate([
Notify_1.notify('isAnySoloed')
], Reaper.prototype, "_isAnySoloed", void 0);
__decorate([
Notify_1.notify('isMetronomeEnabled')
], Reaper.prototype, "_isMetronomeEnabled", void 0);
Reaper = __decorate([
Notify_1.notifyOnPropertyChanged
], Reaper);
exports.Reaper = Reaper;
//# sourceMappingURL=Reaper.js.map