├── package.json
├── lib
├── get-voltage.js
└── augmented-on-ended.js
├── demo
├── index.html
├── main.js
└── bundle.js
├── readme.md
└── index.js
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "snare",
3 | "version": "2.8.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "noise-buffer": "^1.0.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/get-voltage.js:
--------------------------------------------------------------------------------
1 | module.exports = function getVoltage(context) {
2 | var buffer = context.createBuffer(1, 2, 44100);
3 | var data = buffer.getChannelData(0);
4 | data[0] = 1;
5 | data[1] = 1;
6 | var source = context.createBufferSource();
7 | source.buffer = buffer;
8 | source.loop = true;
9 | return source;
10 | }
11 |
--------------------------------------------------------------------------------
/lib/augmented-on-ended.js:
--------------------------------------------------------------------------------
1 | module.exports = function augmentedOnEnded(triggerNode, fn) {
2 | var context = triggerNode.context;
3 | if (context instanceof (window.OfflineAudioContext || window.webkitOfflineAudioContext)) {
4 | context.suspend().then(function() {
5 | fn();
6 | context.resume();
7 | });
8 | } else if (context instanceof (window.AudioContext || window.webkitAudioContext)) {
9 | triggerNode.onended = fn;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
15 | Snare
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ##Usage
2 |
3 | `npm install --save snare`
4 |
5 | ```javascript
6 | var Snare = require('snare');
7 |
8 | // Initialize AudioContext
9 | var context = new AudioContext();
10 |
11 | // Initialize instrument
12 | var snare = Snare(context);
13 |
14 | // Create snare audio node (one time use only)
15 | var snareNode = snare();
16 |
17 | // Connect to target node
18 | snareNode.connect(context.destination);
19 |
20 | /*
21 | * detune is connected to the oscillators' detune
22 | */
23 | snareNode.detune instanceof AudioParam
24 | // -> true
25 | snareNode.detune.value = 1200;
26 |
27 | /*
28 | * snappy controls the gain of the noise
29 | */
30 | snareNode.snappy instanceof AudioParam
31 | // -> true
32 | snareNode.snappy.value = 0.75
33 |
34 | /*
35 | * tone controls the mix between low and high tones
36 | */
37 | snareNode.tone instanceof AudioParam
38 | // -> true
39 | snareNode.tone.value = 0.5
40 |
41 | /*
42 | * duration defaults to 0.3
43 | */
44 | snareNode.duration = 0.2
45 |
46 | // Start
47 | snareNode.start(context.currentTime);
48 | ```
49 |
--------------------------------------------------------------------------------
/demo/main.js:
--------------------------------------------------------------------------------
1 | var Snare = require('../index');
2 | var snare;
3 |
4 | var ct = 0;
5 |
6 | window.reset = function reset() {
7 | if (window.context) {
8 | window.context.close();
9 | }
10 | var context = new AudioContext();
11 | window.context = context;
12 | snare = Snare(context);
13 | }
14 | reset();
15 |
16 | document.getElementById('play').addEventListener('click', function(e) {
17 | if (ct % 100 === 0) {
18 | console.log(ct);
19 | }
20 | ct++;
21 |
22 | /*
23 | var osc = context.createOscillator();
24 | osc.connect(context.destination);
25 | osc.start(context.currentTime);
26 | osc.stop(context.currentTime + 0.03);
27 | */
28 | var snareNode = snare();
29 |
30 | snareNode.detune.value = parseInt(document.getElementById('detune').value);
31 | snareNode.duration = parseFloat(document.getElementById('duration').value);
32 | snareNode.snappy.value = parseFloat(document.getElementById('snappy').value);
33 | snareNode.tone.value = parseFloat(document.getElementById('tone').value);
34 |
35 | snareNode.connect(context.destination);
36 | snareNode.start(context.currentTime);
37 | });
38 |
39 | var interval = false;
40 | document.getElementById('loop').addEventListener('click', function(e) {
41 | if (typeof interval === 'number') {
42 | clearInterval(interval);
43 | interval = false;
44 | } else {
45 | interval = setInterval(function() {
46 | document.getElementById('play').click();
47 | }, 70);
48 | document.getElementById('play').click();
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var NoiseBuffer = require('noise-buffer');
2 | var augmentedOnEnded = require('./lib/augmented-on-ended');
3 | var getVoltage = require('./lib/get-voltage');
4 |
5 |
6 | module.exports = function(context, parameters) {
7 |
8 | var playingNodes = [];
9 |
10 | parameters = parameters || {};
11 | parameters.tone = typeof parameters.tone === 'number' ? parameters.tone : 64;
12 | parameters.snappy = typeof parameters.snappy === 'number' ? parameters.snappy : 64;
13 |
14 | var noiseBuffer = NoiseBuffer(1);
15 |
16 | return function() {
17 |
18 | var masterHighBump = context.createBiquadFilter();
19 | masterHighBump.frequency.value = 4000;
20 | masterHighBump.gain.value = 6;
21 | masterHighBump.type = "peaking";
22 | var masterLowBump = context.createBiquadFilter();
23 | masterLowBump.frequency.value = 200;
24 | masterLowBump.gain.value = 12;
25 | masterLowBump.type = "peaking";
26 | masterHighBump.connect(masterLowBump);
27 |
28 |
29 | var masterBus = context.createGain();
30 | masterBus.gain.value = 0.4;
31 | masterBus.connect(masterHighBump);
32 |
33 |
34 | var noiseHighpass = context.createBiquadFilter();
35 | noiseHighpass.type = "highpass";
36 | noiseHighpass.frequency.value = 1200;
37 | noiseHighpass.connect(masterBus);
38 |
39 |
40 | var oscsHighpass = context.createBiquadFilter();
41 | oscsHighpass.type = "highpass";
42 | oscsHighpass.frequency.value = 400;
43 | oscsHighpass.connect(masterBus);
44 |
45 | var audioNode = context.createGain();
46 |
47 |
48 | var noise = context.createBufferSource();
49 | noise.buffer = noiseBuffer;
50 | noise.loop = true;
51 |
52 |
53 | var snappyGainNode = context.createGain();
54 | snappyGainNode.gain.value = 1;
55 | audioNode.snappy = snappyGainNode.gain;
56 |
57 | var oscsPreChoke = context.createGain();
58 | oscsPreChoke.gain.value = 0;
59 | var noisePreChoke = context.createGain();
60 | noisePreChoke.gain.value = 0;
61 |
62 |
63 | var noiseGain = context.createGain();
64 | noise.connect(snappyGainNode);
65 | snappyGainNode.connect(noiseGain);
66 | noiseGain.connect(noisePreChoke);
67 | noisePreChoke.connect(noiseHighpass);
68 |
69 |
70 |
71 | var oscsGain = context.createGain();
72 | oscsGain.connect(oscsPreChoke);
73 | oscsPreChoke.connect(oscsHighpass);
74 |
75 | var voltage = getVoltage(context);
76 | var detuneGainNode = context.createGain();
77 | detuneGainNode.gain.value = 0;
78 | audioNode.detune = detuneGainNode.gain;
79 | voltage.connect(detuneGainNode);
80 |
81 | var postChoke = context.createGain();
82 | postChoke.gain.value = 0;
83 |
84 |
85 | var oscs = [87.307, 329.628].map(function(frequency) {
86 | var osc = context.createOscillator();
87 | osc.frequency.value = frequency;
88 | detuneGainNode.connect(osc.detune);
89 | return osc;
90 | });
91 |
92 | var toneNode = context.createGain();
93 | toneNode.gain.value = 0.5;
94 | voltage.connect(toneNode);
95 |
96 | audioNode.tone = toneNode.gain;
97 |
98 | var oscAGainNode = context.createGain();
99 | var oscBGainNode = context.createGain();
100 |
101 | oscAGainNode.gain.value = -1;
102 | oscBGainNode.gain.value = 0;
103 |
104 | oscs[0].connect(oscAGainNode);
105 | oscs[1].connect(oscBGainNode);
106 |
107 | toneNode.connect(oscAGainNode.gain);
108 | toneNode.connect(oscBGainNode.gain);
109 |
110 | oscAGainNode.connect(oscsGain);
111 | oscBGainNode.connect(oscsGain);
112 |
113 |
114 | masterLowBump.connect(postChoke);
115 | postChoke.connect(audioNode);
116 |
117 |
118 | augmentedOnEnded(noise, function() {
119 | masterLowBump.disconnect(postChoke);
120 | });
121 |
122 |
123 | audioNode.duration = 0.3;
124 |
125 | audioNode.start = function(when) {
126 |
127 | oscsPreChoke.gain.setValueAtTime(0, when + 0.0001);
128 | oscsPreChoke.gain.linearRampToValueAtTime(1, when + 0.0002);
129 | noisePreChoke.gain.setValueAtTime(0, when + 0.0001);
130 | noisePreChoke.gain.linearRampToValueAtTime(1, when + 0.0002);
131 | postChoke.gain.setValueAtTime(0, when + 0.0001);
132 | postChoke.gain.linearRampToValueAtTime(1, when + 0.0002);
133 |
134 | // each Snare instance is monophonic
135 | while (playingNodes.length) {
136 | playingNodes.pop().stop(when);
137 | }
138 | playingNodes.push(audioNode);
139 |
140 | if (typeof when !== "number") {
141 | when = context.currentTime;
142 | }
143 |
144 | noiseGain.gain.setValueAtTime(0.0001, when);
145 | noiseGain.gain.exponentialRampToValueAtTime(Math.max(0.0001, 1),
146 | when + Math.min(audioNode.duration * (0.01 / 0.3), 0.01));
147 | noiseGain.gain.exponentialRampToValueAtTime(0.0001,
148 | when + audioNode.duration);
149 |
150 | oscsGain.gain.setValueAtTime(0.0001, when);
151 | oscsGain.gain.exponentialRampToValueAtTime(1,
152 | when + Math.min(0.01, audioNode.duration * (0.01 / 0.3)));
153 | oscsGain.gain.exponentialRampToValueAtTime(0.00001,
154 | when + audioNode.duration * 2/3);
155 |
156 | oscs.forEach(function(osc) {
157 | osc.start(when);
158 | osc.stop(when + audioNode.duration);
159 | });
160 |
161 | noise.start(when);
162 | noise.stop(when + audioNode.duration);
163 |
164 | voltage.start(when);
165 | voltage.stop(when + audioNode.duration);
166 |
167 |
168 | };
169 | audioNode.stop = function(when) {
170 |
171 | oscsPreChoke.gain.setValueAtTime(1, when);
172 | oscsPreChoke.gain.linearRampToValueAtTime(0, when + 0.0001);
173 | noisePreChoke.gain.setValueAtTime(1, when);
174 | noisePreChoke.gain.linearRampToValueAtTime(0, when + 0.0001);
175 | postChoke.gain.setValueAtTime(1, when);
176 | postChoke.gain.linearRampToValueAtTime(0, when + 0.0001);
177 |
178 | // Do not stop twice
179 | audioNode.stop = function() {};
180 | audioNode.gain.setValueAtTime(1, when);
181 | audioNode.gain.linearRampToValueAtTime(0, when + 0.01);
182 | try {
183 | oscs.forEach(function(osc) {
184 | osc.stop(when + 0.01);
185 | });
186 | noise.stop(when + 0.01);
187 | voltage.stop(when + 0.01);
188 | } catch (e) {
189 | // likely already stopped
190 | }
191 | };
192 | return audioNode;
193 | };
194 | };
195 |
--------------------------------------------------------------------------------
/demo/bundle.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o