1/*
2 * Copyright (C) 2010 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
17package com.replica.replicaisland;
18
19import java.util.Comparator;
20
21import android.content.Context;
22import android.media.AudioManager;
23import android.media.SoundPool;
24
25public class SoundSystem extends BaseObject {
26    private static final int MAX_STREAMS = 8;
27    private static final int MAX_SOUNDS = 32;
28    private static final SoundComparator sSoundComparator = new SoundComparator();
29
30    public static final int PRIORITY_LOW = 0;
31    public static final int PRIORITY_NORMAL = 1;
32    public static final int PRIORITY_HIGH = 2;
33    public static final int PRIORITY_MUSIC = 3;
34
35    private SoundPool mSoundPool;
36    private FixedSizeArray<Sound> mSounds;
37    private Sound mSearchDummy;
38    private boolean mSoundEnabled;
39    private int[] mLoopingStreams;
40
41    public SoundSystem() {
42        super();
43        mSoundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
44        mSounds = new FixedSizeArray<Sound>(MAX_SOUNDS, sSoundComparator);
45        mSearchDummy = new Sound();
46        mLoopingStreams = new int[MAX_STREAMS];
47        for (int x = 0; x < mLoopingStreams.length; x++) {
48        	mLoopingStreams[x] = -1;
49        }
50    }
51
52    @Override
53    public void reset() {
54        mSoundPool.release();
55        mSounds.clear();
56        mSoundEnabled = true;
57        for (int x = 0; x < mLoopingStreams.length; x++) {
58        	mLoopingStreams[x] = -1;
59        }
60    }
61
62    public Sound load(int resource) {
63        final int index = findSound(resource);
64        Sound result = null;
65        if (index < 0) {
66            // new sound.
67           if (sSystemRegistry.contextParameters != null) {
68               Context context = sSystemRegistry.contextParameters.context;
69               result = new Sound();
70               result.resource = resource;
71               result.soundId = mSoundPool.load(context, resource, 1);
72               mSounds.add(result);
73               mSounds.sort(false);
74           }
75        } else {
76            result = mSounds.get(index);
77        }
78
79        return result;
80    }
81
82    synchronized public final int play(Sound sound, boolean loop, int priority) {
83    	int stream = -1;
84    	if (mSoundEnabled) {
85    		stream = mSoundPool.play(sound.soundId, 1.0f, 1.0f, priority, loop ? -1 : 0, 1.0f);
86    		if (loop) {
87    			addLoopingStream(stream);
88    		}
89    	}
90
91    	return stream;
92    }
93
94    synchronized public final int play(Sound sound, boolean loop, int priority, float volume, float rate) {
95    	int stream = -1;
96    	if (mSoundEnabled) {
97    		stream = mSoundPool.play(sound.soundId, volume, volume, priority, loop ? -1 : 0, rate);
98    		if (loop) {
99    			addLoopingStream(stream);
100    		}
101    	}
102
103    	return stream;
104    }
105
106    public final void stop(int stream) {
107        mSoundPool.stop(stream);
108        removeLoopingStream(stream);
109    }
110
111    public final void pause(int stream) {
112        mSoundPool.pause(stream);
113    }
114
115    public final void resume(int stream) {
116       mSoundPool.resume(stream);
117    }
118
119    public final void stopAll() {
120    	final int count = mLoopingStreams.length;
121    	for (int x = count - 1; x >= 0; x--) {
122    		if (mLoopingStreams[x] >= 0) {
123    			stop(mLoopingStreams[x]);
124    		}
125    	}
126    }
127
128    // HACK: There's no way to pause an entire sound pool, but if we
129    // don't do something when our parent activity is paused, looping
130    // sounds will continue to play.  Rather that reproduce all the bookkeeping
131    // that SoundPool does internally here, I've opted to just pause looping
132    // sounds when the Activity is paused.
133    public void pauseAll() {
134    	final int count = mLoopingStreams.length;
135    	for (int x = 0; x < count; x++) {
136    		if (mLoopingStreams[x] >= 0) {
137    			pause(mLoopingStreams[x]);
138    		}
139    	}
140    }
141
142    private void addLoopingStream(int stream) {
143    	final int count = mLoopingStreams.length;
144    	for (int x = 0; x < count; x++) {
145    		if (mLoopingStreams[x] < 0) {
146    			mLoopingStreams[x] = stream;
147    			break;
148    		}
149    	}
150    }
151
152    private void removeLoopingStream(int stream) {
153    	final int count = mLoopingStreams.length;
154    	for (int x = 0; x < count; x++) {
155    		if (mLoopingStreams[x] == stream) {
156    			mLoopingStreams[x] = -1;
157    			break;
158    		}
159    	}
160    }
161
162    private final int findSound(int resource) {
163        mSearchDummy.resource = resource;
164        return mSounds.find(mSearchDummy, false);
165    }
166
167	synchronized public final void setSoundEnabled(boolean soundEnabled) {
168		mSoundEnabled = soundEnabled;
169	}
170
171	public final boolean getSoundEnabled() {
172		return mSoundEnabled;
173	}
174
175    public class Sound extends AllocationGuard {
176        public int resource;
177        public int soundId;
178    }
179
180    /** Comparator for sounds. */
181    private final static class SoundComparator implements Comparator<Sound> {
182        public int compare(final Sound object1, final Sound object2) {
183            int result = 0;
184            if (object1 == null && object2 != null) {
185                result = 1;
186            } else if (object1 != null && object2 == null) {
187                result = -1;
188            } else if (object1 != null && object2 != null) {
189                result = object1.resource - object2.resource;
190            }
191            return result;
192        }
193    }
194
195
196}
197