DTP Base provides a scalable and secure Node.js application development harness ready for production service.
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.
 
 
 
 

149 lines
3.5 KiB

// base-audio.js
// Copyright (C) 2024 DTP Technologies, LLC
// All Rights Reserved
'use strict';
import DtpLog from 'lib/dtp-log';
const AudioContext = window.AudioContext || window.webkitAudioContext;
export default class BaseAudio {
constructor ( ) {
this.log = new DtpLog('BaseAudio');
}
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;
}
}