/*
 * Copyright (C) 2013 Darth Affe <http://wyrez.org> and contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 * This Code is based on the source provided with a tutorial by
 * http://www.badlogicgames.com (as of 16.05.2013).
 * The original source can be found here:
 * http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/AudioDevice.java
 */
package org.wyrez.audio;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;

/**
 * Class that allows directly passing PCM float data to the sound card for
 * playback.
 *
 * @author Darth Affe
 */
public class AudioDevice {

    private final SourceDataLine out;
    private byte[] buffer;

    /**
     * Constructor, initializes the audio system for 16-bit signed stereo output
     * with a sampling rate of 44100 and a buffer size of 2048.
     *
     * @throws Exception in case the audio system could not be initialized
     */
    public AudioDevice() throws Exception {
        this(44100, 2);
    }

    /**
     * Constructor, initializes the audio system for 16-bit signed output. Sets
     * the buffer size to 1024*channels
     *
     * @param samplingRate The sampling rate
     * @param channels The number of channels
     * @throws Exception in case the audio system could not be initialized
     */
    public AudioDevice(float samplingRate, int channels) throws Exception {
        this(samplingRate, channels, 1024 * channels);
    }

    /**
     * Constructor, initializes the audio system for 16-bit signed output.
     *
     * @param samplingRate The sampling rate
     * @param channels The number of channels
     * @param bufferSize The buffer size (have to be a even number)
     * @throws Exception in case the audio system could not be initialized
     */
    public AudioDevice(float samplingRate, int channels, int bufferSize) throws Exception {
        AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
                samplingRate, 16, channels, channels * 2, samplingRate, false);
        buffer = new byte[bufferSize * 2];
        out = AudioSystem.getSourceDataLine(format);
        out.open(format);
        out.start();
    }

    /**
     * Writes the given samples to the audio device. The samples have to be in
     * the range [-1,1].
     *
     * @param samples The samples.
     */
    public void writeSamples(float[] samples) {
        fillBuffer(samples);
        out.write(buffer, 0, buffer.length);
    }

    private void fillBuffer(float[] samples) {
        for (int i = 0, j = 0; i < samples.length; i++, j += 2) {
            short value = (short) (samples[i] * Short.MAX_VALUE);
            buffer[j] = (byte) (value | 0xff);
            buffer[j + 1] = (byte) (value >> 8);
        }
    }
}
