/* * Audio filter that adds and removes channels, according to the * command line parameter channels. It is stupid and can only add * silence or copy channels, not mix or filter. * * This file is part of MPlayer. * * MPlayer is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * MPlayer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with MPlayer; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include "af.h" #define FR 0 #define TO 1 typedef struct af_channels_s{ int route[AF_NCH][2]; int nr; int router; }af_channels_t; // Local function for copying data static void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, int bps) { switch(bps){ case 1:{ int8_t* tin = (int8_t*)in; int8_t* tout = (int8_t*)out; tin += inos; tout += outos; len = len/ins; while(len--){ *tout=*tin; tin +=ins; tout+=outs; } break; } case 2:{ int16_t* tin = (int16_t*)in; int16_t* tout = (int16_t*)out; tin += inos; tout += outos; len = len/(2*ins); while(len--){ *tout=*tin; tin +=ins; tout+=outs; } break; } case 3:{ int8_t* tin = (int8_t*)in; int8_t* tout = (int8_t*)out; tin += 3 * inos; tout += 3 * outos; len = len / ( 3 * ins); while (len--) { tout[0] = tin[0]; tout[1] = tin[1]; tout[2] = tin[2]; tin += 3 * ins; tout += 3 * outs; } break; } case 4:{ int32_t* tin = (int32_t*)in; int32_t* tout = (int32_t*)out; tin += inos; tout += outos; len = len/(4*ins); while(len--){ *tout=*tin; tin +=ins; tout+=outs; } break; } case 8:{ int64_t* tin = (int64_t*)in; int64_t* tout = (int64_t*)out; tin += inos; tout += outos; len = len/(8*ins); while(len--){ *tout=*tin; tin +=ins; tout+=outs; } break; } default: mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Unsupported number of bytes/sample: %i" " please report this error on the MPlayer mailing list. \n",bps); } } // Make sure the routes are sane static int check_routes(af_channels_t* s, int nin, int nout) { int i; if((s->nr < 1) || (s->nr > AF_NCH)){ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be" " between 1 and %i. Current value is %i\n",AF_NCH,s->nr); return AF_ERROR; } for(i=0;inr;i++){ if((s->route[i][FR] >= nin) || (s->route[i][TO] >= nout)){ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Invalid routing in pair nr. %i.\n", i); return AF_ERROR; } } return AF_OK; } // Initialization and runtime control static int control(struct af_instance* af, int cmd, void* arg) { af_channels_t* s = af->setup; switch(cmd){ case AF_CONTROL_REINIT: // Set default channel assignment if(!s->router){ int i; // Make sure this filter isn't redundant if(af->data->nch == ((struct mp_audio*)arg)->nch) return AF_DETACH; // If mono: fake stereo if(((struct mp_audio*)arg)->nch == 1){ s->nr = min(af->data->nch,2); for(i=0;inr;i++){ s->route[i][FR] = 0; s->route[i][TO] = i; } } else{ s->nr = min(af->data->nch, ((struct mp_audio*)arg)->nch); for(i=0;inr;i++){ s->route[i][FR] = i; s->route[i][TO] = i; } } } af->data->rate = ((struct mp_audio*)arg)->rate; af->data->format = ((struct mp_audio*)arg)->format; af->data->bps = ((struct mp_audio*)arg)->bps; af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch; return check_routes(s,((struct mp_audio*)arg)->nch,af->data->nch); case AF_CONTROL_COMMAND_LINE:{ int nch = 0; int n = 0; // Check number of channels and number of routing pairs sscanf(arg, "%i:%i%n", &nch, &s->nr, &n); // If router scan commandline for routing pairs if(s->nr){ char* cp = &((char*)arg)[n]; int ch = 0; // Sanity check if((s->nr < 1) || (s->nr > AF_NCH)){ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be" " between 1 and %i. Current value is %i\n",AF_NCH,s->nr); } s->router = 1; // Scan for pairs on commandline while((*cp == ':') && (ch < s->nr)){ sscanf(cp, ":%i:%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n); mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Routing from channel %i to" " channel %i\n",s->route[ch][FR],s->route[ch][TO]); cp = &cp[n]; ch++; } } if(AF_OK != af->control(af,AF_CONTROL_CHANNELS | AF_CONTROL_SET ,&nch)) return AF_ERROR; return AF_OK; } case AF_CONTROL_CHANNELS | AF_CONTROL_SET: // Reinit must be called after this function has been called // Sanity check if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of output channels must be" " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]); return AF_ERROR; } af->data->nch=((int*)arg)[0]; if(!s->router) mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Changing number of channels" " to %i\n",af->data->nch); return AF_OK; case AF_CONTROL_CHANNELS | AF_CONTROL_GET: *(int*)arg = af->data->nch; return AF_OK; case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_SET:{ int ch = ((af_control_ext_t*)arg)->ch; int* route = ((af_control_ext_t*)arg)->arg; s->route[ch][FR] = route[FR]; s->route[ch][TO] = route[TO]; return AF_OK; } case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_GET:{ int ch = ((af_control_ext_t*)arg)->ch; int* route = ((af_control_ext_t*)arg)->arg; route[FR] = s->route[ch][FR]; route[TO] = s->route[ch][TO]; return AF_OK; } case AF_CONTROL_CHANNELS_NR | AF_CONTROL_SET: s->nr = *(int*)arg; return AF_OK; case AF_CONTROL_CHANNELS_NR | AF_CONTROL_GET: *(int*)arg = s->nr; return AF_OK; case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_SET: s->router = *(int*)arg; return AF_OK; case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_GET: *(int*)arg = s->router; return AF_OK; } return AF_UNKNOWN; } // Deallocate memory static void uninit(struct af_instance* af) { free(af->setup); if (af->data) free(af->data->audio); free(af->data); } // Filter data through filter static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) { struct mp_audio* c = data; // Current working data struct mp_audio* l = af->data; // Local data af_channels_t* s = af->setup; int i; if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) return NULL; // Reset unused channels memset(l->audio,0,c->len / c->nch * l->nch); if(AF_OK == check_routes(s,c->nch,l->nch)) for(i=0;inr;i++) copy(c->audio,l->audio,c->nch,s->route[i][FR], l->nch,s->route[i][TO],c->len,c->bps); // Set output data c->audio = l->audio; c->len = c->len / c->nch * l->nch; c->nch = l->nch; return c; } // Allocate memory and set function pointers static int af_open(struct af_instance* af){ af->control=control; af->uninit=uninit; af->play=play; af->mul=1; af->data=calloc(1,sizeof(struct mp_audio)); af->setup=calloc(1,sizeof(af_channels_t)); if((af->data == NULL) || (af->setup == NULL)) return AF_ERROR; return AF_OK; } // Description of this filter struct af_info af_info_channels = { "Insert or remove channels", "channels", "Anders", "", AF_FLAGS_REENTRANT, af_open };