Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added an effect by voice #40

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion desktop/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ app.on('ready', () => {
require('electron').protocol.registerBufferProtocol('js', protocolHandler)

app.win = new BrowserWindow({
width: 445,
width: 607,
height: 210,
minWidth: 200,
minHeight: 190,
Expand Down
90 changes: 53 additions & 37 deletions desktop/sources/links/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,65 @@ body { background:black; padding: 30px; font-family: 'input_mono_regular'; font-

/* Mixer */

#mixer { line-height: 15px; color:#666; position: relative; text-transform: uppercase; min-width: 420px}
#mixer { line-height: 15px; color:#666; position: relative; text-transform: uppercase; min-width: 582px}
#mixer > div { position: absolute; width: 95px}
#mixer > div.channel { position: absolute; }
#mixer > div.effect { width:65px; }
#mixer > div .cid { font-family: 'input_mono_medium'; font-weight: normal; }
#mixer > div canvas { position: absolute; right: -10px;top: 0.5px;}
#mixer span { padding: 0px 2.55px; }

#mixer #ch0 { top:calc(0 * 15px); left:0px; }
#mixer #ch1 { top:calc(1 * 15px); left:0px; }
#mixer #ch2 { top:calc(2 * 15px); left:0px; }
#mixer #ch3 { top:calc(3 * 15px); left:0px; }
#mixer #ch4 { top:calc(4 * 15px); left:0px; }
#mixer #ch5 { top:calc(5 * 15px); left:0px; }
#mixer #ch6 { top:calc(6 * 15px); left:0px; }
#mixer #ch7 { top:calc(7 * 15px); left:0px; }

#mixer #ch8 { top:calc(0 * 15px); left:calc(1 * 110px); }
#mixer #ch9 { top:calc(1 * 15px); left:calc(1 * 110px); }
#mixer #cha { top:calc(2 * 15px); left:calc(1 * 110px); }
#mixer #chb { top:calc(3 * 15px); left:calc(1 * 110px); }
#mixer #chc { top:calc(4 * 15px); left:calc(1 * 110px); }
#mixer #chd { top:calc(5 * 15px); left:calc(1 * 110px); }
#mixer #che { top:calc(6 * 15px); left:calc(1 * 110px); }
#mixer #chf { top:calc(7 * 15px); left:calc(1 * 110px); }

#mixer #chbit { top:calc(0 * 15px); left:calc(2 * 110px); }
#mixer #chdis { top:calc(1 * 15px); left:calc(2 * 110px); }
#mixer #chwah { top:calc(2 * 15px); left:calc(2 * 110px); }
#mixer #chche { top:calc(3 * 15px); left:calc(2 * 110px); }
#mixer #chfee { top:calc(4 * 15px); left:calc(2 * 110px); }
#mixer #chdel { top:calc(5 * 15px); left:calc(2 * 110px); }
#mixer #chtre { top:calc(6 * 15px); left:calc(2 * 110px); }
#mixer #chrev { top:calc(7 * 15px); left:calc(2 * 110px); }

#mixer #chpha { top:calc(0 * 15px); left:calc(2 * 150px); }
#mixer #chvib { top:calc(1 * 15px); left:calc(2 * 150px); }
#mixer #chcho { top:calc(2 * 15px); left:calc(2 * 150px); }
#mixer #chste { top:calc(3 * 15px); left:calc(2 * 150px); }
#mixer #chequ { top:calc(4 * 15px); left:calc(2 * 150px); }
#mixer #chcom { top:calc(5 * 15px); left:calc(2 * 150px); }
#mixer #chvol { top:calc(6 * 15px); left:calc(2 * 150px); }
#mixer #chlim { top:calc(7 * 15px); left:calc(2 * 150px); }
#mixer #ch0 { top:calc(0 * 15px); left:0px; }
#mixer #ch0-fx { top:calc(0 * 15px); left:110px; }
#mixer #ch1 { top:calc(1 * 15px); left:0px; }
#mixer #ch1-fx { top:calc(1 * 15px); left:110px; }
#mixer #ch2 { top:calc(2 * 15px); left:0px; }
#mixer #ch2-fx { top:calc(2 * 15px); left:110px; }
#mixer #ch3 { top:calc(3 * 15px); left:0px; }
#mixer #ch3-fx { top:calc(3 * 15px); left:110px; }
#mixer #ch4 { top:calc(4 * 15px); left:0px; }
#mixer #ch4-fx { top:calc(4 * 15px); left:110px; }
#mixer #ch5 { top:calc(5 * 15px); left:0px; }
#mixer #ch5-fx { top:calc(5 * 15px); left:110px; }
#mixer #ch6 { top:calc(6 * 15px); left:0px; }
#mixer #ch6-fx { top:calc(6 * 15px); left:110px; }
#mixer #ch7 { top:calc(7 * 15px); left:0px; }
#mixer #ch7-fx { top:calc(7 * 15px); left:110px; }

#mixer #ch8 { top:calc(0 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #ch8-fx { top:calc(0 * 15px); left:calc(2 * 110px + 1 * 81px); }
#mixer #ch9 { top:calc(1 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #ch9-fx { top:calc(1 * 15px); left:calc(2 * 110px + 1 * 81px); }
#mixer #cha { top:calc(2 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #cha-fx { top:calc(2 * 15px); left:calc(2 * 110px + 1 * 81px); }
#mixer #chb { top:calc(3 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #chb-fx { top:calc(3 * 15px); left:calc(2 * 110px + 1 * 81px); }
#mixer #chc { top:calc(4 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #chc-fx { top:calc(4 * 15px); left:calc(2 * 110px + 1 * 81px); }
#mixer #chd { top:calc(5 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #chd-fx { top:calc(5 * 15px); left:calc(2 * 110px + 1 * 81px); }
#mixer #che { top:calc(6 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #che-fx { top:calc(6 * 15px); left:calc(2 * 110px + 1 * 81px); }
#mixer #chf { top:calc(7 * 15px); left:calc(1 * 110px + 1 * 81px); }
#mixer #chf-fx { top:calc(7 * 15px); left:calc(2 * 110px + 1 * 81px); }

#mixer #chbit { top:calc(0 * 15px); left:calc(2 * 110px + 2 * 81px); }
#mixer #chdis { top:calc(1 * 15px); left:calc(2 * 110px + 2 * 81px); }
#mixer #chwah { top:calc(2 * 15px); left:calc(2 * 110px + 2 * 81px); }
#mixer #chche { top:calc(3 * 15px); left:calc(2 * 110px + 2 * 81px); }
#mixer #chfee { top:calc(4 * 15px); left:calc(2 * 110px + 2 * 81px); }
#mixer #chdel { top:calc(5 * 15px); left:calc(2 * 110px + 2 * 81px); }
#mixer #chtre { top:calc(6 * 15px); left:calc(2 * 110px + 2 * 81px); }
#mixer #chrev { top:calc(7 * 15px); left:calc(2 * 110px + 2 * 81px); }

#mixer #chpha { top:calc(0 * 15px); left:calc(2 * 110px + 3 * 81px); }
#mixer #chvib { top:calc(1 * 15px); left:calc(2 * 110px + 3 * 81px); }
#mixer #chcho { top:calc(2 * 15px); left:calc(2 * 110px + 3 * 81px); }
#mixer #chste { top:calc(3 * 15px); left:calc(2 * 110px + 3 * 81px); }
#mixer #chequ { top:calc(4 * 15px); left:calc(2 * 110px + 3 * 81px); }
#mixer #chcom { top:calc(5 * 15px); left:calc(2 * 110px + 3 * 81px); }
#mixer #chvol { top:calc(6 * 15px); left:calc(2 * 110px + 3 * 81px); }
#mixer #chlim { top:calc(7 * 15px); left:calc(2 * 110px + 3 * 81px); }

/* Commander */

Expand All @@ -60,4 +76,4 @@ body { background:black; padding: 30px; font-family: 'input_mono_regular'; font-
@media (max-width: 380px) {
#pilot #mixer { columns:1 !important; min-width: 0px }
#pilot #mixer > div { max-width: 100%}
}
}
17 changes: 17 additions & 0 deletions desktop/sources/scripts/interface.channel.effect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import EffectInterface from './interface.effect.js'

export default function ChannelEffectInterface (pilot, channelId, id, node) {
EffectInterface.call(this, pilot, id, node)

this.el.id = `ch${parseInt(channelId).toString(16)}-fx`;

this.run = function (msg) {
if (!msg || msg.substr(0, 4).toLowerCase() !== `${parseInt(channelId).toString(16)}${id}`) {
return
}

if (msg.substr(0, 4).toLowerCase() === `${parseInt(channelId).toString(16)}${id}`) {
this.operate(`${msg.substr(1)}`.substr(3))
}
}
}
21 changes: 18 additions & 3 deletions desktop/sources/scripts/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default function Interface (pilot, id, node) {
this.meter = new Tone.Meter(0.95)
this.waveform = new Tone.Waveform(256)

this.effectId = id
this.el = document.createElement('div')
this.el.id = `ch${id}`
this.canvas = document.createElement('canvas')
Expand Down Expand Up @@ -35,15 +36,29 @@ export default function Interface (pilot, id, node) {
host.appendChild(this.el)
}

this.uninstall = function () {
this.el.remove()
}

this.start = function () {
this.updateAll({}, true)
loop()
this.loop()
}

this.stop = function () {
cancelAnimationFrame(this.loopId)
}

this.connect = function (node) {
this.node.connect(node)
}

this.disconnect = function (node) {
this.node.disconnect()
this.node.dispose()
}


function draw (level) {
if (pilot.animate !== true) { return }
if (lastUpdate && performance.now() - lastUpdate < 30) { return }
Expand Down Expand Up @@ -93,8 +108,8 @@ export default function Interface (pilot, id, node) {
context.closePath()
}

function loop () {
requestAnimationFrame(loop)
this.loop = function() {
this.loopId = requestAnimationFrame(this.loop.bind(this))
draw()
}

Expand Down
118 changes: 101 additions & 17 deletions desktop/sources/scripts/mixer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,57 @@ const Tone = require('tone')

import ChannelInterface from './interface.channel.js'
import EffectInterface from './interface.effect.js'
import ChannelEffectInterface from './interface.channel.effect.js'

const EFFECTS = ['bit', 'dis', 'wah', 'che', 'fee', 'del', 'tre', 'rev', 'pha', 'vib', 'cho', 'ste', 'equ', 'com', 'vol', 'lim']

const getEffect = function(type) {
switch(type) {
case 'bit':
return new Tone.BitCrusher(4)
case 'dis':
return new Tone.Distortion(0.05)
case 'wah':
return new Tone.AutoWah(100, 6, 0)
case 'che':
return new Tone.Chebyshev(50)
case 'fee':
return new Tone.FeedbackDelay(0)
case 'del':
return new Tone.PingPongDelay('4n', 0.2)
case 'tre':
return new Tone.Tremolo()
case 'rev':
return new Tone.JCReverb(0)
case 'pha':
return new Tone.Phaser(0.5, 3, 350)
case 'vib':
return new Tone.Vibrato()
case 'cho':
return new Tone.Chorus(4, 2.5, 0.5)
case 'ste':
return new Tone.StereoWidener(0.5, 3, 350)
case 'equ':
return new Tone.EQ3(5, 0, 5)
case 'com':
return new Tone.Compressor(-6, 4)
case 'vol':
return new Tone.Volume(6)
case 'lim':
return new Tone.Limiter(-2)
}

console.warn('Unknown effect', type)

return null
}

export default function Mixer (pilot) {
this.el = document.createElement('div')
this.el.id = 'mixer'

this.channels = []
this.channelsEffects = []
this.effects = {}

this.install = function (host) {
Expand Down Expand Up @@ -37,30 +82,37 @@ export default function Mixer (pilot) {
this.channels[14] = new ChannelInterface(pilot, 14, new Tone.MembraneSynth({ 'octaves': 15, 'oscillator': { 'type': 'triangle' } }))
this.channels[15] = new ChannelInterface(pilot, 15, new Tone.MembraneSynth({ 'octaves': 20, 'oscillator': { 'type': 'square' } }))

// One FX slot (default rev) for each channel
for (const id in this.channels) {
this.channelsEffects[id] = new ChannelEffectInterface(pilot, id, 'rev', getEffect('rev'))
}

// I
this.effects.bitcrusher = new EffectInterface(pilot, 'bit', new Tone.BitCrusher(4))
this.effects.distortion = new EffectInterface(pilot, 'dis', new Tone.Distortion(0.05))
this.effects.autowah = new EffectInterface(pilot, 'wah', new Tone.AutoWah(100, 6, 0))
this.effects.chebyshev = new EffectInterface(pilot, 'che', new Tone.Chebyshev(50))
this.effects.bitcrusher = new EffectInterface(pilot, 'bit', getEffect('bit'))
this.effects.distortion = new EffectInterface(pilot, 'dis', getEffect('dis'))
this.effects.autowah = new EffectInterface(pilot, 'wah', getEffect('wah'))
this.effects.chebyshev = new EffectInterface(pilot, 'che', getEffect('che'))
// II
this.effects.feedback = new EffectInterface(pilot, 'fee', new Tone.FeedbackDelay(0))
this.effects.delay = new EffectInterface(pilot, 'del', new Tone.PingPongDelay('4n', 0.2))
this.effects.tremolo = new EffectInterface(pilot, 'tre', new Tone.Tremolo())
this.effects.reverb = new EffectInterface(pilot, 'rev', new Tone.JCReverb(0))
this.effects.feedback = new EffectInterface(pilot, 'fee', getEffect('fee'))
this.effects.delay = new EffectInterface(pilot, 'del', getEffect('del'))
this.effects.tremolo = new EffectInterface(pilot, 'tre', getEffect('tre'))
this.effects.reverb = new EffectInterface(pilot, 'rev', getEffect('rev'))
// III
this.effects.phaser = new EffectInterface(pilot, 'pha', new Tone.Phaser(0.5, 3, 350))
this.effects.vibrato = new EffectInterface(pilot, 'vib', new Tone.Vibrato())
this.effects.chorus = new EffectInterface(pilot, 'cho', new Tone.Chorus(4, 2.5, 0.5))
this.effects.widener = new EffectInterface(pilot, 'ste', new Tone.StereoWidener(0.5, 3, 350))
this.effects.phaser = new EffectInterface(pilot, 'pha', getEffect('pha'))
this.effects.vibrato = new EffectInterface(pilot, 'vib', getEffect('vib'))
this.effects.chorus = new EffectInterface(pilot, 'cho', getEffect('cho'))
this.effects.widener = new EffectInterface(pilot, 'ste', getEffect('ste'))
// Mastering
this.effects.equalizer = new EffectInterface(pilot, 'equ', new Tone.EQ3(5, 0, 5))
this.effects.compressor = new EffectInterface(pilot, 'com', new Tone.Compressor(-6, 4))
this.effects.volume = new EffectInterface(pilot, 'vol', new Tone.Volume(6))
this.effects.limiter = new EffectInterface(pilot, 'lim', new Tone.Limiter(-2))
this.effects.equalizer = new EffectInterface(pilot, 'equ', getEffect('equ'))
this.effects.compressor = new EffectInterface(pilot, 'com', getEffect('com'))
this.effects.volume = new EffectInterface(pilot, 'vol', getEffect('vol'))
this.effects.limiter = new EffectInterface(pilot, 'lim', getEffect('lim'))

// Connect
for (const id in this.channels) {
this.channels[id].connect(this.effects.bitcrusher.node)
this.channels[id].connect(this.channelsEffects[id].node)

this.channelsEffects[id].node.connect(this.effects.bitcrusher.node)
}

this.effects.bitcrusher.connect(this.effects.distortion.node)
Expand All @@ -85,6 +137,8 @@ export default function Mixer (pilot) {
// Add all instruments to dom
for (const id in this.channels) {
this.channels[id].install(this.el)

this.channelsEffects[id].install(this.el)
}

// Add all effects to dom
Expand All @@ -99,6 +153,7 @@ export default function Mixer (pilot) {
console.log('Synthetiser', 'Starting..')
for (const id in this.channels) {
this.channels[id].start()
this.channelsEffects[id].start()
}
for (const id in this.effects) {
this.effects[id].start()
Expand Down Expand Up @@ -131,6 +186,35 @@ export default function Mixer (pilot) {
// Single
for (const id in this.channels) {
this.channels[id].run(msg)

// Channel effect might have changed
if (msg && `${msg}`.substr(0, 1).toLowerCase() === id.toString(16)) {
let effect = `${msg}`.substr(1, 3).toLowerCase()
let channel = this.channels[id]
let channelEffect = this.channelsEffects[id]

if (EFFECTS.indexOf(effect) > -1 && effect !== channelEffect.effectId) {
let newEffect = getEffect(effect)

if (newEffect) {
// Remove current effect
this.channelsEffects[id].stop()
this.channelsEffects[id].uninstall()
this.channelsEffects[id].disconnect()

// Install new one
this.channelsEffects[id] = new ChannelEffectInterface(pilot, id, effect, newEffect)
this.channelsEffects[id].install(this.el)
this.channelsEffects[id].start()

// Put back in circuit
this.channels[id].connect(this.channelsEffects[id].node)
this.channelsEffects[id].node.connect(this.effects.bitcrusher.node)
}
}
}

this.channelsEffects[id].run(msg)
}
for (const id in this.effects) {
this.effects[id].run(msg)
Expand Down