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");
    }