• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
release()41         public void release();
play(int action)42         public void play(int action);
43     }
44 
getPlayer(Context context)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 
getAudioTypeForSoundPool()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
release()69         public void release() {
70             if (mSound != null) {
71                 mSound.release();
72                 mSound = null;
73             }
74         }
75 
MediaActionSoundPlayer()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
play(int action)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 
SoundPoolPlayer(Context context)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
release()153         public synchronized void release() {
154             if (mSoundPool != null) {
155                 mSoundPool.release();
156                 mSoundPool = null;
157             }
158         }
159 
160         @Override
play(int action)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
onLoadComplete(SoundPool pool, int soundID, int status)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