shithub: sox

ref: a26797de62057819c28732da90a48f18cb938e70
dir: /src/sphere.c/

View raw version
/*
 * NIST Sphere file format handler.
 *
 * August 7, 2000
 *
 * Copyright (C) 2000 Chris Bagwell (cbagwell@sprynet.com)
 *
 */

#include "sox_i.h"
#include <string.h>

static int start_read(sox_format_t * ft)
{
  sox_encoding_t encoding = SOX_ENCODING_SIGN2;
  sox_size_t     header_size, bytes_read;
  sox_size_t     num_samples = 0;
  unsigned       bytes_per_sample = 0;
  unsigned       channels = 1;
  unsigned       rate = 16000;
  char           fldname[64], fldtype[16], fldsval[128];
  char           * buf;

  /* Magic header */
  if (lsx_reads(ft, fldname, 8) || strncmp(fldname, "NIST_1A", 7) != 0) {
    lsx_fail_errno(ft, SOX_EHDR, "Sphere header does not begin with magic word 'NIST_1A'");
    return (SOX_EOF);
  }

  if (lsx_reads(ft, fldsval, 8)) {
    lsx_fail_errno(ft, SOX_EHDR, "Error reading Sphere header");
    return (SOX_EOF);
  }

  /* Determine header size, and allocate a buffer large enough to hold it. */
  sscanf(fldsval, "%u", &header_size);
  buf = lsx_malloc(header_size);

  /* Skip what we have read so far */
  header_size -= 16;

  if (lsx_reads(ft, buf, header_size) == SOX_EOF) {
    lsx_fail_errno(ft, SOX_EHDR, "Error reading Sphere header");
    free(buf);
    return (SOX_EOF);
  }

  header_size -= (strlen(buf) + 1);

  while (strncmp(buf, "end_head", 8) != 0) {
    if (strncmp(buf, "sample_n_bytes", 14) == 0)
      sscanf(buf, "%63s %15s %u", fldname, fldtype, &bytes_per_sample);
    else if (strncmp(buf, "channel_count", 13) == 0)
      sscanf(buf, "%63s %15s %u", fldname, fldtype, &channels);
    else if (strncmp(buf, "sample_count ", 13) == 0)
      sscanf(buf, "%53s %15s %u", fldname, fldtype, &num_samples);
    else if (strncmp(buf, "sample_rate ", 12) == 0)
      sscanf(buf, "%53s %15s %u", fldname, fldtype, &rate);
    else if (strncmp(buf, "sample_coding", 13) == 0) {
      sscanf(buf, "%63s %15s %127s", fldname, fldtype, fldsval);
      if (!strcasecmp(fldsval, "ulaw") || !strcasecmp(fldsval, "mu-law"))
        encoding = SOX_ENCODING_ULAW;
      else if (!strcasecmp(fldsval, "pcm"))
        encoding = SOX_ENCODING_SIGN2;
      else {
        lsx_fail_errno(ft, SOX_EFMT, "sph: unsupported coding `%s'", fldsval);
        free(buf);
        return SOX_EOF;
      }
    }
    else if (strncmp(buf, "sample_byte_format", 18) == 0) {
      sscanf(buf, "%53s %15s %127s", fldname, fldtype, fldsval);
      if (strcmp(fldsval, "01") == 0)         /* Data is little endian. */
        ft->encoding.reverse_bytes = MACHINE_IS_BIGENDIAN;
      else if (strcmp(fldsval, "10") == 0)    /* Data is big endian. */
        ft->encoding.reverse_bytes = MACHINE_IS_LITTLEENDIAN;
      else if (strcmp(fldsval, "1")) {
        lsx_fail_errno(ft, SOX_EFMT, "sph: unsupported coding `%s'", fldsval);
        free(buf);
        return SOX_EOF;
      }
    }

    if (lsx_reads(ft, buf, header_size) == SOX_EOF) {
      lsx_fail_errno(ft, SOX_EHDR, "Error reading Sphere header");
      free(buf);
      return (SOX_EOF);
    }

    header_size -= (strlen(buf) + 1);
  }

  if (!bytes_per_sample)
    bytes_per_sample = encoding == SOX_ENCODING_ULAW? 1 : 2;

  while (header_size) {
    bytes_read = lsx_readbuf(ft, buf, header_size);
    if (bytes_read == 0) {
      free(buf);
      return (SOX_EOF);
    }
    header_size -= bytes_read;
  }
  free(buf);

  if (ft->seekable) {
    /* Check first four bytes of data to see if it's shorten compressed. */
    char           shorten_check[4];

    if (lsx_readchars(ft, shorten_check, sizeof(shorten_check)))
      return SOX_EOF;
    lsx_seeki(ft, -(sox_ssize_t)sizeof(shorten_check), SEEK_CUR);

    if (!memcmp(shorten_check, "ajkg", sizeof(shorten_check))) {
      lsx_fail_errno(ft, SOX_EFMT,
                     "File uses shorten compression, cannot handle this.");
      return (SOX_EOF);
    }
  }

  return lsx_check_read_params(ft, channels, (sox_rate_t)rate, encoding,
      bytes_per_sample << 3, (off_t)(num_samples * channels));
}

static int write_header(sox_format_t * ft)
{
  char buf[128];
  long samples = (ft->olength ? ft->olength : ft->length) / ft->signal.channels;

  lsx_writes(ft, "NIST_1A\n");
  lsx_writes(ft, "   1024\n");

  if (samples) {
    sprintf(buf, "sample_count -i %ld\n", samples);
    lsx_writes(ft, buf);
  }

  sprintf(buf, "sample_n_bytes -i %d\n", ft->encoding.bits_per_sample >> 3);
  lsx_writes(ft, buf);

  sprintf(buf, "channel_count -i %d\n", ft->signal.channels);
  lsx_writes(ft, buf);

  if (ft->encoding.bits_per_sample == 8)
    sprintf(buf, "sample_byte_format -s1 1\n");
  else
    sprintf(buf, "sample_byte_format -s2 %s\n",
            ft->encoding.reverse_bytes != MACHINE_IS_BIGENDIAN ? "10" : "01");
  lsx_writes(ft, buf);

  sprintf(buf, "sample_rate -i %u\n", (unsigned) (ft->signal.rate + .5));
  lsx_writes(ft, buf);

  if (ft->encoding.encoding == SOX_ENCODING_ULAW)
    lsx_writes(ft, "sample_coding -s4 ulaw\n");
  else
    lsx_writes(ft, "sample_coding -s3 pcm\n");

  lsx_writes(ft, "end_head\n");

  lsx_padbytes(ft, 1024 - (sox_size_t)lsx_tell(ft));
  return SOX_SUCCESS;
}

SOX_FORMAT_HANDLER(sphere)
{
  static char const *const names[] = {"sph", "nist", NULL};
  static unsigned const write_encodings[] = {
    SOX_ENCODING_SIGN2, 8, 16, 24, 32, 0,
    SOX_ENCODING_ULAW, 8, 0,
    0
  };
  static sox_format_handler_t const handler = {
    SOX_LIB_VERSION_CODE,
    "SPeech HEader Resources; defined by NIST",
    names, SOX_FILE_REWIND,
    start_read, lsx_rawread, NULL,
    write_header, lsx_rawwrite, NULL,
    lsx_rawseek, write_encodings, NULL
  };
  return &handler;
}