dbf-halloween2015

annotate src/audio/stream.cc @ 3:c37fe5d8a4ed

windows port
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 01 Nov 2015 06:04:28 +0200
parents
children
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 }