Reading sound from a stream

I have a problem… I would like to play sounds that are in a different JAR or ZIP from an applet. At the moment, I load them using the “jar:[url]!/[sound]” technique. It works fine.

However, I would like to have progress bars that keep track of these sounds. I currently have a working progress bar that uses a URLConnection object and a ZipInputStream so I can compare the incoming bytes to the expected bytes. It can return a file’s contents as a byte[]. However, when I try to construct a sound with this code:

// 'input' is the object that reads a file from a stream
byte[] b = input.getBytes("mysound.wav"); /* returns byte[] of the expected length, so I'm pretty sure that there's no extra or missing data */
play(b);

public void play(byte[] b) {
   AudioData ad = new AudioData(b);
   AudioDataStream = new AudioDataStream(ad);
   AudioPlayer.player.start(ads);
}

the AudioPlayer plays the sound with a slight ‘click’ at the beginning, like it’s playing the sound’s header, and if I use a ContinuousDataStream, it plays before each loop of the sound. If I strip the header from the byte[] before calling play(), I hear a corrupted version of the sound (seems reasonable, since AudioData can’t figure out what the sound’s structure if I strip the header…).

Is there a better way to convert a byte[] into a sound?

It seems (I haven’t tried this yet; I get confused by the documentation) that I could use the AudioInputStream and Line object to do this, but I get lost in all of the options and sound engineering jargon.

Never mind. I figured it out. If I know my sample rate, sample size in bits,channels, whether it is signed, and whether it is bigEndian, I can use this constructor:

AudioFormat(float sampleRate, int sampleSizeInBits, int channels, boolean signed, boolean bigEndian) 

to create an AudioFormat object. For example:

 AudioFormat af = new AudioFormat(11025, 8, 1, false, false); // an 8-bit, mono PCM (like a WAV) at 11025khz

With that object, I can create a DataLine.Info object:

  DataLine.Info info = new DataLine.Info(Clip.class, af, 1024); // not sure what the buffer is for yet, but 1024 seems fine for now

then create a Line as a Clip, open it, and start it playing:

  Clip c = (Clip) AudioSystem.getLine(info);
  c.open(af, b, 0, b.length);
  c.start();

Lastly, I remove the header from the byte array so the click goes away.

int headerSize = 64; // not actual size. Brute force, actually. :-)
for (int i=0; i<headerSize; i++) {
b[i] = -126; // signed byte of 1; 1 seems to work better than 0 for some reason?
}

So, if I know exactly what format my sounds are in, I can play them without the click. (Interestingly, loop() still has a click, so I guess I would write a sound player with a loop() command that finds the sound’s duration and just does start() again.)

I hope this is useful for somebody else who wants to play streamed sounds in an applet without a click.

Now to search for (or implement!) the equivalent of AudioFileStream.getFormat(), so the sound file’s format doesn’t have to be known beforehand… if anybody has a suggestion, I’d love to hear it.