vrshoot

view src/audio/stream.cc @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +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 "logger.h"
7 #include "timer.h"
8 #include "kiss_fft.h"
10 struct FFTState {
11 kiss_fft_cfg kiss;
12 kiss_fft_cpx *inbuf, *outbuf;
13 int nsamples;
14 };
16 static ALenum alformat(AudioStreamBuffer *buf)
17 {
18 return buf->channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
19 }
21 AudioStream::AudioStream()
22 {
23 alsrc = 0;
24 poll_interval = 25;
25 done = true;
26 loop = false;
27 volume = 1.0;
29 freqhist = 0;
31 // by default disable FFT processing
32 use_fft = false;
34 pthread_mutex_init(&mutex, 0);
35 }
37 AudioStream::~AudioStream()
38 {
39 stop();
40 }
42 void AudioStream::enable_fft()
43 {
44 use_fft = true;
45 }
47 void AudioStream::disable_fft()
48 {
49 use_fft = false;
50 }
52 bool AudioStream::is_fft_enabled() const
53 {
54 return use_fft;
55 }
57 bool AudioStream::open(const char *fname)
58 {
59 return false;
60 }
62 void AudioStream::close()
63 {
64 }
66 void AudioStream::set_volume(float vol)
67 {
68 volume = vol;
70 pthread_mutex_lock(&mutex);
71 if(alsrc) {
72 alSourcef(alsrc, AL_GAIN, vol);
73 }
74 pthread_mutex_unlock(&mutex);
75 }
77 float AudioStream::get_volume() const
78 {
79 return volume;
80 }
82 static void *thread_func(void *arg)
83 {
84 AudioStream *astr = (AudioStream*)arg;
85 astr->poll_loop();
86 return 0;
87 }
89 void AudioStream::play(AUDIO_PLAYMODE mode)
90 {
91 loop = (mode == AUDIO_PLAYMODE_LOOP);
92 done = false;
94 if(pthread_create(&play_thread, 0, thread_func, this) != 0) {
95 error_log("failed to create music playback thread\n");
96 }
97 }
99 void AudioStream::stop()
100 {
101 pthread_mutex_lock(&mutex);
103 if(alsrc) {
104 done = true;
105 alSourceStop(alsrc);
106 printf("waiting for the music thread to stop\n");
107 pthread_mutex_unlock(&mutex);
108 pthread_join(play_thread, 0);
109 } else {
110 pthread_mutex_unlock(&mutex);
111 }
112 }
114 // gets an array of buffers and returns the index of the one matching id
115 static inline int find_buffer(unsigned int id, unsigned int *barr, int num)
116 {
117 for(int i=0; i<num; i++) {
118 if(barr[i] == id) {
119 return i;
120 }
121 }
122 return -1;
123 }
126 static int queued_idx_list[AUDIO_NUM_BUFFERS];
127 static int queued_idx_head = 0;
128 static int queued_idx_tail = 0;
130 #define BUFQ_UNQUEUE() \
131 do { \
132 queued_idx_tail = (queued_idx_tail + 1) % AUDIO_NUM_BUFFERS; \
133 } while(0)
136 #define BUFQ_QUEUE(idx) \
137 do { \
138 queued_idx_head = (queued_idx_head + 1) % AUDIO_NUM_BUFFERS; \
139 queued_idx_list[queued_idx_head] = idx; \
140 } while(0)
142 // thread function
143 void AudioStream::poll_loop()
144 {
145 long prev_msec = -1000;
146 unsigned int albuf[AUDIO_NUM_BUFFERS];
147 int freqbins[AUDIO_NUM_BUFFERS][AUDIO_FFT_BINS];
149 pthread_mutex_lock(&mutex);
150 alGenSources(1, &alsrc);
151 alSourcei(alsrc, AL_LOOPING, AL_FALSE);
152 alSourcef(alsrc, AL_GAIN, volume);
153 alGenBuffers(AUDIO_NUM_BUFFERS, albuf);
154 AudioStreamBuffer *buf = new AudioStreamBuffer;
156 FFTState fft;
157 fft.kiss = kiss_fft_alloc(AUDIO_FFT_SAMPLES, 0, 0, 0);
158 assert(fft.kiss);
159 fft.inbuf = new kiss_fft_cpx[AUDIO_FFT_SAMPLES];
160 fft.outbuf = new kiss_fft_cpx[AUDIO_FFT_SAMPLES];
161 assert(fft.inbuf && fft.outbuf);
162 fft.nsamples = AUDIO_FFT_SAMPLES;
164 // zero out the inbuf array to get rid of the imaginary parts
165 memset(fft.inbuf, 0, AUDIO_FFT_SAMPLES * sizeof *fft.inbuf);
167 for(int i=0; i<AUDIO_NUM_BUFFERS; i++) {
168 if(more_samples(buf)) {
169 int bufsz = buf->num_samples * buf->channels * 2; // 2 is for 16bit samples
170 alBufferData(albuf[i], alformat(buf), buf->samples, bufsz, buf->sample_rate);
172 if(alGetError()) {
173 fprintf(stderr, "failed to load sample data into OpenAL buffer\n");
174 }
176 alSourceQueueBuffers(alsrc, 1, albuf + i);
177 BUFQ_QUEUE(i);
179 if(alGetError()) {
180 fprintf(stderr, "failed to start streaming audio buffers\n");
181 }
183 // also calculate the frequencies
184 calc_freq(buf, freqbins[i], &fft);
185 } else {
186 break;
187 }
188 }
190 // start playback
191 alSourcePlay(alsrc);
192 while(!done) {
193 // XXX this doesn't work
194 /*
195 // first let's figure out which buffer is currently playing
196 int cur_buf;
197 alGetSourcei(alsrc, AL_BUFFER, &cur_buf);
198 int cur_buf_idx = find_buffer(cur_buf, albuf, AUDIO_NUM_BUFFERS);
200 // make the fft histogram pointer point to the correct frequency bin array
201 freqhist = cur_buf_idx != -1 ? freqbins[cur_buf_idx] : 0;
202 if(!freqhist) {
203 debug_log("skata\n");
204 }
205 */
207 /* find out how many (if any) of the queued buffers are
208 * done, and free to be reused.
209 */
210 int num_buf_done;
211 alGetSourcei(alsrc, AL_BUFFERS_PROCESSED, &num_buf_done);
212 for(int i=0; i<num_buf_done; i++) {
213 int err;
214 // unqueue a buffer...
215 unsigned int buf_id;
216 alSourceUnqueueBuffers(alsrc, 1, &buf_id);
218 if((err = alGetError())) {
219 fprintf(stderr, "failed to unqueue used buffer (error: %x)\n", err);
220 num_buf_done = i;
221 break;
222 }
223 BUFQ_UNQUEUE();
225 // find out which one of our al buffers we just unqueued
226 int bidx = find_buffer(buf_id, albuf, AUDIO_NUM_BUFFERS);
227 assert(bidx != -1);
229 int looping;
231 alGetSourcei(alsrc, AL_LOOPING, &looping);
232 assert(looping == AL_FALSE);
233 /*if((unsigned int)cur_buf == buf_id) {
234 continue;
235 }*/
237 // if there are more data, fill it up and requeue it
238 if(more_samples(buf)) {
239 int bufsz = buf->num_samples * buf->channels * 2; // 2 is for 16bit samples
240 alBufferData(buf_id, alformat(buf), buf->samples, bufsz, buf->sample_rate);
241 if((err = alGetError())) {
242 fprintf(stderr, "failed to load sample data into OpenAL buffer (error: %x)\n", err);
243 }
245 alSourceQueueBuffers(alsrc, 1, &buf_id);
246 if(alGetError()) {
247 fprintf(stderr, "failed to start streaming audio buffers\n");
248 }
249 BUFQ_QUEUE(bidx);
251 // also calculate the frequencies if required
252 if(use_fft) {
253 calc_freq(buf, freqbins[bidx], &fft);
254 }
255 } else {
256 // no more data...
257 if(loop) {
258 rewind();
259 } else {
260 done = true;
261 }
262 }
263 }
264 if(use_fft) {
265 freqhist = freqbins[queued_idx_list[queued_idx_tail]];
266 }
268 if(num_buf_done) {
269 // make sure playback didn't stop
270 int state;
271 alGetSourcei(alsrc, AL_SOURCE_STATE, &state);
272 if(state != AL_PLAYING) {
273 alSourcePlay(alsrc);
274 }
275 }
277 pthread_mutex_unlock(&mutex);
278 long msec = get_time_msec();
279 long dt = msec - prev_msec;
280 prev_msec = msec;
282 if(dt < poll_interval - 5) {
283 sleep_msec(poll_interval - dt);
284 } else {
285 sched_yield();
286 }
287 pthread_mutex_lock(&mutex);
288 }
291 // done with the data, wait for the source to stop playing before cleanup
292 int state;
293 while(alGetSourcei(alsrc, AL_SOURCE_STATE, &state), state == AL_PLAYING) {
294 sched_yield();
295 }
297 freqhist = 0;
299 alDeleteBuffers(AUDIO_NUM_BUFFERS, albuf);
300 alDeleteSources(1, &alsrc);
301 alsrc = 0;
302 pthread_mutex_unlock(&mutex);
304 delete buf;
306 delete [] fft.inbuf;
307 delete [] fft.outbuf;
308 kiss_fft_free(fft.kiss);
309 }
311 int AudioStream::freq_count(int bin) const
312 {
313 if(!freqhist || !use_fft || bin < 0 || bin >= AUDIO_BUFFER_SAMPLES) {
314 return 0;
315 }
316 return freqhist[bin];
317 }
319 #define NORM_FACTOR (1.0f / (float)AUDIO_FFT_SAMPLES)
320 float AudioStream::freq_normalized(int bin) const
321 {
322 // TODO remove the fudge factor
323 return freq_count(bin) * NORM_FACTOR * 0.25;
324 }
326 // frequency range in hertz
327 int AudioStream::freq_count(int range_start, int range_end) const
328 {
329 // NOTE this will probably be something like sampling freq / num-bins Hz per bin...
330 return 0; // TODO
331 }
333 // TODO ok this might be inefficient, copying the data around a lot, optimize later
334 void AudioStream::calc_freq(AudioStreamBuffer *buf, int *bins, FFTState *fft)
335 {
336 kiss_fft_cpx *inptr = fft->inbuf;
337 int16_t *samples = (int16_t*)buf->samples;
338 for(int i=0; i<AUDIO_BUFFER_SAMPLES; i++) {
340 inptr->i = 0;
341 if(i < buf->num_samples) {
342 int left = samples[i * 2];
343 int right = samples[i * 2 + 1];
345 (inptr++)->r = (left + right) / 2;
346 } else {
347 (inptr++)->r = 0;
348 }
349 }
351 kiss_fft(fft->kiss, fft->inbuf, fft->outbuf);
353 // then copy all the relevant data to the bins array
354 int num_out_samples = AUDIO_BUFFER_SAMPLES / 2;
355 int samples_per_bin = num_out_samples / AUDIO_FFT_BINS;
357 long abins[AUDIO_FFT_BINS];
359 int prev_bidx = -1;
360 // ignore the DC bin (0)
361 for(int i=1; i<num_out_samples; i++) {
362 int bidx = i * AUDIO_FFT_BINS / num_out_samples;
363 float x = fft->outbuf[i].r;
364 float y = fft->outbuf[i].i;
365 int val = x * x + y * y;
367 if(bidx != prev_bidx) {
368 abins[bidx] = val;
369 prev_bidx = bidx;
370 } else {
371 abins[bidx] += val;
372 }
373 }
375 for(int i=0; i<AUDIO_FFT_BINS; i++) {
376 long res = abins[i] / (long)samples_per_bin;
377 bins[i] = res;
378 assert(bins[i] == res);
379 }
380 }