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