├── .gitignore
├── LICENSE
├── README.md
├── assets
├── buffers.json
├── maps.json
├── patches
│ ├── aerial drone.json
│ ├── dark factory.json
│ ├── ocean.json
│ ├── rain forest.json
│ └── temple rain.json
└── tilesheet.png
├── audio
├── EMT244 1s.json
├── EMT244 1s.mp3
├── EP Chicago.json
├── EP Chicago.mp3
├── Fat Bass.json
├── Fat Bass.mp3
├── aerial.mp3
├── after daydream.mp3
├── angelus.json
├── angelus.mp3
├── atrem rain.json
├── atrem rain.mp3
├── belllong.json
├── belllong.mp3
├── binaural rain.mp3
├── boat.json
├── boat.mp3
├── campfire.json
├── campfire.mp3
├── car passing.json
├── car passing.mp3
├── cicada summer.json
├── cicada summer.mp3
├── cicada temple.mp3
├── city1.json
├── city1.mp3
├── clock.json
├── clock.mp3
├── crossroads.json
├── crossroads.mp3
├── crume1.mp3
├── crystal cave.mp3
├── cuckoo.json
├── cuckoo.mp3
├── damu drums1.json
├── damu drums1.mp3
├── damu drums2.mp3
├── disco tom.json
├── drop.json
├── drop.mp3
├── dusk to night.json
├── dusk to night.mp3
├── equatorial forest.json
├── equatorial forest.mp3
├── factory hall.json
├── factory hall.mp3
├── fan.json
├── fan.mp3
├── fireworks.json
├── fireworks.mp3
├── fishdog.json
├── fishdog.mp3
├── forest.mp3
├── fridge.json
├── fridge.mp3
├── gangas.json
├── gangas.mp3
├── harbor.json
├── harbor.mp3
├── hats line.json
├── hexagonal room.json
├── hexagonal room.mp3
├── highway.json
├── highway.mp3
├── hihat.json
├── hihat.mp3
├── ice bubbles.json
├── ice bubbles.mp3
├── ice cube.json
├── ice cube.mp3
├── ice cube2.json
├── ice cube2.mp3
├── jungle.json
├── jungle.mp3
├── mechanics1.json
├── mechanics1.mp3
├── mechanics2.json
├── mechanics2.mp3
├── mechanics3.json
├── mechanics3.mp3
├── mechanics4.json
├── mechanics4.mp3
├── mechanics5.json
├── mechanics5.mp3
├── mechanics6.json
├── mechanics6.mp3
├── mechanics7.json
├── mechanics7.mp3
├── mechanics8.json
├── mechanics8.mp3
├── medium room.json
├── medium room.mp3
├── motor1.json
├── motor1.mp3
├── motor2.json
├── motor2.mp3
├── motor3.json
├── motor3.mp3
├── ocean crushing.json
├── ocean crushing.mp3
├── old device.json
├── old device.mp3
├── pebbles.json
├── pebbles.mp3
├── pen.json
├── pen.mp3
├── pool1.json
├── pool1.mp3
├── pool2.json
├── pool2.mp3
├── pool3.json
├── pool3.mp3
├── pool4.json
├── pool4.mp3
├── railway station.json
├── railway station.mp3
├── rooster.json
├── rooster.mp3
├── sailing ship.json
├── sailing ship.mp3
├── sea pebbles.json
├── sea pebbles.mp3
├── sea rock.json
├── sea rock.mp3
├── sea sand.json
├── sea sand.mp3
├── singing bowl.json
├── singing bowl.mp3
├── stock exchange.json
├── stock exchange.mp3
├── strangehead.json
├── strangehead.mp3
├── summer night.json
├── summer night.mp3
├── suzu1.json
├── suzu1.mp3
├── suzu2.json
├── suzu2.mp3
├── suzu3.json
├── suzu3.mp3
├── suzu4.json
├── suzu4.mp3
├── theater.json
├── theater.mp3
├── train1.json
├── train1.mp3
├── triangular room.json
├── triangular room.mp3
├── tropical forest.json
├── tropical forest.mp3
├── tunnel.json
├── tunnel.mp3
├── ueno gong1.json
├── ueno gong1.mp3
├── ueno gong2.json
├── ueno gong2.mp3
├── ueno gong3.json
├── ueno gong3.mp3
├── water runoff.json
├── water runoff.mp3
├── water spring.json
├── water spring.mp3
├── water stream.json
├── water stream.mp3
├── white noise.json
├── woodpecker peck.json
├── woodpecker peck.mp3
├── woodpecker squeak.json
├── woodpecker squeak.mp3
└── x_b.mp3
├── build
├── editor.js
├── index.js
├── modular.min.js
├── styles.css
└── synthEdit.css
├── editor.html
├── fonts
├── Big Pixel demo.otf
├── kongtext.ttf
├── november.ttf
└── telegrama.otf
├── img
├── background.png
├── close.png
├── connector
│ ├── in-B-fill.png
│ ├── in-B.png
│ ├── in-G-fill.png
│ ├── in-G.png
│ ├── in-R-fill.png
│ ├── in-R.png
│ ├── in-Y-fill.png
│ ├── in-Y.png
│ ├── out-B-fill.png
│ ├── out-B.png
│ ├── out-G-fill.png
│ ├── out-G.png
│ ├── out-R-fill.png
│ ├── out-R.png
│ ├── out-Y-fill.png
│ └── out-Y.png
├── iconApplication.png
├── iconIR.png
├── iconLoop.png
├── iconSave.png
├── iconShot.png
├── iconSine.png
├── jack-connect.png
├── jack-free.png
├── knob.png
├── knobMark.png
└── syntheditBG.png
├── index.html
├── main.js
├── package.json
├── project.pixelbox
├── settings.json
├── src
├── core
│ ├── AudioConnector.js
│ ├── Buffer.js
│ ├── Button.js
│ ├── Cable.js
│ ├── Connector.js
│ ├── EventConnector.js
│ ├── EventEmitter.js
│ ├── Knob.js
│ ├── MIDI.js
│ ├── Module.js
│ ├── ParamConnector.js
│ ├── Patch.js
│ ├── audioContext.js
│ ├── connectors.js
│ ├── moduleCategories.js
│ ├── modules.js
│ └── utils.js
├── data
│ ├── BufferData.js
│ ├── ProceduralBuffer.js
│ └── dataTypes.js
├── editor.js
├── loaders
│ ├── loadAudioBuffer.js
│ └── sendRequest.js
├── main.js
├── modular.js
├── modules
│ ├── Amp.js
│ ├── AutoBang.js
│ ├── AutoXFade.js
│ ├── Bang.js
│ ├── BufferSlice.js
│ ├── BufferTrim.js
│ ├── Context.js
│ ├── ControlChange.js
│ ├── Convolver.js
│ ├── DateBang.js
│ ├── Delay.js
│ ├── Envelope.js
│ ├── EventDelay.js
│ ├── EventPool.js
│ ├── Fade.js
│ ├── Filter.js
│ ├── FilterMod.js
│ ├── Gain.js
│ ├── LFO.js
│ ├── MidiIn.js
│ ├── ModDelay.js
│ ├── ModPanner.js
│ ├── NoteDetect.js
│ ├── OnLoadBang.js
│ ├── OneShotSampler.js
│ ├── Oscillator.js
│ ├── Panner.js
│ ├── PlaybackRate.js
│ ├── RandomBang.js
│ ├── Sampler.js
│ ├── SlowLFO.js
│ ├── TestModule.js
│ ├── Volume.js
│ ├── XFadeSampler.js
│ ├── index.js
│ └── noteOnFilter.js
├── synthesizers
│ ├── disco
│ │ ├── editor.js
│ │ └── index.js
│ ├── hats
│ │ ├── editor.js
│ │ └── index.js
│ ├── index.js
│ └── noize
│ │ └── index.js
└── ui
│ ├── Panel.js
│ ├── audioEditor.js
│ ├── beforeClose.js
│ ├── bufferLibrary.js
│ ├── buttonGUI.js
│ ├── cableGUI.js
│ ├── connectorGUI.js
│ ├── connectorMenu.js
│ ├── constants.js
│ ├── domUtils.js
│ ├── dropFile.js
│ ├── knobGUI.js
│ ├── menuHeader.js
│ ├── moduleGUI.js
│ ├── moduleLibrary.js
│ ├── moduleManager.js
│ ├── onWindowResize.js
│ ├── overlay.js
│ └── synthEditor
│ ├── Container.js
│ ├── Knob.js
│ ├── Label.js
│ ├── SynthEditorPanel.js
│ ├── TextInput.js
│ ├── constants.js
│ └── index.js
└── tools
├── commands.js
├── commands
└── audio.js
└── settings.json
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Dependency directory
3 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
4 | node_modules
5 |
6 | # Ignore Mac OS files
7 | .DS_Store
8 |
9 | # Ignore archive build
10 | build.zip
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Cedric Stoquer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | # webAudio doodle
5 |
6 | *Modular* is a cool interface to easily create and test webAudio routing configurations.
7 | Similar to an analog modular synthesizer, you add *modules* in a *patch*.
8 | A module can be a simple webAudio feature or a more complex configuration
9 | and can holds some controls in the form of *knob*, *button*, etc.
10 | Then, the modules can simply be connected together with cables.
11 |
12 | UI is strongly influenced by Clavia's *Nord Modular*'s editor.
13 |
14 | 
15 |
16 |
17 | # [Try the Online Demo!](http://cstoquer.github.io/modular/)
18 |
19 | Nota: Save and Load features are disabled in this demo.
20 | Patch can be exported (`top menu > Patch > Export patch`), and imported by drag & dropping a patch file in the window.
21 |
22 | 
23 |
24 | # Features
25 |
26 | ## Built-in buffer editor
27 |
28 | 
29 |
30 | View the waveform of the audio buffers.
31 | Edit loop points, tags and properties of the audio files and save these as a meta file.
32 |
33 | ## Procedural buffer and synth editor
34 |
35 | 
36 |
37 | Generate audio buffer with built-in synthesizers, or program your own synth with their own GUI using a simple `SynthEditor` API.
38 |
39 | ## MIDI support
40 |
41 | If your browser supports webMIDI, you can have your MIDI keyboard or controller send events to modules.
42 |
43 | 
44 |
45 | ## Search audio in the library
46 |
47 | Filter the audio files in library by type and tag.
48 |
49 | 
50 |
--------------------------------------------------------------------------------
/assets/maps.json:
--------------------------------------------------------------------------------
1 | {
2 | "_type": "maps",
3 | "maps": [
4 | {
5 | "w": 8,
6 | "h": 4,
7 | "name": "map",
8 | "sheet": "",
9 | "data": "!!|J"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/assets/patches/aerial drone.json:
--------------------------------------------------------------------------------
1 | {"_type":"modularPatch","version":1,"modules":[{"_type":"LFO","id":0,"x":2,"y":17,"controls":{"frequency":9.189118911891228},"persistent":[0]},{"_type":"Volume","id":1,"x":2,"y":20,"controls":{"volume":41}},{"_type":"Volume","id":2,"x":3,"y":19,"controls":{"volume":68}},{"_type":"Context","id":3,"x":4,"y":26},{"_type":"ModPanner","id":4,"x":3,"y":17},{"_type":"FilterMod","id":5,"x":3,"y":14,"controls":{},"persistent":[1]},{"_type":"Sampler","id":6,"x":3,"y":10,"controls":{"rate":-6.341708542713576}},{"_type":"Buffer","id":7,"x":3,"y":8,"arguments":[{"_type":"BufferData","id":"damu drums1","uri":"audio/damu drums1.mp3","loop":true,"ir":false,"start":0.027,"end":-0.017,"tag":[]}]},{"_type":"RandomBang","id":8,"x":1,"y":8,"controls":{"min":-62.505050505050505,"max":-53.898989898989896}},{"_type":"Buffer","id":9,"x":4,"y":10,"arguments":[{"_type":"BufferData","id":"ueno gong2","uri":"audio/ueno gong2.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","temple","japan"]}]},{"_type":"Buffer","id":10,"x":5,"y":6,"arguments":[{"_type":"BufferData","id":"aerial","uri":"audio/aerial.mp3","loop":true,"ir":false,"start":0.027,"end":-0.033,"tag":[]}]},{"_type":"Delay","id":11,"x":7,"y":14,"controls":{"delay":-21.373737373737356}},{"_type":"Volume","id":12,"x":6,"y":12,"controls":{"volume":38}},{"_type":"Filter","id":13,"x":6,"y":15,"controls":{"cut":-25.212765957446777,"res":-67.6},"persistent":[0]},{"_type":"Volume","id":14,"x":5,"y":11,"controls":{"volume":22}},{"_type":"Sampler","id":15,"x":5,"y":7,"controls":{"rate":58.658291457286424}},{"_type":"Volume","id":16,"x":5,"y":20,"controls":{"volume":-11}},{"_type":"Fade","id":17,"x":2,"y":9,"controls":{"target":-14,"duration":-37.3467336683417}},{"_type":"AutoBang","id":18,"x":1,"y":12,"controls":{"duration":-35}},{"_type":"RandomBang","id":19,"x":4,"y":7,"controls":{"min":-60.50505050505052,"max":-38.898989898989896}},{"_type":"Fade","id":20,"x":2,"y":12,"controls":{"target":-68,"duration":-62.3467336683417}},{"_type":"OneShotSampler","id":21,"x":4,"y":15,"controls":{"volume":-10}},{"_type":"Filter","id":22,"x":4,"y":18,"controls":{"cut":-28.212765957446777,"res":-54.599999999999994},"persistent":[0]},{"_type":"Buffer","id":23,"x":4,"y":11,"arguments":[{"_type":"BufferData","id":"ueno gong1","uri":"audio/ueno gong1.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","temple","japan"]}]},{"_type":"Buffer","id":24,"x":4,"y":12,"arguments":[{"_type":"BufferData","id":"ueno gong3","uri":"audio/ueno gong3.mp3","loop":false,"ir":false,"start":0.029462585034013604,"end":7.940789115646258,"tag":["bell","temple","japan"]}]},{"_type":"EventPool","id":25,"x":4,"y":13,"controls":{}},{"_type":"Convolver","id":26,"x":4,"y":22,"controls":{}},{"_type":"Buffer","id":27,"x":3,"y":23,"arguments":[{"_type":"BufferData","id":"factory hall","uri":"audio/factory hall.mp3","loop":false,"ir":true,"start":0,"end":0,"tag":["real space","room","reverb"]}]}],"cables":["0:OUT--1:IN","1:OUT--4:pan","2:OUT--3:DEST","2:IN--4:OUT","7:data--6:buffer","11:OUT--13:IN","13:OUT--12:IN","12:OUT--11:IN","10:data--15:buffer","15:OUT--14:IN","14:OUT--12:IN","15:OUT--16:IN","16:IN--13:OUT","16:OUT--3:DEST","6:OUT--5:IN","5:OUT--4:IN","5:CUT--17:OUT","18:OUT--20:TRG","20:OUT--5:CUT","8:OUT--17:TRG","19:OUT--21:trigger","21:OUT--22:IN","24:data--25:IN","23:data--25:IN","9:data--25:IN","25:OUT--21:buffer","19:OUT--25:TRG","27:data--26:buffer","3:DEST--26:OUT","26:IN--22:OUT"]}
--------------------------------------------------------------------------------
/assets/patches/ocean.json:
--------------------------------------------------------------------------------
1 | {"_type":"modularPatch","version":1,"modules":[{"_type":"Buffer","id":0,"x":3,"y":6,"arguments":[{"_type":"BufferData","id":"campfire","uri":"audio/campfire.mp3","loop":true,"ir":false,"start":0.02844897959183674,"end":-0.015,"tag":["field","fire","night"]}]},{"_type":"Buffer","id":1,"x":2,"y":11,"arguments":[{"_type":"BufferData","id":"pebbles","uri":"audio/pebbles.mp3","loop":true,"ir":false,"start":0.027066666666666666,"end":17.884533333333337,"tag":["ocean","pebble","wave"]}]},{"_type":"Buffer","id":2,"x":4,"y":11,"arguments":[{"_type":"BufferData","id":"ocean crushing","uri":"audio/ocean crushing.mp3","loop":true,"ir":false,"start":0.02903945578231292,"end":19.20509387755102,"tag":["ocean","wave"]}]},{"_type":"Sampler","id":3,"x":4,"y":12,"controls":{"rate":-10.341708542713576}},{"_type":"Sampler","id":4,"x":3,"y":7,"controls":{"rate":-32.341708542713576}},{"_type":"Sampler","id":5,"x":2,"y":12,"controls":{"rate":-1.341708542713576}},{"_type":"Volume","id":6,"x":4,"y":15,"controls":{"volume":44}},{"_type":"Volume","id":7,"x":3,"y":10,"controls":{"volume":-31}},{"_type":"Volume","id":8,"x":2,"y":15,"controls":{"volume":-29}},{"_type":"Context","id":9,"x":3,"y":22},{"_type":"Filter","id":10,"x":3,"y":13,"controls":{"cut":-7.2127659574468055,"res":-50.599999999999994},"persistent":[1]},{"_type":"Buffer","id":11,"x":1,"y":12,"arguments":[{"_type":"BufferData","id":"sailing ship","uri":"audio/sailing ship.mp3","loop":true,"ir":false,"start":0.029028571428571428,"end":19.877314285714288,"tag":["boat","ocean"]}]},{"_type":"Sampler","id":12,"x":1,"y":13,"controls":{"rate":-37.341708542713576}},{"_type":"Volume","id":13,"x":1,"y":16,"controls":{"volume":-34}}],"cables":["6:OUT--9:DEST","6:IN--3:OUT","2:data--3:buffer","0:data--4:buffer","4:OUT--7:IN","5:OUT--8:IN","1:data--5:buffer","7:OUT--10:IN","10:OUT--9:DEST","8:OUT--9:DEST","12:OUT--13:IN","11:data--12:buffer","13:OUT--9:DEST"]}
--------------------------------------------------------------------------------
/assets/patches/rain forest.json:
--------------------------------------------------------------------------------
1 | {"_type":"modularPatch","version":1,"modules":[{"_type":"Buffer","id":0,"x":1,"y":2,"arguments":[{"_type":"BufferData","id":"forest","uri":"audio/forest.mp3","loop":true,"ir":false,"start":0.027,"end":-0.021,"tag":["field","nature","forest","bird"]}]},{"_type":"Buffer","id":1,"x":0,"y":11,"arguments":[{"_type":"BufferData","id":"water stream","uri":"audio/water stream.mp3","loop":true,"ir":false,"start":0.04086202504683514,"end":-0.035,"tag":["field","water","stream"]}]},{"_type":"SlowLFO","id":2,"x":4,"y":11,"controls":{"frequency":-0.686870222139845},"persistent":[0]},{"_type":"Sampler","id":3,"x":1,"y":3,"controls":{"rate":-29.341708542713576}},{"_type":"Context","id":4,"x":3,"y":20},{"_type":"Volume","id":5,"x":1,"y":6,"controls":{"volume":-57}},{"_type":"Delay","id":6,"x":2,"y":12,"controls":{"delay":-8.373737373737356}},{"_type":"Volume","id":7,"x":2,"y":9,"controls":{"volume":-21}},{"_type":"Filter","id":8,"x":3,"y":6,"controls":{"cut":-51,"res":-68},"persistent":[1]},{"_type":"Sampler","id":9,"x":3,"y":3,"controls":{"rate":-10.341709213640229}},{"_type":"Volume","id":10,"x":3,"y":11,"controls":{"volume":-61}},{"_type":"Buffer","id":11,"x":3,"y":2,"arguments":[{"_type":"BufferData","id":"dusk to night","uri":"audio/dusk to night.mp3","loop":true,"ir":false,"start":0.053526530612244896,"end":12.34141224489796,"tag":["field","nature","insect","night"]}]},{"_type":"Volume","id":12,"x":0,"y":15,"controls":{"volume":-45}},{"_type":"ModPanner","id":13,"x":3,"y":14},{"_type":"Sampler","id":14,"x":0,"y":12,"controls":{"rate":-20.341708542713576}},{"_type":"Panner","id":15,"x":0,"y":18,"controls":{"pan":-27}},{"_type":"Buffer","id":16,"x":5,"y":5,"arguments":[{"_type":"BufferData","id":"equatorial forest","uri":"audio/equatorial forest.mp3","loop":true,"ir":false,"start":0.027978231292517013,"end":10.110699319727892,"tag":["tropical","nature","forest","insect","rain"]}]},{"_type":"Volume","id":17,"x":5,"y":12,"controls":{"volume":10}},{"_type":"Sampler","id":18,"x":5,"y":7,"controls":{"rate":-8.341708542713576}},{"_type":"AutoXFade","id":19,"x":2,"y":17,"controls":{"duration":5.384615384615358,"volume":68}},{"_type":"RandomBang","id":20,"x":2,"y":23,"controls":{"min":-10.50505050505052,"max":41.101010101010104}},{"_type":"Sampler","id":21,"x":1,"y":21,"controls":{"rate":11.658291457286396}},{"_type":"Buffer","id":22,"x":1,"y":20,"arguments":[{"_type":"BufferData","id":"atrem rain","uri":"audio/atrem rain.mp3","loop":true,"ir":false,"start":0.026677551020408165,"end":13.451102040816327,"tag":["rain"]}]},{"_type":"Volume","id":23,"x":1,"y":24,"controls":{"volume":1}}],"cables":["0:data--3:buffer","3:OUT--5:IN","6:OUT--4:DEST","6:OUT--7:IN","7:OUT--6:IN","9:OUT--8:IN","8:OUT--10:IN","11:data--9:buffer","2:OUT--13:pan","10:OUT--13:IN","13:OUT--4:DEST","1:data--14:buffer","14:OUT--12:IN","6:IN--15:OUT","12:OUT--15:IN","16:data--18:buffer","18:OUT--17:IN","17:OUT--4:DEST","5:OUT--19:A","19:OUT--6:IN","20:OUT--19:TRG","22:data--21:buffer","21:OUT--23:IN","23:OUT--19:B"]}
--------------------------------------------------------------------------------
/assets/patches/temple rain.json:
--------------------------------------------------------------------------------
1 | {"_type":"modularPatch","version":1,"modules":[{"_type":"Buffer","id":0,"x":4,"y":6,"arguments":[{"_type":"BufferData","id":"water runoff","uri":"audio/water runoff.mp3","loop":true,"ir":false,"start":0.027946938775510202,"end":13.06485306122449,"tag":["water","cave","drop"]}]},{"_type":"EventPool","id":1,"x":5,"y":6,"controls":{}},{"_type":"XFadeSampler","id":2,"x":5,"y":9,"controls":{"fadeIn":-39.368421052631575,"fadeOut":-39.368421052631575,"volume":24}},{"_type":"Context","id":3,"x":6,"y":25},{"_type":"OnLoadBang","id":4,"x":4,"y":5,"controls":{}},{"_type":"Buffer","id":5,"x":4,"y":7,"arguments":[{"_type":"BufferData","id":"water spring","uri":"audio/water spring.mp3","loop":true,"ir":false,"start":0.038462585034013605,"end":13.398266666666666,"tag":["water","stream","nature"]}]},{"_type":"Buffer","id":6,"x":6,"y":5,"arguments":[{"_type":"BufferData","id":"ueno gong1","uri":"audio/ueno gong1.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","temple","japan"]}]},{"_type":"Buffer","id":7,"x":6,"y":6,"arguments":[{"_type":"BufferData","id":"ueno gong2","uri":"audio/ueno gong2.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","temple","japan"]}]},{"_type":"Buffer","id":8,"x":6,"y":7,"arguments":[{"_type":"BufferData","id":"ueno gong3","uri":"audio/ueno gong3.mp3","loop":false,"ir":false,"start":0.029462585034013604,"end":7.940789115646258,"tag":["bell","temple","japan"]}]},{"_type":"EventPool","id":9,"x":7,"y":7,"controls":{}},{"_type":"RandomBang","id":10,"x":7,"y":4,"controls":{"min":-66.5050505050505,"max":-32.898989898989896}},{"_type":"OneShotSampler","id":11,"x":7,"y":12,"controls":{"volume":-40}},{"_type":"Convolver","id":12,"x":8,"y":15,"controls":{}},{"_type":"Volume","id":13,"x":8,"y":11,"controls":{"volume":65}},{"_type":"Buffer","id":14,"x":3,"y":12,"arguments":[{"_type":"BufferData","id":"atrem rain","uri":"audio/atrem rain.mp3","loop":true,"ir":false,"start":0.026677551020408165,"end":13.451102040816327,"tag":["rain"]}]},{"_type":"Buffer","id":15,"x":7,"y":16,"arguments":[{"_type":"BufferData","id":"factory hall","uri":"audio/factory hall.mp3","loop":false,"ir":true,"start":0,"end":0,"tag":["real space","room","reverb"]}]},{"_type":"Sampler","id":16,"x":3,"y":13,"controls":{"rate":-17.341708542713576}},{"_type":"Amp","id":17,"x":3,"y":16},{"_type":"Volume","id":18,"x":2,"y":13,"controls":{"volume":3}},{"_type":"SlowLFO","id":20,"x":2,"y":10,"controls":{"frequency":45.313129777860155},"persistent":[0]},{"_type":"Volume","id":21,"x":4,"y":19,"controls":{"volume":68}},{"_type":"Buffer","id":22,"x":6,"y":8,"arguments":[{"_type":"BufferData","id":"drop","uri":"audio/drop.mp3","loop":false,"ir":false,"start":0.028299319727891153,"end":0.6508843537414966,"tag":["water","drop"]}]},{"_type":"PlaybackRate","id":23,"x":7,"y":9,"controls":{"rate":-7.341708542713576}},{"_type":"Buffer","id":24,"x":3,"y":21,"arguments":[{"_type":"BufferData","id":"sailing ship","uri":"audio/sailing ship.mp3","loop":true,"ir":false,"start":0.029028571428571428,"end":19.877314285714288,"tag":["boat","ocean"]}]},{"_type":"Sampler","id":25,"x":3,"y":22,"controls":{"rate":-20.341708370189565}},{"_type":"Volume","id":26,"x":3,"y":25,"controls":{"volume":-32}},{"_type":"RandomBang","id":27,"x":5,"y":3,"controls":{"min":63.494949494949495,"max":68}}],"cables":["0:data--1:IN","1:OUT--2:buffer","2:OUT--3:DEST","5:data--1:IN","10:OUT--9:TRG","11:OUT--3:DEST","12:OUT--3:DEST","11:OUT--13:IN","13:OUT--12:IN","15:data--12:buffer","14:data--16:buffer","20:OUT--18:IN","18:OUT--17:MOD","16:OUT--17:IN","17:OUT--21:IN","21:OUT--3:DEST","22:data--9:IN","8:data--9:IN","7:data--9:IN","6:data--9:IN","9:OUT--11:buffer","23:OUT--11:trigger","10:OUT--23:IN","25:OUT--26:IN","26:OUT--3:DEST","24:data--25:buffer","27:OUT--1:TRG","4:OUT--1:TRG"]}
--------------------------------------------------------------------------------
/assets/tilesheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/assets/tilesheet.png
--------------------------------------------------------------------------------
/audio/EMT244 1s.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"EMT244 1s","uri":"audio/EMT244 1s.mp3","loop":false,"ir":true,"start":0.03258650805674443,"end":0.8154518006988665,"tag":["reverb","hardware"]}
--------------------------------------------------------------------------------
/audio/EMT244 1s.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/EMT244 1s.mp3
--------------------------------------------------------------------------------
/audio/EP Chicago.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"EP Chicago","uri":"audio/EP Chicago.mp3","loop":false,"ir":false,"start":0.027742346938775506,"end":1.8682775510204082,"tag":["EP","synth","note","music"]}
--------------------------------------------------------------------------------
/audio/EP Chicago.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/EP Chicago.mp3
--------------------------------------------------------------------------------
/audio/Fat Bass.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"Fat Bass","uri":"audio/Fat Bass.mp3","loop":false,"ir":false,"start":0.03325500900738183,"end":2.2618762991307637,"tag":["synth","note","analog","music"]}
--------------------------------------------------------------------------------
/audio/Fat Bass.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/Fat Bass.mp3
--------------------------------------------------------------------------------
/audio/aerial.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/aerial.mp3
--------------------------------------------------------------------------------
/audio/after daydream.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/after daydream.mp3
--------------------------------------------------------------------------------
/audio/angelus.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"angelus","uri":"audio/angelus.mp3","loop":true,"ir":false,"start":0.02903945578231292,"end":9.580016326530611,"tag":["bell","church","field"]}
--------------------------------------------------------------------------------
/audio/angelus.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/angelus.mp3
--------------------------------------------------------------------------------
/audio/atrem rain.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"atrem rain","uri":"audio/atrem rain.mp3","loop":true,"ir":false,"start":0.026677551020408165,"end":13.451102040816327,"tag":["rain"]}
--------------------------------------------------------------------------------
/audio/atrem rain.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/atrem rain.mp3
--------------------------------------------------------------------------------
/audio/belllong.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"belllong","uri":"audio/belllong.mp3","loop":false,"ir":false,"start":0.027166978458049888,"end":1.67146505574452,"tag":["bell"]}
--------------------------------------------------------------------------------
/audio/belllong.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/belllong.mp3
--------------------------------------------------------------------------------
/audio/binaural rain.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/binaural rain.mp3
--------------------------------------------------------------------------------
/audio/boat.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"boat","uri":"audio/boat.mp3","loop":true,"ir":false,"start":0.029485671768707485,"end":15.700300461781936,"tag":["boat"]}
--------------------------------------------------------------------------------
/audio/boat.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/boat.mp3
--------------------------------------------------------------------------------
/audio/campfire.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"campfire","uri":"audio/campfire.mp3","loop":true,"start":0.02844897959183674,"end":-0.015,"tag":["field","fire","night"]}
--------------------------------------------------------------------------------
/audio/campfire.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/campfire.mp3
--------------------------------------------------------------------------------
/audio/car passing.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"car passing","uri":"audio/car passing.mp3","loop":true,"ir":false,"start":0.02733469387755102,"end":14.153461224489796,"tag":["car","road","field"]}
--------------------------------------------------------------------------------
/audio/car passing.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/car passing.mp3
--------------------------------------------------------------------------------
/audio/cicada summer.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"cicada summer","uri":"audio/cicada summer.mp3","loop":true,"ir":false,"start":0.02695229828042328,"end":7.3718379039115645,"tag":["ciccada","insect","nature","field","summer"]}
--------------------------------------------------------------------------------
/audio/cicada summer.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/cicada summer.mp3
--------------------------------------------------------------------------------
/audio/cicada temple.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/cicada temple.mp3
--------------------------------------------------------------------------------
/audio/city1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"city1","uri":"audio/city1.mp3","loop":true,"ir":false,"start":0.03172789115646258,"end":15.206385034013604,"tag":["city","field"]}
--------------------------------------------------------------------------------
/audio/city1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/city1.mp3
--------------------------------------------------------------------------------
/audio/clock.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"clock","uri":"audio/clock.mp3","loop":true,"ir":false,"start":0.02949795918367347,"end":7.676338775510204,"tag":["mechanic","clock"]}
--------------------------------------------------------------------------------
/audio/clock.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/clock.mp3
--------------------------------------------------------------------------------
/audio/crossroads.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"crossroads","uri":"audio/crossroads.mp3","loop":true,"ir":false,"start":0.02782857142857143,"end":25.420737414965988,"tag":["city","road","field","car"]}
--------------------------------------------------------------------------------
/audio/crossroads.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/crossroads.mp3
--------------------------------------------------------------------------------
/audio/crume1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/crume1.mp3
--------------------------------------------------------------------------------
/audio/crystal cave.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/crystal cave.mp3
--------------------------------------------------------------------------------
/audio/cuckoo.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"cuckoo","uri":"audio/cuckoo.mp3","loop":true,"ir":false,"start":0.08289523809523809,"end":7.010568707482993,"tag":["bird","nature"]}
--------------------------------------------------------------------------------
/audio/cuckoo.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/cuckoo.mp3
--------------------------------------------------------------------------------
/audio/damu drums1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"damu drums1","uri":"audio/damu drums1.mp3","loop":true,"ir":false,"start":0.02720816326530612,"end":5.259695238095238,"tag":["music","drum","hiphop"]}
--------------------------------------------------------------------------------
/audio/damu drums1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/damu drums1.mp3
--------------------------------------------------------------------------------
/audio/damu drums2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/damu drums2.mp3
--------------------------------------------------------------------------------
/audio/disco tom.json:
--------------------------------------------------------------------------------
1 | {
2 | "_type":"ProceduralBuffer",
3 | "synthesizer":"disco",
4 | "params": {
5 | "freq":440,
6 | "mod":300,
7 | "ampDuration": 0.7,
8 | "ampCurve": 0.2,
9 | "modDuration": 0.7,
10 | "modCurve": 0.5
11 | },
12 | "loop":false,
13 | "tag":["music", "synth", "sfx"]
14 | }
--------------------------------------------------------------------------------
/audio/drop.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"drop","uri":"audio/drop.mp3","loop":false,"ir":false,"start":0.028299319727891153,"end":0.6508843537414966,"tag":["water","drop"]}
--------------------------------------------------------------------------------
/audio/drop.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/drop.mp3
--------------------------------------------------------------------------------
/audio/dusk to night.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"dusk to night","uri":"audio/dusk to night.mp3","loop":true,"start":0.053526530612244896,"end":12.34141224489796,"tag":["field","nature","insect","night"]}
--------------------------------------------------------------------------------
/audio/dusk to night.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/dusk to night.mp3
--------------------------------------------------------------------------------
/audio/equatorial forest.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"equatorial forest","uri":"audio/equatorial forest.mp3","loop":true,"ir":false,"start":0.027978231292517013,"end":10.110699319727892,"tag":["tropical","nature","forest","insect","rain"]}
--------------------------------------------------------------------------------
/audio/equatorial forest.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/equatorial forest.mp3
--------------------------------------------------------------------------------
/audio/factory hall.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"factory hall","uri":"audio/factory hall.mp3","loop":false,"ir":true,"start":0,"end":0,"tag":["real space","room","reverb"]}
--------------------------------------------------------------------------------
/audio/factory hall.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/factory hall.mp3
--------------------------------------------------------------------------------
/audio/fan.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"fan","uri":"audio/fan.mp3","loop":true,"ir":false,"start":0.027586394557823135,"end":3.842361904761905,"tag":["motor","fan"]}
--------------------------------------------------------------------------------
/audio/fan.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/fan.mp3
--------------------------------------------------------------------------------
/audio/fireworks.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"fireworks","uri":"audio/fireworks.mp3","loop":true,"ir":false,"start":0.04865714285714286,"end":23.575009523809523,"tag":["outdoor","explosion","field"]}
--------------------------------------------------------------------------------
/audio/fireworks.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/fireworks.mp3
--------------------------------------------------------------------------------
/audio/fishdog.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"fishdog","uri":"audio/fishdog.mp3","loop":true,"ir":false,"start":0.028334693877551023,"end":13.91088163265306,"tag":["noise"]}
--------------------------------------------------------------------------------
/audio/fishdog.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/fishdog.mp3
--------------------------------------------------------------------------------
/audio/forest.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/forest.mp3
--------------------------------------------------------------------------------
/audio/fridge.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"fridge","uri":"audio/fridge.mp3","loop":true,"ir":false,"start":0.028473469387755104,"end":8.510008163265304,"tag":["motor","house"]}
--------------------------------------------------------------------------------
/audio/fridge.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/fridge.mp3
--------------------------------------------------------------------------------
/audio/gangas.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"gangas","uri":"audio/gangas.mp3","loop":true,"ir":false,"start":0.031857142857142855,"end":17.4386,"tag":["tropical","field","nature","insect"]}
--------------------------------------------------------------------------------
/audio/gangas.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/gangas.mp3
--------------------------------------------------------------------------------
/audio/harbor.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"harbor","uri":"audio/harbor.mp3","loop":true,"ir":false,"start":0.029338775510204078,"end":15.632677551020407,"tag":["ocean","boat","wave"]}
--------------------------------------------------------------------------------
/audio/harbor.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/harbor.mp3
--------------------------------------------------------------------------------
/audio/hats line.json:
--------------------------------------------------------------------------------
1 | {
2 | "_type":"ProceduralBuffer",
3 | "synthesizer":"hats",
4 | "params": {
5 | "uri":"audio/hihat.mp3",
6 | "offset": 1070,
7 | "pattern":"O.......C.....ccO.......C...o...",
8 | "polyphony":2,
9 | "tempo":200,
10 | "close":0.3,
11 | "accent":0.2
12 | },
13 | "loop":true,
14 | "tag":["music", "hats"]
15 | }
--------------------------------------------------------------------------------
/audio/hexagonal room.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"hexagonal room","uri":"audio/hexagonal room.mp3","loop":false,"ir":true,"start":0.016718367346938778,"end":3.3381006802721087,"tag":["real space","reverb","room"]}
--------------------------------------------------------------------------------
/audio/hexagonal room.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/hexagonal room.mp3
--------------------------------------------------------------------------------
/audio/highway.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"highway","uri":"audio/highway.mp3","loop":true,"ir":false,"start":0.030824489795918363,"end":10.010644897959184,"tag":["road","motor"]}
--------------------------------------------------------------------------------
/audio/highway.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/highway.mp3
--------------------------------------------------------------------------------
/audio/hihat.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"hihat","uri":"audio/hihat.mp3","loop":false,"ir":false,"start":0.02475141667995323,"end":0.4953959183673469,"tag":[]}
--------------------------------------------------------------------------------
/audio/hihat.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/hihat.mp3
--------------------------------------------------------------------------------
/audio/ice bubbles.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"ice bubbles","uri":"audio/ice bubbles.mp3","loop":true,"ir":false,"start":0.02834285714285715,"end":12.941213605442178,"tag":["ice","bubble"]}
--------------------------------------------------------------------------------
/audio/ice bubbles.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/ice bubbles.mp3
--------------------------------------------------------------------------------
/audio/ice cube.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"ice cube","uri":"audio/ice cube.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["ice"]}
--------------------------------------------------------------------------------
/audio/ice cube.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/ice cube.mp3
--------------------------------------------------------------------------------
/audio/ice cube2.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"ice cube2","uri":"audio/ice cube2.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["ice"]}
--------------------------------------------------------------------------------
/audio/ice cube2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/ice cube2.mp3
--------------------------------------------------------------------------------
/audio/jungle.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"jungle","uri":"audio/jungle.mp3","loop":true,"ir":false,"start":0.02907891156462585,"end":19.230185034013605,"tag":["jungle","field","nature"]}
--------------------------------------------------------------------------------
/audio/jungle.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/jungle.mp3
--------------------------------------------------------------------------------
/audio/mechanics1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics1","uri":"audio/mechanics1.mp3","loop":true,"ir":false,"start":0.027323809523809525,"end":3.9249727891156456,"tag":["industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics1.mp3
--------------------------------------------------------------------------------
/audio/mechanics2.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics2","uri":"audio/mechanics2.mp3","loop":true,"ir":false,"start":0.027609523809523813,"end":5.802854421768708,"tag":["industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics2.mp3
--------------------------------------------------------------------------------
/audio/mechanics3.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics3","uri":"audio/mechanics3.mp3","loop":true,"ir":false,"start":0.0275265306122449,"end":7.302941496598638,"tag":["industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics3.mp3
--------------------------------------------------------------------------------
/audio/mechanics4.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics4","uri":"audio/mechanics4.mp3","loop":true,"ir":false,"start":0.02721073672524565,"end":4.144521023478836,"tag":["inustrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics4.mp3
--------------------------------------------------------------------------------
/audio/mechanics5.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics5","uri":"audio/mechanics5.mp3","loop":true,"ir":false,"start":0.027477551020408164,"end":5.15204081632653,"tag":["industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics5.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics5.mp3
--------------------------------------------------------------------------------
/audio/mechanics6.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics6","uri":"audio/mechanics6.mp3","loop":true,"ir":false,"start":0.026360544217687073,"end":4.023673469387755,"tag":["industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics6.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics6.mp3
--------------------------------------------------------------------------------
/audio/mechanics7.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics7","uri":"audio/mechanics7.mp3","loop":true,"ir":false,"start":0.030775510204081636,"end":9.813284353741498,"tag":["industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics7.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics7.mp3
--------------------------------------------------------------------------------
/audio/mechanics8.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"mechanics8","uri":"audio/mechanics8.mp3","loop":true,"ir":false,"start":0.031855782312925164,"end":13.238155102040816,"tag":["industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/mechanics8.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/mechanics8.mp3
--------------------------------------------------------------------------------
/audio/medium room.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"medium room","uri":"audio/medium room.mp3","loop":false,"ir":true,"start":0,"end":0,"tag":["reverb","real space","room"]}
--------------------------------------------------------------------------------
/audio/medium room.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/medium room.mp3
--------------------------------------------------------------------------------
/audio/motor1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"motor1","uri":"audio/motor1.mp3","loop":true,"ir":false,"start":0.02782040816326531,"end":3.7326367346938776,"tag":["motor","industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/motor1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/motor1.mp3
--------------------------------------------------------------------------------
/audio/motor2.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"motor2","uri":"audio/motor2.mp3","loop":true,"ir":false,"start":0.027827210884353734,"end":4.705753741496598,"tag":["motor","industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/motor2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/motor2.mp3
--------------------------------------------------------------------------------
/audio/motor3.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"motor3","uri":"audio/motor3.mp3","loop":true,"ir":false,"start":0.027387755102040817,"end":4.294040816326532,"tag":["motor","industrial","mechanic"]}
--------------------------------------------------------------------------------
/audio/motor3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/motor3.mp3
--------------------------------------------------------------------------------
/audio/ocean crushing.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"ocean crushing","uri":"audio/ocean crushing.mp3","loop":true,"ir":false,"start":0.02903945578231292,"end":19.20509387755102,"tag":["ocean","wave"]}
--------------------------------------------------------------------------------
/audio/ocean crushing.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/ocean crushing.mp3
--------------------------------------------------------------------------------
/audio/old device.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"old device","uri":"audio/old device.mp3","loop":true,"ir":false,"start":0.027460950491307635,"end":2.235817799272487,"tag":["noise"]}
--------------------------------------------------------------------------------
/audio/old device.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/old device.mp3
--------------------------------------------------------------------------------
/audio/pebbles.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"pebbles","uri":"audio/pebbles.mp3","loop":true,"ir":false,"start":0.027066666666666666,"end":17.884533333333337,"tag":["ocean","pebble","wave"]}
--------------------------------------------------------------------------------
/audio/pebbles.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/pebbles.mp3
--------------------------------------------------------------------------------
/audio/pen.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"pen","uri":"audio/pen.mp3","loop":true,"ir":false,"start":0.036514285714285714,"end":4.882885714285714,"tag":["pen"]}
--------------------------------------------------------------------------------
/audio/pen.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/pen.mp3
--------------------------------------------------------------------------------
/audio/pool1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"pool1","uri":"audio/pool1.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["pool","ball"]}
--------------------------------------------------------------------------------
/audio/pool1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/pool1.mp3
--------------------------------------------------------------------------------
/audio/pool2.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"pool2","uri":"audio/pool2.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["pool","ball"]}
--------------------------------------------------------------------------------
/audio/pool2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/pool2.mp3
--------------------------------------------------------------------------------
/audio/pool3.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"pool3","uri":"audio/pool3.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["pool","ball"]}
--------------------------------------------------------------------------------
/audio/pool3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/pool3.mp3
--------------------------------------------------------------------------------
/audio/pool4.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"pool4","uri":"audio/pool4.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["pool","ball"]}
--------------------------------------------------------------------------------
/audio/pool4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/pool4.mp3
--------------------------------------------------------------------------------
/audio/railway station.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"railway station","uri":"audio/railway station.mp3","loop":true,"ir":false,"start":0.027697959183673466,"end":18.30930612244898,"tag":["train"]}
--------------------------------------------------------------------------------
/audio/railway station.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/railway station.mp3
--------------------------------------------------------------------------------
/audio/rooster.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"rooster","uri":"audio/rooster.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bird"]}
--------------------------------------------------------------------------------
/audio/rooster.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/rooster.mp3
--------------------------------------------------------------------------------
/audio/sailing ship.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"sailing ship","uri":"audio/sailing ship.mp3","loop":true,"ir":false,"start":0.029028571428571428,"end":19.877314285714288,"tag":["boat","ocean"]}
--------------------------------------------------------------------------------
/audio/sailing ship.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/sailing ship.mp3
--------------------------------------------------------------------------------
/audio/sea pebbles.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"sea pebbles","uri":"audio/sea pebbles.mp3","loop":true,"ir":false,"start":0.031229931972789112,"end":26.05255238095238,"tag":["ocean","pebble","wave"]}
--------------------------------------------------------------------------------
/audio/sea pebbles.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/sea pebbles.mp3
--------------------------------------------------------------------------------
/audio/sea rock.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"sea rock","uri":"audio/sea rock.mp3","loop":true,"ir":false,"start":0.02784757889266818,"end":15.686376672335603,"tag":["ocean"]}
--------------------------------------------------------------------------------
/audio/sea rock.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/sea rock.mp3
--------------------------------------------------------------------------------
/audio/sea sand.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"sea sand","uri":"audio/sea sand.mp3","loop":true,"ir":false,"start":0.030599999999999995,"end":21.737333333333336,"tag":["ocean","wave"]}
--------------------------------------------------------------------------------
/audio/sea sand.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/sea sand.mp3
--------------------------------------------------------------------------------
/audio/singing bowl.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"singing bowl","uri":"audio/singing bowl.mp3","loop":false,"ir":false,"start":0.0270952380952381,"end":14.814127891156463,"tag":["bell"]}
--------------------------------------------------------------------------------
/audio/singing bowl.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/singing bowl.mp3
--------------------------------------------------------------------------------
/audio/stock exchange.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"stock exchange","uri":"audio/stock exchange.mp3","loop":true,"ir":false,"start":0.027342857142857144,"end":18.08022857142857,"tag":["field","room","city","people"]}
--------------------------------------------------------------------------------
/audio/stock exchange.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/stock exchange.mp3
--------------------------------------------------------------------------------
/audio/strangehead.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"strangehead","uri":"audio/strangehead.mp3","loop":true,"ir":false,"start":0.028824489795918364,"end":8.36914693877551,"tag":["noise"]}
--------------------------------------------------------------------------------
/audio/strangehead.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/strangehead.mp3
--------------------------------------------------------------------------------
/audio/summer night.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"summer night","uri":"audio/summer night.mp3","loop":true,"start":0.05488163265306123,"end":19.46671836734694,"tag":["field","nature","insect","night"]}
--------------------------------------------------------------------------------
/audio/summer night.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/summer night.mp3
--------------------------------------------------------------------------------
/audio/suzu1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"suzu1","uri":"audio/suzu1.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","wind","chime"]}
--------------------------------------------------------------------------------
/audio/suzu1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/suzu1.mp3
--------------------------------------------------------------------------------
/audio/suzu2.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"suzu2","uri":"audio/suzu2.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","wind","chime"]}
--------------------------------------------------------------------------------
/audio/suzu2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/suzu2.mp3
--------------------------------------------------------------------------------
/audio/suzu3.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"suzu3","uri":"audio/suzu3.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","wind","chime"]}
--------------------------------------------------------------------------------
/audio/suzu3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/suzu3.mp3
--------------------------------------------------------------------------------
/audio/suzu4.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"suzu4","uri":"audio/suzu4.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","wind","chime"]}
--------------------------------------------------------------------------------
/audio/suzu4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/suzu4.mp3
--------------------------------------------------------------------------------
/audio/theater.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"theater","uri":"audio/theater.mp3","loop":true,"ir":false,"start":0.028122448979591836,"end":8.27275918367347,"tag":["people","room","talking","theater"]}
--------------------------------------------------------------------------------
/audio/theater.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/theater.mp3
--------------------------------------------------------------------------------
/audio/train1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"train1","uri":"audio/train1.mp3","loop":true,"ir":false,"start":0.026632618811413447,"end":17.594976664068405,"tag":["train"]}
--------------------------------------------------------------------------------
/audio/train1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/train1.mp3
--------------------------------------------------------------------------------
/audio/triangular room.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"triangular room","uri":"audio/triangular room.mp3","loop":false,"ir":true,"start":0,"end":0,"tag":["reverb","room","real space"]}
--------------------------------------------------------------------------------
/audio/triangular room.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/triangular room.mp3
--------------------------------------------------------------------------------
/audio/tropical forest.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"tropical forest","uri":"audio/tropical forest.mp3","loop":true,"ir":false,"start":0.031608134920634925,"end":25.260167115457296,"tag":["forest","nature","tropical","bird","insect"]}
--------------------------------------------------------------------------------
/audio/tropical forest.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/tropical forest.mp3
--------------------------------------------------------------------------------
/audio/tunnel.json:
--------------------------------------------------------------------------------
1 | {
2 | "_type":"BufferData",
3 | "id":"tunnel",
4 | "uri":"audio/tunnel.mp3",
5 | "loop":false,
6 | "ir":true,
7 | "start":0,
8 | "end":0,
9 | "tag":["reverb","real space"]
10 | }
--------------------------------------------------------------------------------
/audio/tunnel.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/tunnel.mp3
--------------------------------------------------------------------------------
/audio/ueno gong1.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"ueno gong1","uri":"audio/ueno gong1.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","temple","japan"]}
--------------------------------------------------------------------------------
/audio/ueno gong1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/ueno gong1.mp3
--------------------------------------------------------------------------------
/audio/ueno gong2.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"ueno gong2","uri":"audio/ueno gong2.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bell","temple","japan"]}
--------------------------------------------------------------------------------
/audio/ueno gong2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/ueno gong2.mp3
--------------------------------------------------------------------------------
/audio/ueno gong3.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"ueno gong3","uri":"audio/ueno gong3.mp3","loop":false,"ir":false,"start":0.029462585034013604,"end":7.940789115646258,"tag":["bell","temple","japan"]}
--------------------------------------------------------------------------------
/audio/ueno gong3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/ueno gong3.mp3
--------------------------------------------------------------------------------
/audio/water runoff.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"water runoff","uri":"audio/water runoff.mp3","loop":true,"ir":false,"start":0.027946938775510202,"end":13.06485306122449,"tag":["water","cave","drop"]}
--------------------------------------------------------------------------------
/audio/water runoff.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/water runoff.mp3
--------------------------------------------------------------------------------
/audio/water spring.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"water spring","uri":"audio/water spring.mp3","loop":true,"ir":false,"start":0.038462585034013605,"end":13.398266666666666,"tag":["water","stream","nature"]}
--------------------------------------------------------------------------------
/audio/water spring.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/water spring.mp3
--------------------------------------------------------------------------------
/audio/water stream.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"water stream","uri":"audio/water stream.mp3","loop":true,"start":0.04086202504683514,"end":-0.035,"tag":["field","water","stream"]}
--------------------------------------------------------------------------------
/audio/water stream.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/water stream.mp3
--------------------------------------------------------------------------------
/audio/white noise.json:
--------------------------------------------------------------------------------
1 | {
2 | "_type":"ProceduralBuffer",
3 | "synthesizer":"noize",
4 | "params":{
5 | "length":0.5
6 | },
7 | "loop":true,
8 | "tag":["noise"]
9 | }
--------------------------------------------------------------------------------
/audio/woodpecker peck.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"woodpecker peck","uri":"audio/woodpecker peck.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bird"]}
--------------------------------------------------------------------------------
/audio/woodpecker peck.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/woodpecker peck.mp3
--------------------------------------------------------------------------------
/audio/woodpecker squeak.json:
--------------------------------------------------------------------------------
1 | {"_type":"BufferData","id":"woodpecker squeak","uri":"audio/woodpecker squeak.mp3","loop":false,"ir":false,"start":0,"end":0,"tag":["bird"]}
--------------------------------------------------------------------------------
/audio/woodpecker squeak.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/woodpecker squeak.mp3
--------------------------------------------------------------------------------
/audio/x_b.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/audio/x_b.mp3
--------------------------------------------------------------------------------
/build/synthEdit.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "unispace";
3 | src: url("../fonts/telegrama.otf") format("woff");
4 | }
5 |
6 | .synthEdit-root {
7 | background-color: #313536;
8 | background-image: url(../img/syntheditBG.png);
9 | position: relative;
10 | overflow: hidden;
11 | }
12 |
13 | .synthEdit-header {
14 | display: flex;
15 | flex-direction: row;
16 | flex-wrap: nowrap;
17 | }
18 |
19 | .synthEdit-header-spacer {
20 | display: inline-block;
21 | flex-grow: 1;
22 | }
23 |
24 | .synthedit-header-synthName {
25 | user-select: none;
26 | cursor: default;
27 | display: inline-block;
28 | height: 20px;
29 | border-radius: 3px;
30 | background-color: #AAA;
31 | margin: 2px;
32 | vertical-align: top;
33 | line-height: 20px;
34 | padding: 0 6px;
35 | }
36 |
37 | .synthedit-button {
38 | display: inline-block;
39 | width: 20px;
40 | height: 20px;
41 | border-radius: 3px;
42 | background-repeat: no-repeat;
43 | background-position: 50% 50%;
44 | background-color: #AAA;
45 | margin: 2px;
46 | }
47 |
48 | .synthedit-button:hover {
49 | background-color: #FF0;
50 | }
51 |
52 |
53 | .synthEdit-container {
54 | border: solid 4px #BBB;
55 | margin: 6px;
56 | /*padding: 6px;*/
57 | position: absolute;
58 | border-radius: 8px;
59 | }
60 |
61 | .synthEdit-knob {
62 | width: 32px;
63 | height: 32px;
64 | position: absolute;
65 | cursor: pointer;
66 | }
67 |
68 | .synthEdit-knob-canvas {
69 | width: 32px;
70 | height: 32px;
71 | }
72 |
73 | .synthEdit-textInput {
74 | position: absolute;
75 | height: 32px;
76 | font-family: "unispace";
77 | font-size: 13px;
78 | background-color: #242f38;
79 | color: #9cebff;
80 | border-radius: 4px;
81 | border-color: #5f5e5e;
82 | }
83 |
84 | .synthEdit-toggle {
85 | width: 28px;
86 | height: 28px;
87 | border: solid 2px;
88 | position: absolute;
89 | }
90 |
91 | .synthEdit-label {
92 | user-select: none;
93 | cursor: default;
94 | position: absolute;
95 | font-family: "unispace";
96 | font-size: 13px;
97 | color: #BBB;
98 | text-align: center;
99 | height: 16px;
100 | line-height: 17px;
101 | vertical-align: middle;
102 | }
103 |
--------------------------------------------------------------------------------
/editor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Modular
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/fonts/Big Pixel demo.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/fonts/Big Pixel demo.otf
--------------------------------------------------------------------------------
/fonts/kongtext.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/fonts/kongtext.ttf
--------------------------------------------------------------------------------
/fonts/november.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/fonts/november.ttf
--------------------------------------------------------------------------------
/fonts/telegrama.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/fonts/telegrama.otf
--------------------------------------------------------------------------------
/img/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/background.png
--------------------------------------------------------------------------------
/img/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/close.png
--------------------------------------------------------------------------------
/img/connector/in-B-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-B-fill.png
--------------------------------------------------------------------------------
/img/connector/in-B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-B.png
--------------------------------------------------------------------------------
/img/connector/in-G-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-G-fill.png
--------------------------------------------------------------------------------
/img/connector/in-G.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-G.png
--------------------------------------------------------------------------------
/img/connector/in-R-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-R-fill.png
--------------------------------------------------------------------------------
/img/connector/in-R.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-R.png
--------------------------------------------------------------------------------
/img/connector/in-Y-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-Y-fill.png
--------------------------------------------------------------------------------
/img/connector/in-Y.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/in-Y.png
--------------------------------------------------------------------------------
/img/connector/out-B-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-B-fill.png
--------------------------------------------------------------------------------
/img/connector/out-B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-B.png
--------------------------------------------------------------------------------
/img/connector/out-G-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-G-fill.png
--------------------------------------------------------------------------------
/img/connector/out-G.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-G.png
--------------------------------------------------------------------------------
/img/connector/out-R-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-R-fill.png
--------------------------------------------------------------------------------
/img/connector/out-R.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-R.png
--------------------------------------------------------------------------------
/img/connector/out-Y-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-Y-fill.png
--------------------------------------------------------------------------------
/img/connector/out-Y.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/connector/out-Y.png
--------------------------------------------------------------------------------
/img/iconApplication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/iconApplication.png
--------------------------------------------------------------------------------
/img/iconIR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/iconIR.png
--------------------------------------------------------------------------------
/img/iconLoop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/iconLoop.png
--------------------------------------------------------------------------------
/img/iconSave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/iconSave.png
--------------------------------------------------------------------------------
/img/iconShot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/iconShot.png
--------------------------------------------------------------------------------
/img/iconSine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/iconSine.png
--------------------------------------------------------------------------------
/img/jack-connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/jack-connect.png
--------------------------------------------------------------------------------
/img/jack-free.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/jack-free.png
--------------------------------------------------------------------------------
/img/knob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/knob.png
--------------------------------------------------------------------------------
/img/knobMark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/knobMark.png
--------------------------------------------------------------------------------
/img/syntheditBG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cstoquer/modular/d53f30aa2761eadbc8b19e0a4f2f32f953c85989/img/syntheditBG.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | modular
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | var electron = require('electron');
2 | var app = electron.app; // Module to control application life.
3 | var BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | // Keep a global reference of the window object, if you don't, the window will
7 | // be closed automatically when the JavaScript object is garbage collected.
8 | var mainWindow;
9 |
10 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
11 | function createWindow () {
12 | // Create the browser window.
13 | mainWindow = new BrowserWindow({
14 | width: 1000, // /!\ window size with OS border (not the inner size)
15 | height: 800,
16 | icon: __dirname + '/img/iconApplication.png',
17 | frame: true // add OS window border
18 | });
19 |
20 | // remove menu bar
21 | mainWindow.setMenu(null);
22 |
23 | // and load the index.html of the app.
24 | // console.log(__dirname)
25 | // mainWindow.loadURL("file://${__dirname}/index.html")
26 | mainWindow.loadURL("file://" + __dirname + "/editor.html");
27 |
28 | // Open the DevTools.
29 | // mainWindow.webContents.openDevTools();
30 |
31 | // Emitted when the window is closed.
32 | mainWindow.on('closed', function () {
33 | // Dereference the window object, usually you would store windows
34 | // in an array if your app supports multi windows, this is the time
35 | // when you should delete the corresponding element.
36 | mainWindow = null;
37 | })
38 | }
39 |
40 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
41 | // This method will be called when Electron has finished
42 | // initialization and is ready to create browser windows.
43 | // Some APIs can only be used after this event occurs.
44 | app.on('ready', createWindow);
45 |
46 | // Quit when all windows are closed.
47 | app.on('window-all-closed', function () {
48 | // On OS X it is common for applications and their menu bar
49 | // to stay active until the user quits explicitly with Cmd + Q
50 | if (process.platform !== 'darwin') app.quit();
51 | })
52 |
53 | app.on('activate', function () {
54 | // On OS X it's common to re-create a window in the app when the
55 | // dock icon is clicked and there are no other windows open.
56 | if (mainWindow === null) createWindow();
57 | })
58 |
59 | // In this file you can include the rest of your app's specific main process
60 | // code. You can also put them in separate files and require them here.
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "modular",
3 | "version": "1.0.0",
4 | "description": "modular webAudio",
5 | "author": "Cedric Stoquer",
6 | "main": "main.js",
7 | "scripts": {
8 | "start": "pixelbox",
9 | "build": "browserify src/modular.js | derequire | uglifyjs --compress --mangle > build/modular.min.js",
10 | "editor": "browserify src/editor.js | derequire | replace-stream /_REQUIRE_/ require | uglifyjs --compress --mangle > build/editor.js",
11 | "build": "npm run editor && electron-packager . --icon=icon.ico"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/cstoquer/modular.git"
16 | },
17 | "dependencies": {
18 | },
19 | "devDependencies": {
20 | "pixelbox": "1.0.4",
21 | "browserify": "13.0.0",
22 | "uglify-js": "2.7.5",
23 | "derequire": "2.0.6",
24 | "replace-stream": "0.0.1",
25 | "electron": "^1.3.4"
26 | },
27 | "license": "MIT"
28 | }
29 |
--------------------------------------------------------------------------------
/project.pixelbox:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Modular",
3 | "version": "2.0.0",
4 | "screen": {
5 | "width": 800,
6 | "height": 600,
7 | "pixelSize": {
8 | "width": 1,
9 | "height": 1
10 | },
11 | "fullscreen": false,
12 | "resizable": true
13 | },
14 |
15 | "tileSize": [8, 8],
16 |
17 | "palette": [
18 | "rgba(0, 0, 0, 0)",
19 | "rgba(0, 0, 0, 0.1)"
20 | ],
21 |
22 | "controls": {
23 | "up": 38,
24 | "down": 40,
25 | "left": 37,
26 | "right": 39,
27 | "A": 32,
28 | "B": 88
29 | },
30 |
31 | "touchEvent": {
32 | "multiTouch": false,
33 | "disableContextMenu": false,
34 | "hideMousePointer": false
35 | },
36 |
37 | "loaderColors": [0, 1],
38 |
39 | "components": {
40 | "pixelboxCore": false,
41 | "keyboard": false,
42 | "TINA": false,
43 | "AudioManager": false
44 | }
45 | }
--------------------------------------------------------------------------------
/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "screen": {
3 | "width": 64,
4 | "height": 10,
5 | "pixelSize": [2, 2],
6 | "fullscreen": false
7 | },
8 |
9 | "tileSize": [8, 8],
10 |
11 | "palette": [
12 | "rgba(0, 0, 0, 0)",
13 | "rgba(0, 0, 0, 0.1)"
14 | ],
15 |
16 | "controls": {
17 | "up": 38,
18 | "down": 40,
19 | "left": 37,
20 | "right": 39,
21 | "A": 32,
22 | "B": 88
23 | },
24 |
25 | "touchEvent": {
26 | "multiTouch": false,
27 | "disableContextMenu": false,
28 | "hideMousePointer": false
29 | },
30 |
31 | "loaderColors": [0, 1],
32 |
33 | "components": {
34 | "pixelboxCore": false,
35 | "keyboard": false,
36 | "TINA": false,
37 | "AudioManager": false
38 | }
39 | }
--------------------------------------------------------------------------------
/src/core/AudioConnector.js:
--------------------------------------------------------------------------------
1 | var connectors = require('./connectors');
2 | var Connector = require('./Connector');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function AudioConnector(module, id, descriptor) {
6 | this.endPoint = module; // redefined at bind
7 | this.connections = [];
8 | Connector.call(this, module, id, descriptor);
9 | }
10 | inherits(AudioConnector, Connector);
11 | AudioConnector.prototype.color = '#fd870e';
12 | AudioConnector.prototype.type = 'audio';
13 |
14 | AudioConnector.prototype.bind = function (module, id, descriptor) {
15 | // find endPoint reference
16 | var endPoint = descriptor.endPoint.split('.');
17 | this.endPoint = module;
18 | for (var i = 0; i < endPoint.length; i++) {
19 | this.endPoint = this.endPoint[endPoint[i]];
20 | }
21 |
22 | // reconnect previous connections if endPoint changed
23 | var connections = this.connections;
24 | this.connections = [];
25 | for (var i = connections.length - 1; i >= 0; i--) {
26 | this.connect(connections[i]);
27 | }
28 | };
29 |
30 | AudioConnector.prototype.connect = function (connector) {
31 | Connector.prototype.connect.call(this, connector);
32 | // save connections for rebind
33 | this._addConnection(connector);
34 | connector._addConnection(this);
35 | };
36 |
37 | AudioConnector.prototype.disconnect = function (connector) {
38 | Connector.prototype.disconnect.call(this, connector);
39 | // remove saved connections
40 | this._removeConnection(connector);
41 | connector._removeConnection(this);
42 | };
43 |
44 | AudioConnector.prototype._addConnection = function (connector) {
45 | this.connections.push(connector);
46 | };
47 |
48 | AudioConnector.prototype._removeConnection = function (connector) {
49 | var index = this.connections.indexOf(connector);
50 | if (index === -1) return; // happen to the second connector when a cable is removed
51 | this.connections.splice(index, 1);
52 | };
53 |
54 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
55 | function AudioInput(module, id, descriptor) {
56 | AudioConnector.call(this, module, id, descriptor);
57 | }
58 | inherits(AudioInput, AudioConnector);
59 | AudioInput.prototype.cssClassName = 'audioIn';
60 | AudioInput.prototype.way = 'input';
61 | connectors.register(AudioInput, 'input', 'audio');
62 |
63 | AudioInput.prototype.connect = function (connector) {
64 | AudioConnector.prototype.connect.call(this, connector);
65 | connector.endPoint.connect(this.endPoint);
66 | };
67 |
68 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
69 | function AudioOutput(module, id, descriptor) {
70 | AudioConnector.call(this, module, id, descriptor);
71 | }
72 | inherits(AudioOutput, AudioConnector);
73 | AudioOutput.prototype.cssClassName = 'audioOut';
74 | AudioOutput.prototype.way = 'output';
75 | connectors.register(AudioOutput, 'output', 'audio');
76 |
77 | AudioOutput.prototype.connect = function (connector) {
78 | AudioConnector.prototype.connect.call(this, connector);
79 | this.endPoint.connect(connector.endPoint);
80 | };
81 |
82 | AudioOutput.prototype.disconnect = function (connector) {
83 | AudioConnector.prototype.disconnect.call(this, connector);
84 | this.endPoint.disconnect(connector.endPoint);
85 | };
86 |
87 | // AudioParam can be connected to AudioNode output
88 | // the two following functions allow this
89 | AudioOutput.prototype.isCompatible = function (connector) {
90 | if (connector.type === 'param' && connector.way === 'input') return true;
91 | return AudioConnector.prototype.isCompatible.call(this, connector);
92 | };
--------------------------------------------------------------------------------
/src/core/Buffer.js:
--------------------------------------------------------------------------------
1 | var Module = require('./Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function Buffer(bufferData) {
5 | Module.call(this, bufferData);
6 |
7 | var className = 'bufferAudio';
8 | if (bufferData.ir) className = 'bufferIR';
9 | if (bufferData.type === 'ProceduralBuffer') className = 'bufferProcedural';
10 | this.addClassName(className);
11 | this.setTitle(bufferData.id);
12 |
13 | this.buffer = null;
14 |
15 | if (!bufferData) return console.warn('Buffer module should be initialized with buffer data.');
16 |
17 | var t = this;
18 |
19 | // load buffer
20 | bufferData.loadAudioBuffer(function onBufferLoaded(error) {
21 | if (error) {
22 | console.error('Could not load buffer', bufferData, error);
23 | t.addClassName('bufferFailed');
24 | return;
25 | }
26 | t.buffer = bufferData;
27 | t.onLoad();
28 | });
29 | }
30 | inherits(Buffer, Module);
31 |
32 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
33 | Buffer.prototype.onConnect = function (connector) {
34 | if (!this.buffer) return;
35 | this.$data.emitTo(connector, { _type: 'buffer', buffer: this.buffer });
36 | };
37 |
38 | Buffer.prototype.onLoad = function () {
39 | this.$data.emit({ _type: 'buffer', buffer: this.buffer });
40 | };
41 |
42 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
43 | Buffer.prototype.descriptor = {
44 | type: 'Buffer',
45 | size: 1,
46 | outputs: { data: { type: 'event', x:5, y:0, label: null, onConnect: 'onConnect' } }
47 | };
48 |
49 | module.exports = Buffer;
50 |
--------------------------------------------------------------------------------
/src/core/Button.js:
--------------------------------------------------------------------------------
1 |
2 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
3 | /** Button
4 | *
5 | * @author Cedric Stoquer
6 | */
7 | function Button(module, id, descriptor) {
8 | this.initGUI(module, id, descriptor);
9 |
10 | // init references
11 | this.bind(module, id, descriptor);
12 | }
13 |
14 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15 | Button.prototype.initGUI = function (module, id, descriptor) {};
16 | Button.prototype.setTitle = function (text) {};
17 |
18 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
19 | Button.prototype.bind = function (module, id, descriptor) {
20 | // button action
21 | var endPointDescription = descriptor.endPoint;
22 | if (!endPointDescription) return;
23 |
24 | // find endPoint function
25 | var endPoint = module;
26 | endPointDescription = endPointDescription.split('.');
27 | var funcName = endPointDescription.pop();
28 | for (var i = 0; i < endPointDescription.length; i++) {
29 | endPoint = endPoint[endPointDescription[i]];
30 | }
31 |
32 | // bind references
33 | this.caller = endPoint;
34 | this.endPoint = this.caller[funcName];
35 | };
36 |
37 | module.exports = Button;
38 |
--------------------------------------------------------------------------------
/src/core/Cable.js:
--------------------------------------------------------------------------------
1 |
2 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
3 | /** Cable
4 | * link two connectors
5 | *
6 | * @author Cedric Stoquer
7 | */
8 | function Cable(a, b, color) {
9 | this.endPointA = a;
10 | this.endPointB = b;
11 | this.color = color || '#555';
12 | this.id = this.getId(a, b);
13 |
14 | this.x = 0; // start point x
15 | this.y = 0; // start point y
16 | this.a = 0; // control point 1 x
17 | this.b = 0; // control point 1 y
18 | this.c = 0; // control point 2 x
19 | this.d = 0; // control point 2 y
20 | this.w = 0; // end point x
21 | this.h = 0; // end point y
22 |
23 | this.update();
24 |
25 | a.module.addCable(this);
26 | b.module.addCable(this);
27 | }
28 | module.exports = Cable;
29 |
30 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
31 | Cable.prototype.getId = function (a, b) {
32 | return a.module.id + ':' + a.id + '--' + b.module.id + ':' + b.id;
33 | };
34 |
35 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
36 | Cable.prototype.draw = function () {};
37 | Cable.prototype.update = function () {};
38 |
39 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
40 | Cable.prototype.disconnect = function () {
41 | if (this.endPointA) {
42 | // remove connections
43 | this.endPointA.disconnect(this.endPointB);
44 | this.endPointB.disconnect(this.endPointA);
45 |
46 | // remove cables references from modules
47 | this.endPointA.module.removeCable(this);
48 | this.endPointB.module.removeCable(this);
49 | }
50 | this.endPointA = null;
51 | this.endPointB = null;
52 | };
53 |
--------------------------------------------------------------------------------
/src/core/Connector.js:
--------------------------------------------------------------------------------
1 | var connectors = require('./connectors');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | /** Connector Abstract class
5 | *
6 | * @author Cedric Stoquer
7 | */
8 | function Connector(module, id, descriptor) {
9 | this.module = module;
10 | this.id = id;
11 | this.x = descriptor.x;
12 | this.y = descriptor.y;
13 | this.singleConnection = descriptor.singleConnection === undefined ? false : !!descriptor.singleConnection;
14 |
15 | this.initGUI(module, id, descriptor);
16 | this.bind(module, id, descriptor);
17 | }
18 | module.exports = Connector;
19 |
20 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
21 | Connector.prototype.cssClassName = 'connector';
22 | Connector.prototype.color = '#2da8ff';
23 | Connector.prototype.type = 'none';
24 | Connector.prototype.way = 'input';
25 | connectors.register(Connector, 'input', 'none');
26 |
27 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
28 | Connector.prototype.bind = function (module, id, descriptor) {
29 | /* virtual */
30 | };
31 |
32 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
33 | Connector.prototype.connect = function (connector) {
34 | var patch = this.module.patch;
35 |
36 | // check if one of the connector is a single connection
37 | if (this.singleConnection) patch.disconnect(this);
38 | if (connector.singleConnection) patch.disconnect(connector);
39 |
40 | // add cable
41 | patch.addCable(this, connector, this.color);
42 | };
43 |
44 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
45 | Connector.prototype.disconnect = function (connector) {
46 | /* virtual */
47 | };
48 |
49 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
50 | Connector.prototype.isCompatible = function (connector) {
51 | // TODO: We suppose both connector's modules are in the same patch.
52 | // Do we want patches to be able to connect together ?
53 | if (connector === this) return false;
54 | if (this.type !== connector.type) return false;
55 | if (this.way === connector.way) return false;
56 | return true;
57 | };
58 |
59 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
60 | Connector.prototype.initGUI = function (module, id, descriptor) {};
--------------------------------------------------------------------------------
/src/core/EventConnector.js:
--------------------------------------------------------------------------------
1 | var connectors = require('./connectors');
2 | var Connector = require('./Connector');
3 |
4 | var EVENT_CABLE_COLOR = '#2da8ff';
5 |
6 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7 | function getEndPoint(module, endPointDescriptor) {
8 | endPointDescriptor = endPointDescriptor || '';
9 | endPointDescriptor = endPointDescriptor.split('.');
10 | endPoint = module;
11 | for (var i = 0; i < endPointDescriptor.length; i++) {
12 | endPoint = endPoint[endPointDescriptor[i]];
13 | }
14 | return endPoint
15 | }
16 |
17 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
18 | function EventInput(module, id, descriptor) {
19 | // this.module = module;
20 | Connector.call(this, module, id, descriptor);
21 | this.onConnect = descriptor.onConnect ? getEndPoint(module, descriptor.onConnect) : null;
22 | this.onDisconnect = descriptor.onDisconnect ? getEndPoint(module, descriptor.onDisconnect) : null;
23 | }
24 | inherits(EventInput, Connector);
25 | EventInput.prototype.cssClassName = 'eventIn';
26 | EventInput.prototype.color = EVENT_CABLE_COLOR;
27 | EventInput.prototype.type = 'event';
28 | EventInput.prototype.way = 'input';
29 | connectors.register(EventInput, 'input', 'event');
30 |
31 | EventInput.prototype.bind = function (module, id, descriptor) {
32 | // An event input endPoint is a reference to a function of the module
33 | // that will be called when an event comes in.
34 | this.endPoint = getEndPoint(module, descriptor.endPoint);
35 | };
36 |
37 | EventInput.prototype.connect = function (connector) {
38 | // Connector.prototype.connect.call(this, connector);
39 | connector.connect(this);
40 | if (this.onConnect) this.onConnect.call(this.module, connector);
41 | };
42 |
43 | EventInput.prototype.disconnect = function (connector) {
44 | if (this.onDisconnect) this.onDisconnect.call(this.module, connector);
45 | };
46 |
47 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
48 | function EventOutput(module, id, descriptor) {
49 | Connector.call(this, module, id, descriptor);
50 | // event output endPoints is an array of EventInput connector references.
51 | this.connections = [];
52 | this.onConnect = descriptor.onConnect ? getEndPoint(module, descriptor.onConnect) : null;
53 | this.onDisconnect = descriptor.onDisconnect ? getEndPoint(module, descriptor.onDisconnect) : null;
54 | }
55 | inherits(EventOutput, Connector);
56 | EventOutput.prototype.cssClassName = 'eventOut';
57 | EventOutput.prototype.color = EVENT_CABLE_COLOR;
58 | EventOutput.prototype.type = 'event';
59 | EventOutput.prototype.way = 'output';
60 | connectors.register(EventOutput, 'output', 'event');
61 |
62 | EventOutput.prototype.connect = function (connector) {
63 | Connector.prototype.connect.call(this, connector);
64 | this.connections.push(connector);
65 | if (this.onConnect) this.onConnect.call(this.module, connector);
66 | };
67 |
68 | EventOutput.prototype.disconnect = function (connector) {
69 | var index = this.connections.indexOf(connector);
70 | if (index === -1) return;
71 | this.connections.splice(index, 1);
72 | if (this.onDisconnect) this.onDisconnect.call(this.module, connector);
73 | };
74 |
75 | EventOutput.prototype.emit = function (event) {
76 | event = event || {};
77 | for (var i = 0; i < this.connections.length; i++) {
78 | var connector = this.connections[i];
79 | // bind to the correct 'this' value (the connector's module)
80 | connector.endPoint.call(connector.module, event, this);
81 | }
82 | };
83 |
84 | EventOutput.prototype.emitTo = function (connector, event) {
85 | connector.endPoint.call(connector.module, event, this);
86 | };
87 |
--------------------------------------------------------------------------------
/src/core/EventEmitter.js:
--------------------------------------------------------------------------------
1 | function EventEmitter() {
2 | this._events = {};
3 | };
4 |
5 | module.exports = EventEmitter;
6 |
7 | EventEmitter.listenerCount = function (emitter, event) {
8 | var handlers = emitter._events[event];
9 | return handlers ? handlers.length : 0;
10 | };
11 |
12 | EventEmitter.prototype.on = function (event, fn) {
13 | if (typeof fn !== 'function') throw new TypeError('Tried to register non-function as event handler: ' + event);
14 |
15 | // we emit first, because if event is "newListener" it would go recursive
16 | // this.emit('newListener', event, fn);
17 |
18 | var allHandlers = this._events;
19 | var eventHandlers = allHandlers[event];
20 | if (eventHandlers === undefined) {
21 | // first event handler for this event type
22 | allHandlers[event] = [fn];
23 | } else {
24 | eventHandlers.push(fn);
25 | }
26 |
27 | return this;
28 | };
29 |
30 | EventEmitter.prototype.addListener = EventEmitter.prototype.on;
31 |
32 | EventEmitter.prototype.once = function (event, fn) {
33 | if (!fn.once) {
34 | fn.once = 1;
35 | } else {
36 | fn.once += 1;
37 | }
38 |
39 | return this.on(event, fn);
40 | };
41 |
42 | EventEmitter.prototype.removeListener = function (event, handler) {
43 | var handlers = this._events[event];
44 | if (handlers !== undefined) {
45 | var index = handlers.indexOf(handler);
46 | if (index === -1) {
47 | console.warn('No event listener registered', event, handler)
48 | return this;
49 | }
50 |
51 | handlers.splice(index, 1);
52 | if (handlers.length === 0) delete this._events[event];
53 | }
54 | return this;
55 | };
56 |
57 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
58 |
59 | EventEmitter.prototype.removeAllListeners = function (event) {
60 | if (event) {
61 | delete this._events[event];
62 | } else {
63 | this._events = {};
64 | }
65 | return this;
66 | };
67 |
68 | EventEmitter.prototype.hasListeners = function (event) {
69 | return this._events[event] !== undefined;
70 | };
71 |
72 | EventEmitter.prototype.listeners = function (event) {
73 | var handlers = this._events[event];
74 | if (handlers !== undefined) return handlers.slice();
75 |
76 | return [];
77 | };
78 |
79 | EventEmitter.prototype.emit = function (event) {
80 | var handlers = this._events[event];
81 | if (handlers === undefined) return false;
82 |
83 | // copy handlers into a new array, so that handler removal doesn't affect array length
84 | handlers = handlers.slice();
85 |
86 | var hadListener = false;
87 |
88 | // copy all arguments, but skip the first (the event name)
89 | var args = [];
90 | for (var i = 1; i < arguments.length; i++) {
91 | args.push(arguments[i]);
92 | }
93 |
94 | for (var i = 0, len = handlers.length; i < len; i++) {
95 | var handler = handlers[i];
96 |
97 | handler.apply(this, args);
98 | hadListener = true;
99 |
100 | if (handler.once) {
101 | if (handler.once > 1) {
102 | handler.once--;
103 | } else {
104 | delete handler.once;
105 | }
106 |
107 | this.removeListener(event, handler);
108 | }
109 | }
110 |
111 | return hadListener;
112 | };
--------------------------------------------------------------------------------
/src/core/Knob.js:
--------------------------------------------------------------------------------
1 | var map = require('./utils').map;
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | /** Knob
5 | *
6 | * @author Cedric Stoquer
7 | */
8 | function Knob(module, id, descriptor) {
9 | this.value = 0;
10 | this.x = descriptor.x;
11 | this.y = descriptor.y;
12 | this.min = descriptor.min || 0;
13 | this.max = descriptor.max !== undefined ? descriptor.max : 1;
14 | this.int = descriptor.int || false;
15 |
16 | this.initGUI(module, id, descriptor);
17 |
18 | // initialise
19 | this.bind(module, id, descriptor);
20 | }
21 |
22 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
23 | Knob.prototype.bind = function (module, id, descriptor) {
24 | this.id = id;
25 | this.module = module;
26 | this.valueId = descriptor.value || 'value';
27 | this.endPoint = module;
28 |
29 | var endPoint = descriptor.endPoint;
30 | if (endPoint) {
31 | endPoint = endPoint.split('.');
32 | for (var i = 0; i < endPoint.length; i++) {
33 | this.endPoint = this.endPoint[endPoint[i]];
34 | }
35 | // set default min max from AudioParam if exist
36 | // if (this.endPoint.minValue !== undefined) this.min = this.endPoint.minValue;
37 | // if (this.endPoint.maxValue !== undefined) this.max = this.endPoint.maxValue;
38 | }
39 |
40 | this.initValue();
41 | };
42 |
43 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
44 | /** Initialise Knob properties according to the endPoint current value */
45 | Knob.prototype.initValue = function () {
46 | var value = this.endPoint[this.valueId];
47 | if (value === undefined) value = 0;
48 | this.value = map(value, this.min, this.max, -68, 68);
49 | this.updateGUI();
50 | };
51 |
52 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
53 | /** Set endPoint value in respect to the knob internal value */
54 | Knob.prototype.updateValue = function () {
55 | var value = map(this.value, -68, 68, this.min, this.max);
56 | if (this.int) value = ~~Math.round(value);
57 | this.endPoint[this.valueId] = value;
58 | this.displayValue(value);
59 | };
60 |
61 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
62 | Knob.prototype.getState = function () {
63 | return this.value;
64 | };
65 |
66 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
67 | Knob.prototype.setState = function (value) {
68 | this.value = value;
69 | this.updateValue();
70 | this.updateGUI();
71 | };
72 |
73 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
74 | Knob.prototype.initGUI = function (module, id, descriptor) {};
75 | Knob.prototype.updateGUI = function () {};
76 | Knob.prototype.displayValue = function (value) {};
77 |
78 | module.exports = Knob;
79 |
--------------------------------------------------------------------------------
/src/core/MIDI.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('./EventEmitter');
2 |
3 | MIDI_MESSAGE_TYPES = {
4 | 8: 'note off',
5 | 9: 'note on',
6 | 10: 'aftertouch',
7 | 11: 'control change',
8 | 12: 'program change',
9 | 13: 'channel pressure',
10 | 14: 'pitch bend'
11 | };
12 |
13 | var MIDI = new EventEmitter();
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | function onMessage(message) {
17 | var data = message.data;
18 | var status = data[0];
19 | if (status >= 240) return; // TODO: system common messages
20 | var typeId = (status & 240) >> 4;
21 | var type = MIDI_MESSAGE_TYPES[typeId];
22 | if (!type) return;
23 | var channel = (status & 15);
24 | var data1 = data[1];
25 | var data2 = data[2];
26 |
27 | var e = { _type: 'midi message', channel: channel, midiType: type };
28 |
29 | switch (type) {
30 | case 'note on':
31 | case 'note off':
32 | e.note = data1;
33 | e.velocity = data2;
34 | break;
35 | case 'control change':
36 | e.control = data1;
37 | e.value = data2;
38 | break;
39 | // TODO: aftertouch
40 | // TODO: program change
41 | // TODO: channel pressure
42 | // TODO: pitch bend
43 | default:
44 | e.data1 = data1;
45 | e.data2 = data2;
46 | }
47 |
48 | MIDI.emit('message', e);
49 | }
50 |
51 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
52 | function onMidiFailure(error) {
53 | console.error('requestMIDIAccess failed', error);
54 | }
55 |
56 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
57 | function onMidiSuccess(midiAccess) {
58 | var inputs = midiAccess.inputs.values();
59 | for (var input = inputs.next(); input && !input.done; input = inputs.next()) {
60 | input.value.onmidimessage = onMessage;
61 | }
62 | }
63 |
64 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
65 | var isOpened = false;
66 |
67 | module.exports = MIDI;
68 |
69 | MIDI.open = function openMidi() {
70 | if (isOpened) return;
71 | isOpened = true;
72 | navigator.requestMIDIAccess({ sysex: false }).then(onMidiSuccess, onMidiFailure);
73 | };
--------------------------------------------------------------------------------
/src/core/ParamConnector.js:
--------------------------------------------------------------------------------
1 | var connectors = require('./connectors');
2 | var Connector = require('./Connector');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function ParamConnector(module, id, descriptor) {
6 | this.connections = [];
7 | Connector.call(this, module, id, descriptor);
8 | }
9 | inherits(ParamConnector, Connector);
10 | // ParamConnector.prototype.color = '#a9ee22';
11 | ParamConnector.prototype.color = '#2da8ff';
12 | ParamConnector.prototype.type = 'param';
13 |
14 | ParamConnector.prototype.connect = function (connector) {
15 | Connector.prototype.connect.call(this, connector);
16 | // save connections for rebind
17 | this._addConnection(connector);
18 | connector._addConnection(this);
19 | };
20 |
21 | ParamConnector.prototype.disconnect = function (connector) {
22 | Connector.prototype.disconnect.call(this, connector);
23 | // remove saved connections
24 | this._removeConnection(connector);
25 | connector._removeConnection(this);
26 | };
27 |
28 | ParamConnector.prototype._addConnection = function (connector) {
29 | this.connections.push(connector);
30 | };
31 |
32 | ParamConnector.prototype._removeConnection = function (connector) {
33 | var index = this.connections.indexOf(connector);
34 | if (index === -1) return; // happen to the second connector when a cable is removed
35 | this.connections.splice(index, 1);
36 | };
37 |
38 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
39 | function ParamInput(module, id, descriptor) {
40 | this.endPoint = module; // redefined at bind
41 | this.min = descriptor.min === undefined ? 0 : descriptor.min;
42 | this.max = descriptor.max === undefined ? 1 : descriptor.max;
43 | ParamConnector.call(this, module, id, descriptor);
44 | }
45 | inherits(ParamInput, ParamConnector);
46 | ParamInput.prototype.cssClassName = 'paramIn';
47 | ParamInput.prototype.way = 'input';
48 | connectors.register(ParamInput, 'input', 'param');
49 |
50 | ParamInput.prototype.bind = function (module, id, descriptor) {
51 | // find endPoint reference
52 | var endPoint = descriptor.endPoint.split('.');
53 | this.endPoint = module;
54 | for (var i = 0; i < endPoint.length; i++) {
55 | this.endPoint = this.endPoint[endPoint[i]];
56 | }
57 |
58 | // reconnect previous connections if endPoint changed
59 | var connections = this.connections;
60 | this.connections = [];
61 | for (var i = connections.length - 1; i >= 0; i--) {
62 | this.connect(connections[i]);
63 | }
64 | };
65 |
66 | // AudioParam can be connected to AudioNode output
67 | // the two following functions allow this
68 | ParamInput.prototype.isCompatible = function (connector) {
69 | if (connector.type === 'audio' && connector.way === 'output') return true;
70 | return ParamConnector.prototype.isCompatible.call(this, connector);
71 | };
72 |
73 | ParamInput.prototype.connect = function (connector) {
74 | ParamConnector.prototype.connect.call(this, connector);
75 | if (connector.type === 'audio' && connector.way === 'output') {
76 | connector.endPoint.connect(this.endPoint);
77 | }
78 | };
79 |
80 |
81 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
82 | function ParamOutput(module, id, descriptor) {
83 | ParamConnector.call(this, module, id, descriptor);
84 | }
85 | inherits(ParamOutput, ParamConnector);
86 | ParamOutput.prototype.cssClassName = 'paramOut';
87 | ParamOutput.prototype.way = 'output';
88 | connectors.register(ParamOutput, 'output', 'param');
89 |
90 | ParamOutput.prototype.setAutomation = function (func) {
91 | var connections = this.connections;
92 | for (var i = 0; i < connections.length; i++) {
93 | var connector = connections[i];
94 | func(connector.endPoint, connector.min, connector.max);
95 | }
96 | };
97 |
98 |
--------------------------------------------------------------------------------
/src/core/audioContext.js:
--------------------------------------------------------------------------------
1 | var AudioContext = window.AudioContext || window.webkitAudioContext;
2 | var audioContext = new AudioContext();
3 | module.exports = audioContext;
--------------------------------------------------------------------------------
/src/core/connectors.js:
--------------------------------------------------------------------------------
1 | /** ConnectorFactory
2 | *
3 | * @author Cedric Stoquer
4 | */
5 | connectors = {
6 | input: {},
7 | output: {}
8 | };
9 |
10 | exports.register = function (ConnectorClass, way, type) {
11 | if (!connectors[way]) return;
12 | connectors[way][type] = ConnectorClass;
13 | };
14 |
15 | exports.getConnector = function (way, type) {
16 | if (!connectors[way]) return undefined;
17 | return connectors[way][type];
18 | };
19 |
20 | require('./AudioConnector');
21 | require('./EventConnector');
22 | require('./ParamConnector');
23 |
--------------------------------------------------------------------------------
/src/core/moduleCategories.js:
--------------------------------------------------------------------------------
1 | exports.SAMPLER = 'Sampler';
2 | exports.OSC = 'Osc';
3 | exports.GAIN = 'Gain';
4 | exports.FILTER = 'Filter';
5 | exports.ENVELOPE = 'Envelope';
6 | exports.EFFECT = 'Effect';
7 | exports.DATA = 'Data';
8 | exports.CONTROL = 'Control';
9 | exports.IN_OUT = 'In/Out';
10 | // exports.OTHER = 'misc';
--------------------------------------------------------------------------------
/src/core/modules.js:
--------------------------------------------------------------------------------
1 | var MODULES_CONSTRUCTOR_BY_ID = {};
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | exports.add = function (ModuleConstructor, category) {
5 | var descriptor = ModuleConstructor.prototype.descriptor;
6 | descriptor._category = category;
7 | MODULES_CONSTRUCTOR_BY_ID[descriptor.type] = ModuleConstructor;
8 | };
9 |
10 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
11 | exports.getModuleConstructor = function (type) {
12 | return MODULES_CONSTRUCTOR_BY_ID[type];
13 | };
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | exports.getList = function () {
17 | return MODULES_CONSTRUCTOR_BY_ID;
18 | };
19 |
--------------------------------------------------------------------------------
/src/core/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Utility functions
3 | */
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | /** map a value from an input interval [iMin ~ iMax] to an output interval [oMin ~ oMax]
6 | * preconditions: iMin != iMax
7 | */
8 | exports.map = function (value, iMin, iMax, oMin, oMax) {
9 | return oMin + (oMax - oMin) * (value - iMin) / (iMax - iMin);
10 | };
11 |
12 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13 | /** Make a deep copy of an object */
14 | // exports.copyObject = function (object) {
15 |
16 | // function copyObject(source) {
17 | // if (typeof source === 'object') {
18 | // if (Array.isArray(source)) {
19 | // var arrayCopy = [];
20 | // for (var i = 0; i < source.length; i++) {
21 | // arrayCopy.push(copyObject(source[i]));
22 | // }
23 | // return arrayCopy;
24 | // } else {
25 | // // we assume it's a map object
26 | // var objectCopy = {};
27 | // for (var key in source) {
28 | // objectCopy[key] = copyObject(source[key]);
29 | // }
30 | // return objectCopy;
31 | // }
32 | // } else {
33 | // // we assume it is a simple type
34 | // return source;
35 | // }
36 | // }
37 |
38 | // return copyObject(object);
39 | // };
40 |
41 | exports.copyObject = function (object) {
42 | return JSON.parse(JSON.stringify(object));
43 | };
44 |
--------------------------------------------------------------------------------
/src/data/BufferData.js:
--------------------------------------------------------------------------------
1 | var loadAudioBuffer = require('../loaders/loadAudioBuffer');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function BufferData(id, data) {
5 | this.id = id;
6 | this.buffer = undefined;
7 | this.uri = data.uri;
8 | this.loop = data.loop || false;
9 | this.ir = data.ir || false;
10 | this.start = data.start || 0;
11 | this.end = data.end || 0;
12 | this.tag = data.tag || [];
13 | }
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | // static method
17 |
18 | BufferData.deserialize = function (data) {
19 | // TODO: check for this BufferData existence in the database
20 | return new BufferData(data.id, data);
21 | };
22 |
23 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
24 | BufferData.prototype.serialize = function () {
25 | return {
26 | _type: 'BufferData',
27 | id: this.id,
28 | uri: this.uri,
29 | loop: this.loop,
30 | ir: this.ir,
31 | start: this.start,
32 | end: this.end,
33 | tag: this.tag
34 | };
35 | };
36 |
37 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
38 | BufferData.prototype.loadAudioBuffer = function (cb) {
39 | // check if buffer is already loaded
40 | if (this.buffer) return window.setTimeout(cb, 0);
41 |
42 | var t = this;
43 |
44 | loadAudioBuffer(this.uri, function onLoad(error, buffer) {
45 | if (error) return cb(error);
46 | t.buffer = buffer;
47 | return cb();
48 | });
49 | };
50 |
51 | module.exports = BufferData;
52 |
--------------------------------------------------------------------------------
/src/data/ProceduralBuffer.js:
--------------------------------------------------------------------------------
1 | var synthesizers = require('../synthesizers');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function ProceduralBuffer(id, data) {
5 | this.id = id;
6 | this.buffer = undefined; // audio buffer
7 | this.synthesizer = data.synthesizer; // synthesizer id
8 | this.params = data.params; // synth parameters
9 |
10 | // normal bufferData compatibility
11 | this.loop = data.loop || false;
12 | this.ir = data.ir || false;
13 | this.start = data.start || 0;
14 | this.end = data.end || 0;
15 | this.tag = data.tag || [];
16 | }
17 |
18 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
19 | // static methods & attribute
20 |
21 | ProceduralBuffer.deserialize = function (data) {
22 | // TODO: check for this ProceduralBuffer existence in the database
23 | return new ProceduralBuffer(data.id, data);
24 | };
25 |
26 | ProceduralBuffer.prototype.type = 'ProceduralBuffer';
27 |
28 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
29 | ProceduralBuffer.prototype.serialize = function () {
30 | return {
31 | _type: 'ProceduralBuffer',
32 | id: this.id,
33 | synthesizer: this.synthesizer,
34 | params: this.params,
35 | loop: this.loop,
36 | ir: this.ir,
37 | start: this.start,
38 | end: this.end,
39 | tag: this.tag
40 | };
41 | };
42 |
43 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
44 | ProceduralBuffer.prototype.loadAudioBuffer = function (cb) {
45 | // check if buffer is already generated
46 | if (this.buffer) return window.setTimeout(cb, 0);
47 | this.generateBuffer(cb);
48 | };
49 |
50 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
51 | ProceduralBuffer.prototype.generateBuffer = function (cb) {
52 | // get proper synthesizer
53 | var synth = synthesizers.getSynth(this.synthesizer);
54 | if (!synth) {
55 | // defer callback
56 | window.setTimeout(function () {
57 | cb('Synthesizer ' + this.synthesizer + ' does not exists.');
58 | }, 0);
59 | return;
60 | }
61 |
62 | synth.generate(this, cb);
63 | };
64 |
65 | module.exports = ProceduralBuffer;
66 |
--------------------------------------------------------------------------------
/src/data/dataTypes.js:
--------------------------------------------------------------------------------
1 | var DATA_TYPES = {
2 | 'BufferData': require('./BufferData'),
3 | 'ProceduralBuffer': require('./ProceduralBuffer')
4 | };
5 |
6 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7 | function deserialize(data) {
8 | var type = data._type;
9 | var DataType = DATA_TYPES[type];
10 | if (!DataType) return data;
11 | return DataType.deserialize(data);
12 | }
13 |
14 | exports.deserialize = deserialize;
15 |
16 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
17 | exports.initializeDatabase = function (database) {
18 | for (var id in database) {
19 | var data = database[id];
20 | if (!data.id) data.id = id;
21 | database[id] = deserialize(data);
22 | }
23 | };
24 |
25 |
--------------------------------------------------------------------------------
/src/editor.js:
--------------------------------------------------------------------------------
1 |
2 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
3 | window.inherits = function (Child, Parent) {
4 | Child.prototype = Object.create(Parent.prototype, {
5 | constructor: {
6 | value: Child,
7 | enumerable: false,
8 | writable: true,
9 | configurable: true
10 | }
11 | });
12 | };
13 |
14 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15 | function loadJson(path, cb) {
16 | var xobj = new XMLHttpRequest();
17 | xobj.onreadystatechange = function () {
18 | if (~~xobj.readyState !== 4) return;
19 | if (~~xobj.status !== 200) return cb('xhrError:' + xobj.status);
20 | return cb && cb(null, JSON.parse(xobj.response));
21 | };
22 | xobj.open('GET', path, true);
23 | xobj.send();
24 | }
25 |
26 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
27 | loadJson('assets/buffers.json', function onAssetsLoaded(error, assets) {
28 | if (error) return console.error(error);
29 | window.assets = { buffers: assets };
30 | var main = require('./main.js');
31 | });
32 |
--------------------------------------------------------------------------------
/src/loaders/loadAudioBuffer.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 |
3 | module.exports = function (uri, cb) {
4 | var xobj = new XMLHttpRequest();
5 | xobj.responseType = 'arraybuffer';
6 |
7 | xobj.onreadystatechange = function onXhrStateChange() {
8 | if (~~xobj.readyState !== 4) return;
9 | if (~~xobj.status !== 200 && ~~xobj.status !== 0) {
10 | return cb('xhrError:' + xobj.status);
11 | }
12 | audioContext.decodeAudioData(xobj.response, function onSuccess(buffer) {
13 | return cb(null, buffer);
14 | }, cb);
15 | };
16 |
17 | xobj.open('GET', uri, true);
18 | xobj.send();
19 | };
20 |
--------------------------------------------------------------------------------
/src/loaders/sendRequest.js:
--------------------------------------------------------------------------------
1 | var IS_ELECTRON = (function () {
2 | try {
3 | var isElectron = _REQUIRE_('electron');
4 | return !!isElectron;
5 | } catch (e) {
6 | return false;
7 | }
8 | })();
9 |
10 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
11 | if (IS_ELECTRON) {
12 | module.exports = _REQUIRE_('electron').remote._REQUIRE_('./tools/commands');
13 | } else {
14 | module.exports = function (data, cb) {
15 | var xobj = new XMLHttpRequest();
16 | xobj.open('POST', 'req', true);
17 | xobj.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
18 | xobj.onreadystatechange = function () {
19 | if (~~xobj.readyState !== 4) return;
20 | if (~~xobj.status !== 200) return cb && cb('xhr:' + xobj.status);
21 | var res = JSON.parse(xobj.response);
22 | return cb && cb(res.error, res.result);
23 | };
24 | xobj.send(JSON.stringify(data));
25 | };
26 | }
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main file used only for MODULAR tool ui
3 | * This file is not used for builing the MODULAR stand-alone library
4 | */
5 |
6 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7 | // core & modules
8 |
9 | require('./modules');
10 | require('./data/dataTypes').initializeDatabase(window.assets.buffers);
11 |
12 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13 | // UI
14 |
15 | require('./ui/moduleGUI');
16 | require('./ui/connectorGUI');
17 | require('./ui/cableGUI');
18 | require('./ui/knobGUI');
19 | require('./ui/buttonGUI');
20 |
21 | require('./ui/menuHeader'); // require all panels
22 | require('./ui/moduleManager');
23 | require('./ui/dropFile');
24 | require('./ui/onWindowResize');
25 |
26 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
27 | // register synthesizer editors
28 | var synthEditor = require('./ui/synthEditor');
29 | synthEditor.register('disco', require('./synthesizers/disco/editor'));
30 | synthEditor.register('hats', require('./synthesizers/hats/editor'));
31 |
32 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
33 | require('./modular');
34 |
--------------------------------------------------------------------------------
/src/modular.js:
--------------------------------------------------------------------------------
1 | /**
2 | * main file being builded for Electron
3 | */
4 | window.inherits = require('inherits');
5 |
6 | // require('./modules');
7 | // require('./data/dataTypes');
8 |
9 | window.MODULAR = {
10 | Patch: require('./core/Patch'), // for loading patch
11 | Synths: require('./synthesizers'), // for adding external synthesizers
12 | };
13 |
--------------------------------------------------------------------------------
/src/modules/Amp.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Amp() {
6 | this.node = audioContext.createGain();
7 | this.node.gain.value = 0;
8 | Module.call(this);
9 | }
10 | inherits(Amp, Module);
11 |
12 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13 | Amp.prototype.descriptor = {
14 | type: 'Amp',
15 | name: 'Amp',
16 | size: 2,
17 | inputs: {
18 | IN: { type: 'audio', x:0.0, y:1, endPoint: 'node', label: 'IN' },
19 | MOD: { type: 'param', x:3.5, y:0, endPoint: 'node.gain', label: 'MOD' },
20 | },
21 | outputs: { OUT: { type: 'audio', x:3.5, y:1, endPoint: 'node', label: 'OUT' } }
22 | };
23 |
24 | module.exports = Amp;
--------------------------------------------------------------------------------
/src/modules/AutoBang.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function AutoBang() {
5 | this.data = null;
6 | this.duration = 5;
7 | this.timeout = null;
8 |
9 | Module.call(this);
10 |
11 | this.scheduleNext();
12 | }
13 | inherits(AutoBang, Module);
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | AutoBang.prototype.onDataIn = function (data) {
17 | this.data = data;
18 | };
19 |
20 | AutoBang.prototype.scheduleNext = function () {
21 | var t = this;
22 | this.timeout = window.setTimeout(function () {
23 | t.$OUT.emit(this.data);
24 | t.scheduleNext();
25 | }, this.duration * 1000);
26 | };
27 |
28 | AutoBang.prototype.remove = function () {
29 | // cancel timeout on unload
30 | if (this.timeout !== null) {
31 | window.clearTimeout(this.timeout);
32 | this.timeout = null;
33 | }
34 | Module.prototype.remove.call(this);
35 | };
36 |
37 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
38 | AutoBang.prototype.descriptor = {
39 | type: 'AutoBang',
40 | name: 'AutoBang',
41 | size: 2,
42 | inputs: { IN: { type: 'event', x:0, y:1, label: 'DATA', endPoint: 'onDataIn' } },
43 | outputs: { OUT: { type: 'event', x:2.3, y:1 } },
44 | controls: {
45 | duration: { type: 'knob', x: 4.0, y: 0.1, min: 1, max: 10, value: 'duration' },
46 | }
47 | };
48 |
49 | module.exports = AutoBang;
--------------------------------------------------------------------------------
/src/modules/AutoXFade.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function AutoXFade() {
6 | this.gainA = audioContext.createGain();
7 | this.gainB = audioContext.createGain();
8 | this.mix = audioContext.createGain();
9 |
10 | // internal connections
11 | this.gainA.connect(this.mix);
12 | this.gainB.connect(this.mix);
13 |
14 | // cross fade duration
15 | this.duration = 5;
16 | this.channel = false; // false = channel A, true = channel B
17 |
18 | // initiatise audioParam
19 | this.gainA.gain.value = 1;
20 | this.gainB.gain.value = 0;
21 |
22 | Module.call(this);
23 | }
24 | inherits(AutoXFade, Module);
25 |
26 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
27 | AutoXFade.prototype.trigger = function () {
28 | var currentTime = audioContext.currentTime;
29 |
30 | this.channel = !this.channel;
31 |
32 | var valueA = this.gainA.gain.value;
33 | var valueB = this.gainB.gain.value;
34 |
35 | var targetA = this.channel ? 0 : 1;
36 | var targetB = this.channel ? 1 : 0;
37 |
38 | this.gainA.gain.cancelScheduledValues(0);
39 | this.gainB.gain.cancelScheduledValues(0);
40 |
41 | this.gainA.gain.setValueAtTime(valueA, currentTime);
42 | this.gainA.gain.linearRampToValueAtTime(targetA, currentTime + this.duration);
43 |
44 | this.gainB.gain.setValueAtTime(valueB, currentTime);
45 | this.gainB.gain.linearRampToValueAtTime(targetB, currentTime + this.duration);
46 | };
47 |
48 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
49 | AutoXFade.prototype.descriptor = {
50 | type: 'AutoXFade',
51 | name: 'AutoXFade',
52 | size: 4,
53 | inputs: {
54 | A: { type: 'audio', x:0.0, y:0.8, endPoint: 'gainA', label: 'A' },
55 | B: { type: 'audio', x:0.0, y:1.8, endPoint: 'gainB', label: 'B' },
56 | TRG: { type: 'event', x:0.0, y:3.2, endPoint: 'trigger', label: 'TRG' },
57 | },
58 | outputs: {
59 | OUT: { type: 'audio', x:3.5, y:3.2, endPoint: 'mix', label: 'OUT' }
60 | },
61 | controls: {
62 | duration: { type: 'knob', x: 2.2, y: 0.8, min: 0.5, max: 20, endPoint: null, value: 'duration', label: 'TIME' },
63 | volume: { type: 'knob', x: 4.2, y: 0.8, min: 0, max: 1, endPoint: 'mix.gain', value: 'value', label: 'VOL' },
64 | }
65 | };
66 |
67 | module.exports = AutoXFade;
--------------------------------------------------------------------------------
/src/modules/Bang.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function Bang() {
5 | Module.call(this);
6 | this.data = null;
7 | }
8 | inherits(Bang, Module);
9 |
10 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
11 | Bang.prototype.onDataIn = function (data) {
12 | this.data = data;
13 | };
14 |
15 | Bang.prototype.pushButton = function () {
16 | this.$OUT.emit(this.data);
17 | };
18 |
19 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
20 | Bang.prototype.descriptor = {
21 | type: 'Bang',
22 | name: 'Bang',
23 | size: 2,
24 | inputs: { IN: { type: 'event', x:3.5, y:0, label: 'DATA', endPoint: 'onDataIn', singleConnection: true } },
25 | outputs: { OUT: { type: 'event', x:3.5, y:1, label: 'OUT' } },
26 | controls: { BTN: { type: 'button', x: 1.8, y: 0.1, endPoint: 'pushButton' } }
27 | };
28 |
29 | module.exports = Bang;
--------------------------------------------------------------------------------
/src/modules/BufferTrim.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function BufferTrim() {
6 | this.buffer = null;
7 | Module.call(this);
8 | }
9 | inherits(BufferTrim, Module);
10 |
11 | BufferTrim.prototype.setBuffer = function (event) {
12 | if (event._type !== 'buffer') return;
13 |
14 | var sourceBuffer = event.buffer.buffer;
15 | var numberOfChannels = sourceBuffer.numberOfChannels;
16 | var sampleRate = sourceBuffer.sampleRate;
17 | var offset = event.buffer.start * sampleRate;
18 | var bufferLength = sourceBuffer.length - offset;
19 |
20 | // trim end
21 | if (event.buffer.end) {
22 | if (event.buffer.end > 0) {
23 | bufferLength = (event.buffer.end - event.buffer.start) * sampleRate;
24 | } else {
25 | bufferLength += event.buffer.end * sampleRate;
26 | }
27 | }
28 |
29 | var buffer = audioContext.createBuffer(numberOfChannels, bufferLength, sampleRate);
30 |
31 | for (var channel = 0; channel < numberOfChannels; channel++) {
32 | // buffer.copyToChannel(sourceBuffer.getChannelData(channel), channel, 0);
33 | sourceBuffer.copyFromChannel(buffer.getChannelData(channel), channel, offset);
34 | }
35 |
36 | this.buffer = {
37 | buffer: buffer,
38 | start: 0,
39 | end: 0, // TODO
40 | loop: false // TODO
41 | };
42 |
43 | this.$data.emit({ _type: 'buffer', buffer: this.buffer });
44 | };
45 |
46 | BufferTrim.prototype.onConnect = function (connector) {
47 | if (!this.buffer) return;
48 | this.$data.emitTo(connector, { _type: 'buffer', buffer: this.buffer });
49 | };
50 |
51 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
52 | BufferTrim.prototype.descriptor = {
53 | type: 'BufferTrim',
54 | name: 'BufferTrim',
55 | size: 2,
56 | inputs: { buffer: { type: 'event', x:0, y:1, endPoint: 'setBuffer', label: 'BUF', singleConnection: true } },
57 | outputs: { data: { type: 'event', x:3, y:1, label: 'TRIM', onConnect: 'onConnect' } },
58 | controls: { }
59 | };
60 |
61 | module.exports = BufferTrim;
--------------------------------------------------------------------------------
/src/modules/Context.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Context() {
6 | this.node = audioContext;
7 | Module.call(this);
8 | }
9 | inherits(Context, Module);
10 |
11 | Context.prototype.descriptor = {
12 | type: 'Context',
13 | name: 'Context',
14 | size: 1,
15 | inputs: { DEST: { type: 'audio', x:3, y:0, endPoint: 'node.destination', label: 'DEST' } }
16 | };
17 |
18 | module.exports = Context;
--------------------------------------------------------------------------------
/src/modules/ControlChange.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 | var map = require('../core/utils').map;
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | function ControlChange() {
7 | this.channel = 0;
8 | this.control = 0;
9 | this.learn = false;
10 | this.glide = 0.1;
11 | Module.call(this);
12 | this._setTitle();
13 | }
14 | inherits(ControlChange, Module);
15 |
16 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
17 | ControlChange.prototype.onMessage = function (event) {
18 | if (event._type !== 'midi message') return;
19 | if (event.midiType !== 'control change') return;
20 |
21 | if (this.learn) {
22 | this.channel = event.channel;
23 | this.control = event.control;
24 | this.learn = false;
25 | this.setBorder('');
26 | this._setTitle();
27 | return;
28 | }
29 |
30 | if (event.channel !== this.channel) return;
31 | if (event.control !== this.control) return;
32 |
33 | var currentTime = audioContext.currentTime;
34 |
35 | var glide = this.glide;
36 | var value = map(event.value, 0, 127, 0, 1);
37 |
38 | this.$OUT.setAutomation(function (param, min, max) {
39 | param.cancelScheduledValues(0);
40 | param.setValueAtTime(param.value, currentTime);
41 | param.linearRampToValueAtTime(map(value, 0, 1, min, max), currentTime + glide);
42 | });
43 | };
44 |
45 | ControlChange.prototype.midiLearn = function () {
46 | this.learn = true;
47 | this.setBorder('#F00');
48 | };
49 |
50 | ControlChange.prototype._setTitle = function () {
51 | this.setTitle('CC#' + this.channel + ':' + this.control);
52 | };
53 |
54 | ControlChange.prototype.setState = function (state) {
55 | Module.prototype.setState.call(this, state);
56 | this._setTitle();
57 | };
58 |
59 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
60 | ControlChange.prototype.descriptor = {
61 | type: 'ControlChange',
62 | name: 'Control Change',
63 | size: 3,
64 | inputs: { IN: { type: 'event', x:0, y:1, label: 'IN', endPoint: 'onMessage' } },
65 | outputs: { OUT: { type: 'param', x:0, y:2, label: 'OUT' } },
66 | controls: {
67 | glide: { type: 'knob', x: 4, y: 0.5, min: 0, max: 1, endPoint: '', value: 'glide', label: 'GLID' },
68 | midiLearn: { type: 'button', x: 2.3, y: 1, endPoint: 'midiLearn' }
69 | },
70 | persistent: ['channel', 'control']
71 | };
72 |
73 | module.exports = ControlChange;
--------------------------------------------------------------------------------
/src/modules/Convolver.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Convolver() {
6 | this.node = audioContext.createConvolver();
7 | this.bufferData = null;
8 | Module.call(this);
9 | }
10 | inherits(Convolver, Module);
11 |
12 | Convolver.prototype.setBuffer = function (event) {
13 | if (event._type !== 'buffer') return;
14 |
15 | // a buffer can not be set again, we need to create new convolver.
16 | if (this.bufferData) {
17 | // remove current convolver
18 | this.node.disconnect();
19 |
20 | // create a new convolver
21 | this.node = audioContext.createConvolver();
22 | this.bufferData = null;
23 |
24 | // rebind everything
25 | this.rebind();
26 | }
27 |
28 | this.bufferData = event.buffer;
29 | var buffer = this.bufferData.buffer;
30 | this.node.buffer = buffer;
31 | }
32 |
33 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
34 | Convolver.prototype.descriptor = {
35 | type: 'Convolver',
36 | name: 'Convolver',
37 | size: 3,
38 | inputs: {
39 | IN: { type: 'audio', x:3, y:1, endPoint: 'node', label: 'IN' },
40 | buffer: { type: 'event', x:0, y:1, endPoint: 'setBuffer', label: 'BUF', /*singleConnection: true*/ },
41 | },
42 | outputs: { OUT: { type: 'audio', x:3, y:2, endPoint: 'node', label: 'OUT' } },
43 | controls: {}
44 | };
45 |
46 | module.exports = Convolver;
--------------------------------------------------------------------------------
/src/modules/DateBang.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function DateBang() {
5 | this._hour = 12;
6 | this._minute = 0;
7 | this.data = null;
8 | this.dateTimeout = null;
9 | this.knobTimeout = null;
10 | Module.call(this);
11 | }
12 | inherits(DateBang, Module);
13 |
14 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15 | DateBang.prototype.onDataIn = function (event) {
16 | this.data = data;
17 | };
18 |
19 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
20 | DateBang.prototype.updateTimeout = function () {
21 | var t = this;
22 |
23 | if (this.knobTimeout) window.clearTimeout(this.knobTimeout);
24 |
25 | // wait 1 seconds before setting the bang timeout in case the user is still changing data.
26 | this.knobTimeout = window.setTimeout(function () {
27 | t.setTimeout();
28 | }, 1000);
29 | };
30 |
31 | DateBang.prototype.setTimeout = function () {
32 | var t = this;
33 |
34 | // cancel previous timeout
35 | if (this.dateTimeout) window.clearTimeout(this.dateTimeout);
36 |
37 | // compute when next timeout should occurr (in minutes)
38 | var now = new Date();
39 | var n = now.getHours() * 60 + now.getMinutes();
40 | var d = this._hour * 60 + this._minute;
41 |
42 | var delay = d - n;
43 | if (delay <= 0) delay = 24 * 60 + delay;
44 |
45 | // set timeout
46 | t.dateTimeout = window.setTimeout(function () {
47 | t.dateTimeout = null;
48 | t.$OUT.emit(t.data);
49 | t.setTimeout();
50 | }, delay * 60 * 1000);
51 | };
52 |
53 | DateBang.prototype.remove = function () {
54 | if (this.knobTimeout) window.clearTimeout(this.knobTimeout);
55 | if (this.dateTimeout) window.clearTimeout(this.dateTimeout);
56 | Module.prototype.remove.call(this);
57 | };
58 |
59 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
60 | Object.defineProperty(DateBang.prototype, 'hour', {
61 | get: function() {
62 | return this._hour;
63 | },
64 | set: function(value) {
65 | this._hour = value;
66 | this.updateTimeout();
67 | }
68 | });
69 |
70 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
71 | Object.defineProperty(DateBang.prototype, 'minute', {
72 | get: function() {
73 | return this._minute;
74 | },
75 | set: function(value) {
76 | this._minute = value;
77 | this.updateTimeout();
78 | }
79 | });
80 |
81 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
82 | DateBang.prototype.descriptor = {
83 | type: 'DateBang',
84 | name: 'DateBang',
85 | size: 3,
86 | inputs: { IN: { type: 'event', x:3.5, y:0, label: 'IN', endPoint: 'onDataIn', singleConnection: true } },
87 | outputs: { OUT: { type: 'event', x:3.5, y:2, label: 'OUT' } },
88 | controls: {
89 | hour: { type: 'knob', x: 0, y: 0.6, min: 0, max: 23, int: true, endPoint: null, value: 'hour', label: 'HOUR' },
90 | minute: { type: 'knob', x: 1.8, y: 0.6, min: 0, max: 59, int: true, endPoint: null, value: 'minute', label: 'MIN' },
91 | }
92 | };
93 |
94 | module.exports = DateBang;
--------------------------------------------------------------------------------
/src/modules/Delay.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Delay() {
6 | this.node = audioContext.createDelay(1);
7 | Module.call(this);
8 | }
9 | inherits(Delay, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | Delay.prototype.descriptor = {
13 | type: 'Delay',
14 | name: 'Delay',
15 | size: 3,
16 | inputs: { IN: { type: 'audio', x:0.0, y:1, endPoint: 'node', label: 'IN' } },
17 | outputs: { OUT: { type: 'audio', x:3.6, y:1, endPoint: 'node', label: 'OUT' } },
18 | controls: {
19 | delay: { type: 'knob', x: 2.0, y: 0.3, min: 0.01, max: 1.0, endPoint: 'node.delayTime', value: 'value', label: 'TIME' },
20 | }
21 | };
22 |
23 | module.exports = Delay;
--------------------------------------------------------------------------------
/src/modules/Envelope.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Envelope() {
6 | Module.call(this);
7 | }
8 | inherits(Envelope, Module);
9 |
10 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
11 | Envelope.prototype.onTrigger = function () {
12 | var currentTime = audioContext.currentTime;
13 |
14 | var ATTACK = 1;
15 | var SUSTAIN = 1;
16 | var RELEASE = 2;
17 |
18 | this.$OUT.setAutomation(function (param, min, max) {
19 | var value = param.value;
20 | param.cancelScheduledValues(0);
21 |
22 | param.setValueAtTime(value, currentTime);
23 | param.linearRampToValueAtTime(max, currentTime + ATTACK);
24 | param.setValueAtTime(max, currentTime + ATTACK + SUSTAIN);
25 | param.linearRampToValueAtTime(min, currentTime + ATTACK + SUSTAIN + RELEASE);
26 | });
27 | };
28 |
29 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
30 | Envelope.prototype.descriptor = {
31 | type: 'Envelope',
32 | name: 'Envelope',
33 | size: 2,
34 | inputs: { TRG: { type: 'event', x:0, y:1, label: 'TRG', endPoint: 'onTrigger' } },
35 | outputs: { OUT: { type: 'param', x:3.5, y:1, label: 'OUT' } },
36 | controls: {}
37 | };
38 |
39 | module.exports = Envelope;
--------------------------------------------------------------------------------
/src/modules/EventDelay.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function EventDelay() {
5 | this.delay = 10;
6 | Module.call(this);
7 | }
8 | inherits(EventDelay, Module);
9 |
10 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
11 | EventDelay.prototype.onDataIn = function (event) {
12 | var t = this;
13 |
14 | // FIXME: cancel timeout on destroy
15 | window.setTimeout(function () {
16 | try {
17 | t.$OUT.emit(event);
18 | } catch (e) {
19 | // noop
20 | }
21 | }, this.delay * 1000);
22 | };
23 |
24 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
25 | EventDelay.prototype.descriptor = {
26 | type: 'EventDelay',
27 | name: 'EventDelay',
28 | size: 2,
29 | inputs: { IN: { type: 'event', x:0, y:0.9, label: 'IN', endPoint: 'onDataIn' } },
30 | outputs: { OUT: { type: 'event', x:2, y:0.9, label: 'OUT' } },
31 | controls: { delay: { type: 'knob', x: 4, y: 0, min: 1, max: 200, int: true, endPoint: null, value: 'delay' } }
32 | };
33 |
34 | module.exports = EventDelay;
--------------------------------------------------------------------------------
/src/modules/EventPool.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function EventPool() {
5 | Module.call(this);
6 | this.map = new WeakMap();
7 | this.data = [];
8 | this.index = 0;
9 | }
10 | inherits(EventPool, Module);
11 |
12 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13 | EventPool.prototype.removeData = function (connector) {
14 | var data = this.map.get(connector);
15 | if (!data) return;
16 | this.map.delete(connector);
17 | var index = this.data.indexOf(data);
18 | if (index === -1) return console.error('data not in the pool');
19 | this.data.splice(index, 1);
20 | };
21 |
22 | EventPool.prototype.onDataIn = function (data, connector) {
23 | this.removeData(connector);
24 | this.map.set(connector, data);
25 | this.data.push(data);
26 | };
27 |
28 | EventPool.prototype.onDisconnect = function (connector) {
29 | this.removeData(connector);
30 | };
31 |
32 | EventPool.prototype.onTrigger = function (event) {
33 | if (this.data.length === 0) return;
34 | // TODO: input event can control which data to emit (next, previous, same, random)
35 | this.index = (this.index + 1) % this.data.length; // next data
36 | this.$OUT.emit(this.data[this.index]);
37 | };
38 |
39 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
40 | EventPool.prototype.descriptor = {
41 | type: 'EventPool',
42 | name: 'EventPool',
43 | size: 2,
44 | inputs: {
45 | IN: { type: 'event', x:0, y:1, label: 'DATA', endPoint: 'onDataIn', onDisconnect: 'onDisconnect' },
46 | TRG: { type: 'event', x:3.5, y:0, label: 'TRG', endPoint: 'onTrigger' }
47 | },
48 | outputs: {
49 | OUT: { type: 'event', x:3.5, y:1, label: 'OUT' }
50 | },
51 | controls: {}
52 | };
53 |
54 | module.exports = EventPool;
--------------------------------------------------------------------------------
/src/modules/Fade.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 | var map = require('../core/utils').map;
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | function Fade() {
7 | this.target = 0.5;
8 | this.duration = 4;
9 | Module.call(this);
10 | }
11 | inherits(Fade, Module);
12 |
13 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
14 | Fade.prototype.onTrigger = function () {
15 | var currentTime = audioContext.currentTime;
16 | var target = this.target;
17 | var duration = this.duration;
18 |
19 | this.$OUT.setAutomation(function (param, min, max) {
20 | var value = param.value;
21 | param.cancelScheduledValues(0);
22 | param.setValueAtTime(value, currentTime);
23 | param.linearRampToValueAtTime(map(target, 0, 1, min, max), currentTime + duration);
24 | });
25 | };
26 |
27 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
28 | Fade.prototype.descriptor = {
29 | type: 'Fade',
30 | name: 'Fade',
31 | size: 3,
32 | inputs: { TRG: { type: 'event', x:0, y:1, label: 'TRG', endPoint: 'onTrigger' } },
33 | outputs: { OUT: { type: 'param', x:0, y:2, label: 'ENV' } },
34 | controls: {
35 | target: { type: 'knob', x: 2.1, y: 0.3, min: 0, max: 1, value: 'target', label: 'TO' },
36 | duration: { type: 'knob', x: 4.0, y: 0.3, min: 0.1, max: 20, value: 'duration', label: 'TIME' },
37 | }
38 | };
39 |
40 | module.exports = Fade;
--------------------------------------------------------------------------------
/src/modules/Filter.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | var FILTER_TYPE_ENUM = [
5 | { id: 'lowpass', caption: 'LP' },
6 | { id: 'highpass', caption: 'HP' },
7 | { id: 'bandpass', caption: 'BP' },
8 | // 'lowshelf',
9 | // 'highshelf',
10 | // 'peaking',
11 | // 'notch',
12 | // 'allpass'
13 | ];
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | function Filter() {
17 | this.node = audioContext.createBiquadFilter();
18 | this._filterType = 0;
19 | this.node.type = FILTER_TYPE_ENUM[this._filterType].id;
20 |
21 | Module.call(this);
22 | this.$$type.setTitle(FILTER_TYPE_ENUM[this._filterType].caption);
23 | }
24 | inherits(Filter, Module);
25 |
26 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
27 | Filter.prototype.switchType = function () {
28 | this.filterType += 1;
29 | };
30 |
31 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
32 | Object.defineProperty(Filter.prototype, 'filterType', {
33 | get: function() {
34 | return this._filterType;
35 | },
36 | set: function(value) {
37 | this._filterType = value % FILTER_TYPE_ENUM.length;
38 | this.node.type = FILTER_TYPE_ENUM[this._filterType].id;
39 | this.$$type.setTitle(FILTER_TYPE_ENUM[this._filterType].caption);
40 | }
41 | });
42 |
43 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
44 | Filter.prototype.descriptor = {
45 | type: 'Filter',
46 | name: 'Filter',
47 | size: 4,
48 | inputs: { IN: { type: 'audio', x:0.5, y:3, endPoint: 'node', label: 'IN' } },
49 | outputs: { OUT: { type: 'audio', x:3.0, y:3, endPoint: 'node', label: 'OUT' } },
50 | controls: {
51 | cut: { type: 'knob', x: 2.0, y: 0.3, min: 10.0, max: 8000.0, endPoint: 'node.frequency', value: 'value', label: 'CUT' },
52 | res: { type: 'knob', x: 4.0, y: 0.3, min: 0.00, max: 40.0, endPoint: 'node.Q', value: 'value', label: 'REZ' },
53 | type: { type: 'button', x: 0.2, y: 1.2, endPoint: 'switchType' }
54 | },
55 | persistent: ['filterType']
56 | };
57 |
58 | module.exports = Filter;
--------------------------------------------------------------------------------
/src/modules/FilterMod.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Filter = require('./Filter');
3 |
4 | var FILTER_TYPE_ENUM = [
5 | { id: 'lowpass', caption: 'LP' },
6 | { id: 'highpass', caption: 'HP' },
7 | { id: 'bandpass', caption: 'BP' },
8 | // 'lowshelf',
9 | // 'highshelf',
10 | // 'peaking',
11 | // 'notch',
12 | // 'allpass'
13 | ];
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | function FilterMod() {
17 | Filter.call(this);
18 | }
19 | inherits(FilterMod, Filter);
20 |
21 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
22 | FilterMod.prototype.descriptor = {
23 | type: 'FilterMod',
24 | name: 'FilterMod',
25 | size: 3,
26 | inputs: {
27 | IN: { type: 'audio', x: 2.2, y: 1, endPoint: 'node', label: 'IN' },
28 | CUT: { type: 'param', x: 0.0, y: 1, endPoint: 'node.frequency', label: 'CUT', min: 10.0, max: 8000 },
29 | RES: { type: 'param', x: 0.0, y: 2, endPoint: 'node.Q', label: 'RES', min: 0.00, max: 40 }
30 | },
31 | outputs: { OUT: { type: 'audio', x: 2.2, y: 2, endPoint: 'node', label: 'OUT' } },
32 | controls: {
33 | // cut: { type: 'knob', x: 2.0, y: 0.3, min: 10.0, max: 8000.0, endPoint: 'node.frequency', value: 'value', label: 'CUT' },
34 | // res: { type: 'knob', x: 4.0, y: 0.3, min: 0.00, max: 40.0, endPoint: 'node.Q', value: 'value', label: 'REZ' },
35 | type: { type: 'button', x: 4.2, y: 1.1, endPoint: 'switchType' }
36 | },
37 | persistent: ['filterType']
38 | };
39 |
40 | module.exports = FilterMod;
--------------------------------------------------------------------------------
/src/modules/Gain.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Gain() {
6 | this.node = audioContext.createGain();
7 | Module.call(this);
8 | }
9 | inherits(Gain, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | Gain.prototype.descriptor = {
13 | type: 'Gain',
14 | name: 'Gain x100',
15 | size: 3,
16 | inputs: { IN: { type: 'audio', x:3.5, y:0.2, endPoint: 'node', label: 'IN' } },
17 | outputs: { OUT: { type: 'audio', x:3.5, y:2, endPoint: 'node', label: 'OUT' } },
18 | controls: { gain: { type: 'knob', x: 1.5, y: 0.5, min: 1.0, max: 100.0, endPoint: 'node.gain', value: 'value', label: 'GAIN' } }
19 | };
20 |
21 | module.exports = Gain;
--------------------------------------------------------------------------------
/src/modules/LFO.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | WAVEFORM_TYPE_ENUM = [
5 | { id: 'sine', caption: 'sin' },
6 | { id: 'square', caption: 'sqr' },
7 | { id: 'sawtooth', caption: 'saw' },
8 | { id: 'triangle', caption: 'tri' },
9 | // { id: 'custom', caption: 'usr' }
10 | ];
11 |
12 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13 | function LFO() {
14 | this._waveType = 0;
15 |
16 | this.node = audioContext.createOscillator();
17 | this.node.frequency.value = 2.0;
18 | this.node.type = WAVEFORM_TYPE_ENUM[this._waveType].id;
19 | this.node.start();
20 | Module.call(this);
21 |
22 | this.$$type.setTitle(WAVEFORM_TYPE_ENUM[this._waveType].caption);
23 | }
24 | inherits(LFO, Module);
25 |
26 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
27 | LFO.prototype.switchType = function () {
28 | this.waveform += 1;
29 | };
30 |
31 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
32 | Object.defineProperty(LFO.prototype, 'waveform', {
33 | get: function() {
34 | return this._waveType;
35 | },
36 | set: function(value) {
37 | this._waveType = value % WAVEFORM_TYPE_ENUM.length;
38 | this.node.type = WAVEFORM_TYPE_ENUM[this._waveType].id;
39 | this.$$type.setTitle(WAVEFORM_TYPE_ENUM[this._waveType].caption);
40 | }
41 | });
42 |
43 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
44 | LFO.prototype.descriptor = {
45 | type: 'LFO',
46 | name: 'LFO',
47 | size: 3,
48 | inputs: {},
49 | outputs: { OUT: { type: 'audio', x:5, y:1, endPoint: 'node', label: null } },
50 | controls: {
51 | frequency: { type: 'knob', x: 2.5, y: 0.3, min: 0.001, max: 10.0, endPoint: 'node.frequency', value: 'value', label: 'FREQ' },
52 | type: { type: 'button', x: 0.2, y: 1.2, endPoint: 'switchType' }
53 | },
54 | persistent: ['waveform']
55 | };
56 |
57 | module.exports = LFO;
--------------------------------------------------------------------------------
/src/modules/MidiIn.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 | var MIDI = require('../core/MIDI');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function MidiIn() {
6 | Module.call(this);
7 | MIDI.open();
8 | var t = this;
9 |
10 | this.onMessage = function (event) {
11 | t.$OUT.emit(event);
12 | };
13 |
14 | MIDI.on('message', this.onMessage);
15 | }
16 | inherits(MidiIn, Module);
17 |
18 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
19 | MidiIn.prototype.remove = function () {
20 | MIDI.removeListener('message', this.onMessage);
21 | Module.prototype.remove.call(this);
22 | };
23 |
24 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
25 | MidiIn.prototype.descriptor = {
26 | type: 'MidiIn',
27 | name: 'MidiIn',
28 | size: 1,
29 | inputs: {},
30 | outputs: { OUT: { type: 'event', x: 5, y: 0 } },
31 | controls: {}
32 | };
33 |
34 | module.exports = MidiIn;
--------------------------------------------------------------------------------
/src/modules/ModDelay.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function ModDelay() {
6 | this.node = audioContext.createDelay(1);
7 | Module.call(this);
8 | }
9 | inherits(ModDelay, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | ModDelay.prototype.descriptor = {
13 | type: 'ModDelay',
14 | name: 'ModDelay',
15 | size: 2,
16 | inputs: {
17 | time: { type: 'param', x:0, y:1, endPoint: 'node.delayTime', label: 'TIME' },
18 | IN: { type: 'audio', x:3.6, y:0, endPoint: 'node', label: 'IN' },
19 | },
20 | outputs: { OUT: { type: 'audio', x:3.6, y:1, endPoint: 'node', label: 'OUT' } },
21 | controls: {}
22 | };
23 |
24 | module.exports = ModDelay;
--------------------------------------------------------------------------------
/src/modules/ModPanner.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function ModPanner() {
6 | this.node = audioContext.createStereoPanner();
7 | Module.call(this);
8 | }
9 | inherits(ModPanner, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | ModPanner.prototype.descriptor = {
13 | type: 'ModPanner',
14 | name: 'ModPan',
15 | size: 2,
16 | inputs: {
17 | IN: { type: 'audio', x:3.5, y:0, endPoint: 'node', label: 'IN' },
18 | pan: { type: 'param', x:0.0, y:1, endPoint: 'node.pan', label: 'PAN' },
19 | },
20 | outputs: { OUT: { type: 'audio', x:3.5, y:1, endPoint: 'node', label: 'OUT' } }
21 | };
22 |
23 | module.exports = ModPanner;
--------------------------------------------------------------------------------
/src/modules/NoteDetect.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | var MIDI_NOTE_C4 = 60;
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | function NoteDetect() {
7 | this.note = MIDI_NOTE_C4;
8 | Module.call(this);
9 | }
10 | inherits(NoteDetect, Module);
11 |
12 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13 | NoteDetect.prototype.onEvent = function (event) {
14 | if (event._type !== 'midi message') return;
15 | if (event.midiType !== 'note on') return;
16 | if (event.note !== this.note) return;
17 |
18 | this.$OUT.emit(event);
19 | };
20 |
21 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
22 | NoteDetect.prototype.descriptor = {
23 | type: 'NoteDetect',
24 | name: 'note detect',
25 | size: 3,
26 | inputs: { IN: { type: 'event', x:4, y:0, endPoint: 'onEvent' } },
27 | outputs: { OUT: { type: 'event', x:5, y:0 } },
28 | controls: {
29 | note: { type: 'knob', x: 1.5, y: 0.5, min: 0, max: 127, endPoint: null, value: 'note', label: 'NOTE', int: true }
30 | }
31 | };
32 |
33 | module.exports = NoteDetect;
--------------------------------------------------------------------------------
/src/modules/OnLoadBang.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function OnLoadBang() {
5 | Module.call(this);
6 | this.data = null;
7 | var t = this;
8 | window.setTimeout(function () {
9 | t.$OUT.emit(t.data);
10 | }, 0)
11 | }
12 | inherits(OnLoadBang, Module);
13 |
14 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15 | OnLoadBang.prototype.onDataIn = function (data) {
16 | this.data = data;
17 | };
18 |
19 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
20 | OnLoadBang.prototype.descriptor = {
21 | type: 'OnLoadBang',
22 | name: 'OnLoad',
23 | size: 1,
24 | inputs: { IN: { type: 'event', x:4, y:0, endPoint: 'onDataIn', singleConnection: true } },
25 | outputs: { OUT: { type: 'event', x:5, y:0 } },
26 | controls: {}
27 | };
28 |
29 | module.exports = OnLoadBang;
--------------------------------------------------------------------------------
/src/modules/OneShotSampler.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function OneShotSampler() {
6 | this.node = audioContext.createGain();
7 | this.bufferData = null;
8 | Module.call(this);
9 | }
10 | inherits(OneShotSampler, Module);
11 |
12 | OneShotSampler.prototype.setBuffer = function (event) {
13 | if (event._type !== 'buffer') return;
14 | this.bufferData = event.buffer;
15 | };
16 |
17 | OneShotSampler.prototype.trigger = function (event) {
18 | if (!this.bufferData) return;
19 |
20 | var bufferSource = audioContext.createBufferSource();
21 | bufferSource.connect(this.node);
22 | bufferSource.buffer = this.bufferData.buffer;
23 |
24 | // event can contain some data to alter the way the sample is played:
25 | if (event.playbackRate) bufferSource.playbackRate.value = event.playbackRate;
26 | var offset = event.offset || 0;
27 | // TODO: duration (not nullable, must be >= 0)
28 |
29 | bufferSource.start(0, offset);
30 | };
31 |
32 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
33 | OneShotSampler.prototype.descriptor = {
34 | type: 'OneShotSampler',
35 | name: 'OneShot',
36 | size: 3,
37 | inputs: {
38 | buffer: { type: 'event', x:0, y:2, endPoint: 'setBuffer', label: 'BUF' },
39 | trigger: { type: 'event', x:0, y:1, endPoint: 'trigger', label: 'TRIG' }
40 | },
41 | outputs: { OUT: { type: 'audio', x:5, y:2, endPoint: 'node' } },
42 | controls: { volume: { type: 'knob', x: 2.8, y: 0.5, min: 0, max: 1, endPoint: 'node.gain', value: 'value', label: 'VOL' } }
43 | };
44 |
45 | module.exports = OneShotSampler;
--------------------------------------------------------------------------------
/src/modules/Oscillator.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Oscillator() {
6 | this.node = audioContext.createOscillator();
7 | this.node.frequency.value = 220.0;
8 | // this.node.type = 'square';
9 | this.node.start();
10 | Module.call(this);
11 | }
12 | inherits(Oscillator, Module);
13 |
14 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15 | Oscillator.prototype.descriptor = {
16 | type: 'Oscillator',
17 | name: 'Oscillator',
18 | size: 3,
19 | inputs: { detune: { type: 'param', x:0, y:1, endPoint: 'node.detune', label: 'DTN' } },
20 | outputs: { OUT: { type: 'audio', x:0, y:2, endPoint: 'node', label: 'OUT' } },
21 | controls: { frequency: { type: 'knob', x: 3.7, y: 0.3, min: 110.0, max: 880.0, endPoint: 'node.frequency', value: 'value', label: 'FREQ' } }
22 | };
23 |
24 | module.exports = Oscillator;
--------------------------------------------------------------------------------
/src/modules/Panner.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Panner() {
6 | this.node = audioContext.createStereoPanner();
7 | Module.call(this);
8 | }
9 | inherits(Panner, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | Panner.prototype.descriptor = {
13 | type: 'Panner',
14 | name: 'Pan',
15 | size: 2,
16 | inputs: { IN: { type: 'audio', x:3.5, y: 0, endPoint: 'node', label: 'IN' } },
17 | outputs: { OUT: { type: 'audio', x:3.5, y: 1, endPoint: 'node', label: 'OUT' } },
18 | controls: { pan: { type: 'knob', x: 1.5, y: 0, min: -1, max: 1, endPoint: 'node.pan', value: 'value' } }
19 | };
20 |
21 | module.exports = Panner;
--------------------------------------------------------------------------------
/src/modules/PlaybackRate.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function PlaybackRate() {
5 | this.playbackRate = 1;
6 | this.data = null;
7 | Module.call(this);
8 | }
9 | inherits(PlaybackRate, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | PlaybackRate.prototype.onDataIn = function (event) {
13 | // adding circular check object
14 | // if (!event._circular) event._circular = {};
15 | // if (event._circular[this.id]) return; // circular loop detected
16 | // event._circular[this.id] = true;
17 |
18 | // adding or overwriting attribute
19 | event.playbackRate = this.playbackRate;
20 |
21 | // keep event copy for onConnect FIXME: only last event is kept
22 | this.data = event;
23 |
24 | // emit event
25 | this.$OUT.emit(event);
26 | };
27 |
28 | PlaybackRate.prototype.onConnect = function (connector) {
29 | if (!this.data) return;
30 | this.$OUT.emitTo(connector, this.data);
31 | };
32 |
33 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
34 | PlaybackRate.prototype.descriptor = {
35 | type: 'PlaybackRate',
36 | name: 'PlaybackRate',
37 | size: 3,
38 | inputs: { IN: { type: 'event', x:0.5, y:1.5, endPoint: 'onDataIn' } },
39 | outputs: { OUT: { type: 'event', x:4.5, y:1.5, onConnect: 'onConnect' } },
40 | controls: { rate: { type: 'knob', x: 2, y: 1, min: 0.01, max: 2, endPoint: null, value: 'playbackRate' } }
41 | };
42 |
43 | module.exports = PlaybackRate;
--------------------------------------------------------------------------------
/src/modules/RandomBang.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 | var map = require('../core/utils').map;
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function RandomBang() {
6 | this.data = null;
7 | this.min = 5;
8 | this.max = 20;
9 | this.timeout = null;
10 |
11 | Module.call(this);
12 |
13 | this.scheduleNext();
14 | }
15 | inherits(RandomBang, Module);
16 |
17 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
18 | RandomBang.prototype.onDataIn = function (data) {
19 | this.data = data;
20 | };
21 |
22 | RandomBang.prototype.scheduleNext = function () {
23 | var t = this;
24 | var duration = map(Math.random(), 0, 1, this.min, this.max);
25 | this.timeout = window.setTimeout(function () {
26 | t.$OUT.emit(this.data);
27 | t.scheduleNext();
28 | }, duration * 1000);
29 | };
30 |
31 | RandomBang.prototype.remove = function () {
32 | // cancel timeout on unload
33 | if (this.timeout !== null) {
34 | window.clearTimeout(this.timeout);
35 | this.timeout = null;
36 | }
37 | Module.prototype.remove.call(this);
38 | };
39 |
40 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
41 | RandomBang.prototype.descriptor = {
42 | type: 'RandomBang',
43 | name: 'RandomBang',
44 | size: 3,
45 | inputs: { IN: { type: 'event', x:5, y:1, endPoint: 'onDataIn' } },
46 | outputs: { OUT: { type: 'event', x:5, y:2 } },
47 | controls: {
48 | min: { type: 'knob', x: 0.5, y: 0.5, min: 1, max: 100, value: 'min', label: 'MIN' },
49 | max: { type: 'knob', x: 2.5, y: 0.5, min: 1, max: 100, value: 'max', label: 'MAX' },
50 | }
51 | };
52 |
53 | module.exports = RandomBang;
--------------------------------------------------------------------------------
/src/modules/Sampler.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Sampler() {
6 | this.node = audioContext.createBufferSource();
7 | this.bufferData = null;
8 | Module.call(this);
9 | }
10 | inherits(Sampler, Module);
11 |
12 | Sampler.prototype.setBuffer = function (event) {
13 | if (event._type !== 'buffer') return;
14 |
15 | // a buffer can not be set again, we need to create new bufferSource.
16 | if (this.bufferData) {
17 | // save bufferSource state
18 | var rate = this.node.playbackRate.value;
19 |
20 | // remove current bufferSource
21 | this.node.disconnect();
22 |
23 | // create a new bufferSource
24 | this.node = audioContext.createBufferSource();
25 | this.bufferData = null;
26 |
27 | // set back saved state
28 | this.node.playbackRate.value = rate;
29 |
30 | // rebind everything
31 | this.rebind();
32 | }
33 |
34 | this.bufferData = event.buffer;
35 |
36 | var buffer = this.bufferData.buffer;
37 | var loop = this.bufferData.loop || false;
38 |
39 | this.node.buffer = buffer;
40 | this.node.loop = loop;
41 |
42 | if (loop) {
43 | // set loop points
44 | var loopStart = this.bufferData.start || 0;
45 | var loopEnd = this.bufferData.end || buffer.duration;
46 | // When loop end point is negative, we set endPoint from the end of the buffer
47 | if (loopEnd < 0) loopEnd = buffer.duration + loopEnd;
48 | if (loopEnd < 0) loopEnd = 0;
49 |
50 | this.node.loopStart = loopStart;
51 | this.node.loopEnd = loopEnd;
52 | }
53 |
54 | this.node.start();
55 | }
56 |
57 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
58 | Sampler.prototype.descriptor = {
59 | type: 'Sampler',
60 | name: 'Sampler',
61 | size: 3,
62 | inputs: { buffer: { type: 'event', x:0, y:1, endPoint: 'setBuffer', label: 'BUF', /*singleConnection: true*/ } },
63 | outputs: { OUT: { type: 'audio', x:0, y:2, endPoint: 'node', label: 'OUT' } },
64 | controls: { rate: { type: 'knob', x: 3.7, y: 0.3, min: 0.01, max: 2, endPoint: 'node.playbackRate', value: 'value', label: 'RATE' } }
65 | };
66 |
67 | module.exports = Sampler;
--------------------------------------------------------------------------------
/src/modules/SlowLFO.js:
--------------------------------------------------------------------------------
1 | var LFO = require('./LFO');
2 | var audioContext = require('../core/audioContext');
3 |
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | function SlowLFO() {
7 | LFO.call(this);
8 | this.node.frequency.value = 0.005;
9 | this.$$frequency.initValue();
10 | }
11 | inherits(SlowLFO, LFO);
12 |
13 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
14 | SlowLFO.prototype.descriptor = {
15 | type: 'SlowLFO',
16 | name: 'SlowLFO',
17 | size: 3,
18 | inputs: {},
19 | outputs: { OUT: { type: 'audio', x:5, y:1, endPoint: 'node', label: null } },
20 | controls: {
21 | frequency: { type: 'knob', x: 2.8, y: 0.3, min: 0.0001, max: 0.01, endPoint: 'node.frequency', value: 'value', label: 'FREQ' },
22 | type: { type: 'button', x: 0.2, y: 1.2, endPoint: 'switchType' }
23 | },
24 | persistent: ['waveform']
25 | };
26 |
27 | module.exports = SlowLFO;
--------------------------------------------------------------------------------
/src/modules/TestModule.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4 | function TestModule() {
5 | Module.call(this);
6 | }
7 | inherits(TestModule, Module);
8 |
9 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
10 | TestModule.prototype.doStuff = function (data) {
11 | if (data._type === 'color') {
12 | this.setTitle('#' + data.hex);
13 | this.setColor('#' + data.hex);
14 | }
15 | };
16 |
17 | TestModule.prototype.onConnect = function (connector) {
18 | console.log('module has connected', this, connector);
19 | };
20 |
21 | TestModule.prototype.pushButton = function () {
22 | var hex = ('000' + (~~(Math.random() * 4096)).toString(16)).substr(-3);
23 | this.$B.emit({ _type: 'color', hex: hex });
24 | };
25 |
26 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
27 | TestModule.prototype.descriptor = {
28 | type: 'TestModule',
29 | name: 'TestModule',
30 | size: 5,
31 | inputs: {
32 | A: { type: 'event', x:0.2, y:1, label: 'A', endPoint: 'doStuff' },
33 | },
34 | outputs: {
35 | B: { type: 'event', x:3.2, y:1, label: 'B', onConnect: 'onConnect' }
36 | },
37 | controls: {
38 | a: { type: 'knob', x: 0.1, y: 2.3, label: 'KNB' },
39 | b: { type: 'knob', x: 2.1, y: 2.3, label: 'KNB' },
40 | c: { type: 'button', x: 4.1, y: 2.3, label: 'BTN', endPoint: 'pushButton' }
41 | }
42 | };
43 |
44 | module.exports = TestModule;
--------------------------------------------------------------------------------
/src/modules/Volume.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function Volume() {
6 | this.node = audioContext.createGain();
7 | Module.call(this);
8 | }
9 | inherits(Volume, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | Volume.prototype.descriptor = {
13 | type: 'Volume',
14 | name: 'Volume',
15 | size: 3,
16 | inputs: { IN: { type: 'audio', x:3.5, y:0.2, endPoint: 'node', label: 'IN' } },
17 | outputs: { OUT: { type: 'audio', x:3.5, y:2, endPoint: 'node', label: 'OUT' } },
18 | controls: { volume: { type: 'knob', x: 1.5, y: 0.5, min: 0.0, max: 1.0, endPoint: 'node.gain', value: 'value', label: 'VOL' } }
19 | };
20 |
21 | module.exports = Volume;
--------------------------------------------------------------------------------
/src/modules/XFadeSampler.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../core/audioContext');
2 | var Module = require('../core/Module');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function XFadeSampler() {
6 | // main mix
7 | this.node = audioContext.createGain();
8 |
9 | // current sample
10 | this.bufferSource = null;
11 | this.fadeGain = null;
12 |
13 | // fade durations
14 | this.fadeIn = 5;
15 | this.fadeOut = 5;
16 |
17 | Module.call(this);
18 | }
19 | inherits(XFadeSampler, Module);
20 |
21 | XFadeSampler.prototype.setBuffer = function (event) {
22 | if (event._type !== 'buffer') return;
23 | var bufferData = event.buffer;
24 |
25 | var currentTime = audioContext.currentTime;
26 |
27 | if (this.bufferSource) {
28 | // fade out previous sound
29 | var previousBufferSource = this.bufferSource;
30 | var previousFadeGain = this.fadeGain;
31 | var param = previousFadeGain.gain;
32 | param.cancelScheduledValues(0);
33 | param.setValueAtTime(param.value, currentTime);
34 | param.linearRampToValueAtTime(0, currentTime + this.fadeOut);
35 |
36 | // schedule stop and disconnections
37 | window.setTimeout(function disconnectBufferSource() {
38 | previousBufferSource.stop();
39 | previousBufferSource.disconnect();
40 | previousFadeGain.disconnect();
41 | }, this.fadeOut * 1000);
42 | }
43 |
44 | // create new sound
45 | var bufferSource = audioContext.createBufferSource();
46 | var fadeGain = audioContext.createGain();
47 | bufferSource.connect(fadeGain);
48 | fadeGain.connect(this.node);
49 |
50 | bufferSource.buffer = bufferData.buffer;
51 |
52 | // event can contain some data to alter the way the sample is played:
53 | if (event.playbackRate) bufferSource.playbackRate.value = event.playbackRate;
54 | var offset = event.offset || 0;
55 | // TODO: duration (not nullable, must be >= 0)
56 |
57 | // fade in new sound
58 | var param = fadeGain.gain;
59 | param.setValueAtTime(0, currentTime);
60 | param.linearRampToValueAtTime(1, currentTime + this.fadeIn);
61 |
62 | // set loop
63 | var loop = bufferData.loop || false;
64 | bufferSource.loop = loop;
65 |
66 | if (loop) {
67 | // set loop points
68 | var loopStart = bufferData.start || 0;
69 | var loopEnd = bufferData.end || bufferData.buffer.duration;
70 | // When loop end point is negative, we set endPoint from the end of the buffer
71 | if (loopEnd < 0) loopEnd = bufferData.buffer.duration + loopEnd;
72 | if (loopEnd < 0) loopEnd = 0;
73 |
74 | bufferSource.loopStart = loopStart;
75 | bufferSource.loopEnd = loopEnd;
76 | }
77 |
78 | // start sound
79 | bufferSource.start(0, offset);
80 |
81 | // keep references
82 | this.bufferSource = bufferSource;
83 | this.fadeGain = fadeGain;
84 | };
85 |
86 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
87 | XFadeSampler.prototype.descriptor = {
88 | type: 'XFadeSampler',
89 | name: 'XFadeSampler',
90 | size: 4,
91 | inputs: {
92 | buffer: { type: 'event', x:0.5, y:3.1, endPoint: 'setBuffer', label: 'BUF' }
93 | },
94 | outputs: { OUT: { type: 'audio', x:4.5, y:3.1, endPoint: 'node' } },
95 | controls: {
96 | fadeIn: { type: 'knob', x: 0, y: 0.7, min: 1, max: 20, endPoint: null, value: 'fadeIn', label: 'IN' },
97 | fadeOut: { type: 'knob', x: 2, y: 0.7, min: 1, max: 20, endPoint: null, value: 'fadeOut', label: 'OUT' },
98 | volume: { type: 'knob', x: 4, y: 0.7, min: 0, max: 1, endPoint: 'node.gain', value: 'value', label: 'VOL' },
99 | }
100 | };
101 |
102 | module.exports = XFadeSampler;
--------------------------------------------------------------------------------
/src/modules/index.js:
--------------------------------------------------------------------------------
1 | var modules = require('../core/modules');
2 | var CATEGORY = require('../core/moduleCategories');
3 |
4 |
5 | modules.add(require('../core/Buffer'), CATEGORY.DATA);
6 | modules.add(require('./BufferTrim'), CATEGORY.DATA);
7 | modules.add(require('./BufferSlice'), CATEGORY.DATA);
8 | modules.add(require('./TestModule'), CATEGORY.DATA);
9 | modules.add(require('./EventPool'), CATEGORY.DATA);
10 | modules.add(require('./EventDelay'), CATEGORY.DATA);
11 | modules.add(require('./PlaybackRate'), CATEGORY.DATA);
12 |
13 | // event
14 | modules.add(require('./Bang'), CATEGORY.CONTROL);
15 | modules.add(require('./AutoBang'), CATEGORY.CONTROL);
16 | modules.add(require('./RandomBang'), CATEGORY.CONTROL);
17 | modules.add(require('./DateBang'), CATEGORY.CONTROL);
18 | modules.add(require('./OnLoadBang'), CATEGORY.CONTROL);
19 |
20 | // MIDI
21 | modules.add(require('./MidiIn'), CATEGORY.CONTROL);
22 | modules.add(require('./NoteOnFilter'), CATEGORY.CONTROL);
23 | modules.add(require('./ControlChange'), CATEGORY.CONTROL);
24 | modules.add(require('./NoteDetect'), CATEGORY.CONTROL);
25 |
26 | // Oscillator, LFO
27 | modules.add(require('./Oscillator'), CATEGORY.OSC);
28 | modules.add(require('./LFO'), CATEGORY.OSC);
29 | modules.add(require('./SlowLFO'), CATEGORY.OSC);
30 |
31 | // envelope
32 | modules.add(require('./Envelope'), CATEGORY.ENVELOPE);
33 | modules.add(require('./Fade'), CATEGORY.ENVELOPE);
34 |
35 | // amp, pan
36 | modules.add(require('./Volume'), CATEGORY.GAIN);
37 | modules.add(require('./Amp'), CATEGORY.GAIN);
38 | modules.add(require('./AutoXFade'), CATEGORY.GAIN);
39 | modules.add(require('./Gain'), CATEGORY.GAIN);
40 | modules.add(require('./Panner'), CATEGORY.GAIN);
41 | modules.add(require('./ModPanner'), CATEGORY.GAIN);
42 |
43 | // sampler
44 | modules.add(require('./Sampler'), CATEGORY.SAMPLER);
45 | modules.add(require('./OneShotSampler'), CATEGORY.SAMPLER);
46 | modules.add(require('./XFadeSampler'), CATEGORY.SAMPLER);
47 |
48 | // filter
49 | modules.add(require('./Filter'), CATEGORY.FILTER);
50 | modules.add(require('./FilterMod'), CATEGORY.FILTER);
51 |
52 | // reverb, delay, fx
53 | modules.add(require('./Convolver'), CATEGORY.EFFECT);
54 | modules.add(require('./Delay'), CATEGORY.EFFECT);
55 | modules.add(require('./ModDelay'), CATEGORY.EFFECT);
56 |
57 | // out
58 | modules.add(require('./Context'), CATEGORY.IN_OUT);
59 |
--------------------------------------------------------------------------------
/src/modules/noteOnFilter.js:
--------------------------------------------------------------------------------
1 | var Module = require('../core/Module');
2 |
3 | var MIDI_NOTE_C4 = 60;
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | function noteOnFilter() {
7 | Module.call(this);
8 | }
9 | inherits(noteOnFilter, Module);
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | noteOnFilter.prototype.onEvent = function (event) {
13 | if (event._type !== 'midi message') return;
14 | if (event.midiType !== 'note on') return;
15 |
16 | var pitch = event.note - MIDI_NOTE_C4;
17 | event.playbackRate = Math.pow(2, pitch / 12);
18 |
19 | this.$OUT.emit(event);
20 | };
21 |
22 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
23 | noteOnFilter.prototype.descriptor = {
24 | type: 'noteOnFilter',
25 | name: 'noteOn',
26 | size: 1,
27 | inputs: { IN: { type: 'event', x:4, y:0, endPoint: 'onEvent' } },
28 | outputs: { OUT: { type: 'event', x:5, y:0 } },
29 | controls: {
30 | }
31 | };
32 |
33 | module.exports = noteOnFilter;
--------------------------------------------------------------------------------
/src/synthesizers/disco/editor.js:
--------------------------------------------------------------------------------
1 | exports.create = function (editor, params) {
2 | editor.resize(10, 9);
3 |
4 | editor.addContainer(0, 1, 4, 8);
5 | editor.addContainer(3, 1, 4, 8);
6 | editor.addContainer(6, 1, 4, 8);
7 |
8 | editor.addLabel(1, 0, 2, 'OSC');
9 | editor.addLabel(4, 0, 2, 'MOD');
10 | editor.addLabel(7, 0, 2, 'AMP');
11 |
12 | editor.addLabel(1, 4, 2, 'frq'); editor.addKnob(1, 2).bind(params, 'freq', 300, 2000).autoUpdate();
13 |
14 | editor.addLabel(4, 4, 2, 'mod'); editor.addKnob(4, 2).bind(params, 'mod', -800, 800).autoUpdate();
15 | editor.addLabel(4, 7, 2, 'cv'); editor.addKnob(4, 5).bind(params, 'modCurve', 0.1, 3).autoUpdate();
16 |
17 | editor.addLabel(7, 4, 2, 'd'); editor.addKnob(7, 2).bind(params, 'envDuration', 0.1, 3).autoUpdate();
18 | editor.addLabel(7, 7, 2, 'cv'); editor.addKnob(7, 5).bind(params, 'ampCurve', 0.1, 3).autoUpdate();
19 | };
20 |
--------------------------------------------------------------------------------
/src/synthesizers/disco/index.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../../core/audioContext');
2 | var copyObject = require('../../core/utils').copyObject;
3 |
4 | var SAMPLE_RATE = 44100;
5 | var PI2 = Math.PI * 2;
6 |
7 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
8 | function Osc() {
9 | this.freq = 440;
10 | this.t = 0;
11 | }
12 |
13 | Osc.prototype.reset = function () {
14 | this.t = 0;
15 | };
16 |
17 | Osc.prototype.tic = function () {
18 | var inc = this.freq / SAMPLE_RATE;
19 | this.t += inc;
20 | if (this.t > 1) this.t -= 1;
21 | return Math.sin(this.t * PI2);
22 | };
23 |
24 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
25 | function Envelope() {
26 | this.duration = 1;
27 | this.curve = 0;
28 | this.t = 0;
29 | }
30 |
31 | Envelope.prototype.reset = function () {
32 | this.t = 0;
33 | this.v = 1;
34 | };
35 |
36 | Envelope.prototype.tic = function() {
37 | // TODO: optimize
38 | var d = this.duration * SAMPLE_RATE;
39 | if (this.t > d) return;
40 | this.t += 1;
41 | this.v = Math.pow(1 - this.t / d, this.curve);
42 | };
43 |
44 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
45 | function Synth() {
46 | // components
47 | this.osc = new Osc();
48 | this.modEnv = new Envelope();
49 | this.ampEnv = new Envelope();
50 |
51 | // attributes
52 | this.freq = 440;
53 | this.fmod = 30;
54 | }
55 |
56 | Synth.prototype.reset = function () {
57 | this.osc.reset();
58 | this.modEnv.reset();
59 | this.ampEnv.reset();
60 | };
61 |
62 | Synth.prototype.tic = function () {
63 | // TODO: don't need to update each frame
64 | this.modEnv.tic();
65 | this.ampEnv.tic();
66 |
67 | this.osc.freq = this.freq + this.modEnv.v * this.fmod;
68 |
69 | return this.osc.tic() * this.ampEnv.v;
70 | };
71 |
72 | var synth = new Synth();
73 |
74 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
75 | var DEFAULT_PARAMS = {
76 | freq: 440,
77 | mod: 300,
78 | envDuration: 0.7,
79 | ampCurve: 0.2,
80 | modCurve: 0.5
81 | };
82 |
83 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
84 | exports.generate = function (bufferData, cb) {
85 | // get sound informations
86 | var params = bufferData.params || copyObject(DEFAULT_PARAMS);
87 | var length = params.ampDuration || 1;
88 |
89 | // set synth params
90 | synth.reset();
91 | synth.freq = params.freq || DEFAULT_PARAMS.freq;
92 | synth.fmod = params.mod || DEFAULT_PARAMS.mod;
93 | synth.ampEnv.duration = params.envDuration || DEFAULT_PARAMS.envDuration;
94 | synth.modEnv.duration = params.envDuration || DEFAULT_PARAMS.envDuration;
95 | synth.ampEnv.curve = params.ampCurve || DEFAULT_PARAMS.ampCurve;
96 | synth.modEnv.curve = params.modCurve || DEFAULT_PARAMS.modCurve;
97 |
98 | // create buffer
99 | var buffer = audioContext.createBuffer(1, length * SAMPLE_RATE, SAMPLE_RATE);
100 | var channelData = buffer.getChannelData(0);
101 |
102 | // generate buffer data
103 | for (var i = 0; i < channelData.length; i++) {
104 | channelData[i] = synth.tic();
105 | }
106 |
107 | // append data
108 | bufferData.buffer = buffer;
109 | bufferData.start = 0;
110 | bufferData.end = length;
111 |
112 | // defer the callback
113 | return window.setTimeout(cb, 0);
114 | };
115 |
--------------------------------------------------------------------------------
/src/synthesizers/hats/editor.js:
--------------------------------------------------------------------------------
1 | exports.create = function (editor, params) {
2 | editor.resize(18, 9);
3 |
4 | editor.addContainer( 0, 0, 18, 5);
5 | editor.addContainer( 0, 4, 6, 5);
6 | editor.addContainer( 5, 4, 6, 5);
7 | editor.addContainer(10, 4, 8, 5);
8 |
9 | editor.addLabel(1, 1, 1, 'PATTERN');
10 |
11 | editor.addTextInput(1, 2, 16).bind(params, 'pattern').autoUpdate();
12 | editor.addKnob( 1, 5).bind(params, 'tempo', 50, 300).setAsInt().autoUpdate();
13 | editor.addKnob( 3, 5).bind(params, 'polyphony', 1, 8).setAsInt().autoUpdate();
14 | editor.addKnob( 6, 5).bind(params, 'close', 0.1, 0.8).autoUpdate();
15 | editor.addKnob( 8, 5).bind(params, 'accent', 0.1, 0.8).autoUpdate();
16 | editor.addKnob(15, 5).bind(params, 'offset', 0, 5000).autoUpdate();
17 |
18 | editor.addLabel(1, 7, 2, 'Tmp');
19 | editor.addLabel(3, 7, 2, 'Poly');
20 | editor.addLabel(6, 7, 2, 'Cls');
21 | editor.addLabel(8, 7, 2, 'Acc');
22 | editor.addLabel(14, 7, 3, 'offset');
23 | };
--------------------------------------------------------------------------------
/src/synthesizers/hats/index.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../../core/audioContext');
2 | var loadAudioBuffer = require('../../loaders/loadAudioBuffer');
3 |
4 | var EPSILON = 0.0001;
5 |
6 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7 | function Voice(sample) {
8 | this.sample = sample;
9 | this.head = 0; // position of reading head in the sample
10 | this.decay = 1; // envelope decay
11 | this.env = 1; // current envelope value
12 | this.volume = 1; // final volume (accent)
13 | this.stopped = true;
14 | }
15 |
16 | Voice.prototype.start = function (decay, volume) {
17 | this.decay = decay;
18 | this.volume = volume;
19 | this.head = 0;
20 | this.env = 1;
21 | this.stopped = false;
22 | };
23 |
24 | Voice.prototype.play = function () {
25 | if (this.stopped) return 0;
26 | if (this.head >= this.sample.length) {
27 | this.stopped = true;
28 | return 0;
29 | }
30 |
31 | var sample = this.sample[this.head++];
32 | var volume = this.env * this.volume;
33 | sample *= volume;
34 | this.env *= this.decay;
35 |
36 | if (volume < EPSILON) this.stopped = true;
37 |
38 | return sample;
39 | };
40 |
41 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
42 | exports.generate = function (bufferData, cb) {
43 |
44 | // get sound informations
45 | var params = bufferData.params;
46 | var pattern = params.pattern || 'C...';
47 | var offset = params.offset || 0;
48 | var polyphony = params.polyphony || 1;
49 | var tempo = params.tempo || 120;
50 | var close = params.close || 0.5;
51 | var accent = params.accent || 0.5;
52 |
53 | // load sample buffer
54 | loadAudioBuffer(params.uri, function onBufferLoaded(error, sample) {
55 | if (error) return cb(error);
56 |
57 | // create voices
58 | var sampleLength = sample.length - offset;
59 | var sampleData = new Float32Array(sampleLength);
60 | sample.copyFromChannel(sampleData, 0, offset);
61 | var voices = [];
62 | for (var i = 0; i < polyphony; i++) {
63 | voices.push(new Voice(sampleData));
64 | }
65 |
66 | // pattern values
67 | var sampleRate = sample.sampleRate;
68 | var stepSize = ~~(sampleRate * 15 / tempo); // 1 step = 1/4 beat
69 | var length = stepSize * pattern.length;
70 |
71 | // close ratio
72 | close = Math.pow(close, 10 / sampleLength);
73 |
74 | // note values
75 | var NOTES = {
76 | 'O': { volume: 1, decay: 1 },
77 | 'o': { volume: accent, decay: 1 },
78 | 'C': { volume: 1, decay: close },
79 | 'c': { volume: accent, decay: close }
80 | };
81 |
82 | // create pattern buffer
83 | var buffer = audioContext.createBuffer(1, length, sampleRate);
84 |
85 | // generate buffer data
86 | var channelData = buffer.getChannelData(0);
87 | var step = 0;
88 | var stepPosition = 0;
89 | var currentVoice = 0;
90 | for (var t = 0; t < length; t++) {
91 | if (stepPosition-- === 0) {
92 | stepPosition = stepSize;
93 |
94 | // trigger next note
95 | var note = NOTES[pattern[step]];
96 | // TODO: '|'
97 | if (note) {
98 | currentVoice = (currentVoice + 1) % voices.length;
99 | voices[currentVoice].start(note.decay, note.volume);
100 | }
101 | step++;
102 | }
103 | var value = 0;
104 | for (var i = 0; i < voices.length; i++) {
105 | value += voices[i].play();
106 | }
107 | channelData[t] = value;
108 | }
109 |
110 | // TODO: continue to fill buffer from buffer start if voices are not stopped
111 |
112 | // append data
113 | bufferData.buffer = buffer;
114 | bufferData.start = 0;
115 | bufferData.end = buffer.duration;
116 |
117 | return cb();
118 | });
119 | }
--------------------------------------------------------------------------------
/src/synthesizers/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Synthesizers can be added at runtime (before a patch requiring it is loaded)
3 | *
4 | * a synth is a module that expose a method `generate` taking two parameters:
5 | * - a {BufferData} instance, with informations about synth in bufferData.params
6 | * - a callback {function} to call once the synth has filled the buffer with audio
7 | *
8 | * The synth is responsible for creating the audio buffer, and filling it and set
9 | * the `start` and `end` properties of the bufferData.
10 | *
11 | * Callback should be deffered if the generate function is synchronous, in order
12 | * to be consistent with Buffer API that needs to load audio.
13 | */
14 |
15 | var synthEditor = require('../ui/synthEditor');
16 |
17 | var SYNTHESIZERS = {
18 | 'noize': require('./noize'),
19 | 'hats': require('./hats'),
20 | 'disco': require('./disco'),
21 | };
22 |
23 | exports.getSynth = function (id) {
24 | return SYNTHESIZERS[id];
25 | };
26 |
27 | exports.addSynth = function (id, synth, editor) {
28 | SYNTHESIZERS[id] = synth;
29 | if (editor) synthEditor.register(id, editor);
30 | };
31 |
--------------------------------------------------------------------------------
/src/synthesizers/noize/index.js:
--------------------------------------------------------------------------------
1 | var audioContext = require('../../core/audioContext');
2 |
3 | var SAMPLE_RATE = 44100;
4 | var m_brown = 0.0;
5 |
6 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7 | function white() {
8 | return Math.random() - 0.5;
9 | }
10 |
11 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12 | function brown() {
13 | while (true) {
14 | var r = white();
15 | m_brown += r;
16 | if (m_brown < -8.0 || m_brown > 8.0) m_brown -= r;
17 | else break;
18 | }
19 | return m_brown / 16;
20 | }
21 |
22 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
23 | function colored(color) {
24 | return brown() * color + white() * (1 - color);
25 | }
26 |
27 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
28 | exports.generate = function (bufferData, cb) {
29 | // get sound informations
30 | var params = bufferData.params;
31 | var length = params.length || 1;
32 | var color = params.color || 0;
33 | var noisef = colored;
34 | if (color < 0) noisef = white;
35 | if (color > 1) noisef = brown;
36 |
37 | // create buffer
38 | var buffer = audioContext.createBuffer(1, length * SAMPLE_RATE, SAMPLE_RATE);
39 |
40 | // generate buffer data
41 | var channelData = buffer.getChannelData(0);
42 | for (var i = 0; i < channelData.length; i++) {
43 | channelData[i] = noisef(color);
44 | }
45 |
46 | // append data
47 | bufferData.buffer = buffer;
48 | bufferData.start = 0;
49 | bufferData.end = length;
50 |
51 | // defer the callback
52 | return window.setTimeout(cb, 0);
53 | }
--------------------------------------------------------------------------------
/src/ui/Panel.js:
--------------------------------------------------------------------------------
1 | var domUtils = require('./domUtils');
2 | var createDiv = domUtils.createDiv;
3 | var makeButton = domUtils.makeButton;
4 | var makeDragable = domUtils.makeDragable;
5 |
6 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7 | /** Panel
8 | *
9 | * @author Cedric Stoquer
10 | */
11 | function Panel() {
12 | this._dom = createDiv('panel');
13 |
14 | var t = this;
15 |
16 | // handle header for drag & move
17 | var handle = createDiv('handle', this._dom);
18 | makeDragable(handle, this._dom);
19 |
20 | // zIndex
21 | handle.addEventListener('mousedown', function (e) {
22 | t.setOnTop();
23 | });
24 |
25 | // title
26 | this.title = createDiv('panelTitle', handle);
27 |
28 | // close button
29 | var closeButton = createDiv('closeButton', handle);
30 | makeButton(closeButton, function onPress() {
31 | t.close();
32 | });
33 | }
34 |
35 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
36 | var topPanel = null;
37 | Panel.prototype.setOnTop = function () {
38 | if (topPanel) topPanel.style.zIndex = null;
39 | this._dom.style.zIndex = 11;
40 | topPanel = this._dom;
41 | };
42 |
43 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
44 | Panel.prototype.setTitle = function (title) {
45 | this.title.innerText = title;
46 | };
47 |
48 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
49 | Panel.prototype.open = function () {
50 | this._dom.style.display = '';
51 | this.setOnTop();
52 | };
53 |
54 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
55 | Panel.prototype.close = function () {
56 | // TODO update checkbox in menu header
57 | this._dom.style.display = 'none';
58 | };
59 |
60 | module.exports = Panel;
61 |
--------------------------------------------------------------------------------
/src/ui/beforeClose.js:
--------------------------------------------------------------------------------
1 | var sendRequest = require('../loaders/sendRequest');
2 |
3 | var FLAGS = {
4 | audio: false
5 | };
6 |
7 | exports.setFlag = function (id) {
8 | FLAGS[id] = true;
9 | };
10 |
11 | window.onbeforeunload = function beforeClosing() {
12 | if (FLAGS.audio) sendRequest({ command: 'audio.generateLibrary' });
13 | };
14 |
--------------------------------------------------------------------------------
/src/ui/buttonGUI.js:
--------------------------------------------------------------------------------
1 | var Button = require('../core/Button');
2 | var constants = require('./constants');
3 | var domUtils = require('./domUtils');
4 | var createDiv = domUtils.createDiv;
5 |
6 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7 | Button.prototype.initGUI = function (module, id, descriptor) {
8 | // create dom
9 | var dom = this._dom = createDiv('moduleButton', module._dom);
10 | dom.style.left = (descriptor.x * constants.CONNECTOR_GRID_SIZE) + 'px';
11 | dom.style.top = (descriptor.y * constants.CONNECTOR_GRID_SIZE) + 'px';
12 | if (descriptor.label) createDiv('label knobLabel', dom).innerText = descriptor.label;
13 | this._title = createDiv('moduleButtonTitle', dom);
14 |
15 | // set mouse event
16 | var t = this;
17 | dom.addEventListener('mousedown', function click(e) {
18 | e.stopPropagation();
19 | e.preventDefault();
20 | t.endPoint.call(t.caller);
21 | });
22 | };
23 |
24 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
25 | Button.prototype.setTitle = function (text) {
26 | this._title.innerText = text;
27 | };
28 |
--------------------------------------------------------------------------------
/src/ui/cableGUI.js:
--------------------------------------------------------------------------------
1 | var Cable = require('../core/Cable');
2 | var constants = require('./constants');
3 | var ctx = require('./overlay').ctx;
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | Cable.prototype.draw = function () {
7 | ctx.strokeStyle = this.color;
8 | ctx.beginPath();
9 | ctx.moveTo(this.x, this.y);
10 | ctx.bezierCurveTo(this.a, this.b, this.c, this.d, this.w, this.h);
11 | ctx.stroke();
12 | };
13 |
14 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15 | Cable.prototype.update = function () {
16 | this.x = this.endPointA.module.x * constants.MODULE_WIDTH + this.endPointA.x * constants.CONNECTOR_GRID_SIZE + 8;
17 | this.y = this.endPointA.module.y * constants.MODULE_HEIGHT + this.endPointA.y * constants.CONNECTOR_GRID_SIZE + 8;
18 | this.w = this.endPointB.module.x * constants.MODULE_WIDTH + this.endPointB.x * constants.CONNECTOR_GRID_SIZE + 8;
19 | this.h = this.endPointB.module.y * constants.MODULE_HEIGHT + this.endPointB.y * constants.CONNECTOR_GRID_SIZE + 8;
20 |
21 | var w = (this.w - this.x) / 2;
22 | var h = (this.h - this.y) / 2;
23 |
24 | this.a = ~~(this.x + w * Math.random() + 10 * Math.random() - 5);
25 | this.b = ~~(this.y + h * Math.random() + 10 * Math.random() - 5);
26 | this.c = ~~(this.x + w * (Math.random() + 1) + 10 * Math.random() - 5);
27 | this.d = ~~(this.y + h * (Math.random() + 1) + 10 * Math.random() - 5);
28 | };
29 |
--------------------------------------------------------------------------------
/src/ui/connectorGUI.js:
--------------------------------------------------------------------------------
1 | var Connector = require('../core/Connector');
2 | var constants = require('./constants');
3 | var createDiv = require('./domUtils').createDiv;
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | Connector.prototype.initGUI = function (module, id, descriptor) {
7 | var dom = this._dom = createDiv('connector ' + this.cssClassName, module._dom);
8 | if (descriptor.label) createDiv('label connectorLabel', dom).innerText = descriptor.label;
9 |
10 | if (this.x === undefined) {
11 | // TODO: remove this
12 | dom.style.position = 'relative'
13 | } else {
14 | dom.style.left = (this.x * constants.CONNECTOR_GRID_SIZE + 1) + 'px';
15 | dom.style.top = (this.y * constants.CONNECTOR_GRID_SIZE + 1) + 'px';
16 | }
17 |
18 | dom.connector = this;
19 |
20 | var t = this;
21 | dom.addEventListener('mousedown', function mouseStart(e) {
22 | e.stopPropagation();
23 | e.preventDefault();
24 | window.moduleManager.startConnection(t, e);
25 | });
26 | };
27 |
28 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
29 | Connector.prototype.setState = function () {
30 | this._dom.className = 'connector ' + this.cssClassName + (this._nConnection > 0 ? '-fill' : '');
31 | };
--------------------------------------------------------------------------------
/src/ui/constants.js:
--------------------------------------------------------------------------------
1 | exports.MODULE_WIDTH = 92;
2 | exports.MODULE_HEIGHT = 16;
3 | exports.CONNECTOR_GRID_SIZE = 15;
4 |
5 | // creating CSS class for module size accordingly to constants
6 | (function createModuleSizeStyle() {
7 | var cssStyle = document.createElement('style');
8 | cssStyle.type = 'text/css';
9 | var w = exports.MODULE_WIDTH - 3; // border is 1px, hence the -2
10 | for (var i = 1; i < 10; i++) {
11 | var h = exports.MODULE_HEIGHT * i - 3;
12 | var rules = document.createTextNode('.x' + i + ' { width: ' + w + 'px; height: ' + h + 'px; }');
13 | cssStyle.appendChild(rules);
14 | document.getElementsByTagName('head')[0].appendChild(cssStyle);
15 | }
16 | })();
17 |
--------------------------------------------------------------------------------
/src/ui/domUtils.js:
--------------------------------------------------------------------------------
1 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
2 | /** @module domUtils
3 | * @desc dom utilities
4 | * @author Cedric Stoquer
5 | */
6 | var DOCUMENT_BODY = document.getElementsByTagName('body')[0];
7 |
8 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
9 | exports.createDom = function (type, className, parent) {
10 | parent = parent || DOCUMENT_BODY;
11 | var dom = document.createElement(type);
12 | parent.appendChild(dom);
13 | if (className) dom.className = className;
14 | return dom;
15 | };
16 |
17 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
18 | exports.createDiv = function (className, parent) {
19 | return exports.createDom('div', className, parent);
20 | };
21 |
22 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
23 | exports.removeDom = function (dom, parent) {
24 | parent = parent || DOCUMENT_BODY;
25 | parent.removeChild(dom);
26 | };
27 |
28 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
29 | exports.makeButton = function (dom, onClic) {
30 | dom.addEventListener('mousedown', function (e) {
31 | e.stopPropagation();
32 | e.preventDefault();
33 | onClic(e, dom);
34 | });
35 | return dom;
36 | };
37 |
38 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
39 | function startDrag(dom, e) {
40 | var d = document;
41 |
42 | rect = dom.getBoundingClientRect();
43 |
44 | var startX = e.clientX - rect.left;
45 | var startY = e.clientY - rect.top;
46 |
47 | function dragMove(e) {
48 | e.preventDefault();
49 | dom.style.left = (e.clientX - startX) + 'px';
50 | dom.style.top = (e.clientY - startY) + 'px';
51 | }
52 |
53 | function dragEnd(e) {
54 | e.preventDefault();
55 | d.removeEventListener('mouseup', dragEnd);
56 | d.removeEventListener('mousemove', dragMove);
57 | }
58 |
59 | d.addEventListener('mousemove', dragMove, false);
60 | d.addEventListener('mouseup', dragEnd, false);
61 | }
62 |
63 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
64 | exports.makeDragable = function (handle, target) {
65 | target = target || handle;
66 | handle.addEventListener('mousedown', function (e) {
67 | e.stopPropagation();
68 | e.preventDefault();
69 | startDrag(target, e);
70 | });
71 | return handle;
72 | };
73 |
--------------------------------------------------------------------------------
/src/ui/dropFile.js:
--------------------------------------------------------------------------------
1 | var audioEditor = require('./audioEditor');
2 | var BufferData = require('../data/BufferData');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | function readJson(file) {
6 | var reader = new FileReader();
7 | reader.onload = function (e) {
8 | var contents = e.target.result;
9 | var data;
10 | try {
11 | data = JSON.parse(contents);
12 | } catch (error) {
13 | return console.error(error);
14 | }
15 | if (data._type && data._type === 'modularPatch') {
16 | window.moduleManager.setPatch(data);
17 | }
18 | };
19 | reader.readAsText(file);
20 | }
21 |
22 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
23 | function readMp3(file) {
24 | var id = file.name.split('.');
25 | id.pop();
26 | id = id.join('.');
27 |
28 | // create a bufferData for this file if it doesn't exist yet
29 | var bufferData = window.assets.buffers[id];
30 | if (!bufferData) {
31 | bufferData = new BufferData(id, { uri: 'audio/' + file.name });
32 | }
33 | audioEditor.setBuffer(bufferData);
34 | audioEditor.open();
35 | }
36 |
37 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
38 | function openFiles(e) {
39 | var files = e.dataTransfer.files;
40 | var file = files[0]; // TODO: all files
41 |
42 | var ext = file.name.split('.').pop();
43 |
44 | switch (ext) {
45 | case 'json': readJson(file); break;
46 | case 'mp3': readMp3(file); break;
47 | }
48 | }
49 |
50 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
51 | // drag & drop patch files
52 | document.body.addEventListener('dragover', function handleDragOver(e) {
53 | e.stopPropagation();
54 | e.preventDefault();
55 | e.dataTransfer.dropEffect = 'copy';
56 | }, false);
57 |
58 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
59 | document.body.addEventListener('drop', function (e) {
60 | e.stopPropagation();
61 | e.preventDefault();
62 | openFiles(e);
63 | }, false);
64 |
65 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
66 | document.addEventListener('drop', function (e) {
67 | e.preventDefault();
68 | e.stopPropagation();
69 | openFiles(e);
70 | });
71 |
72 | document.addEventListener('dragover', function (e) {
73 | e.preventDefault();
74 | e.stopPropagation();
75 | });
76 |
--------------------------------------------------------------------------------
/src/ui/knobGUI.js:
--------------------------------------------------------------------------------
1 | var Knob = require('../core/Knob');
2 | var constants = require('./constants');
3 | var createDiv = require('./domUtils').createDiv;
4 |
5 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6 | Knob.prototype.initGUI = function (module, id, descriptor) {
7 | // create dom elements
8 | var dom = this._dom = createDiv('knob', module._dom);
9 | dom.style.left = (this.x * constants.CONNECTOR_GRID_SIZE + 2) + 'px';
10 | dom.style.top = (this.y * constants.CONNECTOR_GRID_SIZE + 2) + 'px';
11 | this._mark = createDiv('knob knobMark', dom);
12 | if (descriptor.label) createDiv('label knobLabel', dom).innerText = descriptor.label;
13 |
14 | this.valueDiplay = createDiv('knobValueDisplay', dom);
15 | this.valueDiplay.style.display = 'none';
16 | if (!descriptor.label) this.valueDiplay.className += ' knobValueNoLabel';
17 |
18 | var t = this;
19 | dom.addEventListener('mousedown', function mouseStart(e) {
20 | e.stopPropagation();
21 | e.preventDefault();
22 |
23 | // var startX = e.clientX;
24 | var startY = e.clientY;
25 | var startV = t.value;
26 | t.valueDiplay.style.display = '';
27 |
28 |
29 | function mouseMove(e) {
30 | e.preventDefault();
31 | var delta = Math.max(-68, Math.min(68, startV + startY - e.clientY));
32 | t._mark.style.transform = 'rotate(' + (delta * 2) + 'deg)';
33 | t.value = delta;
34 | // TODO: add an option to not updating value in real time
35 | t.updateValue();
36 | }
37 |
38 | function mouseUp(e) {
39 | e.preventDefault();
40 | document.removeEventListener('mousemove', mouseMove);
41 | document.removeEventListener('mouseup', mouseUp);
42 | t.valueDiplay.style.display = 'none';
43 | t.updateValue();
44 | }
45 |
46 | document.addEventListener('mousemove', mouseMove, false);
47 | document.addEventListener('mouseup', mouseUp, false);
48 | });
49 | };
50 |
51 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
52 | Knob.prototype.updateGUI = function () {
53 | this._mark.style.transform = 'rotate(' + (this.value * 2) + 'deg)';
54 | };
55 |
56 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
57 | Knob.prototype.displayValue = function (value) {
58 | // TODO: custom display
59 |
60 | var size = 5;
61 | var multiplier = '';
62 |
63 | if (value === 0) {
64 | // keep 0;
65 | } else if (Math.abs(value) < 0.1) {
66 | value *= 1000;
67 | multiplier = 'm';
68 | size = 4;
69 | } else if (Math.abs(value) >= 1000) {
70 | value /= 1000;
71 | multiplier = 'K';
72 | size = 4;
73 | }
74 |
75 | this.valueDiplay.innerText = value.toString().substring(0, size) + multiplier;;
76 | };
--------------------------------------------------------------------------------
/src/ui/moduleLibrary.js:
--------------------------------------------------------------------------------
1 | var Panel = require('./Panel');
2 | var categories = require('../core/moduleCategories');
3 | var modules = require('../core/modules');
4 | var domUtils = require('./domUtils');
5 | var makeButton = domUtils.makeButton;
6 | var createDiv = domUtils.createDiv;
7 |
8 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
9 | /** ModuleLibrary
10 | *
11 | * @author Cedric Stoquer
12 | */
13 | function ModuleLibrary() {
14 | Panel.call(this);
15 | this._dom.style.left = '150px'; // TODO
16 |
17 | this.current = null; // currently opened id
18 | this.tabs = {}; // map of tabs by id
19 | this.lists = {}; // map of list by tab id
20 | this.tabHolder = createDiv('libraryTabHolder', this._dom);
21 | this.listHolder = createDiv('libraryListHolder', this._dom);
22 | // this.list = createDiv('libraryList', this._dom);
23 |
24 | // create tabs from categories
25 | for (var category in categories) {
26 | this.addTab(categories[category]);
27 | }
28 |
29 | this.addEntries(modules.getList());
30 | }
31 | inherits(ModuleLibrary, Panel);
32 |
33 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
34 | ModuleLibrary.prototype.addTab = function (id) {
35 | if (this.tabs[id]) return this.tabs[id];
36 | var tab = createDiv('libraryTab', this.tabHolder);
37 | var list = createDiv('libraryList', this.listHolder);
38 | list.style.height = '150px';
39 | list.style.display = 'none';
40 |
41 | tab.innerText = id;
42 |
43 | this.tabs[id] = tab;
44 | this.lists[id] = list;
45 |
46 | if (!this.current) this.selectTab(id);
47 |
48 | var self = this;
49 | makeButton(tab, function onClick() {
50 | self.selectTab(id);
51 | });
52 |
53 | return tab;
54 | };
55 |
56 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
57 | ModuleLibrary.prototype.selectTab = function (id) {
58 | if (this.current === id) return;
59 |
60 | if (this.current) {
61 | this.lists[this.current].style.display = 'none';
62 | this.tabs[this.current].style.backgroundColor = '';
63 | }
64 |
65 | this.current = id;
66 | this.lists[id].style.display = '';
67 | this.tabs[this.current].style.backgroundColor = '#FF0';
68 | };
69 |
70 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
71 | ModuleLibrary.prototype.addEntries = function (library) {
72 | for (var id in library) {
73 | this.addEntry(library[id]);
74 | }
75 | };
76 |
77 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
78 | ModuleLibrary.prototype.addEntry = function (ModuleConstructor) {
79 | var descriptor = ModuleConstructor.prototype.descriptor;
80 | var category = descriptor._category;
81 |
82 | if (!descriptor.name) return;
83 |
84 | // add entry in UI window
85 | var list = this.lists[category];
86 | if (!list) {
87 | // TODO
88 | return console.error('category is not registered');
89 | }
90 |
91 | var button = createDiv('libraryEntry', list);
92 | button.textContent = descriptor.name;
93 | button.addEventListener('mousedown', function onClick(e) {
94 | var module = window.moduleManager.addModule(new ModuleConstructor());
95 | window.moduleManager.startDrag(module, e);
96 | });
97 |
98 | // TODO: tags
99 | };
100 |
101 | var moduleLibrary = new ModuleLibrary();
102 | module.exports = moduleLibrary;
103 |
--------------------------------------------------------------------------------
/src/ui/onWindowResize.js:
--------------------------------------------------------------------------------
1 | var resetCanvas = require('./overlay').reset;
2 | var moduleManager = require('./moduleManager');
3 |
4 | var timeout = null;
5 |
6 | window.addEventListener('resize', function (e) {
7 | if (timeout !== null) {
8 | window.clearTimeout(timeout);
9 | }
10 |
11 | timeout = window.setTimeout(function () {
12 | resetCanvas();
13 | moduleManager.drawCables();
14 | timeout = null;
15 | }, 50);
16 | });
--------------------------------------------------------------------------------
/src/ui/overlay.js:
--------------------------------------------------------------------------------
1 | function resizeCanvas(canvas) {
2 | canvas.height = window.innerHeight;
3 | canvas.width = window.innerWidth;
4 | canvas.style.width = canvas.width + 'px';
5 | canvas.style.height = canvas.height + 'px';
6 | }
7 |
8 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
9 | var canvas = document.getElementById('cableCanvas');
10 | var overlay = document.getElementById('overlayCanvas');
11 | var ctx = canvas.getContext('2d');
12 | var overCtx = overlay.getContext('2d');
13 |
14 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15 | function resetCanvas() {
16 | resizeCanvas(canvas);
17 | resizeCanvas(overlay);
18 |
19 | ctx.lineCap = 'round';
20 | ctx.shadowColor = '#000';
21 | ctx.shadowBlur = 3;
22 | ctx.lineWidth = 3;
23 | ctx.shadowOffsetX = 1;
24 | ctx.shadowOffsetY = 1;
25 |
26 | overCtx.lineWidth = 3;
27 | overCtx.strokeStyle = '#444';
28 | overCtx.lineCap = 'butt';
29 | overCtx.setLineDash([3, 3]);
30 | }
31 |
32 | resetCanvas();
33 |
34 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
35 | exports.ctx = ctx;
36 | exports.overCtx = overCtx;
37 | exports.reset = resetCanvas;
38 |
--------------------------------------------------------------------------------
/src/ui/synthEditor/Container.js:
--------------------------------------------------------------------------------
1 | var constants = require('./constants');
2 | var domUtils = require('../domUtils');
3 | var createDom = domUtils.createDom;
4 | var createDiv = domUtils.createDiv;
5 | var makeButton = domUtils.makeButton;
6 | var removeDom = domUtils.removeDom;
7 | var GRID_SIZE = constants.GRID_SIZE;
8 |
9 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
10 | function Container(parent) {
11 | this.dom = createDiv('synthEdit-container', parent.dom);
12 | }
13 | module.exports = Container;
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | /** set size in unit (grid based) */
17 | Container.prototype.rect = function(x, y, w, h) {
18 | this.dom.style.left = x * GRID_SIZE + 'px';
19 | this.dom.style.top = y * GRID_SIZE + 'px';
20 | this.dom.style.width = w * GRID_SIZE - 20 + 'px';
21 | this.dom.style.height = h * GRID_SIZE - 20 + 'px';
22 | return this;
23 | };
--------------------------------------------------------------------------------
/src/ui/synthEditor/Label.js:
--------------------------------------------------------------------------------
1 | var constants = require('./constants');
2 | var domUtils = require('../domUtils');
3 | var createDom = domUtils.createDom;
4 | var createDiv = domUtils.createDiv;
5 | var makeButton = domUtils.makeButton;
6 | var removeDom = domUtils.removeDom;
7 | var GRID_SIZE = constants.GRID_SIZE;
8 |
9 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
10 | function Label(parent) {
11 | this.dom = createDiv('synthEdit-label', parent.dom);
12 | }
13 | module.exports = Label;
14 |
15 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16 | Label.prototype.position = function (x, y, w) {
17 | this.dom.style.left = x * GRID_SIZE + 'px';
18 | this.dom.style.top = y * GRID_SIZE + 'px';
19 | this.dom.style.width = w * GRID_SIZE + 'px';
20 | return this;
21 | };
22 |
23 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
24 | Label.prototype.text = function (text) {
25 | this.dom.innerText = text;
26 | return this;
27 | };
28 |
29 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
30 | Label.prototype.color = function (color) {
31 | this.dom.style.color = constants.getColor(color).hi;
32 | return this;
33 | };
--------------------------------------------------------------------------------
/src/ui/synthEditor/TextInput.js:
--------------------------------------------------------------------------------
1 | var constants = require('./constants');
2 | var domUtils = require('../domUtils');
3 | var map = require('../../core/utils').map;
4 | var createDom = domUtils.createDom;
5 | var createDiv = domUtils.createDiv;
6 | var makeButton = domUtils.makeButton;
7 | var GRID_SIZE = constants.GRID_SIZE;
8 |
9 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
10 | function TextInput(parent) {
11 | this.editor = parent; // TODO: should we allow parent to be any else than editor?
12 | this.dom = createDom('input', 'synthEdit-textInput', parent.dom);
13 | this._obj = null;
14 | this._attribute = null;
15 | this._autoUpdate = false;
16 |
17 | this.dom.type = 'text';
18 | this._initMouseEvents();
19 | }
20 | module.exports = TextInput;
21 |
22 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
23 | TextInput.prototype.bind = function (obj, attribute) {
24 | this._obj = obj;
25 | this._attribute = attribute;
26 |
27 | this.dom.value = obj[attribute];
28 | return this;
29 | };
30 |
31 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
32 | TextInput.prototype.autoUpdate = function () {
33 | this._autoUpdate = true;
34 | return this;
35 | };
36 |
37 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
38 | TextInput.prototype.position = function (x, y, w) {
39 | this.dom.style.left = x * GRID_SIZE + 'px';
40 | this.dom.style.top = y * GRID_SIZE + 'px';
41 | this.dom.style.width = w * GRID_SIZE + 'px';
42 | return this;
43 | };
44 |
45 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
46 | TextInput.prototype._initMouseEvents = function () {
47 | var self = this;
48 | this.dom.addEventListener('change', function (e) {
49 | if (!self._obj) return;
50 | self._obj[self._attribute] = self.dom.value;
51 | if (self._autoUpdate) {
52 | self.editor.updateBuffer();
53 | }
54 | });
55 | };
56 |
57 |
--------------------------------------------------------------------------------
/src/ui/synthEditor/constants.js:
--------------------------------------------------------------------------------
1 | exports.GRID_SIZE = 16;
2 |
3 | var COLORS = [
4 | { hi: '#BBB', low: '#444' },
5 | { hi: '#FB0203', low: '#400615' },
6 | { hi: '#07FC0B', low: '#024415' },
7 | { hi: '#00F', low: '#004' },
8 | { hi: '#FF0', low: '#440' },
9 | { hi: '#FF02FF', low: '#410158' },
10 | { hi: '#61F5E2', low: '#275359' },
11 | ];
12 |
13 | exports.COLORS = COLORS;
14 |
15 | var colorLength = COLORS.length;
16 |
17 | exports.getColor = function (color) {
18 | return COLORS[~~color % colorLength];
19 | };
--------------------------------------------------------------------------------
/src/ui/synthEditor/index.js:
--------------------------------------------------------------------------------
1 | var SynthEditorPanel = require('./SynthEditorPanel');
2 |
3 | var synthEditorPanel = new SynthEditorPanel();
4 | // module.exports = synthEditor;
5 | // window.synthEditor = synthEditor;
6 |
7 |
8 | var editorBuilders = {};
9 |
10 | exports.register = function (synthId, editorBuilder) {
11 | editorBuilders[synthId] = editorBuilder;
12 | };
13 |
14 | exports.hasEditor = function (synthId) {
15 | return !!editorBuilders[synthId];
16 | };
17 |
18 | exports.open = function (synthId, bufferData) {
19 | synthEditorPanel.init(synthId, bufferData);
20 | var editorBuilder = editorBuilders[synthId];
21 | if (!editorBuilder) return console.error('there is no editor for synth "' + synthId + '"');
22 | editorBuilder.create(synthEditorPanel, bufferData.params);
23 | // TODO: bind editor header menu with bufferData
24 | // - open audio editor
25 | // - play
26 | // - generate
27 | // - loop property
28 | // - tags ?
29 | // - save ?
30 |
31 | synthEditorPanel.open();
32 | synthEditorPanel.setOnTop();
33 | };
34 |
--------------------------------------------------------------------------------
/tools/commands.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var path = require('path');
3 |
4 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5 | var commands = {};
6 |
7 | function addCommandModule(moduleName, modulePath) {
8 | var module = require(modulePath);
9 | commands[moduleName] = {};
10 | for (var keys = Object.keys(module), i = 0; i < keys.length; i++) {
11 | var commandName = keys[i];
12 | var command = module[commandName];
13 | if (typeof command !== 'function') continue;
14 | commands[moduleName][commandName] = command;
15 | }
16 | }
17 |
18 | function getCommandModules(dir) {
19 | var fileList = fs.readdirSync(dir);
20 |
21 | for (var i = 0; i < fileList.length; i++) {
22 | var fileName = fileList[i];
23 | var moduleName = path.parse(fileName).name;
24 | var modulePath = path.join(dir, fileName);
25 | addCommandModule(moduleName, modulePath);
26 | }
27 | }
28 |
29 | getCommandModules(path.join(process.cwd(), 'tools/commands')); // project's custom commands
30 |
31 | function noop() {};
32 |
33 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
34 | function commandRequest(message, cb) {
35 | cb = cb || noop;
36 |
37 | if (!message.command) return cb('Empty command');
38 | var command = message.command.split('.');
39 | if (command.length !== 2) return cb('Incorrect command format');
40 |
41 | var moduleId = command[0];
42 | var commandId = command[1];
43 |
44 | if (!commands[moduleId] || !commands[moduleId][commandId]) return cb('Unknown command');
45 |
46 | console.log('\033[101mCOMMAND\033[0m ' + moduleId + '.' + commandId);
47 | commands[moduleId][commandId](message, cb);
48 | }
49 |
50 | module.exports = commandRequest;
51 |
--------------------------------------------------------------------------------
/tools/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "disposition": {
3 | "mapEditor": {
4 | "x": 128,
5 | "y": 21,
6 | "width": 280,
7 | "height": 216
8 | },
9 | "tilesheet": {
10 | "x": 416,
11 | "y": 21,
12 | "zoom": 2
13 | },
14 | "tileBrush": {
15 | "x": 0,
16 | "y": 181,
17 | "width": 120,
18 | "height": 56,
19 | "foldConfig": {}
20 | },
21 | "palette": {
22 | "x": 0,
23 | "y": 261
24 | },
25 | "fileExplorer": {
26 | "x": 0,
27 | "y": 21,
28 | "width": 120,
29 | "height": 136,
30 | "assets": {
31 | "maps.json": {}
32 | }
33 | }
34 | },
35 | "windowSize": {
36 | "width": 685,
37 | "height": 331
38 | }
39 | }
--------------------------------------------------------------------------------