/*
* Copyright (c) 2008, 2009, 2010, 2011 Denis Tulskiy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
*/
package com.tulskiy.musique.audio.player.io;
import javax.sound.sampled.*;
import java.util.logging.Logger;
/**
* Author: Denis Tulskiy
* Date: Jul 25, 2010
*/
public class AudioOutput {
public static final int BUFFER_SIZE = (int) (Math.pow(2, 15) / 24) * 24;
private final Logger logger = Logger.getLogger(getClass().getName());
private SourceDataLine line;
private FloatControl volumeControl;
private boolean mixerChanged;
private Mixer mixer;
private float volume = 1f;
private boolean linearVolume = false;
public void init(AudioFormat fmt) throws LineUnavailableException {
//if it is same format and the line is opened, do nothing
if (line != null && line.isOpen()) {
if (mixerChanged || !line.getFormat().matches(fmt)) {
mixerChanged = false;
line.drain();
line.close();
line = null;
} else {
return;
}
}
logger.fine("Audio format: " + fmt);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt, BUFFER_SIZE);
logger.fine("Dataline info: " + info);
if (mixer != null && mixer.isLineSupported(info)) {
line = (SourceDataLine) mixer.getLine(info);
logger.fine("Mixer: " + mixer.getMixerInfo().getDescription());
} else {
line = AudioSystem.getSourceDataLine(fmt);
mixer = null;
}
logger.fine("Line: " + line);
line.open(fmt, BUFFER_SIZE);
line.start();
if (line.isControlSupported(FloatControl.Type.VOLUME)) {
volumeControl = (FloatControl) line.getControl(FloatControl.Type.VOLUME);
volumeControl.setValue(volume * volumeControl.getMaximum());
linearVolume = true;
} else if (line.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
volumeControl = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(linearToDb(volume));
linearVolume = false;
}
}
public void stop() {
if (line != null && line.isOpen())
line.stop();
}
public void start() {
if (line != null && line.isOpen())
line.start();
}
public void close() {
if (line != null) {
line.close();
}
}
public boolean isOpen() {
return line != null && line.isOpen();
}
public void flush() {
if (line != null && line.isOpen())
line.flush();
}
public void write(byte[] buf, int offset, int len) {
line.write(buf, offset, len);
}
public void setVolume(float volume) {
this.volume = volume;
if (volumeControl != null) {
if (linearVolume)
volumeControl.setValue(volumeControl.getMaximum() * volume);
else
volumeControl.setValue(linearToDb(volume));
}
}
public float getVolume(boolean actual) {
if (actual && volumeControl != null) {
if (linearVolume)
return this.volumeControl.getValue() / volumeControl.getMaximum();
else
return dbToLinear(volumeControl.getValue());
} else
return volume;
}
private float linearToDb(double volume) {
return (float) (20 * Math.log10(volume));
}
private float dbToLinear(double volume) {
return (float) Math.pow(10, volume / 20);
}
public void setMixer(Mixer.Info info) {
if (info == null)
mixer = null;
else
mixer = AudioSystem.getMixer(info);
mixerChanged = true;
}
public Mixer.Info getMixer() {
if (mixer != null)
return mixer.getMixerInfo();
else
return null;
}
public boolean isOverrun() {
return line.available() - line.getBufferSize() == 0;
}
public int available() {
if (line != null)
return line.available();
else
return BUFFER_SIZE;
}
public void drain() {
line.drain();
}
}