From 4116ef03e6aab6c2af99a34f87c4b4c19f8a08c9 Mon Sep 17 00:00:00 2001
From: wuxichen <17301714657@163.com>
Date: Wed, 15 Oct 2025 12:02:18 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=89=80=E6=9C=89=E6=A0=BC=E5=BC=8F?=
=?UTF-8?q?=E8=BD=AC=E6=8D=A2=E6=88=90wav?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../translate-h5/src/http/axios-instance.ts | 7 +-
.../translate/component/message/index.tsx | 3 +-
.../home/translate/component/voice/index.tsx | 96 +++++++++++++++----
projects/translate-h5/vite.config.ts | 4 +-
4 files changed, 87 insertions(+), 23 deletions(-)
diff --git a/projects/translate-h5/src/http/axios-instance.ts b/projects/translate-h5/src/http/axios-instance.ts
index 5751940..0935e07 100644
--- a/projects/translate-h5/src/http/axios-instance.ts
+++ b/projects/translate-h5/src/http/axios-instance.ts
@@ -46,6 +46,9 @@ class AxiosInstance {
return this.instance;
}
}
-
-const baseURL = import.meta.env.VITE_BASE_URL || "https://petshy.tashowz.com";
+console.log(import.meta.env.VITE_BASE_URL, "import.meta.env.VITE_BASE_URL");
+// https://petshy.tashowz.com
+//192.168.1.231:48080
+// || "http://192.168.1.231:48080"
+const baseURL = import.meta.env.VITE_BASE_URL;
export const axiosInstance = new AxiosInstance(baseURL).getInstance();
diff --git a/projects/translate-h5/src/view/home/translate/component/message/index.tsx b/projects/translate-h5/src/view/home/translate/component/message/index.tsx
index 0c04af3..4c673ae 100644
--- a/projects/translate-h5/src/view/home/translate/component/message/index.tsx
+++ b/projects/translate-h5/src/view/home/translate/component/message/index.tsx
@@ -74,7 +74,7 @@ function Index(props: DefinedProps) {
break;
default:
console.warn("未知错误");
- };
+ }
Toast.show("音频播放失败");
setIsPlating(false);
};
@@ -127,6 +127,7 @@ function Index(props: DefinedProps) {
{data.map((item, index) => (
playAudio(item.id, item.contentText)}>
{renderAvatar(item.petType)}
+
{item.petName}
diff --git a/projects/translate-h5/src/view/home/translate/component/voice/index.tsx b/projects/translate-h5/src/view/home/translate/component/voice/index.tsx
index 0d876c9..908b426 100644
--- a/projects/translate-h5/src/view/home/translate/component/voice/index.tsx
+++ b/projects/translate-h5/src/view/home/translate/component/voice/index.tsx
@@ -4,9 +4,8 @@ import { Button, Dialog, Image, ProgressCircle, Toast } from "antd-mobile";
import microphoneSvg from "@/assets/translate/microphone.svg";
import microphoneDisabledSvg from "@/assets/translate/microphoneDisabledSvg.svg";
import { createStartRecordSound, createSendSound } from "@/utils/voice";
-import "./index.less";
import VConsole from "vconsole";
-
+import "./index.less";
interface DefinedProps {
onRecordingComplete: (url: string, finalDuration: number, formData: FormData) => void;
isRecording: boolean;
@@ -195,6 +194,74 @@ function Index(props: DefinedProps) {
}
};
+ // 添加音频转换函数
+ const convertToWav = async (blob: Blob): Promise
=> {
+ const arrayBuffer = await blob.arrayBuffer();
+ const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
+ const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
+
+ // 转换为 WAV 格式
+ const wavBuffer = audioBufferToWav(audioBuffer);
+ return new Blob([wavBuffer], { type: "audio/wav" });
+ };
+
+ // WAV 转换辅助函数
+ const audioBufferToWav = (buffer: AudioBuffer): ArrayBuffer => {
+ const length = buffer.length;
+ const numberOfChannels = buffer.numberOfChannels;
+ const sampleRate = buffer.sampleRate;
+ const bitsPerSample = 16;
+ const byteRate = (sampleRate * numberOfChannels * bitsPerSample) / 8;
+ const blockAlign = (numberOfChannels * bitsPerSample) / 8;
+ const dataSize = length * numberOfChannels * (bitsPerSample / 8);
+ const bufferLength = 44 + dataSize;
+ const arrayBuffer = new ArrayBuffer(bufferLength);
+ const view = new DataView(arrayBuffer);
+ // RIFF identifier
+ writeString(view, 0, "RIFF");
+ // file length
+ view.setUint32(4, 36 + dataSize, true);
+ // RIFF type
+ writeString(view, 8, "WAVE");
+ // format chunk identifier
+ writeString(view, 12, "fmt ");
+ // format chunk length
+ view.setUint32(16, 16, true);
+ // sample format (raw)
+ view.setUint16(20, 1, true);
+ // channel count
+ view.setUint16(22, numberOfChannels, true);
+ // sample rate
+ view.setUint32(24, sampleRate, true);
+ // byte rate (sample rate * block align)
+ view.setUint32(28, byteRate, true);
+ // block align (channel count * bytes per sample)
+ view.setUint16(32, blockAlign, true);
+ // bits per sample
+ view.setUint16(34, bitsPerSample, true);
+ // data chunk identifier
+ writeString(view, 36, "data");
+ // data chunk length
+ view.setUint32(40, dataSize, true);
+
+ // write the PCM samples
+ let offset = 44;
+ for (let i = 0; i < length; i++) {
+ for (let channel = 0; channel < numberOfChannels; channel++) {
+ const sample = Math.max(-1, Math.min(1, buffer.getChannelData(channel)[i]));
+ view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7fff, true);
+ offset += 2;
+ }
+ }
+ return arrayBuffer;
+ };
+
+ const writeString = (view: DataView, offset: number, string: string) => {
+ for (let i = 0; i < string.length; i++) {
+ view.setUint8(offset + i, string.charCodeAt(i));
+ }
+ };
+
//开始录音
const onStartRecording = () => {
isCancelledRef.current = false;
@@ -214,6 +281,7 @@ function Index(props: DefinedProps) {
recorderControls.stopRecording();
onResetRecordingState();
}, [recorderControls, recordingDuration]);
+
//录音完成
// 在发送时检查录音时长
const onRecordingComplete = useCallback(
@@ -222,30 +290,17 @@ function Index(props: DefinedProps) {
Toast.show("已取消");
return;
}
+
// 检查blob有效性
if (!blob || blob.size === 0) {
Toast.show("录音数据无效,请重新录音");
-
return;
}
+ // 转换为 WAV 格式以获得最佳兼容性
+ const wavBlob = await convertToWav(blob);
const formData: FormData = new FormData();
- formData.append(
- "file",
- blob,
- new Date().getTime() + "." + blob.type.split("/").pop()?.split(";")[0]
- );
-
- // 把demoWAV替换成blob
-
- // const response = await fetch(demoWAV);
- // const arrayBuffer = await response.arrayBuffer();
- // const demoWAVBlob = new Blob([arrayBuffer], { type: "audio/wav" });
-
- // formData.append("file", demoWAVBlob);
+ formData.append("file", wavBlob, new Date().getTime() + "." + new Date().getTime() + ".wav");
formData.append("dialogId", `${dialogId}`);
-
- // await handleUploadAudio(formData);
- // const audioUrl = URL.createObjectURL(demoWAVBlob);
const audioUrl = URL.createObjectURL(blob);
const audio = new Audio();
audio.src = audioUrl;
@@ -283,6 +338,9 @@ function Index(props: DefinedProps) {
autoGainControl: true,
}}
showVisualizer={false}
+ mediaRecorderOptions={{
+ mimeType: "audio/webm",
+ }}
/>
{renderBtn()}
>
diff --git a/projects/translate-h5/vite.config.ts b/projects/translate-h5/vite.config.ts
index 0587ee7..cfdbfd7 100644
--- a/projects/translate-h5/vite.config.ts
+++ b/projects/translate-h5/vite.config.ts
@@ -5,6 +5,8 @@ import { inspectorServer } from "@react-dev-inspector/vite-plugin";
import basicSsl from "@vitejs/plugin-basic-ssl";
export default defineConfig({
// basicSsl()
+ // target: "https://petshy.tashowz.com",
+ // http://192.168.1.231:48080
plugins: [react()],
server: {
port: 3000,
@@ -12,7 +14,7 @@ export default defineConfig({
open: true,
proxy: {
"/app-api": {
- target: "https://petshy.tashowz.com",
+ target: "http://192.168.1.231:48080",
changeOrigin: true,
},
},