Как стать автором
Обновить

Воспроизведение звука в Java

Время на прочтение3 мин
Количество просмотров81K

Введение


Нормальной русскоязычной информации по теме просто нет. Java-tutorials тоже оставляют желать лучшего. А архитектура javax.sound.sampled хоть и проста, но далеко не тривиальна. Поэтому свой первый пост на Хабре я решил посвятить именно этой теме. Приступим:

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


Тут всё более-менее просто. Импортируем javax.sound.sampled и поехали:
try {
	File soundFile = new File("snd.wav"); //Звуковой файл
	
	//Получаем AudioInputStream
	//Вот тут могут полететь IOException и UnsupportedAudioFileException
	AudioInputStream ais = AudioSystem.getAudioInputStream(soundFile);
	
	//Получаем реализацию интерфейса Clip
	//Может выкинуть LineUnavailableException
	Clip clip = AudioSystem.getClip();
	
	//Загружаем наш звуковой поток в Clip
	//Может выкинуть IOException и LineUnavailableException
	clip.open(ais);
	
	clip.setFramePosition(0); //устанавливаем указатель на старт
	clip.start(); //Поехали!!!

	//Если не запущено других потоков, то стоит подождать, пока клип не закончится
        //В GUI-приложениях следующие 3 строчки не понадобятся
	Thread.sleep(clip.getMicrosecondLength()/1000);
	clip.stop(); //Останавливаем
	clip.close(); //Закрываем
} catch (IOException | UnsupportedAudioFileException | LineUnavailableException exc) {
	exc.printStackTrace();
} catch (InterruptedException exc) {}


Регулятор громкости

Поигравшись со звуками, вы наверняка захотите иметь возможность программно изменять громкость звука. Java Sound API предоставляет такую возможность с фирменной кривотой.
//Получаем контроллер громкости
FloatControl vc = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
//Устанавливаем значение
//Оно должно быть в пределах от vc.getMinimum() до vc.getMaximum()
vc.setValue(5); //Громче обычного

Этот код нужно поместить между строчками clip.open(ais) и clip.setFramePosition(0).

Упрощаем процесс


Ну и наконец, чтобы вы не мучились, выкладываю класс для проигрывания звуков
import java.io.File;
import java.io.IOException;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class Sound implements AutoCloseable {
	private boolean released = false;
	private AudioInputStream stream = null;
	private Clip clip = null;
	private FloatControl volumeControl = null;
	private boolean playing = false;
	
	public Sound(File f) {
		try {
			stream = AudioSystem.getAudioInputStream(f);
			clip = AudioSystem.getClip();
			clip.open(stream);
			clip.addLineListener(new Listener());
			volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
			released = true;
		} catch (IOException | UnsupportedAudioFileException | LineUnavailableException exc) {
			exc.printStackTrace();
			released = false;
			
			close();
		}
	}

	// true если звук успешно загружен, false если произошла ошибка
	public boolean isReleased() {
		return released;
	}
	
	// проигрывается ли звук в данный момент
	public boolean isPlaying() {
		return playing;
	}

	// Запуск
	/*
	  breakOld определяет поведение, если звук уже играется
	  Если breakOld==true, о звук будет прерван и запущен заново
	  Иначе ничего не произойдёт
	*/
	public void play(boolean breakOld) {
		if (released) {
			if (breakOld) {
				clip.stop();
				clip.setFramePosition(0);
				clip.start();
				playing = true;
			} else if (!isPlaying()) {
				clip.setFramePosition(0);
				clip.start();
				playing = true;
			}
		}
	}
	
	// То же самое, что и play(true)
	public void play() {
		play(true);
	}
	
	// Останавливает воспроизведение
	public void stop() {
		if (playing) {
			clip.stop();
		}
	}
	
	public void close() {
		if (clip != null)
			clip.close();
		
		if (stream != null)
			try {
				stream.close();
			} catch (IOException exc) {
				exc.printStackTrace();
			}
	}

	// Установка громкости
	/*
	  x долже быть в пределах от 0 до 1 (от самого тихого к самому громкому)
	*/
	public void setVolume(float x) {
		if (x<0) x = 0;
		if (x>1) x = 1;
		float min = volumeControl.getMinimum();
		float max = volumeControl.getMaximum();
		volumeControl.setValue((max-min)*x+min);
	}
	
	// Возвращает текущую громкость (число от 0 до 1)
	public float getVolume() {
		float v = volumeControl.getValue();
		float min = volumeControl.getMinimum();
		float max = volumeControl.getMaximum();
		return (v-min)/(max-min);
	}

	// Дожидается окончания проигрывания звука
	public void join() {
		if (!released) return;
		synchronized(clip) {
			try {
				while (playing)
					clip.wait();
			} catch (InterruptedException exc) {}
		}
	}
	
	// Статический метод, для удобства
	public static Sound playSound(String path) {
		File f = new File(path);
		Sound snd = new Sound(f);
		snd.play();
		return snd;
	}

	private class Listener implements LineListener {
		public void update(LineEvent ev) {
			if (ev.getType() == LineEvent.Type.STOP) {
				playing = false;
				synchronized(clip) {
					clip.notify();
				}
			}
		}
	}
}


Пользоваться очень просто, например:
Sound.playSound("sounds/hello.wav").join();


Форматы


Пару слов о поддержке форматов звуковых файлов: забудьте про mp3 и вспомните wav. Также поддерживаются au и aif.
Теги:
Хабы:
Всего голосов 21: ↑18 и ↓3+15
Комментарии7

Публикации

Истории

Работа

Java разработчик
347 вакансий

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань