Я реализую приложение, которое записывает и анализирует аудио в режиме реального времени (или, по крайней мере, настолько близко к реальному времени, насколько это возможно), используя обновление 201 версии JDK. Выполняя тест, имитирующий типичные сценарии использования приложенияЯ заметил, что после нескольких часов непрерывной записи звука была введена внезапная задержка где-то между одной и двумя секундами.До этого момента не было заметной задержки.Только после этой критической точки записи в течение нескольких часов эта задержка начала возникать.
Что я пробовал до сих пор
Чтобы проверить, есть ли мой код для синхронизации записи аудиоОбразцы не так, я прокомментировал все, что касается времени.Это оставило меня по существу с этим циклом обновления, который выбирает аудиосэмплы, как только они будут готовы (Примечание: код Котлина):
while (!isInterrupted) {
val audioData = read(sampleSize, false)
listener.audioFrameCaptured(audioData)
}
Это мой метод чтения:
fun read(samples: Int, buffered: Boolean = true): AudioData {
//Allocate a byte array in which the read audio samples will be stored.
val bytesToRead = samples * format.frameSize
val data = ByteArray(bytesToRead)
//Calculate the maximum amount of bytes to read during each iteration.
val bufferSize = (line.bufferSize / BUFFER_SIZE_DIVIDEND / format.frameSize).roundToInt() * format.frameSize
val maxBytesPerCycle = if (buffered) bufferSize else bytesToRead
//Read the audio data in one or multiple iterations.
var bytesRead = 0
while (bytesRead < bytesToRead) {
bytesRead += (line as TargetDataLine).read(data, bytesRead, min(maxBytesPerCycle, bytesToRead - bytesRead))
}
return AudioData(data, format)
}
Однако, даже без какого-либо времени с моей стороны проблема не была решена.Поэтому я немного поэкспериментировал и позволил приложению работать в разных аудиоформатах, что приводит к очень запутанным результатам (я собираюсь использовать 16-битный стереофонический аудиоформат PCM со слабым порядком байтов и частотой дискретизации 44100,0 Гц.по умолчанию, если не указано иное):
- Критическое количество времени, которое должно пройти, прежде чем появится задержка, кажется различным в зависимости от используемой машины.На моем настольном ПК с Windows 10 оно составляет от 6,5 до 7 часов.На моем ноутбуке (также использующем Windows 10), однако, для одного и того же формата аудио это где-то между 4 и 5 часами.
- Количество используемых аудиоканалов, кажется, оказывает влияние.Если я изменю количество каналов со стерео на моно, время до появления задержки удваивается до примерно 13-13,5 часов на моем рабочем столе.
- Уменьшается также размер выборки с 16 до 8 бит.приводит к удвоению времени до начала задержки.Где-то между 13 и 13,5 часами на моем рабочем столе.
- Изменение порядка байтов с младшего к старшему не имеет никакого эффекта.
- Переключение со стереомикса на физический микрофон также не действует.
- Я попытался открыть строку, используя разные размеры буфера (1024, 2048 и 3072 кадра выборки), а также размер буфера по умолчанию.Это также ничего не изменило.
- Очистка TargetDataLine после задержка начала происходить, в результате чего все байты становятся равными нулю в течение приблизительно одной-двух секунд.После этого я снова получаю ненулевые значения.Задержка, однако, все еще там.Если я очищаю линию до критической точки, я не получаю эти нулевые байты.
- Остановка и перезапуск TargetDataLine после задержка также не появляетсяизменить что-либо.
- Однако закрытие и повторное открытие TargetDataLine избавляет от задержки, пока она не появится снова через несколько часов.
- Автоматическая очистка внутреннего буфера TargetDataLines каждые десять минут не приводит кпомогите решить вопрос.Следовательно, переполнение буфера во внутреннем буфере, по-видимому, не является причиной.
- Использование параллельного сборщика мусора во избежание зависания приложения также не помогает.
- Кажется, использованная частота дискретизацииважный.Если я удвою частоту дискретизации до 88200 Гц, задержка начнет происходить где-то между 3 и 3,5 часами времени выполнения.
- Если я позволю ему работать под Linux, используя мой аудиоформат по умолчанию, он все равно будет работать нормально примерно после9 часов работы.
Выводы, которые я сделал:
Эти результаты позволяют мне прийти к выводу, что время, в течение которого я могу записывать звук до того, как эта проблема начинает возникать, зависит от компьютера, на котором запущено приложение, и от скорости передачи в байтах (т. Е. Размера кадра и частоты дискретизации. ) аудио формата. Это кажется верным (хотя я не могу полностью подтвердить это на данный момент), потому что, если я объединю изменения, сделанные в 2 и 3, я бы предположил, что я могу записывать аудиосэмплы в четыре раза дольше (что может быть где-то между 26 и 27 часами), как при использовании моего аудио формата «по умолчанию» до того, как задержка начинает появляться. Поскольку я не нашел времени, чтобы приложение могло работать так долго, я могу только сказать, что оно работало нормально в течение примерно 15 часов, прежде чем мне пришлось остановить его из-за нехватки времени на моей стороне. Таким образом, эту гипотезу еще предстоит подтвердить или опровергнуть.
Согласно результату пункта 13, кажется, что вся проблема возникает только при использовании Windows. Поэтому я думаю , что это может быть ошибкой в специфичных для платформы частях javax.sound.sampled API.
Хотя я думаю, что мог бы найти способ измениться, когда эта проблема начинает возникать, я не удовлетворен результатом. Я мог бы периодически закрывать и открывать линию, чтобы проблема вообще не появлялась. Однако выполнение этого приведет к небольшому произвольному промежутку времени, когда я не смогу захватывать аудиосэмплы. Кроме того, в Javadoc говорится, что некоторые строки вообще не могут быть открыты после закрытия. Поэтому в моем случае это не очень хорошее решение.
В идеале, этой проблемы вообще не должно быть. Есть ли что-то, чего я полностью упускаю, или я испытываю ограничения того, что возможно с API javax.sound.sampled? Как мне вообще избавиться от этой проблемы?
Редактировать: По предложению Xtreme Biker и gidds я создал небольшой пример приложения. Вы можете найти его в этом репозитории Github .