diff --git a/src/components/touch-btn/touch-btn.ts b/src/components/touch-btn/touch-btn.ts index 15e83df1..4a143644 100644 --- a/src/components/touch-btn/touch-btn.ts +++ b/src/components/touch-btn/touch-btn.ts @@ -1,6 +1,6 @@ import { ErrorCode, ShowErrorTipOptions } from './touch-btn.types'; import { get_base_url } from '@/api/request.js'; -import { getToken, getPhoneEnvBool } from '@/common/common.js'; +import { getPhoneEnvBool } from '@/common/common.js'; const IsAndEnv = getPhoneEnvBool('AND') // const url = '/trapractice/voiceCallSocket/voiceCall' @@ -8,7 +8,7 @@ const IsAndEnv = getPhoneEnvBool('AND') // const url = '/traapp/asrAiModelSocket/open'; const url = '/traapp/asrAiModelSocket/open'; const _baseUrl = get_base_url(url).split('http').join('ws'); -export const wsUrl = _baseUrl + url + '?summary=' + getToken(); +export const wsUrl = _baseUrl + url ; // export const wsUrl = 'ws://10.10.10.8:8000/ws/asr'; const windowInfo = uni.getWindowInfo(); // 安全区域信息 @@ -26,6 +26,7 @@ export const horizontalSplitX = safeArea.right / 2; * @returns {Promise} 成功时返回WS实例,失败时reject错误信息 */ export const initWebSocket = (wsUrl : string) : Promise => { + console.log('wsUrl', wsUrl); return new Promise((resolve, reject) => { // 1. 创建WS连接 const ws = uni.connectSocket({ diff --git a/src/components/touch-btn/touch-btn.vue b/src/components/touch-btn/touch-btn.vue index f2aed84a..8cec5510 100644 --- a/src/components/touch-btn/touch-btn.vue +++ b/src/components/touch-btn/touch-btn.vue @@ -116,7 +116,7 @@ import { wsUrl, bottomAreaTopBoundary, horizontalSplitX, initWebSocket, showErrorTip, vibrateLight } from './touch-btn'; import { ref, computed, onMounted, nextTick, watch, reactive } from 'vue'; import { onHide, onLaunch } from '@dcloudio/uni-app'; - import common, { setupKeyboardHeightListener } from '@/common/common'; + import common, { setupKeyboardHeightListener, getToken } from '@/common/common'; import { startAudioRecord, stopAudioRecord, preRequestRecordPermission } from '@/uni_modules/ty-recording'; import { ProtocolCodec, MessageType, ControlCommand, ControlCode, ControlBody } from '@/common/protocolCodec'; import permissionApi from '@/common/permission'; @@ -275,7 +275,7 @@ allText.value = ''; ossKey.value = ''; console.log('开启语音'); - initWebSocket(wsUrl) + initWebSocket(wsUrl + '?summary=' + getToken()) .then((socketTask) => { if (!isRecording.value) { ws.value = null; @@ -328,7 +328,7 @@ } } } catch (e) { - console.log('后端发来的信息有问题', e); + console.log('后端发来的信息有问题', e, res.data); } }); }) @@ -489,9 +489,10 @@ voiceTimer = setTimeout(() => { console.log('1号定时器', voice_to_text_status.value, status.value); voiceTimer = null; - if (workMode.value === 'voice-only') { + if (workMode.value === 'voice-only' || (workMode.value === 'both' && _isOutStatus === '01')) { if (status.value === 'send_over') return; status.value = 'send_over'; // 恢复成默认状态 + console.log('1号定时器纠错'); emit('submitVoice', { ossKey: '', text: '', recordId: recordId.value }); } }, 4000); diff --git a/src/uni_modules/ty-recording/index.uts b/src/uni_modules/ty-recording/index.uts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/uni_modules/ty-recording/utssdk/app-ios/index.uts b/src/uni_modules/ty-recording/utssdk/app-ios/index.uts index 8e5a55a8..fba3e56b 100644 --- a/src/uni_modules/ty-recording/utssdk/app-ios/index.uts +++ b/src/uni_modules/ty-recording/utssdk/app-ios/index.uts @@ -1,7 +1,5 @@ import { StartAudioRecordOptions } from '../interface.uts'; import { StartAudioRecordFailImpl, AudioErrorCode } from '../unierror.uts'; - - import { UTSiOS } from "DCloudUTSFoundation" import { AVAudioEngine, AVAudioSession, AVAudioFormat, AVAudioInputNode, AVAudioPCMBuffer, AVAudioCommonFormat } from "AVFoundation"; import { NSData, NSMutableData } from "Foundation"; @@ -24,7 +22,7 @@ let reuseInt32Array: Int32Array | null = new Int32Array(4800); // 新增:缓存录制的所有Int16 PCM数据 let recordedPCMData: UInt8[] = []; // 3. 保存完整PCM数据到文件 -const sampleRate = 16000.0; // 和采集时的目标格式一致 +const targetSampleRate:Double = 16000.0; // 和采集时的目标格式一致 const channels = 1; // 实际以采集时的声道数为准(可从inputFormat获取) // 新增:录音启动时间戳(毫秒) @@ -105,8 +103,7 @@ export function startAudioRecord(options: StartAudioRecordOptions): void { isRecording = false; const err = new StartAudioRecordFailImpl(9010002); // 麦克风权限被永久拒绝,请前往设置开启 options.onError?.(err) - } else { - + } else { // 权限通过:继续初始化采集逻辑 try { // 创建音频引擎(移到闭包内,保证权限通过后才执行) @@ -132,67 +129,47 @@ export function startAudioRecord(options: StartAudioRecordOptions): void { startTime: recordStartTime, // 启动时间戳 }); const targetFormat = AVAudioFormat.init( - commonFormat= AVAudioCommonFormat.pcmFormatInt16, // 16位整型(文档枚举值) - sampleRate= inputFormat.sampleRate, // 采样率(Double类型,符合文档定义) + commonFormat= AVAudioCommonFormat.pcmFormatInt16, + sampleRate = targetSampleRate, // 采样率(Double类型,符合文档定义) channels= inputFormat.channelCount, // 声道数(AVAudioChannelCount) interleaved= false // 是否交错(Bool,单声道无影响) ); unwrappedInputNode.installTap( onBus= 0, // 总线编号,通常传 0 bufferSize= 2048, // 缓冲区大小,常用 1024/2048 - format= targetFormat, // 音频格式 + format= inputFormat, // 音频格式 block=(buffer: AVAudioPCMBuffer, time: any) => { const frameLen = buffer.frameLength as number; - const bufferListRawPtr = buffer.audioBufferList; - const audioBufferList = bufferListRawPtr.pointee; // 拿到结构体本身 - const audioBufferPtr = audioBufferList.mBuffers; // AudioBuffer指针 - // // console.log(audioBufferPtr.mData); - const mData = audioBufferPtr.mData; + const inputFrameLen = frameLen as Double; + const sampleRateRatio = targetSampleRate / inputFormat.sampleRate; + const outputFrameCounta = inputFrameLen * sampleRateRatio + const outputFrameCount = AVAudioFrameCount(outputFrameCounta); + const outputBufferOpt = AVAudioPCMBuffer.init(pcmFormat= targetFormat!, frameCapacity= outputFrameCount); + outputBufferOpt!.frameLength = outputFrameCount + let inputBlock: AVAudioConverterInputBlock = (inNumPackets, outStatus):AVAudioBuffer => { + outStatus.pointee = AVAudioConverterInputStatus.haveData; // 告诉转换器有输入数据 + return buffer; // 返回当前 Tap 拿到的输入缓冲区 + } + let convertError: NSError | null = null; + const converter = AVAudioConverter.init(from= inputFormat, to= targetFormat!); + let convertStatus = converter!.convert(to=outputBufferOpt!,error=UTSiOS.getPointer(convertError),withInputFrom=inputBlock) + // console.log(convertStatus); + // console.log(AVAudioConverterOutputStatus.haveData); + // console.log(convertStatus == AVAudioConverterOutputStatus.haveData); + const int16Ptr = outputBufferOpt!.int16ChannelData; + const dataPtr = int16Ptr![0]; + // const floatChannelData = outputBufferOpt!.floatChannelData + // const dataPtr = floatChannelData![0]; - // const mDataByteSize:Int = 9600; // 你之前看到的字节数 - // const uint8Ptr = mData!.bindMemory(to= UInt8.self, capacity= mDataByteSize); - // const pcmNormalArray: number[] = []; - // for (let i:Int = 0; i < mDataByteSize; i++) { - // // 直接读取指针的第i个字节,转为0-255的普通数字 - // pcmNormalArray.push(uint8Ptr[i] as number); - // } - // const halfLength:Int = 4800; - // const firstHalf = pcmNormalArray.slice(0, halfLength); - // const secondHalf = pcmNormalArray.slice(halfLength, mDataByteSize); - // options.onFrame?.(firstHalf); - // options.onFrame?.(secondHalf); - - - // options.onFrame?.(pcmNormalArray); - // let uint8 = new Uint8Array([4, 5, 8, 12]); - // let array = Array.from(uint8); - // console.log(array); - // options.onFrame?.(array); - // const audioBuffer = audioBufferPtr.pointee; // 拿到AudioBuffer结构体 - // console.log("音频数据字节数:", audioBuffer.mDataByteSize); // 比如4096(2048帧×2字节) - // console.log("声道数:", audioBuffer.mNumberChannels); // 1 - // console.log("二进制数据指针:", audioBuffer.mData); // 指向16位PCM数据的内存地址 - - // const int16Ptr = buffer.int16ChannelData; - // if (int16Ptr != null && frameLen > 0) { - // const dataPtr = int16Ptr![0]; - // const CHUNK_FRAME_SIZE = frameLen / 2; // 16位=2字节/帧 → 512帧/分片 - // // 关键2:循环拆分 9600 字节为多个小分片 - // for (let startFrame:number = 0; startFrame < frameLen; startFrame += CHUNK_FRAME_SIZE) { - // // 计算当前分片的结束帧(避免越界) - // const endFrame = Math.min(startFrame + CHUNK_FRAME_SIZE, frameLen); - // let _recordedPCMData: UInt8[] = []; - // // 只处理当前分片的帧 - // for (let i: Int = startFrame as Int; i < endFrame; i++) { - // const int16Value = dataPtr[i]; - // let byte1 = UInt8(int16Value & 0xFF); - // let byte2 = UInt8((int16Value >> 8) & 0xFF); - // _recordedPCMData.push(byte1); - // _recordedPCMData.push(byte2); - // } - // options.onFrame?.(_recordedPCMData); - // } - // } + let _recordedPCMData: UInt8[] = []; + for (let i: Int = 0; i < outputFrameCount; i++) { + const int16Value = dataPtr[i]; + let byte1 = UInt8(int16Value & 0xFF); + let byte2 = UInt8((int16Value >> 8) & 0xFF); + _recordedPCMData.push(byte1); + _recordedPCMData.push(byte2); + } + options.onFrame?.(_recordedPCMData); } ); UTSiOS.try(audioEngine!.start()); @@ -309,3 +286,65 @@ function savePCMToFile(pcmData: UInt8[], sampleRate: number, channels: number): return null; } } + + + + +// const int16Ptr = buffer.int16ChannelData; +// if (int16Ptr != null && frameLen > 0) { +// const dataPtr = int16Ptr![0]; +// const CHUNK_FRAME_SIZE = frameLen / 2; // 16位=2字节/帧 → 512帧/分片 +// // 关键2:循环拆分 9600 字节为多个小分片 +// for (let startFrame:number = 0; startFrame < frameLen; startFrame += CHUNK_FRAME_SIZE) { +// // 计算当前分片的结束帧(避免越界) +// const endFrame = Math.min(startFrame + CHUNK_FRAME_SIZE, frameLen); +// let _recordedPCMData: UInt8[] = []; +// // 只处理当前分片的帧 +// for (let i: Int = startFrame as Int; i < endFrame; i++) { +// const int16Value = dataPtr[i]; +// let byte1 = UInt8(int16Value & 0xFF); +// let byte2 = UInt8((int16Value >> 8) & 0xFF); +// _recordedPCMData.push(byte1); +// _recordedPCMData.push(byte2); +// } +// options.onFrame?.(_recordedPCMData); +// } +// } + + + + + + + +// const frameLen = buffer.frameLength as number; + // const bufferListRawPtr = buffer.audioBufferList; + // const audioBufferList = bufferListRawPtr.pointee; // 拿到结构体本身 + // const audioBufferPtr = audioBufferList.mBuffers; // AudioBuffer指针 + // // // console.log(audioBufferPtr.mData); + // const mData = audioBufferPtr.mData; + // console.log(mData); + + // const mDataByteSize:Int = 9600; // 你之前看到的字节数 + // const uint8Ptr = mData!.bindMemory(to= UInt8.self, capacity= mDataByteSize); + // const pcmNormalArray: number[] = []; + // for (let i:Int = 0; i < mDataByteSize; i++) { + // // 直接读取指针的第i个字节,转为0-255的普通数字 + // pcmNormalArray.push(uint8Ptr[i] as number); + // } + // const halfLength:Int = 4800; + // const firstHalf = pcmNormalArray.slice(0, halfLength); + // const secondHalf = pcmNormalArray.slice(halfLength, mDataByteSize); + // options.onFrame?.(firstHalf); + // options.onFrame?.(secondHalf); + + + // options.onFrame?.(pcmNormalArray); + // let uint8 = new Uint8Array([4, 5, 8, 12]); + // let array = Array.from(uint8); + // console.log(array); + // options.onFrame?.(array); + // const audioBuffer = audioBufferPtr.pointee; // 拿到AudioBuffer结构体 + // console.log("音频数据字节数:", audioBuffer.mDataByteSize); // 比如4096(2048帧×2字节) + // console.log("声道数:", audioBuffer.mNumberChannels); // 1 + // console.log("二进制数据指针:", audioBuffer.mData); // 指向16位PCM数据的内存地址 \ No newline at end of file