Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

full duplex ALSA code

By: a guest on Apr 21st, 2010  |  syntax: C  |  size: 13.97 KB  |  views: 433  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #include <stdio.h>
  2. #include <alsa/asoundlib.h>
  3. #include <string.h>
  4. #include <sched.h>
  5. #include <alloca.h>
  6. #include <stdint.h>
  7.  
  8. void hexdump(uint8_t *buffer, int len)
  9. {
  10.         int i;
  11.         int addr=0;
  12.         const int bytecount=16;
  13.  
  14.         fprintf(stderr, "hexdump(): %i bytes\n", len);
  15.         fflush(NULL);
  16.  
  17.         for (i=0; i<len; i++)
  18.         {
  19.                 if ((i % bytecount) == 0)
  20.                         fprintf(stderr, "%04x\t", addr);
  21.                
  22.                 fprintf(stderr, "%02x ", buffer[i]);
  23.  
  24.                 if (((i+1) % bytecount) == 0)
  25.                 {
  26.                         addr += bytecount;
  27.                         fprintf(stderr, "\n");
  28.                 }
  29.         }
  30.        
  31.         if (((i+1) % bytecount) != 0)
  32.                 fprintf(stderr, "\n");
  33. }
  34.  
  35. // returns handle of PCM on success, NULL otherwise
  36. snd_pcm_t *setup_pcm(char *device_name, int stream)
  37. {
  38.         int ret;
  39.         snd_pcm_t *handle=NULL;
  40.         snd_pcm_hw_params_t *hwparams=NULL;
  41.         snd_pcm_sw_params_t *swparams=NULL;
  42.         snd_pcm_format_t pcm_format;
  43.         int dir;
  44.         unsigned int pcm_rate_min, pcm_rate_max, pcm_rate, pcm_rate_real;
  45.         snd_pcm_uframes_t pcm_period_size_min, pcm_period_size_max, pcm_period_size;
  46.         snd_pcm_uframes_t pcm_buffer_size_min, pcm_buffer_size_max, pcm_buffer_size, pcm_buffer_size_real;
  47.         snd_pcm_uframes_t start_threshold, stop_threshold;
  48.         snd_pcm_uframes_t avail_min;
  49.          
  50.  
  51.         if ((stream != SND_PCM_STREAM_PLAYBACK) && (stream != SND_PCM_STREAM_CAPTURE))
  52.         {
  53.                 fprintf(stderr, "invalid stream\n");
  54.                 return NULL;
  55.         }
  56.  
  57.         assert(device_name);
  58.  
  59.         // open sound card
  60.         if ((ret=snd_pcm_open(&handle, device_name, stream, SND_PCM_NONBLOCK))<0)
  61.         //if ((ret=snd_pcm_open(&handle, device_name, stream, 0))<0)
  62.         {
  63.                 fprintf(stderr, "could not open PCM: %s\n", snd_strerror(ret));
  64.                 return NULL;
  65.         }
  66.  
  67.         // auto-alloc and fill hardware params
  68.         snd_pcm_hw_params_alloca(&hwparams);
  69.         snd_pcm_hw_params_any(handle, hwparams);
  70.  
  71.         // set access format
  72.         snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
  73.  
  74.         // set audio format
  75.         pcm_format=SND_PCM_FORMAT_S16_LE;
  76.         snd_pcm_hw_params_set_format (handle, hwparams, pcm_format);
  77.  
  78.         printf("PCM format is %s, %s, %s with %i bits\n",
  79.                 snd_pcm_format_signed(pcm_format)? "signed":"unsigned",
  80.                 snd_pcm_format_linear(pcm_format)? "linear":"not linear",
  81.                 snd_pcm_format_little_endian (pcm_format)? "LE":"BE",
  82.                 snd_pcm_format_width(pcm_format));
  83.  
  84.         // set channels
  85.         snd_pcm_hw_params_set_channels (handle, hwparams, 1);
  86.  
  87.         // get minimum and maximum supported rates
  88.         dir=0;
  89.         if ((ret=snd_pcm_hw_params_get_rate_min(hwparams, &pcm_rate_min, &dir))<0)
  90.         {
  91.                 fprintf(stderr, "could not get minimum supported rate: %s\n", snd_strerror(ret));
  92.                 return NULL;
  93.         }
  94.         dir=0;
  95.         if ((ret=snd_pcm_hw_params_get_rate_max(hwparams, &pcm_rate_max, &dir))<0)
  96.         {
  97.                 fprintf(stderr, "could not get maximum supported rate: %s\n", snd_strerror(ret));
  98.                 return NULL;
  99.         }
  100.  
  101.         printf("PCM rate %u - %u Hz\n", pcm_rate_min, pcm_rate_max);
  102.  
  103.         // check rate
  104.         pcm_rate=8000;
  105.         if ((pcm_rate<pcm_rate_min || pcm_rate>pcm_rate_max))
  106.         {
  107.                 fprintf(stderr, "PCM rate out of range\n");
  108.                 return NULL;
  109.         }
  110.  
  111.         // check if exact rate is available and set up rate
  112.         dir=0;
  113.         if ((ret=snd_pcm_hw_params_test_rate(handle, hwparams, pcm_rate, dir))<0)
  114.         {
  115.                 dir=0;
  116.                 pcm_rate_real=pcm_rate;
  117.                 if ((ret=snd_pcm_hw_params_set_rate_near(handle, hwparams, &pcm_rate_real, &dir))<0)
  118.                 {
  119.                         fprintf(stderr, "could not set nearest PCM rate: %s\n", snd_strerror(ret));
  120.                         return NULL;
  121.                 }
  122.  
  123.                 fprintf(stderr, "exact rate of %u Hz not possible, using nearest possible rate of %u Hz instead\n", pcm_rate, pcm_rate_real);
  124.                 pcm_rate=pcm_rate_real;
  125.         }
  126.         else
  127.         {
  128.                 dir=0;
  129.                 if ((ret=snd_pcm_hw_params_set_rate(handle, hwparams, pcm_rate, dir))<0)
  130.                 {
  131.                         fprintf(stderr, "could not set up PCM rate: %s\n", snd_strerror(ret));
  132.                         return NULL;
  133.                 }
  134.         }
  135.  
  136.         // get minimum and maximum supported period size
  137.         dir=0;
  138.         if ((ret=snd_pcm_hw_params_get_period_size_min(hwparams, &pcm_period_size_min, &dir))<0)
  139.         {
  140.                 fprintf(stderr, "could not get minimum supported period size: %s\n", snd_strerror(ret));
  141.                 return NULL;
  142.         }
  143.         dir=0;
  144.         if ((ret=snd_pcm_hw_params_get_period_size_max(hwparams, &pcm_period_size_max, &dir))<0)
  145.         {
  146.                 fprintf(stderr, "could not get maximum supported period size: %s\n", snd_strerror(ret));
  147.                 return NULL;
  148.         }
  149.        
  150.         printf("PCM period size: %lu - %lu\n", pcm_period_size_min, pcm_period_size_max);
  151.        
  152.         // check period size
  153.         pcm_period_size=160;
  154.         if ((pcm_period_size<pcm_period_size_min || pcm_period_size>pcm_period_size_max))
  155.         {
  156.                 fprintf(stderr, "PCM period size out of range\n");
  157.                 return NULL;
  158.         }
  159.        
  160.         // check if exact period size is available
  161.         dir=0;
  162.         if ((ret=snd_pcm_hw_params_test_period_size(handle, hwparams, pcm_period_size, dir))<0)
  163.         {
  164.                 fprintf(stderr, "requested period size not available: %s\n", snd_strerror(ret));
  165.                 return NULL;
  166.         }
  167.  
  168.         // set up period size
  169.         dir=0;
  170.         if ((ret=snd_pcm_hw_params_set_period_size(handle, hwparams, pcm_period_size, dir))<0)
  171.         {
  172.                 fprintf(stderr, "could not set up PCM rate: %s\n", snd_strerror(ret));
  173.                 return NULL;
  174.         }
  175.  
  176.         // get minimum and maximum supported buffer size
  177.         if ((ret=snd_pcm_hw_params_get_buffer_size_min(hwparams, &pcm_buffer_size_min))<0)
  178.         {
  179.                 fprintf(stderr, "could not get minimum supported buffer size: %s\n", snd_strerror(ret));
  180.                 return NULL;
  181.         }
  182.         if ((ret=snd_pcm_hw_params_get_buffer_size_max(hwparams, &pcm_buffer_size_max))<0)
  183.         {
  184.                 fprintf(stderr, "could not get maximum supported buffer size: %s\n", snd_strerror(ret));
  185.                 return NULL;
  186.         }
  187.        
  188.         printf("PCM buffer size: %lu - %lu\n", pcm_buffer_size_min, pcm_buffer_size_max);
  189.        
  190.         // check buffer size
  191.         // buffer size: 2*period_size with given format and 1 channel (mono)
  192.         pcm_buffer_size=pcm_period_size*snd_pcm_format_size(pcm_format, 1)*2;
  193.         printf("calculated buffer size: %lu\n", pcm_buffer_size);
  194.         if ((pcm_buffer_size<pcm_buffer_size_min || pcm_buffer_size>pcm_buffer_size_max))
  195.         {
  196.                 fprintf(stderr, "PCM period size out of range\n");
  197.                 return NULL;
  198.         }
  199.        
  200.         // check if exact buffer size is available and set up buffer size
  201.         if ((ret=snd_pcm_hw_params_test_buffer_size(handle, hwparams, pcm_buffer_size))<0)
  202.         {
  203.                 pcm_buffer_size_real=pcm_buffer_size;
  204.                 if ((ret=snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &pcm_buffer_size_real))<0)
  205.                 {
  206.                         fprintf(stderr, "could not set nearest buffer size: %s\n", snd_strerror(ret));
  207.                         return NULL;
  208.                 }
  209.  
  210.                 fprintf(stderr, "exact buffer size of %lu bytes not possible, using nearest possible buffer size %lu instead\n",
  211.                         pcm_buffer_size, pcm_buffer_size_real);
  212.                 pcm_buffer_size=pcm_buffer_size_real;
  213.         }
  214.         else
  215.         {
  216.                 if ((ret=snd_pcm_hw_params_set_buffer_size(handle, hwparams, pcm_buffer_size))<0)
  217.                 {
  218.                         fprintf(stderr, "could not set PCM buffer size: %s\n", snd_strerror(ret));
  219.                         return NULL;
  220.                 }
  221.         }
  222.  
  223.         // set up configured hw params
  224.         if ((ret=snd_pcm_hw_params(handle, hwparams))<0)
  225.         {
  226.                 fprintf(stderr, "could not set up hardware params: %s\n", snd_strerror(ret));
  227.                 return NULL;
  228.         }
  229.  
  230.         // alloc and fill software params
  231.         snd_pcm_sw_params_alloca (&swparams);
  232.         snd_pcm_sw_params_current (handle, swparams);
  233.  
  234.         if ((ret=snd_pcm_sw_params_get_avail_min(swparams, &avail_min))<0)
  235.         {
  236.                 fprintf(stderr, "could not get current avail_min setting: %s\n", snd_strerror(ret));
  237.                 return NULL;
  238.         }
  239.  
  240.         printf("avail min: %lu\n", avail_min);
  241.  
  242.         // when to consider the device to be ready for the next data transfer operation
  243.         if ((ret=snd_pcm_sw_params_set_avail_min(handle, swparams, pcm_period_size))<0)
  244.         {
  245.                 fprintf(stderr, "could not set avail_min: %s\n", snd_strerror(ret));
  246.                 return NULL;
  247.         }
  248.  
  249.         // start threshold
  250.         //start_threshold = (double)pcm_rate * (1/1000000);
  251.         start_threshold = pcm_period_size;
  252.         printf("start threshold: %lu frames\n", start_threshold);
  253.         if ((ret=snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold))<0)
  254.         {
  255.                 fprintf(stderr, "could not set start threshold: %s\n", snd_strerror(ret));
  256.                 return NULL;
  257.         }
  258.  
  259.  
  260.         // stop threshold
  261.         stop_threshold=2*pcm_period_size;
  262.         printf("stop threshold: %lu frames\n", stop_threshold);
  263.         if ((ret=snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold))<0)
  264.         {
  265.                 fprintf(stderr, "could not set stop threshold: %s\n", snd_strerror(ret));
  266.                 return NULL;
  267.         }
  268.  
  269.         // set software params
  270.         if ((ret=snd_pcm_sw_params(handle, swparams))<0)
  271.         {
  272.                 fprintf(stderr, "could not set sw params: %s\n", snd_strerror(ret));
  273.                 return NULL;
  274.         }
  275.  
  276.  
  277.         return handle;
  278. }
  279.  
  280. int main(int argc, char **argv)
  281. {
  282.         struct sched_param schp;
  283.         char *device_name=NULL;
  284.         snd_pcm_t *capture_device=NULL, *playback_device=NULL;
  285.         int capture_pollfd_count, playback_pollfd_count, pollfd_count;
  286.         int ret;
  287.         struct pollfd poll_fds[2];
  288.         uint8_t buffer[160*2];
  289.         int i;
  290.         snd_pcm_state_t state;
  291.         snd_pcm_t *device_handle;
  292.         unsigned short revents;
  293.  
  294.         if (argc != 2)
  295.         {
  296.                 printf("Usage: %s <audio device name>\n", argv[0]);
  297.                 exit(EXIT_FAILURE);
  298.         }
  299.  
  300.         device_name=argv[1];
  301.  
  302.         // try to get high scheduler priority
  303.         memset(&schp, 0, sizeof (schp));
  304.         schp.sched_priority = sched_get_priority_max(SCHED_FIFO);
  305.  
  306.         if (sched_setscheduler (0, SCHED_FIFO, &schp)<0)
  307.         {
  308.                 perror("could not sched_setscheduler");
  309.         }
  310.  
  311.         // open and configure capture device
  312.         if ((capture_device=setup_pcm(device_name, SND_PCM_STREAM_CAPTURE))==NULL)
  313.         {
  314.                 fprintf(stderr, "could not open and set up capture device %s\n", device_name);
  315.                 exit(EXIT_FAILURE);
  316.         }
  317.  
  318.         if ((playback_device=setup_pcm(device_name, SND_PCM_STREAM_PLAYBACK))==NULL)
  319.         {
  320.                 fprintf(stderr, "could not open and set up capture device %s\n", device_name);
  321.                 exit(EXIT_FAILURE);
  322.         }
  323.  
  324.         /*
  325.         // try to link capture and playback device so that they start/stop/prepare in sync
  326.         if ((linked=snd_pcm_link(capture_device, playback_device))<0)
  327.         {
  328.                 fprintf(stderr, "could not link capture and playback device: %s\n", snd_strerror(linked));
  329.         }
  330.         */
  331.  
  332.         // get poll info
  333.         capture_pollfd_count=snd_pcm_poll_descriptors_count(capture_device);
  334.         playback_pollfd_count=snd_pcm_poll_descriptors_count(playback_device);
  335.         pollfd_count=capture_pollfd_count+playback_pollfd_count;
  336.  
  337.         printf("capture fds: %i, playback fds: %i\n", capture_pollfd_count, playback_pollfd_count);
  338.  
  339.         assert(capture_pollfd_count==1 && playback_pollfd_count==1);
  340.  
  341.         // set up poll struct
  342.         memset(poll_fds, 0x00, sizeof(poll_fds));
  343.         snd_pcm_poll_descriptors(capture_device, &poll_fds[0], capture_pollfd_count);
  344.         snd_pcm_poll_descriptors(playback_device, &poll_fds[1], playback_pollfd_count);
  345.  
  346.         printf("capture poll fd: %i, playback poll fd: %i\n", poll_fds[0].fd, poll_fds[1].fd);
  347.         printf("capture struct: fd: %i, events: %s%s%s, revents: %i\n",
  348.                 poll_fds[0].fd,
  349.                 (poll_fds[0].events & POLLIN) ? "POLLIN ":"",
  350.                 (poll_fds[0].events & POLLOUT) ? "POLLOUT ":"",
  351.                 (poll_fds[0].events & POLLERR) ? "POLLERR ":"",
  352.                 poll_fds[0].revents);
  353.         printf("playback struct: fd: %i, events: %s%s%s, revents: %i\n",
  354.                 poll_fds[1].fd,
  355.                 (poll_fds[1].events & POLLIN) ? "POLLIN ":"",
  356.                 (poll_fds[1].events & POLLOUT) ? "POLLOUT ":"",
  357.                 (poll_fds[1].events & POLLERR) ? "POLLERR ":"",
  358.                 poll_fds[1].revents);
  359.  
  360.         // prepare capture PCM
  361.         if ((ret=snd_pcm_prepare(capture_device))<0)
  362.         {
  363.                 fprintf(stderr, "could not prepare capture device: %s\n", snd_strerror(ret));
  364.                 exit(EXIT_FAILURE);
  365.         }
  366.        
  367.         // prepare playback PCM
  368.         if ((ret=snd_pcm_prepare(playback_device))<0)
  369.         {
  370.                 fprintf(stderr, "could not prepare playback device: %s\n", snd_strerror(ret));
  371.                 exit(EXIT_FAILURE);
  372.         }
  373.  
  374.         // start capture device
  375.         if ((ret=snd_pcm_start(capture_device))<0)
  376.         {
  377.                 fprintf(stderr, "could not start capture device: %s\n", snd_strerror(ret));
  378.                 exit(EXIT_FAILURE);
  379.         }
  380.  
  381.         memset(buffer, 0x00, sizeof(buffer));
  382.  
  383.         // read samples from capture device and write them to the playback device
  384.         while (1)
  385.         {
  386.                 // poll on both devices
  387.                 poll(poll_fds, pollfd_count, -1);
  388.  
  389.                 for (i=0; i<pollfd_count; i++)
  390.                 {
  391.  
  392.                         if (i==0)
  393.                                 device_handle=capture_device;
  394.                         else
  395.                                 device_handle=playback_device;
  396.  
  397.                         // get demangled revent
  398.                         if ((ret=snd_pcm_poll_descriptors_revents(device_handle, &poll_fds[i], 1, &revents))<0)
  399.                         {
  400.                                 fprintf(stderr, "could not snd_pcm_poll_descriptors_revents: %s\n", snd_strerror(ret));
  401.                                 exit(EXIT_FAILURE);
  402.                         }
  403.                        
  404.                         printf("demangled poll: %s%s%s on %s device\n",
  405.                                 (revents & POLLERR)? "POLLERR ":"",
  406.                                 (revents & POLLIN)? "POLLIN ":"",
  407.                                 (revents & POLLOUT)? "POLLOUT ":"",
  408.                                 (i==0)? "capture":"playback");
  409.  
  410.                         // check if there was an error
  411.                         if (revents & POLLERR)
  412.                         {
  413.                                 state=snd_pcm_state(device_handle);
  414.  
  415.                                 switch(state)
  416.                                 {
  417.                                         case    SND_PCM_STATE_XRUN:
  418.                                                                                                         fprintf(stderr, "XRUN on %s device\n",
  419.                                                                                                                 (poll_fds[i].revents & POLLOUT) ? "playback" : "capture");
  420.  
  421.                                                                                                         // restart
  422.                                                                                                         snd_pcm_prepare(device_handle);
  423.                                                                                                         snd_pcm_start(device_handle);
  424.                                                                                                         break;
  425.                                         default:
  426.                                                                                                         fprintf(stderr, "unknown state (%i) on %s device\n",
  427.                                                                                                                 state, (poll_fds[i].revents & POLLOUT) ? "playback" : "capture");
  428.                                 }
  429.                         }
  430.                        
  431.                         // check for new input data
  432.                         if (revents & POLLIN)
  433.                         {
  434.                                 if ((ret=snd_pcm_readi(capture_device, buffer, 160))<0)
  435.                                 {
  436.                                         if (ret == -EPIPE)
  437.                                         {
  438.                                                 fprintf(stderr, "XRUN while reading from capture device: %s\n", snd_strerror(ret));
  439.                                                 snd_pcm_prepare(capture_device);
  440.                                                 snd_pcm_start(capture_device);
  441.                                         }
  442.                                         else
  443.                                         {
  444.                                                 fprintf(stderr, "could not read from capture device: %s\n", snd_strerror(ret));
  445.                                                 exit(EXIT_FAILURE);
  446.                                         }
  447.                                 }
  448.  
  449.                                 printf("read %i frames\n", ret);
  450.                                 assert(ret==160);
  451.  
  452.                                 hexdump(buffer, ret*2);
  453.                         }
  454.                        
  455.                         // check if we can write the buffer to the playback device
  456.                         if (revents & POLLOUT)
  457.                         {
  458.                                 if ((ret=snd_pcm_writei(playback_device, buffer, 160))<0)
  459.                                 {
  460.                                         if (ret == -EPIPE)
  461.                                         {
  462.                                                 fprintf(stderr, "XRUN while writing to playback device: %s\n", snd_strerror(ret));
  463.                                                 snd_pcm_prepare(capture_device);
  464.                                                 snd_pcm_start(capture_device);
  465.                                         }
  466.                                         else
  467.                                         {
  468.                                                 fprintf(stderr, "could not write to playback device: %s\n", snd_strerror(ret));
  469.                                                 exit(EXIT_FAILURE);
  470.                                         }
  471.                                 }
  472.  
  473.                                 printf("wrote %i frames\n", ret);
  474.                                 assert(ret==160);
  475.                         }
  476.                 }
  477.         } // while(1)
  478.  
  479.         // never reached
  480.         exit(EXIT_SUCCESS);
  481. }