rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdint.h>
|
nuclear@0
|
3 #include <assert.h>
|
nuclear@0
|
4 #include "openal.h"
|
nuclear@0
|
5 #include "stream.h"
|
nuclear@0
|
6 #include "timer.h"
|
nuclear@0
|
7
|
nuclear@0
|
8 static ALenum alformat(AudioStreamBuffer *buf)
|
nuclear@0
|
9 {
|
nuclear@0
|
10 return buf->channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
nuclear@0
|
11 }
|
nuclear@0
|
12
|
nuclear@0
|
13 AudioStream::AudioStream()
|
nuclear@0
|
14 {
|
nuclear@0
|
15 alsrc = 0;
|
nuclear@0
|
16 poll_interval = 25;
|
nuclear@0
|
17 done = true;
|
nuclear@0
|
18 loop = false;
|
nuclear@0
|
19 volume = 1.0;
|
nuclear@0
|
20 pitch = 1.0;
|
nuclear@0
|
21
|
nuclear@0
|
22 pthread_mutex_init(&mutex, 0);
|
nuclear@0
|
23 }
|
nuclear@0
|
24
|
nuclear@0
|
25 AudioStream::~AudioStream()
|
nuclear@0
|
26 {
|
nuclear@0
|
27 stop();
|
nuclear@0
|
28 }
|
nuclear@0
|
29
|
nuclear@0
|
30 bool AudioStream::open(const char *fname)
|
nuclear@0
|
31 {
|
nuclear@0
|
32 return false;
|
nuclear@0
|
33 }
|
nuclear@0
|
34
|
nuclear@0
|
35 void AudioStream::close()
|
nuclear@0
|
36 {
|
nuclear@0
|
37 }
|
nuclear@0
|
38
|
nuclear@0
|
39 void AudioStream::set_volume(float vol)
|
nuclear@0
|
40 {
|
nuclear@0
|
41 if(vol < 0.0) vol = 0.0;
|
nuclear@0
|
42 if(vol > 1.0) vol = 1.0;
|
nuclear@0
|
43
|
nuclear@0
|
44 volume = vol;
|
nuclear@0
|
45
|
nuclear@0
|
46 pthread_mutex_lock(&mutex);
|
nuclear@0
|
47 if(alsrc) {
|
nuclear@0
|
48 alSourcef(alsrc, AL_GAIN, vol);
|
nuclear@0
|
49 }
|
nuclear@0
|
50 pthread_mutex_unlock(&mutex);
|
nuclear@0
|
51 }
|
nuclear@0
|
52
|
nuclear@0
|
53 float AudioStream::get_volume() const
|
nuclear@0
|
54 {
|
nuclear@0
|
55 return volume;
|
nuclear@0
|
56 }
|
nuclear@0
|
57 void AudioStream::set_pitch(float pitch)
|
nuclear@0
|
58 {
|
nuclear@0
|
59 if(pitch < 0.0) pitch = 0.0;
|
nuclear@0
|
60 if(pitch > 1.0) pitch = 1.0;
|
nuclear@0
|
61
|
nuclear@0
|
62 this->pitch = pitch;
|
nuclear@0
|
63
|
nuclear@0
|
64 pthread_mutex_lock(&mutex);
|
nuclear@0
|
65 if(alsrc) {
|
nuclear@0
|
66 alSourcef(alsrc, AL_PITCH, pitch);
|
nuclear@0
|
67 }
|
nuclear@0
|
68 pthread_mutex_unlock(&mutex);
|
nuclear@0
|
69 }
|
nuclear@0
|
70
|
nuclear@0
|
71 float AudioStream::get_pitch() const
|
nuclear@0
|
72 {
|
nuclear@0
|
73 return pitch;
|
nuclear@0
|
74 }
|
nuclear@0
|
75
|
nuclear@0
|
76
|
nuclear@0
|
77 static void *thread_func(void *arg)
|
nuclear@0
|
78 {
|
nuclear@0
|
79 AudioStream *astr = (AudioStream*)arg;
|
nuclear@0
|
80 astr->poll_loop();
|
nuclear@0
|
81 return 0;
|
nuclear@0
|
82 }
|
nuclear@0
|
83
|
nuclear@0
|
84 void AudioStream::play(AUDIO_PLAYMODE mode)
|
nuclear@0
|
85 {
|
nuclear@0
|
86 loop = (mode == AUDIO_PLAYMODE_LOOP);
|
nuclear@0
|
87 done = false;
|
nuclear@0
|
88
|
nuclear@0
|
89 if(pthread_create(&play_thread, 0, thread_func, this) != 0) {
|
nuclear@0
|
90 fprintf(stderr, "failed to create music playback thread\n");
|
nuclear@0
|
91 }
|
nuclear@0
|
92 }
|
nuclear@0
|
93
|
nuclear@0
|
94 void AudioStream::stop()
|
nuclear@0
|
95 {
|
nuclear@0
|
96 pthread_mutex_lock(&mutex);
|
nuclear@0
|
97
|
nuclear@0
|
98 if(alsrc) {
|
nuclear@0
|
99 done = true;
|
nuclear@0
|
100 //alSourceStop(alsrc);
|
nuclear@0
|
101 printf("waiting for the music thread to stop\n");
|
nuclear@0
|
102 pthread_mutex_unlock(&mutex);
|
nuclear@0
|
103 pthread_join(play_thread, 0);
|
nuclear@0
|
104 } else {
|
nuclear@0
|
105 pthread_mutex_unlock(&mutex);
|
nuclear@0
|
106 }
|
nuclear@0
|
107 }
|
nuclear@0
|
108
|
nuclear@0
|
109 // gets an array of buffers and returns the index of the one matching id
|
nuclear@0
|
110 static inline int find_buffer(unsigned int id, unsigned int *barr, int num)
|
nuclear@0
|
111 {
|
nuclear@0
|
112 for(int i=0; i<num; i++) {
|
nuclear@0
|
113 if(barr[i] == id) {
|
nuclear@0
|
114 return i;
|
nuclear@0
|
115 }
|
nuclear@0
|
116 }
|
nuclear@0
|
117 return -1;
|
nuclear@0
|
118 }
|
nuclear@0
|
119
|
nuclear@0
|
120
|
nuclear@0
|
121 static int queued_idx_list[AUDIO_NUM_BUFFERS];
|
nuclear@0
|
122 static int queued_idx_head = 0;
|
nuclear@0
|
123 static int queued_idx_tail = 0;
|
nuclear@0
|
124
|
nuclear@0
|
125 #define BUFQ_UNQUEUE() \
|
nuclear@0
|
126 do { \
|
nuclear@0
|
127 queued_idx_tail = (queued_idx_tail + 1) % AUDIO_NUM_BUFFERS; \
|
nuclear@0
|
128 } while(0)
|
nuclear@0
|
129
|
nuclear@0
|
130
|
nuclear@0
|
131 #define BUFQ_QUEUE(idx) \
|
nuclear@0
|
132 do { \
|
nuclear@0
|
133 queued_idx_head = (queued_idx_head + 1) % AUDIO_NUM_BUFFERS; \
|
nuclear@0
|
134 queued_idx_list[queued_idx_head] = idx; \
|
nuclear@0
|
135 } while(0)
|
nuclear@0
|
136
|
nuclear@0
|
137 // thread function
|
nuclear@0
|
138 void AudioStream::poll_loop()
|
nuclear@0
|
139 {
|
nuclear@0
|
140 long prev_msec = -1000;
|
nuclear@0
|
141 unsigned int albuf[AUDIO_NUM_BUFFERS];
|
nuclear@0
|
142
|
nuclear@0
|
143 pthread_mutex_lock(&mutex);
|
nuclear@0
|
144 alGenSources(1, &alsrc);
|
nuclear@0
|
145 alSourcei(alsrc, AL_LOOPING, AL_FALSE);
|
nuclear@0
|
146 alSourcef(alsrc, AL_GAIN, volume);
|
nuclear@0
|
147 alSourcef(alsrc, AL_PITCH, pitch);
|
nuclear@0
|
148 alGenBuffers(AUDIO_NUM_BUFFERS, albuf);
|
nuclear@0
|
149 AudioStreamBuffer *buf = new AudioStreamBuffer;
|
nuclear@0
|
150
|
nuclear@0
|
151 assert(alGetError() == 0);
|
nuclear@0
|
152 for(int i=0; i<AUDIO_NUM_BUFFERS; i++) {
|
nuclear@0
|
153 if(more_samples(buf)) {
|
nuclear@0
|
154 int bufsz = buf->num_samples * buf->channels * 2; // 2 is for 16bit samples
|
nuclear@0
|
155 alBufferData(albuf[i], alformat(buf), buf->samples, bufsz, buf->sample_rate);
|
nuclear@0
|
156
|
nuclear@0
|
157 if(alGetError()) {
|
nuclear@0
|
158 fprintf(stderr, "failed to load sample data into OpenAL buffer\n");
|
nuclear@0
|
159 }
|
nuclear@0
|
160
|
nuclear@0
|
161 alSourceQueueBuffers(alsrc, 1, albuf + i);
|
nuclear@0
|
162 BUFQ_QUEUE(i);
|
nuclear@0
|
163
|
nuclear@0
|
164 if(alGetError()) {
|
nuclear@0
|
165 fprintf(stderr, "failed to start streaming audio buffers\n");
|
nuclear@0
|
166 }
|
nuclear@0
|
167 } else {
|
nuclear@0
|
168 break;
|
nuclear@0
|
169 }
|
nuclear@0
|
170 }
|
nuclear@0
|
171
|
nuclear@0
|
172 // start playback
|
nuclear@0
|
173 alSourcePlay(alsrc);
|
nuclear@0
|
174 while(!done) {
|
nuclear@0
|
175 /* find out how many (if any) of the queued buffers are
|
nuclear@0
|
176 * done, and free to be reused.
|
nuclear@0
|
177 */
|
nuclear@0
|
178 int num_buf_done;
|
nuclear@0
|
179 alGetSourcei(alsrc, AL_BUFFERS_PROCESSED, &num_buf_done);
|
nuclear@0
|
180 for(int i=0; i<num_buf_done; i++) {
|
nuclear@0
|
181 int err;
|
nuclear@0
|
182 // unqueue a buffer...
|
nuclear@0
|
183 unsigned int buf_id;
|
nuclear@0
|
184 alSourceUnqueueBuffers(alsrc, 1, &buf_id);
|
nuclear@0
|
185
|
nuclear@0
|
186 if((err = alGetError())) {
|
nuclear@0
|
187 fprintf(stderr, "failed to unqueue used buffer (error: %x)\n", err);
|
nuclear@0
|
188 num_buf_done = i;
|
nuclear@0
|
189 break;
|
nuclear@0
|
190 }
|
nuclear@0
|
191 BUFQ_UNQUEUE();
|
nuclear@0
|
192
|
nuclear@0
|
193 // find out which one of our al buffers we just unqueued
|
nuclear@0
|
194 int bidx = find_buffer(buf_id, albuf, AUDIO_NUM_BUFFERS);
|
nuclear@0
|
195 assert(bidx != -1);
|
nuclear@0
|
196
|
nuclear@0
|
197 int looping;
|
nuclear@0
|
198
|
nuclear@0
|
199 alGetSourcei(alsrc, AL_LOOPING, &looping);
|
nuclear@0
|
200 assert(looping == AL_FALSE);
|
nuclear@0
|
201 /*if((unsigned int)cur_buf == buf_id) {
|
nuclear@0
|
202 continue;
|
nuclear@0
|
203 }*/
|
nuclear@0
|
204
|
nuclear@0
|
205 // if there are more data, fill it up and requeue it
|
nuclear@0
|
206 if(more_samples(buf)) {
|
nuclear@0
|
207 int bufsz = buf->num_samples * buf->channels * 2; // 2 is for 16bit samples
|
nuclear@0
|
208 alBufferData(buf_id, alformat(buf), buf->samples, bufsz, buf->sample_rate);
|
nuclear@0
|
209 if((err = alGetError())) {
|
nuclear@0
|
210 fprintf(stderr, "failed to load sample data into OpenAL buffer (error: %x)\n", err);
|
nuclear@0
|
211 }
|
nuclear@0
|
212
|
nuclear@0
|
213 alSourceQueueBuffers(alsrc, 1, &buf_id);
|
nuclear@0
|
214 if(alGetError()) {
|
nuclear@0
|
215 fprintf(stderr, "failed to start streaming audio buffers\n");
|
nuclear@0
|
216 }
|
nuclear@0
|
217 BUFQ_QUEUE(bidx);
|
nuclear@0
|
218 } else {
|
nuclear@0
|
219 // no more data...
|
nuclear@0
|
220 if(loop) {
|
nuclear@0
|
221 printf("audio stream looping...\n");
|
nuclear@0
|
222 rewind();
|
nuclear@0
|
223 } else {
|
nuclear@0
|
224 done = true;
|
nuclear@0
|
225 }
|
nuclear@0
|
226 }
|
nuclear@0
|
227 }
|
nuclear@0
|
228
|
nuclear@0
|
229 if(num_buf_done) {
|
nuclear@0
|
230 // make sure playback didn't stop
|
nuclear@0
|
231 int state;
|
nuclear@0
|
232 alGetSourcei(alsrc, AL_SOURCE_STATE, &state);
|
nuclear@0
|
233 if(state != AL_PLAYING) {
|
nuclear@0
|
234 alSourcePlay(alsrc);
|
nuclear@0
|
235 }
|
nuclear@0
|
236 }
|
nuclear@0
|
237
|
nuclear@0
|
238 pthread_mutex_unlock(&mutex);
|
nuclear@0
|
239 long msec = get_time_msec();
|
nuclear@0
|
240 long dt = msec - prev_msec;
|
nuclear@0
|
241 prev_msec = msec;
|
nuclear@0
|
242
|
nuclear@0
|
243 if(dt < (long)poll_interval) {
|
nuclear@0
|
244 sleep_msec(poll_interval - dt);
|
nuclear@0
|
245 } else {
|
nuclear@0
|
246 sched_yield();
|
nuclear@0
|
247 }
|
nuclear@0
|
248 pthread_mutex_lock(&mutex);
|
nuclear@0
|
249 }
|
nuclear@0
|
250
|
nuclear@0
|
251
|
nuclear@0
|
252 // done with the data, wait for the source to stop playing before cleanup
|
nuclear@0
|
253 int state;
|
nuclear@0
|
254 while(alGetSourcei(alsrc, AL_SOURCE_STATE, &state), state == AL_PLAYING) {
|
nuclear@0
|
255 sched_yield();
|
nuclear@0
|
256 }
|
nuclear@0
|
257
|
nuclear@0
|
258 alDeleteBuffers(AUDIO_NUM_BUFFERS, albuf);
|
nuclear@0
|
259 alDeleteSources(1, &alsrc);
|
nuclear@0
|
260 alsrc = 0;
|
nuclear@0
|
261 pthread_mutex_unlock(&mutex);
|
nuclear@0
|
262
|
nuclear@0
|
263 delete buf;
|
nuclear@0
|
264 }
|