UNPKG

qambi

Version:

MIDI sequencer, loads MIDI files, can record and playback MIDI, uses WebMIDI and WebAudio

193 lines (157 loc) 4.42 kB
import {getSettings} from './settings' const pow = Math.pow const floor = Math.floor //const checkNoteName = /^[A-G]{1}(b{0,2}}|#{0,2})[\-]{0,1}[0-9]{1}$/ const regexCheckNoteName = /^[A-G]{1}(b|bb|#|##){0,1}$/ const regexCheckFullNoteName = /^[A-G]{1}(b|bb|#|##){0,1}(\-1|[0-9]{1})$/ const regexSplitFullName = /^([A-G]{1}(b|bb|#|##){0,1})(\-1|[0-9]{1})$/ const regexGetOctave = /(\-1|[0-9]{1})$/ const noteNames = { sharp: ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'], flat: ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'], 'enharmonic-sharp': ['B#', 'C#', 'C##', 'D#', 'D##', 'E#', 'F#', 'F##', 'G#', 'G##', 'A#', 'A##'], 'enharmonic-flat': ['Dbb', 'Db', 'Ebb', 'Eb', 'Fb', 'Gbb', 'Gb', 'Abb', 'Ab', 'Bbb', 'Bb', 'Cb'] } let noteNameMode let pitch /* settings = { name: 'C', octave: 4, fullName: 'C4', number: 60, frequency: 234.16 // not yet implemented } */ export function getNoteData(settings){ let { fullName, name, octave, mode, number, frequency, } = settings; ({noteNameMode, pitch} = getSettings()) if( typeof name !== 'string' && typeof fullName !== 'string' && typeof number !== 'number' && typeof frequency !== 'number'){ return null } if(number < 0 || number > 127){ console.log('please provide a note between 0 (C-1) and 127 (G9)') return null } mode = _checkNoteNameMode(mode) //console.log(mode) if(typeof number === 'number'){ ({ fullName, name, octave } = _getNoteName(number, mode)) }else if(typeof name === 'string'){ if(regexCheckNoteName.test(name)){ fullName = `${name}${octave}` number = _getNoteNumber(name, octave) }else{ console.log(`invalid name ${name}`) return null } }else if(typeof fullName === 'string'){ if(regexCheckFullNoteName.test(fullName)){ ({ octave, name, } = _splitFullName(fullName)) number = _getNoteNumber(name, octave) }else{ console.log(`invalid fullname ${fullName}`) return null } } let data = { name, octave, fullName, number, frequency: _getFrequency(number), blackKey: _isBlackKey(number), } //console.log(data) return data } function _getNoteName(number, mode = noteNameMode) { //let octave = Math.floor((number / 12) - 2), // → in Cubase central C = C3 instead of C4 let octave = floor((number / 12) - 1) let name = noteNames[mode][number % 12] return { fullName: `${name}${octave}`, name, octave, } } function _getOctave(fullName){ return parseInt(fullName.match(regexGetOctave)[0], 10) } function _splitFullName(fullName){ let octave = _getOctave(fullName) return{ octave, name: fullName.replace(octave, '') } } function _getNoteNumber(name, octave) { let keys = Object.keys(noteNames) let index for(let key of keys){ let mode = noteNames[key] index = mode.findIndex(x => x === name) if(index !== -1){ break } } //number = (index + 12) + (octave * 12) + 12 // → in Cubase central C = C3 instead of C4 let number = (index + 12) + (octave * 12)// → midi standard + scientific naming, see: http://en.wikipedia.org/wiki/Middle_C and http://en.wikipedia.org/wiki/Scientific_pitch_notation if(number < 0 || number > 127){ console.log('please provide a note between 0 (C-1) and 127 (G9)') return -1 } return number } function _getFrequency(number){ return pitch * pow(2, (number - 69) / 12) // midi standard, see: http://en.wikipedia.org/wiki/MIDI_Tuning_Standard } //@TODO: calculate note from frequency function _getPitch(hertz){ //fm = 2(m−69)/12(440 Hz). } function _checkNoteNameMode(mode){ let keys = Object.keys(noteNames) let result = keys.includes(mode) //console.log(result) if(result === false){ if(typeof mode !== 'undefined'){ console.log(`${mode} is not a valid note name mode, using "${noteNameMode}" instead`) } mode = noteNameMode } return mode } function _isBlackKey(noteNumber){ let black switch(true){ case noteNumber % 12 === 1://C# case noteNumber % 12 === 3://D# case noteNumber % 12 === 6://F# case noteNumber % 12 === 8://G# case noteNumber % 12 === 10://A# black = true break; default: black = false } return black }