Этот вопрос касается выполнения неблокирующей, высокопроизводительной операции в nativescript, которая необходима для простой задачи чтения и сохранения необработанного звука с микрофона посредством прямого доступа к оборудованию через собственный Android API. Я полагаю, что довел платформу nativescript до предела своих возможностей, и мне нужна помощь экспертов.
Я создаю WAV-рекордер в Nativescript Android. Собственная реализация описана здесь (соответствующий код ниже).
Короче говоря, это можно сделать, читая аудио поток из буфера 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]);
}
}
}
My Nativescript javascript Реализация:
Я написал машинописный код nativescript, который делает то же самое, что и native Android код выше. Проблема # 1, с которой я столкнулся, заключалась в том, что я не могу запустить while(isRecording)
, потому что поток javascript будет занят работой внутри while
l oop и никогда не сможет отлавливать нажатия кнопок для запуска stopRecording()
.
Я попытался решить проблему # 1, используя setInterval
для асинхронного выполнения, например:
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: Хотя это работает хорошо, во многих случаях пользовательский интерфейс останавливается на 1-10 секунд (например, после нажатия кнопки для остановки записи).
Я пытался изменить временной интервал, который выполняется writeAudioDataToFile()
, с 10 мс до 0 мс и до 1000 мс (при наличии очень большого буфера), но затем пользовательский интерфейс зависал дольше и я потерял в сохраненных данных (буферизованные данные, которые не были сохранены в файл e).
Я попытался разгрузить эту операцию в отдельный поток с помощью рабочего потока nativescript, как описано здесь , где startRecording()
и stopRecording()
вызываются сообщениями, отправленными на нить, подобная этой:
global.onmessage = function(msg) {
if (msg.data === 'startRecording') {
startRecording();
} else if (msg.data === 'stopRecording') {
stopRecording();
}
}
Это решило проблему пользовательского интерфейса, но создало проблему # 3: рекордер stop не был выполнен вовремя (т.е. запись останавливается через 10-50 секунд после ' stopRecording 'msg.data получен рабочим потоком). Я пытался использовать разные временные интервалы в setInterval
внутри рабочего потока (от 0 до 1000 мс), но это не решило проблему и даже заставило stopRecording()
выполняться с большими задержками.
У кого-нибудь есть идея о том, как выполнить такую неблокирующую высокопроизводительную запись в nativescript / javascript? Есть ли лучший подход для решения проблемы № 1 (javascript асинхронное выполнение), который я описал выше?
Спасибо