reaper-osc
Version:
Controls Cockos Reaper using OSC
278 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.Beat = exports.Transport = void 0;
/**
* Contains classes for controlling Reaper's transport
* @module
*/
const Notify_1 = require("./Notify");
const Handlers_1 = require("./Handlers");
const Messages_1 = require("./Messages");
/** The Reaper transport */
let Transport = class Transport {
/**
* @param sendOscMessage A callback used to send OSC messages to Reaper
*/
constructor(sendOscMessage) {
this._beat = '1.1.00';
this._frames = '00:00:00:00';
this._isFastForwarding = false;
this._isPlaying = false;
this._isRecording = false;
this._isRepeatEnabled = false;
this._isRewinding = false;
this._isStopped = false;
this._loopEnd = 0;
this._loopStart = 0;
this._time = 0;
this._handlers = [
new Handlers_1.BooleanMessageHandler('/repeat', value => (this._isRepeatEnabled = value)),
new Handlers_1.BooleanMessageHandler('/record', value => (this._isRecording = value)),
new Handlers_1.BooleanMessageHandler('/stop', value => (this._isStopped = value)),
new Handlers_1.BooleanMessageHandler('/play', value => (this._isPlaying = value)),
new Handlers_1.BooleanMessageHandler('/rewind', value => (this._isRewinding = value)),
new Handlers_1.BooleanMessageHandler('/forward', value => (this._isFastForwarding = value)),
new Handlers_1.FloatMessageHandler('/time', value => (this._time = value)),
new Handlers_1.StringMessageHandler('/beat/str', value => (this._beat = value)),
new Handlers_1.StringMessageHandler('/frames/str', value => (this._frames = value)),
new Handlers_1.FloatMessageHandler('/loop/start/time', value => (this._loopStart = value)),
new Handlers_1.FloatMessageHandler('/loop/end/time', value => (this._loopEnd = value))
];
this._sendOscMessage = sendOscMessage;
}
/** Indicates the current transport beat in format mm.bb.xx */
get beat() {
return this._beat;
}
/** Indicates the current transport from in format h:m:s:f */
get frames() {
return this._frames;
}
/** Indicates whether playback is active */
get isPlaying() {
return this._isPlaying;
}
/** Indicates whether playback is stopped */
get isStopped() {
return this._isStopped;
}
/** Indicates whether recording is active */
get isRecording() {
return this._isRecording;
}
/** Indicates whether rewind is active */
get isRewinding() {
return this._isRewinding;
}
/** Indicates whether fast-foward is active */
get isFastForwarding() {
return this._isFastForwarding;
}
/** Indicates whether repeat is enabled */
get isRepeatEnabled() {
return this._isRepeatEnabled;
}
/** Indicates the end time of the loop (in seconds) */
get loopEnd() {
return this._loopEnd;
}
/** Indicates the start time of the loop (in seconds) */
get loopStart() {
return this._loopStart;
}
/** Indicates the current transport time in seconds */
get time() {
return this._time;
}
/** Jumps to the specified beat (absolute)
* @param beat The beat to jump to
*/
jumpToBeat(beat) {
this._sendOscMessage(new Messages_1.StringMessage('/beat/str', beat.toString()));
}
/** Jumps to the specified frame (absolute)
* @param frame Frame to jump to (in format h:m:s:f). Values in an invalid format will be ignored by Reaper
*/
jumpToFrame(frame) {
this._sendOscMessage(new Messages_1.StringMessage('/frames/str', frame));
}
/** Jumps to the specified time in seconds (absolute)
* @param time The time to jump to (in seconds). If this value is negative, Reaper will jump to 0
*/
jumpToTime(time) {
this._sendOscMessage(new Messages_1.FloatMessage('/time', time));
}
/**
* Jumps to a relative time in seconds.
* Note that the absolute value to jump to is calculated by the library based on the currently known time,
* as Reaper does not appear to support jumping to a relative time via OSC
* @param time The relative time jump (in seconds)
*/
jumpToTimeRelative(time) {
const newTime = Math.max(this._time + time, 0);
this._sendOscMessage(new Messages_1.FloatMessage('/time', newTime));
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onPropertyChanged(property, callback) {
throw new Error('not implemented');
}
/** Toggle pause */
pause() {
this._sendOscMessage(new Messages_1.OscMessage('/pause'));
}
/** Toggle play */
play() {
this._sendOscMessage(new Messages_1.OscMessage('/play'));
}
/**
* Receive and handle an OSC message
* @param message The message to be handled
*/
receive(message) {
for (const handler of this._handlers) {
if (handler.handle(message)) {
return true;
}
}
return false;
}
/** Toggle recording */
record() {
this._sendOscMessage(new Messages_1.OscMessage('/record'));
}
/**
* Sets the loop end time
* @param time End time for the loop (in seconds)
*/
setLoopEnd(time) {
this._sendOscMessage(new Messages_1.FloatMessage('/loop/end/time', time));
}
/**
* Sets the loop start time
* @param time Start time for the loop (in seconds)
*/
setLoopStart(time) {
this._sendOscMessage(new Messages_1.FloatMessage('/loop/start/time', time));
}
/** Start fast fowarding. Will continue until stopped */
startFastForwarding() {
this._sendOscMessage(new Messages_1.BooleanMessage('/forward', true));
}
/** Start rewinding. Will continue until stopped */
startRewinding() {
this._sendOscMessage(new Messages_1.BooleanMessage('/rewind', true));
}
/** Stop playback or recording */
stop() {
this._sendOscMessage(new Messages_1.OscMessage('/stop'));
}
/** Stop fast forwarding */
stopFastForwarding() {
this._sendOscMessage(new Messages_1.BooleanMessage('/forward', false));
}
/** Stop rewinding */
stopRewinding() {
this._sendOscMessage(new Messages_1.BooleanMessage('/rewind', false));
}
/** Toggle repeat on or off */
toggleRepeat() {
this._sendOscMessage(new Messages_1.OscMessage('/repeat'));
}
};
__decorate([
Notify_1.notify('beat')
], Transport.prototype, "_beat", void 0);
__decorate([
Notify_1.notify('frames')
], Transport.prototype, "_frames", void 0);
__decorate([
Notify_1.notify('isFastForwarding')
], Transport.prototype, "_isFastForwarding", void 0);
__decorate([
Notify_1.notify('isPlaying')
], Transport.prototype, "_isPlaying", void 0);
__decorate([
Notify_1.notify('isRecording')
], Transport.prototype, "_isRecording", void 0);
__decorate([
Notify_1.notify('isRepeatEnabled')
], Transport.prototype, "_isRepeatEnabled", void 0);
__decorate([
Notify_1.notify('isRewinding')
], Transport.prototype, "_isRewinding", void 0);
__decorate([
Notify_1.notify('isStopped')
], Transport.prototype, "_isStopped", void 0);
__decorate([
Notify_1.notify('loopEnd')
], Transport.prototype, "_loopEnd", void 0);
__decorate([
Notify_1.notify('loopStart')
], Transport.prototype, "_loopStart", void 0);
__decorate([
Notify_1.notify('time')
], Transport.prototype, "_time", void 0);
Transport = __decorate([
Notify_1.notifyOnPropertyChanged
], Transport);
exports.Transport = Transport;
/** Represents a beat value in Reaper */
class Beat {
/**
* @param measure The measure of the beat
* @param beat The beat in the measure
* @param fraction The beat fraction (must be >= 0 and < 100)
*/
constructor(measure, beat, fraction) {
if (fraction < 0 || fraction >= 100) {
throw new Error(`Invalid fraction ${fraction}, must be >= 0 and < 100`);
}
this._beat = beat;
this._fraction = fraction;
this._measure = measure;
}
/** Indicates the beat portion of the beat (mm) */
get beat() {
return this._beat;
}
/** Indicates the fraction portion of the beat (bb) */
get fraction() {
return this._fraction;
}
/** Indicates the measure of the beat (xx) */
get measure() {
return this._measure;
}
/**
* Parses a string into a Beat
* @param value String value in the format mm.bb.xx
* @returns The parsed beat
* @throws Throws an error when the format is invalid
*/
static parse(value) {
const parts = value.split('.');
if (parts.length != 3) {
throw new Error('Must be in the format mm.bb.xx');
}
const numberParts = [];
parts.forEach(element => {
const intValue = parseInt(element);
numberParts.push(intValue);
});
return new Beat(numberParts[0], numberParts[1], numberParts[2]);
}
/**
* Converts the beat into its string representation in the format mm.bb.xx
*/
toString() {
return `${this.measure}.${this.beat}.${this.fraction}`;
}
}
exports.Beat = Beat;
//# sourceMappingURL=Transport.js.map