chitchatter/src/components/Room/useRoomAudio.ts
2022-11-01 09:10:53 -05:00

149 lines
3.8 KiB
TypeScript

import { useContext, useEffect, useState } from 'react'
import { ShellContext } from 'contexts/ShellContext'
import { PeerActions } from 'models/network'
import { AudioState, Peer } from 'models/chat'
import { PeerRoom } from 'services/PeerRoom'
import { usePeerRoomAction } from './usePeerRoomAction'
interface UseRoomAudioConfig {
peerRoom: PeerRoom
}
export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
const shellContext = useContext(ShellContext)
const [isSpeakingToRoom, setIsSpeakingToRoom] = useState(false)
const [peerAudios, setPeerAudios] = useState<
Record<string, HTMLAudioElement>
>({})
const [audioStream, setAudioStream] = useState<MediaStream | null>()
const [audioDevices, setAudioDevices] = useState<MediaDeviceInfo[]>([])
const [selectedAudioDeviceId, setSelectedAudioDeviceId] = useState<
string | null
>(null)
useEffect(() => {
;(async () => {
if (!audioStream) return
const devices = await window.navigator.mediaDevices.enumerateDevices()
const audioDevices = devices.filter(({ kind }) => kind === 'audioinput')
setAudioDevices(audioDevices)
})()
}, [audioStream])
const [sendAudioChange, receiveAudioChange] = usePeerRoomAction<AudioState>(
peerRoom,
PeerActions.AUDIO_CHANGE
)
receiveAudioChange((audioState, peerId) => {
const newPeerList = shellContext.peerList.map(peer => {
const newPeer: Peer = { ...peer }
if (peer.peerId === peerId) {
newPeer.audioState = audioState
}
return newPeer
})
shellContext.setPeerList(newPeerList)
})
peerRoom.onPeerStream((stream, peerId) => {
const audio = new Audio()
audio.srcObject = stream
audio.autoplay = true
setPeerAudios({ ...peerAudios, [peerId]: audio })
})
useEffect(() => {
;(async () => {
if (isSpeakingToRoom) {
if (!audioStream) {
const newSelfStream = await navigator.mediaDevices.getUserMedia({
audio: selectedAudioDeviceId
? { deviceId: selectedAudioDeviceId }
: true,
video: false,
})
peerRoom.addStream(newSelfStream)
sendAudioChange(AudioState.PLAYING)
shellContext.setAudioState(AudioState.PLAYING)
setAudioStream(newSelfStream)
}
} else {
if (audioStream) {
for (const audioTrack of audioStream.getTracks()) {
audioTrack.stop()
audioStream.removeTrack(audioTrack)
}
peerRoom.removeStream(audioStream, peerRoom.getPeers())
sendAudioChange(AudioState.STOPPED)
shellContext.setAudioState(AudioState.STOPPED)
setAudioStream(null)
}
}
})()
}, [
isSpeakingToRoom,
peerAudios,
peerRoom,
audioStream,
selectedAudioDeviceId,
sendAudioChange,
shellContext,
])
const handleAudioDeviceSelect = async (audioDevice: MediaDeviceInfo) => {
const { deviceId } = audioDevice
setSelectedAudioDeviceId(deviceId)
if (!audioStream) return
for (const audioTrack of audioStream.getTracks()) {
audioTrack.stop()
audioStream.removeTrack(audioTrack)
}
peerRoom.removeStream(audioStream, peerRoom.getPeers())
const newSelfStream = await navigator.mediaDevices.getUserMedia({
audio: {
deviceId,
},
video: false,
})
peerRoom.addStream(newSelfStream)
setAudioStream(newSelfStream)
}
const handleAudioForNewPeer = (peerId: string) => {
if (audioStream) {
peerRoom.addStream(audioStream, peerId)
}
}
const handleAudioForLeavingPeer = (peerId: string) => {
if (audioStream) {
peerRoom.removeStream(audioStream, peerId)
}
}
return {
audioDevices,
isSpeakingToRoom,
setIsSpeakingToRoom,
handleAudioDeviceSelect,
handleAudioForNewPeer,
handleAudioForLeavingPeer,
}
}