mac电脑开发第二次1
This commit is contained in:
@@ -3,10 +3,24 @@ import { StartAudioRecordFailImpl, AudioErrorCode } from '../unierror.uts';
|
|||||||
|
|
||||||
|
|
||||||
import { UTSiOS } from "DCloudUTSFoundation"
|
import { UTSiOS } from "DCloudUTSFoundation"
|
||||||
|
import { AVAudioEngine, AVAudioSession, AVAudioFormat, AVAudioInputNode, AVAudioPCMBuffer } from "AVFoundation";
|
||||||
|
import { NSData, NSMutableData } from "Foundation";
|
||||||
|
|
||||||
// 线程安全的录制状态标识
|
// @argumentLabel("forBus")
|
||||||
|
/**
|
||||||
|
* 音频采集状态(全局变量,保证生命周期)
|
||||||
|
*/
|
||||||
let isRecording = false;
|
let isRecording = false;
|
||||||
|
/**
|
||||||
|
* 音频引擎实例(全局变量,避免异步回调中被释放)
|
||||||
|
*/
|
||||||
|
let audioEngine: AVAudioEngine | null = null;
|
||||||
|
/**
|
||||||
|
* 音频输入节点(全局变量,避免被释放)
|
||||||
|
*/
|
||||||
|
let inputNode: AVAudioInputNode | null = null;
|
||||||
|
// 全局复用缓冲区(避免每次创建新数组)
|
||||||
|
let reuseInt32Array: Int32Array | null = new Int32Array(4800);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动音频采集(iOS端核心实现)
|
* 启动音频采集(iOS端核心实现)
|
||||||
@@ -16,19 +30,83 @@ let isRecording = false;
|
|||||||
export function startAudioRecord(options: StartAudioRecordOptions): void {
|
export function startAudioRecord(options: StartAudioRecordOptions): void {
|
||||||
// 避免重复启动
|
// 避免重复启动
|
||||||
console.log('避免重复启动', isRecording);
|
console.log('避免重复启动', isRecording);
|
||||||
if (isRecording) return;
|
// if (isRecording) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const audioSession = AVAudioSession.sharedInstance();
|
const audioSession = AVAudioSession.sharedInstance();
|
||||||
const authStatus = audioSession.recordPermission();
|
UTSiOS.try(audioSession.setCategory(
|
||||||
console.log('asaadda', authStatus);
|
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;
|
isRecording = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 初始化失败回调错误
|
// 初始化失败回调错误
|
||||||
const err = new StartAudioRecordFailImpl(AudioErrorCode.AUDIO_FORMAT_ILLEGAL);
|
const err = new StartAudioRecordFailImpl(9010002);
|
||||||
options.onError?.(err);
|
options.onError?.(err);
|
||||||
// 重置状态
|
// 重置状态
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +115,34 @@ export function startAudioRecord(options: StartAudioRecordOptions): void {
|
|||||||
* 停止音频采集(配套停止逻辑,保证功能完整)
|
* 停止音频采集(配套停止逻辑,保证功能完整)
|
||||||
* @param options 回调配置
|
* @param options 回调配置
|
||||||
*/
|
*/
|
||||||
@UTSJS.keepAlive
|
|
||||||
export function stopAudioRecord(): void {
|
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