This is a sample application that captures (i.e., records) audio data.
For information about using this utility, see waverec in the QNX Neutrino Utilities Guide.
/* * $QNXLicenseC: * Copyright 2016, QNX Software Systems. All Rights Reserved. * * You must obtain a written license from and pay applicable license fees to QNX * Software Systems before you may reproduce, modify or distribute this software, * or any work that includes all or part of this software. Free development * licenses are available for evaluation and non-commercial purposes. For more * information visit http://licensing.qnx.com or email licensing@qnx.com. * * This file may contain contributions from others. Please review this entire * file for other proprietary rights or license notices, as well as the QNX * Development Suite License Guide at http://licensing.qnx.com/license-guide/ * for other information. * $ */ #include <errno.h> #include <fcntl.h> #include <gulliver.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <sys/ioctl.h> #include <sys/select.h> #include <sys/stat.h> #include <sys/termio.h> #include <sys/types.h> #include <unistd.h> #include <limits.h> #include <ctype.h> #include <sys/asoundlib.h> /* *INDENT-OFF* */ struct { char riff_id[4]; uint32_t wave_len; struct { char fmt_id[8]; uint32_t fmt_len; struct { uint16_t format_tag; uint16_t voices; uint32_t rate; uint32_t char_per_sec; uint16_t block_align; uint16_t bits_per_sample; } fmt; struct { char data_id[4]; uint32_t data_len; } data; } wave; } riff_hdr = { {'R', 'I', 'F', 'F' }, sizeof (riff_hdr.wave), { {'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' }, sizeof (riff_hdr.wave.fmt), { 1, 0, 0, 0, 0, 0 }, { {'d', 'a', 't', 'a' }, 0, } } }; /* *INDENT-ON* */ static snd_mixer_t *mixer_handle = NULL; static snd_pcm_t *pcm_handle = NULL; static snd_pcm_channel_params_t pp; static char *mSampleBfr1 = NULL; static FILE *file1 = NULL; static int stdin_raw = 0; static int dev_raw (int fd) { struct termios termios_p; if (tcgetattr (fd, &termios_p)) return (-1); termios_p.c_cc[VMIN] = 1; termios_p.c_cc[VTIME] = 0; termios_p.c_lflag &= ~(ICANON | ECHO | ISIG); return (tcsetattr (fd, TCSANOW, &termios_p)); } static int dev_unraw (int fd) { struct termios termios_p; if (tcgetattr (fd, &termios_p)) return (-1); termios_p.c_lflag |= (ICANON | ECHO | ISIG); return (tcsetattr (fd, TCSAFLUSH, &termios_p)); } static void cleanup(void) { if (stdin_raw) dev_unraw (fileno (stdin)); if (mSampleBfr1) free(mSampleBfr1); if (mixer_handle) snd_mixer_close (mixer_handle); if (file1) fclose(file1); if (pcm_handle) snd_pcm_close (pcm_handle); } static void cleanup_and_exit(int exit_code) { cleanup(); exit(exit_code); } //***************************************************************************** /* *INDENT-OFF* */ #ifdef __USAGE %C [Options] wavfile Options: -a[card#:]<dev#> the card & device number to record from OR -a[name] the symbolic name of the device to record from -b <size> Sample size (8, 16, 24, 32) -m record in mono (stereo default) -n <voices> the number of voices to record (2 voices default, stereo) -r <rate> record at rate (44100 default | 48000 44100 22050 11025) -t <sec> seconds to record (5 seconds default) -f <frag_size> requested fragment size -v verbosity -c <args>[,args ...] voice matrix configuration -x use mmap interface -i <0|1> Interleave samples (default: 1) -R <value> SRC rate method (0 = linear interpolation, 1 = 7-pt kaiser windowed, 2 = 20-pt remez) -z<num_frags> requested number of fragments -y Nonblocking mode Note: If both 'm' and 'n' are specified in commandline, the one specified later will be used Args: 1=<hw_channel_bitmask> hardware channel bitmask for application voice 1 2=<hw_channel_bitmask> hardware channel bitmask for application voice 2 3=<hw_channel_bitmask> hardware channel bitmask for application voice 3 4=<hw_channel_bitmask> hardware channel bitmask for application voice 4 5=<hw_channel_bitmask> hardware channel bitmask for application voice 5 6=<hw_channel_bitmask> hardware channel bitmask for application voice 6 7=<hw_channel_bitmask> hardware channel bitmask for application voice 7 8=<hw_channel_bitmask> hardware channel bitmask for application voice 8 #endif /* *INDENT-ON* */ //***************************************************************************** volatile int end = 0; static void sig_handler( int sig_no ) { end = 1; return; } static const char * why_failed ( int why_failed ) { switch (why_failed) { case SND_PCM_PARAMS_BAD_MODE: return ("Bad Mode Parameter"); case SND_PCM_PARAMS_BAD_START: return ("Bad Start Parameter"); case SND_PCM_PARAMS_BAD_STOP: return ("Bad Stop Parameter"); case SND_PCM_PARAMS_BAD_FORMAT: return ("Bad Format Parameter"); case SND_PCM_PARAMS_BAD_RATE: return ("Bad Rate Parameter"); case SND_PCM_PARAMS_BAD_VOICES: return ("Bad Vocies Parameter"); case SND_PCM_PARAMS_NO_CHANNEL: return ("No Channel Available"); default: return ("Unknown Error"); } return ("No Error"); } int main (int argc, char **argv) { int i, j; int card = -1; int dev = 0; int ret; unsigned int mSamples; int mSampleRate; int mSampleChannels; int mSampleBits; int mSampleBytes; int mSampleTime; int fragsize = -1; int num_frags = -1; int verbose = 0; int mode = SND_PCM_OPEN_CAPTURE; int rtn; int mixer_fd = 0; int pcm_fd; snd_pcm_channel_info_t pi; snd_mixer_group_t group; snd_pcm_channel_setup_t setup; int bsize, N = 0, c; #define MAX_VOICES 8 uint32_t voice_mask[MAX_VOICES] = { 0 }; struct { snd_pcm_chmap_t map; unsigned int pos[32]; } map; snd_pcm_voice_conversion_t voice_conversion; int voice_override = 0; char *sub_opts, *sub_opts_copy, *value; char *dev_opts[] = { #define CHN1 0 "1", #define CHN2 1 "2", #define CHN3 2 "3", #define CHN4 3 "4", #define CHN5 4 "5", #define CHN6 5 "6", #define CHN7 6 "7", #define CHN8 7 "8", NULL }; char name[_POSIX_PATH_MAX] = { 0 }; int interleave = 1; fd_set rfds, ofds; int use_mmap = 0; int rate_method = 0; snd_pcm_filter_t pevent; mSampleRate = 44100; mSampleChannels = 2; mSampleBits = 16; mSampleBytes = 2; mSampleTime = 5; while ((c = getopt (argc, argv, "b:a:f:mn:r:t:vc:xi:R:z:y")) != EOF) { switch (c) { case 'b': mSampleBits = atoi (optarg); if (mSampleBits != 8 && mSampleBits != 16 && mSampleBits != 24 && mSampleBits != 32) { fprintf(stderr, "Invalid sample size, must be one of 8, 16, 24, 32\n"); return (EXIT_FAILURE); } mSampleBytes = mSampleBits/8; break; case 'a': if (strchr (optarg, ':')) { card = atoi (optarg); dev = atoi (strchr (optarg, ':') + 1); } else if (isalpha (optarg[0]) || optarg[0] == '/') strcpy (name, optarg); else dev = atoi (optarg); if (name[0] != '\0') printf ("Using device /dev/snd/%s\n", name); else printf ("Using card %d device %d \n", card, dev); break; case 'f': fragsize = atoi (optarg); break; case 'i': interleave = atoi(optarg); if (interleave <= 0) interleave = 0; else interleave = 1; break; case 'm': mSampleChannels = 1; break; case 'n': mSampleChannels = atoi (optarg); break; case 'r': mSampleRate = atoi (optarg); break; case 't': mSampleTime = atoi (optarg); break; case 'v': verbose = 1; break; case 'c': sub_opts = sub_opts_copy = strdup (optarg); if (sub_opts == NULL) { perror("Cannot allocate sub_opts"); return (EXIT_FAILURE); } while (*sub_opts != '\0') { int channel = getsubopt (&sub_opts, dev_opts, &value); if( (channel >= 0) && (channel < MAX_VOICES) && value ) { voice_mask[channel] = strtoul (value, NULL, 0); } else { fprintf (stderr, "Invalid channel map specified\n"); free(sub_opts_copy); return (EXIT_FAILURE); } } free(sub_opts_copy); voice_override = 1; break; case 'x': use_mmap = 1; break; case 'R': rate_method = atoi(optarg); if (rate_method < 0 || rate_method > 2) { rate_method = 0; printf("Invalid rate method, using method 0\n"); } break; case 'z': num_frags = atoi (optarg) - 1; break; case 'y': mode |= SND_PCM_OPEN_NONBLOCK; break; default: fprintf(stderr, "Invalid option -%c\n", c); return (EXIT_FAILURE); } } if (optind >= argc) { fprintf(stderr, "no file specified\n"); return (EXIT_FAILURE); } if (name[0] != '\0') { snd_pcm_info_t info; if ((rtn = snd_pcm_open_name (&pcm_handle, name, mode)) < 0) { fprintf(stderr, "snd_pcm_open_name failed - %s\n", snd_strerror(rtn)); return (EXIT_FAILURE); } rtn = snd_pcm_info (pcm_handle, &info); card = info.card; } else { if (card == -1) { if ((rtn = snd_pcm_open_preferred (&pcm_handle, &card, &dev, mode)) < 0) { fprintf(stderr, "snd_pcm_open_preferred failed - %s\n", snd_strerror(rtn)); return (EXIT_FAILURE); } } else { if ((rtn = snd_pcm_open (&pcm_handle, card, dev, mode)) < 0) { fprintf(stderr, "snd_pcm_open failed - %s\n", snd_strerror(rtn)); return (EXIT_FAILURE); } } } if ((file1 = fopen (argv[optind], "w")) == 0) { perror("file open failed"); cleanup_and_exit(EXIT_FAILURE); } if( mSampleTime == 0 ) { mSamples = 0xFFFFFFFF - sizeof(riff_hdr) + 8; } else { mSamples = mSampleRate * mSampleChannels * mSampleBytes * mSampleTime; } riff_hdr.wave.fmt.voices = ENDIAN_LE16 (mSampleChannels); riff_hdr.wave.fmt.rate = ENDIAN_LE32 (mSampleRate); riff_hdr.wave.fmt.char_per_sec = ENDIAN_LE32 (mSampleRate * mSampleChannels * mSampleBytes); riff_hdr.wave.fmt.block_align = ENDIAN_LE16 (mSampleChannels * mSampleBytes); riff_hdr.wave.fmt.bits_per_sample = ENDIAN_LE16 (mSampleBits); riff_hdr.wave.data.data_len = ENDIAN_LE32 (mSamples); riff_hdr.wave_len = ENDIAN_LE32 (mSamples + sizeof (riff_hdr) - 8); fwrite (&riff_hdr, 1, sizeof (riff_hdr), file1); printf ("SampleRate = %d, Channels = %d, SampleBits = %d, SampleBytes = %d\n", mSampleRate, mSampleChannels, mSampleBits, mSampleBytes); /* Enable PCM events */ pevent.enable = (1<<SND_PCM_EVENT_OVERRUN); snd_pcm_set_filter(pcm_handle, SND_PCM_CHANNEL_CAPTURE, &pevent); if (use_mmap) { snd_pcm_plugin_set_enable (pcm_handle, PLUGIN_MMAP); } memset (&pi, 0, sizeof (pi)); pi.channel = SND_PCM_CHANNEL_CAPTURE; if ((rtn = snd_pcm_plugin_info (pcm_handle, &pi)) < 0) { fprintf (stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror (rtn)); cleanup_and_exit(EXIT_FAILURE); } memset (&pp, 0, sizeof (pp)); pp.mode = SND_PCM_MODE_BLOCK; pp.channel = SND_PCM_CHANNEL_CAPTURE; pp.start_mode = SND_PCM_START_DATA; pp.stop_mode = SND_PCM_STOP_STOP; pp.time = 1; pp.buf.block.frag_size = pi.max_fragment_size; if (fragsize != -1) pp.buf.block.frag_size = fragsize; pp.buf.block.frags_max = num_frags; pp.buf.block.frags_min = 1; pp.format.interleave = interleave; pp.format.rate = mSampleRate; pp.format.voices = mSampleChannels; switch (mSampleBits) { case 8: pp.format.format = SND_PCM_SFMT_U8; break; case 16: default: pp.format.format = SND_PCM_SFMT_S16_LE; break; case 24: pp.format.format = SND_PCM_SFMT_S24_LE; break; case 32: pp.format.format = SND_PCM_SFMT_S32_LE; break; } if ((rtn = snd_pcm_plugin_set_src_method(pcm_handle, rate_method)) != rate_method) { fprintf(stderr, "Failed to apply rate_method %d, using %d\n", rate_method, rtn); } if ((rtn = snd_pcm_plugin_params (pcm_handle, &pp)) < 0) { fprintf (stderr, "snd_pcm_plugin_params failed: %s - %s\n", snd_strerror (rtn), why_failed(pp.why_failed)); cleanup_and_exit(EXIT_FAILURE); } if (voice_override) { snd_pcm_plugin_get_voice_conversion (pcm_handle, SND_PCM_CHANNEL_CAPTURE, &voice_conversion); for(i = 0; i < MAX_VOICES; i++) { voice_conversion.matrix[i] = voice_mask[i]; } snd_pcm_plugin_set_voice_conversion (pcm_handle, SND_PCM_CHANNEL_CAPTURE, &voice_conversion); } memset (&setup, 0, sizeof (setup)); memset (&group, 0, sizeof (group)); setup.channel = SND_PCM_CHANNEL_CAPTURE; setup.mixer_gid = &group.gid; if ((rtn = snd_pcm_plugin_setup (pcm_handle, &setup)) < 0) { fprintf (stderr, "snd_pcm_plugin_setup failed: %s\n", snd_strerror (rtn)); cleanup_and_exit(EXIT_FAILURE); } printf ("Format %s \n", snd_pcm_get_format_name (setup.format.format)); printf ("Frag Size %d \n", setup.buf.block.frag_size); printf ("Total Frags %d \n", setup.buf.block.frags); printf ("Rate %d \n", setup.format.rate); bsize = setup.buf.block.frag_size; map.map.channels = 32; if((rtn = snd_pcm_query_channel_map(pcm_handle, &map.map)) == EOK) { printf("Channel map:"); if((rtn = snd_pcm_plugin_get_voice_conversion (pcm_handle, SND_PCM_CHANNEL_CAPTURE, &voice_conversion)) != EOK) { // The hardware map is the same as the voice map for( i = 0; i < map.map.channels; i ++ ) { printf(" (%d)", map.map.pos[i]); } printf("\n"); } else { // Map hardware channels according to the voice map for( i = 0; i < voice_conversion.app_voices; i ++ ) { bool printed = false; printf(" ("); for( j = 0; j < voice_conversion.hw_voices; j ++ ) { if( voice_conversion.matrix[i] & (1<<j) ) { if ( printed ) { printf(" "); } else { printed = true; } printf("%d", map.map.pos[j]); } } printf(")"); } printf("\n"); } } if (group.gid.name[0] == 0) { printf ("Mixer Pcm Group [%s] Not Set \n", group.gid.name); printf ("***>>>> Input Gain Controls Disabled <<<<*** \n"); } else { printf ("Mixer Pcm Group [%s]\n", group.gid.name); if ((rtn = snd_mixer_open (&mixer_handle, setup.mixer_card, setup.mixer_device)) < 0) { fprintf (stderr, "snd_mixer_open failed: %s\n", snd_strerror (rtn)); cleanup_and_exit(EXIT_FAILURE); } } if (tcgetpgrp (0) == getpid ()) { stdin_raw = 1; dev_raw (fileno (stdin)); } mSampleBfr1 = malloc (bsize); if ( mSampleBfr1 == NULL ) { perror("Failed to allocate pcm buffer"); cleanup_and_exit(EXIT_FAILURE); } if ((rtn = snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_CAPTURE)) < 0) { fprintf (stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rtn)); cleanup_and_exit(EXIT_FAILURE); } FD_ZERO (&rfds); FD_ZERO(&ofds); signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); while (!end && N < mSamples) { /* If we are the foreground process group associated with STDIN then include * STDIN in the fdset to handle key presses. */ if (stdin_raw) FD_SET (STDIN_FILENO, &rfds); if (mixer_handle) { /* Include the mixer_handle descriptor in the fdset to handle * mixer events. */ mixer_fd = snd_mixer_file_descriptor (mixer_handle); FD_SET (mixer_fd, &rfds); } rtn = pcm_fd = snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_CAPTURE); FD_SET (pcm_fd, &rfds); FD_SET (pcm_fd, &ofds); if (mixer_handle) rtn = max (mixer_fd, pcm_fd); if (select (rtn + 1, &rfds, NULL, &ofds, NULL) == -1) { perror("select"); break; /* break loop to exit cleanly */ } if (FD_ISSET (STDIN_FILENO, &rfds)) { c = getc (stdin); if (c != EOF) { /* Only handle volume key presses if there is a mixer group */ if (group.gid.name[0] != 0) { if ((rtn = snd_mixer_group_read (mixer_handle, &group)) < 0) fprintf (stderr, "snd_mixer_group_read failed: %s\n", snd_strerror (rtn)); switch (c) { case 'q': group.volume.names.front_left += 1; break; case 'a': group.volume.names.front_left -= 1; break; case 'w': group.volume.names.front_left += 1; group.volume.names.front_right += 1; break; case 's': group.volume.names.front_left -= 1; group.volume.names.front_right -= 1; break; case 'e': group.volume.names.front_right += 1; break; case 'd': group.volume.names.front_right -= 1; break; default: break; } if (group.volume.names.front_left > group.max) group.volume.names.front_left = group.max; if (group.volume.names.front_left < group.min) group.volume.names.front_left = group.min; if (group.volume.names.front_right > group.max) group.volume.names.front_right = group.max; if (group.volume.names.front_right < group.min) group.volume.names.front_right = group.min; if ((rtn = snd_mixer_group_write (mixer_handle, &group)) < 0) fprintf (stderr, "snd_mixer_group_write failed: %s\n", snd_strerror (rtn)); if (group.max==group.min) printf ("Volume Now at %d:%d\n", group.max, group.max); else printf ("Volume Now at %d:%d \n", 100 * (group.volume.names.front_left - group.min) / (group.max - group.min), 100 * (group.volume.names.front_right - group.min) / (group.max - group.min)); } switch (c) { case 'o': sleep(5); break; case 'f': if( (ret = snd_pcm_plugin_flush( pcm_handle, SND_PCM_CHANNEL_CAPTURE )) == 0 ) { printf("Flushing\n"); } else { fprintf(stderr, "Flush failed: %d\n", ret); } break; case 'g': if( (ret = snd_pcm_plugin_prepare( pcm_handle, SND_PCM_CHANNEL_CAPTURE )) == 0 ) { printf("Preparing\n"); } else { fprintf(stderr, "Preparing failed: %d\n", ret); } break; case 'p': if( (ret = snd_pcm_capture_pause( pcm_handle )) == 0 ) { printf("Pausing\n"); } else { fprintf(stderr, "Pause failed: %d\n", ret); } break; case 'r': if( (ret = snd_pcm_capture_resume( pcm_handle )) == 0 ) { printf("Resuming\n"); } else { fprintf(stderr, "Resume failed: %d\n", ret); } break; case 3: //Ctrl-C case 27: // Escape end = 1; break; case 'z': printf("delaying 500ms\n"); delay(500); break; default: break; } } else { cleanup_and_exit(EXIT_SUCCESS); } } if (mixer_handle && FD_ISSET (mixer_fd, &rfds)) { snd_mixer_callbacks_t callbacks = { 0, 0, 0, 0 }; snd_mixer_read (mixer_handle, &callbacks); } if (FD_ISSET (pcm_fd, &rfds)) { snd_pcm_channel_status_t status; int read = 0; read = snd_pcm_plugin_read (pcm_handle, mSampleBfr1, bsize); if (verbose) printf ("bytes read = %d, bsize = %d \n", read, bsize); if (read < bsize) { memset (&status, 0, sizeof (status)); status.channel = SND_PCM_CHANNEL_CAPTURE; if (snd_pcm_plugin_status (pcm_handle, &status) < 0) { fprintf (stderr, "Capture channel status error\n"); cleanup_and_exit(EXIT_FAILURE); } if (status.status == SND_PCM_STATUS_CHANGE || status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_OVERRUN) { if (status.status == SND_PCM_STATUS_CHANGE) { fprintf(stderr, "change: capture channel capability change\n"); if (snd_pcm_plugin_params (pcm_handle, &pp) < 0) { fprintf (stderr, "Capture channel snd_pcm_plugin_params error\n"); cleanup_and_exit(EXIT_FAILURE); } } if (status.status == SND_PCM_STATUS_OVERRUN) { fprintf(stderr, "overrun: capture channel\n"); } if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_CAPTURE) < 0) { fprintf (stderr, "Capture channel prepare error\n"); cleanup_and_exit(EXIT_FAILURE); } } else if (status.status == SND_PCM_STATUS_ERROR) { fprintf(stderr, "error: capture channel failure\n"); cleanup_and_exit(EXIT_FAILURE); } else if (status.status == SND_PCM_STATUS_PREEMPTED) { fprintf(stderr, "error: capture channel preempted\n"); cleanup_and_exit(EXIT_FAILURE); } } else { fwrite (mSampleBfr1, 1, read, file1); N += read; } } if (FD_ISSET (pcm_fd, &ofds)) { snd_pcm_event_t event; if ((rtn = snd_pcm_channel_read_event (pcm_handle, SND_PCM_CHANNEL_CAPTURE, &event)) == EOK) { switch (event.type) { case SND_PCM_EVENT_OVERRUN: printf("Overrun event received\n"); break; default: printf("Unknown PCM event type for capture - %d\n", event.type); break; } } else printf("snd_pcm_channel_read_event() failed with %d\n", rtn); } } printf("Exiting...\n"); /* Update wave header with actual length of audio captured */ riff_hdr.wave.data.data_len = ENDIAN_LE32 (N); riff_hdr.wave_len = ENDIAN_LE32 (N + sizeof (riff_hdr) - 8); fseek(file1, 0, SEEK_SET); fwrite (&riff_hdr, 1, sizeof (riff_hdr), file1); snd_pcm_plugin_flush (pcm_handle, SND_PCM_CHANNEL_CAPTURE); cleanup(); return(EXIT_SUCCESS); } #if defined(__QNXNTO__) && defined(__USESRCVERSION) #include <sys/srcversion.h> __SRCVERSION("$URL$ $Rev$") #endif