【问题标题】:libGDX/Android: How to loop background music without the dreaded gap?libGDX/Android:如何在没有可怕间隙的情况下循环播放背景音乐?
【发布时间】:2015-06-01 09:29:17
【问题描述】:

我正在使用 libGDX 并面临背景音乐无法在各种 Android 设备(例如运行 Lollipop 的 Nexus 7)上完美循环的问题。每当轨道循环(即从结尾跳到开头)时,都会听到明显的间隙。现在我想知道如何在没有干扰间隙的情况下循环播放背景音乐?

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

  • 确保轨道中的样本数量是轨道采样率的精确倍数(如 SO 中某处所述)。
  • .ogg、.m4a、.mp3 和 .wav 等各种音频格式(.ogg 似乎是 SO 的首选解决方案,但不幸的是,它不适用于我的情况)。
  • 使用带有 setLooping(true) 的 Androids MediaPlayer 而不是 libGDX Music 类。
  • 使用 Android 的 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(); }
        }
    

您知道代码 sn-p 有什么问题吗?

【问题讨论】:

标签: android audio libgdx background-music


【解决方案1】:

仅作记录: 它调整为无法解决。最后,我们在文件中多次循环播放背景音乐。这样,差距出现的频率就会降低。这不是问题的真正解决方案,而是我们能找到的最佳解决方法。

【讨论】:

  • 您是如何得出无法解决的结论的?有什么具体的,还是仅仅因为您尝试了所有方法但没有任何效果?
  • Android 错误跟踪器存在问题 - 是的,我在放弃之前尝试了所有可能的选项来循环播放背景音乐。
【解决方案2】:

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

解决方案需要使用音频扩展(已弃用但工作正常),如果您在网上找不到链接here是我正在使用的罐子,还需要一些外部存储空间。

摘要如下

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

这是一些代码

提取音乐文件并保存到外部存储,文件是内部音乐文件的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();
    }
}

【讨论】:

  • 感谢您的帮助。我没有尝试过,但无论如何都会接受你的回答。抱歉耽搁了五年半;-)
猜你喜欢
  • 2020-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-24
  • 1970-01-01
相关资源
最近更新 更多