smoosic
Version:
<sub>[Github site](https://github.com/Smoosic/smoosic) | [source documentation](https://smoosic.github.io/Smoosic/release/docs/modules.html) | [change notes](https://aarondavidnewman.github.io/Smoosic/changes.html) | [application](https://smoosic.github.i
425 lines • 16.1 kB
TypeScript
/**
* shared music theory and audio frequency routines, helper functions etc.
* @module /smo/data/music
*/
import { SmoNote } from './note';
import { Pitch, PitchKey, Clef, PitchLetter, AccidentalArray, AccidentalDisplay } from './common';
import { SmoMicrotone } from './noteModifiers';
/**
* Used for xml clef conversion
* @category SmoObject
*/
export interface ClefSign {
sign: string;
line?: number;
octave?: number;
}
/**
* calculate the pitch frequency, just temperment a=440, etc.
* @category SmoTransform
*/
export declare class SmoAudioPitch {
static _computeFrequencies(): Record<string, number>;
static frequencies: Record<string, number> | null;
static get pitchFrequencyMap(): Record<string, number>;
static _rawPitchToFrequency(smoPitch: Pitch, offset: number): number;
/**
*
* @param smoPitch - pitch from the SMO object
* @param offset - transpose 1/2 steps, 0 means no transpose
* @param tone - optional transpose microtone
* @returns
*/
static smoPitchToFrequency(smoPitch: Pitch, offset: number, tone: SmoMicrotone | null): number;
}
/**
* interface for valid non-tuplet duration value (all the
* base note lengths + dots)
* @category SmoTransform
*/
export interface SimpleDuration {
index: number;
ticks: number;
baseTicks: number;
dots: number;
}
/**
* description of a scale entry, from vex theory routines
* @category SmoTransform
*/
export interface VexNoteValue {
root_index: number;
int_val: number;
}
/**
* Describe the music theory role of an accidental vs. the key signature.
* Used to determine default enharmonic spelling.
* @category SmoTransform
*/
export interface KeySignatureRole {
letter: PitchLetter;
accidental: string;
role: string;
}
/**
* Helper functions that build on the VX music theory routines, and other
* utilities I wish were in VF.Music but aren't
* ## Note on pitch and duration format
* We use some VEX music theory routines and frequently need to convert
* formats from SMO format. We also use the same 'ticks' abstraction for
* durations.
*
* `Smo` uses pitch JSON:
* ```javascript
* {note:'c',accidental:'#',octave:4}
* ```
*
* `Vex` usually uses a canonical string:
*
* `'c#/4'`
*
* Depending on the operation, the octave might be omitted
*
* `Smo` uses a JSON for duration always:
* ```javascript
* {numerator:4096,denominator:1,remainder:0}
* ```
* `Vex` uses a letter duration (`'4'` or `'q'`for 1/4 note) and `'d'` for dot.
*
* I try to indicate whether I am using vex or smo notation in the function name.
* Duration methods start around line 1100
* @category SmoTransform
*/
export declare class SmoMusic {
/**
* Ported from vex, used to convert pitches to numerical values
* */
static get noteValues(): Record<string, VexNoteValue>;
static accidentalDisplay(pitch: Pitch, keySignature: string, duration: number, accArray: AccidentalArray[]): AccidentalDisplay | null;
/**
* return Vex canonical note enharmonic - e.g. Bb to A#
* */
static vexToCannonical(vexKey: string): string;
/**
* A note array (sans octave) in key-signature order
*/
static get circleOfFifths(): PitchKey[];
/**
* Return the number of lines above first ledger line below the staff.
* e.g. middle c in treble clef returns 0. Top line f in treble returns 5.
* @param clef
* @param pitch
* @returns number where 0 is the first ledger line below
*/
static pitchToStaffLine(clef: string, smoPitch: Pitch): number;
/**
* return above if the first pitch is above line 3, else below
* @param note
* @returns
*/
static positionFromStaffLine(note: SmoNote): "above" | "below";
/**
* gives the index into circle-of-fifths array for a pitch, considering enharmonics.
* */
static circleOfFifthsIndex(smoPitch: Pitch): number;
/**
* Get pitch to the right in circle of fifths
* */
static addSharp(smoPitch: Pitch): Pitch;
/**
* Get pitch to the left in circle of fifths
*/
static addFlat(smoPitch: Pitch): Pitch;
/**
* Add @param {number} - sharps
*/
static addSharps(smoPitch: Pitch, distance: number): Pitch;
/**
* Add *distance* sharps/flats to given key
*/
static addFlats(smoPitch: Pitch, distance: number): Pitch;
/**
* Convert array of smo pitches to vex keys, with adjustment for transpose and notehead
* @param pitchAr
* @param keyOffset
* @param noteHead
* @returns {string[]} - array of vex keyx
*/
static smoPitchesToVexKeys(pitchAr: Pitch[], keyOffset: number, noteHead: string | null): string[];
static get scaleIntervals(): Record<string, number[]>;
/**
* return true if the pitches match, except for octave.
* `{ letter: 'a', accidental: '#'}, { letter: 'a', accidental: '#'}` returns true
* `{ letter: 'a', accidental: '#'}, { letter: 'b', accidental: 'b'}` returns false
* */
static smoScalePitchMatch(p1: Pitch, p2: Pitch): boolean;
/**
* Return the number of ledger lines, or 0 if none. positive if
* below the staff, negative if above
* @param clef
* @param pitch
* @returns number where 0 is the top staff line
*/
static pitchToLedgerLine(clef: Clef, pitch: Pitch): number;
/**
* return flag state (up === 1 or down === 2) based on pitch and clef if auto
* */
static flagStateFromNote(clef: Clef, note: SmoNote): number;
/**
* an array of clefs and the xml information they map to
*/
static clefSigns: Record<string, ClefSign>;
/**
* an array of clefs and the xml information they map to
*/
static clefLedgerShift: Record<string, number>;
static scaleTones: string[];
static getScaleTonesForKey(keySignature: string): Record<string, string>;
/**
* The purpose of this table is to keep consistent enharmonic spelling when transposing
* instruments in different keys. It is not theoritically complete, e.g.
* there is no reason to distinguish between #5 used as a leading tone for vi- or
* as an augmented chord, the spelling is the same. It does not show a preference
* for notes that don't have an obvious purpose in the key, e.g. it does not try to compute the
* equivalent to 'e#' in the key of 'c'. The computation of the 'intended key area' is
* beyond the scope of a music program to interpret.
*/
static get enharmonicRoles(): Record<string, KeySignatureRole[]>;
/**
* Find the harmonic role for the given pitch
* @param smoPitch
* @param keySignature
* @returns
*/
static findRoleOfPitch(smoPitch: Pitch, keySignature: string): string;
/**
* Given a harmonic role, find the pitch that matches it. If there is no one, just
* return the raw transposition
* @param role
* @param keySignature
* @param transposedPitch
* @returns
*/
static findPitchForRole(role: string, keySignature: string, transposedPitch: Pitch): Pitch;
static rawTranspose(pitch: Pitch, offset: number): Pitch;
static transposePitchForKey(pitch: Pitch, originalKey: string, destinationKey: string, offset: number): Pitch;
/**
* convert from SMO to VEX format so we can use the VexFlow tables and methods
* example:
* `{letter,octave,accidental}` object to vexKey string `'f#'`
* */
static _pitchToVexKey(smoPitch: Pitch): string;
/**
* convert smo pitch to easy score (vex) format. Mostly used
* for debugging and generating Vex test cases
* @param smoPitch
* @returns - a string that can be converted to a VEX routine, with some difficulty
*/
static pitchToEasyScore(smoPitch: Pitch): string;
/**
* convert a pitch to a format expected by the MIDI writer
* @param smoPitch pitch to convert
* @returns pitch in MIDI string format.
*/
static smoPitchToMidiString(smoPitch: Pitch): string;
static smoPitchesToMidiStrings(smoPitches: Pitch[]): string[];
/**
* filled in from the midi routines borrowed from
* // https://github.com/grimmdude/MidiWriterJS
* @param midiPitch pitch from MIDIwrite
* @returns SMO pitch
*/
static midiPitchToSmoPitch(midiPitch: string): Pitch;
static midiPitchToMidiNumber(midiPitch: string): number;
static pitchToVexKey(smoPitch: Pitch, head?: string | null): string;
/**
* Turns vex pitch string into smo pitch, e.g.
* `cn/4 => {'c','n',4}`
* @param vexPitch
* @returns SmoPitch
* */
static vexToSmoPitch(vexPitch: string): Pitch;
/**
* Convert to smo pitch, without octave
* ``['f#'] => [{letter:'f',accidental:'#'}]``
* */
static vexToSmoKey(vexPitch: string): PitchKey;
static smoPitchesToVex(pitchAr: Pitch[]): string[];
/**
* @param vexKey - pitch in vex format
* @returns pitch in vex format, sans octave
*/
static stripVexOctave(vexKey: string): string;
/**
* compare pitches for frequency match
*/
static pitchArraysMatch(ar1: Pitch[], ar2: Pitch[]): boolean;
/**
* convert pitches to integer pitch representations
* by calling smoPitchToInt
* @param pitches Smo pitches
* @returns
*/
static smoPitchesToIntArray(pitches: Pitch[]): number[];
/**
* convert a pitch to an integer value, used for transpositions, intervals, etc.
* @param pitch
* @returns
*/
static smoPitchToInt(pitch: Pitch): number;
/**
* Convert a number to a SMO pitch
* @param intValue - number of 1/2 steps from `c0`
* @returns
*/
static smoIntToPitch(intValue: number): Pitch;
static pitchKeyToPitch(pk: PitchKey): Pitch;
/**
* Consider instrument transpose when setting key -
* e.g. Eb for Bb instruments is F. Note: return value is not
* a valid VEX key signature. Use vexKeySignatureTranspose for that.
*/
static vexKeySigWithOffset(vexKey: string, offset: number): string;
static _enharmonics: Record<string, string[]> | null;
/**
* return a map of enharmonics for choosing or cycling. notes are in vexKey form.
*/
static get enharmonics(): Record<string, string[]>;
/**
* Get enharmonic equivalent of given notes for cycle/choose
* @param vexKey
* @returns
*/
static getEnharmonics(vexKey: string): string[];
/**
* return the next note from the cycle in `getEnharmonics`
*/
static getEnharmonic(vexKey: string): string;
/**
* Return a pitch a diatonic step away from SmoPitch in vexKey
* @param smoPitch
* @param vexKey
* @param direction
* @returns
*/
static closestTonic(smoPitch: Pitch, vexKey: string, direction: number): Pitch;
static toValidKeySignature(vexKey: string): string;
/**
* When transposing, get the enharmonic that most closely fits the key
* `getEnharmonicInKey` returns an alternate to the given pitch, or the same pitch.
* `getKeyFriendlyEnharmonic` return a pitch for a given key, given the letter name only
* @param smoPitch
* @param keySignature
* @returns
*/
static getEnharmonicInKey(smoPitch: Pitch, keySignature: string): Pitch;
/**
* fix the enharmonic to match the key, if possible
* @example
* `getKeyFriendlyEnharmonic('b','eb'); => returns 'bb'
* return vex string
* `getEnharmonicInKey` returns an alternate to the given pitch, or the same pitch.
* `getKeyFriendlyEnharmonic` return a pitch for a given key, given the letter name only
*/
static getKeyFriendlyEnharmonic(letter: string, keySignature: string): string;
/**
// given a letter pitch (a,b,c etc.), and a key signature, return the actual note
// that you get without accidentals
// `SmoMusic.getKeySignatureKey('F','G'); // returns f#`
* @param letter
* @param keySignature
* @returns
*/
static getKeySignatureKey(letter: PitchLetter, keySignature: string): string;
static getAccidentalForKeySignature(smoPitch: Pitch, keySignature: string): string;
static isPitchInKeySignature(smoPitch: Pitch, keySignature: string): boolean;
static getIntervalInKey(pitch: Pitch, keySignature: string, interval: number): Pitch;
static getLetterNotePitch(prevPitch: Pitch, letter: PitchLetter, key: string): Pitch;
/**
* return the key signature, transposed a number of 1/2 steps in Vex key format
* @param key start key
* @param transposeIndex number of 1/2 steps
* @returns {string} - vex key
*/
static vexKeySignatureTranspose(key: string, transposeIndex: number): string;
static get frequencyMap(): Record<string, number>;
static get letterPitchIndex(): Record<PitchLetter, number>;
/**
* Indicate if a change from letter note 'one' to 'two' needs us to adjust the
* octave due to the `SmoMusic.letterPitchIndex` (b0 is higher than c0)
* */
static letterChangedOctave(one: PitchLetter, two: PitchLetter): number;
/**
* Transpose a `Pitch` `offset` 1/2 steps
* @param pitch
* @param offset
* @returns
*/
static getKeyOffset(pitch: Pitch, offset: number): Pitch;
static get keySignatureLength(): Record<string, number>;
static getSharpsInKeySignature(key: string): number;
static getFlatsInKeySignature(key: string): number;
static midiKeyToVexKey(midiKey: number): string;
static highestDuration: number;
static lowestDuration: number;
static durationsDescending: number[];
static durationsAscending: number[];
static ticksFromSmoDuration(duration: SimpleDuration): number;
static _validDurations: Record<number, SimpleDuration> | null;
static _validDurationKeys: number[];
static get validDurations(): Record<number, SimpleDuration>;
/**
* Get the closest duration from ticks
* @param ticks
* @returns
*/
static closestSmoDurationFromTicks(ticks: number): SimpleDuration | null;
static _ticksToDuration: Record<string, string>;
static get ticksToDuration(): Record<string, string>;
static timeSignatureToTicks(timeSignature: string): number;
static smoTicksToVexDots(ticks: number): number;
static midiTicksForQuantizeTo(ticks: number): number[];
static get midiTicksForQuantizeMap(): Record<number, number[]>;
static midiTicksForQuantize(ticks: number): number[];
static binarySearch(target: number, ix: number, partition: number, input: number[]): {
cost: number;
result: number;
newIx: number;
oldIx: number;
partition: number;
input: number[];
} | {
cost: number;
result: number;
newIx: number;
partition: number;
input: number[];
oldIx?: undefined;
};
static midiTickSearch(target: number, quantize: number): {
cost: number;
result: number;
};
static closestVexDuration(ticks: number): string;
static closestBeamDuration(ticks: number): SimpleDuration;
static closestDurationTickLtEq(ticks: number): number;
/**
* Return array of valid note-lengths from an odd number of ticks,
* so we can come as close as possible to representing the ticks with notes
* @param ticks
* @returns
*/
static splitIntoValidDurations(ticks: number): number[];
static vexStemType(ticks: number): string;
static getNextDottedLevel(ticks: number): number;
static getPreviousDottedLevel(ticks: number): number;
/**
* break the duration up into an array of durations, to split a long
* note up between bars when pasting.
* @param duration
* @returns
*/
static gcdMap(duration: number): number[];
static notesFromLetters(startPitch: Pitch, clef: Clef, keySignature: string, duration: number, letters: string): SmoNote[];
}
//# sourceMappingURL=music.d.ts.map