WedX - журнал о программировании и компьютерных науках

SourceDataLine.start() обнуляет объект

Вот код с симптомами:

      /*
 * 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».

Я думаю, что эта проблема такая же, как здесь.

Я также пытался использовать Clip вместо SourceDataLine, что привело к той же проблеме.

Что интересно, эта проблема не возникает в исходная программа Xuggler, и вызов start() не имел такого плохого эффекта.


  • Тот факт, что вы можете вызывать start(), означает, что mLine в этот момент не равно нулю. Вы уверены, что правильно читаете отладчик? Что произойдет, если вы запустите код нормально (т.е. какую проблему вы видите?) 20.09.2012
  • Объект имеет значение null после вызова start(), что вызывает исключение NullPointerException в if(!mLine.isActive()). 20.09.2012

Ответы:


1

У вас есть как переменная-член mLine, так и локальная переменная mLine. Только последний когда-либо назначается. Когда он выходит за пределы области видимости, в отладчике вы видите переменную-член, которая по-прежнему имеет значение null.

19.09.2012
  • Что ж, mLine на самом деле обнуляется до того, как метод readySoundSystem когда-либо завершится. Если вы заметили, я возвращаю локальную mLine для сохранения в переменной-члене. Последняя строка конструктора this.mLine = readySoundSystem(streamCoder);.Возвращенный mLine имеет значение null сразу после вызова mLine.start(); и перед возвратом mline;. 20.09.2012
  • @MadeOfAir Вы не понимаете свой собственный код. mLine, которое вы возвращаете, является переменной-членом, которая никогда не переставала быть нулевой. Локальный mLine, который вы инициализировали, выпал из области видимости после блока try. 26.09.2012

  • 2

    EJP верен выше в том смысле, что ваша переменная mLine ограничена только блоком try. (Мне пришлось самому запустить ее в отладчике, прежде чем она стала для меня понятна. Подсветка синтаксиса помогла впоследствии, окрашивая переменную экземпляра в другой цвет, чем локальная переменная.) Возвращаемая вами mLline на самом деле является переменной экземпляра.

        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;
    

    потому что строка возврата находится вне блока try, в котором она была объявлена. На практике повторное использование имени переменной экземпляра в качестве локальной переменной небезопасно именно по этой причине. Если вы попытаетесь переименовать переменную во что-то вроде «линии» с помощью инструмента рефакторинга в среде IDE, такой как Eclipse или Idea, тогда он переименует только ссылки в области попытки, еще больше подчеркнув вашу ошибку.

        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");
        }
    
    25.09.2012
    Новые материалы

    Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
    Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

    Лицензии с открытым исходным кодом: руководство для разработчиков и создателей
    В динамичном мире разработки программного обеспечения открытый исходный код стал мощной парадигмой, способствующей сотрудничеству, инновациям и прогрессу, движимому сообществом. В основе..

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..


    Для любых предложений по сайту: [email protected]