/* * aubitswap.cc - swap bits in a 16-bit PCM stream * AYM 2006-05-11 */ /* This file is copyright André Majorel 2006. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include // MUST be unsigned to avoid sign extension typedef unsigned short sample_t; typedef struct { sample_t nmask; sample_t pmask1; sample_t pmask2; int shift; } bitpair_t; static int do_file (const char *ipathname, FILE *ifp, const char *opathname, FILE *ofp); static int atobitpairs (std::vector &bitpairs, const char *str); static const char *quotechar (char c); static void err (const char *fmt, ...); static size_t blocksz_f = 128; static size_t channels = 1; static std::vector bitpairs; static bool debug = false; int main (int argc, char *argv[]) { int status = 0; /* * Parse the command line */ if (argc == 2 && strcmp (argv[1], "--help") == 0) { fprintf (stdout, "Usage:\n" " aubitswap --help\n" " aubitswap [-b num] [-c num] [-D] [-s num,num,...] [file ...]\n" "Options:\n" " -b num I/O block size in frames (default %lu)\n" " -c num Number of channels (default %lu)\n" " -D Print debugging information\n" " --help Print usage to standard output and exit successfully\n" " -s num,num,... Pairs of bit numbers to swap\n" , (unsigned long) blocksz_f, (unsigned long) channels); exit (0); } { int g; while ((g = getopt (argc, argv, "b:c:Ds:")) != EOF) { if (g == 'b') { blocksz_f = atoi (optarg); // FIXME if (blocksz_f < 1) { err ("I/O block size < 1"); exit (1); } } else if (g == 'c') { channels = atoi (optarg); // FIXME if (channels < 1) { err ("number of channels < 1"); exit (1); } } else if (g == 'D') { debug = true; } else if (g == 's') { bitpairs.clear (); if (atobitpairs (bitpairs, optarg) != 0) exit (1); } else if (g == '?') { exit (1); } else { err ("invalid option %s", quotechar (g)); exit (1); } } } /* * Process the arguments */ if (isatty (fileno (stdout))) { err ("standard output is a terminal"); exit (1); } if (argc - optind >= 1) { for (int a = optind; a < argc; a++) { if (strcmp (argv[a], "-") == 0) { if (do_file ("(stdin)", stdin, "(stdout)", stdout) != 0) status = 1; } else { FILE *fp = fopen (argv[a], "rb"); if (fp == NULL) { err ("%s: %s", argv[a], strerror (errno)); status = 1; continue; } if (do_file (argv[a], fp, "(stdout)", stdout) != 0) status = 1; fclose (fp); } } } else /* No arguments, read from stdin */ { if (do_file ("(stdin)", stdin, "(stdout)", stdout) != 0) status = 1; } return status; } /* * do_file - process a file * * Returns 0 on success, non-zero on failure. */ static int do_file (const char *ipathname, FILE *ifp, const char *opathname, FILE *ofp) { size_t bufsz_s = channels * blocksz_f; sample_t buf[bufsz_s]; size_t isamples; while ((isamples = fread (buf, sizeof *buf, bufsz_s, ifp)) != 0) { const sample_t *bmax = buf + isamples; for (sample_t *b = buf; b < bmax; b++) { const bitpair_t *bpmax = &bitpairs[0] + bitpairs.size (); for (const bitpair_t *bp = &bitpairs[0]; bp < bpmax; bp++) { *b = (*b & bp->nmask) | ((*b & bp->pmask1) << bp->shift) | ((*b & bp->pmask2) >> bp->shift); } } size_t osamples = fwrite (buf, sizeof *buf, isamples, ofp); if (osamples != isamples) { err ("%s: write error", opathname); return 1; } } if (ferror (ifp)) { err ("%s: read error", ipathname); return 1; } return 0; } /* * atobitpairs - parse a bit pairs list and update a vector of bitpair_t * * Returns 0 on success, non-zero on failure. */ static int atobitpairs (std::vector &bitpairs, const char *str) { for (const char *c = str;;) { int num1 = -1; int num2 = -1; // Get the first number while (*c >= '0' && *c <= '9') { if (num1 == -1) num1 = 0; num1 = 10 * num1 + *c - '0'; // FIXME pedantically non-portable c++; } if (num1 == -1) { if (*c == '\0') break; err ("expected first bit number, got %s", quotechar (*c)); } if (num1 >= 16) err ("warning: bit number > 15"); // Get the comma if (*c == '\0') { err ("odd number of bit numbers in list"); return 1; } if (*c != ',') { err ("unexpected character %s", quotechar (*c)); return 1; } c++; // Get the second number while (*c >= '0' && *c <= '9') { if (num2 == -1) num2 = 0; num2 = 10 * num2 + *c - '0'; // FIXME pedantically non-portable c++; } if (num2 == -1) { if (*c == '\0') break; err ("expected second bit number, got %s", quotechar (*c)); } if (num2 >= 16) err ("warning: bit number > 15"); // Make sure the next character is valid. If it's a comma, skip over it. if (*c == '\0') ; // We'll deal with in the next iteration else if (*c == ',') c++; else { err ("unexpected character %s", quotechar (*c)); return 1; } // Update the bitpair_t vector. if (num1 == num2) continue; if (num1 > num2) // Make sure num1 < num2 { int n = num2; num2 = num1; num1 = n; } bitpair_t bp; bp.pmask1 = 1 << num1; bp.pmask2 = 1 << num2; bp.nmask = ~ (bp.pmask1 | bp.pmask2); bp.shift = num2 - num1; if (debug) err ("y = (x & %04hX) | ((x & %04hX) << %2d) | ((x & %04hX) >> %2d)", bp.nmask, bp.pmask1, bp.shift, bp.pmask2, bp.shift); bitpairs.push_back (bp); } return 0; } /* * quotechar - return a safe representation of a char * * Not reentrant (returns pointer on static buffer). The string is * guaranteed to be exactly three characters long and not contain * any control or non-ASCII characters. * * FIXME not compatible with EBCDIC. */ static const char hex_digit[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char *quotechar (char c) { static char buf[4]; if (c >= 32 && c <= 126) { buf[0] = '"'; buf[1] = c; buf[2] = '"'; buf[3] = '\0'; } else { buf[0] = hex_digit[((unsigned char) c) >> 4]; buf[1] = hex_digit[((unsigned char) c) & 0x0f]; buf[2] = 'h'; buf[3] = '\0'; } return buf; } /* * err - emit an error message */ static void err (const char *fmt, ...) { va_list argp; fputs ("aubitswap: ", stderr); va_start (argp, fmt); vfprintf (stderr, fmt, argp); va_end (argp); fputc ('\n', stderr); }