Comparing version 1.2.0 to 1.3.0
@@ -36,2 +36,8 @@ 'use strict'; | ||
const scaleConfigTruePythagOpts = { | ||
startFreq: cNoteFreq, | ||
intervals: [0, 12, 24, 36, 48], | ||
mode: 'truePythag' | ||
}; | ||
const scaleFrequencies = freqi.getFreqs(scaleConfigAllOpts); | ||
@@ -41,2 +47,3 @@ // const scaleFrequencies = freqi.getFreqs(scaleConfigMandatoryOpts); | ||
// const scaleFrequencies = freqi.getFreqs(scaleConfigHSeriesOpts); | ||
// const scaleFrequencies = freqi.getFreqs(scaleConfigTruePythagOpts); | ||
console.log('scaleFrequencies', scaleFrequencies); | ||
@@ -64,2 +71,3 @@ | ||
function playSineCb(scale) { | ||
console.log(scale[index]); | ||
playSine(scale[index]); | ||
@@ -89,3 +97,3 @@ if (index >= scale.length - 1) { | ||
if (!connected) { | ||
play(scaleFrequencies, 1000); | ||
play(scaleFrequencies, 2000); | ||
} | ||
@@ -92,0 +100,0 @@ console.log('connected', connected); |
@@ -14,2 +14,3 @@ (function (global, factory) { | ||
var H_SERIES_STR = 'hSeries'; | ||
var TRUE_PYTHAG = 'truePythag'; | ||
var CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; | ||
@@ -267,10 +268,10 @@ var justTuningSystems = { | ||
*/ | ||
function getAllOctaveJustIntervals(interval, justIntervalsArr) { | ||
function getAllOctaveJustIntervals(interval, justIntervalsArrLength) { | ||
var _intervalAbs = Math.abs(interval); | ||
var _mult = _intervalAbs / justIntervalsArr.length; | ||
var _mult = _intervalAbs / justIntervalsArrLength; | ||
var _multFloor = Math.floor(_mult); | ||
var _inRangeIndex = _intervalAbs - (_multFloor * justIntervalsArr.length); | ||
var _negIndex = justIntervalsArr.length - _inRangeIndex; | ||
var _inRangeIndex = _intervalAbs - (_multFloor * justIntervalsArrLength); | ||
var _negIndex = justIntervalsArrLength - _inRangeIndex; | ||
var _multF; | ||
if (_intervalAbs % justIntervalsArr.length === 0) { | ||
if (_intervalAbs % justIntervalsArrLength === 0) { | ||
return { | ||
@@ -284,3 +285,3 @@ mult: _mult, | ||
if (_isPos) { | ||
_newInterval = interval - (_multFloor * justIntervalsArr.length); | ||
_newInterval = interval - (_multFloor * justIntervalsArrLength); | ||
_multF = _multFloor; | ||
@@ -297,2 +298,57 @@ } | ||
} | ||
function raiseOrReduceByFifth(number, _up) { | ||
var upperFifthRatio = 3 / 2; | ||
var lowerFifthRatio = 2 / 3; | ||
if (_up) { | ||
return number * upperFifthRatio; | ||
} | ||
return number * lowerFifthRatio; | ||
} | ||
function multOrDivide(_number, _mult, _up) { | ||
if (_up) { | ||
return _number / _mult; | ||
} | ||
return _number * _mult; | ||
} | ||
function getCorrectIndex(interval, _up, notesInOctave, mult) { | ||
var step = 5; | ||
var multOffset = _up ? mult : mult - 1; | ||
var oct = multOffset * notesInOctave; | ||
var result = notesInOctave; | ||
var prevNum = result; | ||
var actInterval = Math.abs(interval); | ||
for (var index = 0; index < actInterval; index++) { | ||
result = prevNum - step; | ||
prevNum = result; | ||
if (result < 0) { | ||
result = notesInOctave - Math.abs(result); | ||
prevNum = result; | ||
} | ||
} | ||
return result + oct; | ||
} | ||
function getPythagNoteWithinOct(index, notesInOctave, noteFreq, _up) { | ||
var halfOctave = notesInOctave / 2; | ||
var rangeInterval = getAllOctaveJustIntervals(index, notesInOctave).rangeInterval; | ||
if (rangeInterval < halfOctave && index % 2 !== 0 || rangeInterval >= halfOctave && index % 2 === 0) { | ||
return multOrDivide(noteFreq, 2, _up); | ||
} | ||
return noteFreq; | ||
} | ||
function getTruePythagNote(eTNoteConfig, _up) { | ||
if (eTNoteConfig.interval === 0) { | ||
return eTNoteConfig.startFreq; | ||
} | ||
var notesInOctave = 12; | ||
var mult = getAllOctaveJustIntervals(eTNoteConfig.interval, notesInOctave).mult; | ||
var correctIndex = getCorrectIndex(eTNoteConfig.interval, _up, notesInOctave, mult); | ||
var noteFreq = eTNoteConfig.startFreq; | ||
var prevNote = noteFreq; | ||
for (var index = 0; index < correctIndex; index++) { | ||
noteFreq = raiseOrReduceByFifth(prevNote, _up); | ||
noteFreq = getPythagNoteWithinOct(index, notesInOctave, noteFreq, _up); | ||
prevNote = noteFreq; | ||
} | ||
return noteFreq; | ||
} | ||
function getHSeriesNote(eTNoteConfig, _up) { | ||
@@ -328,3 +384,3 @@ var interval; | ||
var _justIntervalsArr = justTuningSystems[eTNoteConfig.mode]; | ||
var _rangeObj = getAllOctaveJustIntervals(eTNoteConfig.interval, _justIntervalsArr); | ||
var _rangeObj = getAllOctaveJustIntervals(eTNoteConfig.interval, _justIntervalsArr.length); | ||
var _ratioFraction = _justIntervalsArr[_rangeObj.rangeInterval][0] / _justIntervalsArr[_rangeObj.rangeInterval][1]; | ||
@@ -366,2 +422,5 @@ var _multiplier = Math.pow(2, _rangeObj.mult); | ||
} | ||
else if (eTNoteConfig.mode === TRUE_PYTHAG) { | ||
_note = getTruePythagNote(eTNoteConfig, _up); | ||
} | ||
else { | ||
@@ -491,5 +550,10 @@ _note = getJustIntNote(eTNoteConfig, _up, justTuningSystems); | ||
addMissingNotesFromInterval: addMissingNotesFromInterval, | ||
getCorrectIndex: getCorrectIndex, | ||
raiseOrReduceByFifth: raiseOrReduceByFifth, | ||
multOrDivide: multOrDivide, | ||
getSingleFreq: getSingleFreq, | ||
getJustIntNote: getJustIntNote, | ||
getHSeriesNote: getHSeriesNote, | ||
getTruePythagNote: getTruePythagNote, | ||
getPythagNoteWithinOct: getPythagNoteWithinOct, | ||
getAllOctaveJustIntervals: getAllOctaveJustIntervals, | ||
@@ -496,0 +560,0 @@ getModes: getModes, |
{ | ||
"name": "freqi", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "A library that generates musical frequencies for use with the Web Audio API", | ||
@@ -5,0 +5,0 @@ "main": "freqi.js", |
@@ -14,6 +14,6 @@ # Freqi | ||
* Return actual sonic frequencies in Hz or relative frequencies for use with audio files | ||
* NEW: Return frequencies from different tuning systems | ||
* Allow for any number of semitones per octave - Equal Temperament mode only | ||
* NEW: Support multiple tuning systems, in particular equal temperament and just intonation | ||
* Allow for any number of semitones per octave - equal temperament mode only | ||
* Return an array of frequencies of any length | ||
* Provide a way of creating frequencies from within the intervals | ||
* Provide a way of creating frequencies from within the intervals themselves | ||
* Handle bad data | ||
@@ -32,9 +32,2 @@ | ||
## Demo | ||
[View the online demo on GitHub here](https://rjbultitude.github.io/freqi/demo/index.html) | ||
Or download/clone it and run `npm install` which will install any dependencies, `npm run build` to create the demo bundle and `npm run start` which will start a new server at localhost:8080. | ||
Then, be sure to navigate to the demo folder in order to see the page i.e. localhost:8080/demo | ||
## Getting Started | ||
@@ -68,18 +61,26 @@ | ||
`intervals` is the set of notes you want to generate frequencies from. These intervals can be any integer, positive or negative e.g. `[-5, 0, 7]`. | ||
`intervals` is the set of notes you want to generate frequencies from. These intervals can be any integer, positive or negative e.g. `[-5, 0, 7]` where `0` is the start frequency. | ||
`startFreq` can be a frequency in Hz, such as `440` (for the note A) or a relative frequency such `1` which is useful for playing back audio files, such as MP3s or OGGs, where `1` is the original playback rate. 440 is the default value. | ||
`startFreq` can be a frequency in Hz, such as `440` (for the note A) or a relative frequency such `1` which is useful for playing back audio files, such as MP3s or OGGs, where `1` is the original playback rate. `440` is the default value. | ||
**NEW** | ||
Freqi now supports Just Intonation tuning systems. By default it will use equal temperament, but to use a Just tuning system set the `mode` to one of the following: | ||
* `pythagorean` | ||
**NEW | Tuning Systems** | ||
Freqi now supports just intonation tuning systems. By default it will use equal temperament, but to use a Just tuning system set the `mode` to one of the following: | ||
* `pythagorean` - tempered pythagorean | ||
* `truePythagorean` - all notes are calculated using fifths (3/2), which introduces the [pyhthagorean comma](https://en.wikipedia.org/wiki/Pythagorean_comma) | ||
* `fiveLimit` | ||
* `diatonic` | ||
* `diatonic` - this system has only 7 notes per octave | ||
* `diatonicIndian` | ||
* `twentyTwoShrutis` | ||
* `gioseffoZarlino` | ||
Once in one of these modes other config params, such as `numNotes`, will have no effect as they are fixed scale systems. | ||
Technically speaking, these Just tuning systems are actually tempered as upper and lower octaves are acheived by increasing or decreasing their frequencies by a power of 2. However, the notes in the first octave of each tuning system use the correct ratios. The object containing the ratios for each system is exported as `justTuningSystems` should you need to view it. If you want to list all the keys use the method `getModes`. | ||
* `twentyTwoShrutis` - as the name suggests, there are 22 notes per octave | ||
* `gioseffoZarlino` - there are 16 notes per octave | ||
Once in one of these modes, other config params, such as `numNotes`, will have no effect as they are fixed scale systems. | ||
Technically speaking, most of the just tuning systems are actually tempered in that the upper and lower octaves are acheived by increasing or decreasing their frequencies by a power of 2. The object containing the ratios for each system is exported as `justTuningSystems` should you need to view it. If you want to list all the tuning system keys, use the method `getModes`. | ||
There is also a mode for the harmonic series `hSeries`, though in its raw state it's not particularly useful for making music, but may be of interest to music theorists. | ||
@@ -114,4 +115,15 @@ | ||
## Demo | ||
[View the online demo on GitHub here](https://rjbultitude.github.io/freqi/demo/index.html) | ||
Or download/clone it and run `npm install` which will install any dependencies, `npm run build` to create the demo bundle and `npm run start` which will start a new server at localhost:8080. | ||
Then, be sure to navigate to the demo folder in order to see the page i.e. localhost:8080/demo | ||
## Release notes | ||
1.3.0 Support for true pythagorean scale added. | ||
1.2.0 Support for Gioseffo Zarlino's 16 note scale added. | ||
1.1.0 As described above, 1.1.0 supports mutliple tuning systems. The source has been refactored to TypeScript and test coverage has been improved. | ||
@@ -118,0 +130,0 @@ |
@@ -52,5 +52,11 @@ /* | ||
interface AllOctaveJustIntervals { | ||
mult: number; | ||
rangeInterval: number; | ||
} | ||
// Constants | ||
const EQ_TEMP_STR = 'eqTemp'; | ||
const H_SERIES_STR = 'hSeries'; | ||
const TRUE_PYTHAG = 'truePythag'; | ||
const CHROMATIC_SCALE: Array<string> = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; | ||
@@ -324,10 +330,10 @@ const justTuningSystems: JustTuningSystems = { | ||
*/ | ||
function getAllOctaveJustIntervals(interval: number, justIntervalsArr: Array): object { | ||
function getAllOctaveJustIntervals(interval: number, justIntervalsArrLength: number): AllOctaveJustIntervals { | ||
const _intervalAbs = Math.abs(interval); | ||
const _mult = _intervalAbs / justIntervalsArr.length; | ||
const _mult = _intervalAbs / justIntervalsArrLength; | ||
const _multFloor = Math.floor(_mult); | ||
const _inRangeIndex = _intervalAbs - (_multFloor * justIntervalsArr.length); | ||
const _negIndex = justIntervalsArr.length - _inRangeIndex; | ||
const _inRangeIndex = _intervalAbs - (_multFloor * justIntervalsArrLength); | ||
const _negIndex = justIntervalsArrLength - _inRangeIndex; | ||
let _multF; | ||
if (_intervalAbs % justIntervalsArr.length === 0) { | ||
if (_intervalAbs % justIntervalsArrLength === 0) { | ||
return { | ||
@@ -341,3 +347,3 @@ mult: _mult, | ||
if (_isPos) { | ||
_newInterval = interval - (_multFloor * justIntervalsArr.length); | ||
_newInterval = interval - (_multFloor * justIntervalsArrLength); | ||
_multF = _multFloor; | ||
@@ -355,2 +361,62 @@ } else { | ||
function raiseOrReduceByFifth(number: number, _up: boolean): number { | ||
const upperFifthRatio = 3/2; | ||
const lowerFifthRatio = 2/3; | ||
if (_up) { | ||
return number * upperFifthRatio; | ||
} | ||
return number * lowerFifthRatio; | ||
} | ||
function multOrDivide(_number: number, _mult: number, _up: boolean): number { | ||
if (_up) { | ||
return _number / _mult; | ||
} | ||
return _number * _mult; | ||
} | ||
function getCorrectIndex(interval: number, _up: boolean, notesInOctave: number, mult: number): number { | ||
const step = 5; | ||
const multOffset = _up ? mult : mult - 1; | ||
const oct = multOffset * notesInOctave; | ||
let result = notesInOctave; | ||
let prevNum = result; | ||
const actInterval = Math.abs(interval); | ||
for (let index = 0; index < actInterval; index++) { | ||
result = prevNum - step; | ||
prevNum = result; | ||
if (result < 0) { | ||
result = notesInOctave - Math.abs(result); | ||
prevNum = result; | ||
} | ||
} | ||
return result + oct; | ||
} | ||
function getPythagNoteWithinOct(index, notesInOctave, noteFreq, _up): number { | ||
const halfOctave = notesInOctave / 2; | ||
const { rangeInterval } = getAllOctaveJustIntervals(index, notesInOctave); | ||
if (rangeInterval < halfOctave && index % 2 !== 0 || rangeInterval >= halfOctave && index % 2 === 0) { | ||
return multOrDivide(noteFreq, 2, _up); | ||
} | ||
return noteFreq; | ||
} | ||
function getTruePythagNote(eTNoteConfig: ETNoteConfig, _up): number { | ||
if (eTNoteConfig.interval === 0) { | ||
return eTNoteConfig.startFreq; | ||
} | ||
const notesInOctave = 12; | ||
const { mult } = getAllOctaveJustIntervals(eTNoteConfig.interval, notesInOctave); | ||
const correctIndex = getCorrectIndex(eTNoteConfig.interval, _up, notesInOctave, mult); | ||
let noteFreq = eTNoteConfig.startFreq; | ||
let prevNote = noteFreq; | ||
for (let index = 0; index < correctIndex; index++) { | ||
noteFreq = raiseOrReduceByFifth(prevNote, _up); | ||
noteFreq = getPythagNoteWithinOct(index, notesInOctave, noteFreq, _up); | ||
prevNote = noteFreq; | ||
} | ||
return noteFreq; | ||
} | ||
function getHSeriesNote(eTNoteConfig: ETNoteConfig, _up): number { | ||
@@ -387,3 +453,3 @@ let interval; | ||
const _justIntervalsArr = justTuningSystems[eTNoteConfig.mode]; | ||
const _rangeObj = getAllOctaveJustIntervals(eTNoteConfig.interval, _justIntervalsArr); | ||
const _rangeObj = getAllOctaveJustIntervals(eTNoteConfig.interval, _justIntervalsArr.length); | ||
const _ratioFraction = _justIntervalsArr[_rangeObj.rangeInterval][0] / _justIntervalsArr[_rangeObj.rangeInterval][1]; | ||
@@ -422,3 +488,6 @@ const _multiplier = Math.pow(2, _rangeObj.mult); | ||
_note = getHSeriesNote(eTNoteConfig, _up); | ||
} else { | ||
} else if (eTNoteConfig.mode === TRUE_PYTHAG) { | ||
_note = getTruePythagNote(eTNoteConfig, _up); | ||
} | ||
else { | ||
_note = getJustIntNote(eTNoteConfig, _up, justTuningSystems); | ||
@@ -545,5 +614,10 @@ } | ||
addMissingNotesFromInterval, | ||
getCorrectIndex, | ||
raiseOrReduceByFifth, | ||
multOrDivide, | ||
getSingleFreq, | ||
getJustIntNote, | ||
getHSeriesNote, | ||
getTruePythagNote, | ||
getPythagNoteWithinOct, | ||
getAllOctaveJustIntervals, | ||
@@ -550,0 +624,0 @@ getModes, |
@@ -716,2 +716,7 @@ 'use strict'; | ||
}; | ||
this.configUp = { | ||
startFreq: 440, | ||
interval: 2, | ||
mode: 'hSeries' | ||
} | ||
}); | ||
@@ -728,2 +733,5 @@ | ||
}); | ||
it('should return the start freq times the interval if _up arg is true', function() { | ||
expect(freqi.getHSeriesNote(this.configUp, true)).to.equal(880); | ||
}); | ||
}); | ||
@@ -743,21 +751,21 @@ | ||
it('should return mult 0 when interval argument is within the array argument length', function() { | ||
expect(freqi.getAllOctaveJustIntervals(2, this.justIntArr).mult).to.equal(0); | ||
expect(freqi.getAllOctaveJustIntervals(2, this.justIntArr.length).mult).to.equal(0); | ||
}); | ||
it('should return mult 1 when interval argument is within the next multiple of the array argument length', function() { | ||
expect(freqi.getAllOctaveJustIntervals(6, this.justIntArr).mult).to.equal(1); | ||
expect(freqi.getAllOctaveJustIntervals(6, this.justIntArr.length).mult).to.equal(1); | ||
}); | ||
it('should return mult 2 when interval argument is double the array argument length', function() { | ||
expect(freqi.getAllOctaveJustIntervals(10, this.justIntArr).mult).to.equal(2); | ||
expect(freqi.getAllOctaveJustIntervals(10, this.justIntArr.length).mult).to.equal(2); | ||
}); | ||
it('should return mult 2 when interval argument is between double and treble the array argument length', function() { | ||
expect(freqi.getAllOctaveJustIntervals(13, this.justIntArr).mult).to.equal(2); | ||
expect(freqi.getAllOctaveJustIntervals(13, this.justIntArr.length).mult).to.equal(2); | ||
}); | ||
it('should return mult 1 when interval argument is within the array argument length but negative', function() { | ||
expect(freqi.getAllOctaveJustIntervals(-2, this.justIntArr).mult).to.equal(1); | ||
expect(freqi.getAllOctaveJustIntervals(-2, this.justIntArr.length).mult).to.equal(1); | ||
}); | ||
it('should return mult 2 when interval argument is double the array argument length but negative', function() { | ||
expect(freqi.getAllOctaveJustIntervals(-10, this.justIntArr).mult).to.equal(2); | ||
expect(freqi.getAllOctaveJustIntervals(-10, this.justIntArr.length).mult).to.equal(2); | ||
}); | ||
it('should return mult 2 when interval argument is between double and treble the array argument length but negative', function() { | ||
expect(freqi.getAllOctaveJustIntervals(-13, this.justIntArr).mult).to.equal(3); | ||
expect(freqi.getAllOctaveJustIntervals(-13, this.justIntArr.length).mult).to.equal(3); | ||
}); | ||
@@ -772,12 +780,84 @@ }); | ||
it('should return rangeInterval 3 when interval argument is 3', function() { | ||
expect(freqi.getAllOctaveJustIntervals(3, this.justIntArr).rangeInterval).to.equal(3); | ||
expect(freqi.getAllOctaveJustIntervals(3, this.justIntArr.length).rangeInterval).to.equal(3); | ||
}); | ||
it('should return rangeInterval 3 when interval argument is greater than the array argument length by three items', function() { | ||
expect(freqi.getAllOctaveJustIntervals(8, this.justIntArr).rangeInterval).to.equal(3); | ||
expect(freqi.getAllOctaveJustIntervals(8, this.justIntArr.length).rangeInterval).to.equal(3); | ||
}); | ||
it('should return rangeInterval 4 when interval argument is greater than the array argument length and negative', function() { | ||
expect(freqi.getAllOctaveJustIntervals(-6, this.justIntArr).rangeInterval).to.equal(4); | ||
expect(freqi.getAllOctaveJustIntervals(-6, this.justIntArr.length).rangeInterval).to.equal(4); | ||
}); | ||
}); | ||
describe('getCorrectIndex', function() { | ||
beforeEach(function() { | ||
this.notesInOct = 12; | ||
this.circleOfFifths = [7, 2, 9, 4, 11, 6, 1, 8, 3, 10, 5]; | ||
}); | ||
it('should return 7 when interval argument is 1', function() { | ||
expect(freqi.getCorrectIndex(1, true, this.notesInOct, 0)).to.equal(7); | ||
}); | ||
it('should return 7 + notesInOct when interval argument is 13', function() { | ||
expect(freqi.getCorrectIndex(13, true, this.notesInOct, 1)).to.equal(19); | ||
}); | ||
}); | ||
describe('raiseOrReduceByFifth', function() { | ||
it('should return number times 1.5 if _up arg is true', function() { | ||
expect(freqi.raiseOrReduceByFifth(1, true)).to.equal(1.5); | ||
}); | ||
it('should return number divided 1.5 if _up arg is false', function() { | ||
expect(freqi.raiseOrReduceByFifth(3, false)).to.equal(2); | ||
}); | ||
}); | ||
describe('multOrDivide', function() { | ||
it('should divide num arg by mult arg if _up arg is true', function() { | ||
expect(freqi.multOrDivide(2, 2, true)).to.equal(1); | ||
}); | ||
it('should multiply num arg by mult arg if _up arg is false', function() { | ||
expect(freqi.multOrDivide(2, 2, false)).to.equal(4); | ||
}); | ||
}); | ||
describe('getPythagNoteWithinOct', function() { | ||
it('should divide freq arg by 2 arg if interval arg is odd and less than half of notesInOct and _up arg is true', function() { | ||
expect(freqi.getPythagNoteWithinOct(3, 12, 440, true)).to.equal(220); | ||
}); | ||
it('should multiply freq arg by 2 arg if interval arg is odd and less than half of notesInOct and _up arg is false', function() { | ||
expect(freqi.getPythagNoteWithinOct(3, 12, 440, false)).to.equal(880); | ||
}); | ||
it('should divide freq arg by 2 arg if interval arg is even and greater than half of notesInOct and _up arg is true', function() { | ||
expect(freqi.getPythagNoteWithinOct(8, 12, 440, true)).to.equal(220); | ||
}); | ||
it('should multiply freq arg by 2 arg if interval arg is even and greater than half of notesInOct and _up arg is false', function() { | ||
expect(freqi.getPythagNoteWithinOct(8, 12, 440, false)).to.equal(880); | ||
}); | ||
}); | ||
describe('getTruePythagNote', function() { | ||
beforeEach(function() { | ||
this.config = { | ||
startFreq: 261.6, | ||
interval: 0 | ||
}; | ||
this.configGNote = { | ||
startFreq: 261.6, | ||
interval: 7 | ||
}; | ||
this.configLowerFNote = { | ||
startFreq: 261.6, | ||
interval: -7 | ||
}; | ||
}); | ||
it('should return the start freq if interval is zero', function() { | ||
expect(freqi.getTruePythagNote(this.config, true)).to.equal(261.6); | ||
}); | ||
it('should return the start freq * 1.5 if interval is seven', function() { | ||
expect(freqi.getTruePythagNote(this.configGNote, true)).to.equal(261.6 * 1.5); | ||
}); | ||
it('should return the start freq / 1.5 if interval is minus seven', function() { | ||
expect(freqi.getTruePythagNote(this.configLowerFNote, false)).to.equal(261.6 / 1.5); | ||
}); | ||
}); | ||
describe('getModes', function() { | ||
@@ -784,0 +864,0 @@ it('should return an array the length of justTuningSystems plus the default (eqTemp) and the harmonic series', function() { |
84025
2108
137