Merge branch 'main' into plugin-sennheiser-wireless

This commit is contained in:
2023-05-11 06:48:52 -05:00
60 changed files with 9056 additions and 1709 deletions
+6 -7
View File
@@ -1,16 +1,15 @@
const _ = require('lodash');
exports.config = {
defaultName: 'Art-Net',
connectionType: 'UDPsocket',
heartbeatInterval: 10000,
defaultPort: 6454,
mayChangePort: false,
heartbeatInterval: 5000,
heartbeatTimeout: 15000,
searchOptions: {
type: 'UDPsocket',
searchBuffer: Buffer.from([0x00]),
devicePort: 6454,
listenPort: 6454,
mayChangePort: false,
validateResponse(msg, info, devices) {
return msg.toString('utf8', 0, 7) === 'Art-Net';
},
@@ -43,7 +42,7 @@ exports.data = function data(_device, buf) {
universe.slots = buf.slice(18);
device.data.ip = device.addresses[0];
if (!_.includes(device.data.orderedUniverses, universeIndex)) {
if (!device.data.orderedUniverses.includes(universeIndex)) {
device.data.orderedUniverses.push(universeIndex);
device.data.orderedUniverses.sort();
universe.slotElems = [];
@@ -77,10 +76,10 @@ exports.update = function update(_device, _document, updateType, updateData) {
if ($elem && data.universe.slotElemsSet) {
for (let i = 0; i < 512; i++) {
data.universe.slotElems[i].innerText = data.universe.slots[i];
data.universe.slotElems[i].textContent = data.universe.slots[i];
}
document.getElementById(`universe-${data.universeIndex}-sequence`).innerText = data.universe.sequence;
document.getElementById(`universe-${data.universeIndex}-sequence`).textContent = data.universe.sequence;
} else {
device.draw();
device.update('elementCache');
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

+11
View File
@@ -0,0 +1,11 @@
<h3>ATEM Configuration</h3>
<ul>
<li>Set the IP Address, Subnet Mask, and Gateway of the ATEM using the <em>ATEM Setup utility</em></li>
<li>The ATEM must be connected to a computer using a USB cable to change its Network settings</li>
</ul>
<h3>ATEM Software Download</h3>
<button href="https://www.blackmagicdesign.com/support/family/atem-live-production-switchers">
Blackmagic Support
</button>
(Look for <em>ATEM Switchers Update</em>)
+240
View File
@@ -0,0 +1,240 @@
const { TransitionStyle, TransitionSelection } = require('atem-connection/dist/enums');
let timerFrameRate;
exports.config = {
defaultName: 'ATEM',
connectionType: 'atem',
defaultPort: 9910,
mayChangePort: false,
searchOptions: {
type: 'UDPScan',
searchBuffer: Buffer.from([
0x10, 0x14, 0x53, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]),
listenPort: 0,
devicePort: 9910,
validateResponse(msg, info) {
// This is a tad bit hacky but works from what I can see.
return Buffer.compare(msg.slice(0, 4), Buffer.from([16, 20, 83, 171])) === 0;
},
},
};
exports.ready = function ready(_device) {
console.log('atem ready');
const device = _device;
device.data = device.connection.state;
device.draw();
// TODO: this is a little hacky but it works? gotta be a way to hook into device.draw() to trigger update
setInterval(() => {
device.update('inputs', device.data);
device.update('fadeToBlack', device.data);
device.update(`transitionPosition`, device.data);
device.update('downstreamKeyers', device.data);
device.update('upstreamKeyers', device.data);
device.update('transitionProperties', device.data);
device.update('deviceInfo', device.data);
}, 1000);
};
exports.update = function update(device, _document, updateType, data) {
const document = _document;
if (updateType.includes('transitionPosition')) {
for (let i = 0; i < data.video.mixEffects.length; i++) {
const mixEffect = data.video.mixEffects[i];
const tbarId = `me-${i}-tbar-div`;
const tbarHandleId = `me-${i}-tbar-handle-div`;
if (document.getElementById(tbarId)) {
document.getElementById(tbarId).style.height = `${mixEffect.transitionPosition.handlePosition / 100}%`;
}
if (document.getElementById(tbarHandleId)) {
document.getElementById(tbarHandleId).style.bottom = `${mixEffect.transitionPosition.handlePosition / 100}%`;
}
document.getElementById(`me-${i}-transition-rate`).textContent = framesToTime(
mixEffect.transitionPosition.remainingFrames
);
if (mixEffect.transitionPosition.inTransition) {
document.getElementById(`me-${i}-auto`).classList.add('atem-red');
} else {
document.getElementById(`me-${i}-auto`).classList.remove('atem-red');
}
}
} else if (updateType.includes('downstreamKeyers')) {
for (let i = 0; i < data.video.downstreamKeyers.length; i++) {
const dsk = data.video.downstreamKeyers[i];
if (dsk.isAuto) {
document.getElementById(`dsk-${i}-auto`).classList.add('atem-red');
} else {
document.getElementById(`dsk-${i}-auto`).classList.remove('atem-red');
}
if (dsk.onAir) {
document.getElementById(`dsk-${i}-onair`).classList.add('atem-red');
} else {
document.getElementById(`dsk-${i}-onair`).classList.remove('atem-red');
}
if (dsk.properties.tie) {
document.getElementById(`dsk-${i}-tie`).classList.add('atem-yellow');
} else {
document.getElementById(`dsk-${i}-tie`).classList.remove('atem-yellow');
}
document.getElementById(`dsk-${i}-rate`).textContent = framesToTime(dsk.remainingFrames);
}
} else if (updateType.includes('fadeToBlack')) {
for (let i = 0; i < data.video.mixEffects.length; i++) {
const fadeToBlack = data.video.mixEffects[i].fadeToBlack;
const ftbRateId = `me-${i}-ftb-rate`;
const ftbId = `me-${i}-ftb`;
document.getElementById(ftbRateId).textContent = framesToTime(fadeToBlack.remainingFrames);
if (fadeToBlack.isFullyBlack) {
if (document.getElementById(ftbId).classList.contains('atem-red')) {
document.getElementById(ftbId).classList.remove('atem-red');
} else {
document.getElementById(ftbId).classList.add('atem-red');
}
} else if (fadeToBlack.inTransition) {
document.getElementById(ftbId).classList.add('atem-red');
} else {
document.getElementById(ftbId).classList.remove('atem-red');
}
}
} else if (
updateType.includes('programInput') ||
updateType.includes('previewInput') ||
updateType.includes('inputs')
) {
for (let i = 0; i < data.video.mixEffects.length; i++) {
const mixEffect = data.video.mixEffects[0];
Object.keys(device.data.inputs)
.map((key) => Number(key))
.forEach((inputId) => {
if (mixEffect.programInput === inputId) {
document.getElementById(`me-${i}-program-input-${inputId}`).classList.add('atem-red');
document.getElementById(`me-${i}-input-${inputId}`).classList.add('atem-red');
} else {
document.getElementById(`me-${i}-program-input-${inputId}`).classList.remove('atem-red');
document.getElementById(`me-${i}-input-${inputId}`).classList.remove('atem-red');
}
if (mixEffect.previewInput === inputId) {
document.getElementById(`me-${i}-preview-input-${inputId}`).classList.add('atem-green');
document.getElementById(`me-${i}-input-${inputId}`).classList.add('atem-green');
} else {
document.getElementById(`me-${i}-preview-input-${inputId}`).classList.remove('atem-green');
document.getElementById(`me-${i}-input-${inputId}`).classList.remove('atem-green');
}
});
}
} else if (updateType.includes('transitionProperties')) {
for (let i = 0; i < data.video.mixEffects.length; i++) {
const transitionProperties = data.video.mixEffects[i].transitionProperties;
Object.keys(TransitionStyle)
.filter((key) => !isNaN(Number(key)))
.map((key) => Number(key))
.forEach((style) => {
if (style === transitionProperties.style) {
document.getElementById(`me-${i}-transition-style-${style}`).classList.add('atem-yellow');
} else {
document.getElementById(`me-${i}-transition-style-${style}`).classList.remove('atem-yellow');
}
if (style === transitionProperties.nextStyle) {
document.getElementById(`me-${i}-transition-style-${style}`).classList.add('atem-yellow');
} else {
document.getElementById(`me-${i}-transition-style-${style}`).classList.remove('atem-yellow');
}
});
Object.keys(TransitionSelection)
.filter((key) => !isNaN(Number(key)))
.map((key) => Number(key))
.forEach((selection) => {
if (transitionProperties.selection.includes(selection)) {
document.getElementById(`me-${i}-transition-selection-${selection}`).classList.add('atem-yellow');
} else {
document.getElementById(`me-${i}-transition-selection-${selection}`).classList.remove('atem-yellow');
}
if (transitionProperties.nextSelection.includes(selection)) {
document.getElementById(`me-${i}-transition-selection-${selection}`).classList.add('atem-yellow');
} else {
document.getElementById(`me-${i}-transition-selection-${selection}`).classList.remove('atem-yellow');
}
});
}
} else if (updateType.includes('upstreamKeyers')) {
for (let i = 0; i < data.video.mixEffects.length; i++) {
const upstreamKeyers = data.video.mixEffects[i].upstreamKeyers;
upstreamKeyers.forEach((upstreamKeyer) => {
if (upstreamKeyer.onAir) {
document.getElementById(`me-${i}-key-${upstreamKeyer.upstreamKeyerId + 1}-onair`).classList.add('atem-red');
} else {
document
.getElementById(`me-${i}-key-${upstreamKeyer.upstreamKeyerId + 1}-onair`)
.classList.remove('atem-red');
}
});
}
} else if (updateType.includes('transitionPreview')) {
for (let i = 0; i < data.video.mixEffects.length; i++) {
const mixEffect = data.video.mixEffects[i];
if (mixEffect.transitionPreview) {
document.getElementById(`me-${i}-transition-preview`).classList.add('atem-red');
} else {
document.getElementById(`me-${i}-transition-preview`).classList.remove('atem-red');
}
}
} else if (updateType.includes('deviceInfo')) {
// videoModes pulled from https://github.com/nrkno/sofie-atem-connection/blob/master/src/enums/index.ts#L238
if ([27, 26, 23, 25, 19, 13, 11, 7, 5].includes(data.settings.videoMode)) {
timerFrameRate = 30;
} else if ([24, 22, 18, 16, 12, 10, 6, 4].includes(data.settings.videoMode)) {
timerFrameRate = 25;
} else if ([21, 20, 15, 14, 9, 8].includes(data.settings.videoMode)) {
timerFrameRate = 24;
} else {
timerFrameRate = 30;
}
if (!device.displayName) {
this.deviceInfoUpdate(device, 'displayName', data.info.productIdentifier);
}
} else {
console.log('unhandled update');
console.log(updateType);
console.log(device.data);
}
};
exports.data = function data(_device, msg) {
const device = _device;
this.deviceInfoUpdate(device, 'status', 'ok');
device.data = msg.state;
msg.pathToChange.forEach((path) => {
device.update(path, device.data);
});
};
function framesToTime(total) {
if (timerFrameRate) {
const seconds = Math.floor(total / timerFrameRate);
const frames = total - seconds * timerFrameRate;
let framesString = String(frames);
if (frames < 10) {
framesString = `0${framesString}`;
} else if (framesString === '0') {
framesString = '00';
}
return `${seconds}:${framesString}`;
}
return '0:00';
}
+203
View File
@@ -0,0 +1,203 @@
body {
background-color: #282828;
}
h1 {
font-family: 'Open Sans', sans-serif;
}
h3 {
color: #6d6d6d !important;
margin: 30px 0px 10px 9px;
font-size: 0.9em;
font-weight: 400;
font-family: 'Open Sans', sans-serif;
}
.me-label {
margin-top: 0;
margin-bottom: 2px;
}
.atem-input {
width: 52px;
height: 52px;
background-image: url('img/button_white.png');
}
.source-wrapper {
display: flex;
flex-wrap: wrap;
background-color: #1f1f1f;
border: #1a1a1a 2px solid;
border-radius: 8px;
max-width: 416px;
padding: 12px;
}
.atem-red {
background-image: url('img/button_red.png');
box-shadow: 0px 0px 10px 1px #ff0000;
z-index: 10000;
}
.atem-green {
background-image: url('img/button_green.png');
box-shadow: 0px 0px 10px 1px #06c300;
z-index: 10000;
}
.atem-yellow {
background-image: url('img/button_yellow.png');
box-shadow: 0px 0px 10px 1px #c7ca00;
z-index: 10000;
}
.atem-disabled {
background-image: url('img/button_off.png');
color: #3a3a3a;
}
.atem-gray {
color: #575757;
}
.source-label {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 11px;
font-weight: bold;
}
.transition-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.transition-settings {
display: flex;
flex-direction: column;
justify-content: space-between;
margin-right: 30px;
}
.tbar-container {
display: flex;
flex-direction: column-reverse;
background-color: #1f1f1f;
border: 2px solid;
border-color: #1a1a1a;
border-radius: 6px;
width: 88px;
height: 429px;
position: relative;
margin-top: 58px;
margin-right: 60px;
}
.tbar-bg {
position: absolute;
background-image: url('img/tbar_bg.png');
width: 88px;
height: 429px;
}
.tbar-handle {
position: absolute;
background-image: url('img/tbar_handle.png');
width: 126px;
height: 50px;
left: -3px;
}
.tbar-div {
width: 100%;
background-color: #7aff58;
overflow: hidden;
}
.dsk {
width: 100%;
}
.dsk .source-wrapper {
display: flex;
flex-direction: column;
align-content: center;
width: 100%;
padding: 5px;
}
.dsk h3 {
text-align: center;
}
.fade-to-black-container {
display: flex;
flex-direction: column;
align-items: flex-end;
margin-right: 30px;
}
.fade-to-black .source-wrapper {
display: flex;
width: 100%;
padding: 5px;
}
.fade-to-black h3 {
text-align: center;
}
.rate-heading {
text-align: center;
font-size: small;
margin-bottom: 4px;
}
.dsk-rate,
.ftb-rate,
.transition-rate {
display: flex;
justify-content: center;
flex-direction: column;
}
.dsk-rate-label,
.ftb-rate-label,
.transition-rate-label {
color: rgb(235, 110, 0);
background-color: black;
border-radius: 3px;
padding: 6px 2px;
text-align: center;
}
.clear {
background: transparent;
border-color: transparent;
background-color: transparent;
}
.hide {
display: none;
}
.no-wrap {
flex-wrap: nowrap;
}
.show-small {
display: none;
}
@media screen and (min-width: 0px) and (max-width: 480px) {
.hide-small {
display: none;
}
.show-small {
display: block;
}
}
.float-left {
float: left;
}
+189
View File
@@ -0,0 +1,189 @@
<header>
<h1><%= listName %></h1>
</header>
<% if (data.video.mixEffects) { %>
<% Object.entries(data.video.mixEffects).forEach(([meIndex, state])=>{ %>
<div class="mixeffect_wrapper" id="me-<%= meIndex %>">
<h1 class="atem-gray me-label">ME<%= Number(meIndex) + 1 %></h1>
<div class="inputs-container show-small">
<h3 class="atem-gray">Preview/Program</h3>
<div class="source-wrapper">
<% Object.values(data.inputs).sort((a,b)=>a.internalPortType<b.internalPortType).forEach((input)=>{ %>
<% if ([0,1,2,3,4].includes(input.internalPortType)) { %>
<div id="me-<%= meIndex %>-input-<%= input.inputId %>" class="atem-input">
<div class="source-label"><%= input.shortName %></div>
</div>
<% } else {%>
<div id="me-<%= meIndex %>-input-<%= input.inputId %>" class="atem-input atem-disabled">
<div class="source-label"><%= input.shortName %></div>
</div>
<% }%>
<% }) %>
</div>
</div>
<div class="float-left" style="margin-right: 30px;">
<div class="program-container hide-small">
<h3 class="atem-gray">Program</h3>
<div class="source-wrapper">
<% Object.values(data.inputs).sort((a,b)=>a.internalPortType<b.internalPortType).forEach((input)=>{ %>
<% if ([0,1,2,3,4].includes(input.internalPortType)) { %>
<div id="me-<%= meIndex %>-program-input-<%= input.inputId %>" class="atem-input">
<div class="source-label"><%= input.shortName %></div>
</div>
<% } else {%>
<div id="me-<%= meIndex %>-program-input-<%= input.inputId %>" class="atem-input atem-disabled">
<div class="source-label"><%= input.shortName %></div>
</div>
<% }%>
<% }) %>
</div>
</div>
<div class="preview hide-small">
<h3 class="atem-gray">Preview</h3>
<div class="source-wrapper">
<% Object.values(data.inputs).forEach((input)=>{ %>
<% if ([0,1,2,3,4].includes(input.internalPortType)) { %>
<div id="me-<%= meIndex %>-preview-input-<%= input.inputId %>" class="atem-input">
<div class="source-label"><%= input.shortName %></div>
</div>
<% } else {%>
<div id="me-<%= meIndex %>-preview-input-<%= input.inputId %>" class="atem-input atem-disabled">
<div class="source-label"><%= input.shortName %></div>
</div>
<% }%>
<% }) %>
</div>
</div>
</div>
<div class="transition-container float-left">
<div class="transition-settings">
<div class="next-transition">
<h3>Next Transition</h3>
<div class="source-wrapper" style="width: 260px;">
<div class="atem-input clear"></div>
<div id="me-<%= meIndex %>-key-1-onair" class="atem-input">
<div class="source-label" style="flex-direction: column;">
<div>ON</div> <div>AIR</div>
</div>
</div>
<div id="me-<%= meIndex %>-key-2-onair" class="atem-input <% if(data.video.downstreamKeyers.length<2){ %> atem-disabled <% } %>">
<div class="source-label" style="flex-direction: column;">
<div>ON</div> <div>AIR</div>
</div>
</div>
<div id="me-<%= meIndex %>-key-3-onair" class="atem-input <% if(data.video.downstreamKeyers.length<3){ %> atem-disabled <% } %>">
<div class="source-label" style="flex-direction: column;">
<div>ON</div> <div>AIR</div>
</div>
</div>
<div id="me-<%= meIndex %>-key-4-onair" class="atem-input <% if(data.video.downstreamKeyers.length<4){ %> atem-disabled <% } %>">
<div class="source-label" style="flex-direction: column;">
<div>ON</div> <div>AIR</div>
</div>
</div>
<div id="me-<%= meIndex %>-transition-selection-1" class="atem-input">
<div class="source-label">BKGD</div>
</div>
<div id="me-<%= meIndex %>-transition-selection-2" class="atem-input">
<div class="source-label">KEY 1</div>
</div>
<div id="me-<%= meIndex %>-transition-selection-4" class="atem-input <% if(data.video.downstreamKeyers.length<2){ %> atem-disabled <% } %>">
<div class="source-label">KEY 2</div>
</div>
<div id="me-<%= meIndex %>-transition-selection-8" class="atem-input <% if(data.video.downstreamKeyers.length<3){ %> atem-disabled <% } %>">
<div class="source-label">KEY 3</div>
</div>
<div id="me-<%= meIndex %>-transition-selection-16" class="atem-input <% if(data.video.downstreamKeyers.length<4){ %> atem-disabled <% } %>">
<div class="source-label">KEY 4</div>
</div>
</div>
</div>
<div class="transition-style">
<h3>Transition Style</h3>
<div class="source-wrapper" style="width: 260px;">
<div id="me-<%= meIndex %>-transition-style-0" class="atem-input">
<div class="source-label">MIX</div>
</div>
<div id="me-<%= meIndex %>-transition-style-1" class="atem-input">
<div class="source-label">DIP</div>
</div>
<div id="me-<%= meIndex %>-transition-style-2" class="atem-input">
<div class="source-label">WIPE</div>
</div>
<div id="me-<%= meIndex %>-transition-style-4" class="atem-input">
<div class="source-label">STING</div>
</div>
<div id="me-<%= meIndex %>-transition-style-3" class="atem-input">
<div class="source-label">DVE</div>
</div>
<div id="me-<%= meIndex %>-transition-preview" class="atem-input">
<div class="source-label" style="flex-direction: column;">
<div>PREV</div> <div>TRANS</div>
</div>
</div>
<div class="atem-input clear"></div>
<div id="me-<%= meIndex %>-cut" class="atem-input">
<div class="source-label">CUT</div>
</div>
<div id="me-<%= meIndex %>-auto" class="atem-input">
<div class="source-label">AUTO</div>
</div>
<div class="transition-rate atem-input clear">
<div class="atem-gray rate-heading">Rate</div>
<div id="me-<%= meIndex %>-transition-rate" class="transition-rate-label">&nbsp;&nbsp;</div>
</div>
</div>
</div>
</div>
<div class="tbar-container">
<div class="tbar-bg"></div>
<div id="me-<%= meIndex %>-tbar-handle-div" class="tbar-handle"></div>
<div id="me-<%= meIndex %>-tbar-div" class="tbar-div" ></div>
</div>
<div class="fade-to-black-container">
<% if (data.video.downstreamKeyers) { %>
<% Object.entries(data.video.downstreamKeyers).forEach(([dskIndex, state])=>{ %>
<div class="dsk">
<h3 class="atem-gray">DSK<%= Number(dskIndex) + 1 %></h3>
<div class="source-wrapper">
<div id="dsk-<%= dskIndex %>-tie" class="atem-input">
<div class="source-label">TIE</div>
</div>
<div class="dsk-rate atem-input clear">
<div class="atem-gray rate-heading">Rate</div>
<div id="dsk-<%= dskIndex %>-rate" class="dsk-rate-label">&nbsp;&nbsp;</div>
</div>
<div id="dsk-<%= dskIndex %>-onair" class="atem-input">
<div class="source-label" style="flex-direction: column;">
<div>ON</div> <div>AIR</div>
</div>
</div>
<div id="dsk-<%= dskIndex %>-auto" class="atem-input">
<div class="source-label">AUTO</div>
</div>
</div>
</div>
<% }) %>
<% } %>
<div class="fade-to-black">
<h3 class="atem-gray">Fade to Black</h3>
<div class="source-wrapper">
<div class="ftb-rate atem-input clear">
<div class="atem-gray rate-heading">Rate</div>
<div id="me-<%= meIndex %>-ftb-rate" class="ftb-rate-label">&nbsp;&nbsp;</div>
</div>
<div id="me-<%= meIndex %>-ftb" class="atem-input">
<div class="source-label">FTB</div>
</div>
</div>
</div>
</div>
</div>
</div>
<% }) %>
<% } %>
+3 -5
View File
@@ -2,17 +2,15 @@
<h4>Shell/ECU &rarr; Network</h4>
<ul>
<li>
Enable <em>&#10004; TCP OSC</em>. The TCP format dropdown does not matter.
</li>
<li>Enable <em>&#10004; TCP OSC</em>. The TCP format should be "TCP Format for OSC 1.1 (SLIP)".</li>
<li>Enable <em>Third Party OSC</em></li>
</ul>
If the "Third Party OSC" option is not available, Eos must be updated to 3.1 or
newer.
If the "Third Party OSC" option is not available, Eos must be updated to 3.1 or newer.
<h4>System Settings &rarr; Show Control &rarr; OSC</h4>
<ul>
<li>Enable <em>&#10004; OSC RX</em></li>
<li>Enable <em>&#10004; OSC TX</em></li>
<li>All other fields may be left to defaults or blank.</li>
<li>If a custom port is desired specify it in under OSC TCP Server Ports.</li>
</ul>
+14 -8
View File
@@ -6,12 +6,14 @@ const Cue = require('./cue');
exports.config = {
defaultName: 'ETC Eos',
connectionType: 'osc',
defaultPort: 3032,
mayChangePort: false,
defaultPort: 3037,
mayChangePort: true,
heartbeatInterval: 5000,
heartbeatTimeout: 6000,
searchOptions: {
type: 'TCPport',
searchBuffer: Buffer.from('\xc0/eos/ping\x00\x00\x2c\x00\x00\x00\xc0', 'ascii'),
testPort: 3032,
testPort: 3037,
validateResponse(msg, info) {
return msg.toString().includes('/eos/out');
},
@@ -21,6 +23,9 @@ exports.config = {
exports.ready = function ready(_device) {
const device = _device;
device.data.EOS = new EOS();
device.templates = {
cue: _.template(fs.readFileSync(path.join(__dirname, `cue.ejs`))),
};
device.send('/eos/get/cuelist/count');
device.send('/eos/get/version');
device.send('/eos/subscribe', [{ type: 'i', value: 1 }]);
@@ -28,6 +33,7 @@ exports.ready = function ready(_device) {
exports.data = function data(_device, osc) {
const device = _device;
this.deviceInfoUpdate(device, 'status', 'ok');
const addressParts = osc.address.split('/');
addressParts.shift();
@@ -48,7 +54,10 @@ exports.data = function data(_device, osc) {
device.send(`/eos/get/cue/${addressParts[4]}/index/${i}`);
}
} else if (match(addressParts, ['eos', 'out', 'get', 'cue', '*', '*', '*', 'list', '*', '*'])) {
this.deviceInfoUpdate(device, 'status', 'ok');
if (device.data.EOS.cueLists[addressParts[4]] === undefined) {
device.data.EOS.cueLists[addressParts[4]] = {};
device.send(`/eos/get/cue/${addressParts[4]}/count`);
}
if (device.data.EOS.cueLists[addressParts[4]][addressParts[5]] === undefined) {
device.data.EOS.cueLists[addressParts[4]][addressParts[5]] = {};
}
@@ -85,14 +94,11 @@ exports.data = function data(_device, osc) {
}
};
const cueTemplate = _.template(fs.readFileSync(path.join(__dirname, `cue.ejs`)));
exports.update = function update(device, doc, updateType, data) {
if (updateType === 'cueData') {
const $elem = doc.getElementById(data.uid);
if ($elem) {
$elem.outerHTML = cueTemplate({
$elem.outerHTML = device.templates.cue({
q: data.cue,
cueNumber: data.cueNumber,
isActive: false,
+11
View File
@@ -3,6 +3,7 @@ table {
border: #404040 1px solid;
border-radius: 4px;
color: #a59baa;
width: 100%;
}
th {
font-size: 14px;
@@ -62,6 +63,16 @@ tr.active-cue .time {
border-color: #c78b07;
}
.list_name {
color: lightgray;
margin-left: 5px;
margin-bottom: 2px;
}
.list_container {
margin-bottom: 10px;
}
@media screen and (min-width: 0px) and (max-width: 750px) {
.hide-medium {
display: none;
+31 -36
View File
@@ -3,45 +3,40 @@
<h2>Eos <%= data.version %></h2>
</header>
<%
const fs = require('fs');
const path = require('path');
let cueTemplate = _.template(fs.readFileSync(path.join(__dirname, `/plugins/eos/cue.ejs`)));
%>
<% for(var i in data.EOS.cueLists){ %>
<strong>List <%= i %></strong>
<table cellspacing="0">
<tr>
<th width="70px">Cue</th>
<th width="30px">Int Up</th>
<th width="30px">Int Down</th>
<th width="40px" class="hide-medium">Focus</th>
<th width="40px" class="hide-medium">Color</th>
<th width="40px" class="hide-medium">Beam</th>
<th width="40px" class="hide-small">Dur</th>
<th width="20px" class="hide-small">M</th>
<th width="20px" class="hide-small">B</th>
<th width="20px" class="hide-small">A</th>
<th width="70px" class="hide-small">Fw/Hg</th>
<th width="150px">Label</th>
<th width="10px" class="hide-medium">Ext Links</th>
</tr>
<div class="list_container">
<div class="list_name">List <%= i %></div>
<table cellspacing="0">
<tr>
<th width="70px">Cue</th>
<th width="30px">Int Up</th>
<th width="30px">Int Down</th>
<th width="40px" class="hide-medium">Focus</th>
<th width="40px" class="hide-medium">Color</th>
<th width="40px" class="hide-medium">Beam</th>
<th width="40px" class="hide-small">Dur</th>
<th width="20px" class="hide-small">M</th>
<th width="20px" class="hide-small">B</th>
<th width="20px" class="hide-small">A</th>
<th width="70px" class="hide-small">Fw/Hg</th>
<th width="150px">Label</th>
<th width="10px" class="hide-medium">Ext Links</th>
</tr>
<% var cues = Object.keys(data.EOS.cueLists[i]).sort(function(a, b){return Number(a)-Number(b)}) %>
<% var cues = Object.keys(data.EOS.cueLists[i]).sort(function(a, b){return Number(a)-Number(b)}) %>
<% for(var j=0; j<cues.length; j++){ %>
<% var q = data.EOS.cueLists[i][cues[j]] %>
<%= cueTemplate({
q: q,
cues: cues,
cueNumber: cues[j],
isActive: (cues[j]==data.EOS.activeCue+"")
}) %>
<% for(var j=0; j<cues.length; j++){ %>
<% var q = data.EOS.cueLists[i][cues[j]] %>
<%= templates.cue({
q: q,
cues: cues,
cueNumber: cues[j],
isActive: (cues[j]==data.EOS.activeCue+"")
}) %>
<% } %>
</table>
<% } %>
</table>
</div>
<% } %>
+2 -2
View File
@@ -3,10 +3,10 @@ const md5 = require('md5');
exports.config = {
defaultName: 'PJLink Projector',
connectionType: 'TCPsocket',
heartbeatInterval: 5000,
heartbeatTimeout: 15000,
defaultPort: 4352,
mayChangePort: false,
heartbeatInterval: 5000,
heartbeatTimeout: 15000,
searchOptions: {
type: 'UDPsocket',
searchBuffer: Buffer.from([0x25, 0x32, 0x53, 0x52, 0x43, 0x48, 0x0d]),
+2 -4
View File
@@ -40,13 +40,11 @@
function displayCueRow(q){
let html = "";
html+= rowTemplate({cue: allCues[q.uniqueID], allCues: allCues, workspace: workspace});
let html = rowTemplate({cue: allCues[q.uniqueID], allCues: allCues, workspace: workspace});
if(q.cues){
q.cues.forEach(q =>{
html+=displayCueRow(q);
html += displayCueRow(q);
})
}
return html;
+64 -37
View File
@@ -5,9 +5,10 @@ const path = require('path');
exports.config = {
defaultName: 'QLab',
connectionType: 'osc',
heartbeatInterval: 50,
heartbeatTimeout: 2000,
defaultPort: 53000,
mayChangePort: true,
heartbeatInterval: 100,
heartbeatTimeout: 5000,
searchOptions: {
type: 'Bonjour',
bonjourName: 'qlab',
@@ -35,11 +36,14 @@ const valuesForKeysString =
'"mode","parent","cartRows","cartColumns","cartPosition","displayName","preWaitElapsed",' +
'"actionElapsed","postWaitElapsed","isPaused"]';
const cueTemplate = _.template(fs.readFileSync(path.join(__dirname, `cue.ejs`)));
const tileTemplate = _.template(fs.readFileSync(path.join(__dirname, `tile.ejs`)));
const cartTemplate = _.template(fs.readFileSync(path.join(__dirname, `cart.ejs`)));
exports.ready = function ready(device) {
exports.ready = function ready(_device) {
const device = _device;
device.templates = {
cue: _.template(fs.readFileSync(path.join(__dirname, `cue.ejs`))),
tile: _.template(fs.readFileSync(path.join(__dirname, `tile.ejs`))),
cart: _.template(fs.readFileSync(path.join(__dirname, `cart.ejs`))),
cuelist: _.template(fs.readFileSync(path.join(__dirname, `cuelist.ejs`))),
};
device.send(`/version`);
device.send('/workspaces');
};
@@ -213,45 +217,55 @@ exports.data = function data(_device, oscData) {
if (workspace) {
const cue = workspace.cues[oscArgs.address.substring(55, 91)];
cue.preWaitElapsed = oscArgs.data;
lastElapsedUpdate = Date.now();
if (cue) {
cue.preWaitElapsed = oscArgs.data;
lastElapsedUpdate = Date.now();
device.update('updateCueData', {
cue,
allCues: workspace.cues,
workspace,
});
device.update('updateCueData', {
cue,
allCues: workspace.cues,
workspace,
});
}
}
}
} else if (match(oscAddressParts, ['reply', 'cue_id', '*', 'actionElapsed'])) {
const oscArgs = JSON.parse(oscData.args[0]);
if (oscArgs.status !== 'error') {
const workspace = device.data.workspaces[oscArgs.workspace_id];
const cue = workspace.cues[oscArgs.address.substring(55, 91)];
if (workspace) {
const cue = workspace.cues[oscArgs.address.substring(55, 91)];
cue.actionElapsed = oscArgs.data;
lastElapsedUpdate = Date.now();
if (cue) {
cue.actionElapsed = oscArgs.data;
lastElapsedUpdate = Date.now();
device.update('updateCueData', {
cue,
allCues: workspace.cues,
workspace,
});
device.update('updateCueData', {
cue,
allCues: workspace.cues,
workspace,
});
}
}
}
} else if (match(oscAddressParts, ['reply', 'cue_id', '*', 'postWaitElapsed'])) {
const oscArgs = JSON.parse(oscData.args[0]);
if (oscArgs.status !== 'error') {
const workspace = device.data.workspaces[oscArgs.workspace_id];
const cue = workspace.cues[oscArgs.address.substring(55, 91)];
if (workspace) {
const cue = workspace.cues[oscArgs.address.substring(55, 91)];
cue.postWaitElapsed = oscArgs.data;
lastElapsedUpdate = Date.now();
if (cue) {
cue.postWaitElapsed = oscArgs.data;
lastElapsedUpdate = Date.now();
device.update('updateCueData', {
cue,
allCues: workspace.cues,
workspace,
});
device.update('updateCueData', {
cue,
allCues: workspace.cues,
workspace,
});
}
}
}
} else if (match(oscAddressParts, ['update', 'workspace', '*', 'cue_id', '*'])) {
const workspace = device.data.workspaces[oscAddressParts[2]];
@@ -276,7 +290,6 @@ exports.data = function data(_device, oscData) {
} else if (match(oscAddressParts, ['update', 'workspace', '*', 'dashboard'])) {
// this workspace might be new, let's check
if (device.data.workspaces[oscAddressParts[2]] === undefined) {
console.log('new workspace!');
device.send('/workspaces');
}
} else if (match(oscAddressParts, ['update', 'workspace', '*', 'cueList', '*', 'playbackPosition'])) {
@@ -292,7 +305,12 @@ exports.data = function data(_device, oscData) {
}
} else if (match(oscAddressParts, ['update', 'workspace', '*', 'disconnect'])) {
delete device.data.workspaces[oscAddressParts[2]];
if (Object.keys(device.data.workspaces).length === 0) {
device.data.permission = 'no workspaces';
}
device.draw();
} else if (match(oscAddressParts, ['reply', 'thump'])) {
// intermittent connection check even if nothing's happening
} else {
// console.log(address)
}
@@ -306,8 +324,8 @@ exports.update = function update(device, doc, updateType, data) {
if (data.cue.type === 'Cue List') {
$elem.outerHTML = `<h3>${data.workspace.displayName} &mdash; ${data.cue.name}</h3>`;
} else if (data.cue.type === 'Cart') {
$elem.outerHTML = cartTemplate({
tileTemplate,
$elem.outerHTML = device.templates.cart({
tileTemplate: device.templates.tile,
cueList: data.cue,
allCues: data.workspace.cues,
});
@@ -315,12 +333,12 @@ exports.update = function update(device, doc, updateType, data) {
// checking that the parent cue is a cart cue
const parentCue = data.workspace.cues[data.cue.parent];
if (parentCue && parentCue.type === 'Cart') {
$elem.outerHTML = tileTemplate(data);
$elem.outerHTML = device.templates.tile(data);
} else {
$elem.outerHTML = cueTemplate(data);
$elem.outerHTML = device.templates.cue(data);
}
} else {
$elem.outerHTML = cueTemplate(data);
$elem.outerHTML = device.templates.cue(data);
}
}
} else if (updateType === 'updatePlaybackPosition') {
@@ -381,7 +399,16 @@ exports.heartbeat = function heartbeat(device) {
interval = 1;
}
if (heartbeatCount % interval === 0) {
if (heartbeatCount % 20 === 0 && device.data.workspaces && Object.keys(device.data.workspaces).length === 0) {
device.send(`/version`);
device.send('/workspaces');
}
if (heartbeatCount % 16 === 0) {
device.send(`/thump`);
}
if (heartbeatCount % interval === 0 && device.data.workspaces && Object.keys(device.data.workspaces).length > 0) {
device.send(`/cue_id/active/preWaitElapsed`);
device.send(`/cue_id/active/actionElapsed`);
device.send(`/cue_id/active/postWaitElapsed`);
+2 -12
View File
@@ -3,16 +3,6 @@
<h2>QLab <%= data.version || "" %></h2>
</header>
<%
const fs = require('fs');
let _ = require('lodash');
const path = require('path');
let cueTemplate = _.template(fs.readFileSync(path.join(__dirname, `/plugins/qlab/cue.ejs`)));
let tileTemplate = _.template(fs.readFileSync(path.join(__dirname, `/plugins/qlab/tile.ejs`)));
let cartTemplate = _.template(fs.readFileSync(path.join(__dirname, `/plugins/qlab/cart.ejs`)));
let listTemplate = _.template(fs.readFileSync(path.join(__dirname, `/plugins/qlab/cuelist.ejs`)));
%>
<% if(data.permission=="ok"){
for(const workspace_id in data.workspaces){
@@ -21,9 +11,9 @@
const ql = workspace.cueLists[cueList_id]; %>
<% if(ql.type=="Cue List"){ %>
<%= listTemplate({cueList: ql, allCues: workspace.cues, rowTemplate: cueTemplate, workspace: workspace}) %>
<%= templates.cuelist({cueList: ql, allCues: workspace.cues, rowTemplate: templates.cue, workspace: workspace}) %>
<% }else if(ql.type=="Cart"){ %>
<%= cartTemplate({cueList: ql, allCues: workspace.cues, tileTemplate: tileTemplate}) %>
<%= templates.cart({cueList: ql, allCues: workspace.cues, tileTemplate: templates.tile}) %>
<% } %>
<% }}}else if(data.permission=="no workspaces"){ %>
+41 -28
View File
@@ -3,9 +3,10 @@ const _ = require('lodash');
exports.config = {
defaultName: 'sACN',
connectionType: 'multicast',
heartbeatInterval: 5000,
defaultPort: 5568,
mayChangePort: false,
heartbeatInterval: 5000,
heartbeatTimeout: 15000,
searchOptions: {
type: 'multicast',
address: getMulticastGroup(1),
@@ -19,12 +20,12 @@ exports.config = {
exports.ready = function ready(device) {
const d = device;
d.data.universes = {};
d.data.priorities = {};
d.data.source = 'Unknown Source';
d.data.orderedUniverses = [];
const networkInterfaces = d.getNetworkInterfaces();
// device.draw();
for (let i = 1; i <= 16; i++) {
for (let j = 0; j < Object.keys(networkInterfaces).length; j++) {
const networkInterfaceID = Object.keys(networkInterfaces)[j];
@@ -39,11 +40,16 @@ exports.data = function data(_device, buf) {
const device = _device;
let universe = device.data.universes[universeIndex];
let priorities = device.data.priorities[universeIndex];
if (!universe) {
device.data.universes[universeIndex] = {};
universe = device.data.universes[universeIndex];
}
if (!priorities) {
device.data.priorities[universeIndex] = new Array(512).fill(0);
priorities = device.data.priorities[universeIndex];
}
universe.sequence = buf.readUInt8(111);
universe.priority = buf.readUInt8(108);
@@ -64,15 +70,20 @@ exports.data = function data(_device, buf) {
universe.slotElems = [];
universe.slotElemsSet = false;
device.draw();
device.update('elementCache');
if (universe.priority > 0) {
device.draw();
device.update('elementCache');
}
}
if (universe.priority > 0) {
device.update('universeData', {
universeIndex,
universe,
startCode: universe.startCode,
});
} else {
device.data.priorities[universeIndex] = buf.slice(126);
}
device.update('universeData', {
universeIndex,
universe,
startCode: universe.startCode,
});
};
exports.heartbeat = function heartbeat(device) {};
@@ -91,21 +102,18 @@ exports.update = function update(_device, doc, updateType, updateData) {
const $elem = doc.getElementById(`universe-${data.universeIndex}`);
if ($elem && data.universe.slotElemsSet) {
if (data.universe.priority > 0) {
for (let i = 0; i < 512; i++) {
data.universe.slotElems[i].innerText = data.universe.slots[i];
}
const $code = doc.getElementById(`universe-${data.universeIndex}-code`);
if (data.startCode === 0xdd) {
$code.innerText = 'Net3';
} else if (data.startCode === 0x17) {
$code.innerText = 'Text';
} else if (data.startCode === 0xcf) {
$code.innerText = 'SIP';
} else if (data.startCode === 0xcc) {
$code.innerText = 'RDM';
}
for (let i = 0; i < 512; i++) {
data.universe.slotElems[i].textContent = data.universe.slots[i];
}
const $code = doc.getElementById(`universe-${data.universeIndex}-code`);
if (data.startCode === 0xdd) {
$code.textContent = 'Net3';
} else if (data.startCode === 0x17) {
$code.textContent = 'Text';
} else if (data.startCode === 0xcf) {
$code.textContent = 'SIP';
} else if (data.startCode === 0xcc) {
$code.textContent = 'RDM';
}
} else {
device.draw();
@@ -113,10 +121,15 @@ exports.update = function update(_device, doc, updateType, updateData) {
}
} else if (updateType === 'elementCache') {
device.data.orderedUniverses.forEach((universeIndex) => {
for (let i = 0; i < 512; i++) {
device.data.universes[universeIndex].slotElems[i] = doc.getElementById(`${universeIndex}-${i}`);
const universe = device.data.universes[universeIndex];
if (doc.getElementById(`${universeIndex}-0`)) {
for (let i = 0; i < 512; i++) {
universe.slotElems[i] = doc.getElementById(`${universeIndex}-${i}`);
universe.slotElems[i].title = `${universeIndex}/${i} ${device.data.priorities[universeIndex][i]}`;
}
universe.slotElemsSet = true;
}
device.data.universes[universeIndex].slotElemsSet = true;
});
}
};
+2 -2
View File
@@ -3,10 +3,10 @@ const Channel = require('./channel');
exports.config = {
defaultName: 'Shure Wireless',
connectionType: 'TCPsocket',
heartbeatInterval: 5000,
heartbeatTimeout: 10000,
defaultPort: 2202,
mayChangePort: false,
heartbeatInterval: 5000,
heartbeatTimeout: 10000,
searchOptions: {
type: 'TCPport',
searchBuffer: Buffer.from('< GET DEVICE_ID >', 'ascii'),
+2 -1
View File
@@ -1,9 +1,10 @@
exports.config = {
defaultName: 'Dataton Watchout',
connectionType: 'TCPsocket',
heartbeatInterval: 250,
defaultPort: 3040,
mayChangePort: false,
heartbeatInterval: 250,
heartbeatTimeout: 5000,
searchOptions: {
type: 'TCPport',
searchBuffer: Buffer.from('authenticate 1\n', 'ascii'),
+44 -46
View File
@@ -1,24 +1,25 @@
exports.config = {
defaultName: 'X32 Mixer',
connectionType: 'osc-udp',
heartbeatInterval: 9000,
defaultPort: 10023,
mayChangePort: false,
heartbeatInterval: 9000,
heartbeatTimeout: 11000,
searchOptions: {
type: 'UDPsocket',
searchBuffer: Buffer.from([0x2f, 0x78, 0x69, 0x6e, 0x66, 0x6f]),
devicePort: 10023,
listenPort: 0,
validateResponse(msg, info) {
return msg.toString().includes('/xinfo') === 0;
return msg.toString().includes('/xinfo');
},
},
};
exports.ready = function ready(device) {
const d = device;
d.data.X32 = new Console();
d.send('/xinfo');
exports.ready = function ready(_device) {
const device = _device;
device.data = new Console();
device.send('/xinfo');
device.send('/batchsubscribe', [
{ type: 's', value: '/ch/meters' },
@@ -43,25 +44,25 @@ function parseAddress(msg) {
return addr;
}
exports.data = function data(device, oscData) {
this.deviceInfoUpdate(device, 'status', 'ok');
exports.data = function data(_device, oscData) {
this.deviceInfoUpdate(_device, 'status', 'ok');
const d = device;
const device = _device;
if (oscData.address === '/xinfo') {
d.data.X32.info.name = oscData.args[1];
d.data.X32.info.ip = oscData.args[0];
d.data.X32.info.firmware = oscData.args[3];
d.data.X32.info.model = oscData.args[2];
device.data.info.name = oscData.args[1];
device.data.info.ip = oscData.args[0];
device.data.info.firmware = oscData.args[3];
device.data.info.model = oscData.args[2];
this.deviceInfoUpdate(device, 'defaultName', d.data.X32.info.name);
this.deviceInfoUpdate(_device, 'defaultName', device.data.info.name);
d.send('/main/st/config/name');
device.send('/main/st/config/name');
for (let i = 0; i <= 32; i++) {
d.send(`/ch/${i.toString().padStart(2, '0')}/config/name`);
device.send(`/ch/${i.toString().padStart(2, '0')}/config/name`);
}
d.draw();
device.draw();
} else if (oscData.address.includes('/ch/meters')) {
const buf = Buffer.from(oscData.args[0]);
@@ -69,12 +70,12 @@ exports.data = function data(device, oscData) {
for (let i = 0; i < 70; i++) {
if (i >= 0 && i < 32) {
// These are channel meters
d.data.X32.inputs.channels[i].meter = Console.getBehringerDB(buf.readFloatLE(offset));
device.data.inputs.channels[i].meter = Console.getBehringerDB(buf.readFloatLE(offset));
}
offset += 4;
}
d.draw();
device.draw();
} else if (oscData.address.includes('/main/meters')) {
const buf = Buffer.from(oscData.args[0]);
let offset = 4; // skip first 4 bytes they are the length bytes
@@ -82,10 +83,10 @@ exports.data = function data(device, oscData) {
for (let i = 0; i < 49; i++) {
if (i === 22) {
// STEREO LEFT METER
d.data.X32.main.stereo.meter[0] = Console.getBehringerDB(buf.readFloatLE(offset));
device.data.main.stereo.meter[0] = Console.getBehringerDB(buf.readFloatLE(offset));
} else if (i === 23) {
// STEREO RIGHT METER
d.data.X32.main.stereo.meter[1] = Console.getBehringerDB(buf.readFloatLE(offset));
device.data.main.stereo.meter[1] = Console.getBehringerDB(buf.readFloatLE(offset));
}
offset += 4;
}
@@ -94,56 +95,53 @@ exports.data = function data(device, oscData) {
if (addr[0] === 'ch') {
const channel = Number(addr[1]);
d.data.X32.inputs.channels[channel - 1].fader = oscData.args[0];
d.data.X32.inputs.channels[channel - 1].faderDB = Console.getBehringerDB(oscData.args[0]);
device.data.inputs.channels[channel - 1].fader = oscData.args[0];
device.data.inputs.channels[channel - 1].faderDB = Console.getBehringerDB(oscData.args[0]);
} else if (addr[0] === 'main') {
d.data.X32.main.stereo.fader = oscData.args[0];
d.data.X32.main.stereo.faderDB = Console.getBehringerDB(oscData.args[0]);
device.data.main.stereo.fader = oscData.args[0];
device.data.main.stereo.faderDB = Console.getBehringerDB(oscData.args[0]);
}
d.draw();
device.draw();
} else if (oscData.address.includes('/mix/on')) {
const addr = parseAddress(oscData.address);
if (addr[0] === 'ch') {
const channel = Number(addr[1]);
d.data.X32.inputs.channels[channel - 1].mute = oscData.args[0];
d.send(`/ch/${addr[1]}/mix/fader`);
device.data.inputs.channels[channel - 1].mute = oscData.args[0];
device.send(`/ch/${addr[1]}/mix/fader`);
} else if (addr[0] === 'main') {
d.data.X32.main.stereo.mute = oscData.args[0];
d.send(`/main/${addr[1]}/mix/fader`);
device.data.main.stereo.mute = oscData.args[0];
device.send(`/main/${addr[1]}/mix/fader`);
}
d.draw();
device.draw();
} else if (oscData.address.includes('/config/name')) {
const addr = parseAddress(oscData.address);
if (addr[0] === 'main') {
if (addr[1] === 'st') {
d.data.X32.main.stereo.name = oscData.args[0];
if (d.data.X32.main.stereo.name === '') {
d.data.X32.main.stereo.name = 'LR';
device.data.main.stereo.name = oscData.args[0];
if (device.data.main.stereo.name === '') {
device.data.main.stereo.name = 'LR';
}
d.send(`/main/${addr[1]}/config/color`);
device.send(`/main/${addr[1]}/config/color`);
}
} else if (addr[0] === 'ch') {
const channel = Number(addr[1]);
d.data.X32.inputs.channels[channel - 1].name = oscData.args[0];
d.send(`/ch/${addr[1]}/config/color`);
device.data.inputs.channels[channel - 1].name = oscData.args[0];
device.send(`/ch/${addr[1]}/config/color`);
}
d.draw();
device.draw();
} else if (oscData.address.includes('/config/color')) {
const addr = parseAddress(oscData.address);
if (addr[0] === 'main') {
d.data.X32.main.stereo.color = oscData.args[0];
d.send(`/main/${addr[1]}/mix/on`);
device.data.main.stereo.color = oscData.args[0];
device.send(`/main/${addr[1]}/mix/on`);
} else if (addr[0] === 'ch') {
const channel = Number(addr[1]);
d.data.X32.inputs.channels[channel - 1].color = oscData.args[0];
d.send(`/ch/${addr[1]}/mix/on`);
device.data.inputs.channels[channel - 1].color = oscData.args[0];
device.send(`/ch/${addr[1]}/mix/on`);
}
d.draw();
} else {
// console.log(oscData);
device.draw();
}
// console.log(msg)
};
exports.heartbeat = function heartbeat(device) {
+1 -7
View File
@@ -30,7 +30,6 @@ tr td:first-child {
padding: 1px 6px;
border-radius: 10px;
color: black;
font-family: PlexMono !important;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
@@ -136,12 +135,7 @@ input[type='range']::-webkit-slider-runnable-track {
.meter {
height: 3px;
background: linear-gradient(
90deg,
rgba(19, 126, 30, 1) 0%,
rgba(255, 238, 30, 1) 85%,
rgba(255, 0, 0, 1) 100%
);
background: linear-gradient(90deg, rgba(19, 126, 30, 1) 0%, rgba(255, 238, 30, 1) 85%, rgba(255, 0, 0, 1) 100%);
}
/* need to find a way to match the color of this with the table row */
+14 -14
View File
@@ -1,6 +1,6 @@
<header>
<h1><%= listName %></h1>
<h2><%= data.model %></h2>
<h2><%= data.info.model %></h2>
</header>
<table class="cv-table">
@@ -13,15 +13,15 @@
</tr>
<tr>
<td width="40px">LR</td>
<td width="100px"><div class="color color-<%= data.X32.main.stereo.color %>"><%- data.X32.main.stereo.name%></div></td>
<td><div class="fader-mute mute-<%=data.X32.main.stereo.mute%>">M</div></td>
<td><%= formatAsDB(data.X32.main.stereo.faderDB) %></td>
<td width="100px"><div class="color color-<%= data.main.stereo.color %>"><%- data.main.stereo.name%></div></td>
<td><div class="fader-mute mute-<%=data.main.stereo.mute%>">M</div></td>
<td><%= formatAsDB(data.main.stereo.faderDB) %></td>
<td>
<% let style = `style=width:${Math.abs(data.X32.main.stereo.meter[0] - 10)}%`; %>
<% let style = `style=width:${Math.abs(data.main.stereo.meter[0] - 10)}%`; %>
<div class="meter">
<div class="meter-cover" <%= style %>></div>
</div>
<% style = `style=width:${Math.abs(data.X32.main.stereo.meter[1] - 10)}%`; %>
<% style = `style=width:${Math.abs(data.main.stereo.meter[1] - 10)}%`; %>
<div class="meter">
<div class="meter-cover" <%= style %>></div>
</div>
@@ -29,22 +29,22 @@
type="range"
min="0"
max="100"
value="<%= data.X32.main.stereo.fader*100 %>"
value="<%= data.main.stereo.fader*100 %>"
disabled />
</td>
</tr>
<% for(var i=0; i<32; i++){ %> <% if(data.X32.inputs.channels[i].name == "end"){break;} %>
<% for(var i=0; i<32; i++){ %> <% if(data.inputs.channels[i].name == "end"){break;} %>
<tr>
<td><%= i+1 %></td>
<td>
<div class="color color-<%= data.X32.inputs.channels[i].color %>">
<%- data.X32.inputs.channels[i].name || i+1 %>
<div class="color color-<%= data.inputs.channels[i].color %>">
<%- data.inputs.channels[i].name || i+1 %>
</div>
</td>
<td><div class="fader-mute mute-<%=data.X32.inputs.channels[i].mute%>">M</div></td>
<td><%= formatAsDB(data.X32.inputs.channels[i].faderDB) %></td>
<td><div class="fader-mute mute-<%=data.inputs.channels[i].mute%>">M</div></td>
<td><%= formatAsDB(data.inputs.channels[i].faderDB) %></td>
<td>
<% let style = `style=width:${Math.abs(data.X32.inputs.channels[i].meter - 10)}%`; %>
<% let style = `style=width:${Math.abs(data.inputs.channels[i].meter - 10)}%`; %>
<div class="meter">
<div class="meter-cover" <%= style %>></div>
</div>
@@ -52,7 +52,7 @@
type="range"
min="0"
max="100"
value="<%= data.X32.inputs.channels[i].fader*100 %>"
value="<%= data.inputs.channels[i].fader*100 %>"
disabled />
</td>
</tr>
+2 -5
View File
@@ -1,9 +1,10 @@
exports.config = {
defaultName: 'X Air Mixer',
connectionType: 'UDPsocket',
heartbeatInterval: 10000,
defaultPort: 10024,
mayChangePort: false,
heartbeatInterval: 9000,
heartbeatTimeout: 15000,
searchOptions: {
type: 'UDPsocket',
searchBuffer: Buffer.from([0x2f, 0x78, 0x69, 0x6e, 0x66, 0x6f]),
@@ -28,10 +29,6 @@ exports.ready = function ready(device) {
d.data.stereoMute = 0;
d.send('/xinfo');
// device.send(Buffer.from("\x2f\x62\x61\x74\x63\x68\x73\x75\x62\x73\x63\x72\x69\x62\x65\x00\x2c\x73\x73\x69\x69\x69\x00\x00\x6d\x65\x74\x65\x72\x73\x2f\x30\x00\x00\x00\x00\x2f\x6d\x65\x74\x65\x72\x73\x2f\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"));
// device.send(Buffer.from("/batchsubscribe\x00,ssiii\x00\x00meters/0\x00\x00\x00\x00/meters/0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"));
// device.send(Buffer.from("/subscribe\x00,si\x00/-stat/solosw/01\x001"));
};
function parseAddress(msg) {