Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <stdio.h>
- #include <alsa/asoundlib.h>
- #include <string.h>
- #include <sched.h>
- #include <alloca.h>
- #include <stdint.h>
- void hexdump(uint8_t *buffer, int len)
- {
- int i;
- int addr=0;
- const int bytecount=16;
- fprintf(stderr, "hexdump(): %i bytes\n", len);
- fflush(NULL);
- for (i=0; i<len; i++)
- {
- if ((i % bytecount) == 0)
- fprintf(stderr, "%04x\t", addr);
- fprintf(stderr, "%02x ", buffer[i]);
- if (((i+1) % bytecount) == 0)
- {
- addr += bytecount;
- fprintf(stderr, "\n");
- }
- }
- if (((i+1) % bytecount) != 0)
- fprintf(stderr, "\n");
- }
- // returns handle of PCM on success, NULL otherwise
- snd_pcm_t *setup_pcm(char *device_name, int stream)
- {
- int ret;
- snd_pcm_t *handle=NULL;
- snd_pcm_hw_params_t *hwparams=NULL;
- snd_pcm_sw_params_t *swparams=NULL;
- snd_pcm_format_t pcm_format;
- int dir;
- unsigned int pcm_rate_min, pcm_rate_max, pcm_rate, pcm_rate_real;
- snd_pcm_uframes_t pcm_period_size_min, pcm_period_size_max, pcm_period_size;
- snd_pcm_uframes_t pcm_buffer_size_min, pcm_buffer_size_max, pcm_buffer_size, pcm_buffer_size_real;
- snd_pcm_uframes_t start_threshold, stop_threshold;
- snd_pcm_uframes_t avail_min;
- if ((stream != SND_PCM_STREAM_PLAYBACK) && (stream != SND_PCM_STREAM_CAPTURE))
- {
- fprintf(stderr, "invalid stream\n");
- return NULL;
- }
- assert(device_name);
- // open sound card
- if ((ret=snd_pcm_open(&handle, device_name, stream, SND_PCM_NONBLOCK))<0)
- //if ((ret=snd_pcm_open(&handle, device_name, stream, 0))<0)
- {
- fprintf(stderr, "could not open PCM: %s\n", snd_strerror(ret));
- return NULL;
- }
- // auto-alloc and fill hardware params
- snd_pcm_hw_params_alloca(&hwparams);
- snd_pcm_hw_params_any(handle, hwparams);
- // set access format
- snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
- // set audio format
- pcm_format=SND_PCM_FORMAT_S16_LE;
- snd_pcm_hw_params_set_format (handle, hwparams, pcm_format);
- printf("PCM format is %s, %s, %s with %i bits\n",
- snd_pcm_format_signed(pcm_format)? "signed":"unsigned",
- snd_pcm_format_linear(pcm_format)? "linear":"not linear",
- snd_pcm_format_little_endian (pcm_format)? "LE":"BE",
- snd_pcm_format_width(pcm_format));
- // set channels
- snd_pcm_hw_params_set_channels (handle, hwparams, 1);
- // get minimum and maximum supported rates
- dir=0;
- if ((ret=snd_pcm_hw_params_get_rate_min(hwparams, &pcm_rate_min, &dir))<0)
- {
- fprintf(stderr, "could not get minimum supported rate: %s\n", snd_strerror(ret));
- return NULL;
- }
- dir=0;
- if ((ret=snd_pcm_hw_params_get_rate_max(hwparams, &pcm_rate_max, &dir))<0)
- {
- fprintf(stderr, "could not get maximum supported rate: %s\n", snd_strerror(ret));
- return NULL;
- }
- printf("PCM rate %u - %u Hz\n", pcm_rate_min, pcm_rate_max);
- // check rate
- pcm_rate=8000;
- if ((pcm_rate<pcm_rate_min || pcm_rate>pcm_rate_max))
- {
- fprintf(stderr, "PCM rate out of range\n");
- return NULL;
- }
- // check if exact rate is available and set up rate
- dir=0;
- if ((ret=snd_pcm_hw_params_test_rate(handle, hwparams, pcm_rate, dir))<0)
- {
- dir=0;
- pcm_rate_real=pcm_rate;
- if ((ret=snd_pcm_hw_params_set_rate_near(handle, hwparams, &pcm_rate_real, &dir))<0)
- {
- fprintf(stderr, "could not set nearest PCM rate: %s\n", snd_strerror(ret));
- return NULL;
- }
- fprintf(stderr, "exact rate of %u Hz not possible, using nearest possible rate of %u Hz instead\n", pcm_rate, pcm_rate_real);
- pcm_rate=pcm_rate_real;
- }
- else
- {
- dir=0;
- if ((ret=snd_pcm_hw_params_set_rate(handle, hwparams, pcm_rate, dir))<0)
- {
- fprintf(stderr, "could not set up PCM rate: %s\n", snd_strerror(ret));
- return NULL;
- }
- }
- // get minimum and maximum supported period size
- dir=0;
- if ((ret=snd_pcm_hw_params_get_period_size_min(hwparams, &pcm_period_size_min, &dir))<0)
- {
- fprintf(stderr, "could not get minimum supported period size: %s\n", snd_strerror(ret));
- return NULL;
- }
- dir=0;
- if ((ret=snd_pcm_hw_params_get_period_size_max(hwparams, &pcm_period_size_max, &dir))<0)
- {
- fprintf(stderr, "could not get maximum supported period size: %s\n", snd_strerror(ret));
- return NULL;
- }
- printf("PCM period size: %lu - %lu\n", pcm_period_size_min, pcm_period_size_max);
- // check period size
- pcm_period_size=160;
- if ((pcm_period_size<pcm_period_size_min || pcm_period_size>pcm_period_size_max))
- {
- fprintf(stderr, "PCM period size out of range\n");
- return NULL;
- }
- // check if exact period size is available
- dir=0;
- if ((ret=snd_pcm_hw_params_test_period_size(handle, hwparams, pcm_period_size, dir))<0)
- {
- fprintf(stderr, "requested period size not available: %s\n", snd_strerror(ret));
- return NULL;
- }
- // set up period size
- dir=0;
- if ((ret=snd_pcm_hw_params_set_period_size(handle, hwparams, pcm_period_size, dir))<0)
- {
- fprintf(stderr, "could not set up PCM rate: %s\n", snd_strerror(ret));
- return NULL;
- }
- // get minimum and maximum supported buffer size
- if ((ret=snd_pcm_hw_params_get_buffer_size_min(hwparams, &pcm_buffer_size_min))<0)
- {
- fprintf(stderr, "could not get minimum supported buffer size: %s\n", snd_strerror(ret));
- return NULL;
- }
- if ((ret=snd_pcm_hw_params_get_buffer_size_max(hwparams, &pcm_buffer_size_max))<0)
- {
- fprintf(stderr, "could not get maximum supported buffer size: %s\n", snd_strerror(ret));
- return NULL;
- }
- printf("PCM buffer size: %lu - %lu\n", pcm_buffer_size_min, pcm_buffer_size_max);
- // check buffer size
- // buffer size: 2*period_size with given format and 1 channel (mono)
- pcm_buffer_size=pcm_period_size*snd_pcm_format_size(pcm_format, 1)*2;
- printf("calculated buffer size: %lu\n", pcm_buffer_size);
- if ((pcm_buffer_size<pcm_buffer_size_min || pcm_buffer_size>pcm_buffer_size_max))
- {
- fprintf(stderr, "PCM period size out of range\n");
- return NULL;
- }
- // check if exact buffer size is available and set up buffer size
- if ((ret=snd_pcm_hw_params_test_buffer_size(handle, hwparams, pcm_buffer_size))<0)
- {
- pcm_buffer_size_real=pcm_buffer_size;
- if ((ret=snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &pcm_buffer_size_real))<0)
- {
- fprintf(stderr, "could not set nearest buffer size: %s\n", snd_strerror(ret));
- return NULL;
- }
- fprintf(stderr, "exact buffer size of %lu bytes not possible, using nearest possible buffer size %lu instead\n",
- pcm_buffer_size, pcm_buffer_size_real);
- pcm_buffer_size=pcm_buffer_size_real;
- }
- else
- {
- if ((ret=snd_pcm_hw_params_set_buffer_size(handle, hwparams, pcm_buffer_size))<0)
- {
- fprintf(stderr, "could not set PCM buffer size: %s\n", snd_strerror(ret));
- return NULL;
- }
- }
- // set up configured hw params
- if ((ret=snd_pcm_hw_params(handle, hwparams))<0)
- {
- fprintf(stderr, "could not set up hardware params: %s\n", snd_strerror(ret));
- return NULL;
- }
- // alloc and fill software params
- snd_pcm_sw_params_alloca (&swparams);
- snd_pcm_sw_params_current (handle, swparams);
- if ((ret=snd_pcm_sw_params_get_avail_min(swparams, &avail_min))<0)
- {
- fprintf(stderr, "could not get current avail_min setting: %s\n", snd_strerror(ret));
- return NULL;
- }
- printf("avail min: %lu\n", avail_min);
- // when to consider the device to be ready for the next data transfer operation
- if ((ret=snd_pcm_sw_params_set_avail_min(handle, swparams, pcm_period_size))<0)
- {
- fprintf(stderr, "could not set avail_min: %s\n", snd_strerror(ret));
- return NULL;
- }
- // start threshold
- //start_threshold = (double)pcm_rate * (1/1000000);
- start_threshold = pcm_period_size;
- printf("start threshold: %lu frames\n", start_threshold);
- if ((ret=snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold))<0)
- {
- fprintf(stderr, "could not set start threshold: %s\n", snd_strerror(ret));
- return NULL;
- }
- // stop threshold
- stop_threshold=2*pcm_period_size;
- printf("stop threshold: %lu frames\n", stop_threshold);
- if ((ret=snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold))<0)
- {
- fprintf(stderr, "could not set stop threshold: %s\n", snd_strerror(ret));
- return NULL;
- }
- // set software params
- if ((ret=snd_pcm_sw_params(handle, swparams))<0)
- {
- fprintf(stderr, "could not set sw params: %s\n", snd_strerror(ret));
- return NULL;
- }
- return handle;
- }
- int main(int argc, char **argv)
- {
- struct sched_param schp;
- char *device_name=NULL;
- snd_pcm_t *capture_device=NULL, *playback_device=NULL;
- int capture_pollfd_count, playback_pollfd_count, pollfd_count;
- int ret;
- struct pollfd poll_fds[2];
- uint8_t buffer[160*2];
- int i;
- snd_pcm_state_t state;
- snd_pcm_t *device_handle;
- unsigned short revents;
- if (argc != 2)
- {
- printf("Usage: %s <audio device name>\n", argv[0]);
- exit(EXIT_FAILURE);
- }
- device_name=argv[1];
- // try to get high scheduler priority
- memset(&schp, 0, sizeof (schp));
- schp.sched_priority = sched_get_priority_max(SCHED_FIFO);
- if (sched_setscheduler (0, SCHED_FIFO, &schp)<0)
- {
- perror("could not sched_setscheduler");
- }
- // open and configure capture device
- if ((capture_device=setup_pcm(device_name, SND_PCM_STREAM_CAPTURE))==NULL)
- {
- fprintf(stderr, "could not open and set up capture device %s\n", device_name);
- exit(EXIT_FAILURE);
- }
- if ((playback_device=setup_pcm(device_name, SND_PCM_STREAM_PLAYBACK))==NULL)
- {
- fprintf(stderr, "could not open and set up capture device %s\n", device_name);
- exit(EXIT_FAILURE);
- }
- /*
- // try to link capture and playback device so that they start/stop/prepare in sync
- if ((linked=snd_pcm_link(capture_device, playback_device))<0)
- {
- fprintf(stderr, "could not link capture and playback device: %s\n", snd_strerror(linked));
- }
- */
- // get poll info
- capture_pollfd_count=snd_pcm_poll_descriptors_count(capture_device);
- playback_pollfd_count=snd_pcm_poll_descriptors_count(playback_device);
- pollfd_count=capture_pollfd_count+playback_pollfd_count;
- printf("capture fds: %i, playback fds: %i\n", capture_pollfd_count, playback_pollfd_count);
- assert(capture_pollfd_count==1 && playback_pollfd_count==1);
- // set up poll struct
- memset(poll_fds, 0x00, sizeof(poll_fds));
- snd_pcm_poll_descriptors(capture_device, &poll_fds[0], capture_pollfd_count);
- snd_pcm_poll_descriptors(playback_device, &poll_fds[1], playback_pollfd_count);
- printf("capture poll fd: %i, playback poll fd: %i\n", poll_fds[0].fd, poll_fds[1].fd);
- printf("capture struct: fd: %i, events: %s%s%s, revents: %i\n",
- poll_fds[0].fd,
- (poll_fds[0].events & POLLIN) ? "POLLIN ":"",
- (poll_fds[0].events & POLLOUT) ? "POLLOUT ":"",
- (poll_fds[0].events & POLLERR) ? "POLLERR ":"",
- poll_fds[0].revents);
- printf("playback struct: fd: %i, events: %s%s%s, revents: %i\n",
- poll_fds[1].fd,
- (poll_fds[1].events & POLLIN) ? "POLLIN ":"",
- (poll_fds[1].events & POLLOUT) ? "POLLOUT ":"",
- (poll_fds[1].events & POLLERR) ? "POLLERR ":"",
- poll_fds[1].revents);
- // prepare capture PCM
- if ((ret=snd_pcm_prepare(capture_device))<0)
- {
- fprintf(stderr, "could not prepare capture device: %s\n", snd_strerror(ret));
- exit(EXIT_FAILURE);
- }
- // prepare playback PCM
- if ((ret=snd_pcm_prepare(playback_device))<0)
- {
- fprintf(stderr, "could not prepare playback device: %s\n", snd_strerror(ret));
- exit(EXIT_FAILURE);
- }
- // start capture device
- if ((ret=snd_pcm_start(capture_device))<0)
- {
- fprintf(stderr, "could not start capture device: %s\n", snd_strerror(ret));
- exit(EXIT_FAILURE);
- }
- memset(buffer, 0x00, sizeof(buffer));
- // read samples from capture device and write them to the playback device
- while (1)
- {
- // poll on both devices
- poll(poll_fds, pollfd_count, -1);
- for (i=0; i<pollfd_count; i++)
- {
- if (i==0)
- device_handle=capture_device;
- else
- device_handle=playback_device;
- // get demangled revent
- if ((ret=snd_pcm_poll_descriptors_revents(device_handle, &poll_fds[i], 1, &revents))<0)
- {
- fprintf(stderr, "could not snd_pcm_poll_descriptors_revents: %s\n", snd_strerror(ret));
- exit(EXIT_FAILURE);
- }
- printf("demangled poll: %s%s%s on %s device\n",
- (revents & POLLERR)? "POLLERR ":"",
- (revents & POLLIN)? "POLLIN ":"",
- (revents & POLLOUT)? "POLLOUT ":"",
- (i==0)? "capture":"playback");
- // check if there was an error
- if (revents & POLLERR)
- {
- state=snd_pcm_state(device_handle);
- switch(state)
- {
- case SND_PCM_STATE_XRUN:
- fprintf(stderr, "XRUN on %s device\n",
- (poll_fds[i].revents & POLLOUT) ? "playback" : "capture");
- // restart
- snd_pcm_prepare(device_handle);
- snd_pcm_start(device_handle);
- break;
- default:
- fprintf(stderr, "unknown state (%i) on %s device\n",
- state, (poll_fds[i].revents & POLLOUT) ? "playback" : "capture");
- }
- }
- // check for new input data
- if (revents & POLLIN)
- {
- if ((ret=snd_pcm_readi(capture_device, buffer, 160))<0)
- {
- if (ret == -EPIPE)
- {
- fprintf(stderr, "XRUN while reading from capture device: %s\n", snd_strerror(ret));
- snd_pcm_prepare(capture_device);
- snd_pcm_start(capture_device);
- }
- else
- {
- fprintf(stderr, "could not read from capture device: %s\n", snd_strerror(ret));
- exit(EXIT_FAILURE);
- }
- }
- printf("read %i frames\n", ret);
- assert(ret==160);
- hexdump(buffer, ret*2);
- }
- // check if we can write the buffer to the playback device
- if (revents & POLLOUT)
- {
- if ((ret=snd_pcm_writei(playback_device, buffer, 160))<0)
- {
- if (ret == -EPIPE)
- {
- fprintf(stderr, "XRUN while writing to playback device: %s\n", snd_strerror(ret));
- snd_pcm_prepare(capture_device);
- snd_pcm_start(capture_device);
- }
- else
- {
- fprintf(stderr, "could not write to playback device: %s\n", snd_strerror(ret));
- exit(EXIT_FAILURE);
- }
- }
- printf("wrote %i frames\n", ret);
- assert(ret==160);
- }
- }
- } // while(1)
- // never reached
- exit(EXIT_SUCCESS);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement