1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.backends.android; 18 19 import java.io.FileDescriptor; 20 import java.io.IOException; 21 import java.util.ArrayList; 22 import java.util.List; 23 24 import android.app.Activity; 25 import android.content.Context; 26 import android.content.res.AssetFileDescriptor; 27 import android.media.AudioManager; 28 import android.media.MediaPlayer; 29 import android.media.SoundPool; 30 31 import com.badlogic.gdx.Audio; 32 import com.badlogic.gdx.Files.FileType; 33 import com.badlogic.gdx.audio.AudioDevice; 34 import com.badlogic.gdx.audio.AudioRecorder; 35 import com.badlogic.gdx.audio.Music; 36 import com.badlogic.gdx.audio.Sound; 37 import com.badlogic.gdx.files.FileHandle; 38 import com.badlogic.gdx.utils.GdxRuntimeException; 39 40 /** An implementation of the {@link Audio} interface for Android. 41 * 42 * @author mzechner */ 43 public final class AndroidAudio implements Audio { 44 private final SoundPool soundPool; 45 private final AudioManager manager; 46 protected final List<AndroidMusic> musics = new ArrayList<AndroidMusic>(); 47 48 public AndroidAudio (Context context, AndroidApplicationConfiguration config) { 49 if (!config.disableAudio) { 50 soundPool = new SoundPool(config.maxSimultaneousSounds, AudioManager.STREAM_MUSIC, 100); 51 manager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 52 if (context instanceof Activity) { 53 ((Activity)context).setVolumeControlStream(AudioManager.STREAM_MUSIC); 54 } 55 } else { 56 soundPool = null; 57 manager = null; 58 } 59 } 60 61 protected void pause () { 62 if (soundPool == null) { 63 return; 64 } 65 synchronized (musics) { 66 for (AndroidMusic music : musics) { 67 if (music.isPlaying()) { 68 music.pause(); 69 music.wasPlaying = true; 70 } else 71 music.wasPlaying = false; 72 } 73 } 74 this.soundPool.autoPause(); 75 } 76 77 protected void resume () { 78 if (soundPool == null) { 79 return; 80 } 81 synchronized (musics) { 82 for (int i = 0; i < musics.size(); i++) { 83 if (musics.get(i).wasPlaying) musics.get(i).play(); 84 } 85 } 86 this.soundPool.autoResume(); 87 } 88 89 /** {@inheritDoc} */ 90 @Override 91 public AudioDevice newAudioDevice (int samplingRate, boolean isMono) { 92 if (soundPool == null) { 93 throw new GdxRuntimeException("Android audio is not enabled by the application config."); 94 } 95 return new AndroidAudioDevice(samplingRate, isMono); 96 } 97 98 /** {@inheritDoc} */ 99 @Override 100 public Music newMusic (FileHandle file) { 101 if (soundPool == null) { 102 throw new GdxRuntimeException("Android audio is not enabled by the application config."); 103 } 104 AndroidFileHandle aHandle = (AndroidFileHandle)file; 105 106 MediaPlayer mediaPlayer = new MediaPlayer(); 107 108 if (aHandle.type() == FileType.Internal) { 109 try { 110 AssetFileDescriptor descriptor = aHandle.getAssetFileDescriptor(); 111 mediaPlayer.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength()); 112 descriptor.close(); 113 mediaPlayer.prepare(); 114 AndroidMusic music = new AndroidMusic(this, mediaPlayer); 115 synchronized (musics) { 116 musics.add(music); 117 } 118 return music; 119 } catch (Exception ex) { 120 throw new GdxRuntimeException("Error loading audio file: " + file 121 + "\nNote: Internal audio files must be placed in the assets directory.", ex); 122 } 123 } else { 124 try { 125 mediaPlayer.setDataSource(aHandle.file().getPath()); 126 mediaPlayer.prepare(); 127 AndroidMusic music = new AndroidMusic(this, mediaPlayer); 128 synchronized (musics) { 129 musics.add(music); 130 } 131 return music; 132 } catch (Exception ex) { 133 throw new GdxRuntimeException("Error loading audio file: " + file, ex); 134 } 135 } 136 137 } 138 139 /** Creates a new Music instance from the provided FileDescriptor. It is the caller's responsibility to close the file 140 * descriptor. It is safe to do so as soon as this call returns. 141 * 142 * @param fd the FileDescriptor from which to create the Music 143 * 144 * @see Audio#newMusic(FileHandle) 145 */ 146 public Music newMusic (FileDescriptor fd) { 147 if (soundPool == null) { 148 throw new GdxRuntimeException("Android audio is not enabled by the application config."); 149 } 150 151 MediaPlayer mediaPlayer = new MediaPlayer(); 152 153 try { 154 mediaPlayer.setDataSource(fd); 155 mediaPlayer.prepare(); 156 157 AndroidMusic music = new AndroidMusic(this, mediaPlayer); 158 synchronized (musics) { 159 musics.add(music); 160 } 161 return music; 162 } catch (Exception ex) { 163 throw new GdxRuntimeException("Error loading audio from FileDescriptor", ex); 164 } 165 } 166 167 /** {@inheritDoc} */ 168 @Override 169 public Sound newSound (FileHandle file) { 170 if (soundPool == null) { 171 throw new GdxRuntimeException("Android audio is not enabled by the application config."); 172 } 173 AndroidFileHandle aHandle = (AndroidFileHandle)file; 174 if (aHandle.type() == FileType.Internal) { 175 try { 176 AssetFileDescriptor descriptor = aHandle.getAssetFileDescriptor(); 177 AndroidSound sound = new AndroidSound(soundPool, manager, soundPool.load(descriptor, 1)); 178 descriptor.close(); 179 return sound; 180 } catch (IOException ex) { 181 throw new GdxRuntimeException("Error loading audio file: " + file 182 + "\nNote: Internal audio files must be placed in the assets directory.", ex); 183 } 184 } else { 185 try { 186 return new AndroidSound(soundPool, manager, soundPool.load(aHandle.file().getPath(), 1)); 187 } catch (Exception ex) { 188 throw new GdxRuntimeException("Error loading audio file: " + file, ex); 189 } 190 } 191 } 192 193 /** {@inheritDoc} */ 194 @Override 195 public AudioRecorder newAudioRecorder (int samplingRate, boolean isMono) { 196 if (soundPool == null) { 197 throw new GdxRuntimeException("Android audio is not enabled by the application config."); 198 } 199 return new AndroidAudioRecorder(samplingRate, isMono); 200 } 201 202 /** Kills the soundpool and all other resources */ 203 public void dispose () { 204 if (soundPool == null) { 205 return; 206 } 207 synchronized (musics) { 208 // gah i hate myself.... music.dispose() removes the music from the list... 209 ArrayList<AndroidMusic> musicsCopy = new ArrayList<AndroidMusic>(musics); 210 for (AndroidMusic music : musicsCopy) { 211 music.dispose(); 212 } 213 } 214 soundPool.release(); 215 } 216 } 217