Google
 

Open Sound System
The Hitchhiker's Guide to OSS 4.1 Internals

Do you have problems with sound/audio application development? Don't panic! Click here for help!

audio/oss_audiofmt.c

Audio format conversion routines used by audio.c

Description





This file is part of Open Sound System.

Copyright (C) 4Front Technologies 1996-2008.

This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.


#undef  NO_COOKED_MODE
#define AUDIO_C

#define GRC	3

/* SINE_DEBUG enables internal test signal (sine wave) */
#undef  SINE_DEBUG

#include "oss_config.h"
#include "ulaw.h"
#define __int8_t_defined
#include "grc3.h"


Audio format conversion stuff

#define CNV_NONE 	0x00000000
#define CNV_SRC		0x00000001

#define	CNV_CHNMASK	0x000000f0
#define CNV_M2S		0x00000010
#define CNV_S2M		0x00000020
#define CNV_MULT	0x00000040

#define CNV_Flog	0x00001000
#define CNV_F8bit	0x00002000
#define CNV_F16bit	0x00004000
#define CNV_F24bit	0x00008000
#define CNV_F32bit	0x00010000

#define CNV_Tlog	0x00100000
#define CNV_T8bit	0x00200000
#define CNV_T16bit	0x00400000
#define CNV_T24bit	0x00800000
#define CNV_T32bit	0x01000000

#define CNV_SIGN	0x10000000
#define CNV_ENDIAN	0x20000000
#define CNV_TOLOG	0x40000000
#define CNV_FROMLOG	0x80000000

static audio_format_info_t audio_format_info[] = {
  {"mu-Law", AFMT_MU_LAW, 0x80, 8, 0, ENDIAN_NONE, 1},
  {"A-Law", AFMT_A_LAW, 0x80, 8, 0, ENDIAN_NONE, 1},
  {"IMA-ADPCM", AFMT_IMA_ADPCM, 0x00, 4, 0, ENDIAN_NONE, 0, ALIGN_LSB, 1},
  {"8", AFMT_U8, 0x80, 8, 1, ENDIAN_NONE, 0},
  {"16(LE)", AFMT_S16_LE, 0x00, 16, 1, ENDIAN_LITTLE, 1},
  {"16(BE)", AFMT_S16_BE, 0x00, 16, 1, ENDIAN_BIG, 1},
  {"8(signed)", AFMT_S8, 0x00, 8, 1, ENDIAN_NONE, 1},
  {"16(unsigned LE)", AFMT_U16_LE, 0x00, 16, 1, ENDIAN_LITTLE, 0},
  {"16(unsigned BE)", AFMT_U16_BE, 0x00, 16, 1, ENDIAN_BIG, 0},
  {"mp2/mp3", AFMT_MPEG, 0x00, 8, 0, ENDIAN_NONE, 0, ALIGN_LSB, 1},
  {"AC3", AFMT_AC3, 0x00, 16, 0, ENDIAN_NONE, 0, ALIGN_LSB, 1},
  {"SPDIF (RAW)", AFMT_SPDIF_RAW, 0x00, 32, 0, ENDIAN_NONE, 0, ALIGN_LSB, 1},
  {"24(LE)", AFMT_S24_LE, 0x00, 32, 1, ENDIAN_LITTLE, 1, ALIGN_LSB},
  {"24(BE)", AFMT_S24_BE, 0x00, 32, 1, ENDIAN_BIG, 1, ALIGN_LSB},
  {"32(LE)", AFMT_S32_LE, 0x00, 32, 1, ENDIAN_LITTLE, 1},
  {"32(BE)", AFMT_S32_BE, 0x00, 32, 1, ENDIAN_BIG, 1},
  {"24(PACKED)", AFMT_S24_PACKED, 0x00, 24, 1, ENDIAN_LITTLE, 1, ALIGN_LSB},
  {0, 0}
};

audio_format_info_p
oss_find_format (unsigned int fmt)
{
  int i;

  i = 0;

  while (audio_format_info[i].fmt != 0)
    {
      if (audio_format_info[i].fmt == fmt)
	return &audio_format_info[i];
      i++;
    }

  return NULL;
}

#if 0

Limiter stuff. Currently not in use.

static const unsigned char limit8[] = {
/*-128*/ 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72,
/*-112*/ 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80,
/* -96*/ 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 85, 86, 86, 87, 87, 88,
/* -80*/ 88, 89, 89, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 95, 95, 96,
/* -64*/ 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 103,
  104,
/* -48*/ 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110,
  111, 111, 112,
/* -32*/ 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118,
  119, 119, 120,
/* -16*/ 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126,
  127, 127, 128,
/*   0*/ 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134,
  134, 135, 135,
/*  16*/ 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, 142,
  142, 143, 143,
/*  32*/ 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, 150,
  150, 151, 151,
/*  48*/ 152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 157, 158,
  158, 159, 159,
/*  64*/ 160, 160, 161, 161, 162, 162, 163, 163, 164, 164, 165, 165, 166,
  166, 167, 167,
/*  80*/ 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, 173, 173, 174,
  174, 175, 175,
/*  96*/ 176, 176, 177, 177, 178, 178, 179, 179, 180, 180, 181, 181, 182,
  182, 183, 183,
/* 112*/ 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, 189, 189, 190,
  190, 191, 191
};

static __inline__ void
limit_8bit (unsigned char *buf, int l)
{
  int i;

  for (i = 0; i < l; i++)
    buf[i] = limit8[buf[i]];
}

static __inline__ void
limit_16bit (short *buf, int l)
{
  int i;
  l /= sizeof (*buf);

  for (i = 0; i < l; i++)
    buf[i] /= 2;
}

static __inline__ void
limit_32bit (int *buf, int l)
{
  int i;
  l /= sizeof (*buf);

  for (i = 0; i < l; i++)
    buf[i] /= 2;
}

static const unsigned char unlimit8[] = {
/*   0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*  16*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*  32*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*  48*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*  64*/ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
/*  80*/ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
/*  96*/ 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94,
/* 112*/ 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122,
  124, 126,
/* 128*/ 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152,
  154, 156, 158,
/* 144*/ 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184,
  186, 188, 190,
/* 160*/ 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216,
  218, 220, 222,
/* 176*/ 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248,
  250, 252, 254,
/* 192*/ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255,
/* 208*/ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255,
/* 224*/ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255,
/* 240*/ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255
};

static __inline__ void
unlimit_8bit (unsigned char *buf, int l)
{
  int i;

  for (i = 0; i < l; i++)
    buf[i] = unlimit8[buf[i]];
}

static __inline__ void
unlimit_16bit (short *buf, int l)
{
  int i;
  l /= sizeof (*buf);

  for (i = 0; i < l; i++)
    buf[i] *= 2;
}

static __inline__ void
unlimit_32bit (int *buf, int l)
{
  int i;
  l /= sizeof (*buf);

  for (i = 0; i < l; i++)
    buf[i] *= 2;
}
#endif

/*ARGSUSED*/
static int
do_src (adev_p adev, dmap_p dmap, int srcrate, int tgtrate, void *p1,
	void *p2, int *len, int channels, int bits, int ssize)
{
#if GRC == 2
  int err, l, l1, l2;

  l = *len;

  err = src_convert (dmap->src, p1, l, bits, p2, 4096, 24, &l1, &l2, 0);
  *len = l2;
  if (l1 != l)
    cmn_err (CE_NOTE, "Everything not taken %d/%d\n", l1, l);

#endif

#if GRC == 3
  int ch, l, size, nsamples, err;

  l = *len / ssize;
  size = TMP_CONVERT_BUF_SIZE / ssize;
  size /= channels;
  nsamples = l / channels;

  if (bits == 24)
    bits = 32;

#  ifdef DO_TIMINGS
  oss_timing_printf ("grc3_convert %d bytes / %d samples * %d channels in. ",
	     *len, nsamples, channels);
#  endif

#if 0
  /* Limit the volume levels before calling GRC3 */
  buf = p1;

  switch (ssize)
    {
    case 1:
      limit_8bit ((unsigned char *) buf, *len);
      break;
    case 2:
      limit_16bit ((short *) buf, *len);
      break;
    case 4:
      limit_32bit ((int *) buf, *len);
      break;
    }
#endif

  for (ch = 0; ch < channels; ch++)
    {
      if ((err = grc3_convert (dmap->srcstate[ch], bits, adev->src_quality,	/* Numbits, quality */
			       p1, p2, nsamples, size, channels, ch)) < 0)
	{
	  cmn_err (CE_WARN, "SRC failed (%d), bits=%d, ssize=%d\n", err, bits,
		   ssize);
	  return OSS_EIO;
	}

      *len = ((grc3state_t *) dmap->srcstate[ch])->outsz * ssize * channels;
      VMEM_CHECK (p2, *len);
    }
#if 0
  buf = p2;
  switch (ssize)
    {
    case 1:
      unlimit_8bit ((unsigned char *) buf, *len);
      break;
    case 2:
      unlimit_16bit ((short *) buf, *len);
      break;
    case 4:
      unlimit_32bit ((int *) buf, *len);
      break;
    }
#endif
#  ifdef DO_TIMINGS
  oss_timing_printf ("-> %d bytes, %d samples out, %d samples in",
	     *len, ((grc3state_t *) dmap->srcstate[0])->outsz,
	     ((grc3state_t *) dmap->srcstate[0])->insz);
#  endif
#endif

  return 0;
}

static int
setup_src (adev_p adev, dmap_p dmap, int srate, int trate, int sch, int tch)
{
  int ch, nch;

  nch = sch;
  if (tch < nch)
    nch = tch;

  if (adev->src_quality < 1)
    adev->src_quality = 1;
  if (adev->src_quality > 5)
    adev->src_quality = 5;

#if GRC == 2
  if (nch > 2)
    {
      cmn_err (CE_WARN, "Too many channels for SRC (%d)\n", nch);
      return OSS_EIO;
    }
  {
    int val;
    val = src_find_output_rate (trate, srate);
    if (src_open (&dmap->src, srate, trate, nch, 0) != 0)
      {
	cmn_err (CE_CONT, "OSS audio: SRC open failed\n");
	return OSS_EIO;
      }
  }
#endif
#if GRC == 3
  if (nch > OSS_MAX_CONVERT_CHANNELS)
    {
      cmn_err (CE_WARN, "Too many channels for SRC (%d)\n", nch);
      return OSS_EIO;
    }
#ifdef DO_TIMINGS
  oss_timing_printf ("grc3_setup %d -> %d Hz, %d channels", srate, trate, nch);
#endif

  for (ch = 0; ch < nch; ch++)
    {
      if (dmap->srcstate[ch] == NULL)
	{
	  dmap->srcstate[ch] = PMALLOC (dmap->osdev, sizeof (grc3state_t));
	}
      grc3_reset (dmap->srcstate[ch]);
      grc3_setup (dmap->srcstate[ch], srate, trate);
    }
#endif
  return 0;
}

#if !defined(__OpenBSD__)
static __inline__ unsigned int
swap32 (unsigned int x)
{

#if 1
  unsigned int y = 0;
  unsigned char *a = ((unsigned char *) &x) + 3;
  unsigned char *b = (unsigned char *) &y;

  *b++ = *a--;
  *b++ = *a--;
  *b++ = *a--;
  *b++ = *a--;

  return y;
#else
  /* Old version */
  return ((x & 0x000000ff) << 24) |
    ((x & 0x0000ff00) << 8) |
    ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24);
#endif
}
#endif

#ifdef SINE_DEBUG
#define SIN_STEPS	48
static short sinebuf[48] = {
  0, 4276, 8480, 12539, 16383, 19947, 23169, 25995,
  28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272,
  28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276,
  0, -4276, -8480, -12539, -16383, -19947, -23169, -25995,
  -28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272,
  -28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276
};
static int sine_ptr = 0;
#endif

static int
cnv_default (adev_p adev, dmap_p dmap, unsigned char **srcp, int *srcl, unsigned char **tgtp,
	     sample_parms * source, sample_parms * target)
{
  void *p1 = *srcp, *p2 = *tgtp, *tmpp;
  int len = *srcl, l, i;


Convert samples to 24 bit (32 bit lsb aligned) if necessary.

  VMEM_CHECK (p1, len);
  VMEM_CHECK (p2, len);
  if (source->fmt != AFMT_S24_NE)
    switch (source->fmt)
      {
      case AFMT_U8:
	{
	  unsigned char *fromp = p1;
	  unsigned int *top = p2;

	  l = len;
	  len = l * sizeof (int);

	  for (i = 0; i < l; i++)
	    {
	      *top++ = ((signed char) (*fromp++ ^ 0x80)) << 16;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S8:
	{
	  char *fromp = p1;
	  int *top = p2;
	  int t;

	  l = len;
	  len = l * sizeof (int);

	  for (i = 0; i < l; i++)
	    {
	      t = (*fromp++) << 24;
	      *top++ = t >> 8;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_MU_LAW:
	{
	  unsigned char *fromp = p1;
	  unsigned int *top = p2;

	  l = len;
	  len = l * sizeof (int);

	  for (i = 0; i < l; i++)
	    {
	      *top++ = ((signed char) (ulaw_dsp[(*fromp++)] ^ 0x80)) << 16;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S16_NE:
	{
	  short *fromp = p1;
	  int *top = p2;

	  l = len / 2;
	  len = l * sizeof (int);

	  for (i = 0; i < l; i++)
	    {
	      *top++ = *fromp++ << 8;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S16_OE:
	{
	  short *fromp = p1;
	  int *top = p2;

	  l = len / 2;
	  len = l * sizeof (int);

	  for (i = 0; i < l; i++)
	    {
	      short tmp;
	      int t;
	      tmp = *fromp++;
	      /* Try to maintain the sign bit by shifting 8 bits too far */
	      t = ((tmp & 0xff) << 24) | ((tmp & 0xff00) << 8);
	      *top++ = t >> 8;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S32_NE:
	{
	  int *fromp = p1;
	  int *top = p2;

	  l = len / 4;
	  /* len = l * sizeof (int); */

	  for (i = 0; i < l; i++)
	    {
	      *top++ = *fromp++ >> 8;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S32_OE:
	{
	  int *fromp = p1;
	  int *top = p2;

	  l = len / 4;
	  /* len = l * sizeof (int); */

	  for (i = 0; i < l; i++)
	    {
	      *top++ = swap32 (*fromp++) >> 8;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S24_OE:
	{
	  int *fromp = p1;
	  int *top = p2;

	  l = len / 4;
	  /* len = l * sizeof (int); */

	  for (i = 0; i < l; i++)
	    {
	      *top++ = swap32 ((unsigned int) (*fromp++));
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S24_PACKED:
	{
	  unsigned char *fromp = p1;
	  int *top = p2;

	  l = len / 3;
	  len = l * sizeof (int);

	  for (i = 0; i < l; i++)
	    {
	      int tmp = 0;

#if 1
	      tmp |= (*fromp++);
	      tmp |= (*fromp++) << 8;
	      tmp |= (*fromp++) << 16;
#else
	      tmp |= (*fromp++) << 16;
	      tmp |= (*fromp++) << 8;
	      tmp |= (*fromp++);
#endif

	      *top++ = tmp;

	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      default:
	cmn_err (CE_WARN, "Unsupported conversion source format %08x\n",
		 source->fmt);
	return OSS_EIO;
      }

  if (source->rate != target->rate && source->channels <= target->channels)
    {
      int err;

      VMEM_CHECK (p1, len);
      err =
	do_src (adev, dmap, source->rate, target->rate, p1, p2, &len,
		source->channels, 32, 4);
      if (err < 0)
	return err;

      tmpp = p1;
      p1 = p2;
      p2 = tmpp;
    }


Convert between mono and stereo


  if (source->channels != target->channels)
    {
      VMEM_CHECK (p1, len);
      if (source->channels == 1 && target->channels == 2)
	{
	  /* Mono -> Stereo */
	  int *fromp = p1, *top = p2;

	  l = len / sizeof (int);	/* Number of (mono) samples */
	  len = len * 2;	/* Number of bytes will get doubled */
	  for (i = 0; i < l; i++)
	    {
	      *top++ = *fromp;
	      *top++ = *fromp++;
	    }
	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
      else if (source->channels == 2 && target->channels == 1)
	{
	  /* Stereo -> mono */
	  int *fromp = p1, *top = p2;

	  len = len / 2;	/* Number of bytes will drop to a half */
	  l = len / sizeof (int);	/* Number of (mono) samples */

	  for (i = 0; i < l; i++)
	    {
	      *top++ = *fromp++;	/* Take just the left channel sample */
	      fromp++;		/* discard the right channel one */
	    }
	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
      else
	{
	  /* Multi channel conversions */

	  int *fromp = p1, *top = p2;
	  int n, nc, sc, tc;

	  l = len / sizeof (int);
	  n = l / source->channels;
	  len = len * target->channels / source->channels;

	  tc = target->channels;
	  sc = source->channels;
	  nc = tc;
	  if (nc > sc)
	    nc = sc;

	  memset (top, 0, len);

	  for (i = 0; i < n; i++)
	    {
	      int c;

	      for (c = 0; c < nc; c++)
		top[c] = fromp[c];

	      fromp += sc;
	      top += tc;
	    }
	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
    }

  if (source->rate != target->rate && source->channels > target->channels)
    {
      int err;

      VMEM_CHECK (p1, len);
      err =
	do_src (adev, dmap, source->rate, target->rate, p1, p2, &len,
		target->channels, 24, 4);
      if (err < 0)
	return err;

      tmpp = p1;
      p1 = p2;
      p2 = tmpp;
    }

#ifdef SINE_DEBUG

Debugging stuff. Overwrite the sample buffer with pure sine wave.

  {
    int *p = p1;
    l = len / sizeof (int);
    for (i = 0; i < l; i += 2)
      {
#if 1
	*p++ = sinebuf[sine_ptr] << 15;	/* Left chn */
	*p++ = sinebuf[sine_ptr] << 15;	/* Right chn */
	sine_ptr = (sine_ptr + 1) % SIN_STEPS;
#else
	static short pp = 0;
	*p++ = pp * 256;
	*p++ = pp * 256;
	pp++;
#endif
      }
  }
#endif


Finally convert the samples from internal 24 bit format to the target format


  if (target->fmt != AFMT_S24_NE)
    switch (target->fmt)
      {
      case AFMT_U8:
	{
	  unsigned int *fromp = p1;
	  unsigned char *top = p2;

	  l = len / sizeof (int);
	  len = l;

	  for (i = 0; i < l; i++)
	    {
	      *top++ = (*fromp++ >> 16) ^ 0x80;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S8:
	{
	  int *fromp = p1;
	  char *top = p2;

	  l = len / sizeof (int);
	  len = l;

	  for (i = 0; i < l; i++)
	    {
	      *top++ = *fromp++ >> 16;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S16_NE:
	{
	  int *fromp = p1;
	  short *top = p2;

	  l = len / sizeof (int);
	  len = l * sizeof (short);

	  for (i = 0; i < l; i++)
	    {
	      *top++ = *fromp++ >> 8;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S16_OE:
	{
	  int *fromp = p1;
	  unsigned char *top = p2;

	  l = len / sizeof (int);
	  len = l * sizeof (short);

	  for (i = 0; i < l; i++)
	    {
	      int tmp;
	      tmp = *fromp++ >> 8;
	      *top++ = tmp & 0xff;
	      *top++ = (tmp >> 8) & 0xff;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S32_NE:
	{
	  int *fromp = p1;
	  int *top = p2;

	  l = len / 4;
	  /* len = l * sizeof (int); */

	  for (i = 0; i < l; i++)
	    {
	      *top++ = *fromp++ << 8;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S32_OE:
	{
	  int *fromp = p1;
	  int *top = p2;

	  l = len / 4;
	  /* len = l * sizeof (int); */

	  for (i = 0; i < l; i++)
	    {
	      *top++ = swap32 ((unsigned int) (*fromp++ << 8));
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S24_OE:
	{
	  int *fromp = p1;
	  int *top = p2;

	  l = len / 4;
	  /* len = l * sizeof (int); */

	  for (i = 0; i < l; i++)
	    {
	      *top++ = swap32 ((unsigned int) (*fromp++));
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_S24_PACKED:
	{
	  int *fromp = p1;
	  unsigned char *top = p2;

	  l = len / 4;
	  len = l * 3;

	  for (i = 0; i < l; i++)
	    {
	      int tmp = *fromp++;
	      *top++ = tmp & 0xff;
	      *top++ = (tmp >> 8) & 0xff;
	      *top++ = (tmp >> 16) & 0xff;
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      case AFMT_MU_LAW:
	{
	  unsigned int *fromp = p1;
	  unsigned char *top = p2;

	  l = len / sizeof (int);
	  len = l;

	  for (i = 0; i < l; i++)
	    {
	      *top++ = dsp_ulaw[((*fromp++ >> 16) & 0xff) ^ 0x80];
	    }

	  tmpp = p1;
	  p1 = p2;
	  p2 = tmpp;
	}
	break;

      default:
	cmn_err (CE_WARN, "Unsupported conversion target format %08x\n",
		 target->fmt);
	return OSS_EIO;
      }

  VMEM_CHECK (p1, len);
  *srcl = len;
  *srcp = p1;
  *tgtp = p2;

  return 0;
}

static int
cnv_srconly (adev_p adev, dmap_p dmap, unsigned char **srcp, int *srcl, unsigned char **tgtp,
	     sample_parms * source, sample_parms * target)
{
  void *p1 = *srcp, *p2 = *tgtp, *tmpp;
  int len = *srcl;
  int err;
  int ssize = 2;
  audio_format_info_p fmt;

  VMEM_CHECK (p1, len);
  VMEM_CHECK (p2, len);

  if ((fmt = oss_find_format (source->fmt)) == NULL)
    {
      cmn_err (CE_WARN, "Unknown audio format %x\n", source->fmt);
      return OSS_EIO;
    }

  switch (fmt->bits)
    {
    default:
    case 8:
      ssize = 1;
      break;
    case 16:
      ssize = 2;
      break;
    case 24:
      ssize = 4;
      break;
    case 32:
      ssize = 4;
      break;
    }

  err =
    do_src (adev, dmap, source->rate, target->rate, p1, p2, &len,
	    source->channels, fmt->bits, ssize);
  if (err < 0)
    return err;

  tmpp = p1;
  p1 = p2;
  p2 = tmpp;

  VMEM_CHECK (p1, len);
  *srcl = len;
  *srcp = p1;
  *tgtp = p2;

  return 0;
}

#include "audiocnv.inc"

static cnv_func_t
select_converter (unsigned int cnv, int expand, audio_format_info_p src_f,
		  audio_format_info_p tgt_f)
{
#if 1
  {
    int expand2 = expand * 100 / UNIT_EXPAND;
    DDB (cmn_err
	 (CE_CONT, "Expand = %d (%d.%02d)\n", expand, expand2 / 100,
	  expand2 % 100));
    DDB (cmn_err (CE_CONT, "Convert = %08x / ", cnv));
    if (cnv & CNV_SRC)
      DDB (cmn_err (CE_CONT, "CNV_SRC "));
    if (cnv & CNV_M2S)
      DDB (cmn_err (CE_CONT, "CNV_M2S "));
    if (cnv & CNV_S2M)
      DDB (cmn_err (CE_CONT, "CNV_S2M "));
    if (cnv & CNV_MULT)
      DDB (cmn_err (CE_CONT, "CNV_MULT "));
    if (cnv & CNV_Flog)
      DDB (cmn_err (CE_CONT, "CNV_Flog "));
    if (cnv & CNV_F8bit)
      DDB (cmn_err (CE_CONT, "CNV_F8bit "));
    if (cnv & CNV_F16bit)
      DDB (cmn_err (CE_CONT, "CNV_F16bit "));
    if (cnv & CNV_F24bit)
      DDB (cmn_err (CE_CONT, "CNV_F24bit "));
    if (cnv & CNV_F32bit)
      DDB (cmn_err (CE_CONT, "CNV_F32bit "));
    if (cnv & CNV_Tlog)
      DDB (cmn_err (CE_CONT, "CNV_Tlog "));
    if (cnv & CNV_T8bit)
      DDB (cmn_err (CE_CONT, "CNV_T8bit "));
    if (cnv & CNV_T16bit)
      DDB (cmn_err (CE_CONT, "CNV_T16bit "));
    if (cnv & CNV_T24bit)
      DDB (cmn_err (CE_CONT, "CNV_T24bit "));
    if (cnv & CNV_T32bit)
      DDB (cmn_err (CE_CONT, "CNV_T32bit "));
    if (cnv & CNV_SIGN)
      DDB (cmn_err (CE_CONT, "CNV_SIGN "));
    if (cnv & CNV_ENDIAN)
      DDB (cmn_err (CE_CONT, "CNV_ENDIAN "));
    if (cnv & CNV_TOLOG)
      DDB (cmn_err (CE_CONT, "CNV_TOLOG "));
    if (cnv & CNV_FROMLOG)
      DDB (cmn_err (CE_CONT, "CNV_FROMLOG "));
    DDB (cmn_err (CE_CONT, "\n"));
  }
  DDB (cmn_err (CE_CONT, "\n"));
#endif

  if (cnv == CNV_SRC)		/* Only SRC is needed */
    if (src_f->fmt & (CONVERTABLE_FORMATS & ~AFMT_MU_LAW))	/* Can convert this */
#ifdef OSS_BIG_ENDIAN
      if (src_f->endianess == ENDIAN_BIG)
#else
      if (src_f->endianess == ENDIAN_LITTLE)
#endif
	{
	  DDB (cmn_err (CE_CONT, "Only SRC\n"));
	  return cnv_srconly;
	}

  if (cnv & CNV_SRC)
    {				/* Needs SRC together with some other conversion. Use the default. */
      return cnv_default;
    }

  if (src_f->endianess != tgt_f->endianess)
    {				/* Needs endianess handling - use the default for the time being. */
      return cnv_default;
    }

#ifdef OSS_BIG_ENDIAN
  if (src_f->endianess != ENDIAN_BIG)
#else
  if (src_f->endianess != ENDIAN_LITTLE)
#endif
    {				/* Opposite endianess - use the default. */
      return cnv_default;
    }


The remaining conversions don't use SRC and the endianess is native so they are easier to optimize.


  switch (cnv)
    {
    case CNV_F8bit | CNV_T16bit:
      return cnv_F8bits_T16bits;
    case CNV_F8bit | CNV_T32bit:
      return cnv_F8bits_T32bits;
    case CNV_F16bit | CNV_T32bit:
      return cnv_F16bits_T32bits;
    case CNV_F32bit | CNV_T16bit:
      return cnv_F32bits_T16bits;
    case CNV_F32bit | CNV_T8bit:
      return cnv_F32bits_T8bits;
    case CNV_F16bit | CNV_T8bit:
      return cnv_F16bits_T8bits;
    }

  DDB (cmn_err (CE_CONT, "Will use the default converter\n"));
  return cnv_default;
}

/*ARGSUSED*/
int
setup_format_conversions (adev_p adev, dmap_p dmap, sample_parms * source,
			  sample_parms * target,
			  sample_parms * user,
			  sample_parms * device, int format_mask)
{
  int expand = UNIT_EXPAND;
  unsigned int cnv = CNV_NONE;
  audio_format_info_p src_f, tgt_f;

  dmap->expand_factor = UNIT_EXPAND;

  if (source->fmt == AFMT_AC3)
    {
      source->channels = target->channels = 2;
      source->rate = target->rate;
      target->fmt = source->fmt;
      dmap->convert_mode = 0;
      dmap->convert_func = NULL;
      return 0;
    }

  if ((src_f = oss_find_format (source->fmt)) == NULL)
    {
      cmn_err (CE_CONT, "internal format error 1 (%x)\n", source->fmt);
      return OSS_EIO;
    }

  if ((tgt_f = oss_find_format (target->fmt)) == NULL)
    {
      cmn_err (CE_CONT, "internal format error 2\n");
      return OSS_EIO;
    }

#ifdef DO_TIMINGS
    oss_timing_printf ("Setting up format conversions for device %d",
	     adev->engine_num);
    oss_timing_printf ("  Speed %d->%d", source->rate, target->rate);
    oss_timing_printf ("  Channels %d->%d", source->channels, target->channels);
    oss_timing_printf ("  Format %02x/%s->%02x/%s", source->fmt, src_f->name,
	     target->fmt, tgt_f->name);
#endif
  DDB (cmn_err
       (CE_CONT, "Setting up format conversions for device %d\n",
	adev->engine_num));
  DDB (cmn_err (CE_CONT, "  Speed %d->%d\n", source->rate, target->rate));
  DDB (cmn_err
       (CE_CONT, "  Channels %d->%d\n", source->channels, target->channels));
  DDB (cmn_err
       (CE_CONT, "  Format %02x/%s->%02x/%s\n", source->fmt, src_f->name,
	target->fmt, tgt_f->name));

  if (source->channels > OSS_MAX_CONVERT_CHANNELS
      || target->channels > OSS_MAX_CONVERT_CHANNELS)
    {
      cmn_err
	(CE_WARN,
	 "Audio format conversions not supported with more than %d channels\n",
	 OSS_MAX_CONVERT_CHANNELS);
      dmap->flags &= ~DMAP_COOKED;
      return OSS_EIO;
    }

  expand = (expand * target->rate) / source->rate;
  expand = (expand * target->channels) / source->channels;
  expand = (expand * tgt_f->bits) / src_f->bits;

  dmap->expand_factor = expand;

  if (source->rate != target->rate)
    cnv |= CNV_SRC;

  if (source->channels != target->channels)	/* Change # of channels */
    {
      if (target->channels > 2 || source->channels > 2)
	cnv |= CNV_MULT;
      else
	{
	  if (source->channels == 1 && target->channels == 2)
	    cnv |= CNV_M2S;
	  else
	    cnv |= CNV_S2M;
	}
    }

  if (source->fmt != target->fmt)
    {

      if (src_f->endianess != tgt_f->endianess)
	if (src_f->endianess != ENDIAN_NONE
	    && tgt_f->endianess != ENDIAN_NONE)
	  {			/* Endianess change */
	    cnv |= CNV_ENDIAN;
	  }

      if (src_f->endianess != ENDIAN_NONE && tgt_f->endianess != ENDIAN_NONE)
	if (src_f->is_signed != tgt_f->is_signed)
	  {
	    cnv |= CNV_SIGN;
	  }

      if (src_f->bits != tgt_f->bits)
	{
	  switch (src_f->bits)
	    {
	    case 8:
	      cnv |= CNV_F8bit;
	      break;
	    case 16:
	      cnv |= CNV_F16bit;
	      break;
	    case 24:
	      cnv |= CNV_F24bit;
	      break;
	    case 32:
	      cnv |= CNV_F32bit;
	      break;
	    default:
	      cnv |= CNV_Flog;
	    }

	  switch (tgt_f->bits)
	    {
	    case 8:
	      cnv |= CNV_T8bit;
	      break;
	    case 16:
	      cnv |= CNV_T16bit;
	      break;
	    case 24:
	      cnv |= CNV_T24bit;
	      break;
	    case 32:
	      cnv |= CNV_T32bit;
	      break;
	    default:
	      cnv |= CNV_Tlog;
	    }
	}

      if (!src_f->is_linear)
	cnv |= CNV_FROMLOG;

      if (!tgt_f->is_linear)
	cnv |= CNV_TOLOG;
    }

  dmap->convert_mode = cnv;

  dmap->convert_func = select_converter (cnv, expand, src_f, tgt_f);

  dmap->tmpbuf_len = dmap->tmpbuf_ptr = 0;
  if (dmap->tmpbuf1 == NULL)
    {
      dmap->tmpbuf1 = PMALLOC (dmap->osdev, TMP_CONVERT_BUF_SIZE+512);
    }

  if (dmap->tmpbuf2 == NULL)
    {
      dmap->tmpbuf2 = PMALLOC (dmap->osdev, TMP_CONVERT_BUF_SIZE+512);
    }

  if (dmap->tmpbuf1 == NULL || dmap->tmpbuf2 == NULL)
    return OSS_ENOSPC;

  if (cnv & CNV_SRC)
    if (setup_src
	(adev, dmap, source->rate, target->rate, source->channels,
	 target->channels) < 0)
      {
	cmn_err (CE_CONT, "internal format error 3\n");
	return OSS_EIO;
      }

  return 0;
}

Copyright (C) 4Front Technologies, 2007. All rights reserved.

Back to index OSS web site


Copyright (C) 4Front Technologies, 2007. All rights reserved.
Back to index OSS web site