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); }
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 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; }
mapper::~mapper() { _free_map(_map); _free_map(_map_def); }
/* 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=NULL; int result; ao_sample_format sformat=*format; sformat.matrix=NULL; /* Get driver id */ if ( (driver = _get_driver(driver_id)) == NULL ) { errno = AO_ENODRIVER; goto error; } funcs = driver->functions; /* Check the driver type */ if (file == NULL && funcs->driver_info()->type != AO_TYPE_LIVE) { errno = AO_ENOTLIVE; goto error; } else if (file != NULL && funcs->driver_info()->type != AO_TYPE_FILE) { errno = AO_ENOTFILE; goto error; } /* Make a new device structure */ if ( (device = _create_device(driver_id, driver, format, file)) == NULL ) { errno = AO_EFAIL; goto error; } /* Initialize the device memory; get static channel mapping */ if (!funcs->device_init(device)) { errno = AO_EFAIL; goto error; } /* Load options */ errno = ao_device_load_options(device,ao_global_options); if(errno) goto error; errno = ao_device_load_options(device,options); if(errno) goto error; /* also sanitize the format input channel matrix */ if(format->matrix){ sformat.matrix = _sanitize_matrix(format->channels, format->matrix, device); if(!sformat.matrix) awarn("Input channel matrix invalid; ignoring.\n"); /* special-case handling of 'M'. */ if(sformat.channels==1 && sformat.matrix && !strcmp(sformat.matrix,"M")){ free(sformat.matrix); sformat.matrix=NULL; } } /* 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; } }else{ device->output_channels = sformat.channels; } /* other housekeeping */ device->input_channels = sformat.channels; device->bytewidth = (sformat.bits+7)>>3; device->rate = sformat.rate; /* Open the device */ result = funcs->open(device, &sformat); if (!result) { errno = AO_EOPENDEVICE; goto error; } /* 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->inter_matrix){ awarn("Driver %s does not support automatic channel mapping;\n" "\tRouting only L/R channels to output.\n\n", info_table[device->driver_id]->short_name); device->inter_matrix=_strdup("L,R"); } { /* walk thorugh the inter matrix, match channels */ char *op=device->inter_matrix; int count=0; device->inter_permute = calloc(device->output_channels,sizeof(int)); adebug("\n"); while(count<device->output_channels){ int m=0,mm; char *h=op; if(*op){ /* find mnemonic offset of inter channel */ while(*h && *h!=',')h++; while(mnemonics[m]){ if(!strncmp(mnemonics[m],op,h-op)) break; m++; } mm=m; /* find match in input if any */ 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->inter_permute[count] = -1; /* display resulting mapping for now */ if(device->inter_permute[count]>=0){ adebug("input %d (%s)\t -> backend %d (%s)\n", device->inter_permute[count], mnemonics[mm], count,mnemonics[m]); }else{ adebug(" \t backend %d (%s)\n", count,mnemonics[m]); } count++; op=h; if(*h)op++; } { char **inch = _tokenize_matrix(sformat.matrix); int i,j; int unflag=0; for(j=0;j<sformat.channels;j++){ for(i=0;i<device->output_channels;i++) if(device->inter_permute[i]==j)break; if(i==device->output_channels){ adebug("input %d (%s)\t -> none\n", j,inch[j]); unflag=1; } } _free_map(inch); if(unflag) awarn("Some input channels are unmapped and will not be used.\n"); } averbose("\n"); } } /* if there's no actual permutation to do, release the permutation vector */ if(device->inter_permute && device->output_channels == device->input_channels){ int i; for(i=0;i<device->output_channels;i++) if(device->inter_permute[i]!=i)break; if(i==device->output_channels){ free(device->inter_permute); device->inter_permute=NULL; } } /* Resolve actual driver byte format */ device->driver_byte_format = _real_byte_format(device->driver_byte_format); /* Only create swap buffer if needed */ if (device->bytewidth>1 && device->client_byte_format != device->driver_byte_format){ adebug("swap buffer required:\n"); adebug(" machine endianness: %d\n",ao_is_big_endian()); adebug(" device->client_byte_format:%d\n",device->client_byte_format); adebug(" device->driver_byte_format:%d\n",device->driver_byte_format); } if ( (device->bytewidth>1 && device->client_byte_format != device->driver_byte_format) || device->inter_permute){ result = _realloc_swap_buffer(device, DEF_SWAP_BUF_SIZE); if (!result) { if(sformat.matrix)free(sformat.matrix); device->funcs->close(device); device->funcs->device_clear(device); free(device); errno = AO_EFAIL; return NULL; /* Couldn't alloc swap buffer */ } } /* If we made it this far, everything is OK. */ if(sformat.matrix)free(sformat.matrix); return device; error: { int errtemp = errno; if(sformat.matrix) free(sformat.matrix); ao_close(device); errno=errtemp; } return NULL; }