Merge branch 'master' of https://gitee.com/lvYeJu/lane-crawford-3
This commit is contained in:
231
src/utils/audioUtils.ts
Normal file
231
src/utils/audioUtils.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* 音频处理工具函数
|
||||
* 为后续集成第三方TTS方案做准备
|
||||
*/
|
||||
|
||||
// 音频文件类型定义
|
||||
export interface AudioFile {
|
||||
blob: Blob
|
||||
name: string
|
||||
size: number
|
||||
type: string
|
||||
duration?: number
|
||||
}
|
||||
|
||||
// 音频格式转换选项
|
||||
export interface AudioConvertOptions {
|
||||
format: 'webm' | 'mp3' | 'wav' | 'ogg'
|
||||
quality?: number
|
||||
bitrate?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Blob转换为指定格式的音频文件
|
||||
* @param audioBlob 原始音频Blob
|
||||
* @param options 转换选项
|
||||
* @returns Promise<Blob> 转换后的音频Blob
|
||||
*/
|
||||
export const convertAudioFormat = async (
|
||||
audioBlob: Blob,
|
||||
options: AudioConvertOptions
|
||||
): Promise<Blob> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const audio = new Audio()
|
||||
const url = URL.createObjectURL(audioBlob)
|
||||
|
||||
audio.onloadedmetadata = () => {
|
||||
// 这里可以添加格式转换逻辑
|
||||
// 目前直接返回原始Blob,实际项目中可以使用Web Audio API或第三方库
|
||||
URL.revokeObjectURL(url)
|
||||
resolve(audioBlob)
|
||||
}
|
||||
|
||||
audio.onerror = () => {
|
||||
URL.revokeObjectURL(url)
|
||||
reject(new Error('音频加载失败'))
|
||||
}
|
||||
|
||||
audio.src = url
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取音频文件信息
|
||||
* @param audioBlob 音频Blob
|
||||
* @returns Promise<AudioFile> 音频文件信息
|
||||
*/
|
||||
export const getAudioFileInfo = async (audioBlob: Blob): Promise<AudioFile> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const audio = new Audio()
|
||||
const url = URL.createObjectURL(audioBlob)
|
||||
|
||||
audio.onloadedmetadata = () => {
|
||||
const audioFile: AudioFile = {
|
||||
blob: audioBlob,
|
||||
name: `audio-${Date.now()}.webm`,
|
||||
size: audioBlob.size,
|
||||
type: audioBlob.type,
|
||||
duration: audio.duration
|
||||
}
|
||||
|
||||
URL.revokeObjectURL(url)
|
||||
resolve(audioFile)
|
||||
}
|
||||
|
||||
audio.onerror = () => {
|
||||
URL.revokeObjectURL(url)
|
||||
reject(new Error('无法获取音频信息'))
|
||||
}
|
||||
|
||||
audio.src = url
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩音频文件
|
||||
* @param audioBlob 原始音频Blob
|
||||
* @param quality 压缩质量 (0-1)
|
||||
* @returns Promise<Blob> 压缩后的音频Blob
|
||||
*/
|
||||
export const compressAudio = async (
|
||||
audioBlob: Blob,
|
||||
quality: number = 0.8
|
||||
): Promise<Blob> => {
|
||||
// 这里可以实现音频压缩逻辑
|
||||
// 目前直接返回原始Blob,实际项目中可以使用Web Audio API
|
||||
return audioBlob
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传音频文件到服务器
|
||||
* @param audioFile 音频文件
|
||||
* @param uploadUrl 上传地址
|
||||
* @returns Promise<string> 服务器返回的文件ID或URL
|
||||
*/
|
||||
export const uploadAudioFile = async (
|
||||
audioFile: AudioFile,
|
||||
uploadUrl: string
|
||||
): Promise<string> => {
|
||||
const formData = new FormData()
|
||||
formData.append('audio', audioFile.blob, audioFile.name)
|
||||
formData.append('duration', audioFile.duration?.toString() || '0')
|
||||
|
||||
try {
|
||||
const response = await fetch(uploadUrl, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`上传失败: ${response.statusText}`)
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
return result.fileId || result.url
|
||||
} catch (error) {
|
||||
console.error('音频上传失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为第三方TTS服务准备音频数据
|
||||
* @param audioBlob 音频Blob
|
||||
* @param serviceType TTS服务类型
|
||||
* @returns Promise<any> 准备好的音频数据
|
||||
*/
|
||||
export const prepareAudioForTTS = async (
|
||||
audioBlob: Blob,
|
||||
serviceType: 'openai' | 'azure' | 'aws' | 'google'
|
||||
): Promise<any> => {
|
||||
const audioFile = await getAudioFileInfo(audioBlob)
|
||||
|
||||
switch (serviceType) {
|
||||
case 'openai':
|
||||
return {
|
||||
file: audioFile.blob,
|
||||
model: 'whisper-1',
|
||||
language: 'zh',
|
||||
response_format: 'json'
|
||||
}
|
||||
|
||||
case 'azure':
|
||||
return {
|
||||
audio: audioFile.blob,
|
||||
language: 'zh-CN',
|
||||
format: 'json'
|
||||
}
|
||||
|
||||
case 'aws':
|
||||
return {
|
||||
Audio: audioFile.blob,
|
||||
LanguageCode: 'zh-CN',
|
||||
MediaFormat: 'webm'
|
||||
}
|
||||
|
||||
case 'google':
|
||||
return {
|
||||
audio: {
|
||||
content: await blobToBase64(audioFile.blob)
|
||||
},
|
||||
config: {
|
||||
encoding: 'WEBM_OPUS',
|
||||
languageCode: 'zh-CN',
|
||||
sampleRateHertz: 16000
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`不支持的TTS服务类型: ${serviceType}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Blob转换为Base64字符串
|
||||
* @param blob Blob对象
|
||||
* @returns Promise<string> Base64字符串
|
||||
*/
|
||||
export const blobToBase64 = (blob: Blob): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => resolve(reader.result as string)
|
||||
reader.onerror = reject
|
||||
reader.readAsDataURL(blob)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证音频文件格式
|
||||
* @param blob 音频Blob
|
||||
* @returns boolean 是否为有效的音频格式
|
||||
*/
|
||||
export const validateAudioFormat = (blob: Blob): boolean => {
|
||||
const validTypes = [
|
||||
'audio/webm',
|
||||
'audio/mp3',
|
||||
'audio/wav',
|
||||
'audio/ogg',
|
||||
'audio/mpeg'
|
||||
]
|
||||
|
||||
return validTypes.includes(blob.type)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取音频录制权限
|
||||
* @returns Promise<boolean> 是否获得权限
|
||||
*/
|
||||
export const requestAudioPermission = async (): Promise<boolean> => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
stream.getTracks().forEach(track => track.stop())
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('获取音频权限失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user