diff options
author | krishna_gadepalli <[email protected]> | 2007-02-02 21:10:56 +0000 |
---|---|---|
committer | krishna_gadepalli <[email protected]> | 2007-02-02 21:10:56 +0000 |
commit | df97090fcae7ad19be60c7256ef00f6c1dbf930b (patch) | |
tree | a0e1b897f58013ce910b9558c1b52a2a3c5eb166 | |
parent | 0b450846c34ca9fe2ac3d462e6e6f1eb1744ae31 (diff) |
- Added "lesson8" a translation of the Ogg/Vorbis OpenAL streaming tutorial to JOAL
- Modified the build.xml to add "lesson?" targets to run the examples.
git-svn-id: file:///home/mbien/NetBeansProjects/JOGAMP/joal-sync/svn-server-sync-demos/joal-demos/trunk@55 235fdd13-0e8c-4fed-b5ee-0a390d04b286
-rwxr-xr-x | make/build.xml | 101 | ||||
-rw-r--r-- | src/java/demos/data/crickets.ogg | bin | 0 -> 3220126 bytes | |||
-rw-r--r-- | src/java/demos/devmaster/lesson8/Credits.txt | 12 | ||||
-rw-r--r-- | src/java/demos/devmaster/lesson8/OggDecoder.java | 276 | ||||
-rw-r--r-- | src/java/demos/devmaster/lesson8/OggStreamer.java | 262 | ||||
-rw-r--r-- | src/java/demos/lib/j-ogg-all.jar | bin | 0 -> 159612 bytes |
6 files changed, 631 insertions, 20 deletions
diff --git a/make/build.xml b/make/build.xml index 688d11a..7ea5595 100755 --- a/make/build.xml +++ b/make/build.xml @@ -32,28 +32,17 @@ <target name="init" depends="java.class.path.validate"> <property name="joal.jar" value="../../joal/build/joal.jar" /> - <property name="classes" value="../build/classes" /> - <property name="src" value="../src" /> - <property name="joal.demos.jar" value="../build/joal-demos.jar" /> - <property name="joal.demos.src.zip" value="../build/joal-demos-src.zip" /> + + <property name="build.dir" value="../build" /> + <property name="src.dir" value="../src" /> + <property name="classes.dir" value="${build.dir}/classes" /> + <property name="joal.demos.jar" value="${build.dir}/joal-demos.jar" /> + <property name="joal.demos.ogg.jar" value="${build.dir}/j-ogg.jar" /> + <property name="joal.demos.src.zip" value="${build.dir}/joal-demos-src.zip" /> </target> - <target name="all" depends="init"> - <mkdir dir="${classes}" /> - <path id="joal.classpath"> - <pathelement path="${classpath}" /> - <pathelement location="${joal.jar}" /> - </path> - <javac destdir="${classes}" source="1.4" debug="true" debuglevel="source,lines"> - <src path="${src}" /> - <classpath refid="joal.classpath" /> - </javac> - <jar destfile="${joal.demos.jar}"> - <fileset dir="${classes}" /> - <fileset dir="${src}/java"> - <include name="demos/data/**" /> - </fileset> - </jar> + <target name="all" depends="jar" description="Build everything"> + <!-- Build a source archive as well --> <delete file="${joal.demos.src.zip}" /> <zip destfile="${joal.demos.src.zip}" @@ -63,9 +52,81 @@ /> </target> + <target name="jar" depends="compile" description="Build JOAL demos jar file"> + <jar destfile="${joal.demos.jar}"> + <fileset dir="${classes.dir}" /> + <fileset dir="${src.dir}/java"> + <include name="demos/data/**" /> + </fileset> + </jar> + </target> + + <target name="compile" depends="init"> + <mkdir dir="${classes.dir}" /> + + <copy file="${src.dir}/java/demos/lib/j-ogg-all.jar" + tofile="${joal.demos.ogg.jar}"/> + + <path id="joal.classpath"> + <pathelement path="${classpath}" /> + <pathelement location="${joal.jar}" /> + <pathelement location="${joal.demos.ogg.jar}" /> + </path> + <javac destdir="${classes.dir}" source="1.4" debug="true" debuglevel="source,lines"> + <src path="${src.dir}" /> + <classpath refid="joal.classpath" /> + </javac> + </target> + <target name="clean" depends="init"> <delete includeEmptyDirs="true" quiet="true"> <fileset dir="../build" /> </delete> </target> + + <target name="lesson1" depends="jar" description="Run lesson1 (Single Static Source)"> + <java classname="demos.devmaster.lesson1.SingleStaticSource" fork="true"> + <classpath> + <pathelement location="${joal.jar}" /> + <pathelement location="${joal.demos.jar}" /> + </classpath> + </java> + </target> + + <target name="lesson2" depends="jar" description="Run lesson2 (Looping and Fade Away)"> + <java classname="demos.devmaster.lesson2.LoopingAndFadeaway" fork="true"> + <classpath> + <pathelement location="${joal.jar}" /> + <pathelement location="${joal.demos.jar}" /> + </classpath> + </java> + </target> + + <target name="lesson3" depends="jar" description="Run lesson3 (Multiple Sources)"> + <java classname="demos.devmaster.lesson3.MultipleSources" fork="true"> + <classpath> + <pathelement location="${joal.jar}" /> + <pathelement location="${joal.demos.jar}" /> + </classpath> + </java> + </target> + + <target name="lesson5" depends="jar" description="Run lesson5 (Sources Sharing Buffers)"> + <java classname="demos.devmaster.lesson5.SourcesSharingBuffers" fork="true"> + <classpath> + <pathelement location="${joal.jar}" /> + <pathelement location="${joal.demos.jar}" /> + </classpath> + </java> + </target> + + <target name="lesson8" depends="jar" description="Run lesson8 (Ogg/Vorbis Streaming)"> + <java classname="demos.devmaster.lesson8.OggStreamer" fork="true"> + <classpath> + <pathelement location="${joal.jar}" /> + <pathelement location="${joal.demos.jar}" /> + <pathelement location="${joal.demos.ogg.jar}" /> + </classpath> + </java> + </target> </project> diff --git a/src/java/demos/data/crickets.ogg b/src/java/demos/data/crickets.ogg Binary files differnew file mode 100644 index 0000000..25dabfd --- /dev/null +++ b/src/java/demos/data/crickets.ogg diff --git a/src/java/demos/devmaster/lesson8/Credits.txt b/src/java/demos/devmaster/lesson8/Credits.txt new file mode 100644 index 0000000..36e7188 --- /dev/null +++ b/src/java/demos/devmaster/lesson8/Credits.txt @@ -0,0 +1,12 @@ + +1. This tutorial is an attempt at translating the OggVorbis streaming + tutorial at http://www.devmaster.net/articles/openal-tutorials/lesson8.php + +2. demos/lib/j-ogg-all.jar + + This software is based on or using the J-Ogg library available from + http://www.j-ogg.de and copyrighted by Tor-Einar Jarnbjo. + +3. demos/data/crickets.ogg + + This sample file is from the Freesound project at http://freesound.iua.upf.edu diff --git a/src/java/demos/devmaster/lesson8/OggDecoder.java b/src/java/demos/devmaster/lesson8/OggDecoder.java new file mode 100644 index 0000000..e1a2a5c --- /dev/null +++ b/src/java/demos/devmaster/lesson8/OggDecoder.java @@ -0,0 +1,276 @@ +/** + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * -Redistribution of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING + * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR + * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS + * LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A + * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + * IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT + * OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS + * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use in the + * design, construction, operation or maintenance of any nuclear facility. + * + */ + +package demos.devmaster.lesson8; + +import de.jarnbjo.ogg.CachedUrlStream; +import de.jarnbjo.ogg.EndOfOggStreamException; +import de.jarnbjo.ogg.LogicalOggStream; +import de.jarnbjo.vorbis.IdentificationHeader; +import de.jarnbjo.vorbis.VorbisStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.ByteBuffer; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.SourceDataLine; + +public class OggDecoder { + + private static int BLOCK_SIZE = 4096*64; + + private VorbisStream vStream; + private LogicalOggStream loStream; + private AudioInputStream ais; + private IdentificationHeader vStreamHdr; + + private AudioFormat audioFormat; + + private URL url; + private boolean swap = false; + private boolean endOfStream = false; + + public OggDecoder(URL url) { + this.url = url; + } + + public boolean initialize() { + try { + CachedUrlStream os = new CachedUrlStream(url); + + loStream = (LogicalOggStream)os.getLogicalStreams().iterator().next(); + vStream = new VorbisStream(loStream); + vStreamHdr = vStream.getIdentificationHeader(); + + audioFormat = new AudioFormat( + (float)vStreamHdr.getSampleRate(), + 16, + vStreamHdr.getChannels(), + true, true); + + ais = new AudioInputStream( + new VorbisInputStream(vStream), audioFormat, -1); + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } + + public int numChannels() { + return vStreamHdr.getChannels(); + } + + public int sampleRate() { + return vStreamHdr.getSampleRate(); + } + + public void setSwap(boolean swap) { + this.swap = swap; + } + + /** + * Swaps bytes. + * @throws ArrayOutOfBoundsException if len is not a multiple of 2. + */ + public static void swapBytes(byte[] b) { + swapBytes(b, 0, b.length); + } + + public static void swapBytes(byte[] b, int off, int len) { + + byte tempByte; + for (int i = off; i < (off+len); i+=2) { + + tempByte = b[i]; + b[i] = b[i+1]; + b[i+1] = tempByte; + } + } + + // play using JavaSound + public void play() { + if (!initialize()) + return; + + dump(); + + try { + DataLine.Info dataLineInfo = + new DataLine.Info(SourceDataLine.class, audioFormat); + + SourceDataLine sourceDataLine = + (SourceDataLine)AudioSystem.getLine(dataLineInfo); + + sourceDataLine.open(audioFormat); + sourceDataLine.start(); + + byte[] buffer = new byte[BLOCK_SIZE]; + int bytesRead; + + while (true) { + if ((bytesRead = read(buffer)) > 0) + sourceDataLine.write(buffer, 0, bytesRead); + + if (bytesRead < buffer.length) + break; + } + + sourceDataLine.drain(); + sourceDataLine.close(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + // play using JavaSound + public void toraw(String fileName) { + if (!initialize()) + return; + + setSwap(true); + dump(); + + try { + byte[] buffer = new byte[BLOCK_SIZE]; + int bytesRead; + + FileOutputStream fos = new FileOutputStream(fileName); + + while (true) { + if ((bytesRead = read(buffer)) > 0) + fos.write(buffer, 0, bytesRead); + + if (bytesRead < buffer.length) + break; + } + + fos.close(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + public int read(byte[] buffer) throws IOException { + + if (endOfStream) + return -1; + + int bytesRead = 0, cnt = 0; + + while (bytesRead < buffer.length) { + if ((cnt = ais.read(buffer, bytesRead, buffer.length-bytesRead)) <= 0) { + endOfStream = true; + break; + } + bytesRead += cnt; + } + + if (swap) + swapBytes(buffer, 0, bytesRead); + + return bytesRead; + } + + public void dump() { + System.err.println("#Channels: " + vStreamHdr.getChannels()); + System.err.println("Sample rate: " + vStreamHdr.getSampleRate()); + System.err.println("Bitrate: nominal=" + + vStreamHdr.getNominalBitrate() + + ", max=" + vStreamHdr.getMaximumBitrate() + + ", min=" + vStreamHdr.getMinimumBitrate()); + } + + public static class VorbisInputStream extends InputStream { + + private VorbisStream source; + + public VorbisInputStream(VorbisStream source) { + this.source = source; + } + + public int read() throws IOException { + return 0; + } + + public int read(byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + public int read(byte[] buffer, int offset, int length) throws IOException { + try { + return source.readPcm(buffer, offset, length); + } catch(EndOfOggStreamException e) { + return -1; + } + } + } + + public static void main(String args[]) { + URL url; + int i = 0; + String rawname = null; + + try { + if (args.length == 0) { + url = OggDecoder.class.getClassLoader().getResource("demos/data/crickets.ogg"); + (new OggDecoder(url)).play(); + } + + for (; i < args.length; i++) { + if (args[i].equals("-r")) { + rawname = args[++i]; + continue; + } + + System.err.println("Playing: " + args[i]); + + url = ((new File(args[i])).exists()) ? + new URL("file:" + args[i]) : new URL(args[i]); + + if (rawname != null) (new OggDecoder(url)).toraw(rawname); + else (new OggDecoder(url)).play(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/java/demos/devmaster/lesson8/OggStreamer.java b/src/java/demos/devmaster/lesson8/OggStreamer.java new file mode 100644 index 0000000..4858f23 --- /dev/null +++ b/src/java/demos/devmaster/lesson8/OggStreamer.java @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * -Redistribution of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING + * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR + * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS + * LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A + * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + * IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT + * OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS + * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use in the + * design, construction, operation or maintenance of any nuclear facility. + * + */ + +package demos.devmaster.lesson8; + +import java.io.File; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.Arrays; +import net.java.games.joal.AL; +import net.java.games.joal.ALException; +import net.java.games.joal.ALFactory; +import net.java.games.joal.util.ALut; +/** + * + * This is a translation of the OggVorbis streamer OpenAL tutorial + * at http://www.devmaster.net/articles/openal-tutorials/lesson8.php + * + * It uses the Java Ogg library from http://www.j-ogg.de to do the Ogg + * file decoding... + * + * @author Krishna K Gadepalli + */ +public class OggStreamer { + + OggDecoder oggDecoder; + + static AL al = null; + static int BUFFER_SIZE = 4096*4; + + static { + // Initialize OpenAL and clear the error bit. + try { + ALut.alutInit(); + al = ALFactory.getAL(); + al.alGetError(); + } catch (ALException e) { + System.err.println("Error initializing OpenAL"); + e.printStackTrace(); + } + } + + // Buffers hold sound data. + private int[] buffers = new int[2]; + + // Sources are points emitting sound. + private int[] source = new int[1]; + + // Position, Velocity, Direction of the source sound. + private float[] sourcePos = { 0.0f, 0.0f, 0.0f }; + private float[] sourceVel = { 0.0f, 0.0f, 0.0f }; + private float[] sourceDir = { 0.0f, 0.0f, 0.0f }; + + private int format; // OpenAL data format + private int rate; // sample rate + + /** Creates a new instance of OggStreamer */ + public OggStreamer(URL url) { + this.oggDecoder = new OggDecoder(url); + } + + public boolean open() { + if (!oggDecoder.initialize()) { + System.err.println("Error initializing ogg stream..."); + return false; + } + + // TODO: I am not if this is the right way to fix the endian + // problems I am having... but this seems to fix it on Linux + oggDecoder.setSwap(true); + + switch (oggDecoder.numChannels()) { + case 1: format = AL.AL_FORMAT_MONO16; break; + case 2: format = AL.AL_FORMAT_STEREO16; break; + default: + System.err.println("Incorrect number of channels.."); + return false; + } + + rate = oggDecoder.sampleRate(); + + al.alGenBuffers(2, buffers, 0); check(); + al.alGenSources(1, source, 0); check(); + + System.err.println("format = 0x" + Integer.toString(format, 16)); + // System.err.println("buffers = " + Arrays.toString(buffers)); + // System.err.println("source = " + Arrays.toString(source )); + + al.alSourcefv(source[0], AL.AL_POSITION , sourcePos, 0); + al.alSourcefv(source[0], AL.AL_VELOCITY , sourceVel, 0); + al.alSourcefv(source[0], AL.AL_DIRECTION, sourceDir, 0); + + al.alSourcef(source[0], AL.AL_ROLLOFF_FACTOR, 0.0f ); + al.alSourcei(source[0], AL.AL_SOURCE_RELATIVE, AL.AL_TRUE); + + return true; + } + + public void release() { + al.alSourceStop(source[0]); + empty(); + + al.alDeleteSources(1, source, 0); check(); + al.alDeleteBuffers(2, buffers, 0); check(); + + // ov_clear(&oggStream); + } + + public boolean playback() { + if (playing()) + return true; + + if (!stream(buffers[0])) + return false; + + if(!stream(buffers[1])) + return false; + + al.alSourceQueueBuffers(source[0], 2, buffers, 0); + al.alSourcePlay(source[0]); + + return true; + } + + public boolean playing() { + int[] state = new int[1]; + + al.alGetSourcei(source[0], AL.AL_SOURCE_STATE, state, 0); + + return (state[0] == AL.AL_PLAYING); + } + + public boolean update() { + int[] processed = new int[1]; + boolean active = true; + + al.alGetSourcei(source[0], AL.AL_BUFFERS_PROCESSED, processed, 0); + + while (processed[0] > 0) + { + int[] buffer = new int[1]; + + al.alSourceUnqueueBuffers(source[0], 1, buffer, 0); check(); + + active = stream(buffer[0]); + + al.alSourceQueueBuffers(source[0], 1, buffer, 0); check(); + + processed[0]--; + } + + return active; + } + + public boolean stream(int buffer) { + byte[] pcm = new byte[BUFFER_SIZE]; + int size = 0; + + try { + if ((size = oggDecoder.read(pcm)) <= 0) + return false; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + ByteBuffer data = ByteBuffer.wrap(pcm, 0, size); + al.alBufferData(buffer, format, data, size, rate); + check(); + + return true; + } + + public void empty() { + } + + private void check() { + if (al.alGetError() != AL.AL_NO_ERROR) + throw new ALException("OpenAL error raised..."); + } + + public boolean play() { + if (!open()) + return false; + + oggDecoder.dump(); + + if (!playback()) + return false; + + while (update()) { + if (playing()) continue; + + if (!playback()) + return false; + } + + return true; + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + if (al == null) + return; + + URL url; + + try { + if (args.length == 0) { + url = OggStreamer.class.getClassLoader().getResource("demos/data/crickets.ogg"); + (new OggStreamer(url)).play(); + } + + for (int i = 0; i < args.length; i++) { + System.err.println("Playing Ogg stream : " + args[i]); + + url = ((new File(args[i])).exists()) ? + new URL("file:" + args[i]) : new URL(args[i]); + + if ((new OggStreamer(url)).play()) continue; + + System.err.println("ERROR!!"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/java/demos/lib/j-ogg-all.jar b/src/java/demos/lib/j-ogg-all.jar Binary files differnew file mode 100644 index 0000000..d234b64 --- /dev/null +++ b/src/java/demos/lib/j-ogg-all.jar |