Захват - это процесс получения сигнала извне компьютера. Распространенным применением аудиозахвата является запись, например запись звука с микрофона в звуковой файл. Однако захват не является синонимом записи, поскольку запись подразумевает, что приложение всегда сохраняет входящие звуковые данные. Приложение, которое захватывает звук, не обязательно сохраняет звук. Вместо этого он может что-то сделать с данными по мере их поступления - например, преобразовать речь в текст - но затем отбросить каждый буфер звука, как только он закончит работу с этим буфером.

Как обсуждалось в главе 2, "Обзор пакета Samples", типичная система аудиовхода в реализации Java Sound API состоит из:

  1. Входной порт, например порт микрофона или линейный вход, который передает входящие аудиоданные в:
  2. Микшер, который помещает входные данные в:
  3. Одна или несколько целевых линий данных, из которых приложение может получать данные.

    Обычно одновременно может быть открыт только один входной порт, но также возможен микшер аудиовхода, который микширует звук с нескольких портов. Другой сценарий состоит из микшера, который не имеет портов, но вместо этого получает аудиовход по сети.

    Интерфейс TargetDataLine был кратко представлен в разделе "Иерархия линейного интерфейса" в главе 2 "Обзор пакета Samples." TargetDataLine прямо аналогичен интерфейсу SourceDataLine, который подробно обсуждался в главе 4 "Воспроизведение аудио." Напомним, что интерфейс SourceDataLine состоит из:

  • Метод write для отправки звука в микшер
  • Метод available определения того, сколько данных можно записать в буфер без блокировки

Аналогичным образом TargetDataLine состоит из:

  • Метод read для получения звука из микшера
  • Метод available определения того, сколько данных можно прочитать из буфера без блокировки

Настройка TargetDataLine

Процесс получения целевой линии данных был описан в главе 3 "Доступ к ресурсам аудиосистемы", но мы повторяем его здесь для удобства:

TargetDataLine line;
DataLine.Info info = new DataLine.Info(TargetDataLine.class, 
    format); // формат - это объект AudioFormat
if (!AudioSystem.isLineSupported(info)) {
    // Обработка ошибкок ...
}
// Получение и открытие линии.
try {
    line = (TargetDataLine) AudioSystem.getLine(info);
    line.open(format);
} catch (LineUnavailableException ex) {
    // Обработка ошибкок ...
}

Вместо этого вы можете вызвать метод getLine класса Mixer, а не класса AudioSystem.

Как показано в этом примере, как только вы получили целевую линию данных, вы зарезервируете ее для использования в приложении, вызвав метод  open класса DataLine, точно так же, как было описано в случае линии исходных данных в главе 4, "Воспроизведение аудио". Однопараметрическая версия метода open заставляет буфер линии иметь размер по умолчанию. Вместо этого вы можете установить размер буфера в соответствии с потребностями вашего приложения, вызвав версию с двумя параметрами:

    void open(AudioFormat format, int bufferSize) 

При выборе размера буфера помните о компромиссе между задержками, вызываемыми длинными буферами, с одной стороны, и риском прерывания звука, если буферы настолько короткие, что вы не можете получить данные достаточно быстро, на с другой стороны. При захвате звука вы рискуете переполнить данные, если не извлечете данные из заполненных буферов достаточно быстро. Если происходит переполнение, некоторые захваченные данные будут отброшены, что, вероятно, вызовет слышимые щелчки и пропуски звука. Это противоположная ситуация по сравнению с воспроизведением, при которой существует риск потери данных, что может привести к пропускам звука. (См. Главу 4,"Воспроизведение аудио" , для получения дополнительной информации о выборе размеров буфера.)

Чтение данных из TargetDataLine

Как только линия открыта, она готова начать сбор данных, но еще не активна. Чтобы фактически начать захват звука, используйте метод start класса DataLine. Это начинает доставку входных аудиоданных в буфер линии для чтения вашим приложением. Ваше приложение должно вызывать start только тогда, когда оно готово начать чтение из линии; в противном случае много обработки тратится на заполнение буфера захвата только для того, чтобы он переполнялся (то есть отбрасывал данные).

Чтобы начать получение данных из буфера, вызовите метод read  класса TargetDataLine's:

int read(byte[] b, int offset, int length) 

Этот метод пытается считать байты данных длины length в массив b, начиная со смещения offset байтовой позиции в массиве. Метод возвращает количество фактически прочитанных байтов.

Как и в случае с методом write класса SourceDataLine, вы можете запросить больше данных, чем фактически помещается в буфере, потому что метод блокируется до тех пор, пока запрошенный объем данных не будет доставлен, даже если вы запрашиваете данные на много буферов.

Чтобы избежать зависания вашего приложения во время записи, вы можете вызвать метод read в цикле, пока не получите весь аудиовход, как в этом примере:

// Предположим, что линия TargetDataLine
// уже получена и открыта.
ByteArrayOutputStream out  = new ByteArrayOutputStream();
int numBytesRead;
byte[] data = new byte[line.getBufferSize() / 5];

// Начните запись звука.
line.start();

// Здесь stopped - это глобальное логическое значение, установленное другим потоком.
while (!stopped) {
   // Прочтите следующий фрагмент данных из TargetDataLine.
   numBytesRead =  line.read(data, 0, data.length);
   // Сохраните этот кусок данных.
   out.write(data, 0, numBytesRead);
}

Обратите внимание, что в этом примере размер массива байтов, в который считываются данные, установлен равным одной пятой размера буфера линии. Если вместо этого вы сделаете его размером с линейный буфер и попытаетесь прочитать весь буфер, вам нужно будет очень точно рассчитать время, потому что данные будут сбрасываться, если микшер должен доставить данные в линию, пока вы читаете из нее. . Используя некоторую часть размера буфера линии, как показано здесь, ваше приложение сможет более успешно использовать доступ к буферу линии с микшером.

Метод read класса TargetDataLine принимает три аргумента: массив байтов, смещение в массиве и количество байтов входных данных, которые вы хотите прочитать. В этом примере третий аргумент - это просто длина вашего байтового массива. Метод read возвращает количество байтов, которые были фактически считаны в ваш массив.

Обычно вы читаете данные из линии в цикле, как в этом примере. В цикле while каждый фрагмент извлеченных данных обрабатывается любым способом, подходящим для приложения - здесь он записывается в ByteArrayOutputStream. Здесь не показано использование отдельного потока для логического значения stopped, которое завершает цикл. Это логическое значение может иметь значение true , когда пользователь нажимает кнопку Stop, а также когда слушатель получает событие CLOSE или STOP из линии. Прослушиватель необходим для событий CLOSE и рекомендуется для событий STOP. В противном случае, если линия каким-то образом останавливается, не переставая быть установленным в true, цикл while будет захватывать нулевые байты на каждой итерации, работая быстро и тратя циклы ЦП. Более подробный пример кода покажет, что цикл повторяется, если захват снова становится активным.

Как и в случае со линией исходных данных, можно слить или очистить целевую линию данных. Например, если вы записываете ввод в файл, вы, вероятно, захотите вызвать метод drain , когда пользователь нажимает кнопку Stop. Метод drain приведет к тому, что оставшиеся данные микшера будут доставлены в буфер целевой линии данных. Если не слить данные, может показаться, что захваченный звук в конце преждевременно обрезается.

В некоторых случаях вам может потребоваться очистить данные. В любом случае, если не промыть и не слить данные, они останутся в микшере. Это означает, что при возобновлении захвата в начале новой записи будет оставшийся звук, что может быть нежелательно. В таком случае может быть полезно очистить целевую линию данных перед перезапуском захвата.

Мониторинг статуса линии

Поскольку интерфейс TargetDataLine расширяет DataLine, целевые линии данных генерируют события так же, как и исходные линии данных. Вы можете зарегистрировать объект для получения событий всякий раз, когда целевая линия данных открывается, закрывается, запускается или останавливается. Дополнительные сведения см. В разделе "Мониторинг статуса линии" в главе 4 "Воспроизведение аудио".

Обработка входящего звука

Как и некоторые исходные линии данных, целевые линии данных некоторых микшеров имеют элементы управления обработкой сигналов, такие как усиление, панорамирование, реверберация или управление частотой дискретизации. Входные порты могут иметь аналогичные элементы управления, особенно элементы управления усилением. Дополнительные сведения о том, как определить, есть ли в линии такие элементы управления, и как их использовать, если они есть, см. В главе 6, "Обработка звука с помощью элементов управления".

 

Предыдущая Следующая