Guest User

Untitled

a guest
Nov 2nd, 2020
106
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 26.03 KB | None | 0 0
  1. /*
  2. playhrt.c                Copyright frankl 2013-2016
  3.                          Copyright Andree Buschmann 2020
  4.  
  5. This file is part of frankl's stereo utilities and was reworked by Andree Buschmann.
  6. See the file License.txt of the distribution and http://www.gnu.org/licenses/gpl.txt
  7. for license details.
  8. */
  9.  
  10. #define _GNU_SOURCE
  11. #include "version.h"
  12. #include "net.h"
  13. #include <sys/types.h>
  14. #include <sys/socket.h>
  15. #include <netdb.h>
  16. #include <getopt.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <unistd.h>
  20. #include <string.h>
  21. #include <time.h>
  22. #include <alsa/asoundlib.h>
  23. #include "cprefresh.h"
  24.  
  25. /* help page */
  26. /* vim hint to remove resp. add quotes:
  27.       s/^"\(.*\)\\n"$/\1/
  28.       s/.*$/"\0\\n"/
  29. */
  30. void usage( ) {
  31.     fprintf(stderr, "playhrt (version %s of frankl's stereo utilities", VERSION);
  32. #ifdef ALSANC
  33.     fprintf(stderr, ", with alsa-lib patch");
  34. #endif
  35.     fprintf(stderr, ", reworked by Andree Buschmann");
  36.     fprintf(stderr, ", with PI control to compensate clock deviation");
  37.     fprintf(stderr, ")\n\nUSAGE:\n");
  38.     fprintf(stderr,
  39. "\n"
  40. "  playhrt [options] \n"
  41. "\n"
  42. "  This program reads raw(!) stereo audio data from stdin, a file or the \n"
  43. "  network and plays it on a local (ALSA) sound device. \n"
  44. "\n"
  45. "  The program repeats in a given number of loops per second: reading\n"
  46. "  a chunk of input data, preparing data for the audio driver, then it\n"
  47. "  sleeps until a specific instant of time and after wakeup it hands data\n"
  48. "  to the audio driver. In contrast to other player programs this is done\n"
  49. "  with a very precise timing such that no buffers underrun or overrun and\n"
  50. "  no reading or writing of data is blocking. Furthermore, the data is\n"
  51. "  refreshed in RAM directly before copying it to the audio driver.\n"
  52. "\n"
  53. "  The Linux kernel needs the highres-timer functionality enabled (on most\n"
  54. "  systems this is the case).\n"
  55. "\n"
  56. "  This reworked version only writes input data directly to the memory\n"
  57. "  of the audio driver (mmap mode).\n"
  58. "\n"
  59. "  USAGE HINTS\n"
  60. "  \n"
  61. "  It is recommended to give this program a high priority and not to run\n"
  62. "  too many other things on the same computer during playback. A high\n"
  63. "  priority can be specified with the 'chrt' command:\n"
  64. "\n"
  65. "  'chrt -f 70 playhrt .....'\n"
  66. "\n"
  67. "  (Depending on the configuration of your computer you may need root\n"
  68. "  privileges for this, in that case use 'sudo chrt -f 99 playhrt ....' \n"
  69. "  or give 'chrt' setuid permissions.)\n"
  70. "\n"
  71. "  While running this program the computer should run as few other things\n"
  72. "  as possible. In particular we recommend to generate the input data\n"
  73. "  on a different computer and to send them via the network to 'playhrt'\n"
  74. "  using the program 'bufhrt' which is also contained in this package. \n"
  75. "  \n"
  76. "  OPTIONS\n"
  77. "\n"
  78. "  --host=hostname, -H hostname\n"
  79. "      the host from which to receive the data , given by name or\n"
  80. "      ip-address.\n"
  81. "\n"
  82. "  --port=portnumber, -P portnumber\n"
  83. "      the port number on the remote host from which to receive data.\n"
  84. "\n"
  85. "  --stdin, -S\n"
  86. "      read data from stdin (instead of --host and --port).\n"
  87. "\n"
  88. "  --device=alsaname, -d alsaname\n"
  89. "      the name of the sound device. A typical name is 'hw:0,0', maybe\n"
  90. "      use 'aplay -l' to find out the correct numbers. It is recommended\n"
  91. "      to use the hardware devices 'hw:...' if possible.\n"
  92. "\n"
  93. "  --rate=intval, -r intval\n"
  94. "      the sample rate of the audio data. Default is 44100 as on CDs.\n"
  95. "\n"
  96. "  --format=formatstring, -f formatstring\n"
  97. "      the format of the samples in the audio data. Currently recognised are\n"
  98. "      'S16_LE'  (signed integer 16 bits, the sample format on CDs),\n"
  99. "      'S24_LE'  (signed integer 24 bits, packed to 4 bytes, used by many DACs)\n"
  100. "      'S24_3LE' (signed integer 24 bits, using 3 bytes per sample), \n"
  101. "      'S32_LE'  (signed integer 32 bits, true 32 bit samples).\n"
  102. "      Default is 'S16_LE'.\n"
  103. "\n"
  104. "  --channels=intval, -c intval\n"
  105. "      the number of channels in the (interleaved) audio stream. The \n"
  106. "      default is 2 (stereo).\n"
  107. "\n"
  108. "  --loops-per-second=intval, -n intval\n"
  109. "      the number of loops per second in which 'playhrt' reads some\n"
  110. "      data from the network into a buffer, sleeps until a precise\n"
  111. "      moment and then writes a chunk of data to the sound device. \n"
  112. "      Typical values would be 1000 or 2000. Default is 1000.\n"
  113. "\n"
  114. "  --non-blocking-write, -N\n"
  115. "      write data to sound device in a non-blocking fashion. This can\n"
  116. "      improve sound quality, but the timing must be very precise.\n"
  117. "\n"
  118. "  --hw-buffer-size=intval, -B intval\n"
  119. "      the buffer size (number of frames) used on the sound device.\n"
  120. "      It may be worth to experiment a bit with this, in particular\n"
  121. "      to try some smaller values. When 'playhrt' is called with\n"
  122. "      '--verbose' it will report on the range allowed by the device.\n"
  123. "      Default is 4096.\n"
  124. " \n"
  125. "  --in-net-buffer-size=intval, -I intval\n"
  126. "      when reading from the network this allows to set the buffer\n"
  127. "      size for the incoming data. This is for finetuning only, normally\n"
  128. "      the operating system chooses sizes to guarantee constant data\n"
  129. "      flow. The actual fill of the buffer during playback can be checked\n"
  130. "      with 'netstat -tpn', it can be up to twice as big as the given\n"
  131. "      intval.\n"
  132. "\n"
  133. "  --sleep=intval, -D intval\n"
  134. "      causes 'playhrt' to sleep for intval microseconds (1/1000000 sec)\n"
  135. "      after opening the sound device and before starting playback.\n"
  136. "      This may sometimes be useful to give other programs time to fill\n"
  137. "      the input buffer of 'playhrt'. Default is no sleep, in this case\n"
  138. "      'playhrt' waits for the input pipeline to provide data.\n"
  139. "\n"
  140. "  --verbose, -v\n"
  141. "      print some information during startup and operation.\n"
  142. "      This option can be given twice for more output about the auto-\n"
  143. "      matic speed control and availability of the audio buffer.\n"
  144. "\n"
  145. "  --version, -V\n"
  146. "      print information about the version of the program and abort.\n"
  147. "\n"
  148. "  --help, -h\n"
  149. "      print this help page and abort.\n"
  150. "\n"
  151. "  EXAMPLES\n"
  152. "\n"
  153. "  We read from myserver on port 5123 stereo data in 32-bit integer\n"
  154. "  format with a sample rate of 192000. We want to run 1000 loops per \n"
  155. "  second (this is in particular a good choice for USB devices), our sound\n"
  156. "  device is 'hw:0,0' and we want to write non-blocking to the device:\n"
  157. "\n"
  158. "  playhrt --host=myserver --port=5123 \\\n"
  159. "      --loops-per-second=1000 \\\n"
  160. "      --device=hw:0,0 --sample-rate=192000 --sample-format=S32_LE \\\n"
  161. "      --non-blocking --verbose \n"
  162. "\n"
  163. "  To play a local CD quality flac file 'music.flac' you need another \n"
  164. "  program to unpack the raw audio data. In this example we use 'sox':\n"
  165. "\n"
  166. "  sox musik.flac -t raw - | playhrt --stdin \\\n"
  167. "          --loops-per-second=1000 --device=hw:0,0 --sample-rate=44100 \\\n"
  168. "          --sample-format=S16_LE --non-blocking --verbose \n"
  169. "\n"
  170. "  ADJUSTING SPEED\n"
  171. "\n"
  172. "  This version of 'playhrt' is automatically adjusting the speed of\n"
  173. "  writing the data to the hardware buffer. This is done via measuring\n"
  174. "  the space left in the hardware buffer and tuning the interval time\n"
  175. "  until the next data write occurs. The targeted value is hw-buffer/2.\n"
  176. "  \n"
  177. "  The automatic adjustment is implemented as PI-control which allows\n"
  178. "  'playhrt' to adjust to fixed and variable deviation of the local clock\n"
  179. "  against the consuming clock (typically a DAC).\n"
  180. "\n"
  181. );
  182. }
  183.  
  184. const char *formattime(char* text, struct timespec mtime)
  185. {
  186.     int hrs  = mtime.tv_sec/3600;
  187.     int min  = (mtime.tv_sec-3600*hrs)/60;
  188.     int sec  = mtime.tv_sec%60;
  189.     int msec = mtime.tv_nsec/1000000;
  190.     int usec = (mtime.tv_nsec-1000000*msec)/1000;
  191.     int nsec = mtime.tv_nsec%1000;
  192.  
  193.     /* formatting into <h:m:s.ms us ns> for better readability */
  194.     sprintf(text, "%02d:%02d:%02d.%03d %03d %03d", hrs, min, sec, msec, usec, nsec);
  195.     return text;
  196. }
  197.  
  198. const char *hms(char* text, struct timespec mtime)
  199. {
  200.     int hrs  = mtime.tv_sec/3600;
  201.     int min  = (mtime.tv_sec-3600*hrs)/60;
  202.     int sec  = mtime.tv_sec%60;
  203.  
  204.     /* formatting into <h:m:s> for better readability */
  205.     sprintf(text, "%02d:%02d:%02d", hrs, min, sec);
  206.     return text;
  207. }
  208.  
  209. int main(int argc, char *argv[])
  210. {
  211.     int sfd, readbytes, readthis, verbose, nrchannels, startcount, sumavg, innetbufsize;
  212.     long loopspersec, sleep, nsec, extransec, count, avgav;
  213.     long long bytecount;
  214.     void *iptr;
  215.     struct timespec mtime;
  216.     struct timespec mtimecheck;
  217.     snd_pcm_t *pcm_handle;
  218.     snd_pcm_hw_params_t *hwparams;
  219.     snd_pcm_sw_params_t *swparams;
  220.     snd_pcm_format_t format;
  221.     char *host, *port, *pcm_name;
  222.     int optc, nonblock, rate, bytespersample, bytesperframe;
  223.     snd_pcm_uframes_t hwbufsize, offset, frames;
  224.     snd_pcm_sframes_t avail;
  225.     const snd_pcm_channel_area_t *areas;
  226.     char text[32];
  227.  
  228.     /* define variables and default for PI control */
  229.     #define LOOPS_AVG 16        /* amount of averaged buffer measurement */
  230.     #define LOOPS_CADENCE 4000  /* measure each LOOPS_CADENCE loops */
  231.     double bufavg = 0;
  232.     double buferr = 0;
  233.     double buferr_i = 0;
  234.     double Ta = 0.0;    /* will be calculated later */
  235.     double Kp = 1.0;    /* value based on tests */
  236.     double Ki = 0.05;   /* value based on tests */
  237.  
  238.     /**********************************************************************/
  239.     /* read and set parameters                                            */
  240.     /**********************************************************************/
  241.  
  242.     /* no parameter given */
  243.     if (argc == 1) {
  244.         usage();
  245.         exit(0);
  246.     }
  247.  
  248.     /* read command line options */
  249.     static struct option longoptions[] = {
  250.         {"host",               required_argument, 0, 'H' },
  251.         {"port",               required_argument, 0, 'P' },
  252.         {"loops-per-second",   required_argument, 0, 'n' },
  253.         {"rate",               required_argument, 0, 'r' },
  254.         {"format",             required_argument, 0, 'f' },
  255.         {"channels",           required_argument, 0, 'c' },
  256.         {"hw-buffer-size",     required_argument, 0, 'B' },
  257.         {"device",             required_argument, 0, 'd' },
  258.         {"sleep",              required_argument, 0, 'D' },
  259.         {"in-net-buffer-size", required_argument, 0, 'I' },
  260.         {"mmap",               no_argument,       0, 'M' },
  261.         {"stdin",              no_argument,       0, 'S' },
  262.         {"non-blocking-write", no_argument,       0, 'N' },
  263.         {"verbose",            no_argument,       0, 'v' },
  264.         {"version",            no_argument,       0, 'V' },
  265.         {"help",               no_argument,       0, 'h' },
  266.         {0,                    0,                 0,  0  }
  267.     };
  268.  
  269.     /* set defaults */
  270.     host = NULL;
  271.     port = NULL;
  272.     loopspersec = 1000;
  273.     rate = 44100;
  274.     format = SND_PCM_FORMAT_S16_LE;
  275.     bytespersample = 2;
  276.     hwbufsize = 4096;
  277.     pcm_name = NULL;
  278.     sfd = -1;
  279.     nrchannels = 2;
  280.     extransec = 0;
  281.     sleep = 0;
  282.     nonblock = 0;
  283.     innetbufsize = 0;
  284.     verbose = 0;
  285.     sumavg = 0;
  286.     avgav = 0;
  287.     buferr_i = 0;
  288.     bytecount = 0;
  289.  
  290.     /* read parameters */
  291.     while ((optc = getopt_long(argc, argv, "H:P:n:r:f:c:B:d:D:I:MSNvVh", longoptions, &optind)) != -1) {
  292.         switch (optc) {
  293.         case 'H':
  294.             host = optarg;
  295.             break;
  296.         case 'P':
  297.             port = optarg;
  298.             break;
  299.         case 'S':
  300.             sfd = 0;
  301.             break;
  302.         case 'n':
  303.             loopspersec = atoi(optarg);
  304.             break;
  305.         case 'r':
  306.             rate = atoi(optarg);
  307.             break;
  308.         case 'f':
  309.             if        (strcmp(optarg, "S16_LE" )==0) {
  310.                 format = SND_PCM_FORMAT_S16_LE;
  311.                 bytespersample = 2;
  312.             } else if (strcmp(optarg, "S24_LE" )==0) {
  313.                 format = SND_PCM_FORMAT_S24_LE;
  314.                 bytespersample = 4;
  315.             } else if (strcmp(optarg, "S24_3LE")==0) {
  316.                 format = SND_PCM_FORMAT_S24_3LE;
  317.                 bytespersample = 3;
  318.             } else if (strcmp(optarg, "S32_LE" )==0) {
  319.                 format = SND_PCM_FORMAT_S32_LE;
  320.                 bytespersample = 4;
  321.             } else {
  322.                 fprintf(stderr, "playhrt: Error. Sample format %s not recognized.\n", optarg);
  323.                 exit(1);
  324.             }
  325.             break;
  326.         case 'c':
  327.             nrchannels = atoi(optarg);
  328.             break;
  329.         case 'B':
  330.             hwbufsize = atoi(optarg);
  331.             break;
  332.         case 'M':
  333.             /* ignore, just kept for compatibility */
  334.             break;
  335.         case 'd':
  336.             pcm_name = optarg;
  337.             break;
  338.         case 'D':
  339.             sleep = atoi(optarg);
  340.             break;
  341.         case 'I':
  342.             innetbufsize = atoi(optarg);
  343.             if (innetbufsize != 0 && innetbufsize < 128)
  344.                 innetbufsize = 128;
  345.             break;
  346.         case 'N':
  347.             nonblock = 1;
  348.             break;
  349.         case 'v':
  350.             verbose += 1;
  351.             break;
  352.         case 'V':
  353.             fprintf(stderr, "playhrt (version %s of frankl's stereo utilities", VERSION);
  354. #ifdef ALSANC
  355.             fprintf(stderr, ", with alsa-lib patch");
  356. #endif
  357.             fprintf(stderr, ", reworked by Andree Buschmann");
  358.             fprintf(stderr, ", with PI control to compensate clock deviation)\n");
  359.             exit(2);
  360.         default:
  361.             usage();
  362.             exit(3);
  363.         }
  364.     }
  365.  
  366.     /**********************************************************************/
  367.     /* calculate and check values from given parameters                   */
  368.     /**********************************************************************/
  369.  
  370.     /* calculate some values from the parameters */
  371.     bytesperframe = bytespersample*nrchannels;  /* bytes per frame */
  372.     frames = rate/loopspersec;                  /* frames per loop */
  373.     nsec = (int) (1000000000/loopspersec);      /* compute nanoseconds per loop (wrt local clock) */
  374.     Ta = (1.0*LOOPS_CADENCE)/loopspersec;       /* delta T seconds */
  375.     Ta = (Ki*Ta>0.2) ? 0.2/Ki : Ta;             /* limit Ta to avoid oscallation */
  376.  
  377.     /* set hwbuffer to a multiple of frames per loop (needed for mmap!) */
  378.     hwbufsize = hwbufsize - (hwbufsize % frames);
  379.  
  380.     /* amount of loops to fill half buffer */
  381.     startcount = hwbufsize/(2*frames);
  382.        
  383.     /* check some arguments and set some parameters */
  384.     if ((host == NULL || port == NULL) && sfd < 0) {
  385.         fprintf(stderr, "playhrt: Error. Must specify --host and --port or --stdin.\n");
  386.         exit(4);
  387.     }
  388.  
  389.     /**********************************************************************/
  390.     /* show playhrt configuration                                         */
  391.     /**********************************************************************/
  392.  
  393.     /* show configuration */
  394.     if (verbose) {
  395.         fprintf(stderr, "playhrt: Version %s\n", VERSION);
  396.         fprintf(stderr, "playhrt: Using mmap access.\n");
  397.         fprintf(stderr, "playhrt: Step size is %ld nsec.\n", nsec);
  398.         fprintf(stderr, "playhrt: %d channels with %d bytes per sample at %d Hz\n", nrchannels, bytespersample, rate);
  399.     }
  400.  
  401.     /**********************************************************************/
  402.     /* setup network connection                                           */
  403.     /**********************************************************************/
  404.  
  405.     /* setup network connection */
  406.     if (host != NULL && port != NULL) {
  407.         sfd = fd_net(host, port);
  408.         if (innetbufsize != 0) {
  409.             if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, (void*)&innetbufsize, sizeof(int)) < 0) {
  410.                 fprintf(stderr, "playhrt: Error setting buffer size for network socket to %d.\n", innetbufsize);
  411.                 exit(5);
  412.             }
  413.         }
  414.     }
  415.  
  416.     /**********************************************************************/
  417.     /* setup sound device                                                 */
  418.     /**********************************************************************/
  419.  
  420.     /* setup sound device */
  421.     snd_pcm_hw_params_malloc(&hwparams);
  422.     if (snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
  423.         fprintf(stderr, "playhrt: Error opening PCM device %s\n", pcm_name);
  424.         exit(6);
  425.     }
  426.     if (nonblock) {
  427.         if (snd_pcm_nonblock(pcm_handle, 1) < 0) {
  428.             fprintf(stderr, "playhrt: Error setting non-block mode.\n");
  429.             exit(7);
  430.         } else if (verbose) {
  431.             fprintf(stderr, "playhrt: Using card in non-block mode.\n");
  432.         }
  433.     }
  434.     if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
  435.         fprintf(stderr, "playhrt: Error configuring this PCM device.\n");
  436.         exit(8);
  437.     }
  438.     if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
  439.         fprintf(stderr, "playhrt: Error setting MMAP access.\n");
  440.         exit(9);
  441.     }
  442.     if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) {
  443.         fprintf(stderr, "playhrt: Error setting format.\n");
  444.         exit(10);
  445.     }
  446.     if (snd_pcm_hw_params_set_rate(pcm_handle, hwparams, rate, 0) < 0) {
  447.         fprintf(stderr, "playhrt: Error setting rate.\n");
  448.         exit(11);
  449.     }
  450.     if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, nrchannels) < 0) {
  451.         fprintf(stderr, "playhrt: Error setting channels to %d.\n", nrchannels);
  452.         exit(12);
  453.     }
  454.     if (verbose) {
  455.         snd_pcm_uframes_t min=1, max=100000000;
  456.         snd_pcm_hw_params_set_buffer_size_minmax(pcm_handle, hwparams, &min, &max);
  457.         fprintf(stderr, "playhrt: Min and max buffer size of device %ld .. %ld - ", min, max);
  458.     }
  459.     if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, hwbufsize) < 0) {
  460.         fprintf(stderr, "\nplayhrt: Error setting buffersize to %ld.\n", hwbufsize);
  461.         exit(13);
  462.     }
  463.     snd_pcm_hw_params_get_buffer_size(hwparams, &hwbufsize);
  464.     if (verbose) {
  465.         fprintf(stderr, "using %ld.\n", hwbufsize);
  466.     }
  467.     if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
  468.         fprintf(stderr, "playhrt: Error setting HW params.\n");
  469.         exit(14);
  470.     }
  471.     snd_pcm_hw_params_free(hwparams);
  472.     if (snd_pcm_sw_params_malloc (&swparams) < 0) {
  473.         fprintf(stderr, "playhrt: Error allocating SW params.\n");
  474.         exit(15);
  475.     }
  476.     if (snd_pcm_sw_params_current(pcm_handle, swparams) < 0) {
  477.         fprintf(stderr, "playhrt: Error getting current SW params.\n");
  478.         exit(16);
  479.     }
  480.     if (snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, hwbufsize/2) < 0) {
  481.         fprintf(stderr, "playhrt: Error setting start threshold.\n");
  482.         exit(17);
  483.     }
  484.     if (snd_pcm_sw_params(pcm_handle, swparams) < 0) {
  485.         fprintf(stderr, "playhrt: Error applying SW params.\n");
  486.         exit(18);
  487.     }
  488.     snd_pcm_sw_params_free (swparams);
  489.  
  490.     /**********************************************************************/
  491.     /* wait to allow filling input pipeline                               */
  492.     /**********************************************************************/
  493.  
  494.     /* get time */
  495.     if (clock_gettime(CLOCK_MONOTONIC, &mtime) < 0) {
  496.         fprintf(stderr, "playhrt: Error getting monotonic clock.\n");
  497.         exit(19);
  498.     }
  499.     if (verbose)
  500.         fprintf(stderr, "playhrt: Wait for pipeline (%s).\n", formattime(text, mtime));
  501.    
  502.     /* use defined sleep to allow input process to fill pipeline */
  503.     if (sleep > 0) {
  504.         mtime.tv_sec = sleep/1000000;
  505.         mtime.tv_nsec = 1000*(sleep - mtime.tv_sec*1000000);
  506.         nanosleep(&mtime, NULL);
  507.     /* waits until pipeline is filled */
  508.     } else {
  509.         fd_set rdfs;
  510.         FD_ZERO(&rdfs);
  511.         FD_SET(sfd, &rdfs);
  512.  
  513.         /* select() waits until pipeline is ready */
  514.         if (select(sfd+1, &rdfs, NULL, NULL, NULL) <=0 ) {
  515.             fprintf(stderr, "playhrt: Error waiting for pipeline data.\n");
  516.             exit(20);
  517.         };
  518.  
  519.         /* now sleep until the pipeline is filled */
  520.         sleep = (long)((fcntl(sfd, F_GETPIPE_SZ)/bytesperframe)*1000000.0/rate); /* us */
  521.         mtime.tv_sec = 0;
  522.         mtime.tv_nsec = sleep*1000;
  523.         nanosleep(&mtime, NULL);
  524.     }
  525.    
  526.     /* get time */
  527.     if (clock_gettime(CLOCK_MONOTONIC, &mtime) < 0) {
  528.         fprintf(stderr, "playhrt: Error getting monotonic clock.\n");
  529.         exit(21);
  530.     }
  531.     if (verbose)
  532.         fprintf(stderr, "playhrt: Pipeline ready    (%s).\n", formattime(text, mtime));
  533.  
  534.     /**********************************************************************/
  535.     /* main loop                                                          */
  536.     /**********************************************************************/
  537.  
  538.     for (count=1; 1; count++) {
  539.  
  540.         /* start playing when half of hwbuffer is filled */
  541.         if (count == startcount) {
  542.             snd_pcm_start(pcm_handle);
  543.             if (verbose) {
  544.                 clock_gettime(CLOCK_MONOTONIC, &mtimecheck);
  545.                 fprintf(stderr, "playhrt: Start playback    (%s).\n", formattime(text, mtimecheck));
  546.             }
  547.         }
  548.  
  549.         /* read amount of frames which can be written to hardware buffer */
  550.         avail = snd_pcm_avail(pcm_handle);
  551.         if (avail < 0) {
  552.             fprintf(stderr, "playhrt: Error on snd_pcm_avail(): %ld.\n", avail);
  553.             exit(22);
  554.         }
  555.  
  556.         /* get address for mmap access, we will write to iptr */
  557.         if (snd_pcm_mmap_begin(pcm_handle, &areas, &offset, &frames) < 0) {
  558.             fprintf(stderr, "playhrt: Error getting mmap address.\n");
  559.             exit(23);
  560.         }
  561.         iptr = areas[0].addr + offset * bytesperframe;
  562.  
  563.         /**********************************************************************/
  564.         /* automatic rate adaption                                            */
  565.         /**********************************************************************/
  566.  
  567.         /* start measurement/adaption in time to finish when LOOPS_CADENCE loops were done */
  568.         if (count > startcount && (count+LOOPS_AVG) % LOOPS_CADENCE == 0) {
  569.             sumavg = LOOPS_AVG;
  570.             avgav = 0;
  571.         }
  572.  
  573.         /* add up buffer level for an amount of LOOPS_AVG measurements */
  574.         if (sumavg) {
  575.             avgav += avail;
  576.             if (sumavg == 1) {
  577.                 bufavg = (double)avgav/LOOPS_AVG;   /* average buffer level */
  578.                 buferr = bufavg - hwbufsize/2;      /* error against target (hwbufsize/2) */
  579.                 buferr_i = buferr_i + buferr;       /* integrated error */
  580.  
  581.                 /* calculate amount of time to be added to default step time */
  582.                 /* to overall match the local clock to the outgoing clock */
  583.                 extransec = (long)(-(Kp * buferr + Ki * Ta * buferr_i) + 0.5);
  584.                 nsec = (int)(1000000000/loopspersec + extransec);
  585.  
  586.                 if (verbose > 1) {
  587.                     /* deviation: >0 local clock it too fast, <0 local clock too slow */
  588.                     double deviation = nsec / (1000000000.0/loopspersec) - 1.0;
  589.                     fprintf(stderr, "playhrt: (%s) buf: %6.1f e: % 6.1f ei: % 6.1f dt: % 4ld ns (%+6.4f%%)\n",
  590.                             hms(text,mtime), bufavg, buferr, buferr_i, extransec, deviation*100);
  591.                 }
  592.             }
  593.             sumavg--;
  594.         }
  595.  
  596.         /**********************************************************************/
  597.         /* read data directly from pipeline into mmap´ed area (iptr)          */
  598.         /**********************************************************************/
  599.  
  600.         /* memset(iptr, 0, frames * bytesperframe); commented out to save time */
  601. //AB        readbytes = read(sfd, iptr, frames * bytesperframe);
  602.  
  603.         /* important: we might need to read several times to get targeted amount of data. */
  604.         readbytes = 0;
  605.         int nloops = 0;
  606.         do {
  607.             readthis = read(sfd, iptr+readbytes, (frames * bytesperframe)-readbytes);
  608.             readbytes += readthis;
  609.             nloops++;
  610.         } while (readbytes < frames * bytesperframe && readthis > 0);
  611.  
  612.         if (verbose && readbytes != frames * bytesperframe)
  613.             fprintf(stderr, "playhrt: Incomplete read (pipe end): read=%d targeted=%ld\n",
  614.                     readbytes, frames * bytesperframe);
  615.         if (nloops>1)
  616.             fprintf(stderr, "playhrt: Multiple reads required (nloops=%d).\n", nloops);
  617.  
  618.         /**********************************************************************/
  619.         /* calculate next wakeup                                              */
  620.         /**********************************************************************/
  621.  
  622.         /* compute time for next wakeup */
  623.         mtime.tv_nsec += nsec;
  624.         if (mtime.tv_nsec > 999999999) {
  625.             mtime.tv_nsec -= 1000000000;
  626.             mtime.tv_sec++;
  627.         }
  628.  
  629.         /**********************************************************************/
  630.         /* sleep until defined wakeup, refresh data, commit data              */
  631.         /**********************************************************************/
  632.  
  633.         /* refreshmem(iptr, readbytes); commented out as called again before commit */
  634.         clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &mtime, NULL);
  635.         refreshmem(iptr, readbytes); /* refresh memory */
  636.         snd_pcm_mmap_commit(pcm_handle, offset, frames);
  637.         bytecount += readbytes;
  638.         if (readthis == 0) /* done */
  639.             break;
  640.     }
  641.  
  642.     /**********************************************************************/
  643.     /* playhrt end, cleanup                                               */
  644.     /**********************************************************************/
  645.  
  646.     /* cleanup network connection and sound device */
  647.     close(sfd);
  648.     snd_pcm_drain(pcm_handle);
  649.     snd_pcm_close(pcm_handle);
  650.     if (verbose) {
  651.         fprintf(stderr, "playhrt: Loops: %ld, bytes: %lld. \n", count, bytecount);
  652.     }
  653.     return 0;
  654. }
  655.  
Add Comment
Please, Sign In to add comment