mirror of
https://github.com/jiojciojsioe3/a3cjroijsiojiorj.git
synced 2024-11-16 16:01:50 +08:00
194 lines
4.7 KiB
JavaScript
194 lines
4.7 KiB
JavaScript
|
class SoundBuffer{
|
|||
|
constructor(){
|
|||
|
this.context = new AudioContext()
|
|||
|
var resume = () => {
|
|||
|
if(this.context.state == "suspended"){
|
|||
|
this.context.resume()
|
|||
|
}
|
|||
|
removeEventListener("click", resume)
|
|||
|
}
|
|||
|
addEventListener("click", resume)
|
|||
|
}
|
|||
|
load(url, gain){
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
var request = new XMLHttpRequest()
|
|||
|
request.open("GET", url)
|
|||
|
request.responseType = "arraybuffer"
|
|||
|
request.addEventListener("load", () => {
|
|||
|
this.context.decodeAudioData(request.response, buffer => {
|
|||
|
resolve(new Sound(gain || {soundBuffer: this}, buffer))
|
|||
|
}, reject)
|
|||
|
})
|
|||
|
request.addEventListener("error", reject)
|
|||
|
request.addEventListener("abort", reject)
|
|||
|
request.send()
|
|||
|
})
|
|||
|
}
|
|||
|
createGain(){
|
|||
|
return new SoundGain(this)
|
|||
|
}
|
|||
|
setCrossfade(gain1, gain2, median){
|
|||
|
gain1.setCrossfade(1 - median)
|
|||
|
gain2.setCrossfade(median)
|
|||
|
}
|
|||
|
getTime(){
|
|||
|
return this.context.currentTime
|
|||
|
}
|
|||
|
convertTime(time, absolute){
|
|||
|
time = (time || 0)
|
|||
|
if(time < 0){
|
|||
|
time = 0
|
|||
|
}
|
|||
|
return time + (absolute ? 0 : this.getTime())
|
|||
|
}
|
|||
|
createSource(sound){
|
|||
|
var source = this.context.createBufferSource()
|
|||
|
source.buffer = sound.buffer
|
|||
|
source.connect(sound.gain.gainNode || this.context.destination)
|
|||
|
return source
|
|||
|
}
|
|||
|
}
|
|||
|
class SoundGain{
|
|||
|
constructor(soundBuffer){
|
|||
|
this.soundBuffer = soundBuffer
|
|||
|
this.gainNode = soundBuffer.context.createGain()
|
|||
|
this.gainNode.connect(soundBuffer.context.destination)
|
|||
|
this.setVolume(1)
|
|||
|
}
|
|||
|
load(url){
|
|||
|
return this.soundBuffer.load(url, this)
|
|||
|
}
|
|||
|
convertTime(time, absolute){
|
|||
|
return this.soundBuffer.convertTime(time, absolute)
|
|||
|
}
|
|||
|
setVolume(amount){
|
|||
|
this.gainNode.gain.value = amount * amount
|
|||
|
this.volume = amount
|
|||
|
}
|
|||
|
setCrossfade(amount){
|
|||
|
this.setVolume(Math.pow(Math.sin(Math.PI / 2 * amount), 1 / 4))
|
|||
|
}
|
|||
|
fadeIn(duration, time, absolute){
|
|||
|
this.fadeVolume(0, this.volume * this.volume, duration, time, absolute)
|
|||
|
}
|
|||
|
fadeOut(duration, time, absolute){
|
|||
|
this.fadeVolume(this.volume * this.volume, 0, duration, time, absolute)
|
|||
|
}
|
|||
|
fadeVolume(vol1, vol2, duration, time, absolute){
|
|||
|
time = this.convertTime(time, absolute)
|
|||
|
this.gainNode.gain.linearRampToValueAtTime(vol1, time)
|
|||
|
this.gainNode.gain.linearRampToValueAtTime(vol2, time + (duration || 0))
|
|||
|
}
|
|||
|
mute(){
|
|||
|
this.gainNode.gain.value = 0
|
|||
|
}
|
|||
|
unmute(){
|
|||
|
this.setVolume(this.volume)
|
|||
|
}
|
|||
|
}
|
|||
|
class Sound{
|
|||
|
constructor(gain, buffer){
|
|||
|
this.gain = gain
|
|||
|
this.buffer = buffer
|
|||
|
this.soundBuffer = gain.soundBuffer
|
|||
|
this.duration = buffer.duration
|
|||
|
this.timeouts = new Set()
|
|||
|
this.sources = new Set()
|
|||
|
}
|
|||
|
getTime(){
|
|||
|
return this.soundBuffer.getTime()
|
|||
|
}
|
|||
|
convertTime(time, absolute){
|
|||
|
return this.soundBuffer.convertTime(time, absolute)
|
|||
|
}
|
|||
|
setTimeouts(time){
|
|||
|
return new Promise(resolve => {
|
|||
|
var relTime = time - this.getTime()
|
|||
|
if(relTime > 0){
|
|||
|
var timeout = setTimeout(() => {
|
|||
|
this.timeouts.delete(timeout)
|
|||
|
resolve()
|
|||
|
}, relTime * 1000)
|
|||
|
this.timeouts.add(timeout)
|
|||
|
}else{
|
|||
|
resolve()
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
clearTimeouts(){
|
|||
|
this.timeouts.forEach(timeout => {
|
|||
|
clearTimeout(timeout)
|
|||
|
this.timeouts.delete(timeout)
|
|||
|
})
|
|||
|
}
|
|||
|
playLoop(time, absolute, seek1, seek2, until){
|
|||
|
time = this.convertTime(time, absolute)
|
|||
|
seek1 = seek1 || 0
|
|||
|
seek2 = seek2 || seek1
|
|||
|
until = until || this.duration
|
|||
|
this.loop = {
|
|||
|
started: time + until - seek1,
|
|||
|
seek: seek2,
|
|||
|
until: until
|
|||
|
}
|
|||
|
this.play(time, true, seek1, until)
|
|||
|
this.addLoop()
|
|||
|
this.loop.interval = setInterval(() => {
|
|||
|
this.addLoop()
|
|||
|
}, 100)
|
|||
|
}
|
|||
|
addLoop(){
|
|||
|
if(this.getTime() > this.loop.started - 1){
|
|||
|
this.play(this.loop.started, true, this.loop.seek, this.loop.until)
|
|||
|
this.loop.started += this.loop.until - this.loop.seek
|
|||
|
}
|
|||
|
}
|
|||
|
play(time, absolute, seek, until){
|
|||
|
time = this.convertTime(time, absolute)
|
|||
|
var source = this.soundBuffer.createSource(this)
|
|||
|
seek = seek || 0
|
|||
|
until = until || this.duration
|
|||
|
this.setTimeouts(time).then(() => {
|
|||
|
this.cfg = {
|
|||
|
started: time,
|
|||
|
seek: seek,
|
|||
|
until: until
|
|||
|
}
|
|||
|
})
|
|||
|
source.start(time, Math.max(0, seek || 0), Math.max(0, until - seek))
|
|||
|
source.startTime = time
|
|||
|
this.sources.add(source)
|
|||
|
source.onended = () => {
|
|||
|
this.sources.delete(source)
|
|||
|
}
|
|||
|
}
|
|||
|
stop(time, absolute){
|
|||
|
time = this.convertTime(time, absolute)
|
|||
|
this.sources.forEach(source => {
|
|||
|
source.stop(Math.max(source.startTime, time))
|
|||
|
})
|
|||
|
this.setTimeouts(time).then(() => {
|
|||
|
if(this.loop){
|
|||
|
clearInterval(this.loop.interval)
|
|||
|
}
|
|||
|
this.clearTimeouts()
|
|||
|
})
|
|||
|
}
|
|||
|
pause(time, absolute){
|
|||
|
if(this.cfg){
|
|||
|
time = this.convertTime(time, absolute)
|
|||
|
this.stop(time, true)
|
|||
|
this.cfg.pauseSeek = time - this.cfg.started + this.cfg.seek
|
|||
|
}
|
|||
|
}
|
|||
|
resume(time, absolute){
|
|||
|
if(this.cfg){
|
|||
|
if(this.loop){
|
|||
|
this.playLoop(time, absolute, this.cfg.pauseSeek, this.loop.seek, this.loop.until)
|
|||
|
}else{
|
|||
|
this.play(time, absolute, this.cfg.pauseSeek, this.cfg.until)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|