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 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 }