Home | History | Annotate | Download | only in camera
      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 
     17 package com.android.camera;
     18 
     19 import android.annotation.TargetApi;
     20 import android.content.Context;
     21 import android.media.AudioManager;
     22 import android.media.MediaActionSound;
     23 import android.media.SoundPool;
     24 import android.os.Build;
     25 import android.util.Log;
     26 
     27 import com.android.camera2.R;
     28 import com.android.camera.util.ApiHelper;
     29 
     30 /*
     31  * This class controls the sound playback according to the API level.
     32  */
     33 public class SoundClips {
     34     // Sound actions.
     35     public static final int FOCUS_COMPLETE = 0;
     36     public static final int START_VIDEO_RECORDING = 1;
     37     public static final int STOP_VIDEO_RECORDING = 2;
     38     public static final int SHUTTER_CLICK = 3;
     39 
     40     public interface Player {
     41         public void release();
     42         public void play(int action);
     43     }
     44 
     45     public static Player getPlayer(Context context) {
     46         if (ApiHelper.HAS_MEDIA_ACTION_SOUND) {
     47             return new MediaActionSoundPlayer();
     48         } else {
     49             return new SoundPoolPlayer(context);
     50         }
     51     }
     52 
     53     public static int getAudioTypeForSoundPool() {
     54         // STREAM_SYSTEM_ENFORCED is hidden API.
     55         return ApiHelper.getIntFieldIfExists(AudioManager.class,
     56                 "STREAM_SYSTEM_ENFORCED", null, AudioManager.STREAM_RING);
     57     }
     58 
     59     /**
     60      * This class implements SoundClips.Player using MediaActionSound,
     61      * which exists since API level 16.
     62      */
     63     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
     64     private static class MediaActionSoundPlayer implements Player {
     65         private static final String TAG = "MediaActionSoundPlayer";
     66         private MediaActionSound mSound;
     67 
     68         @Override
     69         public void release() {
     70             if (mSound != null) {
     71                 mSound.release();
     72                 mSound = null;
     73             }
     74         }
     75 
     76         public MediaActionSoundPlayer() {
     77             mSound = new MediaActionSound();
     78             mSound.load(MediaActionSound.START_VIDEO_RECORDING);
     79             mSound.load(MediaActionSound.STOP_VIDEO_RECORDING);
     80             mSound.load(MediaActionSound.FOCUS_COMPLETE);
     81             mSound.load(MediaActionSound.SHUTTER_CLICK);
     82         }
     83 
     84         @Override
     85         public synchronized void play(int action) {
     86             switch(action) {
     87                 case FOCUS_COMPLETE:
     88                     mSound.play(MediaActionSound.FOCUS_COMPLETE);
     89                     break;
     90                 case START_VIDEO_RECORDING:
     91                     mSound.play(MediaActionSound.START_VIDEO_RECORDING);
     92                     break;
     93                 case STOP_VIDEO_RECORDING:
     94                     mSound.play(MediaActionSound.STOP_VIDEO_RECORDING);
     95                     break;
     96                 case SHUTTER_CLICK:
     97                     mSound.play(MediaActionSound.SHUTTER_CLICK);
     98                     break;
     99                 default:
    100                     Log.w(TAG, "Unrecognized action:" + action);
    101             }
    102         }
    103     }
    104 
    105     /**
    106      * This class implements SoundClips.Player using SoundPool, which
    107      * exists since API level 1.
    108      */
    109     private static class SoundPoolPlayer implements
    110             Player, SoundPool.OnLoadCompleteListener {
    111 
    112         private static final String TAG = "SoundPoolPlayer";
    113         private static final int NUM_SOUND_STREAMS = 1;
    114         private static final int[] SOUND_RES = { // Soundtrack res IDs.
    115                 R.raw.focus_complete,
    116                 R.raw.video_record,
    117         };
    118 
    119         // ID returned by load() should be non-zero.
    120         private static final int ID_NOT_LOADED = 0;
    121 
    122         // Maps a sound action to the id;
    123         private final int[] mSoundRes = {0, 1, 1, 1};
    124         // Store the context for lazy loading.
    125         private Context mContext;
    126         // mSoundPool is created every time load() is called and cleared every
    127         // time release() is called.
    128         private SoundPool mSoundPool;
    129         // Sound ID of each sound resources. Given when the sound is loaded.
    130         private final int[] mSoundIDs;
    131         private final boolean[] mSoundIDReady;
    132         private int mSoundIDToPlay;
    133 
    134         public SoundPoolPlayer(Context context) {
    135             mContext = context;
    136 
    137             mSoundIDToPlay = ID_NOT_LOADED;
    138 
    139             mSoundPool = new SoundPool(NUM_SOUND_STREAMS, getAudioTypeForSoundPool(), 0);
    140             mSoundPool.setOnLoadCompleteListener(this);
    141 
    142             mSoundIDs = new int[SOUND_RES.length];
    143             mSoundIDReady = new boolean[SOUND_RES.length];
    144             for (int i = 0; i < SOUND_RES.length; i++) {
    145                 mSoundIDs[i] = mSoundPool.load(mContext, SOUND_RES[i], 1);
    146                 mSoundIDReady[i] = false;
    147             }
    148         }
    149 
    150         @Override
    151         public synchronized void release() {
    152             if (mSoundPool != null) {
    153                 mSoundPool.release();
    154                 mSoundPool = null;
    155             }
    156         }
    157 
    158         @Override
    159         public synchronized void play(int action) {
    160             if (action < 0 || action >= mSoundRes.length) {
    161                 Log.e(TAG, "Resource ID not found for action:" + action + " in play().");
    162                 return;
    163             }
    164 
    165             int index = mSoundRes[action];
    166             if (mSoundIDs[index] == ID_NOT_LOADED) {
    167                 // Not loaded yet, load first and then play when the loading is complete.
    168                 mSoundIDs[index] = mSoundPool.load(mContext, SOUND_RES[index], 1);
    169                 mSoundIDToPlay = mSoundIDs[index];
    170             } else if (!mSoundIDReady[index]) {
    171                 // Loading and not ready yet.
    172                 mSoundIDToPlay = mSoundIDs[index];
    173             } else {
    174                 mSoundPool.play(mSoundIDs[index], 1f, 1f, 0, 0, 1f);
    175             }
    176         }
    177 
    178         @Override
    179         public void onLoadComplete(SoundPool pool, int soundID, int status) {
    180             if (status != 0) {
    181                 Log.e(TAG, "loading sound tracks failed (status=" + status + ")");
    182                 for (int i = 0; i < mSoundIDs.length; i++ ) {
    183                     if (mSoundIDs[i] == soundID) {
    184                         mSoundIDs[i] = ID_NOT_LOADED;
    185                         break;
    186                     }
    187                 }
    188                 return;
    189             }
    190 
    191             for (int i = 0; i < mSoundIDs.length; i++ ) {
    192                 if (mSoundIDs[i] == soundID) {
    193                     mSoundIDReady[i] = true;
    194                     break;
    195                 }
    196             }
    197 
    198             if (soundID == mSoundIDToPlay) {
    199                 mSoundIDToPlay = ID_NOT_LOADED;
    200                 mSoundPool.play(soundID, 1f, 1f, 0, 0, 1f);
    201             }
    202         }
    203     }
    204 }
    205