Spec-Zone .ru
спецификации, руководства, описания, API
|
Воспроизведение иногда упоминается как представление или рендеринг. Они - общие термины, которые применимы к другим видам носителей помимо звука. Существенная особенность - то, что последовательность данных поставляется куда-нибудь для возможного восприятия пользователем. Если данные основаны на времени, как звук, это должно быть поставлено на корректном уровне. Со звуком даже больше чем видео, важно, чтобы уровень потока данных сохранялся, потому что прерывания к воспроизведению звука часто производят громкие щелчки или раздражающее искажение. API Звука Java разрабатывается, чтобы помочь звукам игры прикладных программ гладко и непрерывно, даже очень длинные звуки.
Предыдущая глава, обсужденная, как получить строку из аудиосистемы или из микшера. Эта глава показывает, как играть звук через строку.
Есть два вида строки, которую можно использовать для того, чтобы играть звук: a Clip
и a SourceDataLine
. Эти два интерфейса были представлены кратко под "Иерархией Интерфейса Строки" в Главе 2, "Краткий обзор Выбранного Пакета." Главное различие между этими двумя - это с a Clip
Вы определяете все звуковые данные когда-то, перед воспроизведением, тогда как с a SourceDataLine
Вы продолжаете писать новые буферы данных непрерывно во время воспроизведения. Хотя есть много ситуаций, в которых Вы могли использовать любого a Clip
или a SourceDataLine
, следующие критерии помогают идентифицировать, какой вид строки лучше подходит для определенной ситуации:
Clip
когда у Вас есть звуковые данные нев реальном времени, которые могут быть предварительно загружены в память.
Например, Вы могли бы считать короткий звуковой файл в клип. Если Вы хотите, чтобы звук воспроизвел не раз, a Clip
более удобно чем a SourceDataLine
, особенно, если Вы хотите, чтобы воспроизведение циклично выполнилось (цикл неоднократно через все или часть звука). Если Вы должны запустить воспроизведение в произвольной позиции в звуке, Clip
интерфейс обеспечивает метод, чтобы сделать это легко. Наконец, воспроизведение от a Clip
обычно имеет меньше задержки чем буферизованное воспроизведение от a SourceDataLine
. Другими словами, потому что звук предварительно загружается в клип, воспроизведение может сразу запуститься вместо того, чтобы иметь необходимость ожидать буфера, чтобы быть заполненным.
SourceDataLine
для того, чтобы передать потоком данные, такие как долгий звуковой файл, который не будет все умещаться в памяти сразу, или звук, данные которого не могут быть известны перед воспроизведением.
Как пример последнего случая, предположите, что Вы контролируете звуковой ввод — то есть, воспроизводя звук, поскольку это получается. Если у Вас нет микшера, который может отослать входное право аудио назад выходной порт, Ваша прикладная программа должна будет взять полученные данные и отправить их микшеру звукового выхода. В этом случае, a SourceDataLine
является более соответствующим чем a Clip
. Другой пример звука, который не может быть известен заранее, происходит, когда Вы синтезируете или управляете звуковыми данными в интерактивном режиме в ответ на ввод пользователя. Например, вообразите игру, которая дает слуховую обратную связь, "превращаясь" от одного звука до другого, поскольку пользователь перемещает мышь. Динамический характер звукового преобразования требует, чтобы прикладная программа обновила звуковые данные непрерывно во время воспроизведения, вместо того, чтобы предоставить все это прежде, чем воспроизведение запустится.
Вы получаете 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
тот, который в настоящий момент играет звук.
Получение a SourceDataLine
подобно получению a Clip
. См. "Получение Строки Требуемого Типа" в Главе 3, "Получая доступ к Ресурсам Аудиосистемы."
Открытие 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
поведение блокирования метода:
DataLine's
available
возвраты метода.
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, "Обрабатывая Аудио со Средствами управления."