diff --git a/src/components/touch-btn/touch-btn.vue b/src/components/touch-btn/touch-btn.vue index 72e1119b..662049e4 100644 --- a/src/components/touch-btn/touch-btn.vue +++ b/src/components/touch-btn/touch-btn.vue @@ -287,7 +287,6 @@ tempText.value = ''; allText.value = ''; ossKey.value = ''; - pressTimer = setTimeout(() => { if (!isRecording.value) { console.log('定时器被取消还是进入了'); @@ -349,7 +348,7 @@ tempText.value = ''; allText.value = allText.value + body; } else if (msgType === MessageType.TIP_MESSAGE) { - tempText.value += body as string; + tempText.value = body as string; } else if (msgType === MessageType.CONTROL_CMD) { // 返回结束消息 const controlBody = body as ControlBody; @@ -418,17 +417,17 @@ } } }); - }, 140); + }, 120); }; // 结束录音 const closePopup = () => { - startRecordingTimer(false); isRecording.value = false; popupRef.value.close(); }; // 结束ws const closeWS = () => { + startRecordingTimer(false); if (ws.value) { ws.value.close({ code: 1000, @@ -484,11 +483,11 @@ status.value = 'voice_send_ing'; //主状态设置为语音发送中 } - if ((_isOutStatus !== '03' || recordDurationMs <= props.minRecordDuration) && recordDurationMs < 640) { + if ((_isOutStatus !== '03' || recordDurationMs <= props.minRecordDuration) && recordDurationMs < 700) { await new Promise((resolve) => { setTimeout(() => { resolve(); - }, 640 - recordDurationMs); + }, 700 - recordDurationMs); }); } diff --git a/src/uni_modules/ty-recording/utssdk/app-android/index.uts b/src/uni_modules/ty-recording/utssdk/app-android/index.uts index eb25f9c0..b4699124 100644 --- a/src/uni_modules/ty-recording/utssdk/app-android/index.uts +++ b/src/uni_modules/ty-recording/utssdk/app-android/index.uts @@ -1,9 +1,7 @@ import { StartAudioRecordOptions, AudioStartRes } from '../interface.uts'; import { StartAudioRecordFailImpl } from '../unierror.uts'; -/** - * 引用Android音频相关系统库 - */ +//引用Android音频相关系统库 import AudioRecord from "android.media.AudioRecord"; import MediaRecorder from "android.media.MediaRecorder"; import AudioFormat from "android.media.AudioFormat"; @@ -42,6 +40,7 @@ export function startAudioRecord(options : StartAudioRecordOptions) : void { } audioRecord = _audioRecord; isRecording.set(true) + console.log('开始采集'); const _recordThread = new Thread(() => { _audioRecord.startRecording(); // 开始采集 const recordId = `${new Date().getTime()}_${Math.floor(Math.random() * 10000)}`; diff --git a/src/uni_modules/ty-recording/utssdk/app-ios/index copy.uts b/src/uni_modules/ty-recording/utssdk/app-ios/index copy.uts deleted file mode 100644 index 32f903d1..00000000 --- a/src/uni_modules/ty-recording/utssdk/app-ios/index copy.uts +++ /dev/null @@ -1,251 +0,0 @@ -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"; -import { Int, Int16 } from 'Swift'; - -// @argumentLabel("forBus") -/** - * 音频采集状态(全局变量,保证生命周期) - */ -let isRecording = false; -/** - * 音频引擎实例(全局变量,避免异步回调中被释放) - */ -let audioEngine: AVAudioEngine | null = null; -/** - * 音频输入节点(全局变量,避免被释放) - */ -let inputNode: AVAudioInputNode | null = null; -// 全局复用缓冲区(避免每次创建新数组) -let reuseInt32Array: Int32Array | null = new Int32Array(4800); -// 新增:缓存录制的所有Int16 PCM数据 -let recordedPCMData: UInt8[] = []; -// 3. 保存完整PCM数据到文件 -const sampleRate = 16000.0; // 和采集时的目标格式一致 -const channels = 1; // 实际以采集时的声道数为准(可从inputFormat获取) - -/** - * 启动音频采集(iOS端核心实现) - * @param options 采集配置及回调 - */ -@UTSJS.keepAlive -export function startAudioRecord(options: StartAudioRecordOptions): void { - // 避免重复启动 - console.log('避免重复启动', isRecording); - // if (isRecording) return; - - try { - recordedPCMData = []; - const audioSession = AVAudioSession.sharedInstance(); - UTSiOS.try(audioSession.setCategory( - AVAudioSession.Category.playAndRecord, - mode=AVAudioSession.Mode.spokenAudio, - options= AVAudioSession.CategoryOptions.duckOthers // 可选:其他音频静音 - )); - // UTSiOS.try(audioSession.setCategory(AVAudioSession.Category.playAndRecord)); - // UTSiOS.try(audioSession.setMode(AVAudioSession.Mode.spokenAudio)); - UTSiOS.try(audioSession.setActive(true)); - audioSession.requestRecordPermission((granted: boolean) => { - // 闭包内处理权限结果(异步执行) - if (granted) { - // 权限通过:继续初始化采集逻辑 - try { - // 创建音频引擎(移到闭包内,保证权限通过后才执行) - audioEngine = AVAudioEngine.init(); - if (audioEngine === null) { // 解包:先判断是否为nil - throw new Error("音频输入节点为空,无法获取音频格式"); - } - inputNode = audioEngine!.inputNode; - if (inputNode === null) { // 解包:先判断是否为nil - throw new Error("音频输入节点为空,无法获取音频格式"); - } - const unwrappedInputNode = inputNode!; - const inputFormat = unwrappedInputNode.inputFormat(forBus= 0); - console.log("硬件原生采样率:", inputFormat.sampleRate); - console.log("硬件原生声道数:", inputFormat.channelCount); - console.log("硬件原生格式:", inputFormat.commonFormat); - - const targetFormat = AVAudioFormat.init( - commonFormat= AVAudioCommonFormat.pcmFormatInt16, // 16位整型(文档枚举值) - sampleRate= inputFormat.sampleRate, // 采样率(Double类型,符合文档定义) - channels= inputFormat.channelCount, // 声道数(AVAudioChannelCount) - interleaved= false // 是否交错(Bool,单声道无影响) - ); - unwrappedInputNode.installTap( - onBus= 0, // 总线编号,通常传 0 - bufferSize= 2048, // 缓冲区大小,常用 1024/2048 - format= targetFormat, // 音频格式 - 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 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); - // } - // } - } - ); - UTSiOS.try(audioEngine!.start()); - if (audioEngine!.isRunning) { - console.log("引擎确实处于运行状态"); - isRecording = true; - } else { - console.error("调用 start() 但引擎未运行"); - } - isRecording = true; - } catch (error) { - isRecording = false; - } - } else { - // 权限被拒绝:回调错误 - isRecording = false; - } - }); - isRecording = true; - } catch (error) { - // 初始化失败回调错误 - const err = new StartAudioRecordFailImpl(9010002); - options.onError?.(err); - // 重置状态 - return; - } -} - -/** - * 停止音频采集(配套停止逻辑,保证功能完整) - * @param options 回调配置 - */ - -export function stopAudioRecord(): void { - console.log('停止音频采集'); - if (audioEngine === null) { // 解包:先判断是否为nil - isRecording = false; - return; // 直接返回,不执行后续逻辑 - } - const _audioEngine = audioEngine! - if (_audioEngine.isRunning) { - _audioEngine.stop(); - } - if (inputNode === null) { // 解包:先判断是否为nil - isRecording = false; - return; // 直接返回,不执行后续逻辑 - } - const _inputNode = inputNode! - _inputNode.removeTap(onBus= 0); // 移除音频回调 - console.log('移除音频回调'); - - // 停用音频会话 - const audioSession = AVAudioSession.sharedInstance(); - - try { - UTSiOS.try(audioSession.setActive(false)); - }catch (e) { - console.log(e) - } - console.log('数组大小',recordedPCMData.length); - const filePath = savePCMToFile(recordedPCMData, sampleRate, channels); - console.log('filePath', filePath); - inputNode = null; - audioEngine = null; // 释放全局引擎 - isRecording = false; - -} - -/** - * 将Int16 PCM数组保存为文件(iOS沙盒路径) - * @param pcmData Int16格式的PCM数据 - * @param sampleRate 采样率 - * @param channels 声道数 - * @returns 文件路径 | null - */ -function savePCMToFile(pcmData: UInt8[], sampleRate: number, channels: number): string | null { - try { - const dataPath = UTSiOS.getDataPath(); - console.log('dataPath', dataPath); - if (dataPath === null) { - throw new Error("获取应用数据路径失败"); - } - // 2. 生成唯一文件名(避免覆盖) - const timestamp = new Date().getTime().toString(); - const filePath = dataPath + "/" + "recording_" + timestamp + ".pcm"; - const fileURL = NSURL.fileURL(withPath= filePath); - console.log(fileURL); - - // 3. 将Int16数组转为NSData(字节数据) - const byteCount = pcmData.length * 2; // 每个Int16占2字节 - const byteArray = Uint8Array.from(recordedPCMData); - const dataOptional = NSMutableData(length= byteArray.length); - // const dataOptional = NSMutableData(length= byteCount); - if (dataOptional === null) throw new Error("创建NSMutableData失败"); - const data = dataOptional!; - - for (let j:Int = 0; j < recordedPCMData.length; j++) { - // 获取当前字节值(UInt8) - const byteValue = recordedPCMData[j]; - // 将单个字节写入 NSMutableData 的指定位置 - data.replaceBytes(in = NSMakeRange(j, 1), withBytes= UTSiOS.getPointer(byteValue), length= 1); - } - - - // 4. 写入文件(UTS兼容的Swift风格) - const writeSuccess = data.write(toFile=filePath, atomically= true); - // console.log(data); - if (writeSuccess) { - console.log("PCM文件保存成功:", filePath); - return filePath; - } else { - throw new Error("文件写入失败"); - } - } catch (error) { - console.error("保存PCM文件出错:", error); - return null; - } -} 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 fba3e56b..2834aa3d 100644 --- a/src/uni_modules/ty-recording/utssdk/app-ios/index.uts +++ b/src/uni_modules/ty-recording/utssdk/app-ios/index.uts @@ -5,34 +5,22 @@ import { AVAudioEngine, AVAudioSession, AVAudioFormat, AVAudioInputNode, AVAudio import { NSData, NSMutableData } from "Foundation"; import { Int, Int16 } from 'Swift'; -/** - * 音频采集状态(全局变量,保证生命周期) - */ +//音频采集状态(全局变量,保证生命周期) let isRecording = false; -/** - * 音频引擎实例(全局变量,避免异步回调中被释放) - */ +//音频引擎实例(全局变量,避免异步回调中被释放) let audioEngine: AVAudioEngine | null = null; -/** - * 音频输入节点(全局变量,避免被释放) - */ +//音频输入节点(全局变量,避免被释放) let inputNode: AVAudioInputNode | null = null; -// 全局复用缓冲区(避免每次创建新数组) -let reuseInt32Array: Int32Array | null = new Int32Array(4800); // 新增:缓存录制的所有Int16 PCM数据 let recordedPCMData: UInt8[] = []; // 3. 保存完整PCM数据到文件 const targetSampleRate:Double = 16000.0; // 和采集时的目标格式一致 const channels = 1; // 实际以采集时的声道数为准(可从inputFormat获取) - // 新增:录音启动时间戳(毫秒) let recordStartTime: number | null = null; -// 新增:实时时长定时器(用于更新UI) -let durationTimer: any = null; -// 预申请权限函数(可在App启动时调用) /** - * 预申请麦克风权限(规范版,含资源管理) + * 预申请麦克风权限 * @param callback 权限申请结果回调(可选) */ export function preRequestRecordPermission(callback?: (granted: boolean) => void) { @@ -76,12 +64,11 @@ export function preRequestRecordPermission(callback?: (granted: boolean) => void * @param options 采集配置及回调 */ @UTSJS.keepAlive -export function startAudioRecord(options: StartAudioRecordOptions): void { +export function startAudioRecord(options: StartAudioRecordOptions){ // 避免重复启动 console.log('避免重复启动', isRecording); if (isRecording) return; try { - recordedPCMData = new Array(); // 获取 iOS 系统的音频会话单例对象。 const audioSession = AVAudioSession.sharedInstance(); // 设置音频会话的类别、模式和附加选项,定义 App 的音频行为。 @@ -108,18 +95,10 @@ export function startAudioRecord(options: StartAudioRecordOptions): void { try { // 创建音频引擎(移到闭包内,保证权限通过后才执行) audioEngine = AVAudioEngine.init(); - if (audioEngine === null) { // 解包:先判断是否为nil - throw new Error("音频输入节点为空,无法获取音频格式"); - } inputNode = audioEngine!.inputNode; - if (inputNode === null) { // 解包:先判断是否为nil - throw new Error("音频输入节点为空,无法获取音频格式"); - } const unwrappedInputNode = inputNode!; const inputFormat = unwrappedInputNode.inputFormat(forBus= 0); - console.log("硬件原生采样率:", inputFormat.sampleRate); - console.log("硬件原生声道数:", inputFormat.channelCount); - console.log("硬件原生格式:", inputFormat.commonFormat); + console.log("硬件原生采样率:", inputFormat.sampleRate); recordStartTime = new Date().getTime(); // 1. 生成录音唯一标识(UUID,方便多录音实例管理) const recordId = `${new Date().getTime()}_${Math.floor(Math.random() * 10000)}`; @@ -153,14 +132,9 @@ export function startAudioRecord(options: StartAudioRecordOptions): void { 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]; - let _recordedPCMData: UInt8[] = []; for (let i: Int = 0; i < outputFrameCount; i++) { const int16Value = dataPtr[i]; @@ -201,7 +175,7 @@ export function startAudioRecord(options: StartAudioRecordOptions): void { * @param options 回调配置 */ -export function stopAudioRecord(): void { +export function stopAudioRecord() { console.log('停止音频采集'); if (audioEngine === null) { // 解包:先判断是否为nil isRecording = false; @@ -218,133 +192,15 @@ export function stopAudioRecord(): void { const _inputNode = inputNode! _inputNode.removeTap(onBus= 0); // 移除音频回调 console.log('移除音频回调'); - // 停用音频会话 const audioSession = AVAudioSession.sharedInstance(); - try { UTSiOS.try(audioSession.setActive(false)); }catch (e) { console.log(e) } - // console.log('数组大小',recordedPCMData.length); - // const filePath = savePCMToFile(recordedPCMData, sampleRate, channels); - // console.log('filePath', filePath); inputNode = null; audioEngine = null; // 释放全局引擎 isRecording = false; } - -/** - * 将Int16 PCM数组保存为文件(iOS沙盒路径) - * @param pcmData Int16格式的PCM数据 - * @param sampleRate 采样率 - * @param channels 声道数 - * @returns 文件路径 | null - */ -function savePCMToFile(pcmData: UInt8[], sampleRate: number, channels: number): string | null { - try { - const dataPath = UTSiOS.getDataPath(); - console.log('dataPath', dataPath); - if (dataPath === null) { - throw new Error("获取应用数据路径失败"); - } - // 2. 生成唯一文件名(避免覆盖) - const timestamp = new Date().getTime().toString(); - const filePath = dataPath + "/" + "recording_" + timestamp + ".pcm"; - const fileURL = NSURL.fileURL(withPath= filePath); - console.log(fileURL); - - // 3. 将Int16数组转为NSData(字节数据) - const byteCount = pcmData.length * 2; // 每个Int16占2字节 - const byteArray = Uint8Array.from(recordedPCMData); - const dataOptional = NSMutableData(length= byteArray.length); - // const dataOptional = NSMutableData(length= byteCount); - if (dataOptional === null) throw new Error("创建NSMutableData失败"); - const data = dataOptional!; - - for (let j:Int = 0; j < recordedPCMData.length; j++) { - // 获取当前字节值(UInt8) - const byteValue = recordedPCMData[j]; - // 将单个字节写入 NSMutableData 的指定位置 - data.replaceBytes(in = NSMakeRange(j, 1), withBytes= UTSiOS.getPointer(byteValue), length= 1); - } - - - // 4. 写入文件(UTS兼容的Swift风格) - const writeSuccess = data.write(toFile=filePath, atomically= true); - // console.log(data); - if (writeSuccess) { - console.log("PCM文件保存成功:", filePath); - return filePath; - } else { - throw new Error("文件写入失败"); - } - } catch (error) { - console.error("保存PCM文件出错:", error); - 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 diff --git a/src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue b/src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue index 5af55e01..6a663418 100644 --- a/src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue +++ b/src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue @@ -136,7 +136,7 @@ }, data() { return { - duration: 300, + duration: 200, ani: [], showPopup: false, showTrans: false, @@ -260,7 +260,7 @@ this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick } if (this.animation) { - this.duration = 300 + this.duration = 200 } else { this.duration = 0 }