Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / channels / drdynvc / audin / pulse / audin_pulse.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol client.
3  * Audio Input Redirection Virtual Channel - PulseAudio implementation
4  *
5  * Copyright 2010-2011 Vic Lee
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <pulse/pulseaudio.h>
24 #include <freerdp/types.h>
25 #include <freerdp/utils/memory.h>
26 #include <freerdp/utils/dsp.h>
27
28 #include "audin_main.h"
29
30 typedef struct _AudinPulseDevice
31 {
32         IAudinDevice iface;
33
34         char device_name[32];
35         uint32 frames_per_packet;
36         pa_threaded_mainloop* mainloop;
37         pa_context* context;
38         pa_sample_spec sample_spec;
39         pa_stream* stream;
40         int format;
41         int block_size;
42         ADPCM adpcm;
43
44         int bytes_per_frame;
45         uint8* buffer;
46         int buffer_frames;
47
48         AudinReceive receive;
49         void* user_data;
50 } AudinPulseDevice;
51
52 static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
53 {
54         pa_context_state_t state;
55         AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
56
57         state = pa_context_get_state(context);
58         switch (state)
59         {
60                 case PA_CONTEXT_READY:
61                         DEBUG_DVC("PA_CONTEXT_READY");
62                         pa_threaded_mainloop_signal (pulse->mainloop, 0);
63                         break;
64
65                 case PA_CONTEXT_FAILED:
66                 case PA_CONTEXT_TERMINATED:
67                         DEBUG_DVC("state %d", (int)state);
68                         pa_threaded_mainloop_signal (pulse->mainloop, 0);
69                         break;
70
71                 default:
72                         DEBUG_DVC("state %d", (int)state);
73                         break;
74         }
75 }
76
77 static boolean audin_pulse_connect(IAudinDevice* device)
78 {
79         pa_context_state_t state;
80         AudinPulseDevice* pulse = (AudinPulseDevice*) device;
81
82         if (!pulse->context)
83                 return false;
84
85         if (pa_context_connect(pulse->context, NULL, 0, NULL))
86         {
87                 DEBUG_WARN("pa_context_connect failed (%d)",
88                         pa_context_errno(pulse->context));
89                 return false;
90         }
91         pa_threaded_mainloop_lock(pulse->mainloop);
92         if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
93         {
94                 pa_threaded_mainloop_unlock(pulse->mainloop);
95                 DEBUG_WARN("pa_threaded_mainloop_start failed (%d)",
96                         pa_context_errno(pulse->context));
97                 return false;
98         }
99         for (;;)
100         {
101                 state = pa_context_get_state(pulse->context);
102                 if (state == PA_CONTEXT_READY)
103                         break;
104                 if (!PA_CONTEXT_IS_GOOD(state))
105                 {
106                         DEBUG_WARN("bad context state (%d)",
107                                 pa_context_errno(pulse->context));
108                         break;
109                 }
110                 pa_threaded_mainloop_wait(pulse->mainloop);
111         }
112         pa_threaded_mainloop_unlock(pulse->mainloop);
113         if (state == PA_CONTEXT_READY)
114         {
115                 DEBUG_DVC("connected");
116                 return true;
117         }
118         else
119         {
120                 pa_context_disconnect(pulse->context);
121                 return false;
122         }
123 }
124
125 static void audin_pulse_free(IAudinDevice* device)
126 {
127         AudinPulseDevice* pulse = (AudinPulseDevice*) device;
128
129         DEBUG_DVC("");
130
131         if (!pulse)
132                 return;
133         if (pulse->mainloop)
134         {
135                 pa_threaded_mainloop_stop(pulse->mainloop);
136         }
137         if (pulse->context)
138         {
139                 pa_context_disconnect(pulse->context);
140                 pa_context_unref(pulse->context);
141                 pulse->context = NULL;
142         }
143         if (pulse->mainloop)
144         {
145                 pa_threaded_mainloop_free(pulse->mainloop);
146                 pulse->mainloop = NULL;
147         }
148         xfree(pulse);
149 }
150
151 static boolean audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
152 {
153         AudinPulseDevice* pulse = (AudinPulseDevice*) device;
154
155         if (!pulse->context)
156                 return 0;
157
158         switch (format->wFormatTag)
159         {
160                 case 1: /* PCM */
161                         if (format->cbSize == 0 &&
162                                 (format->nSamplesPerSec <= PA_RATE_MAX) &&
163                                 (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
164                                 (format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
165                         {
166                                 return true;
167                         }
168                         break;
169
170                 case 6: /* A-LAW */
171                 case 7: /* U-LAW */
172                         if (format->cbSize == 0 &&
173                                 (format->nSamplesPerSec <= PA_RATE_MAX) &&
174                                 (format->wBitsPerSample == 8) &&
175                                 (format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
176                         {
177                                 return true;
178                         }
179                         break;
180
181                 case 0x11: /* IMA ADPCM */
182                         if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
183                                 (format->wBitsPerSample == 4) &&
184                                 (format->nChannels == 1 || format->nChannels == 2))
185                         {
186                                 return true;
187                         }
188                         break;
189         }
190         return false;
191 }
192
193 static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, uint32 FramesPerPacket)
194 {
195         int bs;
196         pa_sample_spec sample_spec = { 0 };
197         AudinPulseDevice* pulse = (AudinPulseDevice*) device;
198
199         if (!pulse->context)
200                 return;
201
202         if (FramesPerPacket > 0)
203         {
204                 pulse->frames_per_packet = FramesPerPacket;
205         }
206
207         sample_spec.rate = format->nSamplesPerSec;
208         sample_spec.channels = format->nChannels;
209         switch (format->wFormatTag)
210         {
211                 case 1: /* PCM */
212                         switch (format->wBitsPerSample)
213                         {
214                                 case 8:
215                                         sample_spec.format = PA_SAMPLE_U8;
216                                         break;
217                                 case 16:
218                                         sample_spec.format = PA_SAMPLE_S16LE;
219                                         break;
220                         }
221                         break;
222
223                 case 6: /* A-LAW */
224                         sample_spec.format = PA_SAMPLE_ALAW;
225                         break;
226
227                 case 7: /* U-LAW */
228                         sample_spec.format = PA_SAMPLE_ULAW;
229                         break;
230
231                 case 0x11: /* IMA ADPCM */
232                         sample_spec.format = PA_SAMPLE_S16LE;
233                         bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
234                         pulse->frames_per_packet = (pulse->frames_per_packet * format->nChannels * 2 /
235                                 bs + 1) * bs / (format->nChannels * 2);
236                         DEBUG_DVC("aligned FramesPerPacket=%d",
237                                 pulse->frames_per_packet);
238                         break;
239         }
240
241         pulse->sample_spec = sample_spec;
242         pulse->format = format->wFormatTag;
243         pulse->block_size = format->nBlockAlign;
244 }
245
246 static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
247 {
248         pa_stream_state_t state;
249         AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
250
251         state = pa_stream_get_state(stream);
252         switch (state)
253         {
254                 case PA_STREAM_READY:
255                         DEBUG_DVC("PA_STREAM_READY");
256                         pa_threaded_mainloop_signal(pulse->mainloop, 0);
257                         break;
258
259                 case PA_STREAM_FAILED:
260                 case PA_STREAM_TERMINATED:
261                         DEBUG_DVC("state %d", (int)state);
262                         pa_threaded_mainloop_signal(pulse->mainloop, 0);
263                         break;
264
265                 default:
266                         DEBUG_DVC("state %d", (int)state);
267                         break;
268         }
269 }
270
271 static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
272 {
273         int frames;
274         int cframes;
275         boolean ret;
276         const void* data;
277         const uint8* src;
278         int encoded_size;
279         uint8* encoded_data;
280         AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
281
282         pa_stream_peek(stream, &data, &length);
283         frames = length / pulse->bytes_per_frame;
284
285         DEBUG_DVC("length %d frames %d", (int) length, frames);
286
287         src = (const uint8*) data;
288         while (frames > 0)
289         {
290                 cframes = pulse->frames_per_packet - pulse->buffer_frames;
291                 if (cframes > frames)
292                         cframes = frames;
293                 memcpy(pulse->buffer + pulse->buffer_frames * pulse->bytes_per_frame,
294                         src, cframes * pulse->bytes_per_frame);
295                 pulse->buffer_frames += cframes;
296                 if (pulse->buffer_frames >= pulse->frames_per_packet)
297                 {
298                         if (pulse->format == 0x11)
299                         {
300                                 encoded_data = dsp_encode_ima_adpcm(&pulse->adpcm,
301                                         pulse->buffer, pulse->buffer_frames * pulse->bytes_per_frame,
302                                         pulse->sample_spec.channels, pulse->block_size, &encoded_size);
303                                 DEBUG_DVC("encoded %d to %d",
304                                         pulse->buffer_frames * pulse->bytes_per_frame, encoded_size);
305                         }
306                         else
307                         {
308                                 encoded_data = pulse->buffer;
309                                 encoded_size = pulse->buffer_frames * pulse->bytes_per_frame;
310                         }
311
312                         ret = pulse->receive(encoded_data, encoded_size, pulse->user_data);
313                         pulse->buffer_frames = 0;
314                         if (encoded_data != pulse->buffer)
315                                 xfree(encoded_data);
316                         if (!ret)
317                                 break;
318                 }
319                 src += cframes * pulse->bytes_per_frame;
320                 frames -= cframes;
321         }
322
323         pa_stream_drop(stream);
324 }
325
326
327 static void audin_pulse_close(IAudinDevice* device)
328 {
329         AudinPulseDevice* pulse = (AudinPulseDevice*) device;
330
331         if (!pulse->context || !pulse->stream)
332                 return;
333
334         DEBUG_DVC("");
335
336         pa_threaded_mainloop_lock(pulse->mainloop);
337         pa_stream_disconnect(pulse->stream);
338         pa_stream_unref(pulse->stream);
339         pulse->stream = NULL;
340         pa_threaded_mainloop_unlock(pulse->mainloop);
341
342         pulse->receive = NULL;
343         pulse->user_data = NULL;
344         if (pulse->buffer)
345         {
346                 xfree(pulse->buffer);
347                 pulse->buffer = NULL;
348                 pulse->buffer_frames = 0;
349         }
350 }
351
352 static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
353 {
354         pa_stream_state_t state;
355         pa_buffer_attr buffer_attr = { 0 };
356         AudinPulseDevice* pulse = (AudinPulseDevice*) device;
357
358         if (!pulse->context)
359                 return;
360         if (!pulse->sample_spec.rate || pulse->stream)
361                 return;
362
363         DEBUG_DVC("");
364
365         pulse->receive = receive;
366         pulse->user_data = user_data;
367
368         pa_threaded_mainloop_lock(pulse->mainloop);
369         pulse->stream = pa_stream_new(pulse->context, "freerdp_audin",
370                 &pulse->sample_spec, NULL);
371         if (!pulse->stream)
372         {
373                 pa_threaded_mainloop_unlock(pulse->mainloop);
374                 DEBUG_DVC("pa_stream_new failed (%d)",
375                         pa_context_errno(pulse->context));
376                 return;
377         }
378         pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
379         pa_stream_set_state_callback(pulse->stream,
380                 audin_pulse_stream_state_callback, pulse);
381         pa_stream_set_read_callback(pulse->stream,
382                 audin_pulse_stream_request_callback, pulse);
383         buffer_attr.maxlength = (uint32_t) -1;
384         buffer_attr.tlength = (uint32_t) -1;
385         buffer_attr.prebuf = (uint32_t) -1;
386         buffer_attr.minreq = (uint32_t) -1;
387         /* 500ms latency */
388         buffer_attr.fragsize = pa_usec_to_bytes(500000, &pulse->sample_spec);
389         if (pa_stream_connect_record(pulse->stream,
390                 pulse->device_name[0] ? pulse->device_name : NULL,
391                 &buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0)
392         {
393                 pa_threaded_mainloop_unlock(pulse->mainloop);
394                 DEBUG_WARN("pa_stream_connect_playback failed (%d)",
395                         pa_context_errno(pulse->context));
396                 return;
397         }
398
399         for (;;)
400         {
401                 state = pa_stream_get_state(pulse->stream);
402                 if (state == PA_STREAM_READY)
403                         break;
404                 if (!PA_STREAM_IS_GOOD(state))
405                 {
406                         DEBUG_WARN("bad stream state (%d)",
407                                 pa_context_errno(pulse->context));
408                         break;
409                 }
410                 pa_threaded_mainloop_wait(pulse->mainloop);
411         }
412         pa_threaded_mainloop_unlock(pulse->mainloop);
413         if (state == PA_STREAM_READY)
414         {
415                 memset(&pulse->adpcm, 0, sizeof(ADPCM));
416                 pulse->buffer = xzalloc(pulse->bytes_per_frame * pulse->frames_per_packet);
417                 pulse->buffer_frames = 0;
418                 DEBUG_DVC("connected");
419         }
420         else
421         {
422                 audin_pulse_close(device);
423         }
424 }
425
426 int FreeRDPAudinDeviceEntry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
427 {
428         AudinPulseDevice* pulse;
429         RDP_PLUGIN_DATA * data;
430
431         pulse = xnew(AudinPulseDevice);
432
433         pulse->iface.Open = audin_pulse_open;
434         pulse->iface.FormatSupported = audin_pulse_format_supported;
435         pulse->iface.SetFormat = audin_pulse_set_format;
436         pulse->iface.Close = audin_pulse_close;
437         pulse->iface.Free = audin_pulse_free;
438
439         data = pEntryPoints->plugin_data;
440         if (data && data->data[0] && strcmp(data->data[0], "audin") == 0 &&
441                 data->data[1] && strcmp(data->data[1], "pulse") == 0)
442         {
443                 strncpy(pulse->device_name, (char*)data->data[2], sizeof(pulse->device_name));
444         }
445
446         pulse->mainloop = pa_threaded_mainloop_new();
447         if (!pulse->mainloop)
448         {
449                 DEBUG_WARN("pa_threaded_mainloop_new failed");
450                 audin_pulse_free((IAudinDevice*) pulse);
451                 return 1;
452         }
453         pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
454         if (!pulse->context)
455         {
456                 DEBUG_WARN("pa_context_new failed");
457                 audin_pulse_free((IAudinDevice*) pulse);
458                 return 1;
459         }
460         pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
461         if (!audin_pulse_connect((IAudinDevice*) pulse))
462         {
463                 audin_pulse_free((IAudinDevice*) pulse);
464                 return 1;
465         }
466
467         pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse);
468
469         return 0;
470 }
471