#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 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 |
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 }
pthread_cond_t eventVariableFilledBuffers = PTHREAD_COND_INITIALIZER |