libGDX / Android:如何在没有可怕差距的情况下循环播放背景音乐?

时间:2015-03-27 22:16:13

标签: android audio libgdx background-music

我正在使用libGDX并面临背景音乐在各种Android设备(例如运行Lollipop的Nexus 7)上无法完美循环的问题。每当轨道循环(即从末端跳到开始)时,明显可见的间隙是可以接受的。现在我想知道背景音乐如何在没有令人不安的间隙的环路中播放?

我已经尝试了各种方法,如:

  • 确保轨道中的样本数量是轨道采样率的精确倍数(如此处所提到的那样)。
  • 各种音频格式,如.ogg,.m4a,.mp3和.wav(.ogg似乎是SO的首选解决方案,但遗憾的是它在我的情况下不起作用)。
  • 使用Androids MediaPlayer和setLooping(true)代替libGDX Music类。
  • 使用过的Androids MediaPlayer.setNextMediaPlayer()。代码如下所示,它播放两个轨道之间没有间隙,但不幸的是,第二个MediaPlayer完成后,第一个不会重新开始!

        /* initialization */
        afd = context.getAssets().openFd(filename);
        firstBackgroundMusic.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
        firstBackgroundMusic.prepare();
        firstBackgroundMusic.setOnCompletionListener(this);
    
        secondBackgroundMusic.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
        secondBackgroundMusic.prepare();
        secondBackgroundMusic.setOnCompletionListener(this);
    
        firstBackgroundMusic.setNextMediaPlayer(secondBackgroundMusic);
        secondBackgroundMusic.setNextMediaPlayer(firstBackgroundMusic);
    
        firstBackgroundMusic.start();
    
    
    
        @Override
        public void onCompletion(MediaPlayer mp) {
            mp.stop();
            try {
                mp.prepare();
            } catch (IOException e) { e.printStackTrace(); }
        }
    

任何想法代码片段有什么问题?

2 个答案:

答案 0 :(得分:3)

仅供记录: 它调整为无法解决。最后,我们在文件中多次循环播放背景音乐。这种差距似乎不那么频繁。它不能解决问题,但我们能找到最好的解决方法。

答案 1 :(得分:0)

这是一个古老的问题,但如果有人遇到同样的问题,我会提供解决方案。

该解决方案需要使用音频扩展程序(已弃用但效果很好),如果您无法找到在线链接here是我正在使用的jar,也需要一些外部存储空间。

摘要如下

  1. 使用解码器(用于ogg的VorbisDecoder类或用于mp3的Mpg123Decoder)提取原始音乐数据并将其保存到外部存储器(您可以检查它是否已经存在,以便只需要提取一次,因为它需要一些时间。)
  2. 使用刚保存到外部存储的文件
  3. 创建RandomAccessFile
  4. 播放时将RandomAccessFile指针设置为文件中的正确位置并读取数据段
  5. 使用AudioDevice类
  6. 播放上述数据段

    这是一些代码

    提取音乐文件并将其保存到外部存储器,文件是内部音乐文件的FileHandle,这是一个ogg,这就是我们使用VorbisDecoder的原因

        FileHandle external=Gdx.files.external("data/com.package.name/music/"+file.name());
        file.copyTo(external);
        VorbisDecoder decoder = new VorbisDecoder(external);
        FileHandle extreactedDataFile=Gdx.files.external("data/com.package.name/music/"+file.nameWithoutExtension()+".mdata");
        if(extreactedDataFile.exists())extreactedDataFile.delete();
        ShortBuffer sbuffer=ByteBuffer.wrap(shortBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
        while(true){
            if(LogoScreen.shouldBreakMusicLoad)break;
            int num=decoder.readSamples(samples, 0,samples.length);
            sbuffer.put(samples,0,num);
            sbuffer.position(0);
            extreactedDataFile.writeBytes(shortBytes,0,num*2, true);
            if(num<=0)break;
        }
        external.delete();
    

    创建一个指向我们刚刚创建的文件的RandomAccessFile

        if(extreactedDataFile.exists()){
            try {
                raf=new RandomAccessFile(Gdx.files.external(extreactedDataFile.path()).file(), "r");
                raf.seek(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    创建一个缓冲区,以便我们可以将从文件中读取的字节转换为一个可以转发到AudioDevice的短数组

    public byte[] rafbufferBytes=new byte[length*2];
    public short[] rafbuffer=new short[length];
    public ShortBuffer sBuffer=ByteBuffer.wrap(rafbufferBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
    

    当我们想要播放文件时,我们创建了一个AudioDevice和一个新线程,我们从raf文件中不断读取并将其提供给AudioDevice

        device = Gdx.audio.newAudioDevice((int)rate/*the hrz of the music e.g 44100*/,isthemusicMONO?);
        currentBytes=0;//set the file to the beggining
        playbackThread = new Thread(new Runnable() {
            @Override
            public synchronized  void run() {
                    while (playing) {
                            if(raf!=null){
                                int length=raf.read(rafbufferBytes);
                                if(length<=0){
                                    ocl.onCompletion(DecodedMusic.this);
                                    length=raf.read(rafbufferBytes);
                                }
                                sBuffer.get(rafbuffer);
                                sBuffer.position(0);
                                if(length>20){
                                    try{
                                        device.writeSamples(rafbuffer,0,length/2);
                                        fft.spectrum(rafbuffer, spectrum);
                                        currentBytes+=length;
                                    }catch(com.badlogic.gdx.utils.GdxRuntimeException ex){
                                        ex.printStackTrace();
                                        device = Gdx.audio.newAudioDevice((int)(rate),MusicPlayer.mono);
                                    }
                                }
                            }
                        }
                    }
        });
        playbackThread.setDaemon(true);
        playbackThread.start();
    

    当我们想要寻找职位时

    public void seek(float pos){
        currentBytes=(int) (rate*pos);
        try {
            raf.seek(currentBytes*4);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }