musicvis-lib
Version:
Music analysis and visualization library
148 lines (134 loc) • 3.5 kB
JavaScript
import { getMidiNoteByNr } from '../fileFormats/Midi.js'
/**
* Stores a sequence of pitches and provides some methods to simplify and
* manipulate it.
*
* @todo implement keepOnlyHighestConcurrentNotes
*/
class PitchSequence {
/**
* @param {number[]} pitches pitches
*/
constructor (pitches = []) {
this._pitches = pitches
}
/**
* Creates a pitch sequence from an array of Notes
*
* @param {Note[]} notes notes
* @returns {PitchSequence} pitch sequence
*/
static fromNotes (notes = []) {
const pitches = [...notes]
.sort((a, b) => {
if (a.start === b.start) {
return a.pitch - b.pitch
}
return a.start - b.start
})
.map(d => d.pitch)
return new PitchSequence(pitches)
}
/**
* @param {string} string a string of Unicode characters
* @returns {PitchSequence} pitch sequence
*/
static fromCharString (string) {
if (!string || string.length === 0) { return new PitchSequence() }
const pitches = [...string].map((d, index) => string.charCodeAt(index))
return new PitchSequence(pitches)
}
/**
* @returns {number[]} pitches
*/
getPitches () {
return this._pitches
}
/**
* @returns {number} number of pitches
*/
length () {
return this._pitches.length
}
/**
* Turns pitch sequence into a string by turning each pitch into a character
* (based on Unicode index)
*
* @returns {string} string representation of note pitches
*/
toCharString () {
if (!this._pitches || this._pitches.length === 0) {
return ''
}
return String.fromCharCode(...this._pitches)
}
/**
* @returns {string} a string with the notes' names
*/
toNoteNameString () {
return this._pitches.map(p => getMidiNoteByNr(p).label).join(' ')
}
/**
* Reverses the order of pitches in this PitchSequence
*
* @returns {PitchSequence} this
*/
reverse () {
this._pitches = this._pitches.reverse()
return this
}
/**
* Takes a sequence of MIDI pitches and nomralizes them to be in [0, 11]
*
* @returns {PitchSequence} this
*/
removeOctaves () {
this._pitches = this._pitches.map(d => d % 12)
return this
}
/**
* Transforms note pitches to intervals, i.e. diffrences between to subsequent
* notes: C, C#, C, D => 1, -1, 2
*
* @returns {number[]} intervals
*/
toIntervals () {
const p = this._pitches
if (!p || p.length === 0 || p.length < 2) {
return []
}
const result = Array.from({ length: p.length - 1 })
for (let index = 1; index < p.length; index++) {
result[index - 1] = p[index] - p[index - 1]
}
return result
}
/**
* @returns {PitchSequence} clone
*/
clone () {
return new PitchSequence(this._pitches)
}
/**
* Returns true if this NoteArray and otherNoteArray have equal attributes.
*
* @param {NoteArray} otherPitchSequence another NoteArray
* @returns {boolean} true if equal
*/
equals (otherPitchSequence) {
if (!(otherPitchSequence instanceof PitchSequence)) {
return false
}
const p = otherPitchSequence.getPitches()
if (this._pitches.length !== p.length) {
return false
}
for (const [index, element] of p.entries()) {
if (this._pitches[index] !== element) {
return false
}
}
return true
}
}
export default PitchSequence