Merge branch 'qianpw' of http://gitea.tashowz.com/tashow/tashow-h5 into qianpw
This commit is contained in:
@@ -46,6 +46,9 @@ class AxiosInstance {
|
|||||||
return this.instance;
|
return this.instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log(import.meta.env.VITE_BASE_URL, "import.meta.env.VITE_BASE_URL");
|
||||||
const baseURL = import.meta.env.VITE_BASE_URL || "https://petshy.tashowz.com";
|
// 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();
|
export const axiosInstance = new AxiosInstance(baseURL).getInstance();
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ function Index(props: DefinedProps) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn("未知错误");
|
console.warn("未知错误");
|
||||||
};
|
}
|
||||||
Toast.show("音频播放失败");
|
Toast.show("音频播放失败");
|
||||||
setIsPlating(false);
|
setIsPlating(false);
|
||||||
};
|
};
|
||||||
@@ -127,6 +127,7 @@ function Index(props: DefinedProps) {
|
|||||||
{data.map((item, index) => (
|
{data.map((item, index) => (
|
||||||
<div className="item" key={index} onClick={() => playAudio(item.id, item.contentText)}>
|
<div className="item" key={index} onClick={() => playAudio(item.id, item.contentText)}>
|
||||||
{renderAvatar(item.petType)}
|
{renderAvatar(item.petType)}
|
||||||
|
|
||||||
<div className="rig">
|
<div className="rig">
|
||||||
<div>
|
<div>
|
||||||
<span className="name">{item.petName}</span>
|
<span className="name">{item.petName}</span>
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ import { Button, Dialog, Image, ProgressCircle, Toast } from "antd-mobile";
|
|||||||
import microphoneSvg from "@/assets/translate/microphone.svg";
|
import microphoneSvg from "@/assets/translate/microphone.svg";
|
||||||
import microphoneDisabledSvg from "@/assets/translate/microphoneDisabledSvg.svg";
|
import microphoneDisabledSvg from "@/assets/translate/microphoneDisabledSvg.svg";
|
||||||
import { createStartRecordSound, createSendSound } from "@/utils/voice";
|
import { createStartRecordSound, createSendSound } from "@/utils/voice";
|
||||||
import "./index.less";
|
|
||||||
import VConsole from "vconsole";
|
import VConsole from "vconsole";
|
||||||
|
import "./index.less";
|
||||||
interface DefinedProps {
|
interface DefinedProps {
|
||||||
onRecordingComplete: (url: string, finalDuration: number, formData: FormData) => void;
|
onRecordingComplete: (url: string, finalDuration: number, formData: FormData) => void;
|
||||||
isRecording: boolean;
|
isRecording: boolean;
|
||||||
@@ -195,6 +194,74 @@ function Index(props: DefinedProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加音频转换函数
|
||||||
|
const convertToWav = async (blob: Blob): Promise<Blob> => {
|
||||||
|
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 = () => {
|
const onStartRecording = () => {
|
||||||
isCancelledRef.current = false;
|
isCancelledRef.current = false;
|
||||||
@@ -214,6 +281,7 @@ function Index(props: DefinedProps) {
|
|||||||
recorderControls.stopRecording();
|
recorderControls.stopRecording();
|
||||||
onResetRecordingState();
|
onResetRecordingState();
|
||||||
}, [recorderControls, recordingDuration]);
|
}, [recorderControls, recordingDuration]);
|
||||||
|
|
||||||
//录音完成
|
//录音完成
|
||||||
// 在发送时检查录音时长
|
// 在发送时检查录音时长
|
||||||
const onRecordingComplete = useCallback(
|
const onRecordingComplete = useCallback(
|
||||||
@@ -222,30 +290,17 @@ function Index(props: DefinedProps) {
|
|||||||
Toast.show("已取消");
|
Toast.show("已取消");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查blob有效性
|
// 检查blob有效性
|
||||||
if (!blob || blob.size === 0) {
|
if (!blob || blob.size === 0) {
|
||||||
Toast.show("录音数据无效,请重新录音");
|
Toast.show("录音数据无效,请重新录音");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 转换为 WAV 格式以获得最佳兼容性
|
||||||
|
const wavBlob = await convertToWav(blob);
|
||||||
const formData: FormData = new FormData();
|
const formData: FormData = new FormData();
|
||||||
formData.append(
|
formData.append("file", wavBlob, new Date().getTime() + "." + new Date().getTime() + ".wav");
|
||||||
"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("dialogId", `${dialogId}`);
|
formData.append("dialogId", `${dialogId}`);
|
||||||
|
|
||||||
// await handleUploadAudio(formData);
|
|
||||||
// const audioUrl = URL.createObjectURL(demoWAVBlob);
|
|
||||||
const audioUrl = URL.createObjectURL(blob);
|
const audioUrl = URL.createObjectURL(blob);
|
||||||
const audio = new Audio();
|
const audio = new Audio();
|
||||||
audio.src = audioUrl;
|
audio.src = audioUrl;
|
||||||
@@ -283,6 +338,9 @@ function Index(props: DefinedProps) {
|
|||||||
autoGainControl: true,
|
autoGainControl: true,
|
||||||
}}
|
}}
|
||||||
showVisualizer={false}
|
showVisualizer={false}
|
||||||
|
mediaRecorderOptions={{
|
||||||
|
mimeType: "audio/webm",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div className={` voice-record`}>{renderBtn()}</div>
|
<div className={` voice-record`}>{renderBtn()}</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { inspectorServer } from "@react-dev-inspector/vite-plugin";
|
|||||||
import basicSsl from "@vitejs/plugin-basic-ssl";
|
import basicSsl from "@vitejs/plugin-basic-ssl";
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
// basicSsl()
|
// basicSsl()
|
||||||
|
// target: "https://petshy.tashowz.com",
|
||||||
|
// http://192.168.1.231:48080
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
@@ -12,7 +14,7 @@ export default defineConfig({
|
|||||||
open: true,
|
open: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
"/app-api": {
|
"/app-api": {
|
||||||
target: "https://petshy.tashowz.com",
|
target: "http://192.168.1.231:48080",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user