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 MediaPlayer mPlayer; 114 private Thread mThread; 115 private boolean mExit; 116 private int mPlayCount; 117 118 private static final String mShutterSound = 119 "/system/media/audio/ui/camera_click.ogg"; 120 private static final String mFocusSound = 121 "/system/media/audio/ui/camera_focus.ogg"; 122 private static final String mVideoStartSound = 123 "/system/media/audio/ui/VideoRecord.ogg"; 124 private static final String mVideoStopSound = 125 "/system/media/audio/ui/VideoRecord.ogg"; 126 127 @Override 128 public void run() { 129 String soundFilePath; 130 switch (mSoundId) { 131 case SHUTTER_CLICK: 132 soundFilePath = mShutterSound; 133 break; 134 case FOCUS_COMPLETE: 135 soundFilePath = mFocusSound; 136 break; 137 case START_VIDEO_RECORDING: 138 soundFilePath = mVideoStartSound; 139 break; 140 case STOP_VIDEO_RECORDING: 141 soundFilePath = mVideoStopSound; 142 break; 143 default: 144 Log.e(TAG, "Unknown sound " + mSoundId + " requested."); 145 return; 146 } 147 mPlayer = new MediaPlayer(); 148 try { 149 mPlayer.setAudioStreamType(AudioManager.STREAM_SYSTEM_ENFORCED); 150 mPlayer.setDataSource(soundFilePath); 151 mPlayer.setLooping(false); 152 mPlayer.prepare(); 153 } catch(IOException e) { 154 Log.e(TAG, "Error setting up sound " + mSoundId, e); 155 return; 156 } 157 158 while(true) { 159 try { 160 synchronized (this) { 161 while(true) { 162 if (mExit) { 163 return; 164 } else if (mPlayCount <= 0) { 165 wait(); 166 } else { 167 mPlayCount--; 168 break; 169 } 170 } 171 } 172 mPlayer.start(); 173 } catch (Exception e) { 174 Log.e(TAG, "Error playing sound " + mSoundId, e); 175 } 176 } 177 } 178 179 public CameraSoundPlayer(int soundId) { 180 mSoundId = soundId; 181 } 182 183 public void play() { 184 if (mThread == null) { 185 mThread = new Thread(this); 186 mThread.start(); 187 } 188 synchronized (this) { 189 mPlayCount++; 190 notifyAll(); 191 } 192 } 193 194 public void release() { 195 if (mThread != null) { 196 synchronized (this) { 197 mExit = true; 198 notifyAll(); 199 } 200 try { 201 mThread.join(); 202 } catch (InterruptedException e) { 203 } 204 mThread = null; 205 } 206 if (mPlayer != null) { 207 mPlayer.release(); 208 mPlayer = null; 209 } 210 } 211 212 @Override 213 protected void finalize() { 214 release(); 215 } 216 } 217 }