Работа с аудиофайлами стандартными средствами Java

 

Среди стандартных средств для работы со звуком существует интерфейс AudioClip.

Экземпляр объекта, реализующего этот интерфейс можно получить методом getAudioClip(), который, кроме того, загружает звуковой файл, а затем пользоваться методами play(), loop() и stop() этого интерфейса для проигрывания музыки.

Для применения данного же приема в приложениях в класс Applet введен статический метод newAudioclp(URL address), загружающий звуковой файл, находящийся по адресу address, и возвращающий объект, реализующий интерфейс Audioclip. Его можно использовать для проигрывания звука в приложении, если конечно звуковая система компьютера уже настроена.

Наиболее популярными форматами, используемыми в приложениях или апплетах, являются WAV, AU и SND.

Ниже приведен код апплета.

package javaaudio1;

 

import java.applet.Applet;

import java.applet.AudioClip;

import java.net.MalformedURLException;

import java.net.URL;

import java.util.logging.Level;

import java.util.logging.Logger;

 

public class AudioApplet1 extends Applet

{

public void init()

{

// Создаем объект AudioClip

// с помощью метода getAudioClip класса Applet,

// в который подаем ссылку на аудиофайл.

AudioClip ac;

Try

{

ac = getAudioClip(new URL("http://localhost/audio/javaaudio.wav"));

ac.play();

}

catch (MalformedURLException ex)

{

Logger.getLogger(AudioApplet1.class.getName()).log(Level.SEVERE, null, ex);

}

}

}

 

В состав виртуальной машины Java, входящей в SUN J2SDK начиная с версии 1.3, включено устройство, проигрывающее звук, записанный в одном из форматов AU, WAVE, AIFF, MIDI, преобразующее, микширующее и записывающее звук в тех же форматах.

Для работы с этим устройством созданы классы, собранные в пакеты javax.sound.sampled, javax.sound.midi, javax.sound.sampled.spi и javax.sound.midi.spi. Перечисленный набор классов для работы со звуком получил название Java Sound API.

Проигрыватель звука, встроенный в 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.

После того как формат записи определен и занесен в объект класса AudioFormat, в объекте класса DataLine. infо собирается информация о входной линии (line) и способе проигрывания clip или SourceDataLine. Далее следует проверить, сможет ли проигрыватель обслуживать линию с таким форматом. Затем надо связать линию с проигрывателем статическим методом getLine () класса AudioSystem. Потом создать поток данных из файла — объект класса Audioinputstream. Из этого потока тоже можно извлечь объект класса AudioFormat методом getFormat ().

Можно задать проигрывание n раз подряд методом loop(n) или бесконечное число раз методом loop (Clip.LOOP_CONTINUOUSLY) . Перед этим необходимо установить начальную n и конечную m позиции повторения методом setLoopPoints(n, m).

Ниже приведен листинг программы, использующей интерфейс Clip.

 

package javaaudio2;

import java.awt.Button;

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.File;

import javax.sound.sampled.AudioFileFormat;

import javax.sound.sampled.AudioFormat;

import javax.sound.sampled.AudioInputStream;

import javax.sound.sampled.AudioSystem;

import javax.sound.sampled.Clip;

import javax.sound.sampled.DataLine;

import javax.swing.JFrame;

import javax.swing.JPanel;

 

public class Main

{

public static void main(String[] args)

{

final PlayAudio pa = new PlayAudio("D:\\javaaudio.wav");

// Создаем объект главного окна приложения.

JFrame f = new JFrame("Audio");

JPanel panel = new JPanel();

panel.setLayout(new FlowLayout());

Button start = new Button("Play");

start.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent arg0)

{

pa.play();

}

});

Button stop = new Button("Stop");

stop.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent arg0)

{

pa.stop();

}

});

panel.add(start);

panel.add(stop);

f.add(panel);

f.setSize(300,100);

f.setLocation(200,200);

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.setVisible(true);

}

}

 

class PlayAudio

{

Clip line;

public PlayAudio(String file)

{

try

{

// Создаем объект, представляющий файл.

File f = new File(file);

// Получаем информацию о способе записи файла.

AudioFileFormat aff = AudioSystem.getAudioFileFormat(f);

// Получаем информацию о способе записи звука.

AudioFormat af = aff.getFormat();

// Собираем всю информацию вместе,

// добавляя сведения о классе.

DataLine.Info info = new DataLine.Info(Clip.class, af);

// Проверяем, можно ли проигрывать такой формат.

if (!AudioSystem.isLineSupported(info))

{

System.err.println("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);

}

}

public void play()

{

line.start();

}

public void stop()

{

line.stop();

}

}

Второй способ, использующий методы интерфейса SourceDataLine, требует предварительного создания буфера произвольного размера.

package javaaudio3;

import java.io.File;

import javax.sound.sampled.AudioFormat;

import javax.sound.sampled.AudioInputStream;

import javax.sound.sampled.AudioSystem;

import javax.sound.sampled.DataLine;

import javax.sound.sampled.SourceDataLine;

public class Main

{

public static void main(String[] args)

{

PlayAudioLine pal = new PlayAudioLine("D:\\javaaudio.wav");

pal.play();

}

}

 

class PlayAudioLine

{

String file;

 

PlayAudioLine(String file)

{

this.file = file;

}

 

public void play()

{

AudioInputStream ais = null;

SourceDataLine line = null;

byte[] b = new byte[2048]; // Буфер данных

try

{

File f = new File(file);

// Создаем входной поток байтов из файла f

ais = AudioSystem.getAudioInputStream(f);

// Извлекаем из потока информацию о способе записи звука

AudioFormat af = ais.getFormat();

// Заносим эту информацию в объект info

DataLine.Info info = new DataLine.Info(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.close();

}

}

 

Проигрывание MIDI-файлов

 

В MIDI-файлах хранится последовательность (sequence) команд для секвенсора (sequencer) — устройства для записи, проигрывания и редактирования MlDI-последовательности, которым может быть физическое устройство или программа. Последовательность состоит из нескольких дорожек (tracks), на которых записаны MIDI-события (events). Каждая дорожка загружается в своем канале (channel). Обычно дорожка содержит звучание одного музыкального инструмента или запись голоса одного исполнителя или запись нескольких исполнителей, микшированную синтезатором (synthesizer).

Для проигрывания MIDI-последовательности в простейшем случае надо создать экземпляр секвенсора, открыть его и направить в него последовательность, извлеченную из файла. После этого следует начать проигрывание методом start (). Закончить проигрывание можно методом stop(), "перемотать" последовательность на начало записи или на указанное время проигрывания

— методами setMicrosecondPositionf(long mcs) или setTickPosition(long tick).

Ниже приведен пример простейшего проигрывателя MIDI-файлов.

package javaaudiomidi;

import javax.sound.midi.*;

import java.io.*;

 

public class Main

{

public static void main(String[] args)

{

JavaAudioMidi jam = new JavaAudioMidi("D:\\javaaudio.mid");

jam.play();

}

}

 

class JavaAudioMidi

{

String file;

public JavaAudioMidi(String file)

{

this.file = file;

}

 

public void play()

{

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();

}

catch (Exception e)

{

System.err.println(e);

}

}

}

 

Варианты заданий

 

Контрольные вопросы

1. Назовите способы добавления изображений.

2. Какие фильтры можно применять к методу drawImage для обработки Image?

3. Каково предназначение класса FilteredlmageSource?

4. Перечислите наиболее популярные форматы, используемые в приложениях или апплетах.

5. Что такое Java Sound API?