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