How to play a OGG-file ?

Can someone please provide me some sample
code on how to load and play a short OGG-file ?

I know there are lots of OGG-decoders available,
but I find it all kind of overwhelming,
especially because I cannot find any
(simple) examples on the subject.

Thanks.

If simplicity outweighs some extra bloat, then you could use my 3D Sound System library (a bit like fishing with dynamite, though ;D). Basically, you would want to download the core SoundSystem, the CodecJOrbis plug-in, and a library plug-in (simplest is LibraryJavaSound, because it is pure Java and doesn’t require any additional libraries to be linked with).

In your code, you’d import the necessary classes:

import pauscode.sound.SoundSystem;
import paulscode.sound.SoundSystemConfig;
import paulscode.sound.SoundSystemException;
import paulscode.sound.libraries.LibraryJavaSound;
import paulscode.sound.codecs.CodecJOrbis;

Then initialize by linking with the plug-ins and instantiating the SoundSystem:

try
{
    SoundSystemConfig.addLibrary( LibraryJavaSound.class );
    SoundSystemConfig.setCodec( "ogg", CodecJOrbis.class );
}
catch( SoundSystemException e )
{
    System.err.println("error linking with the plug-ins" );
}
SoundSystem mySoundSystem = new SoundSystem();

Then to play your ogg:

mySoundSystem.quickPlay( false, "filename.ogg", false,
    0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 0 );

If you go this route, to be more compatible, I’d recommend using the LibraryLWJGLOpenAL or LibraryJOAL plug-ins by default (require you to link with 3rd-party libraries), with LibraryJavaSound as a fallback option.

To eliminate bloat, if you are handling the audio system yourself and just want to decode .ogg files easily, you can use the CodecJOrbis plug-in by itself with some minor tweaking of the code (remove calls to SoundSystemConfig for getting default values. I can help with this if you have problems). Then you can use the CodecJOrbis class stand-alone to decode ogg files much more easily than with the uber-low-level JOrbis library. Basically, use intialize(URL) to connect to a file, getAudioFormat() to get the format of returned data, read() or readAll() to get decoded audio data, endOfStream() to check for end of file, and cleanup() to shut down.

Kevin Glass writes code which is just beautiful - works well, is nice, clean and easy to use.
So I highly recommend EasyOgg, which uses Jogg and Jorbis. Both of which I have used myself before, but EasyOgg is absolutely what you would want.

You can find it here http://www.cokeandcode.com/index.html?page=libs

OggClip ogg = new OggClip("startup2.ogg");
ogg.loop();
ogg.setBalance(-1.0f);
ogg.pause();
ogg.resume();
ogg.stop();
ogg.setGain(1.0f);

I dont know if there is a javadoc, but it just THAT simple, and it works.

EasyOgg looks very promising indeed, but I’m a kinda discouraged by thread
‘Improving EasyOgg’ (http://www.java-gaming.org/index.php?topic=18766).

People seem to have all kind of issues with it and I see several
attempts, (that is, code), to get around those issues.

Looks like after all these years, there’s still no general,
simple and robust way for using OGG-files in games…

The issues that haven’t been address are with Java Sound, not the OGG code.

I use EasyOGG quite a bit - specifically in legends of yore and in my recent LD48 entry. No complaints other than Java Sound doesn’t always work on Linux.

Kev

I have used ogg files since October 2006 for my own first person shooter. Paul Lamb Sound Library works better than my own code (which was enough in the past). His library is quite simple and you can use it with OpenAL or JavaSound, what is wrong with it?

You can also decode Ogg using Jorbis/Jogg and then play it with OpenAL, which is what Slick does (also by Kevin)
OpenAL is surely more robust.
If you wanna do that, you can use Slick or LWJGL directly for OpenAL and then Jorbis/Jogg for decoding.

Also FMOD supports many formats, one of which is OGG. FMod is of course an industry standard for playing audio, and a java lib called NativeFmod (& Ex).
FMod is just brilliant. However its only free for non-commercial games.

You can use OpenAL through JOAL directly too.

I think EasyOGG does a fantastic job of giving you a high-level API over Java Sound. I’ve used it in a couple of projects between 2007 and now, and never had any problems with it. The fact that there is a thread about its bugs does not mean that you shouldn’t use it - look how many threads we’ve had complaining about Thread.sleep!

[quote]The issues that haven’t been address are with Java Sound, not the OGG code.

I use EasyOGG quite a bit - specifically in legends of yore and in my recent LD48 entry. No complaints other than Java Sound doesn’t always work on Linux.
[/quote]
Oh, ok. I was under the impression that you actually did not use it
yourself, because somewhere in the aforementioned thread, you said ;
‘Doh, shows how much I don’t use EasyOGG for my stuff doesn’t it ?’

But this eased my mind and I’ll give it a try now. :slight_smile:

[quote]Paul Lamb Sound Library works better than my own code (which was enough in the past). His library is quite simple and you can use it with OpenAL or JavaSound, what is wrong with it?
[/quote]
There’s nothing wrong with it, but it just overwhelms me a bit,
with its 3 soundsystems and 8 plugins to choose from. :slight_smile:

Haha, I suppose I have added quite a few options over the years, which can be a bit like walking into a giant candy store to someone new just looking at it. The API itself is very simple once you’ve downloaded the options you want (in a case like yours, probably the core SoundSystem, plus LibraryJavaSound and CodecJOrbis plug-ins). But it’s probably overkill for you particular needs anyway - depends on how compatible on various systems you need your application to be (read: problems with JavaSound on Linux and typically better to use OpenAL with JavaSound as a fall-back option). Since the need seems to come up fairly often, I’m planning to make a simpler non-3D sound system once I finish my Simple Line Mixer project. Hopefully it won’t be quite as overwhelming for folks to look at - one base library plus the codec plug-ins.

Yep . I’ve been using your sound system for a long time. I spent like 1-2 hours understanding how to use it , then encapsulated it in my own Sound classes, and I never looked at it again :slight_smile:

I think the effort is valid though. I’ve never had problems with it.

That is my case too. That is why I advise this library. EasyOGG is fine, I gave it a try some years ago but it relies on JavaSound and you may know its limitations…

Below is my attempt to load and play an OGG-file using the ‘OggInputStream’-class.
This code obviously won’t work as it is now, because I don’t know how to create
an AudioFormat-object based on the data available.
And my second problem is that I don’t understand how to use the ‘read’-method,
it needs an byte-array as argument, but since I don’t know the length of the
decoded OGG-file yet, I don’t know how large this array should be (?).

What should be done to get this code-sample working ?

  private void loadAndPlayOgg()
  {
    InputStream input = getClass().getResourceAsStream("explosion.ogg");    
    OggInputStream ogg = new OggInputStream(input);
    
    int format = ogg.getFormat();
    int rate   = ogg.getRate();    
    AudioFormat audioFormat = // How to create an AudioFormat with only these two parameters ??
    
    
    // I think that the size of 'inputBuffer' should be equal to the size of the decoded OGG-file,
    // but I don't know this size, so what now ? 
    
    byte[] inputBuffer = new byte[1024];
    try
    {      
      int readSize = 0;      
      while((readSize = input.read(inputBuffer, 0, 1024)) > -1)
      {
        ogg.read(inputBuffer, 0, readSize); 
      }
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    
    Clip clip = loadClip(inputBuffer, audioFormat);    
    if (clip != null)
    {
      clip.start();
    }   
    
  }
  
  private Clip loadClip(byte[] data, AudioFormat format)
  { 
    DataLine.Info info = new DataLine.Info(Clip.class, format);
    if (!AudioSystem.isLineSupported(info))
    {
      System.out.println("Unsupported Clip File");
      return null;
    }

    Clip clip = null;
    try
    {
      clip = (Clip) AudioSystem.getLine(info);
      clip.open(format, data, 0, data.length);
    }
    catch (LineUnavailableException e)
    {
      e.printStackTrace();
    }    
    return clip;
  }

For the audio format, you will need to determine the number of channels and sample size in bits. I expect you can get that from whatever is returned in “format”. If it indicates things like mono8, mono16, stereo8, and stereo16, then:
mono8: sampleSize 8, channels 1
mono16: sampleSize 16, channels 1
stereo8: sampleSize 8, channels 2
stereo16: sampleSize 16, channels 2

In which case you could create your AudioFormat instance like so:

AudioFormat audioFormat = new AudioFormat( (float) rate, sampleSize, channels, true, false );

I haven’t used OggInputStream before, so I’ll leave that question for someone else with the experience.

If you can get the length of the file from that OggInputStream there may be an easier way, but if not, you could just read the file in by chunks and append them together. The following is thrown together quickly from some source code I had sitting around, so check for typos. It should at least give you the basic idea:

public static final int BUFSIZE = 1024;
byte[] inputBuffer = new byte[BUFSIZE];
byte[] fullBuffer = null;

int readSize = readBuffer( input, inputBuffer );

if( readSize > 0 )
{
    if( readSize < BUFSIZE )
        fullBuffer = trimArray( inputBuffer, readSize );
    else
    {
        while( readSize > 0 )
        {
            readSize = readBuffer( input, inputBuffer );
            if( readSize > 0 )
                fullBuffer = appendByteArrays( fullBuffer, inputBuffer, readSize );
        }
    }
}
else
  ; //TODO: Handle no data situation

Clip clip = loadClip( fullBuffer, audioFormat );

I almost forgot; here are the methods I referenced in the above code:

/**
 * Attempts to read one buffer full of data, and returns
 * the number of bytes read in.
 * @param in Stream to read from.
 * @param buf Buffer to put the data into.
 * @return Bytes read, or 0 if error or end of stream.
 */
private int readBuffer( InputStream in, byte[] buf )
{
    try
    {     
        int readSize = 0;     
        while( ( readSize = in.read( buf, 0, BUFSIZE ) ) > -1 )
        {
            ogg.read( in, 0, readSize );
        }
    }
    catch( IOException e )
    {
        e.printStackTrace();
    }
    return readSize;
}



/**
 * Trims down the size of the array if it is larger than the specified
 * maximum length.
 * @param array Array containing audio data.
 * @param maxLength Maximum size this array may be.
 * @return New array.
 */
    private static byte[] trimArray( byte[] array, int maxLength )
    {
        byte[] trimmedArray = null;
        if( array != null && array.length > maxLength )
        {
            trimmedArray = new byte[maxLength];
            System.arraycopy( array, 0, trimmedArray, 0, maxLength );
        }
        return trimmedArray;
    }

/**
 * Creates a new array with the second array appended to the end of the first
 * array.
 * @param arrayOne The first array.
 * @param arrayTwo The second array.
 * @param arrayTwoBytes The number of bytes to append from the second array.
 * @return Byte array containing information from both arrays.
 */
    private static byte[] appendByteArrays( byte[] arrayOne, byte[] arrayTwo,
                                            int arrayTwoBytes )
    {
        byte[] newArray;
        int bytes = arrayTwoBytes;

        // Make sure we aren't trying to append more than is there:
        if( arrayTwo == null || arrayTwo.length == 0 )
            bytes = 0;
        else if( arrayTwo.length < arrayTwoBytes )
            bytes = arrayTwo.length;

        if( arrayOne == null && (arrayTwo == null || bytes <= 0) )
        {
            // no data, just return
            return null;
        }
        else if( arrayOne == null )
        {
            // create the new array, same length as arrayTwo:
            newArray = new byte[ bytes ];
            // fill the new array with the contents of arrayTwo:
            System.arraycopy( arrayTwo, 0, newArray, 0, bytes );
            arrayTwo = null;
        }
        else if( arrayTwo == null || bytes <= 0 )
        {
            // create the new array, same length as arrayOne:
            newArray = new byte[ arrayOne.length ];
            // fill the new array with the contents of arrayOne:
            System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
            arrayOne = null;
        }
        else
        {
            // create the new array large enough to hold both arrays:
            newArray = new byte[ arrayOne.length + bytes ];
            System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
            // fill the new array with the contents of both arrays:
            System.arraycopy( arrayTwo, 0, newArray, arrayOne.length,
                              bytes );
            arrayOne = null;
            arrayTwo = null;
        }

        return newArray;
    }

IndexOutOfBoundsException !!

Haha, I just appended stuff to the code that Jack_E posted… no guarantees there aren’t bugs. This was more of a way to programmaticaly explain the basic idea of reading the file in pieces and appending them together. (for me at least, that’s the easiest way to grasp unfamiliar concepts)

Yeah…some issues on this…

http://www.java-gaming.org/index.php/topic,24367.msg204698.html#msg204698

There is some interesting code in the above, generously supplied by princec, for playing Ogg files, as used in “Revenge of the Titans”.

I’m working on the plan of decoding OggVorbis into wav and storing them on the client. OggVorbis playback is guaranteed to take more resources than straight up PCM data, since you have to unpack the audio data as part of the streaming process. I’m guessing if you just have a lot of short Clips playing concurrently with no more than one long-playing OggVorbis file, you will be okay. But if you want to have more than that going on at the same time, it could get dicy. (Do other people who like and use the JOrbis.jar resource have problems with memory leaks when looping sounds?)

I keep getting side-tracked from that project, though.