Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epoxy
GitHub Repository: epoxy/proj11
Path: blob/master/SLICK_HOME/src/org/newdawn/slick/openal/OpenALStreamPlayer.java
1461 views
1
package org.newdawn.slick.openal;
2
3
import java.io.IOException;
4
import java.net.URL;
5
import java.nio.ByteBuffer;
6
import java.nio.IntBuffer;
7
8
import org.lwjgl.BufferUtils;
9
import org.lwjgl.openal.AL10;
10
import org.lwjgl.openal.AL11;
11
import org.lwjgl.openal.OpenALException;
12
import org.newdawn.slick.util.Log;
13
import org.newdawn.slick.util.ResourceLoader;
14
15
/**
16
* A generic tool to work on a supplied stream, pulling out PCM data and buffered it to OpenAL
17
* as required.
18
*
19
* @author Kevin Glass
20
* @author Nathan Sweet <[email protected]>
21
* @author Rockstar play and setPosition cleanup
22
*/
23
public class OpenALStreamPlayer {
24
/** The number of buffers to maintain */
25
public static final int BUFFER_COUNT = 3;
26
/** The size of the sections to stream from the stream */
27
private static final int sectionSize = 4096 * 20;
28
29
/** The buffer read from the data stream */
30
private byte[] buffer = new byte[sectionSize];
31
/** Holds the OpenAL buffer names */
32
private IntBuffer bufferNames;
33
/** The byte buffer passed to OpenAL containing the section */
34
private ByteBuffer bufferData = BufferUtils.createByteBuffer(sectionSize);
35
/** The buffer holding the names of the OpenAL buffer thats been fully played back */
36
private IntBuffer unqueued = BufferUtils.createIntBuffer(1);
37
/** The source we're playing back on */
38
private int source;
39
/** The number of buffers remaining */
40
private int remainingBufferCount;
41
/** True if we should loop the track */
42
private boolean loop;
43
/** True if we've completed play back */
44
private boolean done = true;
45
/** The stream we're currently reading from */
46
private AudioInputStream audio;
47
/** The source of the data */
48
private String ref;
49
/** The source of the data */
50
private URL url;
51
/** The pitch of the music */
52
private float pitch;
53
/** Position in seconds of the previously played buffers */
54
private float positionOffset;
55
56
/**
57
* Create a new player to work on an audio stream
58
*
59
* @param source The source on which we'll play the audio
60
* @param ref A reference to the audio file to stream
61
*/
62
public OpenALStreamPlayer(int source, String ref) {
63
this.source = source;
64
this.ref = ref;
65
66
bufferNames = BufferUtils.createIntBuffer(BUFFER_COUNT);
67
AL10.alGenBuffers(bufferNames);
68
}
69
70
/**
71
* Create a new player to work on an audio stream
72
*
73
* @param source The source on which we'll play the audio
74
* @param url A reference to the audio file to stream
75
*/
76
public OpenALStreamPlayer(int source, URL url) {
77
this.source = source;
78
this.url = url;
79
80
bufferNames = BufferUtils.createIntBuffer(BUFFER_COUNT);
81
AL10.alGenBuffers(bufferNames);
82
}
83
84
/**
85
* Initialise our connection to the underlying resource
86
*
87
* @throws IOException Indicates a failure to open the underling resource
88
*/
89
private void initStreams() throws IOException {
90
if (audio != null) {
91
audio.close();
92
}
93
94
OggInputStream audio;
95
96
if (url != null) {
97
audio = new OggInputStream(url.openStream());
98
} else {
99
audio = new OggInputStream(ResourceLoader.getResourceAsStream(ref));
100
}
101
102
this.audio = audio;
103
positionOffset = 0;
104
}
105
106
/**
107
* Get the source of this stream
108
*
109
* @return The name of the source of string
110
*/
111
public String getSource() {
112
return (url == null) ? ref : url.toString();
113
}
114
115
/**
116
* Clean up the buffers applied to the sound source
117
*/
118
private void removeBuffers() {
119
IntBuffer buffer = BufferUtils.createIntBuffer(1);
120
int queued = AL10.alGetSourcei(source, AL10.AL_BUFFERS_QUEUED);
121
122
while (queued > 0)
123
{
124
AL10.alSourceUnqueueBuffers(source, buffer);
125
queued--;
126
}
127
}
128
129
/**
130
* Start this stream playing
131
*
132
* @param loop True if the stream should loop
133
* @throws IOException Indicates a failure to read from the stream
134
*/
135
public void play(boolean loop) throws IOException {
136
this.loop = loop;
137
initStreams();
138
139
done = false;
140
141
AL10.alSourceStop(source);
142
removeBuffers();
143
144
startPlayback();
145
}
146
147
/**
148
* Setup the playback properties
149
*
150
* @param pitch The pitch to play back at
151
*/
152
public void setup(float pitch) {
153
this.pitch = pitch;
154
}
155
156
/**
157
* Check if the playback is complete. Note this will never
158
* return true if we're looping
159
*
160
* @return True if we're looping
161
*/
162
public boolean done() {
163
return done;
164
}
165
166
/**
167
* Poll the bufferNames - check if we need to fill the bufferNames with another
168
* section.
169
*
170
* Most of the time this should be reasonably quick
171
*/
172
public void update() {
173
if (done) {
174
return;
175
}
176
177
float sampleRate = audio.getRate();
178
float sampleSize;
179
if (audio.getChannels() > 1) {
180
sampleSize = 4; // AL10.AL_FORMAT_STEREO16
181
} else {
182
sampleSize = 2; // AL10.AL_FORMAT_MONO16
183
}
184
185
int processed = AL10.alGetSourcei(source, AL10.AL_BUFFERS_PROCESSED);
186
while (processed > 0) {
187
unqueued.clear();
188
AL10.alSourceUnqueueBuffers(source, unqueued);
189
190
int bufferIndex = unqueued.get(0);
191
192
float bufferLength = (AL10.alGetBufferi(bufferIndex, AL10.AL_SIZE) / sampleSize) / sampleRate;
193
positionOffset += bufferLength;
194
195
if (stream(bufferIndex)) {
196
AL10.alSourceQueueBuffers(source, unqueued);
197
} else {
198
remainingBufferCount--;
199
if (remainingBufferCount == 0) {
200
done = true;
201
}
202
}
203
processed--;
204
}
205
206
int state = AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE);
207
208
if (state != AL10.AL_PLAYING) {
209
AL10.alSourcePlay(source);
210
}
211
}
212
213
/**
214
* Stream some data from the audio stream to the buffer indicates by the ID
215
*
216
* @param bufferId The ID of the buffer to fill
217
* @return True if another section was available
218
*/
219
public boolean stream(int bufferId) {
220
try {
221
int count = audio.read(buffer);
222
223
if (count != -1) {
224
bufferData.clear();
225
bufferData.put(buffer,0,count);
226
bufferData.flip();
227
228
int format = audio.getChannels() > 1 ? AL10.AL_FORMAT_STEREO16 : AL10.AL_FORMAT_MONO16;
229
try {
230
AL10.alBufferData(bufferId, format, bufferData, audio.getRate());
231
} catch (OpenALException e) {
232
Log.error("Failed to loop buffer: "+bufferId+" "+format+" "+count+" "+audio.getRate(), e);
233
return false;
234
}
235
} else {
236
if (loop) {
237
initStreams();
238
stream(bufferId);
239
} else {
240
done = true;
241
return false;
242
}
243
}
244
245
return true;
246
} catch (IOException e) {
247
Log.error(e);
248
return false;
249
}
250
}
251
252
/**
253
* Seeks to a position in the music.
254
*
255
* @param position Position in seconds.
256
* @return True if the setting of the position was successful
257
*/
258
public boolean setPosition(float position) {
259
try {
260
if (getPosition() > position) {
261
initStreams();
262
}
263
264
float sampleRate = audio.getRate();
265
float sampleSize;
266
if (audio.getChannels() > 1) {
267
sampleSize = 4; // AL10.AL_FORMAT_STEREO16
268
} else {
269
sampleSize = 2; // AL10.AL_FORMAT_MONO16
270
}
271
272
while (positionOffset < position) {
273
int count = audio.read(buffer);
274
if (count != -1) {
275
float bufferLength = (count / sampleSize) / sampleRate;
276
positionOffset += bufferLength;
277
} else {
278
if (loop) {
279
initStreams();
280
} else {
281
done = true;
282
}
283
return false;
284
}
285
}
286
287
startPlayback();
288
289
return true;
290
} catch (IOException e) {
291
Log.error(e);
292
return false;
293
}
294
}
295
296
/**
297
* Starts the streaming.
298
*/
299
private void startPlayback() {
300
AL10.alSourcei(source, AL10.AL_LOOPING, AL10.AL_FALSE);
301
AL10.alSourcef(source, AL10.AL_PITCH, pitch);
302
303
remainingBufferCount = BUFFER_COUNT;
304
305
for (int i = 0; i < BUFFER_COUNT; i++) {
306
stream(bufferNames.get(i));
307
}
308
309
AL10.alSourceQueueBuffers(source, bufferNames);
310
AL10.alSourcePlay(source);
311
}
312
313
/**
314
* Return the current playing position in the sound
315
*
316
* @return The current position in seconds.
317
*/
318
public float getPosition() {
319
return positionOffset + AL10.alGetSourcef(source, AL11.AL_SEC_OFFSET);
320
}
321
}
322
323
324