diff --git a/DOCS/man/en/af.rst b/DOCS/man/en/af.rst index 34b54eb6c8..bb7f34cf62 100644 --- a/DOCS/man/en/af.rst +++ b/DOCS/man/en/af.rst @@ -184,7 +184,7 @@ Available filters are: Would amplify the sound in the upper and lower frequency region while canceling it almost completely around 1kHz. -``channels=nch[:nr:from1:to1:from2:to2:from3:to3:...]`` +``channels=nch[:routes]`` Can be used for adding, removing, routing and copying audio channels. If only ```` is given, the default routing is used. It works as follows: If the number of output channels is greater than the number of input @@ -195,26 +195,33 @@ Available filters are: ```` number of output channels (1-8) - ```` - number of routes (1-8) - ```` - Pairs of numbers between 0 and 7 that define where to route each - channel. + ```` + List of ``,`` separated routes, in the form ``from1-to1,from2-to2,...``. + Each pair defines where to route each channel. There can be at most + 8 routes. Without this argument, the default routing is used. Since + ``,`` is also used to separate filters, you must quote this argument + with ``[...]`` or similar. .. admonition:: Examples - ``mpv --af=channels=4:4:0:1:1:0:2:2:3:3 media.avi`` + ``mpv --af=channels=4:[0-1,1-0,0-2,1-3] media.avi`` Would change the number of channels to 4 and set up 4 routes that swap channel 0 and channel 1 and leave channel 2 and 3 intact. Observe that if media containing two channels were played back, channels 2 and 3 would contain silence but 0 and 1 would still be swapped. - ``mpv --af=channels=6:4:0:0:0:1:0:2:0:3 media.avi`` + ``mpv --af=channels=6:[0-0,0-1,0-2,0-3] media.avi`` Would change the number of channels to 6 and set up 4 routes that copy channel 0 to channels 0 to 3. Channel 4 and 5 will contain silence. + .. note:: + + You should probably not use this filter. If you want to change the + output channel layout, try the ``format`` filter, which can make mpv + automatically up- and downmix standard channel layouts. + ``format=format:srate:channels:out-format:out-srate:out-channels`` Force a specific audio format/configuration without actually changing the audio data. Keep in mind that the filter system might auto-insert actual diff --git a/audio/filter/af_channels.c b/audio/filter/af_channels.c index d544e4d9c0..b51e8a431e 100644 --- a/audio/filter/af_channels.c +++ b/audio/filter/af_channels.c @@ -35,8 +35,9 @@ typedef struct af_channels_s{ int route[AF_NCH][2]; - int nr; + int nch, nr; int router; + char *routes; }af_channels_t; // Local function for copying data @@ -138,10 +139,12 @@ static int check_routes(af_channels_t* s, int nin, int nout) // Initialization and runtime control static int control(struct af_instance* af, int cmd, void* arg) { - af_channels_t* s = af->setup; + af_channels_t* s = af->priv; switch(cmd){ case AF_CONTROL_REINIT: + mp_audio_set_channels_old(af->data, s->nch); + // Set default channel assignment if(!s->router){ int i; @@ -167,68 +170,19 @@ static int control(struct af_instance* af, int cmd, void* arg) } af->data->rate = ((struct mp_audio*)arg)->rate; + mp_audio_force_interleaved_format((struct mp_audio*)arg); mp_audio_set_format(af->data, ((struct mp_audio*)arg)->format); - mp_audio_force_interleaved_format(af->data); - int r = af_test_output(af,(struct mp_audio*)arg); - if (r != AF_OK) - return r; 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++; - } - } - - struct mp_chmap chmap; - mp_chmap_from_channels(&chmap, nch); - if (AF_OK != af->control(af, AF_CONTROL_SET_CHANNELS, &chmap)) - return AF_ERROR; - return AF_OK; - } - case AF_CONTROL_SET_CHANNELS: - // Reinit must be called after this function has been called - - mp_audio_set_channels(af->data, (struct mp_chmap *)arg); - if(!s->router) - mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Changing number of channels" - " to %i\n",af->data->nch); - return AF_OK; } return AF_UNKNOWN; } -// Deallocate memory -static void uninit(struct af_instance* af) -{ - free(af->setup); -} - // 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; + af_channels_t* s = af->priv; int i; mp_audio_realloc_min(af->data, data->samples); @@ -243,7 +197,6 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) // Set output data c->planes[0] = l->planes[0]; - c->samples = c->samples / c->nch * l->nch; mp_audio_set_channels(c, &l->channels); return c; @@ -251,18 +204,45 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) // Allocate memory and set function pointers static int af_open(struct af_instance* af){ - af->control=control; - af->uninit=uninit; - af->play=play; - af->setup=calloc(1,sizeof(af_channels_t)); - if(af->setup == NULL) - return AF_ERROR; - return AF_OK; + af->control=control; + af->play=play; + af_channels_t *s = af->priv; + + // If router scan commandline for routing pairs + if(s->routes && s->routes[0]){ + char* cp = s->routes; + int ch = 0; + // Scan for pairs on commandline + do { + int n = 0; + if (ch >= AF_NCH) { + mp_msg(MSGT_AFILTER, MSGL_FATAL, + "[channels] Can't have more than %d routes.\n", AF_NCH); + return AF_ERROR; + } + 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++; + } while(*cp == ',' && *(cp++)); + s->nr = ch; + if (s->nr > 0) + s->router = 1; + } + + return AF_OK; } -// Description of this filter +#define OPT_BASE_STRUCT af_channels_t struct af_info af_info_channels = { - .info = "Insert or remove channels", - .name = "channels", - .open = af_open, + .info = "Insert or remove channels", + .name = "channels", + .open = af_open, + .priv_size = sizeof(af_channels_t), + .options = (const struct m_option[]) { + OPT_INTRANGE("nch", nch, 0, 1, AF_NCH, OPTDEF_INT(2)), + OPT_STRING("routes", routes, 0), + {0} + }, };