[xiph-commits] r16982 - in trunk/ao: include/ao src src/plugins/alsa src/plugins/arts src/plugins/esd src/plugins/irix src/plugins/macosx src/plugins/nas src/plugins/oss src/plugins/pulse src/plugins/sun

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Thu Mar 18 12:50:05 PDT 2010


Author: xiphmont
Date: 2010-03-18 12:50:04 -0700 (Thu, 18 Mar 2010)
New Revision: 16982

Modified:
   trunk/ao/include/ao/ao.h
   trunk/ao/include/ao/ao_private.h
   trunk/ao/src/ao_aixs.c
   trunk/ao/src/ao_au.c
   trunk/ao/src/ao_null.c
   trunk/ao/src/ao_raw.c
   trunk/ao/src/ao_wav.c
   trunk/ao/src/ao_wmm.c
   trunk/ao/src/audio_out.c
   trunk/ao/src/plugins/alsa/ao_alsa.c
   trunk/ao/src/plugins/arts/ao_arts.c
   trunk/ao/src/plugins/esd/ao_esd.c
   trunk/ao/src/plugins/irix/ao_irix.c
   trunk/ao/src/plugins/macosx/ao_macosx.c
   trunk/ao/src/plugins/nas/ao_nas.c
   trunk/ao/src/plugins/oss/ao_oss.c
   trunk/ao/src/plugins/pulse/ao_pulse.c
   trunk/ao/src/plugins/sun/ao_sun.c
Log:
Centralize most of the channel matrix setup processing that drivers were doing independently.

Update Windows WMM driver to support matrixing



Modified: trunk/ao/include/ao/ao.h
===================================================================
--- trunk/ao/include/ao/ao.h	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/include/ao/ao.h	2010-03-18 19:50:04 UTC (rev 16982)
@@ -50,6 +50,7 @@
 #define AO_EOPENDEVICE 5
 #define AO_EOPENFILE   6
 #define AO_EFILEEXISTS 7
+#define AO_EBADFORMAT  8
 
 #define AO_EFAIL       100
 

Modified: trunk/ao/include/ao/ao_private.h
===================================================================
--- trunk/ao/include/ao/ao_private.h	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/include/ao/ao_private.h	2010-03-18 19:50:04 UTC (rev 16982)
@@ -63,14 +63,21 @@
 	char *default_driver;
 } ao_config;
 
+typedef enum {
+  AO_OUTPUT_MATRIX_UNDEFINED=0,   /* matrix unset */
+  AO_OUTPUT_MATRIX_FIXED=1,       /* fixed, immutable channel order, eg, ALSA */
+  AO_OUTPUT_MATRIX_COLLAPSIBLE=2, /* fixed order but only used channels sent, eg MACOS */
+  AO_OUTPUT_MATRIX_PERMUTABLE=3,  /* channel map is fully permutable. eg Pulse */
+} ao_outorder;
+
 struct ao_device {
 	int  type; /* live output or file output? */
 	int  driver_id;
 	ao_functions *funcs;
 	FILE *file; /* File for output if this is a file driver */
 
-  /* input not necessarily == output. Right now, byte order and
-     channel mappings may be altered. */
+  /* input not necessarily == output. Right now, byte order, channel
+     count, and channel mappings may be altered. */
 
 	int  client_byte_format;
 	int  machine_byte_format;
@@ -82,10 +89,34 @@
         int output_channels;
         int bytewidth;
         int rate;
-        char *output_matrix;
-        int  *permute_channels;
-	void *internal; /* Pointer to driver-specific data */
 
+        ao_outorder output_matrix_order;
+        char *output_matrix;  /* physical output channel
+                                 ordering/numbering matrix set by
+                                 driver if there's a channel
+                                 name->number mapping useful to the
+                                 backend driver in some way.  Eg,
+                                 Pulse has fully permutable input
+                                 channel masks, but specific channels
+                                 locations (eg, 'Center') still have
+                                 assigned numbers even if not a
+                                 specific slot int he input
+                                 interleave. */
+        int   output_mask;
+        int  *input_map;      /* input permutation mapping from each
+                                 input channel to a location in the
+                                 output_matrix. Made by ao_open,
+                                 intended for convenience use by
+                                 driver in device open. */
+
+        char *inter_matrix;   /* channel matrix as presented to the
+                                 backend API */
+        int  *inter_permute;  /* maps from each channel in the
+                                 inter_matrix back to an input channel
+                                 (if any) */
+
+	void *internal;       /* Pointer to driver-specific data */
+
         int verbose;
 };
 

Modified: trunk/ao/src/ao_aixs.c
===================================================================
--- trunk/ao/src/ao_aixs.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/ao_aixs.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -108,6 +108,7 @@
 	}
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
 
 	return 1; /* Memory alloc successful */
 }
@@ -140,7 +141,7 @@
 
 	init.srate = format->rate;
 	init.bits_per_sample = format->bits;
-	init.channels = format->channels;
+	init.channels = device->output_channels;
 	init.mode = AUDIO_PCM;
 	init.flags = AUDIO_BIG_ENDIAN | AUDIO_TWOS_COMPLEMENT;
 	init.operation = AUDIO_PLAY;
@@ -174,11 +175,10 @@
 	}
 
 	device->driver_byte_format = AO_FMT_NATIVE;
-
-        if(!device->output_matrix){
-          /* set up out matrix such that users are warned about > stereo playback */
-          if(format->channels<=2)
-            device->output_matrix=strdup("L,R");
+        if(!device->inter_matrix){
+          /* set up matrix such that users are warned about > stereo playback */
+          if(device->output_channels<=2)
+            device->inter_matrix=strdup("L,R");
           //else no matrix, which results in a warning
         }
 

Modified: trunk/ao/src/ao_au.c
===================================================================
--- trunk/ao/src/ao_au.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/ao_au.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -112,6 +112,7 @@
 	memset(&(internal->au), 0, sizeof(internal->au));
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
 
 	return 1; /* Memory alloc successful */
 }
@@ -134,7 +135,7 @@
 
 	/* Fill out the header */
 	internal->au.magic = AUDIO_FILE_MAGIC;
-	internal->au.channels = format->channels;
+	internal->au.channels = device->output_channels;
 	if (format->bits == 8)
 		internal->au.encoding = AUDIO_FILE_ENCODING_LINEAR_8;
 	else if (format->bits == 16)
@@ -168,13 +169,14 @@
 		return 0; /* Error writing header */
 	}
 
-        if(!device->output_matrix){
-          /* set up out matrix such that users are warned about > stereo playback */
-          if(format->channels<=2)
-            device->output_matrix=strdup("L,R");
+        if(!device->inter_matrix){
+          /* set up matrix such that users are warned about > stereo playback */
+          if(device->output_channels<=2)
+            device->inter_matrix=strdup("L,R");
           //else no matrix, which results in a warning
         }
 
+
 	return 1;
 }
 

Modified: trunk/ao/src/ao_null.c
===================================================================
--- trunk/ao/src/ao_null.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/ao_null.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -77,6 +77,7 @@
 	internal->byte_counter = 0;
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
 
 	return 1; /* Memory alloc successful */
 }
@@ -97,9 +98,10 @@
 	/* Use whatever format the client requested */
 	device->driver_byte_format = device->client_byte_format;
 
-        if(!device->output_matrix){
-          /* all channels 'unavailable' */
-          device->output_matrix=strdup("X");
+        if(!device->inter_matrix){
+          /* by default, we want inter == in */
+          if(format->matrix)
+            device->inter_matrix = strdup(format->matrix);
         }
 
 	return 1;
@@ -121,7 +123,7 @@
 {
 	ao_null_internal *internal = (ao_null_internal *) device->internal;
 
-	adebug("%ld bytes sent to null device.\n");
+	adebug("%ld bytes sent to null device.\n", internal->byte_counter);
 
 	return 1;
 }

Modified: trunk/ao/src/ao_raw.c
===================================================================
--- trunk/ao/src/ao_raw.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/ao_raw.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -77,6 +77,7 @@
 	internal->byte_order = AO_FMT_NATIVE;
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
 
 	return 1; /* Memory alloc successful */
 }
@@ -107,11 +108,11 @@
 
 	device->driver_byte_format = internal->byte_order;
 
-        if(!device->output_matrix){
-          /* by default, out == in */
-          if(format->matrix)
-            device->output_matrix = strdup(format->matrix);
-        }
+        //if(!device->inter_matrix){
+        ///* by default, inter == in */
+        //if(format->matrix)
+        //  device->inter_matrix = strdup(format->matrix);
+        //}
 
 	return 1;
 }

Modified: trunk/ao/src/ao_wav.c
===================================================================
--- trunk/ao/src/ao_wav.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/ao_wav.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -134,6 +134,8 @@
 	memset(&(internal->wave), 0, sizeof(internal->wave));
 
 	device->internal = internal;
+        device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR");
+        device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE;
 
 	return 1; /* Memory alloc successful */
 }
@@ -145,51 +147,6 @@
 	return 1; /* No options! */
 }
 
-static char *map[]={
-  "L","R","C","LFE","BL","BR","CL","CR","BC","SL","SR",NULL
-};
-#define SPEAKER_RESERVED               0x80000000
-
-static unsigned int _matrix_to_channelmask(char *matrix){
-  unsigned int ret=0;
-  char *p=matrix;
-  while(1){
-    char *h=p;
-    int m=0;
-
-    /* search for seperator */
-    while(*h && *h!=',')h++;
-
-    while(map[m]){
-      if(h-p && !strncmp(map[m],p,h-p) &&
-         strlen(map[m])==h-p)
-        break;
-      m++;
-    }
-    if(map[m])
-      ret |= (1<<m);
-    if(!*h)break;
-    p=h+1;
-  }
-  return ret;
-}
-
-static char *_channelmask_to_matrix(unsigned int mask){
-  int m=0;
-  int count=0;
-  char buffer[80]={0};
-  while(map[m]){
-    if(mask & (1<<m)){
-      if(count)
-        strcat(buffer,",");
-      strcat(buffer,map[m]);
-      count++;
-    }
-    m++;
-  }
-  return strdup(buffer);
-}
-
 static int ao_wav_open(ao_device *device, ao_sample_format *format)
 {
 	ao_wav_internal *internal = (ao_wav_internal *) device->internal;
@@ -197,7 +154,7 @@
 	int size = 0x7fffffff; /* Use a bogus size initially */
 
 	/* Store information */
-	internal->wave.common.wChannels = format->channels;
+	internal->wave.common.wChannels = device->output_channels;
 	internal->wave.common.wBitsPerSample = ((format->bits+7)>>3)<<3;
 	internal->wave.common.wValidBitsPerSample = format->bits;
 	internal->wave.common.dwSamplesPerSec = format->rate;
@@ -223,20 +180,8 @@
 		(internal->wave.common.wBitsPerSample >> 3);
 	internal->wave.common.cbSize = 22;
 	internal->wave.common.subFormat = WAVE_FORMAT_PCM;
+        internal->wave.common.dwChannelMask=device->output_mask;
 
-        /* contruct channel mask; WAV is capable of expressing any
-           channel format currently representable in libao */
-        if(format->matrix){
-          if(device->output_matrix){
-            internal->wave.common.dwChannelMask=_matrix_to_channelmask(device->output_matrix);
-          }else{
-            internal->wave.common.dwChannelMask=_matrix_to_channelmask(format->matrix);
-            device->output_matrix = _channelmask_to_matrix(internal->wave.common.dwChannelMask);
-          }
-        }else{
-          internal->wave.common.dwChannelMask=0;
-        }
-
 	strncpy(internal->wave.data.id, "data",4);
 
 	internal->wave.data.len = size - WAV_HEADER_LEN;

Modified: trunk/ao/src/ao_wmm.c
===================================================================
--- trunk/ao/src/ao_wmm.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/ao_wmm.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -196,44 +196,12 @@
 
   res = internal != NULL;
 
+  device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR");
+  device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE;
+
   return res;
 }
 
-#if 0
-static void CALLBACK
-waveOutProc(HWAVEOUT hwo,
-            UINT uMsg, DWORD_PTR dwInstance,
-            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
-{
-  ao_device *device = (ao_device *) dwInstance;
-  ao_wmm_internal *internal = (ao_wmm_internal *) device->internal;
-
-  switch (uMsg) {
-
-  case WOM_OPEN:
-    /* Sent when the device is opened using the waveOutOpen function. */
-    break;
-
-  case WOM_CLOSE:
-    /* Sent when the device is closed using the waveOutClose function. */
-    break;
-
-  case WOM_DONE:
-    /* Sent when the device driver is finished with a data block sent
-       using the waveOutWrite function. */
-    {
-      LPWAVEHDR lpwvhdr = (LPWAVEHDR) dwParam1;
-      int me = lpwvhdr - internal->wh;
-      lpwvhdr->dwBytesRecorded = 0;
-    }
-    break;
-
-  default:
-    break;
-  }
-}
-#endif
-
 static int _ao_open_device(ao_device *device)
 {
   ao_wmm_internal *internal = (ao_wmm_internal *) device->internal;
@@ -421,10 +389,10 @@
 {
   ao_wmm_internal *internal = (ao_wmm_internal *) device->internal;
   int res = 0;
-  WAVEFORMATEX wavefmt;
+  WAVEFORMATEXTENSIBLE wavefmt;
 
   adebug("open() channels=%d, bits=%d, rate=%d, format %d(%s)\n",
-         format->channels,format->bits,format->rate,format->byte_format,
+         device->output_channels,format->bits,format->rate,format->byte_format,
          format->byte_format==AO_FMT_LITTLE
          ?"little"
          :(format->byte_format==AO_FMT_NATIVE
@@ -440,26 +408,22 @@
   format->byte_format = AO_FMT_LITTLE;
   device->driver_byte_format = AO_FMT_LITTLE;
 
-  if (! (format->channels == 1 || format->channels ==  2) ||
-      ! (format->bits     == 8 || format->bits     == 16) ||
-      ! (format->rate  >= 6000 && format->rate  <= 50000)
-      ) {
-    aerror("open() => unable to handle input format\n");
-    goto error;
-  }
-
   /* $$$ WMM 8 bit samples are unsigned... Not sure for ao ... */
   /* Yes, ao 8 bit PCM is unsigned -- Monty */
 
   /* Make sample format */
   memset(&wavefmt,0,sizeof(wavefmt));
-  wavefmt.wFormatTag      = WAVE_FORMAT_PCM;
-  wavefmt.nChannels       = format->channels;
-  wavefmt.wBitsPerSample  = format->bits;
-  wavefmt.nSamplesPerSec  = format->rate;
-  wavefmt.nBlockAlign     = ((wavefmt.wBitsPerSample+7)>>3)*wavefmt.nChannels;
-  wavefmt.nAvgBytesPerSec = wavefmt.nSamplesPerSec*wavefmt.nBlockAlign;
-  wavefmt.cbSize          = 0;
+  wavefmt.wFormatTag          = WAVE_FORMAT_EXTENSIBLE;
+  wavefmt.nChannels           = device->output_channels;
+  wavefmt.wBitsPerSample      = (((format->bits+7)>>3)<<3);
+  wavefmt.nSamplesPerSec      = format->rate;
+  wavefmt.nBlockAlign         = (wavefmt.wBitsPerSample>>3)*wavefmt.nChannels;
+  wavefmt.nAvgBytesPerSec     = wavefmt.nSamplesPerSec*wavefmt.nBlockAlign;
+  wavefmt.cbSize              = 22;
+  wavefmt.wValidBitsPerSample = format->bits;
+  wavefmt.subFormat           = WAVE_FORMAT_PCM;
+  wavefmt.dwChannelMask       = device->output_mask;
+
   internal->wavefmt       = wavefmt;
 
   /* $$$ later this should be optionnal parms */

Modified: trunk/ao/src/audio_out.c
===================================================================
--- trunk/ao/src/audio_out.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/audio_out.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -424,7 +424,7 @@
 		device->swap_buffer_size = 0;
 		device->internal = NULL;
                 device->output_channels = format->channels;
-                device->permute_channels = NULL;
+                device->inter_permute = NULL;
                 device->output_matrix = NULL;
 	}
 
@@ -585,7 +585,8 @@
 
 /* the channel locations we know right now. code below assumes U is in slot 0, X in 1, M in 2 */
 static char *mnemonics[]={
-  "U","X","M",
+  "X",
+  "M",
   "L","C","R","CL","CR","SL","SR","BL","BC","BR","LFE",
   "A1","A2","A3","A4","A5","A6","A7","A8","A9","A10",
   "A11","A12","A13","A14","A15","A16","A17","A18","A19","A20",
@@ -593,13 +594,13 @@
   "A31","A32",NULL
 };
 
-/* Check the requested maxtrix string for syntax and mnemonics */
-static char *_sanitize_matrix(char *matrix, ao_device *device){
+/* Check the requested matrix string for syntax and mnemonics */
+static char *_sanitize_matrix(int maxchannels, char *matrix, ao_device *device){
   if(matrix){
     char *ret = calloc(strlen(matrix)+1,1); /* can only get smaller */
     char *p=matrix;
     int count=0;
-    while(1){
+    while(count<maxchannels){
       char *h,*t;
       int m=0;
 
@@ -633,8 +634,8 @@
         }
         free(ret);
         return NULL;
-      }
-      count++;
+      }else
+        count++;
       if(!*h)break;
       p=h+1;
     }
@@ -646,6 +647,10 @@
 static int _find_channel(int needle, char *haystack){
   char *p=haystack;
   int count=0;
+
+  /* X does not map to anything, including X! */
+  if(needle==0) return -1;
+
   while(1){
     char *h;
     int m=0;
@@ -668,20 +673,192 @@
   return -1;
 }
 
+static char **_tokenize_matrix(char *matrix){
+  char **ret=NULL;
+  char *p=matrix;
+  int count=0;
+  while(1){
+    char *h,*t;
+
+    /* trim leading space */
+    while(*p && isspace(*p))p++;
+
+    /* search for seperator */
+    h=p;
+    while(*h && *h!=',')h++;
+
+    /* trim trailing space */
+    t=h;
+    while(t>p && isspace(*(t-1)))t--;
+
+    count++;
+    if(!*h)break;
+    p=h+1;
+  }
+
+  ret = calloc(count+1,sizeof(*ret));
+
+  p=matrix;
+  count=0;
+  while(1){
+    char *h,*t;
+
+    /* trim leading space */
+    while(*p && isspace(*p))p++;
+
+    /* search for seperator */
+    h=p;
+    while(*h && *h!=',')h++;
+
+    /* trim trailing space */
+    t=h;
+    while(t>p && isspace(*(t-1)))t--;
+
+    ret[count++] = strndup(p,t-p);
+    if(!*h)break;
+    p=h+1;
+  }
+
+  return ret;
+
+}
+
+static void _free_map(char **m){
+  char **in=m;
+  while(m && *m){
+    free(*m);
+    m++;
+  }
+  if(in)free(in);
+}
+
+static unsigned int _matrix_to_channelmask(int ch, char *matrix, char *premap, int **mout){
+  unsigned int ret=0;
+  char *p=matrix;
+  int *perm=(*mout=malloc(ch*sizeof(*mout)));
+  int i;
+  char **map = _tokenize_matrix(premap);
+
+  for(i=0;i<ch;i++) perm[i] = -1;
+  i=0;
+
+  while(1){
+    char *h=p;
+    int m=0;
+
+    /* search for seperator */
+    while(*h && *h!=',')h++;
+
+    while(map[m]){
+      if(h-p && !strncmp(map[m],p,h-p) &&
+         strlen(map[m])==h-p)
+        break;
+      m++;
+    }
+    /* X is a placeholder, X does not map to X */
+    if(map[m] && strcmp(map[m],"X")){
+      ret |= (1<<m);
+      perm[i] = m;
+    }
+    if(!*h)break;
+    p=h+1;
+    i++;
+  }
+
+  _free_map(map);
+  return ret;
+}
+
+static char *_channelmask_to_matrix(unsigned int mask, char *premap){
+  int m=0;
+  int count=0;
+  char buffer[257]={0};
+  char **map = _tokenize_matrix(premap);
+
+  while(map[m]){
+    if(mask & (1<<m)){
+      if(count)
+        strcat(buffer,",");
+      strcat(buffer,map[m]);
+      count++;
+    }
+    m++;
+  }
+  _free_map(map);
+  return strdup(buffer);
+}
+
+static int _channelmask_bits(unsigned int mask){
+  int count=0;
+  while(mask){
+    if(mask&1)count++;
+    mask/=2;
+  }
+  return count;
+}
+
+static int _channelmask_maxbit(unsigned int mask){
+  int count=0;
+  int max=-1;
+  while(mask){
+    if(mask&1)max=count;
+    mask/=2;
+    count++;
+  }
+  return max;
+}
+
+static char *_matrix_intersect(char *matrix,char *premap){
+  char *p=matrix;
+  char buffer[257]={0};
+  int count=0;
+  char **map = _tokenize_matrix(premap);
+
+  while(1){
+    char *h=p;
+    int m=0;
+
+    /* search for seperator */
+    while(*h && *h!=',')h++;
+
+    while(map[m]){
+      if(h-p && !strncmp(map[m],p,h-p) &&
+         strlen(map[m])==h-p)
+        break;
+      m++;
+    }
+    /* X is a placeholder, X does not map to X */
+    if(map[m] && strcmp(map[m],"X")){
+      if(count)
+        strcat(buffer,",");
+      strcat(buffer,map[m]);
+      count++;
+    }
+
+    if(!*h)break;
+    p=h+1;
+  }
+
+  _free_map(map);
+  return strdup(buffer);
+}
+
+
 /* Open a device.  If this is a live device, file == NULL. */
 static ao_device* _open_device(int driver_id, ao_sample_format *format,
 			       ao_option *options, FILE *file)
 {
 	ao_functions *funcs;
 	driver_list *driver;
-	ao_device *device;
+	ao_device *device=NULL;
 	int result;
         ao_sample_format sformat=*format;
+        sformat.matrix=NULL;
 
 	/* Get driver id */
 	if ( (driver = _get_driver(driver_id)) == NULL ) {
 		errno = AO_ENODRIVER;
-		return NULL; /* No driver exists */
+		goto error;
 	}
 
 	funcs = driver->functions;
@@ -691,39 +868,46 @@
 	    funcs->driver_info()->type != AO_TYPE_LIVE) {
 
 		errno = AO_ENOTLIVE;
-		return NULL;
+		goto error;
 	} else if (file != NULL &&
 		   funcs->driver_info()->type != AO_TYPE_FILE) {
 
 		errno = AO_ENOTFILE;
-		return NULL;
+		goto error;
 	}
 
 	/* Make a new device structure */
 	if ( (device = _create_device(driver_id, driver,
 				      format, file)) == NULL ) {
 		errno = AO_EFAIL;
-		return NULL; /* Couldn't alloc device */
+		goto error;
 	}
 
-	/* Initialize the device memory */
+	/* Initialize the device memory; get static channel mapping */
 	if (!funcs->device_init(device)) {
-		free(device);
 		errno = AO_EFAIL;
-		return NULL; /* Couldn't init internal memory */
+		goto error;
 	}
 
 	/* Load options */
 	while (options != NULL) {
-          /* The output matrix option is handled for the drivers here */
           if(!strcmp(options->key,"matrix")){
+            /* If a driver has a static channel mapping mechanism
+               (physically constant channel mapping, or at least an
+               unvarying set of constants for mapping channels), the
+               output_matrix is already set.  An app/user specified
+               output mapping trumps. */
+            if(device->output_matrix)
+              free(device->output_matrix);
             /* explicitly set the output matrix to the requested
                string; devices must not override. */
-            device->output_matrix = _sanitize_matrix(options->value, device);
+            device->output_matrix = _sanitize_matrix(32, options->value, device);
             if(!device->output_matrix){
+              aerror("Empty or inavlid output matrix\n");
               errno = AO_EBADOPTION;
-              return NULL;
+              goto error;
             }
+            adebug("Sanitized device output matrix: %s\n",device->output_matrix);
           }else if(!strcmp(options->key,"debug")){
             device->verbose=2;
           }else if(!strcmp(options->key,"verbose")){
@@ -733,9 +917,8 @@
           }else{
             if (!funcs->set_option(device, options->key, options->value)) {
               /* Problem setting options */
-              free(device);
               errno = AO_EOPENDEVICE;
-              return NULL;
+              goto error;
             }
           }
 
@@ -744,12 +927,96 @@
 
         /* also sanitize the format input channel matrix */
         if(format->matrix){
-          sformat.matrix = _sanitize_matrix(format->matrix, device);
+          sformat.matrix = _sanitize_matrix(format->channels, format->matrix, device);
           if(!sformat.matrix)
             awarn("Input channel matrix invalid; ignoring.\n");
         }
 
-        /* set up any other housekeeping */
+        /* If device init was able to declare a static channel mapping
+           mechanism, reconcile it to the input now.  Odd drivers that
+           need to communicate with a backend device to determine
+           channel mapping strategy can still bypass this mechanism
+           entirely.  Also, drivers like ALSA may need to adjust
+           strategy depending on what device is successfully opened,
+           etc, but this still saves work later. */
+
+        if(device->output_matrix && sformat.matrix){
+          switch(device->output_matrix_order){
+          case AO_OUTPUT_MATRIX_FIXED:
+            /* Backend channel ordering is immutable and unused
+               channels must still be sent.  Look for the highest
+               channel number we are able to map from the input
+               matrix. */
+            {
+              unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix,
+                                                         device->output_matrix,
+                                                         &device->input_map);
+              int max = _channelmask_maxbit(mask);
+              if(max<0){
+                aerror("Unable to map any channels from input matrix to output");
+                errno = AO_EBADFORMAT;
+                goto error;
+              }
+              device->output_channels = max+1;
+              device->output_mask = mask;
+              device->inter_matrix = strdup(device->output_matrix);
+            }
+            break;
+
+          case AO_OUTPUT_MATRIX_COLLAPSIBLE:
+            /* the ordering of channels submitted to the backend is
+               fixed, but only the channels in use should be present
+               in the audio stream */
+            {
+              unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix,
+                                                         device->output_matrix,
+                                                         &device->input_map);
+              int channels = _channelmask_bits(mask);
+              if(channels<0){
+                aerror("Unable to map any channels from input matrix to output");
+                errno = AO_EBADFORMAT;
+                goto error;
+              }
+              device->output_channels = channels;
+              device->output_mask = mask;
+              device->inter_matrix = _channelmask_to_matrix(mask,device->output_matrix);
+            }
+            break;
+
+          case AO_OUTPUT_MATRIX_PERMUTABLE:
+            /* The ordering of channels is freeform.  Only the
+               channels in use should be sent, and they may be sent in
+               any order.  If possible, leave the input ordering
+               unchanged */
+            {
+              unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix,
+                                                         device->output_matrix,
+                                                         &device->input_map);
+              int channels = _channelmask_bits(mask);
+              if(channels<0){
+                aerror("Unable to map any channels from input matrix to output");
+                errno = AO_EBADFORMAT;
+                goto error;
+              }
+              device->output_channels = channels;
+              device->output_mask = mask;
+              device->inter_matrix = _matrix_intersect(sformat.matrix,device->output_matrix);
+            }
+            break;
+
+          default:
+            aerror("Driver backend failed to set output ordering.\n");
+            errno = AO_EFAIL;
+            goto error;
+          }
+
+          adebug("Channel order submitted to backend: %s\n",device->inter_matrix);
+
+        }else{
+          device->output_channels = sformat.channels;
+        }
+
+        /* other housekeeping */
         device->input_channels = sformat.channels;
         device->bytewidth = (sformat.bits+7)>>3;
         device->rate = sformat.rate;
@@ -757,34 +1024,34 @@
 	/* Open the device */
 	result = funcs->open(device, &sformat);
 	if (!result) {
-                if(sformat.matrix)free(sformat.matrix);
-		funcs->device_clear(device);
-		free(device);
-		errno = AO_EOPENDEVICE;
-		return NULL; /* Couldn't open device */
+          errno = AO_EOPENDEVICE;
+          goto error;
 	}
 
-        /* resolve channel mapping request if any */
+        /* set up permutation based on finalized inter_matrix mapping */
+        /* The only way device->output_channels could be zero here is
+           if the driver has opted to ouput no channels (eg, the null
+           driver). */
         if(sformat.matrix){
-          if(!device->output_matrix){
-            awarn("Driver %s does not support channel matrixing;\n"
+          if(!device->inter_matrix){
+            awarn("Driver %s does not support automatic channel mapping;\n"
                  "continuing without routing channels to specific locations.\n\n",
                  info_table[device->driver_id]->short_name);
           }else{
 
-            /* walk thorugh the output matrix, match outputs to inputs */
-            char *op=device->output_matrix;
+            /* walk thorugh the inter matrix, match channles */
+            char *op=device->inter_matrix;
             int count=0;
-            device->permute_channels = calloc(device->output_channels,sizeof(int));
+            device->inter_permute = calloc(device->output_channels,sizeof(int));
 
             averbose("\n");
 
             while(count<device->output_channels){
-              int m=1,mm;
+              int m=0,mm;
               char *h=op;
 
               if(*op){
-                /* find mnemonic offset of output channel */
+                /* find mnemonic offset of inter channel */
                 while(*h && *h!=',')h++;
                 while(mnemonics[m]){
                   if(!strncmp(mnemonics[m],op,h-op))
@@ -794,22 +1061,22 @@
                 mm=m;
 
                 /* find match in input if any */
-                device->permute_channels[count] = _find_channel(m,sformat.matrix);
-                if(device->permute_channels[count] == -1 && sformat.channels == 1){
-                  device->permute_channels[count] = _find_channel(2,sformat.matrix);
-                  mm=2;
+                device->inter_permute[count] = _find_channel(m,sformat.matrix);
+                if(device->inter_permute[count] == -1 && sformat.channels == 1){
+                  device->inter_permute[count] = _find_channel(1,sformat.matrix);
+                  mm=1;
                 }
               }else
-                device->permute_channels[count] = -1;
+                device->inter_permute[count] = -1;
 
               /* display resulting mapping for now */
-              if(device->permute_channels[count]>=0){
-                averbose("Output %d (%s)\t <- input %d (%s)\n",
-                        count,mnemonics[m],device->permute_channels[count],
-                        mnemonics[mm]);
+              if(device->inter_permute[count]>=0){
+                averbose("input %d (%s)\t -> backend %d (%s)\n",
+                         device->inter_permute[count], mnemonics[mm],
+                         count,mnemonics[m]);
               }else{
-                averbose("Output %d (%s)\t %s\n",
-                        count,mnemonics[m],(m==1?"unmapped":"<- none"));
+                averbose("             \t    backend %d (%s)\n",
+                         count,mnemonics[m]);
               }
               count++;
               op=h;
@@ -821,13 +1088,13 @@
         }
 
         /* if there's no actual permutation to do, release the permutation vector */
-        if(device->permute_channels && device->output_channels == device->input_channels){
+        if(device->inter_permute && device->output_channels == device->input_channels){
           int i;
           for(i=0;i<device->output_channels;i++)
-            if(device->permute_channels[i]!=i)break;
+            if(device->inter_permute[i]!=i)break;
           if(i==device->output_channels){
-            free(device->permute_channels);
-            device->permute_channels=NULL;
+            free(device->inter_permute);
+            device->inter_permute=NULL;
           }
         }
 
@@ -846,7 +1113,7 @@
 
 	if ( (device->bytewidth>1 &&
               device->client_byte_format != device->driver_byte_format) ||
-             device->permute_channels){
+             device->inter_permute){
 
           result = _realloc_swap_buffer(device, DEF_SWAP_BUF_SIZE);
 
@@ -864,6 +1131,12 @@
 	/* If we made it this far, everything is OK. */
         if(sformat.matrix)free(sformat.matrix);
 	return device;
+
+ error:
+        if(sformat.matrix)
+          free(sformat.matrix);
+        ao_close(device);
+        return NULL;
 }
 
 
@@ -1016,7 +1289,7 @@
             int swap = (device->bytewidth>1 &&
                         device->client_byte_format != device->driver_byte_format);
             for(i=0;i<device->output_channels;i++){
-              int ic = device->permute_channels ? device->permute_channels[i] : i;
+              int ic = device->inter_permute ? device->inter_permute[i] : i;
               if(ic==-1){
                 _buffer_zero(device->swap_buffer,i,device->bytewidth,device->output_channels,
                              out_bytes);
@@ -1058,6 +1331,12 @@
 			free(device->swap_buffer);
                 if (device->output_matrix != NULL)
                         free(device->output_matrix);
+                if (device->input_map != NULL)
+                        free(device->input_map);
+                if (device->inter_matrix != NULL)
+                        free(device->inter_matrix);
+                if (device->inter_permute != NULL)
+                        free(device->inter_permute);
 		free(device);
 	}
 

Modified: trunk/ao/src/plugins/alsa/ao_alsa.c
===================================================================
--- trunk/ao/src/plugins/alsa/ao_alsa.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/alsa/ao_alsa.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -145,6 +145,8 @@
 	internal->access_mask = AO_ALSA_ACCESS_MASK;
 
 	device->internal = internal;
+        device->output_matrix = strdup("L,R,BL,BR,C,LFE,SL,SR");
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
 
 	return 1;
 }
@@ -242,14 +244,14 @@
 
 	/* set the number of channels */
 	err = snd_pcm_hw_params_set_channels(internal->pcm_handle,
-			params, (unsigned int)format->channels);
+			params, (unsigned int)device->output_channels);
 	if (err < 0){
           adebug("snd_pcm_hw_params_set_channels() failed.\n");
           return err;
         }
 
 	/* save the sample size in bytes for posterity */
-	internal->sample_size = format->bits * format->channels / 8;
+	internal->sample_size = format->bits * device->output_channels / 8;
 
 	/* set the sample rate */
 	err = snd_pcm_hw_params_set_rate_near(internal->pcm_handle,
@@ -400,6 +402,17 @@
     return err;
   }
 
+  /* this is a hack and fragile if the exact device detection code
+     flow changes!  Nevertheless, this is a useful warning for users.
+     Never fail silently if we can help it! */
+  if(!strcasecmp(dev,"default")){
+    /* default device */
+    if(device->output_channels>2){
+      awarn("ALSA 'default' device plays only channels 0,1.\n");
+      device->output_channels=2;
+    }
+  }
+
   /* try to set up hw params */
   err = alsa_set_hwparams(device,format);
   if(err<0){
@@ -445,7 +458,7 @@
           /* we don't try just 'default' as it's a plug device that
              will accept any number of channels but usually plays back
              everything as stereo. */
-          switch(format->channels){
+          switch(device->output_channels){
           default:
           case 8:
           case 7:
@@ -488,20 +501,15 @@
 	if (format->bits > 8)
 		device->driver_byte_format = device->client_byte_format;
 
-        if(!device->output_matrix){
-          if(!strcasecmp(internal->dev,"default")){
-            /* default device */
-            if(format->channels>2)
-              awarn("ALSA 'default' device plays only L,R channels.\n");
-            device->output_matrix=strdup("L,R");
-          }else{
-            if(strncasecmp(internal->dev,"surround",8)){
-              if(format->channels>2 && device->verbose>=0)
-                awarn("No way to determine hardware %d channel mapping of\n"
-                      "ALSA device '%s'.\n",format->channels, internal->dev);
-              device->output_matrix=strdup("L,R");
-            }else{
-              device->output_matrix=strdup("L,R,BL,BR,C,LFE,SL,SR");
+        if(strcasecmp(internal->dev,"default")){
+          if(strncasecmp(internal->dev,"surround",8)){
+            if(device->output_channels>2 && device->verbose>=0){
+              awarn("No way to determine hardware %d channel mapping of\n"
+                    "ALSA device '%s'.\n",device->output_channels, internal->dev);
+              if(device->inter_matrix){
+                free(device->inter_matrix);
+                device->inter_matrix=NULL;
+              }
             }
           }
         }
@@ -591,8 +599,7 @@
               snd_pcm_drain(internal->pcm_handle);
               snd_pcm_close(internal->pcm_handle);
               internal->pcm_handle=NULL;
-            } else
-              awarn("ao_plugin_close called with uninitialized ao_device->internal->pcm_handle\n");
+            } 
           } else
             awarn("ao_plugin_close called with uninitialized ao_device->internal\n");
 	} else

Modified: trunk/ao/src/plugins/arts/ao_arts.c
===================================================================
--- trunk/ao/src/plugins/arts/ao_arts.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/arts/ao_arts.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -133,6 +133,8 @@
     return 0; /* Could not initialize device memory */
 
   device->internal = internal;
+  device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
+  device->output_matrix=strdup("L,R");
 
   return 1; /* Memory alloc successful */
 }
@@ -167,7 +169,7 @@
   ao_arts_internal *internal = (ao_arts_internal *) device->internal;
   int errorcode=0;
 
-  if(format->channels<1 || format->channels>2){
+  if(device->output_channels<1 || device->output_channels>2){
     /* the docs aren't kidding here--- feed it more than 2
        channels and the server simply stops answering; the
        connection freezes. */
@@ -197,7 +199,7 @@
   device->driver_byte_format = AO_FMT_NATIVE;
   internal->stream = arts_play_stream(format->rate,
                                       format->bits,
-                                      format->channels,
+                                      device->output_channels,
                                       "libao stream");
 
   if(!internal->stream){
@@ -231,9 +233,6 @@
   server_open_count++;
   pthread_mutex_unlock(&mutex);
 
-  if(!device->output_matrix)
-    device->output_matrix=strdup("L,R");
-
   adebug("thread %p: playback stream created!\n",internal->stream);
   adebug("thread %p: buffer size = %d bytes\n",internal->stream,internal->buffersize);
   return 1;

Modified: trunk/ao/src/plugins/esd/ao_esd.c
===================================================================
--- trunk/ao/src/plugins/esd/ao_esd.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/esd/ao_esd.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -133,6 +133,8 @@
 	internal->host = NULL;
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
+        device->output_matrix=strdup("L,R");
 
 	return 1; /* Memory alloc successful */
 }
@@ -167,7 +169,7 @@
 	default : return 0;
 	}
 
-	switch (format->channels)
+	switch (device->output_channels)
 	{
 	case 1 : esd_channels = ESD_MONO;
 		 break;
@@ -186,10 +188,6 @@
 
 	device->driver_byte_format = AO_FMT_NATIVE;
 
-        /* ESD restricted to stereo only */
-        if(!device->output_matrix)
-            device->output_matrix=strdup("L,R");
-
 	return 1;
 }
 

Modified: trunk/ao/src/plugins/irix/ao_irix.c
===================================================================
--- trunk/ao/src/plugins/irix/ao_irix.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/irix/ao_irix.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -100,6 +100,7 @@
 	internal->channels = 2;
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
 
 	/* Device-specific initialization was successful. */
 	return 1;
@@ -124,13 +125,13 @@
 		return 0;
 	}
 
-	if (alSetChannels(internal->alconfig, format->channels) < 0) {
+	if (alSetChannels(internal->alconfig, device->output_channels) < 0) {
 		aerror("alSetChannels(%d) failed: %s\n",
-			format->channels, alGetErrorString(oserror()));
+			device->output_channels, alGetErrorString(oserror()));
 		return 0;
 	}
 
-	internal->channels = format->channels;
+	internal->channels = device->output_channels;
 
 	if (alSetDevice(internal->alconfig, dev) < 0) {
 		aerror("alSetDevice failed: %s\n",
@@ -191,15 +192,13 @@
 	}
 
 	device->driver_byte_format = AO_FMT_NATIVE;
-
-        if(!device->output_matrix){
-          /* set up out matrix such that users are warned about > stereo playback */
-          if(format->channels<=2)
-            device->output_matrix=strdup("L,R");
+        if(!device->inter_matrix){
+          /* set up matrix such that users are warned about > stereo playback */
+          if(device->output_channels<=2)
+            device->inter_matrix=strdup("L,R");
           //else no matrix, which results in a warning
         }
 
-
 	return 1;
 }
 

Modified: trunk/ao/src/plugins/macosx/ao_macosx.c
===================================================================
--- trunk/ao/src/plugins/macosx/ao_macosx.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/macosx/ao_macosx.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -198,11 +198,11 @@
 
   internal->device = device;
   device->internal = internal;
-
+  device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE;
+  device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR");
   return 1; /* Memory alloc successful */
 }
 
-
 int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
   {
   ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
@@ -216,54 +216,10 @@
     }
     internal->buffer_time = buffer;
   }
-  
+
   return 1;
 }
 
-static char *map[]={
-  "L","R","C","LFE","BL","BR","CL","CR","BC","SL","SR",NULL
-};
-
-static unsigned int _matrix_to_channelmask(char *matrix){
-  unsigned int ret=0;
-  char *p=matrix;
-  while(1){
-    char *h=p;
-    int m=0;
-
-    /* search for seperator */
-    while(*h && *h!=',')h++;
-
-    while(map[m]){
-      if(h-p && !strncmp(map[m],p,h-p) &&
-         strlen(map[m])==h-p)
-        break;
-      m++;
-    }
-    if(map[m])
-      ret |= (1<<m);
-    if(!*h)break;
-    p=h+1;
-  }
-  return ret;
-}
-
-static char *_channelmask_to_matrix(unsigned int mask){
-  int m=0;
-  int count=0;
-  char buffer[80]={0};
-  while(map[m]){
-    if(mask & (1<<m)){
-      if(count)
-        strcat(buffer,",");
-      strcat(buffer,map[m]);
-      count++;
-    }
-    m++;
-  }
-  return strdup(buffer);
-}
-
 int ao_plugin_open(ao_device *device, ao_sample_format *format)
 {
   ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
@@ -313,7 +269,7 @@
   if (format->bits > 8)
     requestedDesc.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
 
-  requestedDesc.mChannelsPerFrame = format->channels;
+  requestedDesc.mChannelsPerFrame = device->output_channels;
   requestedDesc.mSampleRate = format->rate;
   requestedDesc.mBitsPerChannel = format->bits;
   requestedDesc.mFramesPerPacket = 1;
@@ -350,8 +306,8 @@
     aerror("Unable to set output sample rate\n");
     return 0;
   }
-  if(requestedDesc.mChannelsPerFrame != format->channels){
-    aerror("Could not configure %d channel output\n",format->channels);
+  if(requestedDesc.mChannelsPerFrame != device->output_channels){
+    aerror("Could not configure %d channel output\n",device->output_channels);
     return 0;
   }
   if(requestedDesc.mBitsPerChannel != format->bits){
@@ -366,7 +322,7 @@
     aerror("Could not configure integer sample output\n");
     return 0;
   }
-  if((requestedDesc.mFormatFlags & kAudioFormatFlagsNativeEndian) != 
+  if((requestedDesc.mFormatFlags & kAudioFormatFlagsNativeEndian) !=
      kAudioFormatFlagsNativeEndian){
     aerror("Could not configure output endianness\n");
     return 0;
@@ -391,18 +347,12 @@
   /* set the channel mapping.  MacOSX AUHAL is capable of mapping any
      channel format currently representable in the libao matrix. */
 
-  if(format->matrix){
+  if(device->output_mask){
     AudioChannelLayout layout;
     memset(&layout,0,sizeof(layout));
 
     layout.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap;
-    
-    if(device->output_matrix){
-      layout.mChannelBitmap = _matrix_to_channelmask(device->output_matrix);
-    }else{
-      layout.mChannelBitmap = _matrix_to_channelmask(format->matrix);
-      device->output_matrix = _channelmask_to_matrix(layout.mChannelBitmap);
-    }
+    layout.mChannelBitmap = device->output_mask;
 
     result = AudioUnitSetProperty(internal->outputAudioUnit,
 				  kAudioUnitProperty_AudioChannelLayout,
@@ -415,8 +365,8 @@
   /* Set the audio callback */
   input.inputProc = (AURenderCallback) audioCallback;
   input.inputProcRefCon = internal;
- 
-  result = AudioUnitSetProperty( internal->outputAudioUnit, 
+
+  result = AudioUnitSetProperty( internal->outputAudioUnit,
 				 kAudioUnitProperty_SetRenderCallback,
 				 kAudioUnitScope_Input,
 				 0, &input, sizeof( input ));
@@ -437,7 +387,7 @@
      we allocate the buffer here instead of ao_plugin_device_init() */
 
   internal->bufferByteCount =  (internal->buffer_time * format->rate / 1000) *
-    (format->channels * format->bits / 8);
+    (device->output_channels * format->bits / 8);
 
   internal->firstValidByteOffset = 0;
   internal->validByteCount = 0;
@@ -451,7 +401,7 @@
   /* limited to stereo for now */
   //if(!device->output_matrix)
   //device->output_matrix=strdup("L,R");
-  
+
   return 1;
 }
 
@@ -465,7 +415,7 @@
   unsigned int firstEmptyByteOffset, emptyByteCount;
 
   while (num_bytes) {
-		
+
     // Get a consistent set of data about the available space in the queue,
     // figure out the maximum number of bytes we can copy in this chunk,
     // and claim that amount of space

Modified: trunk/ao/src/plugins/nas/ao_nas.c
===================================================================
--- trunk/ao/src/plugins/nas/ao_nas.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/nas/ao_nas.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -107,6 +107,8 @@
 	internal->buf_free = 0;
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
+
 	return 1; /* Memory alloc successful */
 }
 
@@ -163,7 +165,7 @@
 	    if ((AuDeviceKind(AuServerDevice(internal->aud, i)) ==
 		 AuComponentKindPhysicalOutput) &&
 		(AuDeviceNumTracks(AuServerDevice(internal->aud, i)) ==
-		 format->channels))
+		 device->output_channels))
 	      break;
 
 	  if ((i == AuServerNumDevices(internal->aud)) ||
@@ -177,7 +179,7 @@
 
 	/* set up flow */
 	AuMakeElementImportClient(&elms[0], format->rate,
-				  nas_format, format->channels, AuTrue,
+				  nas_format, device->output_channels, AuTrue,
 				  internal->buf_size, internal->buf_size / 2,
 				  0, 0);
 	AuMakeElementExportDevice(&elms[1], 0, internal->dev,
@@ -187,10 +189,10 @@
 
 	device->driver_byte_format = AO_FMT_NATIVE;
 
-        if(!device->output_matrix){
+        if(!device->inter_matrix){
           /* set up out matrix such that users are warned about > stereo playback */
-          if(format->channels<=2)
-            device->output_matrix=strdup("L,R");
+          if(device->output_channels<=2)
+            device->inter_matrix=strdup("L,R");
           //else no matrix, which results in a warning
         }
 

Modified: trunk/ao/src/plugins/oss/ao_oss.c
===================================================================
--- trunk/ao/src/plugins/oss/ao_oss.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/oss/ao_oss.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -169,6 +169,8 @@
 	internal->dev = NULL;
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
+        device->output_matrix=strdup("L,R");
 
 	return 1; /* Memory alloc successful */
 }
@@ -216,21 +218,21 @@
 
 	/* Now set all of the parameters */
 
-	switch (format->channels)
+	switch (device->output_channels)
 	{
 	case 1: tmp = 0;
 		break;
 	case 2: tmp = 1;
 		break;
 	default:aerror("Unsupported number of channels: %d.",
-                       format->channels);
+                       device->output_channels);
 		goto ERR;
 	}
 
 	if (ioctl(internal->fd,SNDCTL_DSP_STEREO,&tmp) < 0 ||
-			tmp+1 != format->channels) {
+			tmp+1 != device->output_channels) {
           aerror("cannot set channels to %d\n",
-                 format->channels);
+                 device->output_channels);
           goto ERR;
 	}
 
@@ -282,10 +284,6 @@
           internal->buf_size=1024;
 	}
 
-        /* limited to stereo */
-        if(!device->output_matrix)
-          device->output_matrix=strdup("L,R");
-
 	return 1; /* Open successful */
 
  ERR:

Modified: trunk/ao/src/plugins/pulse/ao_pulse.c
===================================================================
--- trunk/ao/src/plugins/pulse/ao_pulse.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/pulse/ao_pulse.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -138,8 +138,14 @@
     internal->simple = NULL;
     internal->server = NULL;
     internal->sink = NULL;
-    device->internal = internal;
 
+    device->internal = internal;
+    device->output_matrix_order = AO_OUTPUT_MATRIX_PERMUTABLE;
+    device->output_matrix = strdup("L,R,C,BC,BL,BR,LFE,CL,CR,SL,SR,"
+                                   "A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,"
+                                   "A10,A11,A12,A13,A14,A15,A16,A17,A18,A19,"
+                                   "A20,A21,A22,A23,A24,A25,A26,A27,A28,A29,"
+                                   "A30,A31");
     return 1;
 }
 
@@ -159,65 +165,6 @@
 
     return 1;
 }
-
-pa_channel_position_t default_map[]={
-  PA_CHANNEL_POSITION_FRONT_LEFT,
-  PA_CHANNEL_POSITION_FRONT_RIGHT,
-  PA_CHANNEL_POSITION_REAR_LEFT,
-  PA_CHANNEL_POSITION_REAR_RIGHT,
-  PA_CHANNEL_POSITION_FRONT_CENTER,
-  PA_CHANNEL_POSITION_LFE,
-  PA_CHANNEL_POSITION_SIDE_LEFT,
-  PA_CHANNEL_POSITION_SIDE_RIGHT,
-  PA_CHANNEL_POSITION_AUX0,
-  PA_CHANNEL_POSITION_AUX1,
-  PA_CHANNEL_POSITION_AUX2,
-  PA_CHANNEL_POSITION_AUX3,
-  PA_CHANNEL_POSITION_AUX4,
-  PA_CHANNEL_POSITION_AUX5,
-  PA_CHANNEL_POSITION_AUX6,
-  PA_CHANNEL_POSITION_AUX7,
-  PA_CHANNEL_POSITION_AUX8,
-  PA_CHANNEL_POSITION_AUX9,
-  PA_CHANNEL_POSITION_AUX10,
-  PA_CHANNEL_POSITION_AUX11,
-  PA_CHANNEL_POSITION_AUX12,
-  PA_CHANNEL_POSITION_AUX13,
-  PA_CHANNEL_POSITION_AUX14,
-  PA_CHANNEL_POSITION_AUX15,
-  PA_CHANNEL_POSITION_AUX16,
-  PA_CHANNEL_POSITION_AUX17,
-  PA_CHANNEL_POSITION_AUX18,
-  PA_CHANNEL_POSITION_AUX19,
-  PA_CHANNEL_POSITION_AUX20,
-  PA_CHANNEL_POSITION_AUX21,
-  PA_CHANNEL_POSITION_AUX22,
-  PA_CHANNEL_POSITION_AUX23,
-  PA_CHANNEL_POSITION_AUX23};
-
-typedef struct {
-  char *from;
-  pa_channel_position_t to;
-} translate;
-
-translate trans[]={
-  {"M",PA_CHANNEL_POSITION_MONO},
-  {"L",PA_CHANNEL_POSITION_FRONT_LEFT},
-  {"R",PA_CHANNEL_POSITION_FRONT_RIGHT},
-  {"C",PA_CHANNEL_POSITION_FRONT_CENTER},
-  {"BL",PA_CHANNEL_POSITION_REAR_LEFT},
-  {"BR",PA_CHANNEL_POSITION_REAR_RIGHT},
-  {"BC",PA_CHANNEL_POSITION_REAR_CENTER},
-  {"SL",PA_CHANNEL_POSITION_SIDE_LEFT},
-  {"SR",PA_CHANNEL_POSITION_SIDE_RIGHT},
-  {"LFE",PA_CHANNEL_POSITION_LFE},
-  {"U",PA_CHANNEL_POSITION_INVALID},
-  {"X",PA_CHANNEL_POSITION_INVALID},
-  {"CL",PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER},
-  {"CR",PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER},
-  {NULL,PA_CHANNEL_POSITION_INVALID}
-};
-
 int ao_plugin_open(ao_device *device, ao_sample_format *format) {
     char *p=NULL, t[256], t2[256];
     const char *fn = NULL;
@@ -238,10 +185,10 @@
     else
         return 0;
 
-    if (format->channels <= 0 || format->channels > PA_CHANNELS_MAX)
-        return 0;
+    if (device->output_channels <= 0 || device->output_channels > PA_CHANNELS_MAX)
+      return 0;
 
-    ss.channels = format->channels;
+    ss.channels = device->output_channels;
     ss.rate = format->rate;
 
     disable_sigpipe();
@@ -266,67 +213,21 @@
     pa_xfree(p);
     p = NULL;
 
-    if(!device->output_matrix){
-      if(format->matrix){
-        /* request a matrix similar/identical to the input format; let
-           Pulse do the conversion work */
-        int i;
-        char *p=format->matrix,*h;
-        char buffer[129]={0};
-        usemap=1;
-        pa_channel_map_init(&map);
-        map.channels=format->channels;
+    if(device->input_map){
+      int i;
+      pa_channel_map_init(&map);
+      map.channels=device->output_channels;
 
-        for(i=0;i<format->channels;i++){
-          int m=0;
-          h=p;
-          while(*h && *h!=',')h++;
-          while(trans[m].from){
-            if(h-p && !strncmp(trans[m].from,p,h-p) &&
-               strlen(trans[m].from)==h-p)
-              break;
-            m++;
-          }
-          if(i)strcat(buffer,",");
-          if(trans[m].from){
-            map.map[i] = trans[m].to;
-            strcat(buffer,trans[m].from);
-          }else{
-            map.map[i] = PA_CHANNEL_POSITION_INVALID;
-            strcat(buffer,"X");
-          }
-
-          p=h;
-          if(*h)p++;
+      for(i=0;i<device->output_channels;i++){
+        if(device->input_map[i]==-1){
+          map.map[i] = PA_CHANNEL_POSITION_INVALID;
+        }else{
+          map.map[i] = device->input_map[i];
         }
-
-        device->output_matrix = strdup(buffer);
-
-      }else{
-        if(format->channels <= 32){
-          /* set up a channel mapping similar to ALSA */
-          if(format->channels == 1 ){
-            usemap=1;
-            pa_channel_map_init(&map);
-            map.channels=1;
-            map.map[0] = PA_CHANNEL_POSITION_MONO;
-            device->output_matrix=strdup("M");
-          }else{
-            int i;
-            usemap=1;
-            pa_channel_map_init(&map);
-            map.channels=format->channels;
-            for(i=0;i<format->channels;i++)
-              map.map[i] = default_map[i];
-            device->output_matrix=strdup("L,R,BL,BR,C,LFE,SL,SR,"
-                                  "A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,"
-                                  "A11,A12,A13,A14,A15,A16,A17,A18,A19,A20,"
-                                  "A21,A22,A23,A4");
-          }
-        }
       }
     }
 
+
     if (!(internal->simple = pa_simple_new(internal->server, fn ? t : "libao", PA_STREAM_PLAYBACK, internal->sink, fn ? t2 : "libao playback stream", &ss, &map, NULL, NULL)))
         return 0;
 

Modified: trunk/ao/src/plugins/sun/ao_sun.c
===================================================================
--- trunk/ao/src/plugins/sun/ao_sun.c	2010-03-17 15:54:34 UTC (rev 16981)
+++ trunk/ao/src/plugins/sun/ao_sun.c	2010-03-18 19:50:04 UTC (rev 16982)
@@ -125,6 +125,7 @@
 	}
 
 	device->internal = internal;
+        device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
 
 	return 1; /* Memory alloc successful */
 }
@@ -160,7 +161,7 @@
 	info.play.encoding = AUDIO_ENCODING_SLINEAR;
 	info.play.precision = format->bits;
 	info.play.sample_rate = format->rate;
-	info.play.channels = format->channels;
+	info.play.channels = device->output_channels;
 
  	if (ioctl(internal->fd, AUDIO_SETINFO, &info) < 0) {
 		close(internal->fd);
@@ -169,10 +170,10 @@
 
 	device->driver_byte_format = AO_FMT_NATIVE;
 
-        if(!device->output_matrix){
+        if(!device->inter_matrix){
           /* set up out matrix such that users are warned about > stereo playback */
-          if(format->channels<=2)
-            device->output_matrix=strdup("L,R");
+          if(device->output_channels<=2)
+            device->inter_matrix=strdup("L,R");
           //else no matrix, which results in a warning
         }
 



More information about the commits mailing list