Advertisement
Guest User

full duplex ALSA code

a guest
Apr 21st, 2010
827
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 13.97 KB | None | 0 0
  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. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement