Иллюстрированный самоучитель по Java
Проигрывание
звука в Java 2
Проигрыватель звука, встроенный
в JVM, рассчитан на два способа записи звука: моно и стерео оцифровку (digital
audio) с частотой дискретизации (sample rate) от 8 000 до 48 000 Гц и аппроксимацией
(quantization) 8 и 16 битов, и MIDI-последовательности (sequences) типа 0 и
1.
Оцифрованный звук должен храниться
в файлах типа AU, WAVE и AIFF. Его можно проигрывать двумя способами.
Первый способ описан в интерфейсе
clip. Он рассчитан на воспроизведение небольших файлов или неоднократное проигрывание
файла и заключается в том, что весь файл целиком загружается в оперативную память,
а затем проигрывается.
Второй способ описан в интерфейсе
SourceDataLine. Согласно этому способу файл загружается в оперативную память
по частям в буфер, размер которого можно задать произвольно.
Перед загрузкой файла надо задать
формат записи звука в объекте класса AudioFormat. Конструктор этого класса:
AudioFormat(float
sampleRate, int sampleSize, int channels, boolean signed, boolean bigEndian)
требует знания частоты дискретизации
sampleRate (по умолчанию 44 100 Гц), аппроксимации sampleSize, заданной в битах
(по умолчанию 16), числа каналов channels (1 — моно, по умолчанию 2 — стерео),
запись чисел со знаком, signed == true, или без знака, и порядка расположения
байтов в числе bigEndian. Такие сведения обычно неизвестны, поэтому их получают
косвенным образом из файла. Это осуществляется в два шага.
На первом шаге получаем формат файла
статическим методом getAudioFiieFormato класса AudioSystem, на втором — формат
записи звука методом getFormato класса AudioFiieFormat. Это описано в листинге
15.15. После того как формат записи определен и занесен в объект класса AudioFormat,
в объекте класса DataLine. infо собирается информация о входной линии (line)
и способе проигрывания clip или SourceDataLine. Далее следует проверить, сможет
ли проигрыватель обслуживать линию с таким форматом. Затем надо связать линию
с проигрывателем статическим методом getLine () класса AudioSystem. Потом создаем
поток данных из файла — объект класса Audioinputstream. Из этого потока тоже
можно извлечь объект класса AudioFormat методом getFormat (). Данный вариант
выбран в листинге 15.16. Открываем созданный поток методом орепо.
У-фф! Все готово, теперь можно начать
проигрывание методом start (), завершить методом stop(), "перемотать"
в начало методом setFramePosition(0) ИЛИ setMillisecondPosition(0).
Можно задать проигрывание п раз
подряд методом loop(n) или бесконечное число раз методом
loop
(Clip.LOOP_CONTINUOUSLY)
. Перед этим необходимо установить начальную
n и конечную m позиции повторения методом
setLoopPoints(n, m).
По окончании проигрывания следует
закрыть линию методом close ().
Вся эта последовательность действий
показана в листинге 15.15.
Листинг 15.15.
Проигрывание аудиоклипа
import
javax.sound.sampled.*;
import java.io.*;
class PlayAudio{
PlayAudio(String s){
play(s);
}
public void play(String
file){
Clip line = null;
try{
// Создаем объект,
представляющий файл
File f = new
File (file);
// Получаем информацию
о способе записи файла
AudioFileFormat
aff = AudioSystem.getAudioFileFormat(f);
// Получаем информацию
о способе записи звука
AudioFormat af
= aff.getFormat();
// Собираем всю
информацию вместе,
// добавляя сведения
о классе
Class DataLine.Infо
info = new DataLine.Info(Clip.class, af) ;
// Проверяем,
можно ли проигрывать такой формат
if (!AudioSystem.isLineSupported(info)){
System.err.printlnt"Line
is not supported");
System.exit(0);
}
// Получаем линию
связи с файлом
line = (Clip)AudioSystem.getLine(info);
// Создаем поток
байтов из файла
AudioInputStream
ais - AudioSystem.getAudioInputStream(f);
// Открываем линию
line.open(ais);
}catch(Exception
e){
System.err.println(e);
}
// Начинаем проигрывание
line.start();
// Здесь надо
сделать задержку до окончания проигрывания
// или остановить
его следующим методом:
line.stop();
//По окончании
проигрывания закрываем линию
line.close();
}
public static
void main(String[] args){
if (args.length
!= 1)
System.out.printlnt"Usage:
Java PlayAudio filename");
new PlayAudio(args[0]);
}
}
Как видите, методы Java Sound API
выполняют элементарные действия, которые надо повторять из программы в программу.
Как говорят, это методы "низкого уровня" (low level).
Второй способ, использующий методы
интерфейса SourceDataLine, требует предварительного создания буфера произвольного
размера.
Листинг 15.16.
Проигрывание аудиофайла
import javax.sound.sampled.*;
import j ava.io.*;
class PlayAudioLine(
PlayAudioLine(String
s){
play(s);
}
public void play(String
file){
SourceDataLine
line = null;
AudioInputStream
ais = null;
byte[] b = new
byte[2048]; // Буфер данных
try{
File f = new
File(file);
// Создаем входной
поток байтов из файла f
ais = AudioSystem.getAudioInputStream(f);
// Извлекаем из
потока информацию о способе записи звука
AudioFormat af
= ais.getFormat () ;
// Заносим эту
информацию в объект info
DataLine.Infо
info = new DataLine.Infо(SourceDataLine.class, af);
// Проверяем,
приемлем ли такой способ записи звука
if (!AudioSystem.isLineSupported(info)){
System.err.println("Line
is not supported");
System.exit(0);
}
// Получаем входную
линию
line = (SourceDataLine)AudioSystem.getLine(info);
// Открываем линию
line.open(af);
// Начинаем проигрывание
line.start();
// Ждем появления данных в буфере int num = 0;
// Раз за разом
заполняем буфер
while(( num =
ais.read(b)) != -1)
line.write(b,
0, num);
// "Сливаем"
буфер, проигрывая остаток файла
line.drain();
// Закрываем поток
ais.close();
} catch (Exception
e) {
System, err.println
(e);
}
// Останавливаем
проигрывание
line.stop();
// Закрываем линию
line.close();
}
public static
void main(String[] args){
String s = "mrmba.aif";
if (args.length
> 0) s = args[0];
new PlayAudioLine(s)
;
}
}
Управлять проигрыванием файла можно
с помощью событий. Событие класса LineEvent происходит при открытии, OPEN, и
закрытии, CLOSE, потока, при начале, START, и окончании, STOP, проигрывания.
Характер события отмечается указанными константами. Соответствующий интерфейс
LineListener описывает только один метод update ().
В MIDI-файлах хранится
последовательность
(sequence) команд для
секвен-сора
(sequencer) — устройства для записи,
проигрывания и редактирования MlDI-последовательности, которым может быть физическое
устройство или программа. Последовательность состоит из нескольких
дорожек
(tracks), на которых записаны
MIDI-события
(events). Каждая дорожка
загружается в своем
канале
(channel). Обычно дорожка содержит звучание
одного музыкального инструмента или запись голоса одного исполнителя или запись
нескольких исполнителей, микшированную
синтезатором
(synthesizer).
Для проигрывания MIDI-последовательности
в простейшем случае надо создать экземпляр секвенсора, открыть его и направить
в него последовательность, извлеченную из файла, как показано в листинге 15.17.
После этого следует начать проигрывание методом start (). Закончить проигрывание
можно методом stop(), "перемотать" последовательность на начало записи
или на указанное время проигрывания — методами setMicrosecondPositionflong mcs)
или setTickPosition(long tick).
Листинг 15.17.
Проигрывание MIDI-последовательности
import javax.sound.midi.*;
import j ava.io.*;
class PlayMIDK
PlayMIDKString
s) {
play(s);
}
public void play(String
file){
try{
File f = new File(file);
// Получаем секвенсор
по умолчанию
Sequencer sequencer
= MidiSystem.getSequencerО;
// Проверяем,
получен ли секвенсор
if (sequencer
= null) {
System.err.println("Sequencer
is not supported");
System.exit(0);
}
// Открываем секвенсор
sequencer.open();
// Получаем MIDI-последовательность
из файла
Sequence seq
= MidiSystem.getSequence(f);
// Направляем
последовательность в секвенсор
sequencer.setSequence(seq);
// Начинаем проигрывание
sequencer.start();
// Здесь надо
сделать задержку на время проигрывания,
// а затем остановить:
sequencer.stop();
)catch(Exception
e){
System.err.println(e);
}
}
public static
void main(String[] args){
String s = "doom.mid";
if (args.length
> 0) s = args[0];
new PlayMIDI(s);
}
}