Выполнение неблокирующей, высокопроизводительной активности в nativescript / javascript - PullRequest
0 голосов
/ 11 апреля 2020

Этот вопрос касается выполнения неблокирующей, высокопроизводительной операции в 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 асинхронное выполнение), который я описал выше?

Спасибо

1 Ответ

0 голосов
/ 11 апреля 2020

Я бы сохранил полную реализацию Java в фактическом Java, вы можете сделать это, создав файл java в папке вашего плагина: platform / android / java, так что может быть что-то вроде: platform / android /java/org/nativescript/AudioRecord.java

Здесь вы можете делать все по порядку, так что вас не будет беспокоить блокировка пользовательского интерфейса. Вы можете вызывать методы Java непосредственно из NativeScript для запуска и остановки записи. Когда вы создаете свой проект, файл Java будет автоматически скомпилирован и включен.

Вы можете генерировать наборы из вашего Java класса, взяв class.jar из сгенерированного файла .aar вашего плагина ({ plugin_name} .aar) и создайте для него объявления типов: https://docs.nativescript.org/core-concepts/android-runtime/metadata/generating-typescript-declarations

Таким образом, вы получаете всю информацию о методе / классе / типе, доступную в вашем редакторе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...