mac电脑开发第二次1
This commit is contained in:
@@ -3,10 +3,24 @@ import { StartAudioRecordFailImpl, AudioErrorCode } from '../unierror.uts';
|
||||
|
||||
|
||||
import { UTSiOS } from "DCloudUTSFoundation"
|
||||
import { AVAudioEngine, AVAudioSession, AVAudioFormat, AVAudioInputNode, AVAudioPCMBuffer } from "AVFoundation";
|
||||
import { NSData, NSMutableData } from "Foundation";
|
||||
|
||||
// 线程安全的录制状态标识
|
||||
// @argumentLabel("forBus")
|
||||
/**
|
||||
* 音频采集状态(全局变量,保证生命周期)
|
||||
*/
|
||||
let isRecording = false;
|
||||
|
||||
/**
|
||||
* 音频引擎实例(全局变量,避免异步回调中被释放)
|
||||
*/
|
||||
let audioEngine: AVAudioEngine | null = null;
|
||||
/**
|
||||
* 音频输入节点(全局变量,避免被释放)
|
||||
*/
|
||||
let inputNode: AVAudioInputNode | null = null;
|
||||
// 全局复用缓冲区(避免每次创建新数组)
|
||||
let reuseInt32Array: Int32Array | null = new Int32Array(4800);
|
||||
|
||||
/**
|
||||
* 启动音频采集(iOS端核心实现)
|
||||
@@ -16,19 +30,83 @@ let isRecording = false;
|
||||
export function startAudioRecord(options: StartAudioRecordOptions): void {
|
||||
// 避免重复启动
|
||||
console.log('避免重复启动', isRecording);
|
||||
if (isRecording) return;
|
||||
// if (isRecording) return;
|
||||
|
||||
try {
|
||||
const audioSession = AVAudioSession.sharedInstance();
|
||||
const authStatus = audioSession.recordPermission();
|
||||
console.log('asaadda', authStatus);
|
||||
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);
|
||||
unwrappedInputNode.installTap(
|
||||
onBus= 0, // 总线编号,通常传 0
|
||||
bufferSize= 1024, // 缓冲区大小,常用 1024/2048
|
||||
format= inputFormat, // 音频格式
|
||||
block=(buffer: AVAudioPCMBuffer, time: any) => {
|
||||
const floatData = buffer.floatChannelData;
|
||||
const channelData = floatData![0];
|
||||
const frameLength = buffer.frameLength as number;
|
||||
const bytesPerSample = 2; // 16位整型=2字节/样本
|
||||
const totalBytes = frameLength * bytesPerSample;
|
||||
|
||||
console.log(channelData);
|
||||
console.log(totalBytes);
|
||||
const pcmData = NSMutableData.dataWithCapacity(20); // 可变Data,支持写入
|
||||
|
||||
// const data = new Float32Array(int32Array.byteLength);
|
||||
// data.set(int32Array.buffer, 1); // 内存块拷贝,替代逐字节写入
|
||||
|
||||
options.onFrame?.(pcmData);
|
||||
// if (floatData !== null) {
|
||||
// // 单声道取第0个通道(立体声可取[0]左声道、[1]右声道)
|
||||
// const channelData = floatData[0];
|
||||
|
||||
// options.onFrame?.(channelData)
|
||||
// }
|
||||
}
|
||||
);
|
||||
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(AudioErrorCode.AUDIO_FORMAT_ILLEGAL);
|
||||
const err = new StartAudioRecordFailImpl(9010002);
|
||||
options.onError?.(err);
|
||||
// 重置状态
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +115,34 @@ export function startAudioRecord(options: StartAudioRecordOptions): void {
|
||||
* 停止音频采集(配套停止逻辑,保证功能完整)
|
||||
* @param options 回调配置
|
||||
*/
|
||||
@UTSJS.keepAlive
|
||||
|
||||
export function stopAudioRecord(): void {
|
||||
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); // 移除音频回调
|
||||
|
||||
|
||||
// 停用音频会话
|
||||
const audioSession = AVAudioSession.sharedInstance();
|
||||
|
||||
try {
|
||||
UTSiOS.try(audioSession.setActive(false));
|
||||
}catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
inputNode = null;
|
||||
audioEngine = null; // 释放全局引擎
|
||||
isRecording = false;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user