dbf-halloween2015

view src/audio/stream.cc @ 0:50683c78264e

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