1 /* 2 * Copyright (C) 2011 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.hardware; 18 19 import android.media.AudioManager; 20 import android.media.MediaPlayer; 21 import android.os.SystemProperties; 22 import android.util.Log; 23 24 import java.io.IOException; 25 26 /** 27 * <p>Use this class to play an appropriate sound when implementing a custom 28 * still or video recording mechanism through the preview callbacks.</p> 29 * 30 * <p>There is no need to play sounds when using {@link #android.hardware.Camera#takePicture} 31 * or {@link android.media.MediaRecorder} for still images or video, 32 * respectively, as these play their own sounds when needed.</p> 33 * 34 * @hide 35 */ 36 public class CameraSound { 37 private static final String TAG = "CameraSound"; 38 /** 39 * The sound used by {@link android.hardware.Camera#takePicture} to 40 * indicate still image capture. 41 */ 42 public static final int SHUTTER_CLICK = 0; 43 44 /** 45 * A sound to indicate that focusing has completed. Because deciding 46 * when this occurs is application-dependent, this sound is not used by 47 * any methods in the Camera class. 48 */ 49 public static final int FOCUS_COMPLETE = 1; 50 51 /** 52 * The sound used by {@link android.media.MediaRecorder#start} to 53 * indicate the start of video recording. 54 */ 55 public static final int START_VIDEO_RECORDING = 2; 56 57 /** 58 * The sound used by {@link android.media.MediaRecorder#stop} to 59 * indicate the end of video recording. 60 */ 61 public static final int STOP_VIDEO_RECORDING = 3; 62 63 private static final int NUM_SOUNDS = 4; 64 private CameraSoundPlayer[] mCameraSoundPlayers; 65 66 public CameraSound() { 67 } 68 69 /** 70 * <p>Play one of the predefined platform sounds for camera actions.</p> 71 * 72 * <p>Use this method to play a platform-specific sound for various camera 73 * actions. The sound playing is done asynchronously, with the same behavior 74 * and content as the sounds played by {@link #takePicture takePicture}, 75 * {@link android.media.MediaRecorder#start MediaRecorder.start}, and 76 * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p> 77 * 78 * <p>Using this method makes it easy to match the default device sounds 79 * when recording or capturing data through the preview callbacks.</p> 80 * 81 * @param soundId The type of sound to play, selected from SHUTTER_CLICK, 82 * FOCUS_COMPLETE, START_VIDEO_RECORDING, or STOP_VIDEO_RECORDING. 83 * @see android.hardware#takePicture 84 * @see android.media.MediaRecorder 85 * @see #SHUTTER_CLICK 86 * @see #FOCUS_COMPLETE 87 * @see #START_VIDEO_RECORDING 88 * @see #STOP_VIDEO_RECORDING 89 */ 90 public void playSound(int soundId) { 91 if (mCameraSoundPlayers == null) { 92 mCameraSoundPlayers = new CameraSoundPlayer[NUM_SOUNDS]; 93 } 94 if (mCameraSoundPlayers[soundId] == null) { 95 mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId); 96 } 97 mCameraSoundPlayers[soundId].play(); 98 } 99 100 public void release() { 101 if (mCameraSoundPlayers != null) { 102 for (CameraSoundPlayer csp: mCameraSoundPlayers) { 103 if (csp != null) { 104 csp.release(); 105 } 106 } 107 mCameraSoundPlayers = null; 108 } 109 } 110 111 private static class CameraSoundPlayer implements Runnable { 112 private int mSoundId; 113 private int mAudioStreamType; 114 private MediaPlayer mPlayer; 115 private Thread mThread; 116 private boolean mExit; 117 private int mPlayCount; 118 119 private static final String mShutterSound = 120 "/system/media/audio/ui/camera_click.ogg"; 121 private static final String mFocusSound = 122 "/system/media/audio/ui/camera_focus.ogg"; 123 private static final String mVideoStartSound = 124 "/system/media/audio/ui/VideoRecord.ogg"; 125 private static final String mVideoStopSound = 126 "/system/media/audio/ui/VideoRecord.ogg"; 127 128 @Override 129 public void run() { 130 String soundFilePath; 131 switch (mSoundId) { 132 case SHUTTER_CLICK: 133 soundFilePath = mShutterSound; 134 break; 135 case FOCUS_COMPLETE: 136 soundFilePath = mFocusSound; 137 break; 138 case START_VIDEO_RECORDING: 139 soundFilePath = mVideoStartSound; 140 break; 141 case STOP_VIDEO_RECORDING: 142 soundFilePath = mVideoStopSound; 143 break; 144 default: 145 Log.e(TAG, "Unknown sound " + mSoundId + " requested."); 146 return; 147 } 148 mPlayer = new MediaPlayer(); 149 try { 150 mPlayer.setAudioStreamType(mAudioStreamType); 151 mPlayer.setDataSource(soundFilePath); 152 mPlayer.setLooping(false); 153 mPlayer.prepare(); 154 } catch(IOException e) { 155 Log.e(TAG, "Error setting up sound " + mSoundId, e); 156 return; 157 } 158 159 while(true) { 160 try { 161 synchronized (this) { 162 while(true) { 163 if (mExit) { 164 return; 165 } else if (mPlayCount <= 0) { 166 wait(); 167 } else { 168 mPlayCount--; 169 break; 170 } 171 } 172 } 173 mPlayer.start(); 174 } catch (Exception e) { 175 Log.e(TAG, "Error playing sound " + mSoundId, e); 176 } 177 } 178 } 179 180 public CameraSoundPlayer(int soundId) { 181 mSoundId = soundId; 182 if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) { 183 mAudioStreamType = AudioManager.STREAM_MUSIC; 184 } else { 185 mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED; 186 } 187 } 188 189 public void play() { 190 if (mThread == null) { 191 mThread = new Thread(this); 192 mThread.start(); 193 } 194 synchronized (this) { 195 mPlayCount++; 196 notifyAll(); 197 } 198 } 199 200 public void release() { 201 if (mThread != null) { 202 synchronized (this) { 203 mExit = true; 204 notifyAll(); 205 } 206 try { 207 mThread.join(); 208 } catch (InterruptedException e) { 209 } 210 mThread = null; 211 } 212 if (mPlayer != null) { 213 mPlayer.release(); 214 mPlayer = null; 215 } 216 } 217 218 @Override 219 protected void finalize() { 220 release(); 221 } 222 } 223 }