18872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb/* 28872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Copyright (C) 2012 The Android Open Source Project 38872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * 48872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License"); 58872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * you may not use this file except in compliance with the License. 68872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * You may obtain a copy of the License at 78872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * 88872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * http://www.apache.org/licenses/LICENSE-2.0 98872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * 108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Unless required by applicable law or agreed to in writing, software 118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS, 128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * See the License for the specific language governing permissions and 148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * limitations under the License. 158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */ 168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbpackage com.android.camera; 188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.annotation.TargetApi; 208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.content.Context; 218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.media.AudioManager; 228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.media.MediaActionSound; 238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.media.SoundPool; 24638e6f06c877d90b907f66ea9c22b3c6b73c7384Sascha Haeberlingimport android.os.Build; 258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 262bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log; 27b50b5cbfbc0a67db6fc43373363b10381c9c61a3Angus Kongimport com.android.camera.util.ApiHelper; 282bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera2.R; 298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb/* 318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * This class controls the sound playback according to the API level. 328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */ 338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbpublic class SoundClips { 348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // Sound actions. 358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public static final int FOCUS_COMPLETE = 0; 368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public static final int START_VIDEO_RECORDING = 1; 378872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public static final int STOP_VIDEO_RECORDING = 2; 38f5aa51e6e8da349c8f1a604b431af243cc0802c0Angus Kong public static final int SHUTTER_CLICK = 3; 398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public interface Player { 418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public void release(); 428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public void play(int action); 438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public static Player getPlayer(Context context) { 468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (ApiHelper.HAS_MEDIA_ACTION_SOUND) { 478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb return new MediaActionSoundPlayer(); 488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } else { 498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb return new SoundPoolPlayer(context); 508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 5302cc9b9fbd42edfe6edd6db872f7eec84a274849Sascha Haeberling public static int getAudioTypeForSoundPool() { 54638e6f06c877d90b907f66ea9c22b3c6b73c7384Sascha Haeberling // STREAM_SYSTEM_ENFORCED is hidden API. 5502cc9b9fbd42edfe6edd6db872f7eec84a274849Sascha Haeberling return ApiHelper.getIntFieldIfExists(AudioManager.class, 5602cc9b9fbd42edfe6edd6db872f7eec84a274849Sascha Haeberling "STREAM_SYSTEM_ENFORCED", null, AudioManager.STREAM_RING); 5702cc9b9fbd42edfe6edd6db872f7eec84a274849Sascha Haeberling } 5802cc9b9fbd42edfe6edd6db872f7eec84a274849Sascha Haeberling 598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb /** 608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * This class implements SoundClips.Player using MediaActionSound, 618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * which exists since API level 16. 628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */ 63638e6f06c877d90b907f66ea9c22b3c6b73c7384Sascha Haeberling @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private static class MediaActionSoundPlayer implements Player { 652bca210e5fc8a77685775ffb403096167b017dceAngus Kong private static final Log.Tag TAG = new Log.Tag("MediaActSndPlayer"); 668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private MediaActionSound mSound; 678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb @Override 698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public void release() { 708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (mSound != null) { 718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound.release(); 728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound = null; 738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public MediaActionSoundPlayer() { 778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound = new MediaActionSound(); 788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound.load(MediaActionSound.START_VIDEO_RECORDING); 798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound.load(MediaActionSound.STOP_VIDEO_RECORDING); 808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound.load(MediaActionSound.FOCUS_COMPLETE); 81f5aa51e6e8da349c8f1a604b431af243cc0802c0Angus Kong mSound.load(MediaActionSound.SHUTTER_CLICK); 828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb @Override 858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public synchronized void play(int action) { 868872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb switch(action) { 878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb case FOCUS_COMPLETE: 888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound.play(MediaActionSound.FOCUS_COMPLETE); 898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb break; 908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb case START_VIDEO_RECORDING: 918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound.play(MediaActionSound.START_VIDEO_RECORDING); 928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb break; 938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb case STOP_VIDEO_RECORDING: 948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSound.play(MediaActionSound.STOP_VIDEO_RECORDING); 958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb break; 96f5aa51e6e8da349c8f1a604b431af243cc0802c0Angus Kong case SHUTTER_CLICK: 97f5aa51e6e8da349c8f1a604b431af243cc0802c0Angus Kong mSound.play(MediaActionSound.SHUTTER_CLICK); 98f5aa51e6e8da349c8f1a604b431af243cc0802c0Angus Kong break; 998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb default: 1008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb Log.w(TAG, "Unrecognized action:" + action); 1018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb /** 1068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * This class implements SoundClips.Player using SoundPool, which 1078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * exists since API level 1. 1088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */ 1098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private static class SoundPoolPlayer implements 1108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb Player, SoundPool.OnLoadCompleteListener { 1118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1122bca210e5fc8a77685775ffb403096167b017dceAngus Kong private static final Log.Tag TAG = new Log.Tag("SoundPoolPlayer"); 1138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private static final int NUM_SOUND_STREAMS = 1; 1148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private static final int[] SOUND_RES = { // Soundtrack res IDs. 115f5aa51e6e8da349c8f1a604b431af243cc0802c0Angus Kong R.raw.focus_complete, 116f5aa51e6e8da349c8f1a604b431af243cc0802c0Angus Kong R.raw.video_record, 117eeeed4fa00ed842050bc7e0edd3b37c262802a5bSpike Sprague R.raw.video_record, 118eeeed4fa00ed842050bc7e0edd3b37c262802a5bSpike Sprague R.raw.shutter 1198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb }; 1208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // ID returned by load() should be non-zero. 1228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private static final int ID_NOT_LOADED = 0; 1238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // Maps a sound action to the id; 125eeeed4fa00ed842050bc7e0edd3b37c262802a5bSpike Sprague private final int[] mSoundRes = {0, 1, 2, 3}; 1268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // Store the context for lazy loading. 1278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private Context mContext; 1288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // mSoundPool is created every time load() is called and cleared every 1298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // time release() is called. 1308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private SoundPool mSoundPool; 1318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // Sound ID of each sound resources. Given when the sound is loaded. 1328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private final int[] mSoundIDs; 1338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private final boolean[] mSoundIDReady; 1348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb private int mSoundIDToPlay; 1358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public SoundPoolPlayer(Context context) { 1378872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mContext = context; 1388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDToPlay = ID_NOT_LOADED; 1408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 14102cc9b9fbd42edfe6edd6db872f7eec84a274849Sascha Haeberling mSoundPool = new SoundPool(NUM_SOUND_STREAMS, getAudioTypeForSoundPool(), 0); 1428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundPool.setOnLoadCompleteListener(this); 1438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDs = new int[SOUND_RES.length]; 1458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDReady = new boolean[SOUND_RES.length]; 1468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb for (int i = 0; i < SOUND_RES.length; i++) { 1478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDs[i] = mSoundPool.load(mContext, SOUND_RES[i], 1); 1488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDReady[i] = false; 1498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb @Override 1538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public synchronized void release() { 1548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (mSoundPool != null) { 1558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundPool.release(); 1568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundPool = null; 1578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb @Override 1618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public synchronized void play(int action) { 1628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (action < 0 || action >= mSoundRes.length) { 1638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb Log.e(TAG, "Resource ID not found for action:" + action + " in play()."); 1648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb return; 1658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb int index = mSoundRes[action]; 1688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (mSoundIDs[index] == ID_NOT_LOADED) { 1698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // Not loaded yet, load first and then play when the loading is complete. 1708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDs[index] = mSoundPool.load(mContext, SOUND_RES[index], 1); 1718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDToPlay = mSoundIDs[index]; 1728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } else if (!mSoundIDReady[index]) { 1738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb // Loading and not ready yet. 1748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDToPlay = mSoundIDs[index]; 1758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } else { 1768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundPool.play(mSoundIDs[index], 1f, 1f, 0, 0, 1f); 1778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb @Override 1818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb public void onLoadComplete(SoundPool pool, int soundID, int status) { 1828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (status != 0) { 1838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb Log.e(TAG, "loading sound tracks failed (status=" + status + ")"); 1848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb for (int i = 0; i < mSoundIDs.length; i++ ) { 1858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (mSoundIDs[i] == soundID) { 1868872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDs[i] = ID_NOT_LOADED; 1878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb break; 1888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb return; 1918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 1938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb for (int i = 0; i < mSoundIDs.length; i++ ) { 1948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (mSoundIDs[i] == soundID) { 1958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDReady[i] = true; 1968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb break; 1978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 1998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb 2008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb if (soundID == mSoundIDToPlay) { 2018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundIDToPlay = ID_NOT_LOADED; 2028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb mSoundPool.play(soundID, 1f, 1f, 0, 0, 1f); 2038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 2048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 2058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb } 2068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb} 207