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