src/CaptureAudio.c File Reference

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include <alsa/asoundlib.h>
#include <sys/time.h>
#include <math.h>
#include <pthread.h>

Defines

#define STARTED   1
#define NOT_STARTED   0
#define INCREASE   2
#define DECREASE   0
#define CAPTURE_RATE_DEFAULT   48000
#define CAPTURE_CHANNELS_DEFAULT   2
#define CAPTURE_FORMAT_DEFAULT   SND_PCM_FORMAT_S16
#define PLAYBACK_RATE_DEFAULT   48000
#define PLAYBACK_CHANNELS_DEFAULT   2
#define PLAYBACK_FORMAT_DEFAULT   SND_PCM_FORMAT_S16

Functions

void modify_num_frames_ready (int cmd, int val)
void * read_in (void *_handle)
void * write_out (void *_handle)
int main (int argc, char *argv[])

Variables

pthread_cond_t eventVariableFilledBuffers = PTHREAD_COND_INITIALIZER

Define Documentation

#define CAPTURE_CHANNELS_DEFAULT   2

#define CAPTURE_FORMAT_DEFAULT   SND_PCM_FORMAT_S16

#define CAPTURE_RATE_DEFAULT   48000

#define DECREASE   0

#define INCREASE   2

#define NOT_STARTED   0

#define PLAYBACK_CHANNELS_DEFAULT   2

#define PLAYBACK_FORMAT_DEFAULT   SND_PCM_FORMAT_S16

#define PLAYBACK_RATE_DEFAULT   48000

#define STARTED   1


Function Documentation

int main ( int  argc,
char *  argv[] 
)

00626 {
00627    struct option long_option[] =
00628  {
00629     {"help", 0, NULL, 'h'},
00630     {"list", 1, NULL, 'l'},
00631     {"device", 1, NULL, 'D'},
00632     {"device_cap", 1, NULL, 'd'},
00633     {"rate", 1, NULL, 'r'},
00634     {"channels", 1, NULL, 'c'},
00635     {"buffer", 1, NULL, 'b'},
00636     {"period", 1, NULL, 'p'},
00637     {"format", 1, NULL, 'o'},
00638     {"List", 1, NULL, 'L'},
00639     {"verbose", 1, NULL, 'v'},
00640     {NULL, 0, NULL, 0},
00641  };
00642    /* playback variables */
00643    int k, err;
00644    snd_pcm_t *handle;
00645    snd_pcm_hw_params_t *hwparams;
00646    snd_pcm_hw_params_alloca(&hwparams);
00647    // write-out thread
00648    pthread_t thread_out;
00649    int ret_thread_out;
00650 
00651    /* capture variables */
00652    snd_pcm_t *handle_cap;
00653    int count;
00654    // hardware parameters struct for capture
00655    snd_pcm_hw_params_t *hwparams_cap;
00656    snd_pcm_hw_params_alloca(&hwparams_cap);
00657    // read-in thread
00658    pthread_t thread_in;
00659    int ret_thread_in;
00660 
00661    /* help and other variables */
00662    int morehelp;
00663    char **devices;
00664    int devicesNum;
00665    int i;
00666    // to hold all the options entered from command line
00667    char options_entered[11] = {0};
00668    char temp[2] = {0};
00669 
00670    morehelp = 0;
00671 
00672    while (1)
00673    {
00674       int c;
00675       if ((c = getopt_long(argc, argv, "hlD:d:r:c:b:p:o:Lv", long_option, NULL)) < 0)
00676          break;
00677       // Collect all the entered options
00678       temp[0] = c;
00679       strcat(options_entered, temp);
00680 
00681       switch (c) {
00682          case 'h':
00683             morehelp++;
00684             break;
00685          case 'l':
00686             // display the list of VISION ALSA capture devices and ALSA playback devices
00687             list_alsa_devices(SND_PCM_STREAM_CAPTURE);
00688             list_alsa_devices(SND_PCM_STREAM_PLAYBACK);
00689             return 0;
00690          case 'D':
00691             device = strdup(optarg);
00692             break;
00693          case 'd':
00694             device_cap = strdup(optarg);
00695             break;
00696          case 'r':
00697             // Unsupported rates are handled by snd_pcm_hw_params_set_rate_near()
00698             rate_cap = atoi(optarg);
00699             break;
00700          case 'c':
00701             channels_cap = atoi(optarg);
00702             break;
00703          case 'b':
00704             buffer_time = atoi(optarg);
00705             buffer_time = buffer_time < 1000 ? 1000 : buffer_time;
00706             buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
00707             break;
00708          case 'p':
00709             period_time = atoi(optarg);
00710             period_time = period_time < 1000 ? 1000 : period_time;
00711             period_time = period_time > 1000000 ? 1000000 : period_time;
00712             break;
00713          case 'o':
00714             for (format_cap = 0; format_cap < SND_PCM_FORMAT_LAST; format_cap++)
00715             {
00716                const char *format_name = snd_pcm_format_name(format_cap);
00717                if (format_name)
00718                   if (!strcasecmp(format_name, optarg))
00719                      break;
00720             }
00721             if (format_cap == SND_PCM_FORMAT_LAST)
00722             {
00723                printf("*** Format for capture is not an ALSA format => using default; (Use -L to display the list of ALSA formats)\n");
00724                format_cap = CAPTURE_FORMAT_DEFAULT;
00725             }
00726             if (!snd_pcm_format_linear(format_cap) &&
00727                !(format_cap == SND_PCM_FORMAT_FLOAT_LE || format_cap == SND_PCM_FORMAT_FLOAT_BE))
00728             {
00729                printf("Invalid (non-linear/float) format %s\n", optarg);
00730                return 1;
00731             }
00732             break;
00733          case 'L':
00734             // display the list of ALSA formats
00735             printf("List of ALSA formats: ");
00736             for (i = 0; i < SND_PCM_FORMAT_LAST; i++)
00737                printf("%s ", snd_pcm_format_name(i));
00738             printf("\n");
00739             return 0;
00740          case 'v':
00741             verbose = 1;
00742             break;
00743       }
00744    }
00745    if (morehelp) {
00746       help();
00747       return 0;
00748    }
00749    err = snd_output_stdio_attach(&output, stdout, 0);
00750    if (err < 0) {
00751       printf("Output failed: %s\n", snd_strerror(err));
00752       return 0;
00753    }
00754 
00755    // program started with no device arguments: get default capture & playback devices
00756    if((argc == 1) || ((strchr(options_entered, 'D') == 0) && (strchr(options_entered, 'd') == 0)))
00757    {
00758       // Allocate memory for capture and playback devices names
00759       device = calloc(32, sizeof(char));
00760       device_cap = calloc(32, sizeof(char));
00761       printf("\n*** No capture and playback devices specified: USING DEFAULT CAPTURE AND PLAYBACK DEVICES!\n");
00762       acquire_default_device(device_cap, SND_PCM_STREAM_CAPTURE);
00763       acquire_default_device(device, SND_PCM_STREAM_PLAYBACK);
00764    }
00765    // In case only playback device was specified
00766    if ((strchr(options_entered, 'D') != 0) && (strchr(options_entered, 'd') == 0))
00767    {
00768       printf("*** No capture device specified: USING DEFAULT CAPTURE DEVICE!\n");
00769       device_cap = calloc(32, sizeof(char));
00770       acquire_default_device(device_cap, SND_PCM_STREAM_CAPTURE);
00771    }
00772    // In case only capture device was specified
00773    if ((strchr(options_entered, 'd') != 0) && (strchr(options_entered, 'D') == 0))
00774    {
00775       printf("*** No playback device specified: USING DEFAULT PLAYBACK DEVICE!\n");
00776       device = calloc(32, sizeof(char));
00777       acquire_default_device(device, SND_PCM_STREAM_PLAYBACK);
00778    }
00779 
00780    /* Playback: PCM open and parameters initialisation */
00781    printf("\nPLAYBACK:\n");
00782    // Open the PCM interface for playback in blocking mode
00783    if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
00784       printf("Playback open error: %s\n", snd_strerror(err));
00785       return 0;
00786    }
00787    printf("Playback device is %s\n", device);
00788    if ((err = set_hwparams_playback(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
00789       printf("Setting of hwparams failed: %s\n", snd_strerror(err));
00790       exit(EXIT_FAILURE);
00791    }
00792 
00793    /* Capture: PCM open and parameters initialisation */
00794    printf("\nCAPTURE:\n");
00795    // Open the PCM interface for capture
00796    // in non-blocking mode
00797    if ((err = snd_pcm_open(&handle_cap, device_cap, SND_PCM_STREAM_CAPTURE, 1)) < 0)
00798    {
00799       printf("Capture open error: %s\n", snd_strerror(err));
00800       return 0;
00801    }
00802    printf("Capture device is %s\n", device_cap);
00803    // Set capture parameters
00804    if ((err = set_hwparams_capture(handle_cap, hwparams_cap, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
00805    {
00806       printf("Setting of hwparams_cap for capture failed: %s\n", snd_strerror(err));
00807       exit(EXIT_FAILURE);
00808    }
00809 
00810    // print out detailed setup of capture and playback card
00811    if (verbose > 0)
00812    {
00813     printf("PLAYBACK SETUP VERBOSELY:\n");
00814     snd_pcm_dump(handle, output);
00815     printf("\nCAPTURE SETUP VERBOSELY:\n");
00816     snd_pcm_dump(handle_cap, output);
00817    }
00818 
00819    // Notify the user in case rates are not equal for capture and playback
00820    if (rate_cap != rate)
00821       printf("*** Note that rate for capture and rate for playback are not equal => Playback sound artifacts will occur!\n");
00822 
00823    /* Allocate the buffer for audio data */
00824    // Get the sample resolution in bits
00825    if ((count = snd_pcm_hw_params_get_sbits(hwparams_cap)) < 0)
00826    {
00827       printf("Getting sample resolution for capture failed: %s\n", snd_strerror(count));
00828       exit(EXIT_FAILURE);
00829    }
00830    // to bytes
00831    sampleResCap = count / 8;
00832    buffer_cap_size = period_size_cap * sampleResCap * channels_cap;    // period_size_in_frames * bytes_per_sample * channels
00833    buffers_cap = (unsigned char *) calloc(buffers_cap_no, buffer_cap_size);
00834    if (buffers_cap == NULL)
00835    {
00836       printf("Not enough memory\n");
00837       exit(EXIT_FAILURE);
00838    }
00839    // Create capture thread
00840    ret_thread_in = pthread_create(&thread_in, NULL, read_in, (void*) handle_cap);
00841    if(ret_thread_in)
00842    {
00843        printf("Error - pthread_create() for thread_in; return code: %d\n", ret_thread_in);
00844        exit(EXIT_FAILURE);
00845    }
00846 
00847    // Create playback thread
00848    ret_thread_out = pthread_create(&thread_out, NULL, write_out, (void*) handle);
00849    if(ret_thread_out)
00850    {
00851        printf("Error - pthread_create() for thread_out; return code: %d\n",ret_thread_out);
00852        exit(EXIT_FAILURE);
00853    }
00854 
00855    // Suspend the execution of main() until these threads terminate
00856    pthread_join(thread_in, NULL);
00857    pthread_join(thread_out, NULL);
00858 
00859    // Close playback
00860    snd_pcm_close(handle);
00861    // Close capture
00862    snd_pcm_drain(handle_cap);
00863    snd_pcm_close(handle_cap);
00864    free(buffers_cap);
00865    // Free memory for device names
00866    // Note that it is valid to free even if we specified device in command line (no calloc as done for default) because in such case strdup allocated memory
00867    free(device);
00868    free(device_cap);
00869 
00870    return 0;
00871 }

void modify_num_frames_ready ( int  cmd,
int  val 
)

00061 {
00062    // lock the mutex
00063    pthread_mutex_lock (&mutex_num_frames_ready);
00064 
00065    /* Modify how many frames are ready to go out;
00066     * do not allow to make it < 0 or bigger than the number of frames we can house in the buffers;
00067     */
00068    switch (cmd) {
00069       case INCREASE:
00070          numFramesReady = numFramesReady + val;
00071          if (numFramesReady > (buffers_cap_no * period_size_cap))
00072             numFramesReady = buffers_cap_no * period_size_cap;
00073          break;
00074       case DECREASE:
00075          if (val > numFramesReady)
00076             numFramesReady = 0;
00077          else
00078             numFramesReady = numFramesReady - val;
00079          break;
00080       default:
00081          //do nothing
00082          break;
00083    }
00084 
00085    // unlock the mutex
00086    pthread_mutex_unlock (&mutex_num_frames_ready);
00087 }

void* read_in ( void *  _handle  ) 

00091 {
00092    snd_pcm_t* handle_cap = (snd_pcm_t*) _handle;
00093    int j, error, rc;
00094 
00095    readInPtr = buffers_cap;
00096    // space left in the buffers in frames
00097    spaceLeftInBuffs = buffers_cap_no * period_size_cap;
00098 
00099    //Read loop
00100    while(1)
00101    {
00102       // Sleep for 30ms so that we do not read data too often. If we read too often, we get a lot of -EAGAINs since data is not ready.
00103       // Note that sleep time should depend on DMA and capture rate: ((rate_in_bytes_per_sec * x) / 1000) = period_size_in_bytes
00104       // ... we should wait for at least x msec not to get -EAGAINs
00105       usleep(30000);
00106       // Read whatever the driver has got ready;
00107       // but no more than we can house in the buffers
00108       error = snd_pcm_readi (handle_cap, readInPtr, spaceLeftInBuffs);
00109       if (error == -EAGAIN)
00110          continue;
00111       else if (error < 0)
00112       {
00113          printf("readi error: (%d)\n", error);
00114          exit(1);
00115       }
00116       else
00117       {
00118          /* After successful readi(), increase the pointer and decrease how much space is left in the buffers */
00119          readInPtr = readInPtr + (error * sampleResCap * channels_cap);    // frames_read * bytes_per_sample * channels;
00120          spaceLeftInBuffs = spaceLeftInBuffs - error;
00121 
00122          /* Cyclic buffer: reset the pointer */
00123          if(spaceLeftInBuffs == 0)
00124          {
00125             readInPtr = buffers_cap;
00126             spaceLeftInBuffs = buffers_cap_no * period_size_cap;
00127          }
00128 
00129          /* Increment the number of frames ready to be written out to sound card; */
00130          modify_num_frames_ready(INCREASE, error);
00131 
00132          /*
00133           * We have filled some buffers; send out the news for whoever is interested;
00134           * This covers two cases:
00135           * (1) Initially we wait until the buffer is filled with at least (3 * period_size) of data before we start playback,
00136           * (2) or we want to wake up the thread which is waiting for data
00137           */
00138          // start playback when we have at least (3 * period_size) of data in the buffer
00139          // note that period size for playback and period size for capture are now set to be equal
00140          // it simplifies calculations for various clauses (e.g. the one beneath) and things become clearer in general
00141          //if((numFramesReady >= ((buffers_cap_no * period_size_cap)/2)) || (is_started == STARTED))
00142          if((numFramesReady >= (period_size_cap * 3)) || (is_started == STARTED))
00143          {
00144             is_started = STARTED;
00145             rc = pthread_cond_signal(&eventVariableFilledBuffers);
00146             if (rc != 0)
00147             {
00148                printf("read_in: signalling event of filled buffers failed: (%d)\n", rc);
00149                exit(EXIT_FAILURE);
00150             }
00151          }
00152       }
00153    }
00154 }

void* write_out ( void *  _handle  ) 

00158 {
00159    snd_pcm_t* handle = (snd_pcm_t*) _handle;
00160    int cptr, err;
00161 
00162    writeOutPtr = buffers_cap;
00163 
00164    while(1)
00165    {
00166       /* Wait for the event to be signalled that buffers have been filled;
00167        * This covers two cases:
00168        * (1) Initially, when we start writing out, we want to wait for the buffers to get filled;
00169        * (2) Similarly, if it happens that we write_out faster than read_in, we want to wait;
00170        */
00171       pthread_cond_wait(&eventVariableFilledBuffers, &mutex_protect_event_variable);
00172 
00173       while(numFramesReady >= period_size)
00174       {
00175          cptr = period_size;
00176          while (cptr > 0)
00177          {
00178             err = snd_pcm_writei(handle, writeOutPtr, cptr);
00179             if (err == -EAGAIN)
00180                continue;
00181             if (err < 0)
00182             {
00183                printf("write_out error: %s\n", snd_strerror(err));
00184                if (xrun_recovery(handle, err) < 0)
00185                {
00186                   printf("write_out error: %s\n", snd_strerror(err));
00187                   exit(EXIT_FAILURE);
00188                }
00189                break; // skip one period
00190             }
00191             cptr -= err;
00192 
00193             // We have successfully written_out some frames;
00194             // increment write_out pointer
00195             //writeOutPtr = writeOutPtr + (err * sizeof(buffers_cap[0]) * sampleResCap * channels_cap);
00196             writeOutPtr = writeOutPtr + (err * sampleResCap * channels_cap);
00197             // frames_written_out * bytes_per_sample * channels;
00198             // Decrease the variable which shows how many frames are ready to be written out to sound card;
00199             modify_num_frames_ready(DECREASE, err);
00200 
00201             /* Cyclic buffer */
00202             if(writeOutPtr >= (buffers_cap + buffers_cap_no * buffer_cap_size))
00203             {
00204                writeOutPtr = buffers_cap;
00205             }
00206          }
00207       }
00208    }
00209 }


Variable Documentation

pthread_cond_t eventVariableFilledBuffers = PTHREAD_COND_INITIALIZER


Generated on Fri Jan 20 10:36:57 2017 for Vision Utils by  doxygen 1.4.7