synthing

a waveform sequencing synth on the web
Log | Files | Refs | Submodules

commit f41c795a4b6ffd91a5440f19cbb51ad3f71170f6
parent fe7c8df2a07849953cb08d8ebcc06d78db248dfa
Author: Massimo Siboldi <mdsiboldi@gmail.com>
Date:   Sat,  9 Dec 2017 17:07:10 -0800

Add adsr interface and knobs, no design

Diffstat:
Msrc/App/index.js | 59++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/Polyphonic.js | 8++++++--
Msrc/WaveEditor/index.js | 21++++++++++-----------
Msrc/WaveManager/index.js | 3+--
Msrc/helpers.js | 10+++++++++-
5 files changed, 80 insertions(+), 21 deletions(-)

diff --git a/src/App/index.js b/src/App/index.js @@ -4,6 +4,7 @@ import WaveManager from '../WaveManager/'; import Synth from '../Synth/'; import './App.css'; import consts from '../consts.js'; +import helpers from '../helpers.js'; const initialWave = new Array(consts.BUF_SIZE).fill(0); @@ -42,6 +43,48 @@ const boolArray = { } } +//TODO this +class Knob extends Component { + componentDidMount() { + const handleMove = (ev) => { + let step = this.props.step || 0.5; + let newVal = this.props.val - (ev.movementY * step); + if (typeof(this.props.minVal) === 'number' && (newVal < this.props.minVal)) { + newVal = this.props.minVal; + } else if (typeof(this.props.maxVal) === 'number' && (newVal > this.props.maxVal)) { + newVal = this.props.maxVal; + } + this.props.update(newVal); + } + this.knobRef.addEventListener('mousedown', (ev) => { + ev.preventDefault(); + document.addEventListener('mousemove', handleMove); + helpers.oneTime(document, 'mouseup', (ev) => { + document.removeEventListener('mousemove', handleMove); + }); + }) + } + render() { + return ( + <div ref={(knob) => {this.knobRef = knob}}>{this.props.val}</div> + ) + } +} + + +const Adsr = (props) => ( + <div> + {['a', 'd', 's', 'r'].map((letter) => ( + <Knob + val={props.adsr[letter]} + minVal={0} + maxVal={3} + step={0.1} + update={(newVal) => {props.update(letter, newVal)}} + /> + ))} + </div> +) class App extends Component { @@ -62,14 +105,20 @@ class App extends Component { pos: {x: 0, y: 0} }, adsr: { - attackTime: 0.1, - decayTime: 1, - sustainLevel: 0.4, - releaseTime: 2 + a: 0.3, + d: 1, + s: 0.4, + r: 1 } } } + updateAdsr = (letter, val) => { + this.setState({ + adsr: Object.assign({}, this.state.adsr, {[letter]: val}) + }); + } + editingWaveform = () => this.state.waveforms[this.state.editingWaveformIdx].waveform activeWaveforms = () => this.state.waveforms.reduce((accum, val) => { @@ -151,7 +200,6 @@ class App extends Component { beats={this.state.waveforms[idx].beats} beat={this.state.beat} updateBeat={(i, val) => { - console.log('updating', i, val); this.updateWaveform(idx, { beats: boolArray.update( this.state.waveforms[idx].beats, @@ -174,6 +222,7 @@ class App extends Component { this.updateWaveform(this.state.editingWaveformIdx, {waveform}); }} ></WaveEditor> + <Adsr adsr={this.state.adsr} update={this.updateAdsr} /> <button onClick={() => {this.setBeats(this.state.numBeats + 1)}}>+ beat</button> <button onClick={() => {this.setBeats(this.state.numBeats - 1)}}>- beat</button> <button onClick={() => this.addWaveform()}>+</button> diff --git a/src/Polyphonic.js b/src/Polyphonic.js @@ -3,14 +3,18 @@ import Envelope from 'envelope-generator'; export default class Polyphonic { constructor(audioContext) { - this.voices = []; this.audioContext = audioContext; this.periodicWave = null; } addVoice(note, adsr) { let osc = this.audioContext.createOscillator(); let gain = this.audioContext.createGain(); - let envelope = new Envelope(this.audioContext, adsr); + let envelope = new Envelope(this.audioContext, { + attackTime: adsr.a, + decayTime: adsr.d, + sustainTime: adsr.s, + releaseTime: adsr.r + }); osc.frequency.value = note.frequency; gain.gain.setValueAtTime(0, 0) if (this.periodicWave) { diff --git a/src/WaveEditor/index.js b/src/WaveEditor/index.js @@ -38,20 +38,19 @@ export default class waveEditor extends Component { componentDidMount() { drawArea(this.props.waveform, this.canvasRef); const handleMove = (ev) => { + console.log('handling move'); this.updateWaveform({ - x: ev.x, - y: ev.y + x: ev.x + window.scrollX, + y: ev.y + window.scrollY }, this.props.waveform); } - document.addEventListener('mousedown', (ev) => { - if (ev.target === this.canvasRef) { - document.addEventListener('mousemove', handleMove); - } - }); - document.addEventListener('mouseup', (ev) => { - document.removeEventListener('mousemove', handleMove); - this.setState({ - prevZone: null + this.canvasRef.addEventListener('mousedown', (ev) => { + document.addEventListener('mousemove', handleMove); + helpers.oneTime(document, 'mouseup', (ev) => { + document.removeEventListener('mousemove', handleMove); + this.setState({ + prevZone: null + }); }); }); } diff --git a/src/WaveManager/index.js b/src/WaveManager/index.js @@ -1,8 +1,7 @@ -import { h, Component} from 'preact'; +import { h, Component } from 'preact'; export default class WaveManager extends Component { render() { - console.log(this.props.beats); return ( <div class="wave-manager"> <div class="buttons"> diff --git a/src/helpers.js b/src/helpers.js @@ -2,5 +2,13 @@ export default { linear: (m, x, b) => (m * x) + b, bounded: (val, min, max) => val < min ? min : (val > max ? max : val), scale: (buf, amt) => buf.map(val => val * amt), - add: (arr1, arr2) => arr1.map((v, i) => v + arr2[i]) + add: (arr1, arr2) => arr1.map((v, i) => v + arr2[i]), + oneTime: (target, type, handler) => { + const doOnce = (ev) => { + target.removeEventListener(type, doOnce); + handler(ev); + } + target.addEventListener(type, doOnce); + } + };