all files / interval-density/ index.js

100% Statements 18/18
100% Branches 4/4
100% Functions 2/2
100% Lines 15/15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50    18×                                                           16× 43× 43× 42×        
'use strict'
 
var gamut = require('music-gamut')
var semitones = require('semitones')
var ic = require('interval-class')
function isNote (a) { return a.length !== 2 }
 
/**
 * Get the intervals analysis of a collection of notes
 *
 * Returns an array with the format `[p, m, n, s, d, t]` where:
 *
 * - p: the number of perfect fourths or fifths
 * - m: the number of major thirds or minor sixths
 * - n: the number of major sixths or minor thirds
 * - s: the number of major seconds or minor sevenths
 * - d: the number of major sevents or minor seconds
 * - t: the number of tritones
 *
 * This is, mostly, an academic puzzle to show the expresiveness of tonal.
 * Implements the ideas found in "The Analysis of Intervals" chapter from
 * [Harmonic Materials of Modern Music]():
 *
 * > The letters _pmn_, therefore, represent intervals commonly considered
 * consonant, whereas the letters _sdt_ represent the intervals commonly
 * considered dissonant. (...) A sonority represented, for example, by the
 * symbol `sd^2`, indicating a triad composed of one major second and two minor
 * seconds, would be recognized as a highly dissonant sound, while the symbol
 * `pmn` would indicate a consonant sound.
 *
 * @name interval.density
 * @function
 * @param {Array|String} notes - the notes to analyze
 * @return {Array} the _pmnsdt_ array
 */
module.exports = function (notes) {
  var a, b, i
  notes = gamut.parse(notes).filter(isNote)
  var len = notes.length
  var result = [0, 0, 0, 0, 0, 0]
  for (a = 0; a < len; a++) {
    for (b = a; b < len; b++) {
      i = ic(semitones(notes[b]) - semitones(notes[a]))
      if (i === 6) result[5] = result[5] + 1
      else if (i > 0) result[5 - i] = result[5 - i] + 1
    }
  }
  return result
}