Browse Source
- nodemon and webpack now basically work like my Gulp setup used to in DTP Base - Connecting to rooms and sending chat messages - Chat messages are rendered server-side and HTML is emitted to clients - Chat room messages are created with POST requests at /chat/room/:roomId/message - ChatAudio system added - SFX_CHAT_MESSAGE added and played when message is received - more socket management to guarantee pulling out of room when leaving - person-through-window added as "Leave room" icon to disconnectdevelop
15 changed files with 521 additions and 48 deletions
@ -0,0 +1,2 @@ |
|||
include message |
|||
+renderChatMessage(message) |
@ -0,0 +1,151 @@ |
|||
// chat-audio.js
|
|||
// Copyright (C) 2024 DTP Technologies, LLC
|
|||
// All Rights Reserved
|
|||
|
|||
'use strict'; |
|||
|
|||
const DTP_COMPONENT_NAME = 'ChatAudio'; |
|||
|
|||
import DtpLog from 'lib/dtp-log'; |
|||
|
|||
const AudioContext = window.AudioContext || window.webkitAudioContext; |
|||
|
|||
export default class ChatAudio { |
|||
|
|||
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; |
|||
} |
|||
} |
Binary file not shown.
@ -1,4 +1,9 @@ |
|||
{ |
|||
"verbose": true, |
|||
"ignore": ["dist"] |
|||
"ignore": [ |
|||
"dist", |
|||
"client/**/*", |
|||
"lib/client/**/*", |
|||
"node_modules/**/*" |
|||
] |
|||
} |
Loading…
Reference in new issue