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