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 }