]> git.mxchange.org Git - flightgear.git/blob - utils/iaxclient/lib/audio_openal.c
Improve OpenAL driver for IAXClient
[flightgear.git] / utils / iaxclient / lib / audio_openal.c
1 #include "iaxclient_lib.h"
2
3 #ifdef __APPLE__
4 #include <OpenAL/al.h>
5 #include <OpenAL/alc.h>
6 #elif defined(OPENALSDK)
7 #include <al.h>
8 #include <alc.h>
9 #else
10 #include <AL/al.h>
11 #include <AL/alc.h>
12 #endif
13
14 struct openal_priv_data
15 {
16     int sample_rate;
17     int num_buffers;
18     int buffers_head;
19     int buffers_tail;
20     int buffers_free;
21     ALuint* buffers;
22     ALCcontext* out_ctx;
23     ALuint source;
24     ALCdevice* in_dev;
25     double input_level;
26     double output_level;
27 };
28
29 static struct iaxc_audio_device device = { 
30     "default", 
31     IAXC_AD_INPUT | IAXC_AD_OUTPUT | IAXC_AD_RING | IAXC_AD_INPUT_DEFAULT | IAXC_AD_OUTPUT_DEFAULT | IAXC_AD_RING_DEFAULT,
32     0 
33 };
34
35 static int openal_error(const char* function, int err)
36 {
37     fprintf(stderr, "OpenAl function %s failed with code %d\n", function, err);
38     return -1;
39 }
40
41 int openal_input(struct iaxc_audio_driver *d, void *samples, int *nSamples)
42 {
43     int err;
44     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
45
46     ALCint available;
47     ALCsizei request;
48
49     alcGetIntegerv(priv->in_dev, ALC_CAPTURE_SAMPLES, sizeof(available), &available);
50     /* do not return less data than caller wanted, iaxclient does not like it */
51     request = (available < *nSamples) ? 0 : *nSamples;
52     if (request > 0)
53     {
54         err = alcGetError(priv->in_dev);
55         alcCaptureSamples(priv->in_dev, samples, request);    
56         err = alcGetError(priv->in_dev);
57         if (err)
58         {
59             openal_error("alcCaptureSamples", err);
60             *nSamples = 0;
61             return 1;
62         }
63         // software mute, but keep data flowing for sync purposes
64         if (priv->input_level == 0)
65         {
66             memset(samples, 0, 2 * request);
67         }
68     }
69     *nSamples = request;
70     
71     return 0;
72 }
73
74 static void openal_unqueue(struct openal_priv_data* priv)
75 {
76     int i;
77     ALint err;
78     ALint processed;
79
80     alGetSourcei(priv->source, AL_BUFFERS_PROCESSED, &processed);
81
82 #ifdef OPENAL_DEBUG
83     {
84         ALint queued;
85         ALint state;
86         
87         alGetSourcei(priv->source, AL_BUFFERS_QUEUED, &queued);
88         alGetSourcei(priv->source, AL_SOURCE_STATE, &state);
89
90         fprintf(stderr, "free: %d processed: %d queued: %d head: %d tail: %d state: %d\n", 
91             priv->buffers_free, processed, queued, priv->buffers_head, priv->buffers_tail, state);
92     }
93 #endif
94         
95     alGetError();
96     for(i = 0; i < processed; i++)
97     {
98         alSourceUnqueueBuffers(priv->source, 1, priv->buffers + priv->buffers_tail);
99         err = alGetError();
100         if (err)
101         {
102             openal_error("alSourceUnqueueBuffers", err);
103             break;
104         }
105         if (++priv->buffers_tail >= priv->num_buffers)
106         {
107             priv->buffers_tail = 0;
108         }
109         ++priv->buffers_free;
110     }
111 }
112
113 int openal_output(struct iaxc_audio_driver *d, void *samples, int nSamples)
114 {
115     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
116
117     openal_unqueue(priv);
118     /* If we run out of buffers, wait for an arbitrary number to become free */
119     if (priv->buffers_free == 0)
120     {
121         while(priv->buffers_free < 4)
122         {
123             iaxc_millisleep(100);
124             openal_unqueue(priv);
125         }
126     }
127     
128     if (priv->buffers_free > 0)
129     {
130         ALuint buffer = priv->buffers[priv->buffers_head++];
131         if (priv->buffers_head >= priv->num_buffers)
132         {
133             priv->buffers_head = 0;
134         }
135         
136         alBufferData(buffer, AL_FORMAT_MONO16, samples, nSamples * 2, priv->sample_rate);
137         alSourceQueueBuffers(priv->source, 1, &buffer);
138         --priv->buffers_free;
139         
140         /* delay start of output until we have 2 buffers */
141         if (priv->buffers_free == priv->num_buffers - 2)
142         {
143             ALint state;
144         
145             alGetSourcei(priv->source, AL_SOURCE_STATE, &state);
146             if (state != AL_PLAYING)
147             {
148 #ifdef OPENAL_DEBUG
149                 fprintf(stderr, "calling alSourcePlay\n");
150 #endif      
151                 alSourcePlay(priv->source);
152             }
153         }
154     } else {
155         fprintf(stderr, "openal_output buffer overflow\n");
156         return 1;
157     }
158     
159     return 0;
160 }
161
162 int openal_select_devices(struct iaxc_audio_driver *d, int input, int output, int ring)
163 {
164     return (input != 0 || output !=0 || ring != 0) ? -1 : 0;
165 }
166
167 int openal_selected_devices(struct iaxc_audio_driver *d, int *input, int *output, int *ring)
168 {
169     *input = 0;
170     *output = 0;
171     *ring = 0;
172     
173     return 0;
174 }
175
176 /* 
177     Apparently iaxclient calls openal_start a gazillion times and doesn't call openal_stop.
178     So let's just make them no-ops.
179 */
180 int openal_start(struct iaxc_audio_driver *d)
181 {
182     int iret = 0;
183     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
184     if (priv) /* just to stop compiler noise */
185         iret = 0;
186     return iret;
187 }
188
189 int openal_stop(struct iaxc_audio_driver *d)
190 {
191     int iret = 0;
192     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
193     if (priv) /* just to stop compiler noise */
194         iret = 0;
195     return iret;
196 }
197
198 float openal_input_level_get(struct iaxc_audio_driver *d)
199 {
200     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
201
202     return (float)priv->input_level;
203 }
204
205 float openal_output_level_get(struct iaxc_audio_driver *d)
206 {
207     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
208
209     return priv->output_level;
210 }
211
212 int openal_input_level_set(struct iaxc_audio_driver *d, float level)
213 {
214     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
215     priv->input_level = level;
216
217     return 0;
218 }
219
220 int openal_output_level_set(struct iaxc_audio_driver *d, float level)
221 {
222     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
223     priv->output_level = level;
224     alSourcef(priv->source, AL_GAIN, level);
225
226     return 0;
227 }
228
229 int openal_play_sound(struct iaxc_sound *s, int ring)
230 {
231     return 0;
232 }
233
234 int openal_stop_sound(int id)
235 {
236     return 0;
237 }
238
239 int openal_mic_boost_get(struct iaxc_audio_driver *d)
240 {
241     return 0;
242 }
243
244 int openal_mic_boost_set(struct iaxc_audio_driver *d, int enable)
245 {
246     return 0;
247 }
248
249 int openal_destroy(struct iaxc_audio_driver *d)
250 {
251     struct openal_priv_data* priv = (struct openal_priv_data*)(d->priv);
252
253     alcCaptureStop(priv->in_dev);
254     alcCaptureCloseDevice(priv->in_dev);
255     alDeleteSources(1, &priv->source);
256
257     return 0;
258 }
259
260 int openal_initialize(struct iaxc_audio_driver *d, int sample_rate)
261 {
262     struct openal_priv_data* priv = malloc(sizeof(struct openal_priv_data));
263     int err = alGetError();
264     d->priv = priv;
265
266     // First we are looking for input device
267     priv->in_dev = alcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 800);
268     if (!priv->in_dev) return openal_error("alcCaptureOpenDevice", alGetError());
269
270     alcCaptureStart(priv->in_dev);
271     if ((err = alGetError())) return openal_error("alcCaptureStart", err);
272
273     // Then we look for output device
274     priv->out_ctx = alcGetCurrentContext();
275
276     if( priv->out_ctx == NULL ) { // FGCom standalone only
277        ALCdevice* out_dev = alcOpenDevice(NULL);
278        if (out_dev == 0) return openal_error("alcOpenDevice", alGetError());
279
280        priv->out_ctx = alcCreateContext(out_dev, 0);
281        if (priv->out_ctx == 0) return openal_error("alcCreateContext", alGetError());
282     }
283
284     alcMakeContextCurrent(priv->out_ctx);
285     if ((err = alGetError())) return openal_error("alcMakeContextCurrent", err);
286
287     priv->sample_rate = sample_rate;    
288     priv->num_buffers = 20;
289     priv->input_level = 1;
290     priv->output_level = 1;
291     priv->buffers_head = 0;
292     priv->buffers_tail = 0;
293     priv->buffers_free = priv->num_buffers;
294     priv->buffers = (ALuint*)malloc(sizeof(ALuint) * priv->num_buffers);
295
296     alGenBuffers(priv->num_buffers, priv->buffers);
297     if ((err = alGetError())) return openal_error("alGenBuffers", err);
298     
299     alGenSources(1, &priv->source);
300     if ((err = alGetError())) return openal_error("alGenSources", err);
301
302     d->initialize = openal_initialize;
303     d->destroy = openal_destroy;
304     d->select_devices = openal_select_devices;
305     d->selected_devices = openal_selected_devices;
306     d->start = openal_start;
307     d->stop = openal_stop;
308     d->output = openal_output;
309     d->input = openal_input;
310     d->input_level_get = openal_input_level_get;
311     d->input_level_set = openal_input_level_set;
312     d->output_level_get = openal_output_level_get;
313     d->output_level_set = openal_output_level_set;
314     d->mic_boost_get = openal_mic_boost_get;
315     d->mic_boost_set = openal_mic_boost_set;
316     d->play_sound = openal_play_sound;
317     d->stop_sound = openal_stop_sound;
318     d->nDevices = 1;
319     d->devices = &device;
320     
321     return 0;
322 }