Skip to content

Commit

Permalink
refactoring: name factories consistently
Browse files Browse the repository at this point in the history
  • Loading branch information
a-cordier committed Apr 16, 2018
1 parent 323845b commit a27cac9
Show file tree
Hide file tree
Showing 27 changed files with 1,088 additions and 598 deletions.
1,229 changes: 787 additions & 442 deletions dist/wasa.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "wasa",
"version": "0.0.19",
"version": "0.0.20",
"description": "High level tooling for Web Audio API",
"main": "dist/wasa.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/common/utils.js
@@ -1,4 +1,3 @@

import { times } from 'ramda'

export const mandatory = (parameterName = '') => {
Expand Down Expand Up @@ -34,3 +33,4 @@ export const wrapNode = (audioNode = mandatory()) => ({
return { connect }
},
})

4 changes: 4 additions & 0 deletions src/constants/filter-types.js
@@ -1,3 +1,7 @@
/**
* FilterTypes provides constants for filter type assignation
* @type {Object}
*/
export const FilterTypes = Object.freeze({
LOW_PASS: 'lowpass',
BAND_PASS: 'bandpass',
Expand Down
4 changes: 4 additions & 0 deletions src/constants/wave-forms.js
@@ -1,3 +1,7 @@
/**
* WaveForms provides a hash of constants for oscillator type assignation
* @type {Object}
*/
export const WaveForms = Object.freeze({
SQUARE: 'square',
SAWTOOTH: 'sawtooth',
Expand Down
9 changes: 1 addition & 8 deletions src/core/note.js
@@ -1,11 +1,4 @@
/**
* note module exports a set of utility functions and constants for midi
* to symbol and midi to frequency conversion
* @module note
*/

import { isNil } from 'ramda'

/**
* @typedef {Object} Note
* @property {string} pitchClass - The pitch in chromatic scale (english notation)
Expand Down Expand Up @@ -51,7 +44,7 @@ export function symbolToMidi(pitchClass, octave) {
/**
* Computes the pitch class and octave for the given midi value
* @param {number} midiValue - Octave value for note
* @returns {module:note~Note}
* @returns {Note}
*/
export function midiToSymbol(midiValue) {
const pitchClassIndex = (midiValue - (12 * 2)) % 12
Expand Down
5 changes: 2 additions & 3 deletions src/core/sequencer.js
Expand Up @@ -8,10 +8,9 @@ import { mandatory } from '../common/utils'
/**
* @function
* @param audioContext
* @returns {*}
* @constructor
* @returns {Sequencer}
*/
export const Sequencer = (audioContext = mandatory()) => {
export const createSequencer = (audioContext = mandatory()) => {
/* time values */
let ticksPerQuarterNote = 4
let startTime = 0
Expand Down
26 changes: 13 additions & 13 deletions src/core/sequencer.spec.js
@@ -1,17 +1,17 @@
import test from 'ava'
import sinon from 'sinon'
import { Sequencer } from '.'
import { createSequencer } from '.'
import { AudioContextMock } from '../mock/audio-context.mock'

test('Sequencer factory creates a sequencer object', (t) => {
test('createSequencer factory creates a sequencer object', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const sequencer = Sequencer(audioContext)
const sequencer = createSequencer(audioContext)
t.true(typeof sequencer === 'object')
})

test('Calling start triggers onStart handler', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
Sequencer(audioContext)
createSequencer(audioContext)
.onStart(() => {
t.pass()
})
Expand All @@ -20,38 +20,38 @@ test('Calling start triggers onStart handler', (t) => {

test('Calling stop triggers onStop handler', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
Sequencer(audioContext)
createSequencer(audioContext)
.onStop(() => {
t.pass()
})
.start()
.stop()
})

test('Sequencer factory returns object with a loopMode getter and setter', (t) => {
test('createSequencer factory returns object with a loopMode getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const sequencer = Sequencer(audioContext)
const sequencer = createSequencer(audioContext)
sequencer.setLoopMode(true)
t.is(true, sequencer.getLoopMode())
})

test('Sequencer factory returns object with a length getter and setter', (t) => {
test('createSequencer factory returns object with a length getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const sequencer = Sequencer(audioContext)
const sequencer = createSequencer(audioContext)
sequencer.setLength(16)
t.is(16, sequencer.getLength())
})

test('Sequencer factory returns object with a division getter and setter', (t) => {
test('createSequencer factory returns object with a division getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const sequencer = Sequencer(audioContext)
const sequencer = createSequencer(audioContext)
sequencer.setDivision(4)
t.is(4, sequencer.getDivision())
})

test('Sequencer factory returns object with a tempo getter and setter', (t) => {
test('createSequencer factory returns object with a tempo getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const sequencer = Sequencer(audioContext)
const sequencer = createSequencer(audioContext)
sequencer.setTempo(120)
t.is(120, sequencer.getTempo())
})
41 changes: 41 additions & 0 deletions src/macros/effects/bit-crusher.js
@@ -0,0 +1,41 @@
import * as R from 'ramda'

export const createBitCrusher = (audioContext) => {
const bufferSize = 512
const scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1)
let bits = 16
let normFreq = 0.05
const step = 0.5 ** bits
let phaser = 0
let last = 0
scriptProcessor.onaudioprocess = (event) => {
const input = event.inputBuffer.getChannelData(0)
const output = event.outputBuffer.getChannelData(0)
R.times((i) => {
phaser += normFreq
if (phaser >= 1) {
phaser -= 1
last = step * Math.floor((input[i] / step) + 0.5)
}
output[i] = last
}, bufferSize)
}

return {
connect({ connect, getInput }) {
scriptProcessor.connect(getInput())
return { connect }
},
getInput() {
return scriptProcessor
},
setFrequencyValue(value) {
normFreq = value
return this
},
setBitsValue(value) {
bits = value
return this
},
}
}
7 changes: 3 additions & 4 deletions src/macros/effects/delay.js
@@ -1,4 +1,4 @@
export const Delay = (audioContext) => {
export const createDelay = (audioContext) => {
/* audio nodes */
const output = audioContext.createGain()
const filter = audioContext.createBiquadFilter()
Expand Down Expand Up @@ -38,10 +38,9 @@ export const Delay = (audioContext) => {
setDivisionValue(value) {
division = value
delayTimeSeconds = divisionToDelayTime(division, tempo)
const feedbackValue = feedback.gain.value
feedback.gain.value = 0
delay.disconnect(feedback)
delay.delayTime.value = delayTimeSeconds
feedback.gain.value = feedbackValue
delay.connect(feedback)
return this
},
getDivisionValue() {
Expand Down
30 changes: 15 additions & 15 deletions src/macros/effects/delay.spec.js
@@ -1,55 +1,55 @@
import test from 'ava'
import sinon from 'sinon'
import { Delay } from './delay'
import { createDelay } from './delay'

import { AudioContextMock } from '../../mock/audio-context.mock'

test('Delay factory returns an object', (t) => {
test('createDelay factory returns an object', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const delay = Delay(audioContext)
const delay = createDelay(audioContext)
t.true(typeof delay === 'object')
})

test('Delay connect method returns an object with a connect method', (t) => {
test('createDelay connect method returns an object with a connect method', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const delay = Delay(audioContext)
const delay = createDelay(audioContext)
const nextInChain = {
getInput: () => audioContext.createGain(),
connect() {},
}
t.true(typeof delay.connect(nextInChain).connect === 'function')
})

test('Delay connect method returns an object with a getInput method', (t) => {
test('createDelay connect method returns an object with a getInput method', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const delay = Delay(audioContext)
const delay = createDelay(audioContext)
t.true(typeof delay.getInput() === 'object')
})

test('Delay factory returns object with a tempo getter and setter', (t) => {
test('createDelay factory returns object with a tempo getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const delay = Delay(audioContext)
const delay = createDelay(audioContext)
delay.setTempoValue(120)
t.is(120, delay.getTempoValue())
})

test('Delay factory returns object with a division getter and setter', (t) => {
test('createDelay factory returns object with a division getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const delay = Delay(audioContext)
const delay = createDelay(audioContext)
delay.setDivisionValue(4)
t.is(4, delay.getDivisionValue())
})

test('Delay factory returns object with a feedback getter and setter', (t) => {
test('createDelay factory returns object with a feedback getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const delay = Delay(audioContext)
const delay = createDelay(audioContext)
delay.setFeedbackValue(0.8)
t.is(0.8, delay.getFeedbackValue())
})

test('Delay factory returns object with a frequency getter and setter', (t) => {
test('createDelay factory returns object with a frequency getter and setter', (t) => {
const audioContext = AudioContextMock(sinon.sandbox.create())
const delay = Delay(audioContext)
const delay = createDelay(audioContext)
delay.setFrequencyValue(200)
t.is(200, delay.getFrequencyValue())
})
48 changes: 36 additions & 12 deletions src/macros/effects/distortion.js
@@ -1,28 +1,52 @@
export const Distortion = (audioContext) => {
import * as R from 'ramda'

export const createDistortion = (audioContext) => {
const makeDistortionCurve = (amount) => {
const k = typeof amount === 'number' ? amount : 50
const nSamples = 44100
const { sampleRate } = audioContext
const curve = new Float32Array(44100)
const deg = Math.PI / 180
for (let i = 0; i < nSamples; i += 1) {
const x = (i * 2) / (nSamples - 1)
curve[i] = ((3 + k) * x * 20 * deg) / ((Math.PI + k) * Math.abs(x))
}
R.times((i) => {
const x = (i * 2) / (sampleRate - 1)
const a = (3 + amount) * x * 20 * deg
const b = Math.PI + (amount * Math.abs(x))
curve[i] = a / b
}, sampleRate)
return curve
}

const preGain = audioContext.createGain()
const postGain = audioContext.createGain()
const dist = audioContext.createWaveShaper()
dist.curve = makeDistortionCurve(100)
preGain.connect(dist).connect(postGain)
dist.curve = makeDistortionCurve(400)
dist.oversample = '4x'
preGain.gain.value = 50
postGain.gain.value = 1
return {
connect({ connect, getInput }) {
dist.connect(getInput())
return connect
postGain.connect(getInput())
return { connect }
},
getInput() {
return dist
return preGain
},
setCurve(amount) {
setCurveAmount(amount) {
dist.curve = makeDistortionCurve(amount)
return this
},
setPreGainValue(value) {
preGain.gain.value = value
return this
},
getPreGainValue() {
return preGain.gain.value
},
setPostGainValue(value) {
postGain.gain.value = value
return this
},
getPostGainValue() {
return postGain.gain.value
},
}
}
24 changes: 24 additions & 0 deletions src/macros/effects/noise-convolver.js
@@ -0,0 +1,24 @@
import * as R from 'ramda'

export const createNoiseConvolver = (audioContext) => {
const convolver = audioContext.createConvolver()
const bufferSize = audioContext.sampleRate
const buffer = audioContext.createBuffer(2, bufferSize / 2, bufferSize)
const left = buffer.getChannelData(0)
const right = buffer.getChannelData(1)
R.times((i) => {
left[i] = Math.random()
right[i] = Math.random()
}, buffer.length)
convolver.buffer = buffer

return {
connect({ connect, getInput }) {
convolver.connect(getInput())
return { connect }
},
getInput() {
return convolver
},
}
}

0 comments on commit a27cac9

Please sign in to comment.