След: Звук
Воспроизведение Аудио
Домашняя страница > Звук

Воспроизведение Аудио

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

Ранее Вы видели, как получить строку из аудиосистемы или из микшера. Здесь Вы изучите, как играть звук через строку.

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

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

Вы получаете a Clip как описано ранее при Получении Строки Требуемого Типа; Создайте 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. Открытие SourceDataLine также подобно открытию a Clip, в этом цель состоит в том, чтобы еще раз зарезервировать строку. Однако, Вы используете различный метод, наследованный от DataLine:

void open(AudioFormat format)

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

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

void open(AudioFormat format, int bufferSize)

Для непротиворечивости с подобными методами, bufferSize параметр выражается в байтах, но он должен соответствовать целому числу фреймов.

Вместо того, чтобы использовать открытый метод, описанный выше, также возможно открыть 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 задержка между моментом, что исходная строка поставляет данные микшеру и момент, что микшер поставляет данные своей цели, является negligible⠀” то есть, намного меньше чем время одной выборки.) Это 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.

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

Как только Вы запустили звуковую игру, как Вы находите, когда она заканчивается? Мы видели одно решение above⠀” вызов drain метод после записи последнего буфера data⠀”, но тот подход применим только к 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 событие. (См. предыдущее обсуждение Выбора Портов Ввода и вывода.)

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

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

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

boolean isSynchronizationSupported(Line[] lines, boolean  maintainSync)

Первый параметр определяет группу определенных строк данных, и второй параметр указывает на точность, с которой должна сохраняться синхронизация. Если второй параметр true, запрос спрашивает, способен ли микшер к поддержанию демонстрационно-точной точности в управлении указанными строками всегда; иначе, точная синхронизация требуется только во время запуска и операций остановки, не всюду по воспроизведению.

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

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


Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Доступ к Ресурсам Аудиосистемы
Следующая страница: Получение Аудио



Spec-Zone.ru - all specs in one place