]> git.mxchange.org Git - flightgear.git/blob - utils/iaxclient/lib/audio_openal.c
fbc9c4b02b201cce7a1294ded327f0e0b3f1d089
[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     return 0;
252 }
253
254 int openal_initialize(struct iaxc_audio_driver *d, int sample_rate)
255 {
256     struct openal_priv_data* priv = malloc(sizeof(struct openal_priv_data));
257     int err = alGetError();
258     d->priv = priv;
259    
260     priv->out_ctx = alcGetCurrentContext();
261
262     if( priv->out_ctx == NULL ) { // FGCom standalone only
263        ALCdevice* out_dev = alcOpenDevice(0);
264        if (out_dev == 0) return openal_error("alcOpenDevice", alGetError());
265
266        priv->out_ctx = alcCreateContext(out_dev, 0);
267        if (priv->out_ctx == 0) return openal_error("alcCreateContext", alGetError());
268     }
269
270     alcMakeContextCurrent(priv->out_ctx);
271     if ((err = alGetError())) return openal_error("alcMakeContextCurrent", err);
272
273     priv->sample_rate = sample_rate;    
274     priv->num_buffers = 20;
275     priv->input_level = 1;
276     priv->output_level = 1;
277     priv->buffers_head = 0;
278     priv->buffers_tail = 0;
279     priv->buffers_free = priv->num_buffers;
280     priv->buffers = (ALuint*)malloc(sizeof(ALuint) * priv->num_buffers);
281
282     alGenBuffers(priv->num_buffers, priv->buffers);
283     if ((err = alGetError())) return openal_error("alGenBuffers", err);
284     
285     alGenSources(1, &priv->source);
286     if ((err = alGetError())) return openal_error("alGenSources", err);
287
288     priv->in_dev = alcCaptureOpenDevice(0, 8000, AL_FORMAT_MONO16, 800);
289     if (!priv->in_dev) return openal_error("alcCaptureOpenDevice", 0);
290
291     alcCaptureStart(priv->in_dev);
292     if ((err = alGetError())) return openal_error("alcCaptureStart", err);
293
294     d->initialize = openal_initialize;
295     d->destroy = openal_destroy;
296     d->select_devices = openal_select_devices;
297     d->selected_devices = openal_selected_devices;
298     d->start = openal_start;
299     d->stop = openal_stop;
300     d->output = openal_output;
301     d->input = openal_input;
302     d->input_level_get = openal_input_level_get;
303     d->input_level_set = openal_input_level_set;
304     d->output_level_get = openal_output_level_get;
305     d->output_level_set = openal_output_level_set;
306     d->mic_boost_get = openal_mic_boost_get;
307     d->mic_boost_set = openal_mic_boost_set;
308     d->play_sound = openal_play_sound;
309     d->stop_sound = openal_stop_sound;
310     d->nDevices = 1;
311     d->devices = &device;
312     
313     return 0;
314 }