• 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.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 {
release()38         public void release();
play(int action)39         public void play(int action);
40     }
41 
getPlayer(Context context)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
release()60         public void release() {
61             if (mSound != null) {
62                 mSound.release();
63                 mSound = null;
64             }
65         }
66 
MediaActionSoundPlayer()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
play(int action)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 
SoundPoolPlayer(Context context)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
release()140         public synchronized void release() {
141             if (mSoundPool != null) {
142                 mSoundPool.release();
143                 mSoundPool = null;
144             }
145         }
146 
147         @Override
play(int action)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
onLoadComplete(SoundPool pool, int soundID, int status)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