tonegenerator
Advanced tools
Comparing version 0.2.1 to 0.3.0
73
index.js
@@ -6,7 +6,48 @@ /* | ||
*/ | ||
function generateCycle(cycle, volume) { | ||
var shapes = { | ||
sine: function (i, cycle, volume) { | ||
// i / cycle => value between 0 and 1 | ||
// 0 = beginning of circly | ||
// 0.25 Math.sin = 1 | ||
// 0.5 Math.sin = 0 | ||
// 0.75 Math.sin = -1 | ||
// 1 Math.sin = 1 | ||
return Math.min(volume * Math.sin((i/cycle) * Math.PI * 2), volume - 1); | ||
}, | ||
triangle: function (i, cycle, volume) { | ||
var halfCycle = cycle / 2 | ||
var level | ||
if (i < halfCycle) { | ||
level = (volume * 2) * (i / halfCycle) - volume; | ||
} else { | ||
i = i - halfCycle | ||
level = -(volume * 2) * (i / halfCycle) + volume; | ||
} | ||
return Math.min(level, volume - 1); | ||
}, | ||
saw: function (i, cycle, volume) { | ||
return Math.min((volume * 2) * (i / cycle) - volume, volume - 1); | ||
}, | ||
square: function (i, cycle, volume) { | ||
if(i > cycle / 2) { | ||
return volume - 1; | ||
} | ||
return -volume; | ||
} | ||
} | ||
function generateCycle(cycle, volume, shape) { | ||
var data = []; | ||
var tmp; | ||
var generator = typeof shape == 'function' ? shape : shapes[shape]; | ||
if (!generator) { | ||
throw new Error('Invalid wave form: "' + shape + '" choose between: ' + Object.keys(shapes)); | ||
} | ||
for(var i = 0; i < cycle; i++) { | ||
tmp = Math.min(volume * Math.sin((i/cycle) * Math.PI * 2), volume - 1) | ||
tmp = generator(i, cycle, volume); | ||
data[i] = Math.round(tmp); | ||
@@ -17,7 +58,9 @@ } | ||
module.exports = function(freq, lengthInSecs, volume, rate) { | ||
freq = freq || 440; | ||
rate = rate || 44100 | ||
lengthInSecs = lengthInSecs || 2.0; | ||
volume = volume || 30 | ||
function generateWaveForm(opts) { | ||
opts = opts || {} | ||
var freq = opts.freq || 440; | ||
var rate = opts.rate || 44100 | ||
var lengthInSecs = opts.lengthInSecs || 2.0; | ||
var volume = opts.volume || 30; | ||
var shape = opts.shape || 'sine'; | ||
@@ -30,3 +73,3 @@ var cycle = Math.floor(rate/freq); | ||
for(var i = 0; i < cycles; i++) { | ||
ret = ret.concat(generateCycle(cycle, volume)); | ||
ret = ret.concat(generateCycle(cycle, volume, shape)); | ||
} | ||
@@ -37,3 +80,17 @@ | ||
module.exports = function() { | ||
// to support both old interface and the new one: | ||
var opts = arguments[0] | ||
if (arguments.length > 1 && typeof opts === "number") { | ||
opts = {} | ||
opts.freq = arguments[0] | ||
opts.lengthInSecs = arguments[1] | ||
opts.volume = arguments[2] | ||
opts.rate = arguments[3] | ||
} | ||
return generateWaveForm(opts) | ||
} | ||
module.exports.MAX_16 = 32768; | ||
module.exports.MAX_8 = 128; |
{ | ||
"name": "tonegenerator", | ||
"description": "Generates a tone as raw PCM WAV data, so you can do operations on it", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"scripts": { | ||
@@ -6,0 +6,0 @@ "test": "node test.js", |
ToneGenerator for node.js | ||
==== | ||
This thing generates raw PCM data, specified by | ||
a frequency and length in seconds. | ||
This thing generates an array of numbers in waveforms. This waveform can be written into a wavefile as raw PCM data. | ||
It does not play the sounds. If you want to play sounds, make sure to read the examples on how to write wave files below. | ||
@@ -10,13 +10,38 @@ ## Generating Tones: | ||
```javascript | ||
tone(frequency, lengthInSeconds, volume = 30, sampleRate = 44100) | ||
var tone = require('tonegenerator') | ||
// New Interface! More options!! | ||
var tonedata = tone({ | ||
freq: 440, | ||
lengthInSecs: 2.0, | ||
volume: 30, | ||
sampleRate: 44100, | ||
shape: 'triangle' | ||
}) | ||
// The old interface, still available for compatibility | ||
var tonedata = tone(frequency, lengthInSeconds, volume = 30, sampleRate = 44100) | ||
``` | ||
#### Using the new interface | ||
- **freq** frequency in hertz. *defaults to 440* | ||
- **lengthInSecs** controls the length of the array output together with the samplerate *defaults to 2.0* | ||
- **volume** controls max/min for the array values. If you intend to write 8-bit it should be less than or equal to tone.MAX_8, if 16 bit it should be less than or equal to tone.MAX_16. *defaults to 30* | ||
- **sampleRate** number of samples per second. Together with lengthInSecs, this define the length of the output array (lengthInSeccs * sampleRate). *defaults to 44100* | ||
- **shape** controls the wave shape. Options are *'triangle', 'square', 'sine', 'saw'*. You can also pass in a custom function, see the tests for an example of this. *defaults to 'sine'* | ||
#### Using the old interface | ||
The old interface takes four arguments: *freq, lengthInSecs, volume, sampleRate*. | ||
**volume** and **sampleRate** are optional, the default is shown above. | ||
**If you want to specify sampleRate, you have to specify volume!** | ||
**Shape is not available in the old interface. If you want to specify sampleRate, you have to specify volume!** | ||
#### Useful constants | ||
```javascript | ||
tone.MAX_8 // max volume to be used with 8-bit sound | ||
tone.MAX_16 // max volume for 16 bit sound | ||
```javascript | ||
var tone = require('tonegenerator'); | ||
var A440 = tone(440, 20, 30); // get PCM data for a 440hz A, 20 seconds, volume 30 | ||
var A440_low_sample = tone(440, 20, 30, 22050); // this array has lower sample rate and will only be half as long | ||
var A440 = tone({ freq: 440, lengthInSeconds: 20, volume: 30 }); // get PCM data for a 440hz A, 20 seconds, volume 30 | ||
var A440_low_sample = tone(440, 20, 30, 22050); // (old interface) this array has lower sample rate and will only be half as long | ||
``` | ||
@@ -30,5 +55,5 @@ | ||
// An A-major chord | ||
var tone1 = tone(440, 2, 60) | ||
var tone2 = tone(554.37, 2, 30) | ||
var tone3 = tone(659.26, 2, 30) | ||
var tone1 = tone({ freq: 440, lengthInSecs: 2, volume: 60 }) | ||
var tone2 = tone({ freq: 554.37, lengthInSecs: 2, volume: 30 }) | ||
var tone3 = tone({ freq: 659.26, lengthInSecs: 2, volume: 30 }) | ||
@@ -63,3 +88,3 @@ // "playing" one tone at the time | ||
var file = fs.createWriteStream('8bit-example.wav') | ||
var samples = tone(440, 2, tone.MAX_8) | ||
var samples = tone({ freq: 440, lengthInSecs: 2, volume: tone.MAX_8 }) | ||
@@ -96,3 +121,3 @@ file.write(header(samples.length, { | ||
var file = fs.createWriteStream('16bit-example.wav') | ||
var samples = tone(440, 2, tone.MAX_16) | ||
var samples = tone({ freq: 440, lengthInSecs: 2, volume: tone.MAX_16 }) | ||
@@ -134,5 +159,5 @@ file.write(header(samples.length * 2, { | ||
// A loud A for channel 1 | ||
var channel1 = tone(440, 2, tone.MAX_16) | ||
var channel1 = tone({ freq: 440, lengthInSecs: 2, volume: tone.MAX_16 }) | ||
// A not so loud C for channel 2 | ||
var channel2 = tone(554.37, 2, tone.MAX_16 / 4) | ||
var channel2 = tone({ freq: 554.37, lengthInSecs: 2, volume: tone.MAX_16 / 4 }) | ||
@@ -139,0 +164,0 @@ // create an array where the 2 channels are interleaved: |
70
test.js
@@ -7,6 +7,8 @@ var assert = require('assert') | ||
var tone1 = tonegenerator(440, 2, 10) | ||
var tone2 = tonegenerator(440, 2, 30) | ||
var tonefrequency = tonegenerator(440, 2, 10, 22050) | ||
var tone1 = tonegenerator({ freq: 440, lengthInSecs: 2, volume: 10 }) | ||
var tone2 = tonegenerator({ freq: 440, lengthInSecs: 2, volume: 30 }) | ||
var tonefrequency = tonegenerator({ freq: 440, lengthInSecs: 2, volume: 10, rate: 22050 }) | ||
console.log('Testing Sine Waves') | ||
assert(Array.isArray(tone1), 'Data is an array') | ||
@@ -23,2 +25,64 @@ | ||
console.log('Testing Triangle Waves') | ||
var tone4 = tonegenerator({ freq: 440, lengthInSecs: 2, volume: 10, shape: 'triangle' }) | ||
// test that max is correct | ||
assert.strictEqual(Math.max.apply(Math, tone4), 9) | ||
// test that min is correct | ||
assert.strictEqual(Math.min.apply(Math, tone4), -10) | ||
// test that there are intermediate values | ||
assert(tone4.includes(0)) | ||
assert(tone4.includes(-1)) | ||
console.log('Testing Saw Waves') | ||
var tone5 = tonegenerator({ freq: 440, lengthInSecs: 2, volume: 10, shape: 'saw' }) | ||
// test first val, should be min | ||
assert.strictEqual(tone5.shift(), -10) | ||
// test last val, should be max | ||
assert.strictEqual(tone5.pop(), 9) | ||
// test that there's intermediate values generated | ||
assert(tone5.includes(0)) | ||
console.log('Testing Square Waves') | ||
var tone6 = tonegenerator({ freq: 440, lengthInSecs: 2, volume: 10, shape: 'square' }) | ||
// test first val | ||
assert.strictEqual(tone6.pop(), 9) | ||
// test last val | ||
assert.strictEqual(tone6.shift(), -10) | ||
// make sure there's no intermediate values | ||
assert(!tone6.includes(0)) | ||
console.log('Testing Custom Waves') | ||
var tone7 = tonegenerator({ | ||
freq: 440, | ||
lengthInSecs: 2, | ||
volume: 11, | ||
shape: function (i, cycle, volume) { | ||
return volume - 1; | ||
} | ||
}) | ||
// test first val | ||
assert.strictEqual(tone7.pop(), 10) | ||
// test last val | ||
assert.strictEqual(tone7.shift(), 10) | ||
// make sure there's no intermediate values | ||
assert(!tone7.includes(0)) | ||
console.log('Testing old-style interface') | ||
var tone8 = tonegenerator(440, 2, 10) | ||
var tone9 = tonegenerator(440, 2, 30) | ||
var tonefrequency2 = tonegenerator(440, 2, 10, 22050) | ||
// takes the volume argument - 1 as max | ||
assert.strictEqual(Math.max.apply(Math, tone8), 9) | ||
// takes the volume argument as min | ||
assert.strictEqual(Math.min.apply(Math, tone8), -10) | ||
// takes the volume argument as max | ||
assert.strictEqual(Math.max.apply(Math, tone9), 29) | ||
assert.equal(tone8.length/2, tonefrequency2.length, 'when halving audio sampling rate, the array length should be half of default') | ||
console.log('...done') |
12339
146
192