-fix build system issues
[oweals/gnunet.git] / src / conversation / gnunet-helper-audio-record.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file conversation/gnunet-helper-audio-record.c
22  * @brief program to record audio data from the microphone
23  * @author Siomon Dieterle
24  * @author Andreas Fuchs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_protocols.h"
30 #include "conversation.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_core_service.h"
33
34 #include <pulse/simple.h>
35 #include <pulse/error.h>
36 #include <pulse/rtclock.h>
37
38 #include <pulse/pulseaudio.h>
39 #include <opus/opus.h>
40 #include <opus/opus_types.h>
41
42 #define SAMPLING_RATE 48000
43
44
45 /**
46  * Specification for recording. May change in the future to spec negotiation.
47  */
48 static pa_sample_spec sample_spec = {
49   .format = PA_SAMPLE_FLOAT32LE,
50   .rate = SAMPLING_RATE,
51   .channels = 1
52 };
53
54 /**
55  * Pulseaudio mainloop api
56  */
57 static pa_mainloop_api *mainloop_api;
58
59 /**
60  * Pulseaudio mainloop
61  */
62 static pa_mainloop *m;
63
64 /**
65  * Pulseaudio context
66  */
67 static pa_context *context;
68
69 /**
70  * Pulseaudio recording stream
71  */
72 static pa_stream *stream_in;
73
74 /**
75  * Pulseaudio io events
76  */
77 static pa_io_event *stdio_event;
78
79 /**
80  * OPUS encoder
81  */
82 static OpusEncoder *enc;
83
84 /**
85  *
86  */
87 static unsigned char *opus_data;
88
89 /**
90  * PCM data buffer for one OPUS frame
91  */
92 static float *pcm_buffer;
93
94 /**
95  * Length of the pcm data needed for one OPUS frame
96  */
97 static int pcm_length;
98
99 /**
100  * Number of samples for one frame
101  */
102 static int frame_size;
103
104 /**
105 * Maximum length of opus payload
106 */
107 static int max_payload_bytes = 1500;
108
109 /**
110  * Audio buffer
111  */
112 static char *transmit_buffer;
113
114 /**
115  * Length of audio buffer
116  */
117 static size_t transmit_buffer_length;
118
119 /**
120  * Read index for transmit buffer
121  */
122 static size_t transmit_buffer_index;
123
124 /**
125  * Audio message skeleton
126  */
127 static struct AudioMessage *audio_message;
128
129
130 /**
131  * Pulseaudio shutdown task
132  */
133 static void
134 quit (int ret)
135 {
136   mainloop_api->quit (mainloop_api, ret);
137   exit (ret);
138 }
139
140
141 /**
142  * Creates OPUS packets from PCM data
143  */
144 static void
145 packetizer ()
146 {
147   static unsigned long long toff;
148   char *nbuf;
149   size_t new_size;
150   const char *ptr;
151   size_t off;
152   ssize_t ret;
153   int len; // FIXME: int?
154   size_t msg_size;
155
156   while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
157   {
158     memcpy (pcm_buffer,
159             &transmit_buffer[transmit_buffer_index],
160             pcm_length);
161     transmit_buffer_index += pcm_length;
162     len =
163       opus_encode_float (enc, pcm_buffer, frame_size, opus_data,
164                          max_payload_bytes);
165     if (len > UINT16_MAX - sizeof (struct AudioMessage))
166     {
167       GNUNET_break (0);
168       len = UINT16_MAX - sizeof (struct AudioMessage);
169     }
170     msg_size = sizeof (struct AudioMessage) + len;
171     audio_message->header.size = htons ((uint16_t) msg_size);
172     memcpy (&audio_message[1], opus_data, len);
173
174     toff += msg_size;
175     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
176                 "Sending %u bytes of audio data (total: %llu)\n",
177                 (unsigned int) msg_size,
178                 toff);
179     ptr = (const char *) audio_message;
180     off = 0;
181     while (off < msg_size)
182     {
183       ret = write (1, &ptr[off], msg_size - off);
184       if (0 >= ret)
185       {
186         if (-1 == ret)
187           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write");
188         quit (2);
189       }
190       off += ret;
191     }
192   }
193
194   new_size = transmit_buffer_length - transmit_buffer_index;
195   if (0 != new_size)
196   {
197     nbuf = pa_xmalloc (new_size);
198     memmove (nbuf,
199              &transmit_buffer[transmit_buffer_index],
200              new_size);
201     pa_xfree (transmit_buffer);
202     transmit_buffer = nbuf;
203   }
204   else
205   {
206     pa_xfree (transmit_buffer);
207     transmit_buffer = NULL;
208   }
209   transmit_buffer_index = 0;
210   transmit_buffer_length = new_size;
211 }
212
213
214 /**
215  * Pulseaudio callback when new data is available.
216  */
217 static void
218 stream_read_callback (pa_stream * s,
219                       size_t length,
220                       void *userdata)
221 {
222   const void *data;
223
224   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
225               "Got %u/%u bytes of PCM data\n",
226               length,
227               pcm_length);
228
229   GNUNET_assert (NULL != s);
230   GNUNET_assert (length > 0);
231   if (stdio_event)
232     mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
233
234   if (pa_stream_peek (s, (const void **) &data, &length) < 0)
235   {
236     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
237                 _("pa_stream_peek() failed: %s\n"),
238                 pa_strerror (pa_context_errno (context)));
239     quit (1);
240     return;
241   }
242   GNUNET_assert (NULL != data);
243   GNUNET_assert (length > 0);
244   if (NULL != transmit_buffer)
245   {
246     transmit_buffer = pa_xrealloc (transmit_buffer,
247                                    transmit_buffer_length + length);
248     memcpy (&transmit_buffer[transmit_buffer_length],
249             data,
250             length);
251     transmit_buffer_length += length;
252   }
253   else
254   {
255     transmit_buffer = pa_xmalloc (length);
256     memcpy (transmit_buffer, data, length);
257     transmit_buffer_length = length;
258     transmit_buffer_index = 0;
259   }
260   pa_stream_drop (s);
261   packetizer ();
262 }
263
264
265 /**
266  * Exit callback for SIGTERM and SIGINT
267  */
268 static void
269 exit_signal_callback (pa_mainloop_api * m,
270                       pa_signal_event * e,
271                       int sig,
272                       void *userdata)
273 {
274   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
275               _("Got signal, exiting.\n"));
276   quit (1);
277 }
278
279
280 /**
281  * Pulseaudio stream state callback
282  */
283 static void
284 stream_state_callback (pa_stream * s, void *userdata)
285 {
286   GNUNET_assert (NULL != s);
287
288   switch (pa_stream_get_state (s))
289   {
290   case PA_STREAM_CREATING:
291   case PA_STREAM_TERMINATED:
292     break;
293   case PA_STREAM_READY:
294     {
295       const pa_buffer_attr *a;
296       char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
297       char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
298
299       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
300                   _("Stream successfully created.\n"));
301
302       if (!(a = pa_stream_get_buffer_attr (s)))
303       {
304         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305                     _("pa_stream_get_buffer_attr() failed: %s\n"),
306                     pa_strerror (pa_context_errno
307                                  (pa_stream_get_context (s))));
308
309       }
310       else
311       {
312         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
313                     _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
314                     a->maxlength, a->fragsize);
315       }
316       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
317                   _("Using sample spec '%s', channel map '%s'.\n"),
318                   pa_sample_spec_snprint (sst, sizeof (sst),
319                                           pa_stream_get_sample_spec (s)),
320                   pa_channel_map_snprint (cmt, sizeof (cmt),
321                                           pa_stream_get_channel_map (s)));
322
323       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
324                   _("Connected to device %s (%u, %ssuspended).\n"),
325                   pa_stream_get_device_name (s),
326                   pa_stream_get_device_index (s),
327                   pa_stream_is_suspended (s) ? "" : "not ");
328     }
329     break;
330   case PA_STREAM_FAILED:
331   default:
332     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333                 _("Stream error: %s\n"),
334                 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
335     quit (1);
336   }
337 }
338
339
340 /**
341  * Pulseaudio context state callback
342  */
343 static void
344 context_state_callback (pa_context * c,
345                         void *userdata)
346 {
347   GNUNET_assert (c);
348
349   switch (pa_context_get_state (c))
350   {
351   case PA_CONTEXT_CONNECTING:
352   case PA_CONTEXT_AUTHORIZING:
353   case PA_CONTEXT_SETTING_NAME:
354     break;
355   case PA_CONTEXT_READY:
356   {
357     int r;
358     pa_buffer_attr na;
359
360     GNUNET_assert (!stream_in);
361     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
362                 _("Connection established.\n"));
363     if (! (stream_in =
364            pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
365     {
366       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367                   _("pa_stream_new() failed: %s\n"),
368                   pa_strerror (pa_context_errno (c)));
369       goto fail;
370     }
371     pa_stream_set_state_callback (stream_in, &stream_state_callback, NULL);
372     pa_stream_set_read_callback (stream_in, &stream_read_callback, NULL);
373     memset (&na, 0, sizeof (na));
374     na.maxlength = UINT32_MAX;
375     na.fragsize = pcm_length;
376     if ((r = pa_stream_connect_record (stream_in, NULL, &na,
377                                        PA_STREAM_ADJUST_LATENCY)) < 0)
378     {
379       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380                   _("pa_stream_connect_record() failed: %s\n"),
381                   pa_strerror (pa_context_errno (c)));
382       goto fail;
383     }
384
385     break;
386   }
387   case PA_CONTEXT_TERMINATED:
388     quit (0);
389     break;
390   case PA_CONTEXT_FAILED:
391   default:
392     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393                 _("Connection failure: %s\n"),
394                 pa_strerror (pa_context_errno (c)));
395     goto fail;
396   }
397   return;
398
399 fail:
400   quit (1);
401 }
402
403
404 /**
405  * Pulsaudio init
406  */
407 static void
408 pa_init ()
409 {
410   int r;
411   int i;
412
413   if (!pa_sample_spec_valid (&sample_spec))
414   {
415     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
416                 _("Wrong Spec\n"));
417   }
418   /* set up main record loop */
419   if (!(m = pa_mainloop_new ()))
420   {
421     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
422                 _("pa_mainloop_new() failed.\n"));
423   }
424   mainloop_api = pa_mainloop_get_api (m);
425
426   /* listen to signals */
427   r = pa_signal_init (mainloop_api);
428   GNUNET_assert (r == 0);
429   pa_signal_new (SIGINT, &exit_signal_callback, NULL);
430   pa_signal_new (SIGTERM, &exit_signal_callback, NULL);
431
432   /* connect to the main pulseaudio context */
433
434   if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
435   {
436     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
437                 _("pa_context_new() failed.\n"));
438   }
439   pa_context_set_state_callback (context, &context_state_callback, NULL);
440   if (pa_context_connect (context, NULL, 0, NULL) < 0)
441   {
442     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
443                 _("pa_context_connect() failed: %s\n"),
444                 pa_strerror (pa_context_errno (context)));
445   }
446   if (pa_mainloop_run (m, &i) < 0)
447   {
448     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
449                 _("pa_mainloop_run() failed.\n"));
450   }
451 }
452
453
454 /**
455  * OPUS init
456  */
457 static void
458 opus_init ()
459 {
460   int channels = 1;
461   int err;
462
463   frame_size = SAMPLING_RATE / 50;
464   pcm_length = frame_size * channels * sizeof (float);
465   pcm_buffer = pa_xmalloc (pcm_length);
466   opus_data = GNUNET_malloc (max_payload_bytes);
467   enc = opus_encoder_create (SAMPLING_RATE,
468                              channels,
469                              OPUS_APPLICATION_VOIP,
470                              &err);
471   opus_encoder_ctl (enc,
472                     OPUS_SET_PACKET_LOSS_PERC(1));
473   opus_encoder_ctl (enc,
474                     OPUS_SET_COMPLEXITY(10));
475   opus_encoder_ctl (enc,
476                     OPUS_SET_INBAND_FEC(1));
477   opus_encoder_ctl (enc,
478                     OPUS_SET_SIGNAL (OPUS_SIGNAL_VOICE));
479 }
480
481
482 /**
483  * The main function for the record helper.
484  *
485  * @param argc number of arguments from the command line
486  * @param argv command line arguments
487  * @return 0 ok, 1 on error
488  */
489 int
490 main (int argc, char *argv[])
491 {
492   GNUNET_assert (GNUNET_OK ==
493                  GNUNET_log_setup ("gnunet-helper-audio-record",
494                                    "WARNING",
495                                    NULL));
496   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497               "Audio source starts\n");
498   audio_message = GNUNET_malloc (UINT16_MAX);
499   audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
500   opus_init ();
501   pa_init ();
502   return 0;
503 }