All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.libav.audio.AudioStreamPlayer Maven / Gradle / Ivy

/*
 * Copyright (C) 2012 Ondrej Perutka
 *
 * 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 library 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 along with this library. If not, see 
 * .
 */
package org.libav.audio;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.*;

/**
 * Audio stream player. It provides audio playback via Java Sound API. It does
 * not support audio mixing.
 * 
 * @author Ondrej Perutka
 */
public class AudioStreamPlayer {
    
    private SourceDataLine dataLine;
    private AudioInputStream inputStream;
    
    private boolean running;
    private boolean stop;
    
    private PlayerThread player;
    private Thread thread;
    
    private float volume;

    /**
     * Create a new audio stream player and set the input stream and the data
     * line. The input stream and the data line do not have to use the same
     * audio format, but the conversion must be supported if the audio formats
     * are not same.
     * 
     * @param inputStream an input stream
     * @param dataLine a data line
     * @throws IllegalArgumentException if the necessary conversion is not
     * supported
     */
    public AudioStreamPlayer(AudioInputStream inputStream, SourceDataLine dataLine) {
        this.dataLine = dataLine;
        if (dataLine.getFormat().matches(inputStream.getFormat()))
            this.inputStream = inputStream;
        else
            this.inputStream = AudioSystem.getAudioInputStream(dataLine.getFormat(), inputStream);
        
        this.running = false;
        this.stop = false;
        
        this.player = new PlayerThread();
        this.thread = null;
        
        this.volume = 1f;
    }
    
    /**
     * Create a new audio stream player, set the input stream and use a data 
     * line of the given format. The input stream format and the data line 
     * format do not have to be the same, but the conversion must be supported 
     * if they are not.
     * 
     * @param inputStream an input stream
     * @param outputFormat a data line format
     * @throws LineUnavailableException if there is no data line for the given
     * data line format
     * @throws IllegalArgumentException if the necessary conversion is not
     * supported
     */
    public AudioStreamPlayer(AudioInputStream inputStream, AudioFormat outputFormat) throws LineUnavailableException {
        this(inputStream, openDataLine(outputFormat));
    }
    
    /**
     * Start audio playback.
     */
    public synchronized void play() {
        if (running)
            return;
        
        stop = false;
        running = true;
        thread = new Thread(player, "AudioPlayer thread");
        thread.setDaemon(true);
        thread.start();
    }
    
    /**
     * Stop playback.
     */
    public synchronized void stop() {
        stop = true;
        running = false;
        
        if (thread == null)
            return;
        
        try {
            thread.interrupt();
            thread.join();
        } catch (InterruptedException ex) {
            Logger.getLogger(getClass().getName()).log(Level.WARNING, "interrupted while waiting for the audio player to be stopped");
        }
    }
    
    private void setGain(FloatControl gainControl, float volume) {
        double min = Math.pow(10, gainControl.getMinimum() / 20.0);
        double g = 20 * Math.log10(min + volume * (1 - min));
        if (g > gainControl.getMaximum())
            g = gainControl.getMaximum();
        
        gainControl.setValue((float)g);
    }
    
    private void setVolume(FloatControl volumeControl, float volume) {
        double range = volumeControl.getMaximum() - volumeControl.getMinimum();
        double v = volumeControl.getMinimum() + volume * range;
        if (v > volumeControl.getMaximum())
            v = volumeControl.getMaximum();
        
        volumeControl.setValue((float)v);
    }
    
    /**
     * Set the output volume.
     * 
     * @param volume a volume
     */
    public void setVolume(float volume) {
        // The Oracle's JDK uses MASTER_GAIN but the OpenJDK uses VOLUME
        if (dataLine.isControlSupported(FloatControl.Type.MASTER_GAIN))
            setGain((FloatControl)dataLine.getControl(FloatControl.Type.MASTER_GAIN), volume);
        else if (dataLine.isControlSupported(FloatControl.Type.VOLUME))
            setVolume((FloatControl)dataLine.getControl(FloatControl.Type.VOLUME), volume);
        else
            throw new RuntimeException("volume is not supported");
        
        this.volume = volume;
    }
    
    /**
     * Get the currently set volume.
     * 
     * @return currently set volume
     */
    public float getVolume() {
        return volume;
    }
    
    /**
     * Close the audio stream player and its data line.
     */
    public synchronized void close() {
        stop();
        dataLine.close();
    }
    
    /**
     * Wait until the playback stops.
     * 
     * @throws InterruptedException if the thread is interrupted while waiting
     */
    public void join() throws InterruptedException {
        if (thread != null)
            thread.join();
    }
    
    /**
     * Flush the data line internal buffer.
     */
    public void flushDataLine() {
        dataLine.flush();
    }
    
    private class PlayerThread implements Runnable {
        @Override
        public void run() {
            AudioFormat af = dataLine.getFormat();
            byte[] frame = new byte[(int)af.getSampleRate() * af.getSampleSizeInBits() * af.getChannels() / 800];
            int len;
            
            dataLine.start();
            while (!stop) {
                try {
                    len = inputStream.read(frame);
                    if (len == -1)
                        break;
                    dataLine.write(frame, 0, len);
                } catch (IOException ex) {
                    if (ex.getCause() instanceof InterruptedException)
                        stop = true;
                    else
                        Logger.getLogger(AudioStreamPlayer.class.getName()).log(Level.SEVERE, "audio input stream became unreadable", ex);
                }
            }
            if (!stop)
                dataLine.drain();
            dataLine.stop();
        }
    }
    
    private static SourceDataLine openDataLine(AudioFormat format) throws LineUnavailableException {
        SourceDataLine result = AudioSystem.getSourceDataLine(format);
        result.open(format);
        return result;
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy