/*- * Copyright (c) 2008 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SND_FEEDER_FULL_MULTIFORMAT #undef SND_FEEDER_MULTIFORMAT #define SND_FEEDER_MULTIFORMAT 1 #endif #include "version.h" #include "oss_compat.h" #include "pcm.h" #include "matrix.h" #include "matrix_map.h" #include "g711.h" #include "intpcm.h" #include "waveutil.h" #define SND_USE_FXDIV #include "snd_fxdiv_gen.h" #include "feeder_compat.h" #include "feeder_rate.c" #include "feeder_stdio.c" #include "feeder_format.c" #include "feeder_matrix.c" #include "feeder_volume.c" #include "feeder_eq.c" #if !defined(AFMT_S_NE) #if defined(Z_16) || (defined(Z_SPEEX) && !defined(FLOATING_POINT)) || \ defined(Z_CG) || defined(Z_ORION) #define AFMT_S_NE AFMT_S16_NE #else #define AFMT_S_NE AFMT_S32_NE #endif #endif #ifndef Z_POLYPHASE_MAX #define Z_POLYPHASE_MAX 0 static int feeder_rate_polyphase_max = 0; #endif enum { FEEDER_IO_TYPE_UNKNOWN, FEEDER_IO_TYPE_RAW, FEEDER_IO_TYPE_WAVE, FEEDER_IO_TYPE_DSP }; struct feeder_global_opts { uint32_t bufsize; uint32_t volume; uint32_t afmt_ne; int32_t eqpreamp; int quality; int matrix_swap; int verbose; int eqtreble, eqbass; int bit_perfect; int direction; int expensive; }; struct feeder_io_opts { struct wave_info waveinfo; uint32_t afmt; int type, running, exclusive, latency; char *file; }; struct feeder_chain_desc { struct { uint32_t afmt; uint32_t channels; uint32_t rate; uint32_t maxsize; int quality; } current; struct { uint32_t afmt; uint32_t channels; uint32_t rate; uint32_t maxsize; int quality; } target; struct { int use; int treble, bass; int preamp; } eq; struct pcm_feederdesc desc; struct pcm_channel *channel; struct pcmchan_matrix m_in, m_out; uint32_t matrix_swap; uint32_t afmt_ne; int use_volume; int expensive; }; static const struct { uint32_t afmt; const char *sfmt, *alias; int prefered; } afmt_tab[] = { { AFMT_S8, "s8", NULL, 0 }, { AFMT_S16_LE, "s16le", "16", 1 }, { AFMT_S24_LE, "s24le", "24", 1 }, { AFMT_S32_LE, "s32le", "32", 1 }, { AFMT_S16_BE, "s16be", NULL, 0 }, { AFMT_S24_BE, "s24be", NULL, 0 }, { AFMT_S32_BE, "s32be", NULL, 0 }, { AFMT_U8, "u8", "8", 1 }, { AFMT_U16_LE, "u16le", NULL, 0 }, { AFMT_U24_LE, "u24le", NULL, 0 }, { AFMT_U32_LE, "u32le", NULL, 0 }, { AFMT_U16_BE, "u16be", NULL, 0 }, { AFMT_U24_BE, "u24be", NULL, 0 }, { AFMT_U32_BE, "u32be", NULL, 0 }, { AFMT_A_LAW, "alaw", NULL, 0 }, { AFMT_MU_LAW, "mulaw", "ulaw", 0 }, { AFMT_AC3, "ac3", NULL, 0 }, }; #define AFMT_TAB_SIZE ((int)(sizeof(afmt_tab) / sizeof(afmt_tab[0]))) static void sfmt_swap_sign(char *sfmt) { if (sfmt[0] == 'u') sfmt[0] = 's'; else if (sfmt[0] == 's') sfmt[0] = 'u'; } static void sfmt_swap_endian(char *sfmt) { if (strlen(sfmt) == 5) { if (sfmt[3] == 'l') sfmt[3] = 'b'; else if (sfmt[3] == 'b') sfmt[3] = 'l'; } } static uint32_t sfmt2afmt(const char *sfmt) { int i; for (i = 0; i < AFMT_TAB_SIZE; i++) { if (strcmp(sfmt, afmt_tab[i].sfmt) == 0 || (afmt_tab[i].alias != NULL && strcmp(sfmt, afmt_tab[i].alias) == 0)) return (afmt_tab[i].afmt); } return (0x00000000); } static uint32_t bit2afmt(int bit) { int i; for (i = 0; i < AFMT_TAB_SIZE; i++) { if (AFMT_BIT(afmt_tab[i].afmt) == bit && afmt_tab[i].prefered != 0) return (afmt_tab[i].afmt); } return (0x00000000); } static const char * afmt2sfmt(uint32_t afmt) { int i; for (i = 0; i < AFMT_TAB_SIZE; i++) { if (AFMT_ENCODING(afmt) == afmt_tab[i].afmt) return (afmt_tab[i].sfmt); } return (NULL); } static uint32_t afmt_swap_sign(uint32_t afmt) { const char *sfmt; char buf[8]; sfmt = afmt2sfmt(afmt); if (sfmt != NULL) { strcpy(buf, sfmt); sfmt_swap_sign(buf); return (sfmt2afmt(buf)); } return (afmt); } static uint32_t afmt_swap_endian(uint32_t afmt) { const char *sfmt; char buf[8]; sfmt = afmt2sfmt(afmt); if (sfmt != NULL) { strcpy(buf, sfmt); sfmt_swap_endian(buf); return (sfmt2afmt(buf)); } return (afmt); } static struct pcm_channel *pcmchannel = NULL; static struct feeder_global_opts g_opt; static struct feeder_io_opts in_opt, out_opt; static struct timeval tv1, tv2; static int benchmark = 0; static void feeder_benchmark_start(void) { benchmark = 1; (void)gettimeofday(&tv1, NULL); } static void feeder_benchmark_stop(void) { double elapsed; if (benchmark == 0) return; (void)gettimeofday(&tv2, NULL); benchmark = 0; elapsed = (double)(((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec)) / 1000000.0; fprintf(stderr, "\n Conversion Time: %.4f sec [%.4f x realtime]", elapsed, (double)out_opt.waveinfo.data_size / (double)(out_opt.waveinfo.rate * (out_opt.waveinfo.bit >> 3) * out_opt.waveinfo.channels) / elapsed); fprintf(stderr, "\n Output Frames: %d", out_opt.waveinfo.data_size / ((out_opt.waveinfo.bit >> 3) * out_opt.waveinfo.channels)); fprintf(stderr, "\n Buffering: bufsize=%u , total=%u bytes\n\n", g_opt.bufsize, out_opt.waveinfo.data_size); if (pcmchannel != NULL && pcmchannel->feeder != NULL) { fprintf(stderr, " Feeders:-\n"); FEEDER_DUMP(pcmchannel->feeder); fprintf(stderr, "\n"); } } static void sigint_handler(int sig) { fprintf(stderr, "\n\nAborting...\n"); feeder_benchmark_stop(); if (out_opt.type == FEEDER_IO_TYPE_WAVE && out_opt.running != 0) { out_opt.waveinfo.seekable = 1; (void)wave_header_write(&(out_opt.waveinfo)); } if (pcmchannel != NULL) chn_destroy(pcmchannel); exit(sig); } static int usage(void) { fprintf(stderr, "usage: %s [-b -B bufsize -q quality -n -h " "[-d direction] " "-e treble:bass -p eq_preamp -F -P -R roundhz -S -v left:right] " "[-t type -f format -c channels -r rate -l latency -X] infile " "[-t type -f format -c channels -r rate -l latency -X] outfile\n", getprogname()); return (EX_USAGE); } static int parse_options(int *argc, char ***argv, struct feeder_global_opts *gopt, struct feeder_io_opts *iopt) { long optl; int i, ch; optind = 1; optreset = 1; while ((ch = getopt(*argc, *argv, "B:bc:d:e:EFf:l:p:q:Qr:t:v:hnPR:SVX")) != -1) { switch (ch) { case 'b': gopt->bit_perfect = 1; break; case 'B': optl = strtol(optarg, NULL, 10); if (optl > SND_FXDIV_MAX) optl = SND_FXDIV_MAX; else if (optl < 1) optl = 1; gopt->bufsize = (uint32_t)optl; break; case 'c': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Supported channels: %d to %d, " "mono, stereo, swap\n", SND_CHN_MIN, SND_CHN_MAX); return (EX_USAGE); } else if (strcmp(optarg, "mono") == 0) optl = 1; else if (strcmp(optarg, "stereo") == 0) optl = 2; else if (strcmp(optarg, "swap") == 0) { gopt->matrix_swap = 1; if (iopt->waveinfo.channels != 0) optl = iopt->waveinfo.channels; else break; } else optl = strtol(optarg, NULL, 10); if (optl < SND_CHN_MIN || optl > SND_CHN_MAX) { warnx("total channels out of range -- %ld", optl); return (EINVAL); } iopt->waveinfo.channels = (uint32_t)optl; if (iopt->afmt & AFMT_16BIT) iopt->waveinfo.format = (iopt->waveinfo.channels > 2) ? WAVE_FORMAT_EXT : WAVE_FORMAT_PCM; break; case 'd': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Direction: play, rec\n"); return (EX_USAGE); } if (strcasecmp(optarg, "play") == 0) g_opt.direction = PCMDIR_PLAY; else if (strcasecmp(optarg, "rec") == 0) g_opt.direction = PCMDIR_REC; else { warnx("invalid direction -- %s", optarg); return (EINVAL); } break; case 'e': { int left, right; if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Bass/Treble gain: " "\n"); return (EX_USAGE); } i = sscanf(optarg, "%d:%d", &left, &right); if ((i == 1 || i == 2) && (left >= 0 && left <= 100) && (i == 1 || (right >= 0 && right <= 100))) { gopt->eqtreble = left; gopt->eqbass = (i == 1) ? left : right; } else { warnx("invalid volume -- %s", optarg); return (EINVAL); } } break; case 'E': g_opt.expensive = 1; break; case 'f': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Supported format: "); for (i = 0; i < AFMT_TAB_SIZE; i++) { fprintf(stderr, "%s%s", afmt_tab[i].sfmt, (i < (AFMT_TAB_SIZE - 1)) ? ", " : ""); } fprintf(stderr, "\n"); return (EX_USAGE); } iopt->afmt = sfmt2afmt(optarg); if (iopt->afmt == 0) { warnx("unsupported format -- %s", optarg); return (EINVAL); } iopt->waveinfo.bit = AFMT_BIT(iopt->afmt); if (iopt->afmt & AFMT_BIGENDIAN) iopt->waveinfo.endian = WAVE_BIG_ENDIAN; else iopt->waveinfo.endian = WAVE_LITTLE_ENDIAN; if (iopt->afmt == AFMT_MU_LAW) iopt->waveinfo.format = WAVE_FORMAT_ULAW; else if (iopt->afmt == AFMT_A_LAW) iopt->waveinfo.format = WAVE_FORMAT_ALAW; else if (iopt->afmt & (AFMT_24BIT | AFMT_32BIT)) iopt->waveinfo.format = WAVE_FORMAT_EXT; else if (iopt->afmt & AFMT_16BIT) iopt->waveinfo.format = (iopt->waveinfo.channels > 2) ? WAVE_FORMAT_EXT : WAVE_FORMAT_PCM; break; case 'F': feeder_rate_polyphase_max = -1; break; case 'l': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Latency (OSS \"Policy\"): 0 - 10\n"); return (EX_USAGE); } optl = strtol(optarg, NULL, 10); if (optl < 0 || optl > 10) { warnx("Latency (OSS \"Policy\") out of range" "-- %s", optarg); return (EX_USAGE); } iopt->latency = (int)optl; break; case 'p': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "EQ Preamp: -/+ %d.0dB, %d.%ddB step\n", FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV, FEEDEQ_GAIN_STEP - ((FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV) * FEEDEQ_GAIN_DIV)); return (EX_USAGE); } optl = (int32_t)feed_eq_scan_preamp_arg(optarg); if (optl < FEEDEQ_PREAMP_MIN || optl > FEEDEQ_PREAMP_MAX) { warnx("EQ Preamp out of range -- %s", optarg); return (EINVAL); } gopt->eqpreamp = (int32_t)optl; break; case 'q': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Resampling quality: %d to %d, " "zoh, linear, sinc\n", Z_QUALITY_MIN, Z_QUALITY_MAX); return (EX_USAGE); } else if (strcmp(optarg, "zoh") == 0) optl = Z_QUALITY_ZOH; else if (strcmp(optarg, "linear") == 0) optl = Z_QUALITY_LINEAR; else if (strcmp(optarg, "sinc") == 0) optl = Z_QUALITY_SINC; else optl = strtol(optarg, NULL, 10); if (optl < Z_QUALITY_MIN || optl > Z_QUALITY_MAX) { warnx("resampling quality out of range -- %ld", optl); return (EINVAL); } gopt->quality = (int)optl; break; case 'Q': gopt->verbose = 0; break; case 'r': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Supported sampling rate: " "%d to %d Hz\n", FEEDRATE_RATEMIN, FEEDRATE_RATEMAX); #ifdef Z_MASK fprintf(stderr, "(Maximum factor: %d)\n", Z_MASK); #endif return (EX_USAGE); } optl = strtol(optarg, NULL, 10); if (optl < FEEDRATE_RATEMIN || optl > FEEDRATE_RATEMAX) { warnx("sampling rate out of range -- %ld", optl); return (EINVAL); } iopt->waveinfo.rate = (uint32_t)optl; break; case 't': if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Supported file type: raw, wav, cd, dsp\n"); return (EX_USAGE); } else if (strcmp(optarg, "raw") == 0) iopt->type = FEEDER_IO_TYPE_RAW; else if (strcmp(optarg, "wav") == 0 || strcmp(optarg, "wave") == 0) iopt->type = FEEDER_IO_TYPE_WAVE; else if (strcmp(optarg, "cd") == 0) { iopt->type = FEEDER_IO_TYPE_RAW; iopt->afmt = AFMT_S16_LE; iopt->waveinfo.bit = 16; iopt->waveinfo.rate = 44100; iopt->waveinfo.channels = 2; } else if (strcmp(optarg, "dsp") == 0 || strcmp(optarg, "oss") == 0) iopt->type = FEEDER_IO_TYPE_DSP; else { warnx("unknown file type -- %s", optarg); return (EINVAL); } break; case 'v': { int left, right; if (strcmp(optarg, "help") == 0) { fprintf(stderr, "Volume +/-%%: " "\n"); return (EX_USAGE); } i = sscanf(optarg, "%d:%d", &left, &right); if ((i == 1 || i == 2) && (left >= -100 && left <= 100) && (i == 1 || (right >= -100 && right <= 100))) { gopt->volume = ((left + 100) << 8); gopt->volume |= ((i == 2) ? right : left) + 100; } else { warnx("invalid volume -- %s", optarg); return (EINVAL); } } break; case 'n': gopt->afmt_ne = 0; break; case 'P': feeder_rate_polyphase_max = INT32_MAX >> 2; break; case 'R': #if defined(Z_ROUNDHZ) && defined(Z_ROUNDHZ_MIN) && defined(Z_ROUNDHZ_MAX) if (strcmp(optarg, "help") == 0) { fprintf(stderr, "ROUNDHZ min=%d <-> max=%d\n", Z_ROUNDHZ_MIN, Z_ROUNDHZ_MAX); return (EX_USAGE); } optl = strtol(optarg, NULL, 10); if (optl < Z_ROUNDHZ_MIN || optl > Z_ROUNDHZ_MAX) { warnx("roundhz out of range -- %ld", optl); return (EINVAL); } feeder_rate_round = (int)optl; #else fprintf(stderr, "ROUNDHZ not supported.\n"); return (EX_USAGE); #endif break; case 'S': feeder_rate_polyphase_max = 0; break; case 'V': fprintf(stderr, "%s version " __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH"\n", getprogname()); return (EX_USAGE); case 'X': iopt->exclusive = O_EXCL; break; case 'h': case '?': default: return (usage()); break; } } *argc -= optind; *argv += optind; if (*argc > 0) { iopt->file = *(*argv); if (iopt->type == FEEDER_IO_TYPE_UNKNOWN && iopt->file != NULL && strlen(iopt->file) >= 5 && strcasecmp(iopt->file + strlen(iopt->file) - 4, ".wav") == 0) iopt->type = FEEDER_IO_TYPE_WAVE; } if (iopt->file == NULL) { warnx("require input/output file argument"); return (usage()); } return (0); } static int feeder_build_stdin(struct feeder_chain_desc *cdesc) { struct pcm_feeder *f; cdesc->desc.type = FEEDER_STDIO; cdesc->desc.in = cdesc->current.afmt; cdesc->desc.out = cdesc->desc.in; f = FEEDER_CREATE(feeder_stdin, &(cdesc->desc)); if (f == NULL) { warnx("failed to create feeder_stdin"); return (EX_IOERR); } if (cdesc->current.maxsize != 0 && FEEDER_SET(f, FEEDSTDIO_MAXBYTES, cdesc->current.maxsize) != 0) { warnx("failed to set maximum bytes on feeder_stdin -- %u", cdesc->current.maxsize); FEEDER_DESTROY(f); return (EX_IOERR); } return (chn_addfeeder(cdesc->channel, f)); } static int feeder_build_stdout(struct feeder_chain_desc *cdesc) { struct pcm_feeder *f; cdesc->desc.type = FEEDER_STDIO; cdesc->desc.in = cdesc->current.afmt; cdesc->desc.out = cdesc->desc.in; f = FEEDER_CREATE(feeder_stdout, &(cdesc->desc)); if (f == NULL) { warnx("could not create stdout feeder"); return (EX_IOERR); } return (chn_addfeeder(cdesc->channel, f)); } static int feeder_build_format(struct feeder_chain_desc *cdesc) { struct pcm_feeder *f; cdesc->desc.type = FEEDER_FORMAT; cdesc->desc.in = cdesc->current.afmt; cdesc->desc.out = cdesc->target.afmt; f = FEEDER_CREATE(feeder_format, &(cdesc->desc)); if (f == NULL) { warnx("failed to create feeder_format"); return (EX_IOERR); } if (FEEDER_SET(f, FEEDFORMAT_CHANNELS, cdesc->current.channels) != 0) { warnx("failed to set channels on feeder_format -- %u", cdesc->current.channels); FEEDER_DESTROY(f); return (EX_IOERR); } cdesc->current.afmt = cdesc->target.afmt; return (chn_addfeeder(cdesc->channel, f)); } static int feeder_build_formatne(struct feeder_chain_desc *cdesc, uint32_t nformat) { uint32_t oafmt, ochannels; int ret; if (nformat == 0) nformat = cdesc->afmt_ne; if (nformat == 0 || AFMT_ENCODING(cdesc->current.afmt) == nformat) return (0); oafmt = cdesc->target.afmt; ochannels = cdesc->target.channels; cdesc->target.afmt = SND_FORMAT(nformat, cdesc->current.channels, AFMT_EXTCHANNEL(cdesc->current.afmt)); ret = feeder_build_format(cdesc); if (ret != 0) return (ret); cdesc->target.afmt = oafmt; cdesc->target.channels = ochannels; return (0); } static int matrix_swap(struct pcmchan_matrix *src) { struct pcmchan_matrix dst; uint32_t i; dst = *src; dst.id = SND_CHN_MATRIX_MISC; for (i = 0; i < dst.channels; i++) dst.map[i] = src->map[src->channels - i - 1]; for (i = 0; i < (sizeof(dst.offset) / sizeof(dst.offset[0])); i++) dst.offset[i] = -1; for (i = 0; dst.map[i].type != SND_CHN_T_MAX; i++) dst.offset[dst.map[i].type] = i; *src = dst; return (0); } static int feeder_build_matrix(struct feeder_chain_desc *cdesc) { struct pcm_feeder *f; int ret; ret = feeder_build_formatne(cdesc, 0); if (ret != 0) return (ret); cdesc->desc.type = FEEDER_MATRIX; cdesc->desc.in = cdesc->current.afmt; cdesc->desc.out = SND_FORMAT(cdesc->current.afmt, cdesc->target.channels, AFMT_EXTCHANNEL(cdesc->target.afmt)); f = FEEDER_CREATE(feeder_matrix, &(cdesc->desc)); if (f == NULL) { warnx("failed to create feeder_matrix"); return (EX_IOERR); } if (feeder_matrix_setup(f, &cdesc->m_in, &cdesc->m_out) != 0) { warnx("feeder_matrix_setup() failed"); return (EX_IOERR); } cdesc->current.channels = cdesc->target.channels; cdesc->current.afmt = cdesc->desc.out; cdesc->m_in = cdesc->m_out; cdesc->matrix_swap = 0; return (chn_addfeeder(cdesc->channel, f)); } static int feeder_build_rate(struct feeder_chain_desc *cdesc) { struct pcm_feeder *f; int ret; ret = feeder_build_formatne(cdesc, 0); if (ret != 0) return (ret); cdesc->desc.type = FEEDER_RATE; cdesc->desc.in = cdesc->current.afmt; cdesc->desc.out = cdesc->desc.in; f = FEEDER_CREATE(feeder_rate, &(cdesc->desc)); if (f == NULL) { warnx("failed to create feeder_rate"); return (EX_IOERR); } if (FEEDER_SET(f, FEEDRATE_SRC, cdesc->current.rate) != 0) { warnx("failed to set source rate on feeder_rate -- %u", cdesc->current.rate); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDRATE_DST, cdesc->target.rate) != 0) { warnx("failed to set destination rate on feeder_rate " "-- %u", cdesc->target.rate); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDRATE_QUALITY, cdesc->target.quality) != 0) { warnx("failed to set resampling quality on feeder_rate " "-- %d", cdesc->target.quality); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDRATE_CHANNELS, cdesc->current.channels) != 0) { warnx("failed to set channels on feeder_rate -- %u", cdesc->current.channels); FEEDER_DESTROY(f); return (EX_IOERR); } cdesc->current.rate = cdesc->target.rate; #ifdef SND_DIAGNOSTIC g_opt.verbose = 0; #endif return (chn_addfeeder(cdesc->channel, f)); } static int feeder_build_volume(struct feeder_chain_desc *cdesc) { struct pcm_feeder *f; int ret; ret = feeder_build_formatne(cdesc, 0); if (ret != 0) return (ret); cdesc->desc.type = FEEDER_VOLUME; cdesc->desc.in = cdesc->current.afmt; cdesc->desc.out = cdesc->desc.in; f = FEEDER_CREATE(feeder_volume, &(cdesc->desc)); if (f == NULL) { warnx("failed to create feeder_volume"); return (EX_IOERR); } if (FEEDER_SET(f, FEEDVOLUME_CLASS, SND_VOL_C_PCM) != 0) { warnx("failed to set volume class on feeder_volume"); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDVOLUME_CHANNELS, cdesc->current.channels) != 0) { warnx("failed to set channels on feeder_volume -- %u", cdesc->current.channels); FEEDER_DESTROY(f); return (EX_IOERR); } ret = feeder_volume_apply_matrix(f, &cdesc->m_in); if (ret != 0) return (ret); cdesc->use_volume = 0; return (chn_addfeeder(cdesc->channel, f)); } static int feeder_build_eq(struct feeder_chain_desc *cdesc) { struct pcm_feeder *f; int ret; ret = feeder_build_formatne(cdesc, 0); if (ret != 0) return (ret); cdesc->desc.type = FEEDER_EQ; cdesc->desc.in = cdesc->current.afmt; cdesc->desc.out = cdesc->desc.in; f = FEEDER_CREATE(feeder_eq, &(cdesc->desc)); if (f == NULL) { warnx("failed to create feeder_eq"); return (EX_IOERR); } if (FEEDER_SET(f, FEEDEQ_CHANNELS, cdesc->current.channels) != 0) { warnx("failed to set channels on feeder_eq -- %u", cdesc->current.channels); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDEQ_RATE, cdesc->current.rate) != 0) { warnx("failed to set rate on feeder_eq -- %u", cdesc->current.rate); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDEQ_TREBLE, cdesc->eq.treble) != 0) { warnx("failed to set treble on feeder_eq -- %u", cdesc->eq.treble); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDEQ_BASS, cdesc->eq.bass) != 0) { warnx("failed to set bass on feeder_eq -- %u", cdesc->eq.bass); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDEQ_PREAMP, cdesc->eq.preamp) != 0) { warnx("failed to set preamp on feeder_eq -- %d", cdesc->eq.preamp); FEEDER_DESTROY(f); return (EX_IOERR); } if (FEEDER_SET(f, FEEDEQ_STATE, FEEDEQ_ENABLE) != 0) { warnx("failed to enable feeder_eq"); FEEDER_DESTROY(f); return (EX_IOERR); } cdesc->eq.use = 0; return (chn_addfeeder(cdesc->channel, f)); } static uint32_t feeder_fmtopts[] = { #if defined(SND_FEEDER_FULL_MULTIFORMAT) AFMT_S8, AFMT_U8, #endif #if defined(SND_FEEDER_MULTIFORMAT) || defined(SND_FEEDER_FULL_MULTIFORMAT) AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE, AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE, AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE, #else AFMT_S16_NE, AFMT_S32_NE, #endif 0 }; #define FEEDER_BW(c, t) ((c)->t.channels * (c)->t.rate) #define FEEDRATE_UP(c) ((c)->target.rate > (c)->current.rate) #define FEEDRATE_DOWN(c) ((c)->target.rate < (c)->current.rate) #define FEEDRATE_REQUIRED(c) (FEEDRATE_UP(c) || FEEDRATE_DOWN(c)) #define FEEDMATRIX_UP(c) ((c)->target.channels > (c)->current.channels) #define FEEDMATRIX_DOWN(c) ((c)->target.channels < (c)->current.channels) #define FEEDMATRIX_REQUIRED(c) (FEEDMATRIX_UP(c) || \ FEEDMATRIX_DOWN(c) || \ ((c)->matrix_swap != 0 && \ (c)->current.channels > 1)) #define FEEDFORMAT_REQUIRED(c) (AFMT_ENCODING((c)->current.afmt) != \ AFMT_ENCODING((c)->target.afmt)) #define FEEDVOLUME_REQUIRED(c) ((c)->use_volume != 0) #define FEEDEQ_VALIDRATE(c, t) (feeder_eq_validrate((c)->t.rate) != 0) #define FEEDEQ_ECONOMY(c) (FEEDER_BW(c, current) < FEEDER_BW(c, target)) #define FEEDEQ_REQUIRED(c) ((c)->eq.use != 0 && \ FEEDEQ_VALIDRATE(c, current)) #if defined(SND_FEEDER_FULL_MULTIFORMAT) #define FEEDFORMAT_NE_REQUIRED(c) ((c)->afmt_ne != AFMT_S_NE) #elif defined(FEEDER_MULTIFORMAT) #define FEEDFORMAT_NE_REQUIRED(c) ((c)->afmt_ne != AFMT_S_NE && \ ((c)->current.afmt & AFMT_8BIT)) #else #define FEEDFORMAT_NE_REQUIRED(c) ((c)->afmt_ne != AFMT_S_NE && \ !(cdesc.current.afmt & \ (AFMT_S16_NE | AFMT_S32_NE | \ AFMT_S_NE))) #endif static int feeder_build_chain(struct pcm_channel *c) { struct pcmchan_matrix *m; struct feeder_chain_desc cdesc; int i, left, right, center, ret; cdesc.current.afmt = feeder_matrix_default_format( SND_FORMAT(in_opt.afmt, in_opt.waveinfo.channels, AFMT_EXTCHANNEL(in_opt.afmt))); cdesc.current.channels = in_opt.waveinfo.channels; cdesc.current.rate = in_opt.waveinfo.rate; cdesc.current.quality = g_opt.quality; cdesc.current.maxsize = 0; cdesc.target.afmt = feeder_matrix_default_format( SND_FORMAT(out_opt.afmt, out_opt.waveinfo.channels, AFMT_EXTCHANNEL(out_opt.afmt))); cdesc.target.channels = out_opt.waveinfo.channels; cdesc.target.rate = out_opt.waveinfo.rate; cdesc.target.quality = g_opt.quality; cdesc.target.maxsize = 0; cdesc.channel = c; cdesc.use_volume = 0; cdesc.afmt_ne = g_opt.afmt_ne; cdesc.matrix_swap = g_opt.matrix_swap; cdesc.eq.use = (g_opt.eqtreble != -1 && g_opt.eqbass != -1) ? 1 : 0; cdesc.eq.treble = g_opt.eqtreble; cdesc.eq.bass = g_opt.eqbass; cdesc.eq.preamp = g_opt.eqpreamp; cdesc.expensive = g_opt.expensive; #if !defined(Z_MULTIFORMAT) || Z_MULTIFORMAT == 0 if (FEEDRATE_REQUIRED(&cdesc)) cdesc.afmt_ne = AFMT_S_NE; #endif /*{ static uint32_t keke[] = { SND_FORMAT(S16_LE, 1), SND_FORMAT(S16_LE, 2), SND_FORMAT(S16_LE, 4), SND_FORMAT(S32_LE, 1), SND_FORMAT(S32_LE, 2), SND_FORMAT(S32_LE, 4), SND_FORMAT(S32_LE, 6), SND_FORMAT(S32_LE, 8), 0 }; uint32_t ff, bst; ff = SND_FORMAT(U16_BE, 3); bst = chn_fmtbest(ff, keke); warnx("0x%08x -> 0x%08x", ff, bst); }*/ if (FEEDFORMAT_NE_REQUIRED(&cdesc)) { cdesc.afmt_ne = chn_fmtbest(AFMT_ENCODING(cdesc.target.afmt), feeder_fmtopts); if (cdesc.afmt_ne == 0) { warnx("chn_fmtbest failed!"); cdesc.afmt_ne = (cdesc.target.afmt & (AFMT_24BIT | AFMT_32BIT)) ? AFMT_S32_NE : AFMT_S16_NE; } } left = (g_opt.volume & 0xff00) >> 8; if (left > 100) { left = ((left - 100) * (SND_VOL_0DB_MAX - SND_VOL_0DB_PCM)) / 100; left += SND_VOL_0DB_PCM; } else if (left < 100) left = (left * SND_VOL_0DB_PCM) / 100; else left = SND_VOL_0DB_PCM; right = g_opt.volume & 0xff; if (right > 100) { right = ((right - 100) * (SND_VOL_0DB_MAX - SND_VOL_0DB_PCM)) / 100; right += SND_VOL_0DB_PCM; } else if (right < 100) right = (right * SND_VOL_0DB_PCM) / 100; else right = SND_VOL_0DB_PCM; center = (left + right) >> 1; for (i = 0; i != SND_CHN_T_MAX; i++) { if ((1 << i) & SND_CHN_LEFT_MASK) CHN_SETVOLUME(c, SND_VOL_C_PCM, i, left); else if ((1 << i) & SND_CHN_RIGHT_MASK) CHN_SETVOLUME(c, SND_VOL_C_PCM, i, right); else CHN_SETVOLUME(c, SND_VOL_C_PCM, i, center); } if (left != SND_VOL_0DB_PCM || right != SND_VOL_0DB_PCM) cdesc.use_volume = 1; bzero(&(cdesc.desc), sizeof(cdesc.desc)); if (in_opt.type == FEEDER_IO_TYPE_WAVE) cdesc.current.maxsize = in_opt.waveinfo.data_size; m = feeder_matrix_format_map(cdesc.current.afmt); if (m == NULL) return (EINVAL); cdesc.m_in = *m; m = feeder_matrix_format_map(cdesc.target.afmt); if (m == NULL) return (EINVAL); cdesc.m_out = *m; /*{ struct pcmchan_matrix xx; xx = cdesc.m_in; xx.channels[0].members &= ~SND_CHN_T_MASK_FC; warnx("%d", feeder_matrix_compare(&xx, &cdesc.m_in)); }*/ if (cdesc.matrix_swap != 0) { if (c->direction == PCMDIR_PLAY) { if (cdesc.target.channels > 1) matrix_swap(&cdesc.m_out); else cdesc.matrix_swap = 0; } else { if (cdesc.current.channels > 1) matrix_swap(&cdesc.m_in); else cdesc.matrix_swap = 0; } } #define FEEDER_BUILD(t) do { \ ret = feeder_build_##t(&cdesc); \ if (ret != 0) \ return (ret); \ } while (0) FEEDER_BUILD(stdin); if (c->direction == PCMDIR_PLAY) goto feeder_chain_pcmdir_play; else if (c->direction == PCMDIR_REC) goto feeder_chain_pcmdir_rec; else goto feeder_chain_out; feeder_chain_pcmdir_play: /* PCMDIR_PLAY */ if (FEEDMATRIX_UP(&cdesc)) { if (FEEDEQ_REQUIRED(&cdesc) && (!FEEDEQ_VALIDRATE(&cdesc, target) || (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc)))) FEEDER_BUILD(eq); if (FEEDRATE_REQUIRED(&cdesc)) FEEDER_BUILD(rate); FEEDER_BUILD(matrix); if (FEEDVOLUME_REQUIRED(&cdesc)) FEEDER_BUILD(volume); /*if (FEEDMATRIX_SWAP_REQUIRED(&cdesc)) FEEDER_BUILD(matrixswap);*/ if (FEEDEQ_REQUIRED(&cdesc)) FEEDER_BUILD(eq); } else if (FEEDMATRIX_DOWN(&cdesc)) { FEEDER_BUILD(matrix); if (FEEDVOLUME_REQUIRED(&cdesc)) FEEDER_BUILD(volume); /*if (FEEDMATRIX_SWAP_REQUIRED(&cdesc)) FEEDER_BUILD(matrixswap);*/ if (FEEDEQ_REQUIRED(&cdesc) && (!FEEDEQ_VALIDRATE(&cdesc, target) || FEEDEQ_ECONOMY(&cdesc))) FEEDER_BUILD(eq); if (FEEDRATE_REQUIRED(&cdesc)) FEEDER_BUILD(rate); if (FEEDEQ_REQUIRED(&cdesc)) FEEDER_BUILD(eq); } else { if (FEEDRATE_DOWN(&cdesc)) { if (FEEDEQ_REQUIRED(&cdesc) && !FEEDEQ_VALIDRATE(&cdesc, target)) { if (FEEDVOLUME_REQUIRED(&cdesc)) FEEDER_BUILD(volume); FEEDER_BUILD(eq); } FEEDER_BUILD(rate); } if (FEEDMATRIX_REQUIRED(&cdesc)) FEEDER_BUILD(matrix); if (FEEDVOLUME_REQUIRED(&cdesc)) FEEDER_BUILD(volume); /*if (FEEDMATRIX_SWAP_REQUIRED(&cdesc)) FEEDER_BUILD(matrixswap);*/ if (FEEDRATE_UP(&cdesc)) { if (FEEDEQ_REQUIRED(&cdesc) && !FEEDEQ_VALIDRATE(&cdesc, target)) FEEDER_BUILD(eq); FEEDER_BUILD(rate); } if (FEEDEQ_REQUIRED(&cdesc)) FEEDER_BUILD(eq); } goto feeder_chain_out; feeder_chain_pcmdir_rec: /* PCMDIR_REC */ if (FEEDMATRIX_UP(&cdesc)) { if (FEEDEQ_REQUIRED(&cdesc) && (!FEEDEQ_VALIDRATE(&cdesc, target) || (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc)))) FEEDER_BUILD(eq); if (FEEDRATE_REQUIRED(&cdesc)) FEEDER_BUILD(rate); /*if (FEEDMATRIX_SWAP_REQUIRED(&cdesc)) FEEDER_BUILD(matrixswap);*/ FEEDER_BUILD(matrix); if (FEEDVOLUME_REQUIRED(&cdesc)) FEEDER_BUILD(volume); if (FEEDEQ_REQUIRED(&cdesc)) FEEDER_BUILD(eq); } else if (FEEDMATRIX_DOWN(&cdesc)) { /*if (FEEDMATRIX_SWAP_REQUIRED(&cdesc)) FEEDER_BUILD(matrixswap);*/ FEEDER_BUILD(matrix); if (FEEDVOLUME_REQUIRED(&cdesc)) FEEDER_BUILD(volume); if (FEEDEQ_REQUIRED(&cdesc) && (!FEEDEQ_VALIDRATE(&cdesc, target) || FEEDEQ_ECONOMY(&cdesc))) FEEDER_BUILD(eq); if (FEEDRATE_REQUIRED(&cdesc)) FEEDER_BUILD(rate); if (FEEDEQ_REQUIRED(&cdesc)) FEEDER_BUILD(eq); } else { if (FEEDRATE_DOWN(&cdesc)) { if (FEEDEQ_REQUIRED(&cdesc) && !FEEDEQ_VALIDRATE(&cdesc, target)) FEEDER_BUILD(eq); FEEDER_BUILD(rate); } if (FEEDMATRIX_REQUIRED(&cdesc)) FEEDER_BUILD(matrix); /*if (FEEDMATRIX_SWAP_REQUIRED(&cdesc)) FEEDER_BUILD(matrixswap);*/ if (FEEDVOLUME_REQUIRED(&cdesc)) FEEDER_BUILD(volume); if (FEEDRATE_UP(&cdesc)) { if (FEEDEQ_REQUIRED(&cdesc) && !FEEDEQ_VALIDRATE(&cdesc, target)) FEEDER_BUILD(eq); FEEDER_BUILD(rate); } if (FEEDEQ_REQUIRED(&cdesc)) FEEDER_BUILD(eq); } feeder_chain_out: if (FEEDFORMAT_REQUIRED(&cdesc)) FEEDER_BUILD(format); FEEDER_BUILD(stdout); return (0); } int main(int argc, char **argv) { uint32_t sec1, sec8; int ret; if (0) afmt_swap_sign(0x00000000); bzero(&g_opt, sizeof(g_opt)); bzero(&in_opt, sizeof(in_opt)); bzero(&out_opt, sizeof(out_opt)); g_opt.afmt_ne = AFMT_S_NE; g_opt.bufsize = FEEDSTDIO_BUFSIZE; g_opt.quality = Z_QUALITY_SINC; g_opt.matrix_swap = 0; g_opt.verbose = 1; g_opt.volume = (100 << 8) | 100; g_opt.eqtreble = -1; g_opt.eqbass = -1; g_opt.eqpreamp = FEEDEQ_PREAMP_DEFAULT; g_opt.direction = PCMDIR_PLAY; g_opt.expensive = 0; #define IO_FILENO(x) fileno((x).waveinfo.fp) in_opt.waveinfo.fp = stdin; in_opt.latency = 5; out_opt.waveinfo.fp = stdout; out_opt.latency = 5; feeder_rate_polyphase_max = 0; ret = parse_options(&argc, &argv, &g_opt, &in_opt); if (ret != 0) return (ret); ret = parse_options(&argc, &argv, &g_opt, &out_opt); if (ret != 0) return (ret); if (argc != 1) return (usage()); if (strcmp(in_opt.file, "-") != 0) { int fd; fd = open(in_opt.file, O_RDONLY | in_opt.exclusive); if (fd == -1) { warn("open() file=%s", in_opt.file); return (EX_IOERR); } in_opt.waveinfo.fp = fdopen(fd, "r"); if (in_opt.waveinfo.fp == NULL) { warn("fdopen()"); close(fd); return (EX_IOERR); } } #define FIXUP_IO(o) do { \ if ((o).type != FEEDER_IO_TYPE_DSP) { \ struct stat st; \ int dummy = 0; \ if (fstat(IO_FILENO(o), &st) == 0 && \ S_ISCHR(st.st_mode) != 0 && \ ioctl(IO_FILENO(o), SNDCTL_DSP_CHANNELS, &dummy) != \ -1) \ (o).type = FEEDER_IO_TYPE_DSP; \ } \ } while (0) FIXUP_IO(in_opt); if (in_opt.type == FEEDER_IO_TYPE_UNKNOWN) in_opt.type = FEEDER_IO_TYPE_RAW; if (in_opt.type == FEEDER_IO_TYPE_RAW && (in_opt.afmt == 0 || in_opt.waveinfo.channels == 0 || in_opt.waveinfo.rate == 0)) { warnx("raw io input require format, channels and " "sampling rate arguments"); if (in_opt.waveinfo.fp != stdin) fclose(in_opt.waveinfo.fp); return (usage()); } switch (in_opt.type) { case FEEDER_IO_TYPE_WAVE: ret = wave_header_read(&(in_opt.waveinfo)); if (ret != 0) { if (in_opt.waveinfo.fp != stdin) fclose(in_opt.waveinfo.fp); return (ret); } switch (in_opt.waveinfo.format) { case WAVE_FORMAT_PCM: case WAVE_FORMAT_EXT: in_opt.afmt = bit2afmt(in_opt.waveinfo.bit); if (in_opt.waveinfo.endian == WAVE_BIG_ENDIAN) in_opt.afmt = afmt_swap_endian(in_opt.afmt); break; case WAVE_FORMAT_ALAW: in_opt.afmt = AFMT_A_LAW; break; case WAVE_FORMAT_ULAW: in_opt.afmt = AFMT_MU_LAW; break; default: warnx("unsupported format -- 0x%04x", in_opt.waveinfo.format); if (in_opt.waveinfo.fp != stdin) fclose(in_opt.waveinfo.fp); return (EFTYPE); break; } if (in_opt.afmt == 0 || in_opt.waveinfo.channels < SND_CHN_MIN || in_opt.waveinfo.channels > SND_CHN_MAX || in_opt.waveinfo.rate < FEEDRATE_RATEMIN || in_opt.waveinfo.rate > FEEDRATE_RATEMAX) { warnx("Don't know how to handle wave " "afmt=0x%08x channels=%u rate=%u", in_opt.afmt, in_opt.waveinfo.channels, in_opt.waveinfo.rate); if (in_opt.waveinfo.fp != stdin) fclose(in_opt.waveinfo.fp); return (EFTYPE); } break; case FEEDER_IO_TYPE_DSP: if (in_opt.afmt == 0) in_opt.afmt = AFMT_S16_LE; if (in_opt.waveinfo.channels == 0) in_opt.waveinfo.channels = 2; if (in_opt.waveinfo.rate == 0) in_opt.waveinfo.rate = 44100; in_opt.waveinfo.bit = AFMT_BIT(in_opt.afmt); if ((in_opt.afmt & (AFMT_24BIT | AFMT_32BIT)) || (in_opt.waveinfo.channels > 2)) in_opt.waveinfo.format = WAVE_FORMAT_EXT; else in_opt.waveinfo.format = WAVE_FORMAT_PCM; if (ioctl(IO_FILENO(in_opt), SNDCTL_DSP_SETFMT, &(in_opt.afmt)) == -1 || ioctl(IO_FILENO(in_opt), SNDCTL_DSP_CHANNELS, &(in_opt.waveinfo.channels)) == -1 || ioctl(IO_FILENO(in_opt), SNDCTL_DSP_SPEED, &(in_opt.waveinfo.rate)) == -1 || ioctl(IO_FILENO(in_opt), SNDCTL_DSP_POLICY, &(in_opt.latency)) == -1) { warn("DSP ioctl()"); fclose(in_opt.waveinfo.fp); return (EX_IOERR); } break; default: break; } if (out_opt.type == FEEDER_IO_TYPE_UNKNOWN) out_opt.type = in_opt.type; if (out_opt.afmt == 0) out_opt.afmt = in_opt.afmt; if (out_opt.waveinfo.format == 0) out_opt.waveinfo.format = in_opt.waveinfo.format; if (out_opt.waveinfo.bit == 0) out_opt.waveinfo.bit = in_opt.waveinfo.bit; if (out_opt.waveinfo.channels == 0) out_opt.waveinfo.channels = in_opt.waveinfo.channels; if (out_opt.waveinfo.rate == 0) out_opt.waveinfo.rate = in_opt.waveinfo.rate; if (out_opt.waveinfo.endian == 0) out_opt.waveinfo.endian = in_opt.waveinfo.endian; if (g_opt.bufsize < WAVE_BLOCK_ALIGN(&(out_opt.waveinfo))) g_opt.bufsize = WAVE_BLOCK_ALIGN(&(out_opt.waveinfo)); g_opt.bufsize -= g_opt.bufsize % WAVE_BLOCK_ALIGN(&(out_opt.waveinfo)); pcmchannel = chn_create(g_opt.direction, g_opt.bufsize); if (pcmchannel == NULL) { warn("chn_create()"); if (in_opt.waveinfo.fp != stdin) fclose(in_opt.waveinfo.fp); return (ENOMEM); } pcmchannel->in_fp = in_opt.waveinfo.fp; ret = feeder_build_chain(pcmchannel); if (ret != 0) { chn_destroy(pcmchannel); return (ret); } if (strcmp(out_opt.file, "-") != 0) { struct stat st; int fd; if (stat(out_opt.file, &st) == 0 && S_ISCHR(st.st_mode)) fd = open(out_opt.file, O_WRONLY | out_opt.exclusive); else fd = open(out_opt.file, O_WRONLY | O_CREAT | O_TRUNC); if (fd == -1) { warn("open() file=%s", out_opt.file); chn_destroy(pcmchannel); return (EX_IOERR); } out_opt.waveinfo.fp = fdopen(fd, "w"); if (out_opt.waveinfo.fp == NULL) { warn("fdopen()"); close(fd); chn_destroy(pcmchannel); return (EX_IOERR); } } pcmchannel->out_fp = out_opt.waveinfo.fp; signal(SIGINT, sigint_handler); FIXUP_IO(out_opt); if (out_opt.type == FEEDER_IO_TYPE_DSP) { g_opt.bit_perfect ^= 1; (void)ioctl(IO_FILENO(out_opt), SNDCTL_DSP_COOKEDMODE, &(g_opt.bit_perfect)); if (ioctl(IO_FILENO(out_opt), SNDCTL_DSP_SETFMT, &(out_opt.afmt)) == -1) warn("DSP ioctl(SETFMT)"); if (ioctl(IO_FILENO(out_opt), SNDCTL_DSP_CHANNELS, &(out_opt.waveinfo.channels)) == -1) warn("DSP ioctl(CHANNELS)"); if (ioctl(IO_FILENO(out_opt), SNDCTL_DSP_SPEED, &(out_opt.waveinfo.rate)) == -1) warn("DSP ioctl(SPEED)"); if (ioctl(IO_FILENO(out_opt), SNDCTL_DSP_POLICY, &(out_opt.latency)) == -1) warn("DSP ioctl(POLICY)"); out_opt.type = FEEDER_IO_TYPE_RAW; if (0) { unsigned long long chnorder; int xret; chnorder = 0x0000000000000000ULL; xret = ioctl(IO_FILENO(out_opt), SNDCTL_DSP_GET_CHNORDER, &chnorder); warnx("Channel order: %d 0x%016llx", xret, chnorder); /*chnorder = 0x0000000087654321ULL; xret = ioctl(IO_FILENO(out_opt), SNDCTL_DSP_SET_CHNORDER, &chnorder); warnx("Channel order: %d 0x%016llx", xret, chnorder);*/ } } if (out_opt.type == FEEDER_IO_TYPE_WAVE) { out_opt.waveinfo.seekable = 0; out_opt.waveinfo.data_size = WAVE_DATA_SIZE_BIG; (void)wave_header_write(&(out_opt.waveinfo)); out_opt.waveinfo.data_size = 0; if (ftell(out_opt.waveinfo.fp) == -1) { warn("impossible to fix wave header"); out_opt.type = FEEDER_IO_TYPE_RAW; } else out_opt.running = 1; } sec1 = out_opt.waveinfo.rate * out_opt.waveinfo.channels * (out_opt.waveinfo.bit >> 3); if (sec1 < 1) sec1 = 1; sec8 = sec1 >> 3; if (sec8 < 1) sec8 = 1; if (g_opt.verbose != 0) { #define VOL_LEFT() ((g_opt.volume >> 8) & 0xff) #define VOL_RIGHT() (g_opt.volume & 0xff) fprintf(stderr, "\n Files: %s -> %s\n", in_opt.file, out_opt.file); fprintf(stderr, " Conversion: %s (%u Hz) -> %s (%u Hz)\n", afmt2sfmt(in_opt.afmt), in_opt.waveinfo.rate, afmt2sfmt(out_opt.afmt), out_opt.waveinfo.rate); fprintf(stderr, " Channels: %u -> %u\n", in_opt.waveinfo.channels, out_opt.waveinfo.channels); if (VOL_LEFT() != 100 || VOL_RIGHT() != 100) fprintf(stderr, " Gain/Attenuation: L %d%% / R %d%%\n", VOL_LEFT() - 100, VOL_RIGHT() - 100); if (g_opt.eqtreble != -1 || g_opt.eqbass != -1) { fprintf(stderr, " Treble/Bass: %d%% / %d%%\n", g_opt.eqtreble, g_opt.eqbass); if (g_opt.eqpreamp != FEEDEQ_PREAMP_DEFAULT) fprintf(stderr, " EQ Preamp: %c%d.%ddB\n", FEEDEQ_PREAMP_SIGNMARK(g_opt.eqpreamp), FEEDEQ_PREAMP_IPART(g_opt.eqpreamp), FEEDEQ_PREAMP_FPART(g_opt.eqpreamp)); } if (in_opt.waveinfo.rate != out_opt.waveinfo.rate) fprintf(stderr, " Resample Quality: %d (%s)\n", g_opt.quality, (g_opt.quality == 0) ? "zero-order hold" : ((g_opt.quality < 2) ? "linear interpolation" : "sinc interpolation")); fprintf(stderr, "\n"); } feeder_benchmark_start(); while ((ret = chn_feed(pcmchannel)) != 0) { if (g_opt.verbose != 0) { static const char throbber[4] = "-\\|/"; static uint32_t idx = 0, cnt1 = 0, cnt2 = 0; static uint32_t sc = 0; #define print_progress() fprintf(stderr, \ " Progress: [%10u Bytes] " \ "%c [Time: %02u:%02u:%02u]\r", \ out_opt.waveinfo.data_size, \ throbber[idx], \ sc / (60 * 60), (sc / 60) % 60, \ sc % 60) if (out_opt.waveinfo.data_size == 0) print_progress(); cnt1 += ret; cnt2 += ret; if (cnt1 >= sec1) { cnt1 %= sec1; sc++; print_progress(); } if (cnt2 >= sec8) { cnt2 %= sec8; idx++; idx &= 3; print_progress(); } } out_opt.waveinfo.data_size += ret; } if (g_opt.verbose != 0) fprintf(stderr, "\n"); feeder_benchmark_stop(); if (out_opt.type == FEEDER_IO_TYPE_WAVE) { out_opt.waveinfo.seekable = 1; (void)wave_header_write(&(out_opt.waveinfo)); out_opt.running = 0; } chn_destroy(pcmchannel); pcmchannel = NULL; return (EX_OK); }