import { ModificationBase, NoteDetails } from './ModificationBase.mjs';
import * as WebAudioApiErrors from '../modules/Errors.mjs';
/**
* Class representing a Tuplet modification.
*
* A Tuplet modification causes a note to play for only 1/N of the duration of the next
* longest standard note duration. For example, three eighth notes in a triplet would
* take the same amount of time to play as a single quarter note. As an alternate
* formulation, an N-tuplet-modified note would play for 2/N of its printed duration.
*
* @extends ModificationBase
*/
export class Tuplet extends ModificationBase {
/**
* Constructs a new {@link Tuplet} modification object.
*/
constructor(tempo, key, details) {
super(tempo, key, details);
}
/**
* Returns a list of all parameters available for use in this modification, including whether
* the parameter is required or optional when playing back either a "sequence" or just a
* single "note".
*
* @returns {Object<string,Object<string,string[]>>} List of modification-specific parameter keys and when they are required
*/
static getParameters() {
return {
required: {
singleNote: ['numNotes', 'intoNumNotes'],
sequence: []
},
optional: {
singleNote: [],
sequence: []
}
};
}
/**
* Returns whether this modification can be used to modify a sequence of notes.
*
* @returns {boolean} Whether this modification can be used to modify a sequence of notes
*/
static canModifySequence() {
return true;
}
static inferParametersFromSequence(sequence) {
return { 'numNotes': sequence.length, 'intoNumNotes': ((sequence.length == 3) ? 2 : 4) };
}
/**
* Returns a list of all modified notes, durations, and velocities as generated by the
* corresponding modification class.
*
* The `details` variable must contain the following two keys:
*
* `numNotes`: Number of notes taking part in the tuplet
* `intoNumNotes`: Number of notes of the same duration that should total the full duration of the tuplet
*
* @param {Object<string, number>} details - Timing information about the triplet
* @returns {NoteDetails[]} List of {@link NoteDetails} to replace the original note
*/
getModifiedNoteDetails(details) {
if (!('numNotes' in details) || !('intoNumNotes' in details))
throw new WebAudioApiErrors.WebAudioValueError('The "details" variable must contain the following keys: numNotes, intoNumNotes');
else if ((Number(details.numNotes) < 0) || (Number(details.intoNumNotes) < 0))
throw new WebAudioApiErrors.WebAudioValueError('The "numNotes" and "intoNumNotes" parameters must be positive');
else if (Number(details.numNotes) < Number(details.intoNumNotes))
throw new WebAudioApiErrors.WebAudioValueError('The "numNotes" parameters must be greater than the "intoNumNotes" parameter');
return [new NoteDetails(
this.unmodifiedDetails.note,
this.unmodifiedDetails.velocity,
this.unmodifiedDetails.duration * (Number(details.numNotes) / Number(details.intoNumNotes)),
undefined,
this.unmodifiedDetails.usedDuration * (Number(details.numNotes) / Number(details.intoNumNotes))
)];
}
}