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 
     26 import com.android.camera.debug.Log;
     27 import com.android.camera.util.ApiHelper;
     28 import com.android.camera2.R;
     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 Log.Tag TAG = new Log.Tag("MediaActSndPlayer");
     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 Log.Tag TAG = new Log.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                 R.raw.video_record,
    118                 R.raw.shutter
    119         };
    120 
    121         // ID returned by load() should be non-zero.
    122         private static final int ID_NOT_LOADED = 0;
    123 
    124         // Maps a sound action to the id;
    125         private final int[] mSoundRes = {0, 1, 2, 3};
    126         // Store the context for lazy loading.
    127         private Context mContext;
    128         // mSoundPool is created every time load() is called and cleared every
    129         // time release() is called.
    130         private SoundPool mSoundPool;
    131         // Sound ID of each sound resources. Given when the sound is loaded.
    132         private final int[] mSoundIDs;
    133         private final boolean[] mSoundIDReady;
    134         private int mSoundIDToPlay;
    135 
    136         public SoundPoolPlayer(Context context) {
    137             mContext = context;
    138 
    139             mSoundIDToPlay = ID_NOT_LOADED;
    140 
    141             mSoundPool = new SoundPool(NUM_SOUND_STREAMS, getAudioTypeForSoundPool(), 0);
    142             mSoundPool.setOnLoadCompleteListener(this);
    143 
    144             mSoundIDs = new int[SOUND_RES.length];
    145             mSoundIDReady = new boolean[SOUND_RES.length];
    146             for (int i = 0; i < SOUND_RES.length; i++) {
    147                 mSoundIDs[i] = mSoundPool.load(mContext, SOUND_RES[i], 1);
    148                 mSoundIDReady[i] = false;
    149             }
    150         }
    151 
    152         @Override
    153         public synchronized void release() {
    154             if (mSoundPool != null) {
    155                 mSoundPool.release();
    156                 mSoundPool = null;
    157             }
    158         }
    159 
    160         @Override
    161         public synchronized void play(int action) {
    162             if (action < 0 || action >= mSoundRes.length) {
    163                 Log.e(TAG, "Resource ID not found for action:" + action + " in play().");
    164                 return;
    165             }
    166 
    167             int index = mSoundRes[action];
    168             if (mSoundIDs[index] == ID_NOT_LOADED) {
    169                 // Not loaded yet, load first and then play when the loading is complete.
    170                 mSoundIDs[index] = mSoundPool.load(mContext, SOUND_RES[index], 1);
    171                 mSoundIDToPlay = mSoundIDs[index];
    172             } else if (!mSoundIDReady[index]) {
    173                 // Loading and not ready yet.
    174                 mSoundIDToPlay = mSoundIDs[index];
    175             } else {
    176                 mSoundPool.play(mSoundIDs[index], 1f, 1f, 0, 0, 1f);
    177             }
    178         }
    179 
    180         @Override
    181         public void onLoadComplete(SoundPool pool, int soundID, int status) {
    182             if (status != 0) {
    183                 Log.e(TAG, "loading sound tracks failed (status=" + status + ")");
    184                 for (int i = 0; i < mSoundIDs.length; i++ ) {
    185                     if (mSoundIDs[i] == soundID) {
    186                         mSoundIDs[i] = ID_NOT_LOADED;
    187                         break;
    188                     }
    189                 }
    190                 return;
    191             }
    192 
    193             for (int i = 0; i < mSoundIDs.length; i++ ) {
    194                 if (mSoundIDs[i] == soundID) {
    195                     mSoundIDReady[i] = true;
    196                     break;
    197                 }
    198             }
    199 
    200             if (soundID == mSoundIDToPlay) {
    201                 mSoundIDToPlay = ID_NOT_LOADED;
    202                 mSoundPool.play(soundID, 1f, 1f, 0, 0, 1f);
    203             }
    204         }
    205     }
    206 }
    207