diff --git a/src/utilities/MusicFunctions.ts b/src/utilities/MusicFunctions.ts index 95bf281..4187fa5 100644 --- a/src/utilities/MusicFunctions.ts +++ b/src/utilities/MusicFunctions.ts @@ -1,18 +1,8 @@ import { PITCH_CLASS_LETTERS } from '../Constants.js'; -import { Note, PitchClass, Scale } from '../types.js'; +import { Note, PitchClass } from '../types.js'; import { rearrangeArray } from './GeneralFunctions.js'; import { enharmonicPitchClass, getPitchClassIndex, normalizePitchClass } from './PureMusicUtils.js'; -/** - * Returns an array of notes with a specific octave. - * @param {Array} pitchClasses Array of pitch classes. - * @param {number} octave Octave to assign to notes.. - * @returns {Array} - */ -export function pitchClassesToNotes(pitchClasses: Array, octave: number): Array { - return pitchClasses.map(pitchClass => `${pitchClass}${octave}`); -} - /** * Returns an array of notes that represent a chord played on a piano in a certain octave. * @param {Array} pitchClasses @@ -25,22 +15,48 @@ export function pitchClassesToPianoChordNotes(pitchClasses: Array, o if(inversion) { pitchClasses = rearrangeArray(pitchClasses, inversion) as Array; } - + // const notes = pitchClassesToNotes(pitchClasses, octave); + // for(let i=1; i< notes.length; ++i) { + // const prevNote = notes[i-1]; + // const normalizedPrevNote = normalizeNote(prevNote); + // const note = notes[i]; + // const normalizedNote = normalizeNote(note); + // const { pitchClass, octave } = noteToObject(note); + // const { pitchClass: prevPitchClass, octave: prevOctave } = noteToObject(prevNote); + // const pcIndex = getPitchClassIndex(normalizePitchClass(pitchClass)); + // const prevPcIndex = getPitchClassIndex(normalizePitchClass(pitchClasses[i - 1])); + // if(pitchClass[0] === 'C' && prevPitchClass[0] === 'B') { + // notes[i] = `${pitchClass}${octave + 1}` as Note; + // } + // } + // return notes; let currentOctave = octave; + let wasNextOctave = false; - return pitchClasses.map((pitchClass, i) => { + const notes = pitchClasses.map((pitchClass, i) => { if(i !== 0) { const pcIndex = getPitchClassIndex(normalizePitchClass(pitchClass)); const prevPcIndex = getPitchClassIndex(normalizePitchClass(pitchClasses[i - 1])); // Checking if the octave needs to be incremented: - // We can know it definitely needs to be incremented if the current pitch class is C. - // Otherwise, we need to check if the current pitch class pitch class index is smaller than the previous one, as that would indicate that weve passed the B pitch class. - if((pcIndex < prevPcIndex && pitchClass[0] !== 'B') || pitchClass[0] === 'C') { + // We can know it definitely needs to be incremented if the current *ABSOLUTE* pitch class is C and the raw pitch class is not B. + const isC = normalizePitchClass(pitchClass) === 'C' && pitchClass[0] !== 'B'; + // Otherwise, we need to check if the current pitch class index is smaller than the previous one, as that would indicate that we've passed the B pitch class. + const isPassedB = pcIndex < prevPcIndex && pitchClass[0] !== 'B'; + // It should also be incremented if the raw pitch class is from the next octave regardless of the pitch class. + const isNextOctave = ['C', 'D'].includes(pitchClass[0]) && ['A', 'B'].includes(pitchClasses[i - 1][0]); + if((isPassedB || (isC) || isNextOctave) && !wasNextOctave) { currentOctave++; + } + if(wasNextOctave) { + wasNextOctave = false; + } + if(isNextOctave) { + wasNextOctave = true; } } return `${pitchClass}${currentOctave}`; }) as Array; + return notes; } /** diff --git a/src/utilities/PureMusicUtils.ts b/src/utilities/PureMusicUtils.ts index d833f3a..b9eb672 100644 --- a/src/utilities/PureMusicUtils.ts +++ b/src/utilities/PureMusicUtils.ts @@ -8,6 +8,34 @@ import { NoteAsObject, PitchClass, PitchClassLetter, Accidental, Note, Octave, RawPitchClass, RawFlatPitchClass, RawSharpPitchClass, } from '../types'; +/** + * Returns an array with numbers that represent the index of each pitch class as a number. + * @param pitchClasses + * @returns + */ +export function pitchClassesToNumbers(pitchClasses: Array) { + return pitchClasses.map(pc => getPitchClassIndex(pc)); +} + +/** + * Returns an array of notes with a specific octave. + * @param {Array} pitchClasses Array of pitch classes. + * @param {number} octave Octave to assign to notes.. + * @returns {Array} + */ +export function pitchClassesToNotes(pitchClasses: Array, octave: number): Array { + return pitchClasses.map(pitchClass => `${pitchClass}${octave}` as Note); +} + +/** + * + * @param notes + * @returns + */ +export function notesToPitchClasses(notes: Array) { + return notes.map(note => noteToObject(note as Note).pitchClass); +} + /** * Calculate the pure interval between 2 pitch classes. * @param {PitchClass} pitchClass1 first note @@ -157,9 +185,13 @@ export function normalizeNote(note: Note): Note { const { pitchClass, octave } = noteToObject(note); const normalizedPitchClass = normalizePitchClass(pitchClass); let octaveDifference = 0; - if(pitchClass[0] === 'B' && pitchClass.includes('#' || 'x')) { + const bWithSharps = pitchClass[0] === 'B' && pitchClass.includes('#' || 'x'); + const aWithSharps = pitchClass === 'A#x'; + const cWithFlats = pitchClass[0] === 'C' && pitchClass.includes('b'); + const dWithFlats = pitchClass === 'Dbbb'; + if(bWithSharps || aWithSharps) { octaveDifference = 1; - } else if(pitchClass[0] === 'C' && pitchClass.includes('b')) { + } else if(cWithFlats || dWithFlats) { octaveDifference = -1; } return `${normalizedPitchClass}${octave + octaveDifference}` as Note; diff --git a/test/utilities/music-functions.spec.js b/test/utilities/music-functions.spec.js index 9dfd1ba..e08ba25 100644 --- a/test/utilities/music-functions.spec.js +++ b/test/utilities/music-functions.spec.js @@ -1,6 +1,6 @@ import { extractOctave, extractPitchClass, getPitchClassesInterval, notesInRange, noteToObject, - pitchClassesToNotes, pitchClassesToPianoChordNotes, transposeNote, intervalsToNotes, + pitchClassesToPianoChordNotes, transposeNote, intervalsToNotes, spellScale, } from '../../lib/index.js'; @@ -46,14 +46,6 @@ describe('Music addon functions', () => { }); }); - describe('#pitchClassesToNotes', () => { - it('should return an array of notes when input is valid', () => { - const pitchClasses = ['C', 'E']; - const stub = ['C3', 'E3']; - expect(pitchClassesToNotes(pitchClasses, 3)).to.eql(stub); - }); - }); - describe('#pitchClassesToPianoChordNotes', () => { let g, gChord; beforeEach(() => { @@ -80,7 +72,14 @@ describe('Music addon functions', () => { expect(pitchClassesToPianoChordNotes(bigChord, 3)).to.eql(stub); }); - it('with a scale', () => { + it('with a flats scale', () => { + const bigChord = spellScale(intervalsToNotes('E', [0, 1, 2, 3, 5, 7, 9, 10])); + const stub = ['E3', 'F3', 'Gb3', 'Abb3', 'Bbb3', 'Cb4', 'Db4', 'Ebb4']; + + expect(pitchClassesToPianoChordNotes(bigChord, 3)).to.eql(stub); + }); + + it('with a sharps scale', () => { const bigChord = spellScale(intervalsToNotes('E', [0, 3, 4, 6, 8, 9, 11])); const stub = ['E3', 'Fx3', 'G#3', 'A#3', 'B#3', 'C#4', 'D#4']; diff --git a/test/utilities/pure-music-utils.spec.js b/test/utilities/pure-music-utils.spec.js index aa562c4..2256d54 100644 --- a/test/utilities/pure-music-utils.spec.js +++ b/test/utilities/pure-music-utils.spec.js @@ -1,5 +1,5 @@ import { - enharmonicPitchClass, getNotesInterval, getPatternFromNotes, getPatternFromPitchClasses, isPitchClass, isNote, normalizePitchClass, toFlat, normalizeNote, + enharmonicPitchClass, getNotesInterval, getPatternFromNotes, getPatternFromPitchClasses, isPitchClass, isNote, normalizePitchClass, toFlat, normalizeNote, pitchClassesToNotes, } from '../../lib/index.js'; describe('#PureMusicUtils', () => { @@ -194,4 +194,12 @@ describe('#PureMusicUtils', () => { expect(isNote('C')).to.equal(false); }); }); + + describe('#pitchClassesToNotes', () => { + it('should return an array of notes when input is valid', () => { + const pitchClasses = ['C', 'E']; + const stub = ['C3', 'E3']; + expect(pitchClassesToNotes(pitchClasses, 3)).to.eql(stub); + }); + }); });