Package com.badlogic.gdx.backends.openal

Source Code of com.badlogic.gdx.backends.openal.OpenALMusic

/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/

package com.badlogic.gdx.backends.openal;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.openal.AL11;

import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.GdxRuntimeException;

import static org.lwjgl.openal.AL10.*;

/** @author Nathan Sweet */
public abstract class OpenALMusic implements Music {
  static private final int bufferSize = 4096 * 10;
  static private final int bufferCount = 3;
  static private final int bytesPerSample = 2;
  static private final byte[] tempBytes = new byte[bufferSize];
  static private final ByteBuffer tempBuffer = BufferUtils.createByteBuffer(bufferSize);

  private final OpenALAudio audio;
  private IntBuffer buffers;
  private int sourceID = -1;
  private int format, sampleRate;
  private boolean isLooping, isPlaying;
  private float volume = 1;
  private float pan = 0;
  private float renderedSeconds, secondsPerBuffer;

  protected final FileHandle file;

  private OnCompletionListener onCompletionListener;

  public OpenALMusic (OpenALAudio audio, FileHandle file) {
    this.audio = audio;
    this.file = file;
    if (audio != null) {
      if (!audio.noDevice) audio.music.add(this);
    }
    this.onCompletionListener = null;
  }

  protected void setup (int channels, int sampleRate) {
    this.format = channels > 1 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
    this.sampleRate = sampleRate;
    secondsPerBuffer = (float)bufferSize / bytesPerSample / channels / sampleRate;
  }

  public void play () {
    if (audio.noDevice) return;
    if (sourceID == -1) {
      sourceID = audio.obtainSource(true);
      if (sourceID == -1) return;
      if (buffers == null) {
        buffers = BufferUtils.createIntBuffer(bufferCount);
        alGenBuffers(buffers);
        if (alGetError() != AL_NO_ERROR) throw new GdxRuntimeException("Unabe to allocate audio buffers.");
      }
      alSourcei(sourceID, AL_LOOPING, AL_FALSE);
      setPan(pan, volume);
      for (int i = 0; i < bufferCount; i++) {
        int bufferID = buffers.get(i);
        if (!fill(bufferID)) break;
        alSourceQueueBuffers(sourceID, bufferID);
      }
      if (alGetError() != AL_NO_ERROR) {
        stop();
        return;
      }
    }
    alSourcePlay(sourceID);
    isPlaying = true;
  }

  public void stop () {
    if (audio.noDevice) return;
    if (sourceID == -1) return;
    reset();
    audio.freeSource(sourceID);
    sourceID = -1;
    renderedSeconds = 0;
    isPlaying = false;
  }

  public void pause () {
    if (audio.noDevice) return;
    if (sourceID != -1) alSourcePause(sourceID);
    isPlaying = false;
  }

  public boolean isPlaying () {
    if (audio.noDevice) return false;
    if (sourceID == -1) return false;
    return isPlaying;
  }

  public void setLooping (boolean isLooping) {
    this.isLooping = isLooping;
  }

  public boolean isLooping () {
    return isLooping;
  }

  public void setVolume (float volume) {
    this.volume = volume;
    if (audio.noDevice) return;
    if (sourceID != -1) alSourcef(sourceID, AL_GAIN, volume);
  }

  public float getVolume () {
    return this.volume;
  }

  public void setPan (float pan, float volume) {
    this.volume = volume;
    this.pan = pan;
    if (audio.noDevice) return;
    if (sourceID == -1) return;
    alSource3f(sourceID, AL_POSITION, MathUtils.cos((pan - 1) * MathUtils.PI / 2), 0,
      MathUtils.sin((pan + 1) * MathUtils.PI / 2));
    alSourcef(sourceID, AL_GAIN, volume);
  }

  public float getPosition () {
    if (audio.noDevice) return 0;
    if (sourceID == -1) return 0;
    return renderedSeconds + alGetSourcef(sourceID, AL11.AL_SEC_OFFSET);
  }

  /** Fills as much of the buffer as possible and returns the number of bytes filled. Returns <= 0 to indicate the end of the
   * stream. */
  abstract public int read (byte[] buffer);

  /** Resets the stream to the beginning. */
  abstract public void reset ();

  public int getChannels () {
    return format == AL_FORMAT_STEREO16 ? 2 : 1;
  }

  public int getRate () {
    return sampleRate;
  }

  public void update () {
    if (audio.noDevice) return;
    if (sourceID == -1) return;

    boolean end = false;
    int buffers = alGetSourcei(sourceID, AL_BUFFERS_PROCESSED);
    while (buffers-- > 0) {
      int bufferID = alSourceUnqueueBuffers(sourceID);
      if (bufferID == AL_INVALID_VALUE) break;
      renderedSeconds += secondsPerBuffer;
      if (end) continue;
      if (fill(bufferID))
        alSourceQueueBuffers(sourceID, bufferID);
      else
        end = true;
    }
    if (end && alGetSourcei(sourceID, AL_BUFFERS_QUEUED) == 0) {
      if (onCompletionListener != null) onCompletionListener.onCompletion(this);
      stop();
    }

    // A buffer underflow will cause the source to stop.
    if (isPlaying && alGetSourcei(sourceID, AL_SOURCE_STATE) != AL_PLAYING) alSourcePlay(sourceID);
  }

  private boolean fill (int bufferID) {
    tempBuffer.clear();
    int length = read(tempBytes);
    if (length <= 0) {
      if (isLooping) {
        reset();
        renderedSeconds = 0;
        length = read(tempBytes);
        if (length <= 0) return false;
      } else
        return false;
    }
    tempBuffer.put(tempBytes, 0, length).flip();
    alBufferData(bufferID, format, tempBuffer, sampleRate);
    return true;
  }

  public void dispose () {
    if (audio.noDevice) return;
    if (buffers == null) return;
    if (sourceID != -1) {
      reset();
      audio.music.removeValue(this, true);
      audio.freeSource(sourceID);
      sourceID = -1;
    }
    alDeleteBuffers(buffers);
    buffers = null;
    onCompletionListener = null;
  }

  public void setOnCompletionListener (OnCompletionListener listener) {
    onCompletionListener = listener;
  }

  public int getSourceId () {
    return sourceID;
  }
}
TOP

Related Classes of com.badlogic.gdx.backends.openal.OpenALMusic

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.