这个问题是关于在 nativescript 中运行一个非阻塞、高性能的活动,这是通过原生 Android API 直接访问硬件从麦克风读取和保存原始音频的简单任务所需的。我相信我已经将 nativescript 框架发挥到了极致,我需要专家的帮助。
我正在 Nativescript Android 中构建一个 WAV 录音机。本机实现在此处描述(相关代码如下)。
简而言之,这可以通过从 android.media.AudioRecord 缓冲区读取音频流,然后将缓冲区写入单独线程中的文件来完成,如下所述:
原生 Android 实现
startRecording()
由按下按钮触发,并启动一个运行的新线程writeAudioDataToFile()
:
private void startRecording() {
// ... init Recorder
recorder.startRecording();
isRecording = true;
recordingThread = new Thread(new Runnable() {
@Override
public void run() {
writeAudioDataToFile();
}
}, "AudioRecorder Thread");
recordingThread.start();
}
isRecording
通过设置为false
(stopRecording()
由按下按钮触发)停止录制:
private void stopRecording() {
isRecording = false;
recorder.stop();
recorder.release();
recordingThread = null;
}
如果 isRecording = false,则停止读取和保存缓冲区:
private void writeAudioDataToFile() {
// ... init file and buffer
ByteArrayOutputStream recData = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(recData);
int read = 0;
while(isRecording) {
read = recorder.read(data, 0, bufferSize);
for(int i = 0; i < bufferReadResult; i++) {
dos.writeShort(buffer[i]);
}
}
}
我的Nativescript javascript 实现:
我编写了一个与上面的原生 Android 代码相同的 nativescript 打字稿代码。我面临的问题 #1 是我无法运行while(isRecording)
,因为 javascript 线程将忙于在while
循环内运行,并且永远无法捕获按钮点击运行stopRecording()
。
我试图通过使用setInterval
异步执行来解决问题 #1,如下所示:
startRecording()
由按下按钮触发,并设置执行的时间间隔为 10 毫秒writeAudioDataToFile()
:
startRecording() {
this.audioRecord.startRecording();
this.audioBufferSavingTimer = setInterval(() => this.writeAudioDataToFile(), 10);
}
writeAudioDataToFile()
回调每 10 毫秒排队一次:
writeAudioDataToFile() {
let bufferReadResult = this.audioRecord.read(
this.buffer,
0,
this.minBufferSize / 4
);
for (let i = 0; i < bufferReadResult; i++) {
dos.writeShort(buffer[i]);
}
}
通过清除时间间隔(stopRecording()
由按下按钮触发)停止录制:
stopRecording() {
clearInterval(this.audioBufferSavingTimer);
this.audioRecord.stop();
this.audioRecord.release();
}
问题 2:虽然这很好用,但在许多情况下,它会使 UI 冻结 1-10 秒(例如在单击按钮停止录制后)。
我尝试将执行的时间间隔writeAudioDataToFile()
从 10 毫秒更改为 0 毫秒,再到 1000 毫秒(虽然缓冲区很大),但是 UI 冻结时间更长,并且我在保存的数据中遇到了丢失(未保存的缓冲数据到文件)。
我尝试通过使用此处描述的 nativescript 工作线程将此操作卸载到单独的线程,其中startRecording()
和stopRecording()
由发送到线程的消息调用,如下所示:
global.onmessage = function(msg) {
if (msg.data === 'startRecording') {
startRecording();
} else if (msg.data === 'stopRecording') {
stopRecording();
}
}
这解决了 UI 问题,但产生了问题 #3:记录器停止未按时执行(即在工作线程接收到 'stopRecording' msg.data 后 10 到 50 秒停止记录)。我尝试在setInterval
工作线程内部使用不同的时间间隔(0ms 到 1000ms),但这并没有解决问题,甚至导致stopRecording()
执行延迟更大。
有谁知道如何在 nativescript/javascript 中执行这种非阻塞的高性能记录活动?有没有更好的方法来解决我上面描述的问题 #1(javascript 异步执行)?
谢谢