症状のあるコードは次のとおりです。
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.zove.xuggleraudio;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IPacket;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
/**
* Class that represents an audio player with the expected
* controls (start, stop, pause, resume).
* @author Mciavilli
*/
public class Audio
{
//The name of the file to be played
private final String filename;
//Our connection to the mixer
private final SourceDataLine mLine;
//The index of the audio stream inside the file
private final int audioId;
//Xuggler media container
private final IContainer container;
//The stream decoder
private final IStreamCoder streamCoder;
/*
* Constructor that takes a String argument
*/
public Audio(String filename)
{
this.filename = filename;
//Create Xuggler container object
this.container = IContainer.make();
//Open the container
if(container.open(filename, IContainer.Type.READ, null) < 0)
throw new IllegalArgumentException("Invalid file name: " + this.filename);
//find the audio stream within contained streams
this.audioId = getAudioId(container);
//get the audio stream
IStream stream = container.getStream(audioId);
//get the stream decoder
this.streamCoder = stream.getStreamCoder();
//open the stream decoder
if (this.streamCoder.open() < 0)
throw new RuntimeException("could not open audio decoder for container: "
+ filename);
//Get a pipe to the sound mixer
this.mLine = readySoundSystem(streamCoder);
}
private int getAudioId(IContainer container)
{
//see how many streams are there
int numStreams = container.getNumStreams();
int audioId = -1;
for(int i = 0; i < numStreams ; i++)
{
IStream stream = container.getStream(i);
IStreamCoder streamCoder = stream.getStreamCoder();
if(streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO)
audioId = i;
break;
}//end for statement
//No audio stream found
if(audioId == -1)
throw new RuntimeException("Failed to find an audio stream in:" +
this.filename);
return audioId;
}//end method getAudioId
private SourceDataLine readySoundSystem(IStreamCoder aAudioCoder)
{
AudioFormat audioFormat = new AudioFormat(aAudioCoder.getSampleRate(),
(int)IAudioSamples.findSampleBitDepth(aAudioCoder.getSampleFormat()),
aAudioCoder.getChannels(),
true, /* xuggler defaults to signed 16 bit samples */
false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
try
{
SourceDataLine mLine = (SourceDataLine) AudioSystem.getLine(info);
/**
* if that succeeded, try opening the line.
*/
mLine.open(audioFormat);
/**
* And if that succeed, start the line.
*/
mLine.start();
}
catch (LineUnavailableException e)
{
throw new RuntimeException("could not open audio line");
}
return mLine;
}//end method readySoundSystem
/*
* starts playing the file.
* returns true if successful.
* should be called only once per Audio object
*/
public boolean start()
{
if(!mLine.isActive())
{
IPacket packet = IPacket.make();
while(container.readNextPacket(packet) >= 0)
{
/*
* Now we have a packet, let's see if it belongs to our audio stream
*/
if (packet.getStreamIndex() == this.audioId)
{
/*
* We allocate a set of samples with the same number of channels as the
* coder tells us is in this buffer.
*
* We also pass in a buffer size (1024 in our example), although Xuggler
* will probably allocate more space than just the 1024 (it's not important why).
*/
IAudioSamples samples = IAudioSamples.make(1024, this.streamCoder.getChannels());
/*
* A packet can actually contain multiple sets of samples (or frames of samples
* in audio-decoding speak). So, we may need to call decode audio multiple
* times at different offsets in the packet's data. We capture that here.
*/
int offset = 0;
/*
* Keep going until we've processed all data
*/
while(offset < packet.getSize())
{
int bytesDecoded = this.streamCoder.decodeAudio(samples, packet, offset);
if (bytesDecoded < 0)
throw new RuntimeException("got error decoding audio in: " + filename);
offset += bytesDecoded;
/*
* Some decoder will consume data in a packet, but will not be able to construct
* a full set of samples yet. Therefore you should always check if you
* got a complete set of samples from the decoder
*/
if (samples.isComplete())
{
playSound(samples);
}
}//end inner while block
}//end inner if block
else
{
/*
* This packet isn't part of our audio stream, so we just silently drop it.
*/
do {} while(false);
}//end else block
}//end outer while block
//success!
return true;
}//end outer if block
//The sound is already playing
return false;
}//end method start
private void playSound(IAudioSamples aSamples)
{
/**
* We're just going to dump all the samples into the line.
*/
byte[] rawBytes = aSamples.getData().getByteArray(0, aSamples.getSize());
this.mLine.write(rawBytes, 0, aSamples.getSize());
}//end method playJavaSound
/*
* stops the playback
* returns true if suucessful
*/
public boolean stop()
{
if(mLine.isActive())
{
this.mLine.stop();
return true;
}
return false;
}
public static void main(String args[]) throws InterruptedException
{
if(args.length != 1)
throw new IllegalArgumentException("illegal arguments passed");
Audio audio = new Audio(args[0]);
audio.start();
Thread.sleep(10 * 1000);
audio.stop();
}
}//end class Audio
問題の原因となる行は104行目です。
mLine.start();
デバッガーをチェックしたところ、この行が実行されるまでmLineオブジェクト(SourceDataLineオブジェクト)は問題ありません。これにより、mLineは「null」に等しくなります。
この問題はここと同じだと思います。
SourceDataLineの代わりにClipを使用しようとしましたが、同じ問題が発生しました。
興味深いのは、この問題は元のXugglerプログラムでは発生せず、start()を呼び出してもそれほど悪影響はなかったことです。