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