📄 aplay.c
字号:
/* * aplay.c - plays and records * * CREATIVE LABS CHANNEL-files * Microsoft WAVE-files * SPARC AUDIO .AU-files * Raw Data * * Copyright (c) by Jaroslav Kysela <perex@perex.cz> * Based on vplay program by Michael Beck * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#define _GNU_SOURCE#include <stdio.h>#include <malloc.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <getopt.h>#include <fcntl.h>#include <ctype.h>#include <errno.h>#include <limits.h>#include <time.h>#include <locale.h>#include <alsa/asoundlib.h>#include <assert.h>#include <sys/poll.h>#include <sys/uio.h>#include <sys/time.h>#include <sys/signal.h>#include <asm/byteorder.h>#include "aconfig.h"#include "gettext.h"#include "formats.h"#include "version.h"#ifndef LLONG_MAX#define LLONG_MAX 9223372036854775807LL#endif#define DEFAULT_FORMAT SND_PCM_FORMAT_U8#define DEFAULT_SPEED 8000#define FORMAT_DEFAULT -1#define FORMAT_RAW 0#define FORMAT_VOC 1#define FORMAT_WAVE 2#define FORMAT_AU 3/* global data */static snd_pcm_sframes_t (*readi_func)(snd_pcm_t *handle, void *buffer, snd_pcm_uframes_t size);static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, snd_pcm_uframes_t size);static snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);static snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);enum { VUMETER_NONE, VUMETER_MONO, VUMETER_STEREO};static char *command;static snd_pcm_t *handle;static struct { snd_pcm_format_t format; unsigned int channels; unsigned int rate;} hwparams, rhwparams;static int timelimit = 0;static int quiet_mode = 0;static int file_type = FORMAT_DEFAULT;static int open_mode = 0;static snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;static int mmap_flag = 0;static int interleaved = 1;static int nonblock = 0;static u_char *audiobuf = NULL;static snd_pcm_uframes_t chunk_size = 0;static unsigned period_time = 0;static unsigned buffer_time = 0;static snd_pcm_uframes_t period_frames = 0;static snd_pcm_uframes_t buffer_frames = 0;static int avail_min = -1;static int start_delay = 0;static int stop_delay = 0;static int verbose = 0;static int vumeter = VUMETER_NONE;static int buffer_pos = 0;static size_t bits_per_sample, bits_per_frame;static size_t chunk_bytes;static int test_position = 0;static snd_output_t *log;static int fd = -1;static off64_t pbrec_count = LLONG_MAX, fdcount;static int vocmajor, vocminor;/* needed prototypes */static void playback(char *filename);static void capture(char *filename);static void playbackv(char **filenames, unsigned int count);static void capturev(char **filenames, unsigned int count);static void begin_voc(int fd, size_t count);static void end_voc(int fd);static void begin_wave(int fd, size_t count);static void end_wave(int fd);static void begin_au(int fd, size_t count);static void end_au(int fd);static const struct fmt_capture { void (*start) (int fd, size_t count); void (*end) (int fd); char *what; long long max_filesize;} fmt_rec_table[] = { { NULL, NULL, N_("raw data"), LLONG_MAX }, { begin_voc, end_voc, N_("VOC"), 16000000LL }, /* FIXME: can WAV handle exactly 2GB or less than it? */ { begin_wave, end_wave, N_("WAVE"), 2147483648LL }, { begin_au, end_au, N_("Sparc Audio"), LLONG_MAX }};#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)#define error(...) do {\ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ putc('\n', stderr); \} while (0)#else#define error(args...) do {\ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ fprintf(stderr, ##args); \ putc('\n', stderr); \} while (0)#endif static void usage(char *command){ snd_pcm_format_t k; printf(_("Usage: %s [OPTION]... [FILE]...\n""\n""-h, --help help\n"" --version print current version\n""-l, --list-devices list all soundcards and digital audio devices\n""-L, --list-pcms list device names\n""-D, --device=NAME select PCM by name\n""-q, --quiet quiet mode\n""-t, --file-type TYPE file type (voc, wav, raw or au)\n""-c, --channels=# channels\n""-f, --format=FORMAT sample format (case insensitive)\n""-r, --rate=# sample rate\n""-d, --duration=# interrupt after # seconds\n""-M, --mmap mmap stream\n""-N, --nonblock nonblocking mode\n""-F, --period-time=# distance between interrupts is # microseconds\n""-B, --buffer-time=# buffer duration is # microseconds\n"" --period-size=# distance between interrupts is # frames\n"" --buffer-size=# buffer duration is # frames\n""-A, --avail-min=# min available space for wakeup is # microseconds\n""-R, --start-delay=# delay for automatic PCM start is # microseconds \n"" (relative to buffer size if <= 0)\n""-T, --stop-delay=# delay for automatic PCM stop is # microseconds from xrun\n""-v, --verbose show PCM structure and setup (accumulative)\n""-V, --vumeter=TYPE enable VU meter (TYPE: mono or stereo)\n""-I, --separate-channels one file for each channel\n"" --disable-resample disable automatic rate resample\n"" --disable-channels disable automatic channel conversions\n"" --disable-format disable automatic format conversions\n"" --disable-softvol disable software volume control (softvol)\n"" --test-position test ring buffer position\n") , command); printf(_("Recognized sample formats are:")); for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { const char *s = snd_pcm_format_name(k); if (s) printf(" %s", s); } printf(_("\nSome of these may not be available on selected hardware\n")); printf(_("The availabled format shortcuts are:\n")); printf(_("-f cd (16 bit little endian, 44100, stereo)\n")); printf(_("-f cdr (16 bit big endian, 44100, stereo)\n")); printf(_("-f dat (16 bit little endian, 48000, stereo)\n"));}static void device_list(void){ snd_ctl_t *handle; int card, err, dev, idx; snd_ctl_card_info_t *info; snd_pcm_info_t *pcminfo; snd_ctl_card_info_alloca(&info); snd_pcm_info_alloca(&pcminfo); card = -1; if (snd_card_next(&card) < 0 || card < 0) { error(_("no soundcards found...")); return; } printf(_("**** List of %s Hardware Devices ****\n"), snd_pcm_stream_name(stream)); while (card >= 0) { char name[32]; sprintf(name, "hw:%d", card); if ((err = snd_ctl_open(&handle, name, 0)) < 0) { error("control open (%i): %s", card, snd_strerror(err)); goto next_card; } if ((err = snd_ctl_card_info(handle, info)) < 0) { error("control hardware info (%i): %s", card, snd_strerror(err)); snd_ctl_close(handle); goto next_card; } dev = -1; while (1) { unsigned int count; if (snd_ctl_pcm_next_device(handle, &dev)<0) error("snd_ctl_pcm_next_device"); if (dev < 0) break; snd_pcm_info_set_device(pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { if (err != -ENOENT) error("control digital audio info (%i): %s", card, snd_strerror(err)); continue; } printf(_("card %i: %s [%s], device %i: %s [%s]\n"), card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info), dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo)); count = snd_pcm_info_get_subdevices_count(pcminfo); printf( _(" Subdevices: %i/%i\n"), snd_pcm_info_get_subdevices_avail(pcminfo), count); for (idx = 0; idx < (int)count; idx++) { snd_pcm_info_set_subdevice(pcminfo, idx); if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { error("control digital audio playback info (%i): %s", card, snd_strerror(err)); } else { printf(_(" Subdevice #%i: %s\n"), idx, snd_pcm_info_get_subdevice_name(pcminfo)); } } } snd_ctl_close(handle); next_card: if (snd_card_next(&card) < 0) { error("snd_card_next"); break; } }}static void pcm_list(void){ void **hints, **n; char *name, *descr, *descr1, *io; const char *filter; if (snd_device_name_hint(-1, "pcm", &hints) < 0) return; n = hints; filter = stream == SND_PCM_STREAM_CAPTURE ? "Input" : "Output"; while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); descr = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); if (io != NULL && strcmp(io, filter) != 0) goto __end; printf("%s\n", name); if ((descr1 = descr) != NULL) { printf(" "); while (*descr1) { if (*descr1 == '\n') printf("\n "); else putchar(*descr1); descr1++; } putchar('\n'); } __end: if (name != NULL) free(name); if (descr != NULL) free(descr); if (io != NULL) free(io); n++; } snd_device_name_free_hint(hints);}static void version(void){ printf("%s: version " SND_UTIL_VERSION_STR " by Jaroslav Kysela <perex@perex.cz>\n", command);}static void signal_handler(int sig){ if (verbose==2) putchar('\n'); if (!quiet_mode) fprintf(stderr, _("Aborted by signal %s...\n"), strsignal(sig)); if (stream == SND_PCM_STREAM_CAPTURE) { if (fmt_rec_table[file_type].end) { fmt_rec_table[file_type].end(fd); fd = -1; } stream = -1; } if (fd > 1) { close(fd); fd = -1; } if (handle && sig != SIGABRT) { snd_pcm_close(handle); handle = NULL; } exit(EXIT_FAILURE);}enum { OPT_VERSION = 1, OPT_PERIOD_SIZE, OPT_BUFFER_SIZE, OPT_DISABLE_RESAMPLE, OPT_DISABLE_CHANNELS, OPT_DISABLE_FORMAT, OPT_DISABLE_SOFTVOL, OPT_TEST_POSITION};int main(int argc, char *argv[]){ int option_index; static const char short_options[] = "hnlLD:qt:c:f:r:d:MNF:A:R:T:B:vV:IPC"; static const struct option long_options[] = { {"help", 0, 0, 'h'}, {"version", 0, 0, OPT_VERSION}, {"list-devnames", 0, 0, 'n'}, {"list-devices", 0, 0, 'l'}, {"list-pcms", 0, 0, 'L'}, {"device", 1, 0, 'D'}, {"quiet", 0, 0, 'q'}, {"file-type", 1, 0, 't'}, {"channels", 1, 0, 'c'}, {"format", 1, 0, 'f'}, {"rate", 1, 0, 'r'}, {"duration", 1, 0 ,'d'}, {"mmap", 0, 0, 'M'}, {"nonblock", 0, 0, 'N'}, {"period-time", 1, 0, 'F'}, {"period-size", 1, 0, OPT_PERIOD_SIZE}, {"avail-min", 1, 0, 'A'}, {"start-delay", 1, 0, 'R'}, {"stop-delay", 1, 0, 'T'}, {"buffer-time", 1, 0, 'B'}, {"buffer-size", 1, 0, OPT_BUFFER_SIZE}, {"verbose", 0, 0, 'v'}, {"vumeter", 1, 0, 'V'}, {"separate-channels", 0, 0, 'I'}, {"playback", 0, 0, 'P'}, {"capture", 0, 0, 'C'}, {"disable-resample", 0, 0, OPT_DISABLE_RESAMPLE}, {"disable-channels", 0, 0, OPT_DISABLE_CHANNELS}, {"disable-format", 0, 0, OPT_DISABLE_FORMAT}, {"disable-softvol", 0, 0, OPT_DISABLE_SOFTVOL}, {"test-position", 0, 0, OPT_TEST_POSITION}, {0, 0, 0, 0} }; char *pcm_name = "default"; int tmp, err, c; int do_device_list = 0, do_pcm_list = 0; snd_pcm_info_t *info;#ifdef ENABLE_NLS setlocale(LC_ALL, ""); textdomain(PACKAGE);#endif snd_pcm_info_alloca(&info); err = snd_output_stdio_attach(&log, stderr, 0); assert(err >= 0); command = argv[0]; file_type = FORMAT_DEFAULT; if (strstr(argv[0], "arecord")) { stream = SND_PCM_STREAM_CAPTURE; file_type = FORMAT_WAVE; command = "arecord"; start_delay = 1; } else if (strstr(argv[0], "aplay")) { stream = SND_PCM_STREAM_PLAYBACK; command = "aplay"; } else { error(_("command should be named either arecord or aplay")); return 1; } chunk_size = -1; rhwparams.format = DEFAULT_FORMAT; rhwparams.rate = DEFAULT_SPEED; rhwparams.channels = 1; while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { switch (c) { case 'h': usage(command); return 0; case OPT_VERSION: version(); return 0; case 'l': do_device_list = 1; break; case 'L': do_pcm_list = 1; break; case 'D': pcm_name = optarg; break; case 'q': quiet_mode = 1; break; case 't': if (strcasecmp(optarg, "raw") == 0) file_type = FORMAT_RAW; else if (strcasecmp(optarg, "voc") == 0) file_type = FORMAT_VOC; else if (strcasecmp(optarg, "wav") == 0) file_type = FORMAT_WAVE; else if (strcasecmp(optarg, "au") == 0 || strcasecmp(optarg, "sparc") == 0) file_type = FORMAT_AU; else { error(_("unrecognized file format %s"), optarg); return 1; } break; case 'c': rhwparams.channels = strtol(optarg, NULL, 0); if (rhwparams.channels < 1 || rhwparams.channels > 32) { error(_("value %i for channels is invalid"), rhwparams.channels); return 1; } break; case 'f': if (strcasecmp(optarg, "cd") == 0 || strcasecmp(optarg, "cdr") == 0) { if (strcasecmp(optarg, "cdr") == 0) rhwparams.format = SND_PCM_FORMAT_S16_BE; else rhwparams.format = file_type == FORMAT_AU ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE; rhwparams.rate = 44100; rhwparams.channels = 2; } else if (strcasecmp(optarg, "dat") == 0) { rhwparams.format = file_type == FORMAT_AU ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE; rhwparams.rate = 48000; rhwparams.channels = 2; } else { rhwparams.format = snd_pcm_format_value(optarg); if (rhwparams.format == SND_PCM_FORMAT_UNKNOWN) { error(_("wrong extended format '%s'"), optarg); exit(EXIT_FAILURE); } } break; case 'r': tmp = strtol(optarg, NULL, 0); if (tmp < 300) tmp *= 1000; rhwparams.rate = tmp; if (tmp < 2000 || tmp > 192000) { error(_("bad speed value %i"), tmp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -