node-red-contrib-music
Version:
Synthesise music with node-red. The beat node creates regular beats at a rate you can control, and which can be sunchronised with other machines. The divider node adds information to beat events, dividing beats into bars, bars into phrases, phrases into s
165 lines (126 loc) • 5.68 kB
HTML
<script type="text/javascript">
const fxtypesURL = "node-red-contrib-music/fxtypes";
let fxtypes;
$.getJSON(fxtypesURL, function (data) {
fxtypes = data;
});
RED.nodes.registerType('soundfx',{
category: 'music',
color: '#a6bbcf',
defaults: {
fxtype: {value: "reverb", required: "true"},
volume: {value: 100},
fxcontrols: {value: {}}
},
inputs:1,
inputLabels:"supercollider instructions, fxcontrol:",
outputs:1,
outputLabels:["supercollider instructions"],
icon: "effect.png",
label: function() {
return this.name || this.fxtype || "soundfx";
},
oneditprepare: function(){
node = this;
function setFXDescription(){
let fxdescription = fxtypes[node.fxtype].description || " ";
$('#node-input-fxtype-description').html(fxdescription);
}
function populateFxtypeSelect(){
let fxtypeList = []
for(let fxtype in fxtypes){
fxtypeList.push(fxtype);
}
fxtypeList.sort();
const select = $("#node-input-fxtype");
select.empty();
for(let fxtype of fxtypeList){
let row = $("<option value='" + fxtype + "'>" + fxtype + "</option>");
if(node.fxtype === fxtype){
row.attr("selected", "selected");
}
select.append(row);
}
setFXDescription();
select.change(function () {
const option = $(this).find('option:selected').val();
if (option !== node.fxtype) {
node.fxtype = option;
setFXDescription();
node.fxcontrols = {};
$('#node-input-fxcontrol-container').empty();
populateFxcontrols();
}
}
);
}
function populateFxcontrols(){
const fxcontrolSpecs = fxtypes[node.fxtype].fxcontrols || {};
const container = $('#node-input-fxcontrol-container');
let noControls = true;
for(let fxcontrol in fxcontrolSpecs){
let row = $('<div/>', {class: 'form-row'}).appendTo(container);
let id = 'node-input-fxcontrol-' + fxcontrol;
let fxdetails = fxcontrolSpecs[fxcontrol] || {};
let max = fxdetails.max || 1;
let min = fxdetails.min || 0;
let default_ = fxdetails.default;
let description = fxdetails.description?(fxdetails.description + '. '):'';
let info = `${description}min: ${min}, max: ${max}, default: ${default_}`;
let value = node.fxcontrols[fxcontrol] || default_;
let labelElement = $(`<label>${fxcontrol}</label>`, {'for': id}).appendTo(row);
let step = Math.pow(10, Math.floor(Math.log10(max - min)) - 2);
let valueField = $('<input/>',{id: id, class: "node-input-fxcontrol", width: "7em", type:"number", min:min, max: max, step: step}).appendTo(row);
valueField.val(value);
let infoElement = $(`<div><small>${info}</small></div>`, {style: 'float:right'}).appendTo(row);
noControls = false;
}
if (noControls) {
$("<div><i>None</i></div>div").appendTo(container);
}
}
populateFxtypeSelect();
populateFxcontrols();
},
oneditsave: function(){
let fxcontrols = $('.node-input-fxcontrol');
fxcontrols.each(function(){
const control_id = $(this).attr('id');
const control = control_id.split('-').pop();
const value =$(this).val();
node.fxcontrols[control] = value;
}
);
}
});
</script>
<script type="text/x-red" data-template-name="soundfx">
<div class="form-row">
<label for="node-input-fxtype" width="25em">Effect Type</label>
<select id="node-input-fxtype">
</select>
</div>
<div class="form-row" id="node-input-fxtype-description"></div>
<div class="form-row" width="25em"><b>Effect controls (fxcontrol)</b></div>
<div id="node-input-fxcontrol-container">
</div>
</script>
<script type="text/x-red" data-help-name="soundfx">
<p>Add a SuperCollider sound effect to a synth sound</p>
<h3>Inputs</h3>
<p>A <code>soundfx</code> node gets its input from a <code>synth</code> node.</p>
<dl class='message-properties'>
<dt class="optional">topic <span class="property-type">string</span></dt>
<dd>
<p>If <code>msg.topic</code> starts with <code>fxcontrol:</code> then the rest of the <code>msg.topic</code> is interpreted as a SuperCollider sound effect parameter name, and the <code>msg.payload</code> is used as its value. The parameters (fxcontrol) that can be changed vary from effect to effect, and their details can be seen in the configuration.</p></dd>
</dl>
<h3>Outputs</h3>
<p>A <code>soundfx</code> node sends its output to a <code>osc</code> node.</p>
<h3>Configuration</h3>
<dl class='message-properties'>
<dt><span class='optional'>Effect type</span> <span class='property-type'>string</span></dt>
<dd>The name of the sound effect type to use.</dd>
<dt><span class='optional'>Effect controls (fxcontrol)</span> <span class='property-type'>number</span></dt>
<dd>Each sound effect has its own set of parameters that control its operation.</dd>
</dl>
</script>