UNPKG

gibberwocky

Version:

music sequencing for Live and Max/MSP

310 lines (241 loc) 7.75 kB
let Gibber = null let Note = { names: [ 'c','db','d','eb','e','f','gb','g','ab','a','bb','b' ], indices: { c: 0, 'c#': 1, db: 1, d:2, 'd#':3, eb:3, e:4, fb:4, f:5, 'f#':6, gb:6, g:7, 'g#':8, ab:8, a:9, 'a#':10, bb:10, b:11, cb:11 }, getMIDI() { return this.value }, getFrequency() { return Math.pow( 2, (this.value - 69) / 12 ) * 440 }, getString() { let octave = Math.floor( ( this.value / 12 ) ) - 1, index = this.value % 12 return Note.names[ index ] + octave }, create( value ) { let midiValue = Note.convertToMIDI( value ), note = Object.create( this ) note.value = midiValue return note }, convertToMIDI( value ) { let midiValue if( typeof value === 'string' ) { midiValue = this.convertStringToMIDI( value ) } else { midiValue = Scale.master.getMIDINumber( value ) } return midiValue }, convertStringToMIDI( stringValue ) { let octave = parseInt( stringValue.substr( -1 ) ), noteName = stringValue.substr( 0, stringValue.length === 2 ? 1 : 2 ), noteNum = Note.indices[ noteName ] return ( octave + 1 ) * 12 + noteNum }, convertMIDIToString( midiValue ) { }, convertMIDIToFrequency( midiValue ) { }, convertScaleMemberToMIDI( scaleIndex, scale ) { } } let Chord = { create( str ) { let chord = Object.create( this ) let [ root, octave, quality, extension ] = Chord.parseString( str ) Object.assign( chord, { root, octave, quality, extension, notes: [] }) chord.notes[ 0 ] = parseInt( Note.convertStringToMIDI( root + octave ) ) let _quality = Chord.qualities[ chord.quality ] for( let i = 0; i < _quality.length; i++ ) { chord.notes.push( chord.notes[ 0 ] + _quality[ i ] ) } if( chord.extension ) { // split each extension into array chord.extensions = extension.split(/(b?#?\d+)/i) for( let i = 0; i < chord.extensions.length; i++ ) { let _extension = chord.extensions[ i ] if( _extension !== '' ) chord.notes.push( Chord.extensions[ _extension ]( chord.notes ) ) } } return chord }, qualities: { min: [ 3, 7 ], maj: [ 4, 7 ], dim: [ 3, 6 ], aug: [ 4, 8 ], sus: [ 5, 7 ] }, extensions: { ['7'] ( notes ) { return notes[ 2 ] + 3 }, ['#7'] ( notes ) { return notes[ 2 ] + 4 }, ['9'] ( notes ) { return notes[ 2 ] + 7 }, ['b9'] ( notes ) { return notes[ 2 ] + 6 } }, parseString( str ) { let [ chord, root, octave, quality, extension ] = str.match(/([A-Za-z]b?#?)(\d)([a-z]{3})([b?#?\d]*)/i) return [ root.toLowerCase(), octave, quality.toLowerCase(), extension ] }, } let Scale = { create( root, mode ) { let scale = Object.create( this ) scale.rootNumber = scale.baseNumber = Note.convertToMIDI( root ) scale.__degree = 'i' scale.quality = 'minor' scale.root = function( v ) { if( typeof v === 'string' ) { root = v scale.baseNumber = Note.convertToMIDI( root ) const degree = Scale.degrees[ scale.quality ][ scale.__degree ] scale.rootNumber = degree.offset + scale.baseNumber }else if( typeof v === 'number' ) { scale.baseNumber = v const degree = Scale.degrees[ scale.quality ][ scale.__degree ] scale.rootNumber = degree.offset + scale.baseNumber }else if( typeof v === 'number' ) { }else{ return root } } scale.modulate = scale.root scale.degree = function( __degree ) { if( typeof __degree === 'string' ) { const degree = Scale.degrees[ scale.quality ][ __degree ] scale.__degree = __degree scale.rootNumber = degree.offset + scale.baseNumber scale.mode( degree.mode ) } else { return scale.__degree } } scale.modeNumbers = Scale.modes[ mode ] scale.mode = function( v ) { if( typeof v === 'string' ) { mode = v mode = mode[0].toLowerCase() + mode.slice(1) scale.modeNumbers = Scale.modes[ mode ] }else{ return mode } } scale.root.valueOf = () => { return root } scale.mode.valueOf = () => { return mode } if( Gibber !== null ) { Gibber.addSequencingToMethod( scale, 'root', 3 ) Gibber.addSequencingToMethod( scale, 'modulate', 3 ) Gibber.addSequencingToMethod( scale, 'mode', 2 ) Gibber.addSequencingToMethod( scale, 'degree',1 ) } return scale }, getMIDINumber( scaleDegree ) { let mode = this.modeNumbers, isNegative = scaleDegree < 0, octave = Math.floor( scaleDegree / mode.length ), degree = isNegative ? mode[ Math.abs( mode.length + (scaleDegree % mode.length ) ) ] : mode[ scaleDegree % mode.length ], out if( degree === undefined ) degree = 0 out = isNegative ? this.rootNumber + (octave * 12 ) + degree : this.rootNumber + (octave * 12 ) + degree return out }, degrees: { major: {}, minor: {} }, __getBaseNumber( chord ) { const start = scale.baseNumber }, __initDegrees() { const base = [ 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii' ] const scales = [ { name:'minor', values:Scale.modes.aeolian }, { name:'major', values:Scale.modes.ionian } ] for( let scale of scales ) { let name = scale.name let values = scale.values for( let i = 0; i < base.length; i++ ) { const chord = base[ i ] this.degrees[ name ][ chord ] = { mode:'aeolian', offset: values[i] } } for( let i = 0; i < base.length; i++ ) { const chord = base[ i ].toUpperCase() this.degrees[ name ][ chord ] = { mode:'ionian', offset: values[i] } } for( let i = 0; i < base.length; i++ ) { const chord = base[ i ] + '7' this.degrees[ name ][ chord ] = { mode:'dorian', offset: values[i] } } for( let i = 0; i < base.length; i++ ) { const chord = base[ i ].toUpperCase() + '7' this.degrees[ name ][ chord ] = { mode:'mixolydian', offset: values[i] } } for( let i = 0; i < base.length; i++ ) { const chord = base[ i ] + 'o' this.degrees[ name ][ chord ] = { mode:'locrian', offset: values[i] } } for( let i = 0; i < base.length; i++ ) { const chord = base[ i ] + 'M7' this.degrees[ name ][ chord ] = { mode:'melodicminor', offset: values[i] } } } }, modes: { ionian: [0,2,4,5,7,9,11], dorian: [0,2,3,5,7,9,10], phrygian: [0,1,3,5,7,8,10], lydian: [0,2,4,6,7,9,11], mixolydian: [0,2,4,5,7,9,10], aeolian: [0,2,3,5,7,8,10], locrian: [0,1,3,5,6,8,10], melodicminor:[0,2,3,5,7,8,11], wholeHalf: [0,2,3,5,6,8,9,11], halfWhole: [0,1,3,4,6,7,9,10], chromatic: [0,1,2,3,4,5,6,7,8,9,10,11], } } Scale.modes.major = Scale.modes.ionian Scale.modes.minor = Scale.modes.aeolian Scale.modes.blues = Scale.modes.mixolydian module.exports = { Note, Chord, Scale, init( _Gibber ) { Gibber = _Gibber; Scale.__initDegrees() Scale.master = Scale.create( 'c4','aeolian' ) return this }, export( obj ) { obj.Theory = this obj.Scale = Scale.master obj.ntof = Note.convertToMIDI const base = [ 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii' ] for( let chord of base ) { obj[ chord ] = chord const upper = chord.toUpperCase() obj[ upper ] = upper obj[ chord+'7'] = chord+'7' obj[ upper+'7'] = upper+'7' obj[ chord+'M7'] = chord+'M7' } } }