Home | History | Annotate | Download | only in hardware
      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 }