Fix for loading sound off the filesystem on android

I have managed to get sounds loading off the filesystem on android devices with a few changes to the AndroidAudioRender class.

Adding at the top of the file:
[java]
private com.jme3.asset.AssetManager jme3AssetManager;

class SoundFileInfo{
    FileDescriptor fd;
    long startOffset;
    long length;
}

[/java]

And some changes to playSource and playSourceInstance:
[java]
public void playSource(AudioSource src) {
if (audioDisabled) {
return;
}

    AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();

    MediaPlayer mp = musicPlaying.get(src);
    if (mp == null) {
        mp = new MediaPlayer();
        mp.setOnCompletionListener(this);
        mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
    }

    try {
        if (src.getStatus() == Status.Stopped) {
            mp.reset();
            AssetKey key = audioData.getAssetKey();

	// FIXME: Still need to get zip files working.
            FileDescriptor fd = null;
            long startOffset = 0;
            long length = 0;

	SoundFileInfo soundInfo = getSoundInfo(key);

            mp.setDataSource(soundInfo.fd, soundInfo.startOffset, soundInfo.length);
            mp.prepare();
            setSourceParams(src, mp);
            src.setChannel(0);
            src.setStatus(Status.Playing);
            musicPlaying.put(src, mp);
            mp.start();
        } else {
            mp.start();
        }
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

[/java]
and…
[java]
public void playSourceInstance(AudioSource src) {
if (audioDisabled) {
return;
}

    AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();

    if (!(audioData.getAssetKey() instanceof AudioKey)) {
        throw new IllegalArgumentException("Asset is not a AudioKey");
    }

    AudioKey assetKey = (AudioKey) audioData.getAssetKey();

    try {

        if (audioData.getId() < 0) { // found something to load
	SoundFileInfo soundInfo = getSoundInfo(assetKey);
            int soundId = soundPool.load(soundInfo.fd, soundInfo.startOffset, soundInfo.length, 1);

// int soundId = soundPool.load(
// assetManager.openFd(assetKey.getName()), 1);
audioData.setId(soundId);
}

        int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);

        if (channel == 0) {
            soundpoolStillLoading.put(audioData.getId(), src);
        } else {
            if (src.getStatus() != Status.Stopped) {
                soundPool.stop(channel);
                src.setStatus(Status.Stopped);
            }
            src.setChannel(channel); // receive a channel at the last
            setSourceParams(src);
            // playing at least


        }
    } catch (IOException e) {
        logger.log(Level.SEVERE,
                "Failed to load sound " + assetKey.getName(), e);
        audioData.setId(-1);
    }
}

[/java]
I also added the method getSoundInfo which returns all three required pieces of info to play the sound in a wrapped block
[java]
private SoundFileInfo getSoundInfo(AssetKey key) throws IOException {
SoundFileInfo ret = new SoundFileInfo();
if(jme3AssetManager != null){
AssetInfo aInfo = jme3AssetManager.locateAsset(key);
System.out.println(key);
if(aInfo != null){
System.out.println(aInfo.getClass());
InputStream is = aInfo.openStream();
if(is instanceof FileInputStream){
ret.fd = ((FileInputStream)is).getFD();
ret.startOffset = 0;
ret.length = is.available();
}
}
}
if(ret.fd == null){
AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
ret.fd = afd.getFileDescriptor();
ret.startOffset = afd.getStartOffset();
ret.length = afd.getLength();
}

    return ret;
}

[/java]

As far as the fixme comment I made about zip files, I can implement this, but it will require some code provided by Google (Apache license) to do without reinventing the wheel. If that won’t be detremental to the project, I’ll get on it, but wanted to check for opinions on that before I started.

3 Likes

@DragonEagle
I wasn’t aware there was an issue with playing sounds on Android. They seem to work on my projects. What was the issue?

Sounds work fine if loaded from the apk file using the game assets, but jme won’t look outside the apk file for sounds so if you try to load them off the file system, it gives a FileNotFoundException. You get a similar issue if trying to load them from a zip file.

oh, usually we were changing things in the asset build file so that the sounds were copied in android’s asset folder.

Doesn’t work if your game works with user supplied content. It was just something I needed to tweak for one of my projects and wanted to offer it as a contrib back. I can always just set it aside as a personal patch and re apply it as needed for my work.

1 Like

Here’s a link to the full patch file -p1 which includes the changes to JmeAndroidSystem which I forgot to add above
http://pastebin.com/PYsqAz0M

1 Like
@DragonEagle said: Doesn't work if your game works with user supplied content. It was just something I needed to tweak for one of my projects and wanted to offer it as a contrib back. I can always just set it aside as a personal patch and re apply it as needed for my work.
oh ok, right.

Thanks for the patch, that will definitely be useful.