1
2 #include <SDL2/SDL.h>
3 #include <SDL2/SDL_audio.h>
4
5 #include <math.h>
6
7 extern "C" {
8 #include <al.h>
9 #include <alc.h>
10 }
11
12 #include "audio.h"
13 #include "audio_stream.h"
14
15
16 // ======== PRIVATE PROTOTYPES =============
17 struct stream_internal_s
18 {
19 ALuint source;
20 ALuint buffers[TR_AUDIO_STREAM_NUMBUFFERS];
21 };
22
23 bool Audio_FillALBuffer(ALuint buf_number, Uint8* buffer_data, Uint32 buffer_size, int sample_bitsize, int channels, int frequency);
24 void Audio_SetFX(ALuint source);
25 void Audio_UnsetFX(ALuint source);
26
27
StreamTrack_Init(stream_track_p s)28 void StreamTrack_Init(stream_track_p s)
29 {
30 s->type = TR_AUDIO_STREAM_TYPE_ONESHOT;
31 s->state = TR_AUDIO_STREAM_STOPPED;
32 s->linked_buffers = 0;
33 s->buffer_offset = 0;
34 s->current_volume = 0.0f;
35 s->track = -1;
36 s->internal = (struct stream_internal_s*)malloc(sizeof(struct stream_internal_s));
37 alGenBuffers(TR_AUDIO_STREAM_NUMBUFFERS, s->internal->buffers);
38 alGenSources(1, &s->internal->source);
39 if(alIsSource(s->internal->source))
40 {
41 alSource3f(s->internal->source, AL_POSITION, 0.0f, 0.0f, -1.0f); // OpenAL tut says this.
42 alSource3f(s->internal->source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
43 alSource3f(s->internal->source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
44 alSourcef (s->internal->source, AL_ROLLOFF_FACTOR, 0.0f );
45 alSourcei (s->internal->source, AL_SOURCE_RELATIVE, AL_TRUE );
46 alSourcei (s->internal->source, AL_LOOPING, AL_FALSE ); // No effect, but just in case...
47 }
48 }
49
StreamTrack_Clear(stream_track_p s)50 void StreamTrack_Clear(stream_track_p s)
51 {
52 StreamTrack_Stop(s);
53 s->buffer_offset = 0;
54 s->linked_buffers = 0;
55 if(alIsSource(s->internal->source))
56 {
57 alSourceStop(s->internal->source);
58 alDeleteSources(1, &s->internal->source);
59 s->internal->source = 0;
60 }
61 alDeleteBuffers(TR_AUDIO_STREAM_NUMBUFFERS, s->internal->buffers);
62 free(s->internal);
63 s->internal = NULL;
64 }
65
66
StreamTrack_SetEffects(stream_track_p s,int value)67 void StreamTrack_SetEffects(stream_track_p s, int value)
68 {
69 if(value)
70 {
71 Audio_SetFX(s->internal->source);
72 }
73 else
74 {
75 Audio_UnsetFX(s->internal->source);
76 }
77 }
78
79
StreamTrack_IsNeedUpdateBuffer(stream_track_p s)80 int StreamTrack_IsNeedUpdateBuffer(stream_track_p s)
81 {
82 if(alIsSource(s->internal->source))
83 {
84 if(s->linked_buffers >= TR_AUDIO_STREAM_NUMBUFFERS)
85 {
86 ALint processed = 0;
87 alGetSourcei(s->internal->source, AL_BUFFERS_PROCESSED, &processed);
88 return processed > 0;
89 }
90 return 1;
91 }
92 return 0;
93 }
94
95
StreamTrack_UpdateBuffer(stream_track_p s,uint8_t * buff,size_t size,int sample_bitsize,int channels,int frequency)96 int StreamTrack_UpdateBuffer(stream_track_p s, uint8_t *buff, size_t size, int sample_bitsize, int channels, int frequency)
97 {
98 if(alIsSource(s->internal->source))
99 {
100 if(s->linked_buffers >= TR_AUDIO_STREAM_NUMBUFFERS)
101 {
102 ALint processed = 0;
103 // Check if any track buffers were already processed.
104 // by doc: "Buffer queuing loop must operate in a new thread"
105 alGetSourcei(s->internal->source, AL_BUFFERS_PROCESSED, &processed);
106 if(processed > 0)
107 {
108 ALuint buffer_index = 0;
109 alSourceUnqueueBuffers(s->internal->source, 1, &buffer_index);
110 if(Audio_FillALBuffer(buffer_index, buff, size, sample_bitsize, channels, frequency))
111 {
112 s->buffer_offset += size;
113 alSourceQueueBuffers(s->internal->source, 1, &buffer_index);
114 return 1;
115 }
116 return -1;
117 }
118 return 0;
119 }
120 else
121 {
122 ALuint buffer_index = s->internal->buffers[s->linked_buffers];
123 ++s->linked_buffers;
124 if(Audio_FillALBuffer(buffer_index, buff, size, sample_bitsize, channels, frequency))
125 {
126 alSourceQueueBuffers(s->internal->source, 1, &buffer_index);
127 s->buffer_offset += size;
128 return 1;
129 }
130 return -1;
131 }
132 }
133 return -1;
134 }
135
136
StreamTrack_Play(stream_track_p s)137 int StreamTrack_Play(stream_track_p s)
138 {
139 if(alIsSource(s->internal->source))
140 {
141 ALint state = 0;
142 alGetSourcei(s->internal->source, AL_SOURCE_STATE, &state);
143 if(state != AL_PLAYING)
144 {
145 s->state = TR_AUDIO_STREAM_PLAYING;
146 alSourcePlay(s->internal->source);
147 }
148 alSourcef(s->internal->source, AL_GAIN, s->current_volume);
149 return 1;
150 }
151 return -1;
152 }
153
154
StreamTrack_Stop(stream_track_p s)155 int StreamTrack_Stop(stream_track_p s)
156 {
157 if(alIsSource(s->internal->source))
158 {
159 ALint queued = 0;
160 ALint state = AL_STOPPED; // AL_STOPPED, AL_INITIAL, AL_PLAYING, AL_PAUSED
161 alGetSourcei(s->internal->source, AL_SOURCE_STATE, &state);
162 if(state != AL_STOPPED)
163 {
164 alSourceStop(s->internal->source);
165 }
166
167 alGetSourcei(s->internal->source, AL_BUFFERS_QUEUED, &queued);
168 while(0 < queued--)
169 {
170 ALuint buffer;
171 alSourceUnqueueBuffers(s->internal->source, 1, &buffer);
172 }
173 s->linked_buffers = 0;
174 s->buffer_offset = 0;
175 s->state = TR_AUDIO_STREAM_STOPPED;
176 return 1;
177 }
178 return -1;
179 }
180
181
StreamTrack_Pause(stream_track_p s)182 int StreamTrack_Pause(stream_track_p s)
183 {
184 if(alIsSource(s->internal->source) && (s->state == TR_AUDIO_STREAM_PLAYING))
185 {
186 alSourcePause(s->internal->source);
187 s->state = TR_AUDIO_STREAM_PAUSED;
188 }
189 }
190
191
StreamTrack_CheckForEnd(stream_track_p s)192 int StreamTrack_CheckForEnd(stream_track_p s)
193 {
194 if(alIsSource(s->internal->source) && (s->state != TR_AUDIO_STREAM_STOPPED))
195 {
196 ALint processed = 0;
197 ALint state = AL_STOPPED; // AL_STOPPED, AL_INITIAL, AL_PLAYING, AL_PAUSED
198 alGetSourcei(s->internal->source, AL_SOURCE_STATE, &state);
199 if((state == AL_STOPPED) || ((state == AL_PAUSED) && (s->state != TR_AUDIO_STREAM_PAUSED)))
200 {
201 return 1;
202 }
203
204 alGetSourcei(s->internal->source, AL_BUFFERS_PROCESSED, &processed);
205 return (processed >= s->linked_buffers) ? (1) : (0);
206 }
207 return 0;
208 }
209
210
StreamTrack_UpdateState(stream_track_p s,float time,float volume)211 int StreamTrack_UpdateState(stream_track_p s, float time, float volume)
212 {
213 if((s->state != TR_AUDIO_STREAM_STOPPED) && alIsSource(s->internal->source))
214 {
215 ALint state = 0;
216 ALfloat inc = 0.0f;
217 alGetSourcei(s->internal->source, AL_SOURCE_STATE, &state);
218
219 if(StreamTrack_CheckForEnd(s))
220 {
221 StreamTrack_Stop(s);
222 return 0;
223 }
224
225 switch(s->type)
226 {
227 case TR_AUDIO_STREAM_TYPE_BACKGROUND:
228 inc = time * TR_AUDIO_STREAM_CROSSFADE_BACKGROUND;
229 break;
230
231 case TR_AUDIO_STREAM_TYPE_ONESHOT:
232 inc = time * TR_AUDIO_STREAM_CROSSFADE_ONESHOT;
233 break;
234
235 case TR_AUDIO_STREAM_TYPE_CHAT:
236 inc = time * TR_AUDIO_STREAM_CROSSFADE_CHAT;
237 break;
238 }
239
240 if(s->state == TR_AUDIO_STREAM_STOPPING)
241 {
242 s->current_volume -= inc;
243 if(s->current_volume <= 0.0f)
244 {
245 s->current_volume = 0.0f;
246 StreamTrack_Stop(s);
247 return 0;
248 }
249 else
250 {
251 alSourcef(s->internal->source, AL_GAIN, s->current_volume);
252 }
253 }
254 else if((s->state == TR_AUDIO_STREAM_PLAYING) && (s->current_volume < volume))
255 {
256 s->current_volume += inc;
257 if(s->current_volume > volume)
258 {
259 s->current_volume = volume;
260 }
261 alSourcef(s->internal->source, AL_GAIN, s->current_volume);
262 }
263
264 return 1;
265 }
266
267 return 0;
268 }