packages feed

jammittools-0.2.0.1: cbits/aifc2wav-5.1.c

/* public domain
 * ima4, sowt and sdx2 handled
 * sdx2 not hardly tested, may fail, contact me if problems
 * endian-independant code
 * sed@free.fr for comments/suggestions/anything
 * http://sed.free.fr/aifc2wav.html
 * Wed, 22 Jan 2003
 * Last update: Fri, 30 Mar 2007
 *
 * algo, tables ripped from http://www.pcisys.net/~melanson/codecs/adpcm.txt
 * the above link looks dead (2007-03-30), try the following link
 *   http://wiki.multimedia.cx/index.php?title=Category:ADPCM_Audio_Codecs
 *
 * file format in : ftp://ftp.sgi.com/sgi/aiff-c.9.26.91.ps.Z
 *
 * http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
 * helped for handling extended numbers
 *
 * http://ewhac.best.vwh.net/3DO-old/music/
 * for sdx2 code (see aif2raw.c there)
 */

/* handled chunks types */
#define FORM        1
#define COMM        2
#define FVER        3
#define SSND        4

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int index_table[16] = {
    -1, -1, -1, -1, 2, 4, 6, 8,
    -1, -1, -1, -1, 2, 4, 6, 8,
};

static int step_table[89] = {
    7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
    19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
    50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
    130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
    337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
    876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
    2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
    5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};

/* parse a chunk - waits a 34 bytes buffer, returns a little endian buffer */
void decode_chunk(unsigned char *input, unsigned char *output, short *static_predictor)
{
  char d;
  int i;
  short predictor;
  short step_index;
  int hnb, lnb;
  int diff;
  int sign, delta;
  short step;

  d = *input++;
  predictor=d << 8;
  d = *input++;
  step_index = d & 127;
  predictor |= d&128;

  // onyxite: use this channel's last packet's last sample as predictor
  predictor = *static_predictor;

  /* is this necessary ? */
  if (step_index < 0) step_index = 0;
  if (step_index > 88) step_index = 88;

  step = step_table[step_index];

  for (i=0; i<32; i++, input++) {
    d = *input;
    hnb = (d>>4) & 15;
    lnb = d & 15;

    /* decode lnb */
    step_index += index_table[lnb];
    /* necessary ? */
    if (step_index<0) step_index = 0;
    if (step_index>88) step_index = 88;

    sign = lnb & 8;
    delta = lnb & 7;
    diff = step >> 3;
    if (delta & 4) diff += step;
    if (delta & 2) diff += step >> 1;
    if (delta & 1) diff += step >> 2;
    /* take care of clamping (Adam Sampson <ats@offog.org>) */
    if (sign) {
      int p = ((int) predictor) - diff;
      if (p < -32768)
        predictor = -32768;
      else
        predictor = p;
    } else {
      int p = ((int) predictor) + diff;
      if (p > 32767)
        predictor = 32767;
      else
        predictor = p;
    }

    *output = predictor&255;
    output++;
    *output = (predictor>>8)&255;
    output++;

    step = step_table[step_index];

    /* decode hnb */
    step_index += index_table[hnb];
    if (step_index<0) step_index = 0;
    if (step_index>88) step_index = 88;

    sign = hnb & 8;
    delta = hnb & 7;
    diff = step >> 3;
    if (delta & 4) diff += step;
    if (delta & 2) diff += step >> 1;
    if (delta & 1) diff += step >> 2;
    /* take care of clamping (Adam Sampson <ats@offog.org>) */
    if (sign) {
      int p = ((int) predictor) - diff;
      if (p < -32768)
        predictor = -32768;
      else
        predictor = p;
    } else {
      int p = ((int) predictor) + diff;
      if (p > 32767)
        predictor = 32767;
      else
        predictor = p;
    }

    *output = predictor&255;
    output++;
    *output = (predictor>>8)&255;
    output++;

    // onyxite: save this sample so (if it's the last sample in this packet)
    // it becomes the predictor for the next packet in this channel
    *static_predictor = predictor;

    step = step_table[step_index];
  }
}

/* reads the FILE, return the type of the chunk, size too */
/* return -1 on error, -2 if unhandled chunk, -3 if EOF */
int get_type(FILE *f, long *size)
{
  int ret;

  unsigned char buf[8];

  ret=fread(buf, 1, 8, f);

  if (ret == 0 && feof(f))
    return -3;

  if (ferror(f) || ret != 8)
    return -1;

  *size = (unsigned)(buf[4]<<24) + (buf[5]<<16) + (buf[6]<<8) + buf[7];

  if (!strncmp((char*)buf, "FORM", 4)) {
    /* let's check if it's AIFC or not */
    if (fread(buf, 1, 4, f) != 4)
      return -1;

    if (strncmp((char*)buf, "AIFC", 4)) {
      buf[4] = 0;
      fprintf(stderr, "unsupported type %s\nonly AIFC handled\n", buf);
      return -1;
    }

    *size -= 4;
    return FORM;
  }

  if (!strncmp((char*)buf, "COMM", 4))
    return COMM;

  if (!strncmp((char*)buf, "FVER", 4))
    return FVER;

  if (!strncmp((char*)buf, "SSND", 4))
    return SSND;

  if (!strncmp((char*)buf, "FLLR", 4))
    return -2;

  buf[4] = 0;
  fprintf(stderr, "warning: unhandled chunk %s (size=%ld)\n", buf, *size);

  return -2;
}

/* transform an int into a little endian buffer */
unsigned char *itole(unsigned long l, unsigned char *t)
{
  *t = l & 255;
  *(t+1) = (l >>  8) & 255;
  *(t+2) = (l >> 16) & 255;
  *(t+3) = (l >> 24) & 255;
  return t;
}

/* transform a short into a little endian buffer */
unsigned char *stole(unsigned short l, unsigned char *t)
{
  *t = l & 255;
  *(t+1) = (l >>  8) & 255;
  return t;
}

/* different flags to know what chunk here, what chunk has been proceeded */
#define FORM_DONE        1
#define COMM_DONE        2
#define FVER_DONE        4
#define SSND_DONE        8

/* we don't want stack space for those */
static unsigned char buf[24];
static unsigned char in[34];
static unsigned char out[256];  /* only 128 if mono, 256 if stereo */
static unsigned char out2[128]; /* for stereo, another buffer (we must
                                 * interleave the two buffers in the WAV file)
                                 */

/* != file types supported */
#define IMA4        1
#define SOWT        2
#define SDX2        3

int aifc2wav_main(int n, char **v)
{
  long size;
  int flags = 0;
  char *input_file;
  char *output_file;
  FILE *fin, *fout;
  int nb_chans;                /* number of channels, must be 1 or 2 */
  int nb_frames;        /* number of sound chunks */
  int nb_bits;                /* nb bits / sample, must be 16 */
  long double freq;        /* sample rate */
  long ssnd_pos;        /* pos of beginning of the stream in the input file */
  long total_size=1;        /* some files are bigger than what says FORM size
                         * so we must only parse the nb of bytes given by
                         * FORM size (init to 1 for the while to run)
                         */
  int sound_type=0;        /* the type of the data in the sound file */
  long riff_size;        /* the riff size - depends of the type of data sound */
  short *last;                /* for SDX2 files */

  short predictor_left = 0;
  short predictor_right = 0;

  if (n != 3) {
    fprintf(stderr, "usage: %s <input AIFC file> <output WAV file>\n", v[0]);
    return 1;
  }

  input_file = v[1];
  output_file = v[2];

  fin = fopen(input_file, "rb");
  if (!fin) {
    perror(input_file);
    return 1;
  }

  fout = fopen(output_file, "wb");
  if (!fout) {
    perror(output_file);
    fclose(fin);
    return 1;
  }

  /* we don't care about the order (only FORM must be first) */
  /* we require only one SSND, one COMM, at most one FVER, that's all */
  /* total_size must be != 0 before to come here */
  while(total_size)
  switch (get_type(fin, &size)) {
    case FORM :
      if (flags) goto bad_file;

      flags = FORM_DONE;
      total_size = size;

      break;

    case COMM :
      if (!(flags & FORM_DONE)) goto bad_file;

      if (flags & COMM_DONE) goto bad_file;

      total_size -= 8;
      if (total_size < 0) goto in_error_size;

      flags |= COMM_DONE;

      if (fread(buf, 1, 22, fin) != 22)
        goto bad_file;
      total_size -= 22;
      if (total_size < 0) goto in_error_size;

      nb_chans = (buf[0]<<8) + buf[1];
      nb_frames = (unsigned)(buf[2]<<24) + (buf[3]<<16) + (buf[4]<<8) + buf[5];
      nb_bits = (buf[6]<<8) + buf[7];

      /* converting this silly extended format into freq
       * IAFC people are weird, why extended ?
       * this code is far from efficient
       * based on :
       * http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
       * this may fail on your machine, it works on ia32 and sparc
       */
      {
        int sign = (buf[8] >> 7) & 1;
        int exp = ((buf[8] & 127) << 8) + buf[9] - 16383;
        int j = (buf[10]>>7)&1;
        unsigned long frac = ((buf[10]&127) << 24) +
                              (buf[11] << 16) +
                              (buf[12] << 8) +
                              (buf[13]);
        unsigned long frac2 = (unsigned)(buf[14] << 24) +
                              (buf[15] << 16) +
                              (buf[16] << 8) +
                              (buf[17]);
        int i;

        freq = frac2;
        for (i=0; i<32; i++) freq /= 2;
        freq += frac;
        for (i=0; i<31; i++) freq /= 2;
        if (j) freq += 1.;
        while (exp) {
          freq *= 2;
          exp--;
        }

        if (sign) freq = -freq;
      }

      if (nb_chans != 1 && nb_chans != 2) {
        fprintf(stderr, "nb of channels = %d\nonly 1 or 2 channels handled\n",
                nb_chans);
        goto bad_file;
      }

      // onyxite: assume 16 bit samples
      if (nb_bits == 0) {
        nb_bits = 16;
      }
      
      if (nb_bits != 16) {
        fprintf(stderr, "bits per sample = %d\nonly 16 bits handled\n",
                nb_bits);
        goto bad_file;
      }

      /* 1e7 is enough ? */
      if (freq <= 0 || freq >=1e7) {
        fprintf(stderr, "freq = %Lf\nonly 0 < freq < 1e7 handled\n", freq);
        goto bad_file;
      }

      /* let's test if it's a ima4, sowt or SDX2 or something else */
      memcpy(buf, &buf[18], 4);
      buf[4] = 0;
      if (strcmp((char*)buf, "ima4") &&
          strcmp((char*)buf, "sowt") &&
          strcmp((char*)buf, "SDX2")) {
        fprintf(stderr,
          "compression scheme = %s\nonly ima4, sowt and SDX2 handled\n", buf);
        goto bad_file;
      }
      sound_type = IMA4;
      if (!strcmp((char*)buf, "sowt"))
        sound_type = SOWT;
      else if (!strcmp((char*)buf, "SDX2"))
        sound_type = SDX2;

      /* should we optimize here ? no */
      /* the doc says there may be a non-counted byte */
      if (size & 1) size++;
      size -= 22;
      total_size -= size;
      if (total_size < 0) goto in_error_size;

      while(size > 0) {
        int c = fgetc(fin);
        if (c == EOF) {
          fprintf(stderr, "error with COMM chunk, bad size\n");
          goto bad_file;
        }
        size--;
      }

      break;

    case FVER : {
      unsigned long version;

      if (!(flags & FORM_DONE)) goto bad_file;

      if (flags & FVER_DONE) goto bad_file;

      flags |= FVER_DONE;

      total_size -= 8;
      if (total_size < 0) goto in_error_size;

      if (fread(buf, 1, 4, fin) != 4)
        goto bad_file;
      total_size -= 4;
      if (total_size < 0) goto in_error_size;

      version = (unsigned)(buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3];

      if (version != 2726318400U) {
        fprintf(stderr, "version = %lu\nonly 2726318400 handled\n", version);
        goto bad_file;
      }

      /* size should be 4, what to do ? if we don't read, there might
       * be a problem, but someone might not set it to 4 and still
       * telling us there is nothing more... well, let's read it
       */
      /* the doc says there may be a non-counted byte */
      if (size & 1) size++;
      size -= 4;
      total_size -= size;
      if (total_size < 0) goto in_error_size;

      while(size > 0) {
        int c = fgetc(fin);
        if (c == EOF) {
          fprintf(stderr, "error with FVER chunk, bad size\n");
          goto bad_file;
        }
        size--;
      }

      break;
    }

    case SSND : {
      unsigned long offset, blocksize;
      unsigned long nbbytes=0;

      if (!(flags & FORM_DONE)) goto bad_file;

      if (flags & SSND_DONE) {
        fprintf(stderr, "more than one SSND\n");
        goto bad_file;
      }

      total_size -= 8;
      if (total_size < 0) goto in_error_size;

      flags |= SSND_DONE;

      if (fread(buf, 1, 8, fin) != 8)
        goto bad_file;
      total_size -= 8;
      if (total_size < 0) goto in_error_size;

      offset = (unsigned)(buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3];
      blocksize = (unsigned)(buf[4]<<24) + (buf[5]<<16) + (buf[6]<<8) + buf[7];

#if 0
      if (offset || blocksize) {
        fprintf(stderr, "offset = %lu, blocksize = %lu\nthey both must be 0\n",
                offset, blocksize);
        goto bad_file;
      }
#endif

      ssnd_pos = ftell(fin) + offset;
      if (ssnd_pos == -1) {
        perror("ftell");
        goto bad_file;
      }

      /* the doc says there may be a non-counted byte */
      if (size & 1) size++;

      /* we should optimize here, no ? */
      size -= 8;
      total_size -= size;
      if (total_size < 0) goto in_error_size;

      while(size > 0) {
        int c = fgetc(fin);
#if 0
        if (c == EOF) {
          fprintf(stderr, "error with SSND chunk, bad size\n");
          goto bad_file;
        }
#endif
        if (c == EOF) {
          if (!(flags & COMM_DONE)) goto bad_file;

          /* a little hack to update nb_frames for sowt only */
          fprintf(stderr, "warning - bad size in SSND, the output file will"
                          " probably be wrong\n");

          nbbytes -= offset;
          if (sound_type == SDX2) {
            fprintf(stderr, "error - for SDX2 files, I don't know what to"
                            " do\nContact sed@free.fr to see what"
                            " we can do.\n");
            goto both_error;
          } else if (sound_type == SOWT)
            nbbytes /= 2;
          else
            nbbytes /= 34;
          if (nb_chans == 2) nbbytes /= 2;
          nb_frames = nbbytes;

          /* we go to all_parsed, because it's EOF */
          goto all_parsed;
        }

        nbbytes++;
        size--;
      }

      break;
    }

    /* -1 is error */
    case -1:
        goto bad_file;

    /* -2 is unhandled chunk type, so pass it */
    case -2: {
      if (!(flags & FORM_DONE)) goto bad_file;

      total_size -= 8;
      if (total_size < 0) goto in_error_size;

      /* the doc says there may be a non-counted byte */
      if (size & 1) size++;
      total_size -= size;
      if (total_size < 0) goto in_error_size;

      while (size) {
        int c = fgetc(fin);
        if (c == EOF)
          goto bad_file;
        size --;
      }
      break;
    }

    /* -3 is EOF */
    case -3 :
      /* this may be an error, let's just produce a warning */
      fprintf(stderr, "warning: your file is shorter than expected\n");
      goto all_parsed;

    default : 
      fprintf(stderr, "you should not read this...\n");
      goto bad_file;
  }

all_parsed:
  /* we must have parsed COMM and SSND */
  if ((flags & (COMM_DONE | SSND_DONE)) != (COMM_DONE | SSND_DONE)) {

    if (!(flags & COMM_DONE))
      fprintf(stderr, "no COMM chunk found\n");

    if (!(flags & SSND_DONE))
      fprintf(stderr, "no SSND chunk found\n");

    goto bad_file;
  }

  /* is freq an integer ? */
  if (freq != (long double)((int)freq))
    fprintf(stderr, "warning, freq = %Lf will be truncated to %d\n",
        freq, (int)freq);

  /* all is alright */

  /* write header of wav */
  /* start with RIFF */
  if (fwrite("RIFF", 1, 4, fout) != 4) goto out_error;

  if (sound_type == IMA4)
    riff_size = nb_frames * 128;
  else
    riff_size = nb_frames * 2;
  if (nb_chans == 2)
    riff_size *= 2;

  /* then size of the stuff */
  if (fwrite(itole(riff_size+36, buf), 1, 4, fout) != 4) goto out_error;

  /* then WAVEfmt */
  if (fwrite("WAVEfmt ", 1, 8, fout) != 8) goto out_error;

  /* length of fmt chunk (16) */
  if (fwrite(itole(16, buf), 4, 1, fout) != 1) goto out_error;

  /* 1 => PCM */
  if (fwrite(stole(1, buf), 2, 1, fout) != 1) goto out_error;

  /* nb of channels */
  if (fwrite(stole(nb_chans, buf), 2, 1, fout) != 1) goto out_error;

  /* freq */
  if (fwrite(itole((int)freq, buf), 4, 1, fout) != 1) goto out_error;

  /* don't know what */
  if (fwrite(itole((int)freq * nb_chans * (nb_bits/8), buf), 4, 1, fout) != 1)
    goto out_error;

  /* don't know what */
  if (fwrite(stole(nb_chans * (nb_bits/8), buf), 2, 1, fout) != 1)
    goto out_error;

  /* nb bits */
  if (fwrite(stole(nb_bits, buf), 2, 1, fout) != 1) goto out_error;

  /* data chunk */
  if (fwrite("data", 4, 1, fout) != 1) goto out_error;

  /* data size */
  if (fwrite(itole(riff_size, buf), 1, 4, fout) != 4) goto out_error;

  /* let's kick all the frames out */
  if (fseek(fin, ssnd_pos, SEEK_SET) == -1) goto out_error;

  if (sound_type != SOWT) goto not_sowt;

  /* sowt */
  while (nb_frames) {
    nb_frames--;

    if (fread(in, 1, 2, fin) != 2) {
      perror(input_file);
      goto bad_file;
    }
    if (fwrite(in, 1, 2, fout) != 2)
      goto out_error;

    if (nb_chans == 1)
      continue;

    if (fread(in, 1, 2, fin) != 2) {
      perror(input_file);
      goto bad_file;
    }
    if (fwrite(in, 1, 2, fout) != 2)
      goto out_error;
  }

  goto file_done;

not_sowt:
  /* SDX2? */
  if (sound_type != SDX2) goto not_sdx2;

  last = malloc(nb_chans * sizeof(short));
  if (!last) {
    perror("memory");
    goto both_error;
  }
  memset(last, 0, sizeof(short) * nb_chans);

  while (nb_frames > 0) {
    signed char src;
    unsigned short dst;
    int i;

    nb_frames--;

    for (i=0; i<nb_chans; i++) {
      if (fread(&src, 1, 1, fin) != 1) {
        perror(input_file);
        free(last);
        goto bad_file;
      }

      if (src & 1)
        dst = last[i] + ((src * abs(src)) << 1);
      else
        dst = (src * abs(src)) << 1;

      last[i] = dst;

      /* out is 256 bytes, so we're fine */
      out[0] = dst&255;
      out[1] = (dst>>8)&255;

      if (fwrite(out, 1, 2, fout) != 2) {
        free(last);
        goto out_error;
      }
    }
  }

  free(last);

  goto file_done;

not_sdx2:
  /* ima4 */
  while (nb_frames) {
    if (fread(in, 1, 34, fin) != 34) {
      perror(input_file);
      goto bad_file;
    }

    decode_chunk(in, out, &predictor_left);

    /* if stereo mode the next chunk is right channel */
    if (nb_chans == 2) {
      int i;

      if (fread(in, 1, 34, fin) != 34) {
        perror(input_file);
        goto bad_file;
      }

      decode_chunk(in, out2, &predictor_right);

      /* we must now interleave the two channels */

      /* first part, move the first channel to its correct position */
      /* we should optimize this code */
      for (i=63; i>0; i--) {
        out[i*4] = out[i*2];
        out[i*4+1] = out[i*2+1];
      }

      /* second, add right channel to output buffer */
      /* we should optimize this code */
      for (i=0; i<64; i++) {
        out[i*4+2] = out2[i*2];
        out[i*4+3] = out2[i*2+1];
      }
    }

    if (fwrite(out, 1, nb_chans == 2 ? 256 : 128, fout) !=
                (nb_chans == 2 ? 256 : 128))
      goto out_error;

    nb_frames--;
  }

file_done:
  fclose(fin);
  fclose(fout);
  return 0;

in_error_size:
  fprintf(stderr, "the size information of your file is bad\n");

bad_file:
  fprintf(stderr, "%s: bad sound file, can't load\n", input_file);
  fprintf(stderr, "contact sed@free.fr to add support for your file\n");

both_error:
  fclose(fin);
  fclose(fout);
  remove(output_file);
  return 1;

out_error:
  perror(output_file);
  goto both_error;
}