// 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; } }