Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

<Содержание

Глава 4: Воспроизведение Аудио

Воспроизведение иногда упоминается как представление или рендеринг. Они - общие термины, которые применимы к другим видам носителей помимо звука. Существенная особенность - то, что последовательность данных поставляется куда-нибудь для возможного восприятия пользователем. Если данные основаны на времени, как звук, это должно быть поставлено на корректном уровне. Со звуком даже больше чем видео, важно, чтобы уровень потока данных сохранялся, потому что прерывания к воспроизведению звука часто производят громкие щелчки или раздражающее искажение. API Звука Java разрабатывается, чтобы помочь звукам игры прикладных программ гладко и непрерывно, даже очень длинные звуки.

Предыдущая глава, обсужденная, как получить строку из аудиосистемы или из микшера. Эта глава показывает, как играть звук через строку.

Есть два вида строки, которую можно использовать для того, чтобы играть звук: a Clip и a SourceDataLine. Эти два интерфейса были представлены кратко под "Иерархией Интерфейса Строки" в Главе 2, "Краткий обзор Выбранного Пакета." Главное различие между этими двумя - это с a Clip Вы определяете все звуковые данные когда-то, перед воспроизведением, тогда как с a SourceDataLine Вы продолжаете писать новые буферы данных непрерывно во время воспроизведения. Хотя есть много ситуаций, в которых Вы могли использовать любого a Clip или a SourceDataLine, следующие критерии помогают идентифицировать, какой вид строки лучше подходит для определенной ситуации:

Используя Клип

Вы получаете a Clip как описано ранее при "Получении Строки Требуемого Типа" в Главе 3, "Получая доступ к Ресурсам Аудиосистемы": Создайте a DataLine.Info объект с Clip.class для первого параметра, и передачи это DataLine.Info как параметр getLine метод AudioSystem или Mixer.

Установка Клипа для Воспроизведения

Получение строки только означает, что Вы получили способ обратиться к нему; getLine фактически не резервирует строку для Вас. Поскольку у микшера могло бы быть ограниченное количество строк требуемого доступного типа, это может произойти это после того, как Вы вызываете getLine чтобы получить клип, другая прикладная программа вскакивает и захватывает клип прежде, чем Вы будете готовы запустить воспроизведение. Чтобы фактически использовать клип, Вы должны зарезервировать это для монопольного использования своей программы, вызывая один из следующих Clip методы:

   void open(AudioInputStream stream) 
   void open(AudioFormat format, byte[] data, int offset,
   int bufferSize)
Несмотря на bufferSize параметр во втором open метод выше, Clip (в отличие от этого SourceDataLine) не включает методов для того, чтобы записать новые данные в буфер. bufferSize параметр здесь только определяет сколько из байтового массива, чтобы загрузиться в клип. Это не буфер, в который можно впоследствии загрузить больше данных, как Вы можете с a SourceDataLine's буфер.

После открытия клипа можно определить в том, какая точка в данных это должно запустить воспроизведение, используя Clip's setFramePosition или setMicroSecondPosition методы. Иначе, это запустится вначале. Можно также сконфигурировать воспроизведение, чтобы неоднократно циклически повторяться, используя setLoopPoints метод.

Запуск и Остановка Воспроизведения

Когда Вы готовы запустить воспроизведение, просто вызовите start метод. Чтобы остановить или приостановить клип, вызовите stop метод, и возобновлять воспроизведение, вызывает start снова. Клип помнит позицию носителей, где это остановило воспроизведение, таким образом нет никакой потребности в явной паузе и методах резюме. Если Вы не хотите, чтобы это возобновило, где это кончило, можно "перемотать" клип к началу (или к любой другой позиции, в этом отношении) использование фрейма - или располагающие микросекунду упомянутые выше методы.

A Clip's громкость звука и состояние действия (активный против неактивного) могут контролироваться, вызывая DataLine методы getLevel и isActive, соответственно. Активное Clip тот, который в настоящий момент играет звук.

Используя SourceDataLine

Получение a SourceDataLine подобно получению a Clip. См. "Получение Строки Требуемого Типа" в Главе 3, "Получая доступ к Ресурсам Аудиосистемы."

Установка SourceDataLine для Воспроизведения

Открытие SourceDataLine также подобно открытию a Clip, в этом цель состоит в том, чтобы еще раз зарезервировать строку. Однако, Вы используете различный метод, наследованный от DataLine:

    void open(AudioFormat format) 
Заметьте это, когда Вы открываете a SourceDataLine, Вы еще не связываете звуковых данных со строкой, в отличие от открытия a Clip. Вместо этого Вы только определяете формат аудиоданных, которые Вы хотите играть. Система выбирает буферную длину по умолчанию.

Можно также предусмотреть определенную буферную длину в байтах, используя эту разновидность:

    void open(AudioFormat format, int bufferSize) 
Для непротиворечивости с подобными методами, buffersize параметр выражается в байтах, но он должен соответствовать целому числу фреймов.

Как Вы выбрали бы размер буфера? Это зависит от потребностей Вашей программы.

Чтобы запуститься с, более короткие буферные размеры означают меньше задержки. Когда Вы отправляете новые данные, Вы слышите это скорее. Для некоторых прикладных программ, особенно очень интерактивных, этот вид скорости отклика важен. Например, в игре, начало воспроизведения, возможно, должно было бы плотно синхронизироваться с визуальным событием. Таким программам, возможно, понадобилась бы задержка меньше чем 0.1 секунд. Как другой пример, приложение конференц-связи должно избежать задержек и воспроизведения и получения. Однако, много прикладных программ могут предоставить большую задержку, до секунды или больше, потому что не имеет значения точно, когда звук начинает играть, пока задержка не смущает или раздражает пользователя. Это могло бы иметь место для прикладной программы, что потоки большой аудиофайл, используя односекундный буферизуют. Пользователь, вероятно, не будет заботиться, занимает ли воспроизведение секунду, чтобы запуститься, потому что сам звук длится так долго, и опыт не является очень интерактивным.

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

Таким образом, оптимальное значение для размера буфера является тем, которое минимизирует задержку только до градуса, это является приемлемым для Вашей прикладной программы, сохраняя это достаточно большой, чтобы уменьшить риск недостаточного наполнения буфера и избежать ненужного потребления ресурсов ЦП. Для программы как приложение конференц-связи задержки являются более раздражающими чем звук низкого качества, таким образом, маленький размер буфера предпочтителен. Для того, чтобы передать музыку потоком, начальная задержка является приемлемой, но разрывы в звуке не. Таким образом для того, чтобы передать музыку потоком больший размер буфера — говорит, секунда — предпочтительна. (Отметьте, что высокие демонстрационные уровни делают буферы более крупными с точки зрения числа байтов, которые являются модулями для того, чтобы измерить размер буфера в DataLine API.)

Вместо того, чтобы использовать открытый метод, описанный выше, также возможно открыть a SourceDataLine использование Line's open() метод, без параметров. В этом случае строка открывается с ее аудиоформатом по умолчанию и размером буфера. Однако, невозможно изменить их позже. Если Вы хотите знать аудиоформат строки по умолчанию и размер буфера, можно вызвать DataLine's getFormat и getBufferSize методы, даже прежде, чем строка когда-либо открывалась.

Запуск и Остановка Воспроизведения

Однажды SourceDataLine открыто, можно начать играть звук. Вы делаете это, вызывая DataLine's запустите метод, и затем запись данных неоднократно к буферу воспроизведения строки.

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

    int write(byte[] b, int offset, int length) 
Смещение в массив выражается в байтах, как длина массива.

Строка начинает отправлять данные как можно скорее его микшеру. Когда сам микшер поставляет данные своей цели, SourceDataLine генерирует a START событие. (В типичной реализации API Звука Java задержка между моментом, что исходная строка поставляет данные микшеру и момент, что микшер поставляет данные своей цели, незначительна — то есть, намного меньше чем время одной выборки.) Это START событие отправляется слушателям строки, как объяснено ниже при "Контроле Состояния Строки." Строку теперь считают активной, таким образом, isActive метод DataLine возвратится true. Заметьте, что все это происходит только, как только буфер содержит данные, чтобы играть, не обязательно правильный, когда метод запуска вызывается. Если Вы вызвали start на новом SourceDataLine но никогда не писал данные в буфер, строка никогда не будет активной и a START событие никогда не отправлялось бы. (Однако, в этом случае, isRunning метод DataLine возвратился бы true.)

Так, как Вы знаете, сколько данных, чтобы записать в буфер, и когда отправить второй пакет данных? К счастью, Вы не нуждаетесь ко времени во втором вызове записи, чтобы синхронизироваться с концом первого буфера! Вместо этого можно использовать в своих интересах write поведение блокирования метода:

Вот пример итерации через блоки данных, которые читаются из потока, пишущий один блок за один раз в SourceDataLine для воспроизведения:
// read chunks from a stream and write them to a source data 
line 
line.start();
while (total < totalToRead && !stopped)}
    numBytesRead = stream.read(myData, 0, numBytesToRead);
    if (numBytesRead == -1) break;
    total += numBytesRead; 
    line.write(myData, 0, numBytesRead);

}
Если Вы не хотите write метод, чтобы блокировать, можно сначала вызвать available метод (в цикле), чтобы узнать, сколько байтов может быть записано без блокирования, и затем ограничить numBytesToRead переменная к этому числу, прежде, чем читать из потока. В примере, данном, тем не менее, блокирование, не будет иметь значения очень, так как метод записи вызывается в цикле, который не будет завершаться, пока последний буфер не пишется в заключительной итерации цикла. Используете ли Вы метод блокирования, Вы будете, вероятно, хотеть вызвать этот цикл воспроизведения в отдельном потоке от остальной части прикладной программы, так, чтобы Ваша программа, казалось, не заморозилась, играя длинный звук. На каждой итерации цикла можно протестировать, запросил ли пользователь воспроизведение остановиться. Такой запрос должен установить stopped булевская переменная, используемая в коде выше, к true.

С тех пор write возвраты перед всеми данными закончили играть, как Вы изучаете, когда воспроизведение фактически завершилось? Один путь состоит в том, чтобы вызвать drain метод DataLine после записи ценности последнего буфера данных. Этот метод блоки до всех данных игрался. Когда управление возвращается к Вашей программе, можно освободить строку, при желании, без страха перед преждевременным отключением воспроизведения любых аудиосэмплов:

line.write(b, offset, numBytesToWrite); 
//this is the final invocation of write
line.drain();
line.stop();
line.close();
line = null;
Можно преднамеренно остановить воспроизведение преждевременно, конечно. Например, прикладная программа могла бы предоставить пользователю Кнопку остановки. Вызвать DataLine's stop метод, чтобы сразу остановить воспроизведение, даже в середине буфера. Это листы любые неиграемые данные в буфере, так, чтобы, если Вы впоследствии вызываете start, воспроизведение возобновляет, где оно кончило. Если это не то, что Вы хотите произойти, можно отбросить данные, оставленные в буфере, вызывая flush.

A SourceDataLine генерирует a STOP событие всякий раз, когда поток данных был остановлен, инициировалась ли эта остановка методом утечки, методом остановки, или методом сброса, или потому что конец буфера воспроизведения был достигнут перед вызванной прикладной программой write снова обеспечить новые данные. A STOP событие не обязательно означает что stop метод был вызван, и это не обязательно означает что последующий вызов isRunning возвратится false. Это действительно, однако, означает это isActive возвратится false. (Когда start метод был вызван, isRunning метод возвратится true, даже если a STOP событие сгенерировано, и оно начнет возвращаться false только однажды stop метод вызывается.) Важно понять это START и STOP события соответствуют isActive, не к isRunning.

Контроль Состояния Строки

Как только Вы запустили звуковую игру, как Вы находите, когда она заканчивается? Мы видели одно решение выше — вызов drain метод после записи последнего буфера данных — но тот подход применим только к a SourceDataLine. Другой подход, который работает на обоих SourceDataLines и Clips, должен зарегистрироваться, чтобы получить уведомления от строки всякий раз, когда строка изменяет свое состояние. Эти уведомления сгенерированы в форме LineEvent объекты, из которых есть четыре типа: OPEN, CLOSE, START, и STOP.

Любой объект в Вашей программе, которая реализует LineListener интерфейс может зарегистрироваться, чтобы получить такие уведомления. Реализовывать LineListener интерфейс, объект просто нуждается в методе обновления, который берет a LineEvent параметр. Чтобы зарегистрировать этот объект как один из слушателей строки, Вы вызываете следующий Line метод:

public void addLineListener(LineListener listener)

Всякий раз, когда строка открывается, завершения, запускается, или остановки, она передается update обменивайтесь сообщениями всем его слушателям. Ваш объект может запросить LineEvent то, что это получает. Сначала Вы могли бы вызвать LineEvent.getLine чтобы удостовериться строка, которая остановилась, является той, о которой Вы заботитесь. В случае мы обсуждаем здесь, Вы хотите знать, заканчивается ли звук, таким образом, Вы видите ли LineEvent имеет тип STOP. Если это, Вы могли бы проверить текущую позицию звука, которая также сохранена в LineEvent объект, и сравнивает это с длиной звука (если известный), чтобы видеть, достигло ли это конца и не было остановлено некоторыми другими средствами (такими как щелчок пользователя Кнопка остановки, хотя Вы, вероятно, будете в состоянии решить что причина в другом месте в Вашем коде).

В том же направлении, если Вы должны знать, когда строка открывается, закрывается, или запускается, Вы используете тот же самый механизм. LineEvents сгенерированы различными видами строк, не только Clips и SourceDataLines. Однако, в случае a Port невозможно рассчитывать на то, чтобы заставлять событие узнать об открытом или закрытом состоянии строки. Например, a Port могло бы быть первоначально открытым, когда это создается, таким образом, Вы не вызываете open метод и Port никогда не генерирует OPEN событие. (См. "Порты Входа и выхода выбора" в Главе 3, "Получая доступ к Ресурсам Аудиосистемы.")

Синхронизация Воспроизведения на Многократных Строках

Если Вы воспроизводите многократные дорожки аудио одновременно, Вы, вероятно, хотите иметь их, все запускают и останавливаются в точно то же самое время. Некоторые микшеры облегчают это поведение с их synchronize метод, который позволяет Вам применять операции такой как open, close, start, и stop группе строк данных, используя единственную команду, вместо того, чтобы иметь необходимость управлять каждой строкой индивидуально. Кроме того степень точности, с которой операции применяются к строкам, управляема.

Чтобы узнать, предлагает ли определенный микшер эту функцию указанной группы строк данных, вызовите Mixer интерфейс isSynchronizationSupported метод:

boolean isSynchronizationSupported(Line[] lines, 
  boolean  maintainSync)
Первый параметр определяет группу определенных строк данных, и второй параметр указывает на точность, с которой должна сохраняться синхронизация. Если второй параметр true, запрос спрашивает, способен ли микшер к поддержанию демонстрационно-точной точности в управлении указанными строками всегда; иначе, точная синхронизация требуется только во время запуска и операций остановки, не всюду по воспроизведению.

Обработка Исходящего Аудио

У некоторых строк исходных данных есть обрабатывающие сигнал средства управления, такие как усиление, панорамирование, реверберация, и средства управления демонстрационного уровня. Подобные средства управления, особенно получите средства управления, мог бы присутствовать на выходных портах также. Для получения дополнительной информации по тому, как определить, есть ли у строки такие средства управления, и как использовать их, если она делает, см. Главу 6, "Обрабатывая Аудио со Средствами управления."

 


Oracle и/или его филиалы Авторское право © 1993, 2011, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами