189 lines
4.1 KiB
JavaScript
189 lines
4.1 KiB
JavaScript
import Gain from 'Tone/core/Gain'
|
|
import Tone from 'Tone/core/Tone'
|
|
import Frequency from 'Tone/type/Frequency'
|
|
import Pedal from './Pedal'
|
|
import Note from './Note'
|
|
import Harmonics from './Harmonics'
|
|
import Release from './Release'
|
|
import Salamander from './Salamander'
|
|
|
|
/**
|
|
* @class Multisampled Grand Piano using [Salamander Piano Samples](https://archive.org/details/SalamanderGrandPianoV3)
|
|
* @extends {Tone}
|
|
*/
|
|
export default class Piano extends Tone{
|
|
|
|
constructor(range=[21, 108], velocities=1, release=true){
|
|
|
|
super(0, 1)
|
|
|
|
this._loaded = false
|
|
|
|
this._heldNotes = new Map()
|
|
|
|
this._sustainedNotes = new Map()
|
|
|
|
this._notes = new Note(range, velocities).connect(this.output)
|
|
|
|
this._pedal = new Pedal(release).connect(this.output)
|
|
|
|
if (release){
|
|
this._harmonics = new Harmonics(range).connect(this.output)
|
|
|
|
this._release = new Release(range).connect(this.output)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load all the samples
|
|
* @param {String} baseUrl The url for the Salamander base folder
|
|
* @return {Promise}
|
|
*/
|
|
load(url){
|
|
const promises = [this._notes.load(url), this._pedal.load(url)]
|
|
if (this._harmonics){
|
|
promises.push(this._harmonics.load(url))
|
|
}
|
|
if (this._release){
|
|
promises.push(this._release.load(url))
|
|
}
|
|
return Promise.all(promises).then(() => {
|
|
this._loaded = true
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Put the pedal down at the given time. Causes subsequent
|
|
* notes and currently held notes to sustain.
|
|
* @param {Time} time The time the pedal should go down
|
|
* @returns {Piano} this
|
|
*/
|
|
pedalDown(time){
|
|
if (this._loaded){
|
|
time = this.toSeconds(time)
|
|
if (!this._pedal.isDown(time)){
|
|
this._pedal.down(time)
|
|
}
|
|
}
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Put the pedal up. Dampens sustained notes
|
|
* @param {Time} time The time the pedal should go up
|
|
* @returns {Piano} this
|
|
*/
|
|
pedalUp(time){
|
|
if (this._loaded){
|
|
time = this.toSeconds(time)
|
|
if (this._pedal.isDown(time)){
|
|
this._pedal.up(time)
|
|
// dampen each of the notes
|
|
this._sustainedNotes.forEach((notes) => {
|
|
notes.forEach((note) => {
|
|
note.stop(time)
|
|
})
|
|
})
|
|
this._sustainedNotes.clear()
|
|
}
|
|
}
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Play a note.
|
|
* @param {String|Number} note The note to play
|
|
* @param {Number} velocity The velocity to play the note
|
|
* @param {Time} time The time of the event
|
|
* @return {Piano} this
|
|
*/
|
|
keyDown(note, velocity=0.8, time=Tone.now()){
|
|
if (this._loaded){
|
|
time = this.toSeconds(time)
|
|
|
|
if (this.isString(note)){
|
|
note = Math.round(Frequency(note).toMidi())
|
|
}
|
|
|
|
if (!this._heldNotes.has(note)){
|
|
let key = this._notes.start(note, velocity, time)
|
|
if (key){
|
|
this._heldNotes.set(note, key)
|
|
}
|
|
}
|
|
}
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Release a held note.
|
|
* @param {String|Number} note The note to stop
|
|
* @param {Time} time The time of the event
|
|
* @return {Piano} this
|
|
*/
|
|
keyUp(note, time=Tone.now()){
|
|
if (this._loaded){
|
|
time = this.toSeconds(time)
|
|
|
|
if (this.isString(note)){
|
|
note = Math.round(Frequency(note).toMidi())
|
|
}
|
|
|
|
if (this._heldNotes.has(note)){
|
|
|
|
let key = this._heldNotes.get(note)
|
|
this._heldNotes.delete(note)
|
|
|
|
if (this._release){
|
|
this._release.start(note, time)
|
|
}
|
|
|
|
if (this._pedal.isDown(time)){
|
|
let notes = []
|
|
if (this._sustainedNotes.has(note)){
|
|
notes = this._sustainedNotes.get(note)
|
|
}
|
|
notes.push(key)
|
|
this._sustainedNotes.set(note, notes)
|
|
} else {
|
|
let dampenGain = key.stop(time)
|
|
if (this._harmonics){
|
|
this._harmonics.start(note, dampenGain, time)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Set the volumes of each of the components
|
|
* @param {String} param
|
|
* @param {Decibels} vol
|
|
* @return {Piano} this
|
|
* @example
|
|
* //either as an string
|
|
* piano.setVolume('release', -10)
|
|
*/
|
|
setVolume(param, vol){
|
|
switch(param){
|
|
case 'note':
|
|
this._notes.volume = vol
|
|
break
|
|
case 'pedal':
|
|
this._pedal.volume = vol
|
|
break
|
|
case 'release':
|
|
if (this._release){
|
|
this._release.volume = vol
|
|
}
|
|
break
|
|
case 'harmonics':
|
|
if (this._harmonics){
|
|
this._harmonics.volume = vol
|
|
}
|
|
break
|
|
}
|
|
return this
|
|
}
|
|
} |