import { ModificationBase, NoteDetails } from './ModificationBase.mjs';
import * as WebAudioApiErrors from '../modules/Errors.mjs';
/**
* Class representing a Tie modification.
*
* A Tie modification causes a note to continue to play for both its own duration and
* the duration of the note to which it is tied.
*
* @extends ModificationBase
*/
export class Tie extends ModificationBase {
/**
* Constructs a new {@link Tie} 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: [],
sequence: []
},
optional: {
singleNote: ['tiedDuration'],
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, index) {
const params = { 'tiedDuration': 0 };
for (let i = index; i < sequence.length; ++i)
params.tiedDuration = 1.0 / ((params.tiedDuration ? (1.0 / params.tiedDuration) : 0.0) + (1.0 / (Array.isArray(sequence[i][0]) ? sequence[i][0][1] : sequence[i][1])));
return params;
}
/**
* Returns a list of all modified notes, durations, and velocities as generated by the
* corresponding modification class.
*
* The `details` variable may contain the following key:
*
* `tiedDuration`: The duration of the note to which this note is tied
*
* @param {Object<string, number>} details - Information about the tied phrase
* @returns {NoteDetails[]} List of {@link NoteDetails} to replace the original note
*/
getModifiedNoteDetails(details) {
if (!('tiedDuration' in details)) {
if (!('implicit' in details))
throw new WebAudioApiErrors.WebAudioValueError('The "details" variable must contain the following keys: tiedDuration');
details.tiedDuration = details.implicit;
}
if (Number(details.tiedDuration) < 0.0)
throw new WebAudioApiErrors.WebAudioValueError(`The target tied duration (${details.tiedDuration}) cannot be negative`);
const duration = (this.unmodifiedDetails.duration < 0) ? -this.unmodifiedDetails.duration : (60.0 / ((this.unmodifiedDetails.duration / this.tempo.beatBase) * this.tempo.beatsPerMinute));
const tiedDuration = (Number(details.tiedDuration) > 0) ? (60.0 / ((Number(details.tiedDuration) / this.tempo.beatBase) * this.tempo.beatsPerMinute)) : 0.0;
return [new NoteDetails(
this.unmodifiedDetails.note,
this.unmodifiedDetails.velocity,
-(duration + tiedDuration),
0.0,
this.unmodifiedDetails.duration
)];
}
}