You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.6 KiB
151 lines
3.6 KiB
// time-tracker-audio.js
|
|
// Copyright (C) 2024 DTP Technologies, LLC
|
|
// All Rights Reserved
|
|
|
|
'use strict';
|
|
|
|
const DTP_COMPONENT_NAME = 'TimeTrackerAudio';
|
|
|
|
import DtpLog from 'lib/dtp-log';
|
|
|
|
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
|
|
export default class TimeTrackerAudio {
|
|
|
|
constructor ( ) {
|
|
this.log = new DtpLog(DTP_COMPONENT_NAME);
|
|
}
|
|
|
|
start ( ) {
|
|
this.log.info('start', 'starting Web Audio API main context');
|
|
this.ctx = new AudioContext();
|
|
|
|
this.masterVolume = this.ctx.createGain();
|
|
this.masterVolume.connect(this.ctx.destination);
|
|
|
|
this.musicGain = this.ctx.createGain();
|
|
this.musicGain.value = 0.7;
|
|
this.musicGain.connect(this.masterVolume);
|
|
|
|
this.sounds = { };
|
|
}
|
|
|
|
async loadSound (soundId, url) {
|
|
if (this.sounds[soundId]) {
|
|
throw new Error('Already have sound registered for soundId');
|
|
}
|
|
|
|
const audioBuffer = await this.loadAudioBuffer(url);
|
|
const sound = { soundId, url, audioBuffer };
|
|
this.sounds[soundId] = sound;
|
|
|
|
return sound;
|
|
}
|
|
|
|
hasSound (soundId) {
|
|
return !!this.sounds[soundId];
|
|
}
|
|
|
|
playSound (soundId, options) {
|
|
const sound = this.sounds[soundId];
|
|
if (!sound) {
|
|
throw new Error(`Invalid soundId: ${soundId}`);
|
|
}
|
|
|
|
this.log.info('playSound', 'playing sound', { soundId });
|
|
return { soundId, ...this.playAudioBuffer(sound.audioBuffer, options) };
|
|
}
|
|
|
|
loadAudioBuffer (url) {
|
|
return new Promise(async (resolve, reject) => {
|
|
const response = await fetch(url);
|
|
const audioData = await response.arrayBuffer();
|
|
this.ctx.decodeAudioData(audioData, resolve, reject);
|
|
});
|
|
}
|
|
|
|
playAudioBuffer (buffer, options) {
|
|
options = Object.assign({
|
|
gain: 0.4,
|
|
loop: false,
|
|
}, options);
|
|
|
|
const source = this.ctx.createBufferSource();
|
|
source.buffer = buffer;
|
|
source.loop = options.loop;
|
|
|
|
const gainNode = this.ctx.createGain();
|
|
gainNode.gain.value = options.gain;
|
|
|
|
source.connect(gainNode);
|
|
gainNode.connect(this.masterVolume);
|
|
|
|
source.start();
|
|
|
|
return { gain: gainNode, source };
|
|
}
|
|
|
|
setMusicStream (url) {
|
|
let source;
|
|
|
|
this.stopMusicStream();
|
|
this.log.debug('setMusicStream', 'setting new music stream', { url });
|
|
this.music = new Audio();
|
|
this.music.setAttribute('loop', 'loop');
|
|
|
|
source = document.createElement('source');
|
|
source.setAttribute('src', `${url}.ogg`);
|
|
source.setAttribute('type', 'audio/ogg');
|
|
this.music.appendChild(source);
|
|
|
|
source = document.createElement('source');
|
|
source.setAttribute('src', `${url}.mp3`);
|
|
source.setAttribute('type', 'audio/mp3');
|
|
this.music.appendChild(source);
|
|
}
|
|
|
|
playMusicStream ( ) {
|
|
if (this.musicSource) {
|
|
return;
|
|
}
|
|
|
|
this.musicSource = this.ctx.createMediaElementSource(this.music);
|
|
this.musicSource.connect(this.musicGain);
|
|
|
|
this.log.debug('playMusicStream', 'starting music stream playback');
|
|
this.music.play();
|
|
}
|
|
|
|
stopMusicStream ( ) {
|
|
if (!this.musicSource) {
|
|
return;
|
|
}
|
|
|
|
this.log.debug('pauseMusicStream', 'stopping music stream playback');
|
|
this.music.pause();
|
|
|
|
this.musicGain.value = 0;
|
|
|
|
this.musicSource.disconnect(this.musicGain);
|
|
delete this.musicSource;
|
|
}
|
|
|
|
get musicVolume ( ) { return this.musicGain.gain.value; }
|
|
set musicVolume (volume) {
|
|
this.musicGain.gain.value = volume;
|
|
}
|
|
|
|
get haveMusicStream ( ) {
|
|
return !!this.music &&
|
|
!!this.musicSource &&
|
|
!!this.musicGain
|
|
;
|
|
}
|
|
|
|
get isMusicPaused ( ) {
|
|
if (!this.music) {
|
|
return true;
|
|
}
|
|
return this.music.paused;
|
|
}
|
|
}
|