2

I want to implement a cool effect, when there is an explosion the music gets slightly slower for a moment.

The Music class of libgdx doesn't allow changing the pitch of the sound, I tried using the Sound class instead of the Music to play my music but its very slow at loading the files ( 5 sec for a 5 mb file, on Desktop! ).

So, the question is if there is a way to workaround this, or if there is an external library, that works both on desktop and android and can work along with libgdx.

user3666197
  • 1
  • 6
  • 50
  • 92
SteveL
  • 3,331
  • 4
  • 32
  • 57

3 Answers3

8

After some searching I found a way by editing the libgdx source code.

You need to use the AudioDevice object to play your music sample by sample ,to do that you need to include the audio-extension of libgdx.

We gonna edit the libgdx source code so you need to download it and replace the gdx.jar , gdx-backend-android.jar and gdx-backend-lwjgl.jar with the correct libgdx projects(they have the same name without the jar extension)

1)Edit the AudioDevice.java inside com.badlogic.gdx.audio package of the gdx project

add the following code inside the interface

public void setSpeed(float val);

2)Edit the AndroidAudioDevice.java inside com.badlogic.gdx.backends.android package of the gdx-backend-android project

The Android side of the AudioDevice class relies on the AudioTrack class of the Android sdk ,this class has a setPlaybackRate(..) method.

add the following code inside the class

@Override
public void setSpeed (float speed) {
    track.setPlaybackRate((int)(track.getSampleRate()*speed));
}

3)Edit the OpenALAudioDevice.java inside com.badlogic.gdx.backends.lwjgl.audio package of the gdx-backend-lwjgl project

The Desktop side of the AudioDevice relies on OpenAL (the opengl of audio) which has a handy set pitch method

add the following inside the class

@Override
public void setSpeed (float speed) {
    alSourcef(sourceID, AL_PITCH, speed);
}

4)Play the audio

Here is the code for loading and playing the sound file

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.AudioDevice;
import com.badlogic.gdx.audio.io.Mpg123Decoder;
import com.badlogic.gdx.files.FileHandle;

public class MusicBeat {

static short[] samples = new short[2048];
Mpg123Decoder decoder;
AudioDevice device;
static FileHandle externalFile;
public boolean playing=true;
public MusicBeat(String name )
{
        FileHandle file=Gdx.files.internal(name);
        FileHandle external=Gdx.files.external("myappname/"+name);
        if(!external.exists())file.copyTo(external); //copy the file to the external storage only if it doesnt exists yet
        decoder = new Mpg123Decoder(external);
        device = Gdx.audio.newAudioDevice(decoder.getRate(),decoder.getChannels() == 1 ? true : false);
        playing=false;
        externalFile=file;
}

void play()
{
    playing=true;
    Thread playbackThread = new Thread(new Runnable() {
        @Override
        public synchronized  void run() {
            int readSamples = 0;
            while ( playing) {
                if(decoder!=null){
                    if((readSamples = decoder.readSamples(samples, 0,samples.length))<=0){
                        decoder.dispose();
                        decoder = new Mpg123Decoder(externalFile);
                        playing=false;
                    }
                    device.writeSamples(samples, 0, readSamples);
                }
            }
        }
    });
    playbackThread.setDaemon(true);
    playbackThread.start();
}

public void stop(){
    playing=false;
    decoder.dispose();
}
public void setVolume(float vol){
    device.setVolume(vol);
}
public void setSpeed(float speed){
    device.setSpeed(speed);
}

}

Where to get the audio extension?(required for the AudioDevice)

The audio extension seems to have been deprecated and the jars cant be found easily, I have uploaded them here, its an old version but should work just fine.

Easier way?

If your game is only intent to run on desktop ,the Music class of libgdx on desktop relies again on OpenAL which gives as the power to play with the pitch ,so you just need to edit the Music interface(OpenALMusic on desktop) instead of the AudioDevice and get out the whole play sample by sample thing out of the equation,unfortunately as dawez said the Music class on android relies on MediaPlayer which is not giving us the pitch change option.

Conclusion :This method doesnt seem nice to me ,If your game really needs the pitch thing and it doesn't makes sense without it then go with it ,otherwise its just too much effort for such a small detail .

SteveL
  • 3,331
  • 4
  • 32
  • 57
  • +1 for finding a solution/workaround and posting the code for something that so far has been known as "impossible" . Well done! – dawez Nov 01 '14 at 19:50
1

It seems that you cannot change the pitch of Music in Android.

To play music in libgdx you refer to the Interface Music:

public interface Music extends Disposable {

This is implemented by

package com.badlogic.gdx.backends.android;
public class AndroidMusic implements Music, MediaPlayer.OnCompletionListener {
        private MediaPlayer player;

You are interested in the player object. That is of type MediaPlayer

package android.media;
public class MediaPlayer {

So now it boils down to the question if android is able to support pitching on the MediaPlayer class. Short answer no, long answer:Speed Control of MediaPlayer in Android

A workaround that you can do is to use a SoundPool even if that is slow, the loading can be started while the user is the loading screen. Otherwise you can try can split the music files in chunks and load them as you go still using a SoundPool. So you would do something like a lazy loading when the current part is coming to its end.

If you manage to find a suitable a solution, please post the relative code!

Community
  • 1
  • 1
dawez
  • 2,714
  • 2
  • 29
  • 50
1

With the release of Android 6.0 (API level 23), "PlaybackParams" can be added to the MediaPlayer. These include playback speed and pitch among others.

Robert Columbia
  • 6,313
  • 15
  • 32
  • 40
  • This is not a qualified answer. It is treated as a comment and it should be posted there. Once you have enough reputation you can do that. – Mathews Sunny Dec 14 '17 at 13:38