Java SourceDataLine.start()使对象为空
以下是出现症状的代码:Java SourceDataLine.start()使对象为空,java,audio,nullpointerexception,javasound,xuggler,Java,Audio,Nullpointerexception,Javasound,Xuggler,以下是出现症状的代码: /* * 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.xug
/*
* 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
当我检查调试器时,mLine对象(SourceDataLine对象)在执行这一行之前是正常的,这会导致mLine等于“null”
我想这个问题和你的一样
我还尝试使用Clip而不是SourceDataLine,结果也出现了同样的问题
有趣的是,这个问题在中没有发生,并且调用start()也没有那么坏的影响。您有一个成员变量mLine和一个局部变量mLine。只有后者才会被分配。当它超出范围时,您在调试器中看到的是成员变量,该变量仍然为空。EJP在上面是正确的,因为您的mLine变量的范围仅限于try块。(在对我有意义之前,我必须自己在调试器中运行它。之后,语法高亮显示起到了帮助作用,使我的实例变量的颜色不同于局部变量。)返回的mLine实际上就是实例变量
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;
基本上与以下内容相同:
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 this.mLine;
因为返回mLine在声明它的try块之外。实际上,由于这个原因,将实例变量的名称重新用作局部变量是不安全的。如果您试图在诸如Eclipse或Idea之类的IDE中使用重构工具将变量重命名为类似“line”的名称,那么它只会重命名try范围中的引用,从而进一步突出显示您的bug
try
{
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
/**
* if that succeeded, try opening the line.
*/
line.open(audioFormat);
/**
* And if that succeed, start the line.
*/
line.start();
}
catch (LineUnavailableException e)
{
throw new RuntimeException("could not open audio line");
}
return mLine;
如果然后尝试在return语句中手动重新设置引用的键,则会出现编译错误
try
{
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
/**
* if that succeeded, try opening the line.
*/
line.open(audioFormat);
/**
* And if that succeed, start the line.
*/
line.start();
}
catch (LineUnavailableException e)
{
throw new RuntimeException("could not open audio line");
}
return line; //Will not compile!
正确的修复方法是将return语句塞入try块中:
try
{
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
/**
* if that succeeded, try opening the line.
*/
line.open(audioFormat);
/**
* And if that succeed, start the line.
*/
line.start();
return line;
}
catch (LineUnavailableException e)
{
throw new RuntimeException("could not open audio line");
}
您可以调用
start()
这一事实意味着mLine
此时不为空。您确定正确读取了调试器吗?如果您正常运行代码会发生什么情况(即,您看到了什么问题?)。调用start()后,对象为null。这会导致“if(!mLine.isActive())”处出现NullPointerException。那么,“mLine”实际上在方法“readySoundSystem”完成之前为null。如果您观察到,我将返回本地“mLine”要保存在成员变量中。构造函数的最后一行“this.mLine=readySoundSystem(streamCoder);”。在调用“mLine.start();”之后和“return mLine;”之前,返回的“mLine”为空@madeofar您不理解自己的代码。您返回的mLine
是成员变量,它从未停止过为null。您初始化的本地mLine
在try
块之后超出范围。
try
{
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
/**
* if that succeeded, try opening the line.
*/
line.open(audioFormat);
/**
* And if that succeed, start the line.
*/
line.start();
return line;
}
catch (LineUnavailableException e)
{
throw new RuntimeException("could not open audio line");
}