view miscutil/pcm16-to-ulaw.c @ 550:de333989a12b

document gsm[e]fr-decode-tw5[-r] utilities
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 05 Oct 2024 02:16:48 +0000
parents c7f02428bda6
children
line wrap: on
line source

/*
 * This program reads a 16-bit PCM recording in raw format (robe by default,
 * or LE with -l option) and converts it to 8-bit PCM in North American
 * mu-law encoding.  It is based on the ulaw_compress() function from ITU-T
 * G.191 STL, using the most significant 14 bits of each input linear PCM
 * sample, rather than just 13.  A command line option is also provided
 * to truncate each input value to 13 bits prior to mu-law encoding, to
 * replicate the effect of using a table lookup conversion method that
 * only considers the upper 13 bits - specify -t option to select this mode.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

static unsigned
ulaw_compress(input)
    unsigned input;
{
    short i;                    /* aux.var. */
    short absno;                /* absolute value of linear (input) sample */
    short segno;                /* segment (Table 2/G711, column 1) */
    short low_nibble;           /* low nibble of log companded sample */
    short high_nibble;          /* high nibble of log companded sample */
    unsigned output;

    /* -------------------------------------------------------------------- */
    /* Input is 14-bit right-justified in this version */
    /* Compute absolute value; adjust for easy processing */
    /* -------------------------------------------------------------------- */
    absno = input >= 0x2000     /* compute 1's complement in case of */
      ? (~input & 0x1FFF) + 33          /* negative samples */
      : input + 33;                     /* NB: 33 is the difference value */
    /* between the thresholds for */
    /* A-law and u-law. */
    if (absno > (0x1FFF))       /* limitation to "absno" < 8192 */
      absno = (0x1FFF);

    /* Determination of sample's segment */
    i = absno >> 6;
    segno = 1;
    while (i != 0) {
      segno++;
      i >>= 1;
    }

    /* Mounting the high-nibble of the log-PCM sample */
    high_nibble = (0x0008) - segno;

    /* Mounting the low-nibble of the log PCM sample */
    low_nibble = (absno >> segno)       /* right shift of mantissa and */
      &(0x000F);                /* masking away leading '1' */
    low_nibble = (0x000F) - low_nibble;

    /* Joining the high-nibble and the low-nibble of the log PCM sample */
    output = (high_nibble << 4) | low_nibble;

    /* Add sign bit */
    if (input < 0x2000)
      output = output | (0x0080);

    return output;
}

main(argc, argv)
	char **argv;
{
	int opt, little_endian = 0, truncate = 0;
	char *infname, *outfname;
	FILE *inf, *outf;
	uint8_t inb[2];
	uint16_t ins;
	int cc;
	extern int optind;

	while ((opt = getopt(argc, argv, "lt")) != EOF) {
		switch (opt) {
		case 'l':
			little_endian = 1;
			continue;
		case 't':
			truncate = 1;
			continue;
		default:
		usage:
			fprintf(stderr,
				"usage: %s [-l] [-t] input.pcm16 output.ulaw\n",
				argv[0]);
			exit(1);
		}
	}
	if (argc != optind + 2)
		goto usage;
	infname = argv[optind];
	outfname = argv[optind+1];

	inf = fopen(infname, "r");
	if (!inf) {
		perror(infname);
		exit(1);
	}
	outf = fopen(outfname, "w");
	if (!outf) {
		perror(outfname);
		exit(1);
	}
	for (;;) {
		cc = fread(inb, 1, 2, inf);
		if (cc <= 0)
			break;
		if (cc & 1) {
			fprintf(stderr, "error: %s has odd number of bytes\n",
				infname);
			exit(1);
		}
		if (little_endian)
			ins = ((uint16_t) inb[1] << 8) | ((uint16_t) inb[0]);
		else
			ins = ((uint16_t) inb[0] << 8) | ((uint16_t) inb[1]);
		ins >>= 2;
		if (truncate)
			ins &= ~1;
		putc(ulaw_compress(ins), outf);
	}
	fclose(outf);
	exit(0);
}