修复语音通话前后端播放标识
This commit is contained in:
+2
-2
@@ -6,7 +6,7 @@ ENV = 'development'
|
||||
|
||||
# VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
|
||||
|
||||
#VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
|
||||
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
|
||||
|
||||
|
||||
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
|
||||
@@ -17,7 +17,7 @@ ENV = 'development'
|
||||
#VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
|
||||
# sit
|
||||
|
||||
VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
|
||||
# VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
|
||||
|
||||
|
||||
# UAT
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
if (options.gain > 20.0) {
|
||||
options.gain = 20.0;
|
||||
}
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const os = plus.os.name;
|
||||
if (os === 'Android') {
|
||||
console.log('是安卓');
|
||||
@@ -136,7 +136,12 @@
|
||||
audioContext = new AudioContext();
|
||||
await audioContext.audioWorklet.addModule('static/js/processor.worklet.js');
|
||||
}
|
||||
|
||||
//#endif
|
||||
//#ifdef H5
|
||||
audioContext = new AudioContext();
|
||||
await audioContext.audioWorklet.addModule('static/js/processor.worklet.js');
|
||||
//#endif
|
||||
|
||||
mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
sampleRate: options.sampleRate,
|
||||
|
||||
@@ -12,31 +12,72 @@
|
||||
></view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onUnmounted, watch, onMounted } from 'vue';
|
||||
const isStop = ref(false);
|
||||
const currentPCMChunk = ref(false);
|
||||
const audioOutput = ref('earpiece');
|
||||
// 响应式状态
|
||||
const isPlaying = ref(false); // 是否正在播放
|
||||
const volume = ref(1); // 播放音量(0~1)
|
||||
const playPCM = (pcmData) => {
|
||||
if (!pcmData || pcmData.byteLength === 0) return;
|
||||
currentPCMChunk.value = pcmData;
|
||||
};
|
||||
const close = () => {
|
||||
isStop.value = false;
|
||||
};
|
||||
const changeStreamPlaying = () => {
|
||||
console.log('播放结束');
|
||||
};
|
||||
const switchOutput = (type) => {
|
||||
if (['speaker', 'earpiece'].includes(type)) {
|
||||
audioOutput.value = type;
|
||||
<script>
|
||||
export default {
|
||||
// 对应组合式API的响应式数据
|
||||
data() {
|
||||
return {
|
||||
isStop: false,
|
||||
currentPCMChunk: false,
|
||||
playNumber: '',
|
||||
audioOutput: 'earpiece',
|
||||
isPlaying: false, // 是否正在播放
|
||||
volume: 1 // 播放音量(0~1)
|
||||
};
|
||||
},
|
||||
|
||||
// 生命周期钩子对应
|
||||
mounted() {
|
||||
// 原onMounted逻辑(此处无代码,保留空钩子)
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
// 原onUnmounted逻辑(此处无代码,保留空钩子)
|
||||
},
|
||||
|
||||
// 对应组合式API的方法
|
||||
methods: {
|
||||
// 播放PCM数据
|
||||
playPCM(pcmData) {
|
||||
if (!pcmData || pcmData.byteLength === 0) return;
|
||||
this.currentPCMChunk = pcmData;
|
||||
},
|
||||
|
||||
// 设置播放id
|
||||
setPlayNumber(id) {
|
||||
console.log('组件内设置播放', id);
|
||||
this.playNumber = id;
|
||||
},
|
||||
|
||||
// 关闭/重置停止状态
|
||||
close() {
|
||||
this.isStop = false;
|
||||
},
|
||||
|
||||
// 播放状态变更
|
||||
changeStreamPlaying({ status }) {
|
||||
if (status === false) {
|
||||
console.log('播放结束', this.playNumber);
|
||||
this.$emit('playOver', this.playNumber)
|
||||
}
|
||||
},
|
||||
|
||||
// 切换音频输出方式
|
||||
switchOutput(type) {
|
||||
if (['speaker', 'earpiece'].includes(type)) {
|
||||
this.audioOutput = type;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 若需要watch监听(原组合式API的watch),补充如下(当前代码无watch逻辑,可删)
|
||||
watch: {
|
||||
// 示例:监听playNumber变化(如有需要)
|
||||
// playNumber(newVal) {
|
||||
// console.log('播放id变更:', newVal);
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ playPCM, close, switchOutput });
|
||||
</script>
|
||||
|
||||
<script module="renderJS" lang="renderjs">
|
||||
@@ -67,7 +108,7 @@
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
|
||||
// 2. 安卓 H5 特殊处理:通过 Audio 对象的 speakerphone 属性(部分浏览器支持)
|
||||
const audio = new Audio();
|
||||
if (audio.setSpeakerphoneOn) {
|
||||
@@ -76,7 +117,7 @@
|
||||
audio.play(); // 触发设置生效
|
||||
audio.pause(); // 无需播放实际音频
|
||||
}
|
||||
|
||||
|
||||
// 3. 小程序端(如微信小程序):调用小程序原生 API
|
||||
if (wx && wx.setAudioRoute) {
|
||||
// 微信小程序专属:切换音频输出(需基础库 2.10.0+)
|
||||
@@ -86,11 +127,11 @@
|
||||
fail: (e) => console.error('小程序路由切换失败:', e)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 4. 兜底提示(非 5+ 环境)
|
||||
console.log(`当前环境不支持强制音频路由,已模拟${type === 'earpiece' ? '听筒' : '扬声器'}效果`);
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
mounted() {
|
||||
const _this = this;
|
||||
@@ -135,8 +176,9 @@
|
||||
},
|
||||
// 播放结束回调(通知主线程)
|
||||
callback(e) {
|
||||
console.log('播放结束回调', e);
|
||||
if (e === 'ended') {
|
||||
this.$ownerInstance.callMethod('changeStreamPlaying', { type: false });
|
||||
this.$ownerInstance.callMethod('changeStreamPlaying', {status:false});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,13 @@ const _baseUrl = get_base_url(url).split('http').join('ws')
|
||||
export default function useWebSocket(aaas = null, options = {}) {
|
||||
const isRecording = ref(false) //是否接通
|
||||
const ws = ref(null)
|
||||
const initConnection = (authParams = {}, onStartRecord, sssssss) => {
|
||||
const initConnection = ({
|
||||
authParams = {},
|
||||
onStartRecord,
|
||||
playPcmCallback,
|
||||
setPlayNumber = () => {}, //设置当前播放的音频的id,用于播放完成回调
|
||||
onError = () => {}
|
||||
}) => {
|
||||
const wsUrl = _baseUrl + url + '?summary=' + getToken()
|
||||
const finalAuthParams = {
|
||||
...DEFAULT_AUTH_PARAMS,
|
||||
@@ -57,6 +63,7 @@ export default function useWebSocket(aaas = null, options = {}) {
|
||||
body
|
||||
} = ProtocolCodec.unpack(res.data);
|
||||
switch (msgType) {
|
||||
|
||||
case MessageType.IDENTITY:
|
||||
console.log('身份校验', body);
|
||||
onStartRecord(body)
|
||||
@@ -66,16 +73,34 @@ export default function useWebSocket(aaas = null, options = {}) {
|
||||
if (buffer.byteLength < 2) break;
|
||||
const uint8 = new Uint8Array(buffer);
|
||||
const numArray = Array.from(uint8);
|
||||
// currentPCMChunk.value = numArray;
|
||||
sssssss(numArray)
|
||||
// console.log('音频消息', body);
|
||||
playPcmCallback(numArray)
|
||||
break;
|
||||
case MessageType.ERROR:
|
||||
|
||||
switch (body.errCode) {
|
||||
case 1:
|
||||
// ASR或TTS初始化出错
|
||||
case 4:
|
||||
// 通话接通失败,请稍后再试!
|
||||
break;
|
||||
default:
|
||||
// 其他错误码的默认处理(如果需要)
|
||||
break;
|
||||
}
|
||||
console.log('错误', body);
|
||||
// this.handleErrorMessage(body);
|
||||
break;
|
||||
case MessageType.CONTROL_CMD:
|
||||
// 控制消息
|
||||
console.log('控制消息', msgType, body);
|
||||
if (body.type === 1) { // 开始播放,记录当前播放的语音id
|
||||
setPlayNumber(body.lock)
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
console.log('其他消息', body);
|
||||
console.log('其他消息', msgType, body);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
+50
-34
@@ -4,8 +4,8 @@
|
||||
<view class="show-box">
|
||||
<nav-bar :is_seat="true"></nav-bar>
|
||||
<view class="switch-div">
|
||||
<view class="switch-text">场景提示</view>
|
||||
<switch class="switch" :checked="!sceneDescShow" @click="switchChange" />
|
||||
<!-- <view class="switch-text">场景提示</view> -->
|
||||
<!-- <switch class="switch" :checked="!sceneDescShow" @click="switchChange" /> -->
|
||||
</view>
|
||||
<view class="fl1-div-1"></view>
|
||||
<view class="role-info">
|
||||
@@ -21,11 +21,11 @@
|
||||
<view class="close-btn" @click="closeTalking"></view>
|
||||
</view>
|
||||
<talk-microphone ref="talkMicrophoneRef"></talk-microphone>
|
||||
<talk-speaker ref="talkSpeakerRef"></talk-speaker>
|
||||
<talk-speaker ref="talkSpeakerRef" @playOver="playOver"></talk-speaker>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { getUserInfo } from '@/common/common';
|
||||
import { getUserInfo,getToken } from '@/common/common';
|
||||
import { queryTraPartnerCharacterInfoById, partnerChatReportTrigger } from '@/api/sparring.js';
|
||||
import common from '@/common/common';
|
||||
import talkMicrophone from './components/talk-microphone.vue';
|
||||
@@ -47,7 +47,6 @@
|
||||
ControlCommand,
|
||||
ProtocolConst
|
||||
} from './js/ProtocolCodec';
|
||||
import { getToken } from '@/common/common';
|
||||
const dataInfo = reactive({
|
||||
traId: '',
|
||||
chaId: '',
|
||||
@@ -63,32 +62,34 @@
|
||||
|
||||
// 关闭通话
|
||||
const closeTalking = async () => {
|
||||
talkMicrophoneRef.value.stop();
|
||||
talkSpeakerRef.value.close();
|
||||
close();
|
||||
common.navigateBack(1, () => {
|
||||
partnerChatReportTrigger({
|
||||
execId: dataInfo.execId,
|
||||
traId: dataInfo.traId,
|
||||
talkingType: dataInfo.isPreview ? 'preview' : 'practice'
|
||||
}).then((res) => {
|
||||
common.redirectTo(
|
||||
'/pages/sparring/result?isOver=1&traId=' +
|
||||
dataInfo.traId +
|
||||
'&execId=' +
|
||||
dataInfo.execId +
|
||||
'&type=' +
|
||||
dataInfo.traType +
|
||||
'&isPreview=' +
|
||||
(dataInfo.isPreview ? '1' : '')
|
||||
);
|
||||
});
|
||||
common.show('', '挂断电话将结束训练,是否确认?').then((res) => {
|
||||
if (res) {
|
||||
talkMicrophoneRef.value.stop();
|
||||
talkSpeakerRef.value.close();
|
||||
close();
|
||||
common.navigateBack(1, () => {
|
||||
partnerChatReportTrigger({
|
||||
execId: dataInfo.execId,
|
||||
traId: dataInfo.traId,
|
||||
talkingType: dataInfo.isPreview ? 'preview' : 'practice'
|
||||
}).then((res) => {
|
||||
common.redirectTo(
|
||||
'/pages/sparring/result?isOver=1&traId=' +
|
||||
dataInfo.traId +
|
||||
'&execId=' +
|
||||
dataInfo.execId +
|
||||
'&type=' +
|
||||
dataInfo.traType +
|
||||
'&isPreview=' +
|
||||
(dataInfo.isPreview ? '1' : '')
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
function frameRecorded(res) {
|
||||
// console.log('res', res);
|
||||
send(res);
|
||||
}
|
||||
|
||||
|
||||
const onCurrentDecibels = (res) => {
|
||||
console.log('res', res);
|
||||
@@ -101,8 +102,8 @@
|
||||
|
||||
// 2. 格式化函数:总秒数 → mm:ss(分钟无上限,不进位小时)
|
||||
const formatTime = (totalSeconds) => {
|
||||
if (totalSeconds <= 0) return '0:00'; // 过期显示0:00
|
||||
const minutes = Math.floor(totalSeconds / 60); // 分钟无上限
|
||||
if (totalSeconds <= 0) return '00:00'; // 过期显示0:00
|
||||
const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0'); // 分钟无上限
|
||||
const seconds = totalSeconds % 60; // 秒数
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`; // 秒补零
|
||||
};
|
||||
@@ -115,7 +116,7 @@
|
||||
const diff = now - targetTimestamp.value;
|
||||
formattedTime.value = formatTime(Math.max(0, Math.floor(diff / 1000)));
|
||||
};
|
||||
|
||||
|
||||
const onStartRecord = (body) => {
|
||||
dataInfo.execId = body.execId;
|
||||
targetTimestamp.value = Date.now();
|
||||
@@ -125,7 +126,6 @@
|
||||
timer = setInterval(updateRemaining, 1000);
|
||||
// 立即执行一次
|
||||
updateRemaining();
|
||||
|
||||
talkMicrophoneRef.value.start({
|
||||
sampleRate: 16000,
|
||||
frameSize: 1024,
|
||||
@@ -134,6 +134,16 @@
|
||||
onFrameRecorded: ({ isLastFrame, frameBuffer }) => send(ProtocolCodec.pack(MessageType.AUDIO_DATA, frameBuffer))
|
||||
});
|
||||
};
|
||||
const playOver = (id) => {
|
||||
send(ProtocolCodec.pack(MessageType.CONTROL_CMD, {
|
||||
type:2,
|
||||
lock:id
|
||||
}))
|
||||
};
|
||||
const onError = (body) => {
|
||||
// websocket错误处理
|
||||
|
||||
};
|
||||
onLoad((e) => {
|
||||
dataInfo.traId = e.traId ?? '';
|
||||
dataInfo.chaId = e.chaId ?? '';
|
||||
@@ -147,7 +157,13 @@
|
||||
|
||||
});
|
||||
onMounted(() => {
|
||||
initConnection(dataInfo, onStartRecord, (res) => talkSpeakerRef.value.playPCM(res));
|
||||
initConnection({
|
||||
authParams:Object.assign({}, dataInfo),
|
||||
onStartRecord:onStartRecord,
|
||||
playPcmCallback:(res) => talkSpeakerRef.value.playPCM(res),
|
||||
setPlayNumber:(id) => talkSpeakerRef.value.setPlayNumber(id),
|
||||
onError:onError
|
||||
});
|
||||
});
|
||||
onUnmounted(() => {
|
||||
console.log('组件卸载');
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user