1 /* 2 * Copyright (C) 2012 The Android Open Source Project 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 android.media; 18 19 import android.media.AudioManager; 20 import android.media.SoundPool; 21 import android.util.Log; 22 23 /** 24 * <p>A class for producing sounds that match those produced by various actions 25 * taken by the media and camera APIs. </p> 26 * 27 * <p>This class is recommended for use with the {@link android.hardware.camera2} API, since the 28 * camera2 API does not play any sounds on its own for any capture or video recording actions.</p> 29 * 30 * <p>With the older {@link android.hardware.Camera} API, use this class to play an appropriate 31 * camera operation sound when implementing a custom still or video recording mechanism (through the 32 * Camera preview callbacks with 33 * {@link android.hardware.Camera#setPreviewCallback Camera.setPreviewCallback}, or through GPU 34 * processing with {@link android.hardware.Camera#setPreviewTexture Camera.setPreviewTexture}, for 35 * example), or when implementing some other camera-like function in your application.</p> 36 * 37 * <p>There is no need to play sounds when using 38 * {@link android.hardware.Camera#takePicture Camera.takePicture} or 39 * {@link android.media.MediaRecorder} for still images or video, respectively, 40 * as the Android framework will play the appropriate sounds when needed for 41 * these calls.</p> 42 * 43 */ 44 public class MediaActionSound { 45 private static final int NUM_MEDIA_SOUND_STREAMS = 1; 46 47 private SoundPool mSoundPool; 48 private int[] mSoundIds; 49 private int mSoundIdToPlay; 50 51 private static final String[] SOUND_FILES = { 52 "/system/media/audio/ui/camera_click.ogg", 53 "/system/media/audio/ui/camera_focus.ogg", 54 "/system/media/audio/ui/VideoRecord.ogg", 55 "/system/media/audio/ui/VideoRecord.ogg" 56 }; 57 58 private static final String TAG = "MediaActionSound"; 59 /** 60 * The sound used by 61 * {@link android.hardware.Camera#takePicture Camera.takePicture} to 62 * indicate still image capture. 63 * @see #play 64 */ 65 public static final int SHUTTER_CLICK = 0; 66 67 /** 68 * A sound to indicate that focusing has completed. Because deciding 69 * when this occurs is application-dependent, this sound is not used by 70 * any methods in the media or camera APIs. 71 * @see #play 72 */ 73 public static final int FOCUS_COMPLETE = 1; 74 75 /** 76 * The sound used by 77 * {@link android.media.MediaRecorder#start MediaRecorder.start()} to 78 * indicate the start of video recording. 79 * @see #play 80 */ 81 public static final int START_VIDEO_RECORDING = 2; 82 83 /** 84 * The sound used by 85 * {@link android.media.MediaRecorder#stop MediaRecorder.stop()} to 86 * indicate the end of video recording. 87 * @see #play 88 */ 89 public static final int STOP_VIDEO_RECORDING = 3; 90 91 private static final int SOUND_NOT_LOADED = -1; 92 93 /** 94 * Construct a new MediaActionSound instance. Only a single instance is 95 * needed for playing any platform media action sound; you do not need a 96 * separate instance for each sound type. 97 */ MediaActionSound()98 public MediaActionSound() { 99 mSoundPool = new SoundPool(NUM_MEDIA_SOUND_STREAMS, 100 AudioManager.STREAM_SYSTEM_ENFORCED, 0); 101 mSoundPool.setOnLoadCompleteListener(mLoadCompleteListener); 102 mSoundIds = new int[SOUND_FILES.length]; 103 for (int i = 0; i < mSoundIds.length; i++) { 104 mSoundIds[i] = SOUND_NOT_LOADED; 105 } 106 mSoundIdToPlay = SOUND_NOT_LOADED; 107 } 108 109 /** 110 * Preload a predefined platform sound to minimize latency when the sound is 111 * played later by {@link #play}. 112 * @param soundName The type of sound to preload, selected from 113 * SHUTTER_CLICK, FOCUS_COMPLETE, START_VIDEO_RECORDING, or 114 * STOP_VIDEO_RECORDING. 115 * @see #play 116 * @see #SHUTTER_CLICK 117 * @see #FOCUS_COMPLETE 118 * @see #START_VIDEO_RECORDING 119 * @see #STOP_VIDEO_RECORDING 120 */ load(int soundName)121 public synchronized void load(int soundName) { 122 if (soundName < 0 || soundName >= SOUND_FILES.length) { 123 throw new RuntimeException("Unknown sound requested: " + soundName); 124 } 125 if (mSoundIds[soundName] == SOUND_NOT_LOADED) { 126 mSoundIds[soundName] = 127 mSoundPool.load(SOUND_FILES[soundName], 1); 128 } 129 } 130 131 /** 132 * <p>Play one of the predefined platform sounds for media actions.</p> 133 * 134 * <p>Use this method to play a platform-specific sound for various media 135 * actions. The sound playback is done asynchronously, with the same 136 * behavior and content as the sounds played by 137 * {@link android.hardware.Camera#takePicture Camera.takePicture}, 138 * {@link android.media.MediaRecorder#start MediaRecorder.start}, and 139 * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p> 140 * 141 * <p>With the {@link android.hardware.camera2 camera2} API, this method can be used to play 142 * standard camera operation sounds with the appropriate system behavior for such sounds.</p> 143 144 * <p>With the older {@link android.hardware.Camera} API, using this method makes it easy to 145 * match the default device sounds when recording or capturing data through the preview 146 * callbacks, or when implementing custom camera-like features in your application.</p> 147 * 148 * <p>If the sound has not been loaded by {@link #load} before calling play, 149 * play will load the sound at the cost of some additional latency before 150 * sound playback begins. </p> 151 * 152 * @param soundName The type of sound to play, selected from 153 * SHUTTER_CLICK, FOCUS_COMPLETE, START_VIDEO_RECORDING, or 154 * STOP_VIDEO_RECORDING. 155 * @see android.hardware.Camera#takePicture 156 * @see android.media.MediaRecorder 157 * @see #SHUTTER_CLICK 158 * @see #FOCUS_COMPLETE 159 * @see #START_VIDEO_RECORDING 160 * @see #STOP_VIDEO_RECORDING 161 */ play(int soundName)162 public synchronized void play(int soundName) { 163 if (soundName < 0 || soundName >= SOUND_FILES.length) { 164 throw new RuntimeException("Unknown sound requested: " + soundName); 165 } 166 if (mSoundIds[soundName] == SOUND_NOT_LOADED) { 167 mSoundIdToPlay = 168 mSoundPool.load(SOUND_FILES[soundName], 1); 169 mSoundIds[soundName] = mSoundIdToPlay; 170 } else { 171 mSoundPool.play(mSoundIds[soundName], 1.0f, 1.0f, 0, 0, 1.0f); 172 } 173 } 174 175 private SoundPool.OnLoadCompleteListener mLoadCompleteListener = 176 new SoundPool.OnLoadCompleteListener() { 177 public void onLoadComplete(SoundPool soundPool, 178 int sampleId, int status) { 179 if (status == 0) { 180 if (mSoundIdToPlay == sampleId) { 181 soundPool.play(sampleId, 1.0f, 1.0f, 0, 0, 1.0f); 182 mSoundIdToPlay = SOUND_NOT_LOADED; 183 } 184 } else { 185 Log.e(TAG, "Unable to load sound for playback (status: " + 186 status + ")"); 187 } 188 } 189 }; 190 191 /** 192 * Free up all audio resources used by this MediaActionSound instance. Do 193 * not call any other methods on a MediaActionSound instance after calling 194 * release(). 195 */ release()196 public void release() { 197 if (mSoundPool != null) { 198 mSoundPool.release(); 199 mSoundPool = null; 200 } 201 } 202 } 203