Skip to content

Commit 86bd0f9

Browse files
authored
Merge pull request #4505 from traPtitech/fix/audio_device_id
オーディオデバイスの設定を認識しない問題を修正
2 parents 5d689c0 + 7dfe2bc commit 86bd0f9

File tree

5 files changed

+45
-25
lines changed

5 files changed

+45
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
<script setup lang="ts">
22
import { useQall } from '/@/composables/qall/useQall'
3-
import AudioComponent from './AudioComponent.vue'
3+
import VoiceComponent from './VoiceComponent.vue'
44
55
const { tracksMap, screenShareTrackSidMap, screenShareTracks } = useQall()
66
</script>
77
<template>
88
<template v-for="[sid, track] in Array.from(tracksMap.entries())" :key="sid">
9-
<AudioComponent
9+
<VoiceComponent
1010
v-if="
1111
track.trackPublication?.kind === 'audio' &&
1212
track.isRemote &&
1313
!screenShareTracks?.some?.(([_, valueSid]) => valueSid === sid)
1414
"
1515
:track-info="track"
16-
:is-show="false"
1716
/>
1817
</template>
1918
</template>

src/components/Main/MainView/QallView/UserCard.vue

+18-1
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,26 @@ import { computed } from 'vue'
33
import type { TrackInfo } from '/@/composables/qall/useLiveKitSDK'
44
import { useUsersStore } from '/@/store/entities/users'
55
import { buildUserIconPath } from '/@/lib/apis'
6+
import { useQall } from '/@/composables/qall/useQall'
67
78
const { trackInfo } = defineProps<{
89
trackInfo: TrackInfo
910
}>()
11+
const { speakerIdentitys } = useQall()
1012
const { findUserByName } = useUsersStore()
1113
const user = computed(() => findUserByName(trackInfo.username))
1214
const userIconFileId = computed(() => user.value?.iconFileId ?? '')
1315
const iconImage = computed(() => buildUserIconPath(userIconFileId.value))
16+
const isSpeaking = computed(() => {
17+
return (
18+
user.value &&
19+
speakerIdentitys.value.some(s => s.name === trackInfo.username)
20+
)
21+
})
1422
</script>
1523

1624
<template>
17-
<div v-if="user" :class="$style.UserCard">
25+
<div v-if="user" :class="$style.UserCard" :data-is-speaking="isSpeaking">
1826
<div :class="$style.OuterIcon">
1927
<img :src="iconImage" :class="$style.OuterImage" />
2028
</div>
@@ -23,6 +31,7 @@ const iconImage = computed(() => buildUserIconPath(userIconFileId.value))
2331
</div>
2432

2533
<div :class="$style.NameLabel">{{ trackInfo.username }}</div>
34+
<div v-show="isSpeaking" :class="$style.borderBox"></div>
2635
</div>
2736
</template>
2837

@@ -35,6 +44,14 @@ const iconImage = computed(() => buildUserIconPath(userIconFileId.value))
3544
border-radius: 12px;
3645
pointer-events: none;
3746
user-select: none;
47+
box-sizing: border-box;
48+
}
49+
50+
.borderBox {
51+
border: 2px solid $common-ui-qall;
52+
width: 100%;
53+
height: 100%;
54+
border-radius: 12px;
3855
}
3956
4057
.InnerIcon {

src/components/Main/MainView/QallView/AudioComponent.vue src/components/Main/MainView/QallView/VoiceComponent.vue

+2-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { useUsersStore } from '/@/store/entities/users'
55
import { buildUserIconPath } from '/@/lib/apis'
66
import AudioTrack from './AudioTrack.vue'
77
import { useUserVolume } from '/@/store/app/userVolume'
8-
import UserCard from './UserCard.vue'
98
const { trackInfo, isShow } = defineProps<{
109
trackInfo: TrackInfo
1110
isShow?: boolean
@@ -29,14 +28,6 @@ const parseToFloat = (value: number | string): number => {
2928
</script>
3029

3130
<template>
32-
<div :class="isShow ? $style.container : []">
33-
<UserCard :track-info="trackInfo" />
34-
<AudioTrack :track-info="trackInfo" :volume="parseToFloat(volume)" />
35-
</div>
31+
<AudioTrack :track-info="trackInfo" :volume="parseToFloat(volume)" />
3632
</template>
37-
<style lang="scss" module>
38-
.container {
39-
width: 100%;
40-
height: 100%;
41-
}
42-
</style>
33+
<style lang="scss" module></style>

src/composables/qall/useLiveKitSDK.ts

+21-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
AudioPresets,
55
createLocalScreenTracks,
66
Room,
7-
LocalVideoTrack
7+
LocalVideoTrack,
8+
LocalAudioTrack
89
} from 'livekit-client'
910
import type {
1011
RemoteTrack,
@@ -85,7 +86,7 @@ type CameraProcessor = {
8586
const room = ref<Room>()
8687
const audioContext = ref<AudioContext>()
8788
const isRnnoiseSupported = computed(() => !!audioContext.value)
88-
const speakerIdentity = ref<string[]>([])
89+
const speakerIdentitys = ref<{ identity: string; name?: string }[]>([])
8990
const tracksMap: Ref<Map<string, TrackInfo>> = ref(new Map())
9091
const cameraProcessorMap: Ref<Map<string, CameraProcessor>> = ref(new Map())
9192
const screenShareTrackSidMap = ref<Map<string, string>>(new Map())
@@ -141,7 +142,7 @@ function handleLocalTrackPublished(
141142

142143
function handleActiveSpeakerChange(speakers: Participant[]) {
143144
// show UI indicators when participant is speaking
144-
speakerIdentity.value = speakers.map(s => s.identity)
145+
speakerIdentitys.value = speakers
145146
}
146147

147148
function handleDisconnect() {
@@ -264,8 +265,7 @@ async function leaveRoom() {
264265
const addMicTrack = async () => {
265266
let stream: MediaStream | undefined
266267

267-
const noiseSuppression = useRtcSettings().noiseSuppression
268-
.value as NoiseSuppressionType
268+
const { noiseSuppression, audioInputDeviceId } = useRtcSettings()
269269
try {
270270
if (!room.value?.localParticipant?.permissions?.canPublish) {
271271
throw new Error('権限がありません')
@@ -275,12 +275,21 @@ const addMicTrack = async () => {
275275
audioContext.value = new AudioContext()
276276
}
277277

278-
stream = await navigator.mediaDevices.getUserMedia({ audio: true })
278+
stream = await navigator.mediaDevices.getUserMedia({
279+
audio: {
280+
deviceId: {
281+
ideal: audioInputDeviceId.value
282+
},
283+
autoGainControl: true,
284+
noiseSuppression: true,
285+
echoCancellation: true
286+
}
287+
})
279288
const source = audioContext.value.createMediaStreamSource(stream)
280289

281290
let lastNode: AudioNode = source
282291

283-
if (noiseSuppression === 'rnnoise') {
292+
if (noiseSuppression.value === 'rnnoise') {
284293
const [rnnoiseBinary] = await Promise.all([
285294
loadRnnoiseWasmBinary(),
286295
audioContext.value?.audioWorklet.addModule(rnnoiseWorkletPath)
@@ -291,7 +300,7 @@ const addMicTrack = async () => {
291300
})
292301
source.connect(rnnoiseNode)
293302
lastNode = rnnoiseNode
294-
} else if (noiseSuppression === 'speex') {
303+
} else if (noiseSuppression.value === 'speex') {
295304
const [speexBinary] = await Promise.all([
296305
loadSpeexWasmBinary(),
297306
audioContext.value?.audioWorklet.addModule(speexWorkletPath)
@@ -313,9 +322,11 @@ const addMicTrack = async () => {
313322
}
314323

315324
audioTrackId.value = audioTrack.id
325+
const livekitAudioTrack = new LocalAudioTrack(audioTrack, undefined, false)
326+
livekitAudioTrack.source = Track.Source.Microphone
316327

317328
// Publish the processed stream
318-
await room.value.localParticipant.publishTrack(audioTrack, {
329+
await room.value.localParticipant.publishTrack(livekitAudioTrack, {
319330
audioPreset: AudioPresets.speech,
320331
forceStereo: true,
321332
red: false,
@@ -334,7 +345,6 @@ const addMicTrack = async () => {
334345
addErrorToast('マイクの共有に失敗しました')
335346
}
336347
}
337-
338348
const removeMicTrack = async () => {
339349
try {
340350
if (!room.value) {
@@ -633,6 +643,7 @@ export const useLiveKitSDK = () => {
633643
tracksMap,
634644
screenShareTrackSidMap,
635645
screenShareTracks,
646+
speakerIdentitys,
636647
isMicOn,
637648
qallMitt
638649
}

src/composables/qall/useQall.ts

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const {
5858
tracksMap,
5959
screenShareTrackSidMap,
6060
screenShareTracks,
61+
speakerIdentitys,
6162
isMicOn,
6263
qallMitt
6364
} = useLiveKitSDK()
@@ -321,6 +322,7 @@ export const useQall = () => {
321322
isMicOn,
322323
isCameraOn,
323324
isScreenSharing,
325+
speakerIdentitys,
324326
selectedTrack
325327
}
326328
}

0 commit comments

Comments
 (0)