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

com.github.mathiewz.slick.ibxm.OpenALMODPlayer Maven / Gradle / Ivy

Go to download

The main purpose of this libraryis to modernize and maintain the slick2D library.

The newest version!
package com.github.mathiewz.slick.ibxm;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;

/**
 * A streaming mod/xm play back system
 *
 * @author Kevin Glass
 */
public class OpenALMODPlayer {
    /** The size of the sections to stream from the mod file */
    private static final int sectionSize = 4096 * 10;

    /** Holds the OpenAL buffer names */
    private IntBuffer bufferNames;
    /** The IBXM reference */
    private IBXM ibxm;
    /** The length of the track in frames */
    private int songDuration;
    /** The data read for this section */
    private final byte[] data = new byte[sectionSize * 4];
    /** The byte buffer passed to OpenAL containing the section */
    private final ByteBuffer bufferData = BufferUtils.createByteBuffer(sectionSize * 4);
    /** The buffer holding the names of the OpenAL buffer thats been fully played back */
    private final IntBuffer unqueued = BufferUtils.createIntBuffer(1);
    /** The source we're playing back on */
    private int source;
    /** True if sound works */
    private boolean soundWorks = true;
    /** The module being played */
    private Module module;
    /** True if we should loop the track */
    private boolean loop;
    /** True if we've completed play back */
    private boolean done = true;
    /** The number of buffers remaining to be played back */
    private int remainingBufferCount;

    /**
     * Initialise OpenAL LWJGL styley
     */
    public void init() {
        try {
            AL.create();
            soundWorks = true;
        } catch (LWJGLException e) {
            System.err.println("Failed to initialise LWJGL OpenAL");
            soundWorks = false;
            return;
        }

        if (soundWorks) {
            IntBuffer sources = BufferUtils.createIntBuffer(1);
            AL10.alGenSources(sources);

            if (AL10.alGetError() != AL10.AL_NO_ERROR) {
                System.err.println("Failed to create sources");
                soundWorks = false;
            } else {
                source = sources.get(0);
            }

        }
    }

    /**
     * Play a mod or xm track streamed from the specified location
     *
     * @param in
     *            The input stream to read the music from
     * @param loop
     *            True if the track should be looped
     * @param start
     *            True if the music should be started
     * @throws IOException
     *             The input stream to read the music from
     */
    public void play(InputStream in, boolean loop, boolean start) throws IOException {
        play(source, in, loop, start);
    }

    /**
     * Play a mod or xm track streamed from the specified location
     *
     * @param source
     *            The OpenAL source to play the music on
     * @param in
     *            The input stream to read the music from
     * @param loop
     *            True if the track should be looped
     * @param start
     *            True if the music should be started
     * @throws IOException
     *             The input stream to read the music from
     */
    public void play(int source, InputStream in, boolean loop, boolean start) throws IOException {
        if (!soundWorks) {
            return;
        }

        done = false;
        this.loop = loop;
        this.source = source;

        module = loadModule(in);
        play(module, source, loop, start);
    }

    /**
     * Play a mod or xm track streamed from the specified location
     *
     * @param module
     *            The moudle to play back
     * @param source
     *            The OpenAL source to play the music on
     * @param start
     *            True if the music should be started
     * @param loop
     *            True if the track should be looped
     */
    public void play(Module module, int source, boolean loop, boolean start) {
        this.source = source;
        this.loop = loop;
        this.module = module;
        done = false;

        ibxm = new IBXM(48000);
        ibxm.set_module(module);
        songDuration = ibxm.calculate_song_duration();

        if (bufferNames != null) {
            AL10.alSourceStop(source);
            bufferNames.flip();
            AL10.alDeleteBuffers(bufferNames);
        }

        bufferNames = BufferUtils.createIntBuffer(2);
        AL10.alGenBuffers(bufferNames);
        remainingBufferCount = 2;

        for (int i = 0; i < 2; i++) {
            stream(bufferNames.get(i));
        }
        AL10.alSourceQueueBuffers(source, bufferNames);
        AL10.alSourcef(source, AL10.AL_PITCH, 1.0f);
        AL10.alSourcef(source, AL10.AL_GAIN, 1.0f);

        if (start) {
            AL10.alSourcePlay(source);
        }
    }

    /**
     * Setup the playback properties
     *
     * @param pitch
     *            The pitch to play back at
     * @param gain
     *            The volume to play back at
     */
    public void setup(float pitch, float gain) {
        AL10.alSourcef(source, AL10.AL_PITCH, pitch);
        AL10.alSourcef(source, AL10.AL_GAIN, gain);
    }

    /**
     * Check if the playback is complete. Note this will never
     * return true if we're looping
     *
     * @return True if we're looping
     */
    public boolean done() {
        return done;
    }

    /**
     * Load a module using the IBXM
     *
     * @param in
     *            The input stream to read the module from
     * @return The module loaded
     * @throws IOException
     *             Indicates a failure to access the module
     */
    public static Module loadModule(InputStream in) throws IOException {
        Module module;
        DataInputStream din;
        byte[] xm_header, s3m_header, mod_header;
        din = new DataInputStream(in);
        module = null;
        xm_header = new byte[60];
        din.readFully(xm_header);

        if (FastTracker2.is_xm(xm_header)) {
            module = FastTracker2.load_xm(xm_header, din);
        } else {
            s3m_header = new byte[96];
            System.arraycopy(xm_header, 0, s3m_header, 0, 60);
            din.readFully(s3m_header, 60, 36);

            if (ScreamTracker3.is_s3m(s3m_header)) {
                module = ScreamTracker3.load_s3m(s3m_header, din);
            } else {
                mod_header = new byte[1084];
                System.arraycopy(s3m_header, 0, mod_header, 0, 96);
                din.readFully(mod_header, 96, 988);
                module = ProTracker.load_mod(mod_header, din);
            }
        }
        din.close();

        return module;
    }

    /**
     * Poll the bufferNames - check if we need to fill the bufferNames with another
     * section.
     *
     * Most of the time this should be reasonably quick
     */
    public void update() {
        if (done) {
            return;
        }

        int processed = AL10.alGetSourcei(source, AL10.AL_BUFFERS_PROCESSED);

        while (processed > 0) {
            unqueued.clear();

            AL10.alSourceUnqueueBuffers(source, unqueued);
            if (stream(unqueued.get(0))) {
                AL10.alSourceQueueBuffers(source, unqueued);
            } else {
                remainingBufferCount--;
                if (remainingBufferCount == 0) {
                    done = true;
                }
            }
            processed--;
        }

        int state = AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE);

        if (state != AL10.AL_PLAYING) {
            AL10.alSourcePlay(source);
        }
    }

    /**
     * Stream one section from the mod/xm into an OpenAL buffer
     *
     * @param bufferId
     *            The ID of the buffer to fill
     * @return True if another section was available
     */
    public boolean stream(int bufferId) {
        int frames = sectionSize;
        boolean reset = false;
        boolean more = true;

        if (frames > songDuration) {
            frames = songDuration;
            reset = true;
        }

        ibxm.get_audio(data, frames);
        bufferData.clear();
        bufferData.put(data);
        bufferData.limit(frames * 4);

        if (reset) {
            if (loop) {
                ibxm.seek(0);
                ibxm.set_module(module);
                songDuration = ibxm.calculate_song_duration();
            } else {
                more = false;
                songDuration -= frames;
            }
        } else {
            songDuration -= frames;
        }

        bufferData.flip();
        AL10.alBufferData(bufferId, AL10.AL_FORMAT_STEREO16, bufferData, 48000);

        return more;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy