rev |
line source |
nuclear@54
|
1 #include <stdio.h>
|
nuclear@53
|
2 #include "stream.h"
|
nuclear@54
|
3 #include "openal.h"
|
nuclear@54
|
4
|
nuclear@54
|
5 AudioStream::AudioStream()
|
nuclear@54
|
6 {
|
nuclear@54
|
7 alsrc = 0;
|
nuclear@54
|
8 poll_interval = 250;
|
nuclear@54
|
9 done = true;
|
nuclear@54
|
10 loop = false;
|
nuclear@54
|
11 }
|
nuclear@53
|
12
|
nuclear@53
|
13 AudioStream::~AudioStream()
|
nuclear@53
|
14 {
|
nuclear@54
|
15 stop();
|
nuclear@53
|
16 }
|
nuclear@53
|
17
|
nuclear@55
|
18 void AudioStream::play(enum PlayMode mode)
|
nuclear@53
|
19 {
|
nuclear@55
|
20 loop = (mode == PlayMode::loop);
|
nuclear@54
|
21 done = false;
|
nuclear@54
|
22 play_thread = std::thread(&AudioStream::poll_loop, this);
|
nuclear@53
|
23 }
|
nuclear@53
|
24
|
nuclear@53
|
25 void AudioStream::stop()
|
nuclear@53
|
26 {
|
nuclear@55
|
27 std::lock_guard<std::mutex> lock(mutex);
|
nuclear@55
|
28
|
nuclear@54
|
29 if(alsrc) {
|
nuclear@54
|
30 done = true;
|
nuclear@54
|
31 alSourceStop(alsrc);
|
nuclear@54
|
32 play_thread.join();
|
nuclear@54
|
33 }
|
nuclear@53
|
34 }
|
nuclear@54
|
35
|
nuclear@54
|
36 static ALenum alformat(AudioStreamBuffer *buf)
|
nuclear@54
|
37 {
|
nuclear@54
|
38 return buf->channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
nuclear@54
|
39 }
|
nuclear@54
|
40
|
nuclear@54
|
41 // thread function
|
nuclear@54
|
42 void AudioStream::poll_loop()
|
nuclear@54
|
43 {
|
nuclear@54
|
44 static const int num_buffers = 3;
|
nuclear@54
|
45 unsigned int albuf[num_buffers];
|
nuclear@54
|
46
|
nuclear@55
|
47 mutex.lock();
|
nuclear@55
|
48
|
nuclear@54
|
49 alGenSources(1, &alsrc);
|
nuclear@54
|
50 alGenBuffers(num_buffers, albuf);
|
nuclear@54
|
51
|
nuclear@55
|
52 AudioStreamBuffer *buf = new AudioStreamBuffer;
|
nuclear@55
|
53
|
nuclear@54
|
54 for(int i=0; i<num_buffers; i++) {
|
nuclear@55
|
55 if(more_samples(buf)) {
|
nuclear@55
|
56 int bufsz = buf->num_samples * buf->channels * 2; // 2 is for 16bit samples
|
nuclear@55
|
57 printf("buffer data: %d samples, %d rate, %d channels\n", buf->num_samples, buf->sample_rate, buf->channels);
|
nuclear@55
|
58 alBufferData(albuf[i], alformat(buf), buf->samples, bufsz, buf->sample_rate);
|
nuclear@55
|
59 if(alGetError()) {
|
nuclear@55
|
60 fprintf(stderr, "failed to load sample data into OpenAL buffer\n");
|
nuclear@55
|
61 }
|
nuclear@55
|
62
|
nuclear@54
|
63 alSourceQueueBuffers(alsrc, 1, albuf + i);
|
nuclear@55
|
64 if(alGetError()) {
|
nuclear@55
|
65 fprintf(stderr, "failed to start streaming audio buffers\n");
|
nuclear@55
|
66 }
|
nuclear@54
|
67 } else {
|
nuclear@54
|
68 break;
|
nuclear@54
|
69 }
|
nuclear@54
|
70 }
|
nuclear@55
|
71 // start playback
|
nuclear@55
|
72 alSourcePlay(alsrc);
|
nuclear@55
|
73 mutex.unlock();
|
nuclear@54
|
74
|
nuclear@54
|
75 while(!done) {
|
nuclear@55
|
76 mutex.lock();
|
nuclear@54
|
77 /* find out how many (if any) of the queued buffers are
|
nuclear@54
|
78 * done, and free to be reused.
|
nuclear@54
|
79 */
|
nuclear@54
|
80 int num_buf_done;
|
nuclear@54
|
81 alGetSourcei(alsrc, AL_BUFFERS_PROCESSED, &num_buf_done);
|
nuclear@54
|
82 for(int i=0; i<num_buf_done; i++) {
|
nuclear@54
|
83 // unqueue a buffer...
|
nuclear@54
|
84 unsigned int buf_id;
|
nuclear@54
|
85 alSourceUnqueueBuffers(alsrc, 1, &buf_id);
|
nuclear@54
|
86
|
nuclear@54
|
87 // if there are more data, fill it up and requeue it
|
nuclear@55
|
88 if(more_samples(buf)) {
|
nuclear@55
|
89 int bufsz = buf->num_samples * buf->channels * 2; // 2 is for 16bit samples
|
nuclear@55
|
90 printf("buffer data: %d samples, %d rate, %d channels\n", buf->num_samples, buf->sample_rate, buf->channels);
|
nuclear@55
|
91 alBufferData(buf_id, alformat(buf), buf->samples, bufsz, buf->sample_rate);
|
nuclear@54
|
92 if(alGetError()) {
|
nuclear@54
|
93 fprintf(stderr, "failed to load sample data into OpenAL buffer\n");
|
nuclear@54
|
94 }
|
nuclear@55
|
95
|
nuclear@55
|
96 alSourceQueueBuffers(alsrc, 1, albuf + i);
|
nuclear@55
|
97 if(alGetError()) {
|
nuclear@55
|
98 fprintf(stderr, "failed to start streaming audio buffers\n");
|
nuclear@55
|
99 }
|
nuclear@54
|
100 } else {
|
nuclear@54
|
101 // no more data...
|
nuclear@54
|
102 if(loop) {
|
nuclear@54
|
103 rewind();
|
nuclear@54
|
104 } else {
|
nuclear@54
|
105 done = true;
|
nuclear@54
|
106 }
|
nuclear@54
|
107 }
|
nuclear@54
|
108 }
|
nuclear@54
|
109
|
nuclear@54
|
110 if(num_buf_done) {
|
nuclear@54
|
111 // make sure playback didn't stop
|
nuclear@54
|
112 int state;
|
nuclear@54
|
113 alGetSourcei(alsrc, AL_SOURCE_STATE, &state);
|
nuclear@54
|
114 if(state != AL_PLAYING) {
|
nuclear@54
|
115 alSourcePlay(alsrc);
|
nuclear@54
|
116 }
|
nuclear@54
|
117 }
|
nuclear@54
|
118
|
nuclear@55
|
119 mutex.unlock();
|
nuclear@55
|
120
|
nuclear@54
|
121 std::chrono::milliseconds dur{poll_interval};
|
nuclear@54
|
122 std::this_thread::sleep_for(dur);
|
nuclear@54
|
123 }
|
nuclear@54
|
124
|
nuclear@55
|
125 mutex.lock();
|
nuclear@55
|
126
|
nuclear@54
|
127 // done with the data, wait for the source to stop playing before cleanup
|
nuclear@54
|
128 int state;
|
nuclear@54
|
129 while(alGetSourcei(alsrc, AL_SOURCE_STATE, &state), state == AL_PLAYING) {
|
nuclear@54
|
130 std::this_thread::yield();
|
nuclear@54
|
131 }
|
nuclear@54
|
132
|
nuclear@54
|
133 alDeleteBuffers(num_buffers, albuf);
|
nuclear@54
|
134 alDeleteSources(1, &alsrc);
|
nuclear@54
|
135 alsrc = 0;
|
nuclear@55
|
136
|
nuclear@55
|
137 mutex.unlock();
|
nuclear@54
|
138 }
|