diff options
author | 2013-05-12 21:47:55 +0200 | |
---|---|---|
committer | 2013-05-12 21:47:55 +0200 | |
commit | e6e5a7b221ef2fcdd5a1982d6fdcb627100447d2 (patch) | |
tree | 08b54ef9bb771434fc7fbe9185793503d3ba314c /audio/out | |
parent | 6a83ef1552de4a1a71da49e45647ce1a4ce64e53 (diff) | |
parent | 48f94311516dc1426644b3e68b2a48c22727e1e7 (diff) |
Merge branch 'audio_changes'
Conflicts:
audio/out/ao_lavc.c
Diffstat (limited to 'audio/out')
-rw-r--r-- | audio/out/ao.c | 12 | ||||
-rw-r--r-- | audio/out/ao.h | 14 | ||||
-rw-r--r-- | audio/out/ao_alsa.c | 1345 | ||||
-rw-r--r-- | audio/out/ao_coreaudio.c | 19 | ||||
-rw-r--r-- | audio/out/ao_dsound.c | 91 | ||||
-rw-r--r-- | audio/out/ao_jack.c | 23 | ||||
-rw-r--r-- | audio/out/ao_lavc.c | 85 | ||||
-rw-r--r-- | audio/out/ao_null.c | 11 | ||||
-rw-r--r-- | audio/out/ao_openal.c | 82 | ||||
-rw-r--r-- | audio/out/ao_oss.c | 36 | ||||
-rw-r--r-- | audio/out/ao_pcm.c | 38 | ||||
-rw-r--r-- | audio/out/ao_portaudio.c | 11 | ||||
-rw-r--r-- | audio/out/ao_pulse.c | 106 | ||||
-rw-r--r-- | audio/out/ao_rsound.c | 13 | ||||
-rw-r--r-- | audio/out/ao_sdl.c | 18 | ||||
-rw-r--r-- | audio/out/audio_out_internal.h | 3 |
16 files changed, 960 insertions, 947 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c index 9fb201a333..10badcfa07 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -250,14 +250,24 @@ void ao_resume(struct ao *ao) ao->driver->resume(ao); } +bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s, + struct mp_chmap *map) +{ + return mp_chmap_sel_adjust(s, map); +} +bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s, + struct mp_chmap *map, int num) +{ + return mp_chmap_sel_get_def(s, map, num); +} int old_ao_init(struct ao *ao, char *params) { assert(!global_ao); global_ao = ao; ao_subdevice = params ? talloc_strdup(ao, params) : NULL; - if (ao->driver->old_functions->init(ao->samplerate, ao->channels, + if (ao->driver->old_functions->init(ao->samplerate, &ao->channels, ao->format, 0) == 0) { global_ao = NULL; return -1; diff --git a/audio/out/ao.h b/audio/out/ao.h index 4a7c928824..d908841457 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -23,6 +23,8 @@ #include "core/bstr.h" #include "core/mp_common.h" +#include "audio/chmap.h" +#include "audio/chmap_sel.h" enum aocontrol { // _VOLUME commands take struct ao_control_vol pointer for input/output. @@ -55,7 +57,7 @@ typedef struct ao_info { /* interface towards mplayer and */ typedef struct ao_old_functions { int (*control)(int cmd, void *arg); - int (*init)(int rate, int channels, int format, int flags); + int (*init)(int rate, const struct mp_chmap *channels, int format, int flags); void (*uninit)(int immed); void (*reset)(void); int (*get_space)(void); @@ -68,7 +70,6 @@ typedef struct ao_old_functions { struct ao; struct ao_driver { - bool is_new; bool encode; const struct ao_info *info; const struct ao_old_functions *old_functions; @@ -86,9 +87,9 @@ struct ao_driver { /* global data used by mplayer and plugins */ struct ao { int samplerate; - int channels; + struct mp_chmap channels; int format; - int bps; + int bps; // bytes per second int outburst; int buffersize; double pts; @@ -121,6 +122,11 @@ void ao_reset(struct ao *ao); void ao_pause(struct ao *ao); void ao_resume(struct ao *ao); +bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s, + struct mp_chmap *map); +bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s, + struct mp_chmap *map, int num); + int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg); int old_ao_init(struct ao *ao, char *params); void old_ao_uninit(struct ao *ao, bool cut_audio); diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index 366b912b76..93327881e5 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -36,6 +36,7 @@ #include <alloca.h> #include "config.h" +#include "core/options.h" #include "core/subopt-helper.h" #include "audio/mixer.h" #include "core/mp_msg.h" @@ -46,292 +47,372 @@ #include <alsa/asoundlib.h> #include "ao.h" -#include "audio_out_internal.h" #include "audio/format.h" - -static const ao_info_t info = -{ - "ALSA-0.9.x-1.x audio output", - "alsa", - "Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>", - "under development" +#include "audio/reorder_ch.h" + +struct priv { + snd_pcm_t *alsa; + snd_pcm_format_t alsa_fmt; + size_t bytes_per_sample; + int can_pause; + snd_pcm_sframes_t prepause_frames; + float delay_before_pause; }; -LIBAO_EXTERN(alsa) - -static snd_pcm_t *alsa_handler; -static snd_pcm_format_t alsa_format; - #define BUFFER_TIME 500000 // 0.5 s #define FRAGCOUNT 16 -static size_t bytes_per_sample; +#define ALSA_DEVICE_SIZE 256 -static int alsa_can_pause; -static snd_pcm_sframes_t prepause_frames; -static float delay_before_pause; +#define CHECK_ALSA_ERROR(message) \ + do { \ + if (err < 0) { \ + mp_msg(MSGT_VO, MSGL_ERR, "[AO_ALSA] %s: %s\n", \ + (message), snd_strerror(err)); \ + goto alsa_error; \ + } \ + } while (0) -#define ALSA_DEVICE_SIZE 256 +static float get_delay(struct ao *ao); +static int play(struct ao *ao, void *data, int len, int flags); static void alsa_error_handler(const char *file, int line, const char *function, - int err, const char *format, ...) + int err, const char *format, ...) { - char tmp[0xc00]; - va_list va; - - va_start(va, format); - vsnprintf(tmp, sizeof tmp, format, va); - va_end(va); - - if (err) - mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s: %s\n", - file, line, function, tmp, snd_strerror(err)); - else - mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s\n", - file, line, function, tmp); + char tmp[0xc00]; + va_list va; + + va_start(va, format); + vsnprintf(tmp, sizeof tmp, format, va); + va_end(va); + + if (err) { + mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s: %s\n", + file, line, function, tmp, snd_strerror(err)); + } else { + mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s\n", + file, line, function, tmp); + } } /* to set/get/query special features/parameters */ -static int control(int cmd, void *arg) +static int control(struct ao *ao, enum aocontrol cmd, void *arg) { - switch(cmd) { - case AOCONTROL_GET_MUTE: - case AOCONTROL_SET_MUTE: - case AOCONTROL_GET_VOLUME: - case AOCONTROL_SET_VOLUME: + snd_mixer_t *handle = NULL; + switch (cmd) { + case AOCONTROL_GET_MUTE: + case AOCONTROL_SET_MUTE: + case AOCONTROL_GET_VOLUME: + case AOCONTROL_SET_VOLUME: { - int err; - snd_mixer_t *handle; - snd_mixer_elem_t *elem; - snd_mixer_selem_id_t *sid; - - char *mix_name = "Master"; - char *card = "default"; - int mix_index = 0; - - long pmin, pmax; - long get_vol, set_vol; - float f_multi; - - if(AF_FORMAT_IS_IEC61937(ao_data.format)) - return CONTROL_TRUE; - - if(mixer_channel) { - char *test_mix_index; - - mix_name = strdup(mixer_channel); - if ((test_mix_index = strchr(mix_name, ','))){ - *test_mix_index = 0; - test_mix_index++; - mix_index = strtol(test_mix_index, &test_mix_index, 0); - - if (*test_mix_index){ - mp_tmsg(MSGT_AO,MSGL_ERR, - "[AO_ALSA] Invalid mixer index. Defaulting to 0.\n"); - mix_index = 0 ; - } - } - } - if(mixer_device) card = mixer_device; - - //allocate simple id - snd_mixer_selem_id_alloca(&sid); - - //sets simple-mixer index and name - snd_mixer_selem_id_set_index(sid, mix_index); - snd_mixer_selem_id_set_name(sid, mix_name); - - if (mixer_channel) { - free(mix_name); - mix_name = NULL; - } - - if ((err = snd_mixer_open(&handle, 0)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer open error: %s\n", snd_strerror(err)); - return CONTROL_ERROR; - } - - if ((err = snd_mixer_attach(handle, card)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer attach %s error: %s\n", - card, snd_strerror(err)); - snd_mixer_close(handle); - return CONTROL_ERROR; - } - - if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer register error: %s\n", snd_strerror(err)); - snd_mixer_close(handle); - return CONTROL_ERROR; - } - err = snd_mixer_load(handle); - if (err < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer load error: %s\n", snd_strerror(err)); - snd_mixer_close(handle); - return CONTROL_ERROR; - } - - elem = snd_mixer_find_selem(handle, sid); - if (!elem) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to find simple control '%s',%i.\n", - snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); - snd_mixer_close(handle); - return CONTROL_ERROR; - } - - snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax); - f_multi = (100 / (float)(pmax - pmin)); - - switch (cmd) { - case AOCONTROL_SET_VOLUME: { - ao_control_vol_t *vol = arg; - set_vol = vol->left / f_multi + pmin + 0.5; - - //setting channels - if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting left channel, %s\n", - snd_strerror(err)); - goto mixer_error; - } - mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol); - - set_vol = vol->right / f_multi + pmin + 0.5; - - if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting right channel, %s\n", - snd_strerror(err)); - goto mixer_error; - } - mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n", - set_vol, pmin, pmax, f_multi); - break; - } - case AOCONTROL_GET_VOLUME: { - ao_control_vol_t *vol = arg; - snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); - vol->left = (get_vol - pmin) * f_multi; - snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); - vol->right = (get_vol - pmin) * f_multi; - mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right); - break; - } - case AOCONTROL_SET_MUTE: { - bool *mute = arg; - if (!snd_mixer_selem_has_playback_switch(elem)) - goto mixer_error; - if (!snd_mixer_selem_has_playback_switch_joined(elem)) { - snd_mixer_selem_set_playback_switch( - elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute); - } - snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, - !*mute); - break; - } - case AOCONTROL_GET_MUTE: { - bool *mute = arg; - if (!snd_mixer_selem_has_playback_switch(elem)) - goto mixer_error; - int tmp = 1; - snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, - &tmp); - *mute = !tmp; - if (!snd_mixer_selem_has_playback_switch_joined(elem)) { - snd_mixer_selem_get_playback_switch( - elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); - *mute &= !tmp; + int err; + snd_mixer_elem_t *elem; + snd_mixer_selem_id_t *sid; + + char *mix_name = "Master"; + char *card = "default"; + int mix_index = 0; + + long pmin, pmax; + long get_vol, set_vol; + float f_multi; + + if (AF_FORMAT_IS_IEC61937(ao->format)) + return CONTROL_TRUE; + + if (ao->opts->mixer_channel) { + char *test_mix_index; + + mix_name = strdup(ao->opts->mixer_channel); + if ((test_mix_index = strchr(mix_name, ','))) { + *test_mix_index = 0; + test_mix_index++; + mix_index = strtol(test_mix_index, &test_mix_index, 0); + + if (*test_mix_index) { + mp_tmsg(MSGT_AO, MSGL_ERR, + "[AO_ALSA] Invalid mixer index. Defaulting to 0.\n"); + mix_index = 0; + } + } + } + if (ao->opts->mixer_device) + card = ao->opts->mixer_device; + + //allocate simple id + snd_mixer_selem_id_alloca(&sid); + + //sets simple-mixer index and name + snd_mixer_selem_id_set_index(sid, mix_index); + snd_mixer_selem_id_set_name(sid, mix_name); + + if (ao->opts->mixer_channel) { + free(mix_name); + mix_name = NULL; } - break; - } - } - snd_mixer_close(handle); - return CONTROL_OK; - mixer_error: - snd_mixer_close(handle); - return CONTROL_ERROR; + + err = snd_mixer_open(&handle, 0); + CHECK_ALSA_ERROR("Mixer open error"); + + err = snd_mixer_attach(handle, card); + CHECK_ALSA_ERROR("Mixer attach error"); + + err = snd_mixer_selem_register(handle, NULL, NULL); + CHECK_ALSA_ERROR("Mixer register error"); + + err = snd_mixer_load(handle); + CHECK_ALSA_ERROR("Mixer load error"); + + elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + mp_tmsg(MSGT_AO, MSGL_ERR, + "[AO_ALSA] Unable to find simple control '%s',%i.\n", + snd_mixer_selem_id_get_name(sid), + snd_mixer_selem_id_get_index(sid)); + goto alsa_error; + } + + snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax); + f_multi = (100 / (float)(pmax - pmin)); + + switch (cmd) { + case AOCONTROL_SET_VOLUME: { + ao_control_vol_t *vol = arg; + set_vol = vol->left / f_multi + pmin + 0.5; + + //setting channels + err = snd_mixer_selem_set_playback_volume + (elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol); + CHECK_ALSA_ERROR("Error setting left channel"); + mp_msg(MSGT_AO, MSGL_DBG2, "left=%li, ", set_vol); + + set_vol = vol->right / f_multi + pmin + 0.5; + + err = snd_mixer_selem_set_playback_volume + (elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol); + CHECK_ALSA_ERROR("Error setting right channel"); + mp_msg(MSGT_AO, MSGL_DBG2, + "right=%li, pmin=%li, pmax=%li, mult=%f\n", + set_vol, pmin, pmax, + f_multi); + break; + } + case AOCONTROL_GET_VOLUME: { + ao_control_vol_t *vol = arg; + snd_mixer_selem_get_playback_volume + (elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); + vol->left = (get_vol - pmin) * f_multi; + snd_mixer_selem_get_playback_volume + (elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); + vol->right = (get_vol - pmin) * f_multi; + mp_msg(MSGT_AO, MSGL_DBG2, "left=%f, right=%f\n", vol->left, + vol->right); + break; + } + case AOCONTROL_SET_MUTE: { + bool *mute = arg; + if (!snd_mixer_selem_has_playback_switch(elem)) + goto alsa_error; + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_set_playback_switch + (elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute); + } + snd_mixer_selem_set_playback_switch + (elem, SND_MIXER_SCHN_FRONT_LEFT, !*mute); + break; + } + case AOCONTROL_GET_MUTE: { + bool *mute = arg; + if (!snd_mixer_selem_has_playback_switch(elem)) + goto alsa_error; + int tmp = 1; + snd_mixer_selem_get_playback_switch + (elem, SND_MIXER_SCHN_FRONT_LEFT, &tmp); + *mute = !tmp; + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_get_playback_switch + (elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); + *mute &= !tmp; + } + break; + } + } + snd_mixer_close(handle); + return CONTROL_OK; } - } //end switch - return CONTROL_UNKNOWN; + } //end switch + return CONTROL_UNKNOWN; + +alsa_error: + if (handle) + snd_mixer_close(handle); + return CONTROL_ERROR; } -static void parse_device (char *dest, const char *src, int len) +static void parse_device(char *dest, const char *src, int len) { - char *tmp; - memmove(dest, src, len); - dest[len] = 0; - while ((tmp = strrchr(dest, '.'))) - tmp[0] = ','; - while ((tmp = strrchr(dest, '='))) - tmp[0] = ':'; + char *tmp; + memmove(dest, src, len); + dest[len] = 0; + while ((tmp = strrchr(dest, '.'))) + tmp[0] = ','; + while ((tmp = strrchr(dest, '='))) + tmp[0] = ':'; } -static void print_help (void) +static void print_help(void) { - mp_tmsg (MSGT_AO, MSGL_FATAL, - "\n[AO_ALSA] -ao alsa commandline help:\n"\ - "[AO_ALSA] Example: mpv -ao alsa:device=hw=0.3\n"\ - "[AO_ALSA] Sets first card fourth hardware device.\n\n"\ - "[AO_ALSA] Options:\n"\ - "[AO_ALSA] noblock\n"\ - "[AO_ALSA] Opens device in non-blocking mode.\n"\ - "[AO_ALSA] device=<device-name>\n"\ - "[AO_ALSA] Sets device (change , to . and : to =)\n"); + mp_tmsg(MSGT_AO, MSGL_FATAL, + "\n[AO_ALSA] -ao alsa commandline help:\n" \ + "[AO_ALSA] Example: mpv -ao alsa:device=hw=0.3\n" \ + "[AO_ALSA] Sets first card fourth hardware device.\n\n" \ + "[AO_ALSA] Options:\n" \ + "[AO_ALSA] noblock\n" \ + "[AO_ALSA] Opens device in non-blocking mode.\n" \ + "[AO_ALSA] device=<device-name>\n" \ + "[AO_ALSA] Sets device (change , to . and : to =)\n"); } -static int str_maxlen(void *strp) { - strarg_t *str = strp; - return str->len <= ALSA_DEVICE_SIZE; +static int str_maxlen(void *strp) +{ + strarg_t *str = strp; + return str->len <= ALSA_DEVICE_SIZE; } -static int try_open_device(const char *device, int open_mode, int try_ac3) + +static const int mp_to_alsa_format[][2] = { + {AF_FORMAT_S8, SND_PCM_FORMAT_S8}, + {AF_FORMAT_U8, SND_PCM_FORMAT_U8}, + {AF_FORMAT_U16_LE, SND_PCM_FORMAT_U16_LE}, + {AF_FORMAT_U16_BE, SND_PCM_FORMAT_U16_BE}, + {AF_FORMAT_S16_LE, SND_PCM_FORMAT_S16_LE}, + {AF_FORMAT_S16_BE, SND_PCM_FORMAT_S16_BE}, + {AF_FORMAT_U32_LE, SND_PCM_FORMAT_U32_LE}, + {AF_FORMAT_U32_BE, SND_PCM_FORMAT_U32_BE}, + {AF_FORMAT_S32_LE, SND_PCM_FORMAT_S32_LE}, + {AF_FORMAT_S32_BE, SND_PCM_FORMAT_S32_BE}, + {AF_FORMAT_U24_LE, SND_PCM_FORMAT_U24_3LE}, + {AF_FORMAT_U24_BE, SND_PCM_FORMAT_U24_3BE}, + {AF_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE}, + {AF_FORMAT_S24_BE, SND_PCM_FORMAT_S24_3BE}, + {AF_FORMAT_FLOAT_LE, SND_PCM_FORMAT_FLOAT_LE}, + {AF_FORMAT_FLOAT_BE, SND_PCM_FORMAT_FLOAT_BE}, + {AF_FORMAT_AC3_LE, SND_PCM_FORMAT_S16_LE}, + {AF_FORMAT_AC3_BE, SND_PCM_FORMAT_S16_BE}, + {AF_FORMAT_IEC61937_LE, SND_PCM_FORMAT_S16_LE}, + {AF_FORMAT_IEC61937_BE, SND_PCM_FORMAT_S16_BE}, + {AF_FORMAT_MPEG2, SND_PCM_FORMAT_MPEG}, + {AF_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN}, +}; + +static int find_alsa_format(int af_format) { - int err, len; - char *ac3_device, *args; - - if (try_ac3) { - /* to set the non-audio bit, use AES0=6 */ - len = strlen(device); - ac3_device = malloc(len + 7 + 1); - if (!ac3_device) - return -ENOMEM; - strcpy(ac3_device, device); - args = strchr(ac3_device, ':'); - if (!args) { - /* no existing parameters: add it behind device name */ - strcat(ac3_device, ":AES0=6"); - } else { - do - ++args; - while (isspace(*args)); - if (*args == '\0') { - /* ":" but no parameters */ - strcat(ac3_device, "AES0=6"); - } else if (*args != '{') { - /* a simple list of parameters: add it at the end of the list */ - strcat(ac3_device, ",AES0=6"); - } else { - /* parameters in config syntax: add it inside the { } block */ - do - --len; - while (len > 0 && isspace(ac3_device[len])); - if (ac3_device[len] == '}') - strcpy(ac3_device + len, " AES0=6}"); - } + for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) { + if (mp_to_alsa_format[n][0] == af_format) + return mp_to_alsa_format[n][1]; + } + return SND_PCM_FORMAT_UNKNOWN; +} + +// Lists device names and their implied channel map. +// The second item must be resolvable with mp_chmap_from_str(). +// Source: http://www.alsa-project.org/main/index.php/DeviceNames +// (Speaker names are slightly different from mpv's.) +static const char *device_channel_layouts[][2] = { + {"default", "fc"}, + {"default", "fl-fr"}, + {"rear", "bl-br"}, + {"center_lfe", "fc-lfe"}, + {"side", "sl-sr"}, + {"surround40", "fl-fr-fc-bc"}, + {"surround50", "fl-fr-bl-br-fc"}, + {"surround41", "fl-fr-bl-br-lfe"}, + {"surround51", "fl-fr-bl-br-fc-lfe"}, + {"surround71", "fl-fr-bl-br-fc-lfe-sl-sr"}, +}; + +#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0])) + +#define NUM_ALSA_CHMAPS ARRAY_LEN(device_channel_layouts) + +static const char *select_chmap(struct ao *ao) +{ + struct mp_chmap_sel sel = {0}; + struct mp_chmap maps[NUM_ALSA_CHMAPS]; + for (int n = 0; n < NUM_ALSA_CHMAPS; n++) { + mp_chmap_from_str(&maps[n], bstr0(device_channel_layouts[n][1])); + mp_chmap_sel_add_map(&sel, &maps[n]); + }; + + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + return NULL; + + for (int n = 0; n < NUM_ALSA_CHMAPS; n++) { + if (mp_chmap_equals(&ao->channels, &maps[n])) + return device_channel_layouts[n][0]; + } + + char *name = mp_chmap_to_str(&ao->channels); + mp_tmsg(MSGT_AO, MSGL_ERR, + "[AO_ALSA] channel layout %s (%d ch) not supported.\n", + name, ao->channels.num); + talloc_free(name); + return "default"; +} + +static int try_open_device(struct ao *ao, const char *device, int open_mode, + int try_ac3) +{ + struct priv *p = ao->priv; + int err, len; + char *ac3_device, *args; + + if (try_ac3) { + /* to set the non-audio bit, use AES0=6 */ + len = strlen(device); + ac3_device = malloc(len + 7 + 1); + if (!ac3_device) + return -ENOMEM; + strcpy(ac3_device, device); + args = strchr(ac3_device, ':'); + if (!args) { + /* no existing parameters: add it behind device name */ + strcat(ac3_device, ":AES0=6"); + } else { + do { + ++args; + } while (isspace(*args)); + if (*args == '\0') { + /* ":" but no parameters */ + strcat(ac3_device, "AES0=6"); + } else if (*args != '{') { + /* a simple list of parameters: add it at the end of the list */ + strcat(ac3_device, ",AES0=6"); + } else { + /* parameters in config syntax: add it inside the { } block */ + do { + --len; + } while (len > 0 && isspace(ac3_device[len])); + if (ac3_device[len] == '}') + strcpy(ac3_device + len, " AES0=6}"); + } + } + err = snd_pcm_open + (&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, open_mode); + free(ac3_device); + if (!err) + return 0; } - err = snd_pcm_open(&alsa_handler, ac3_device, SND_PCM_STREAM_PLAYBACK, - open_mode); - free(ac3_device); - if (!err) - return 0; - } - return snd_pcm_open(&alsa_handler, device, SND_PCM_STREAM_PLAYBACK, - open_mode); + return snd_pcm_open + (&p->alsa, device, SND_PCM_STREAM_PLAYBACK, open_mode); } /* open & setup audio device - return: 1=success 0=fail -*/ -static int init(int rate_hz, int channels, int format, int flags) + return: 0=success -1=fail + */ +static int init(struct ao *ao, char *params) { int err; int block; @@ -340,89 +421,29 @@ static int init(int rate_hz, int channels, int format, int flags) snd_pcm_uframes_t bufsize; snd_pcm_uframes_t boundary; const opt_t subopts[] = { - {"block", OPT_ARG_BOOL, &block, NULL}, - {"device", OPT_ARG_STR, &device, str_maxlen}, - {NULL} + {"block", OPT_ARG_BOOL, &block, NULL}, + {"device", OPT_ARG_STR, &device, str_maxlen}, + {NULL} }; + struct priv *p = talloc_zero(ao, struct priv); + ao->priv = p; + char alsa_device[ALSA_DEVICE_SIZE + 1]; // make sure alsa_device is null-terminated even when using strncpy etc. memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1); - mp_msg(MSGT_AO,MSGL_V,"alsa-init: requested format: %d Hz, %d channels, %x\n", rate_hz, - channels, format); - alsa_handler = NULL; - mp_msg(MSGT_AO,MSGL_V,"alsa-init: using ALSA %s\n", snd_asoundlib_version()); + mp_msg(MSGT_AO, MSGL_V, + "alsa-init: requested format: %d Hz, %d channels, %x\n", + ao->samplerate, ao->channels.num, ao->format); + p->alsa = NULL; + mp_msg(MSGT_AO, MSGL_V, "alsa-init: using ALSA %s\n", snd_asoundlib_version()); - prepause_frames = 0; - delay_before_pause = 0; + p->prepause_frames = 0; + p->delay_before_pause = 0; snd_lib_error_set_handler(alsa_error_handler); - ao_data.samplerate = rate_hz; - ao_data.format = format; - ao_data.channels = channels; - - switch (format) - { - case AF_FORMAT_S8: - alsa_format = SND_PCM_FORMAT_S8; - break; - case AF_FORMAT_U8: - alsa_format = SND_PCM_FORMAT_U8; - break; - case AF_FORMAT_U16_LE: - alsa_format = SND_PCM_FORMAT_U16_LE; - break; - case AF_FORMAT_U16_BE: - alsa_format = SND_PCM_FORMAT_U16_BE; - break; - case AF_FORMAT_AC3_LE: - case AF_FORMAT_S16_LE: - case AF_FORMAT_IEC61937_LE: - alsa_format = SND_PCM_FORMAT_S16_LE; - break; - case AF_FORMAT_AC3_BE: - case AF_FORMAT_S16_BE: - case AF_FORMAT_IEC61937_BE: - alsa_format = SND_PCM_FORMAT_S16_BE; - break; - case AF_FORMAT_U32_LE: - alsa_format = SND_PCM_FORMAT_U32_LE; - break; - case AF_FORMAT_U32_BE: - alsa_format = SND_PCM_FORMAT_U32_BE; - break; - case AF_FORMAT_S32_LE: - alsa_format = SND_PCM_FORMAT_S32_LE; - break; - case AF_FORMAT_S32_BE: - alsa_format = SND_PCM_FORMAT_S32_BE; - break; - case AF_FORMAT_U24_LE: - alsa_format = SND_PCM_FORMAT_U24_3LE; - break; - case AF_FORMAT_U24_BE: - alsa_format = SND_PCM_FORMAT_U24_3BE; - break; - case AF_FORMAT_S24_LE: - alsa_format = SND_PCM_FORMAT_S24_3LE; - break; - case AF_FORMAT_S24_BE: - alsa_format = SND_PCM_FORMAT_S24_3BE; - break; - case AF_FORMAT_FLOAT_LE: - alsa_format = SND_PCM_FORMAT_FLOAT_LE; - break; - case AF_FORMAT_FLOAT_BE: - alsa_format = SND_PCM_FORMAT_FLOAT_BE; - break; - - default: - alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1 - break; - } - //subdevice parsing // set defaults block = 1; @@ -432,347 +453,271 @@ static int init(int rate_hz, int channels, int format, int flags) * while opening the abstract alias for the spdif subdevice * 'iec958' */ - if (AF_FORMAT_IS_IEC61937(format)) { - device.str = "iec958"; - mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", channels); - } - else - /* in any case for multichannel playback we should select - * appropriate device - */ - switch (channels) { - case 1: - case 2: - device.str = "default"; - mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n"); - break; - case 4: - if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) - // hack - use the converter plugin - device.str = "plug:surround40"; - else - device.str = "surround40"; - mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n"); - break; - case 6: - if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) - device.str = "plug:surround51"; - else - device.str = "surround51"; - mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n"); - break; - case 8: - if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) - device.str = "plug:surround71"; - else - device.str = "surround71"; - mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround71\n"); - break; - default: - device.str = "default"; - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] %d channels are not supported.\n",channels); + device.str = NULL; + if (AF_FORMAT_IS_IEC61937(ao->format)) { + device.str = "iec958"; + mp_msg(MSGT_AO, MSGL_V, + "alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", + ao->channels.num); + } else { + device.str = select_chmap(ao); + if (strcmp(device.str, "default") != 0 && ao->format == AF_FORMAT_FLOAT_NE) + { + // hack - use the converter plugin (why the heck?) + device.str = talloc_asprintf(ao, "plug:%s", device.str); } + } device.len = strlen(device.str); - if (subopt_parse(ao_subdevice, subopts) != 0) { + if (subopt_parse(params, subopts) != 0) { print_help(); return 0; } parse_device(alsa_device, device.str, device.len); - mp_msg(MSGT_AO,MSGL_V,"alsa-init: using device %s\n", alsa_device); - - alsa_can_pause = 1; - - if (!alsa_handler) { - int open_mode = block ? 0 : SND_PCM_NONBLOCK; - int isac3 = AF_FORMAT_IS_IEC61937(format); - //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC - if ((err = try_open_device(alsa_device, open_mode, isac3)) < 0) - { - if (err != -EBUSY && !block) { - mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Open in nonblock-mode failed, trying to open in block-mode.\n"); - if ((err = try_open_device(alsa_device, 0, isac3)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err)); - return 0; - } - } else { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err)); - return 0; - } - } - - if ((err = snd_pcm_nonblock(alsa_handler, 0)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AL_ALSA] Error setting block-mode %s.\n", snd_strerror(err)); - } else { - mp_msg(MSGT_AO,MSGL_V,"alsa-init: pcm opened in blocking mode\n"); - } - - snd_pcm_hw_params_t *alsa_hwparams; - snd_pcm_sw_params_t *alsa_swparams; - - snd_pcm_hw_params_alloca(&alsa_hwparams); - snd_pcm_sw_params_alloca(&alsa_swparams); - - // setting hw-parameters - if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get initial parameters: %s\n", - snd_strerror(err)); - return 0; - } - - err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (err < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set access type: %s\n", - snd_strerror(err)); - return 0; - } - - /* workaround for nonsupported formats - sets default format to S16_LE if the given formats aren't supported */ - if ((err = snd_pcm_hw_params_test_format(alsa_handler, alsa_hwparams, - alsa_format)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_INFO, - "[AO_ALSA] Format %s is not supported by hardware, trying default.\n", af_fmt2str_short(format)); - alsa_format = SND_PCM_FORMAT_S16_LE; - if (AF_FORMAT_IS_AC3(ao_data.format)) - ao_data.format = AF_FORMAT_AC3_LE; - else if (AF_FORMAT_IS_IEC61937(ao_data.format)) - ao_data.format = AF_FORMAT_IEC61937_LE; - else - ao_data.format = AF_FORMAT_S16_LE; - } - - if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams, - alsa_format)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set format: %s\n", - snd_strerror(err)); - return 0; - } - - if ((err = snd_pcm_hw_params_set_channels_near(alsa_handler, alsa_hwparams, - &ao_data.channels)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set channels: %s\n", - snd_strerror(err)); - return 0; - } - - /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11) - prefer our own resampler, since that allows users to choose the resampler, - even per file if desired */ - if ((err = snd_pcm_hw_params_set_rate_resample(alsa_handler, alsa_hwparams, - 0)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to disable resampling: %s\n", - snd_strerror(err)); - return 0; - } - - if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams, - &ao_data.samplerate, NULL)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set samplerate-2: %s\n", - snd_strerror(err)); - return 0; + mp_msg(MSGT_AO, MSGL_V, "alsa-init: using device %s\n", alsa_device); + + p->can_pause = 1; + + int open_mode = block ? 0 : SND_PCM_NONBLOCK; + int isac3 = AF_FORMAT_IS_IEC61937(ao->format); + //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC + err = try_open_device(ao, alsa_device, open_mode, isac3); + if (err < 0) { + if (err != -EBUSY && !block) { + mp_tmsg(MSGT_AO, MSGL_INFO, "[AO_ALSA] Open in nonblock-mode " + "failed, trying to open in block-mode.\n"); + err = try_open_device(ao, alsa_device, 0, isac3); } + CHECK_ALSA_ERROR("Playback open error"); + } - bytes_per_sample = af_fmt2bits(ao_data.format) / 8; - bytes_per_sample *= ao_data.channels; - ao_data.bps = ao_data.samplerate * bytes_per_sample; - - if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams, - &(unsigned int){BUFFER_TIME}, NULL)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set buffer time near: %s\n", - snd_strerror(err)); - return 0; - } - - if ((err = snd_pcm_hw_params_set_periods_near(alsa_handler, alsa_hwparams, - &(unsigned int){FRAGCOUNT}, NULL)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set periods: %s\n", - snd_strerror(err)); - return 0; - } - - /* finally install hardware parameters */ - if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set hw-parameters: %s\n", - snd_strerror(err)); - return 0; - } - // end setting hw-params - - - // gets buffersize for control - if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get buffersize: %s\n", snd_strerror(err)); - return 0; - } - else { - ao_data.buffersize = bufsize * bytes_per_sample; - mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize); - } - - if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO ALSA] Unable to get period size: %s\n", snd_strerror(err)); - return 0; - } else { - mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size); - } - ao_data.outburst = chunk_size * bytes_per_sample; - - /* setting software parameters */ - if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get sw-parameters: %s\n", - snd_strerror(err)); - return 0; - } - if ((err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get boundary: %s\n", - snd_strerror(err)); - return 0; - } - /* start playing when one period has been written */ - if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, chunk_size)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set start threshold: %s\n", - snd_strerror(err)); - return 0; - } - /* disable underrun reporting */ - if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, boundary)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set stop threshold: %s\n", - snd_strerror(err)); - return 0; - } - /* play silence when there is an underrun */ - if ((err = snd_pcm_sw_params_set_silence_size(alsa_handler, alsa_swparams, boundary)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set silence size: %s\n", - snd_strerror(err)); - return 0; - } - if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get sw-parameters: %s\n", - snd_strerror(err)); - return 0; - } - /* end setting sw-params */ - - alsa_can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams); - - mp_msg(MSGT_AO,MSGL_V,"alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n", - ao_data.samplerate, ao_data.channels, (int)bytes_per_sample, ao_data.buffersize, - snd_pcm_format_description(alsa_format)); - - } // end switch alsa_handler (spdif) - return 1; + err = snd_pcm_nonblock(p->alsa, 0); + if (err < 0) { + mp_tmsg(MSGT_AO, MSGL_ERR, + "[AL_ALSA] Error setting block-mode %s.\n", + snd_strerror(err)); + } else { + mp_msg(MSGT_AO, MSGL_V, "alsa-init: pcm opened in blocking mode\n"); + } + + snd_pcm_hw_params_t *alsa_hwparams; + snd_pcm_sw_params_t *alsa_swparams; + + snd_pcm_hw_params_alloca(&alsa_hwparams); + snd_pcm_sw_params_alloca(&alsa_swparams); + + // setting hw-parameters + err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams); + CHECK_ALSA_ERROR("Unable to get initial parameters"); + + err = snd_pcm_hw_params_set_access + (p->alsa, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); + CHECK_ALSA_ERROR("Unable to set access type"); + + p->alsa_fmt = find_alsa_format(ao->format); + if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) { + p->alsa_fmt = SND_PCM_FORMAT_S16; + ao->format = AF_FORMAT_S16_NE; + } + + err = snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt); + if (err < 0) { + mp_tmsg(MSGT_AO, MSGL_INFO, "[AO_ALSA] Format %s is not supported " + "by hardware, trying default.\n", af_fmt2str_short(ao->format)); + p->alsa_fmt = SND_PCM_FORMAT_S16_LE; + if (AF_FORMAT_IS_AC3(ao->format)) + ao->format = AF_FORMAT_AC3_LE; + else if (AF_FORMAT_IS_IEC61937(ao->format)) + ao->format = AF_FORMAT_IEC61937_LE; + else + ao->format = AF_FORMAT_S16_LE; + } + + err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt); + CHECK_ALSA_ERROR("Unable to set format"); + + int num_channels = ao->channels.num; + err = snd_pcm_hw_params_set_channels_near + (p->alsa, alsa_hwparams, &num_channels); + CHECK_ALSA_ERROR("Unable to set channels"); + + if (num_channels != ao->channels.num) { + mp_tmsg(MSGT_AO, MSGL_ERR, + "[AO_ALSA] Couldn't get requested number of channels.\n"); + mp_chmap_from_channels_alsa(&ao->channels, num_channels); + } + + /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11) + prefer our own resampler, since that allows users to choose the resampler, + even per file if desired */ + err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0); + CHECK_ALSA_ERROR("Unable to disable resampling"); + + err = snd_pcm_hw_params_set_rate_near + (p->alsa, alsa_hwparams, &ao->samplerate, NULL); + CHECK_ALSA_ERROR("Unable to set samplerate-2"); + + p->bytes_per_sample = af_fmt2bits(ao->format) / 8; + p->bytes_per_sample *= ao->channels.num; + ao->bps = ao->samplerate * p->bytes_per_sample; + + err = snd_pcm_hw_params_set_buffer_time_near + (p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL); + CHECK_ALSA_ERROR("Unable to set buffer time near"); + + err = snd_pcm_hw_params_set_periods_near + (p->alsa, alsa_hwparams, &(unsigned int){FRAGCOUNT}, NULL); + CHECK_ALSA_ERROR("Unable to set periods"); + + /* finally install hardware parameters */ + err = snd_pcm_hw_params(p->alsa, alsa_hwparams); + CHECK_ALSA_ERROR("Unable to set hw-parameters"); + + // end setting hw-params + + // gets buffersize for control + err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize); + CHECK_ALSA_ERROR("Unable to get buffersize"); + + ao->buffersize = bufsize * p->bytes_per_sample; + mp_msg(MSGT_AO, MSGL_V, "alsa-init: got buffersize=%i\n", + ao->buffersize); + + err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL); + CHECK_ALSA_ERROR("Unable to get period size"); + + mp_msg(MSGT_AO, MSGL_V, "alsa-init: got period size %li\n", chunk_size); + ao->outburst = chunk_size * p->bytes_per_sample; + + /* setting software parameters */ + err = snd_pcm_sw_params_current(p->alsa, alsa_swparams); + CHECK_ALSA_ERROR("Unable to get sw-parameters"); + + err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary); + CHECK_ALSA_ERROR("Unable to get boundary"); + + /* start playing when one period has been written */ + err = snd_pcm_sw_params_set_start_threshold + (p->alsa, alsa_swparams, chunk_size); + CHECK_ALSA_ERROR("Unable to set start threshold"); + + /* disable underrun reporting */ + err = snd_pcm_sw_params_set_stop_threshold + (p->alsa, alsa_swparams, boundary); + CHECK_ALSA_ERROR("Unable to set stop threshold"); + + /* play silence when there is an underrun */ + err = snd_pcm_sw_params_set_silence_size + (p->alsa, alsa_swparams, boundary); + CHECK_ALSA_ERROR("Unable to set silence size"); + + err = snd_pcm_sw_params(p->alsa, alsa_swparams); + CHECK_ALSA_ERROR("Unable to get sw-parameters"); + + /* end setting sw-params */ + + p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams); + + mp_msg(MSGT_AO, MSGL_V, + "alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n", + ao->samplerate, ao->channels.num, (int)p->bytes_per_sample, + ao->buffersize, snd_pcm_format_description(p->alsa_fmt)); + + return 0; + +alsa_error: + return -1; } // end init /* close audio device */ -static void uninit(int immed) +static void uninit(struct ao *ao, bool immed) { + struct priv *p = ao->priv; - if (alsa_handler) { - int err; + if (p->alsa) { + int err; + + if (!immed) + snd_pcm_drain(p->alsa); - if (!immed) - snd_pcm_drain(alsa_handler); - - if ((err = snd_pcm_close(alsa_handler)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm close error: %s\n", snd_strerror(err)); - return; - } - else { - alsa_handler = NULL; - mp_msg(MSGT_AO,MSGL_V,"alsa-uninit: pcm closed\n"); + err = snd_pcm_close(p->alsa); + CHECK_ALSA_ERROR("pcm close error"); + + p->alsa = NULL; + mp_msg(MSGT_AO, MSGL_V, "alsa-uninit: pcm closed\n"); + } else { + mp_tmsg(MSGT_AO, MSGL_ERR, "[AO_ALSA] No handler defined!\n"); } - } - else { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] No handler defined!\n"); - } + +alsa_error: ; } -static void audio_pause(void) +static void audio_pause(struct ao *ao) { + struct priv *p = ao->priv; int err; - if (alsa_can_pause) { - delay_before_pause = get_delay(); - if ((err = snd_pcm_pause(alsa_handler, 1)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm pause error: %s\n", snd_strerror(err)); - return; - } - mp_msg(MSGT_AO,MSGL_V,"alsa-pause: pause supported by hardware\n"); + if (p->can_pause) { + p->delay_before_pause = get_delay(ao); + err = snd_pcm_pause(p->alsa, 1); + CHECK_ALSA_ERROR("pcm pause error"); + mp_msg(MSGT_AO, MSGL_V, "alsa-pause: pause supported by hardware\n"); } else { - if (snd_pcm_delay(alsa_handler, &prepause_frames) < 0 - || prepause_frames < 0) - prepause_frames = 0; - delay_before_pause = prepause_frames / (float)ao_data.samplerate; + if (snd_pcm_delay(p->alsa, &p->prepause_frames) < 0 + || p->prepause_frames < 0) + p->prepause_frames = 0; + p->delay_before_pause = p->prepause_frames / (float)ao->samplerate; - if ((err = snd_pcm_drop(alsa_handler)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm drop error: %s\n", snd_strerror(err)); - return; - } + err = snd_pcm_drop(p->alsa); + CHECK_ALSA_ERROR("pcm drop error"); } + +alsa_error: ; } -static void audio_resume(void) +static void audio_resume(struct ao *ao) { + struct priv *p = ao->priv; int err; - if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) { - mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Pcm in suspend mode, trying to resume.\n"); - while ((err = snd_pcm_resume(alsa_handler)) == -EAGAIN) sleep(1); + if (snd_pcm_state(p->alsa) == SND_PCM_STATE_SUSPENDED) { + mp_tmsg(MSGT_AO, MSGL_INFO, + "[AO_ALSA] Pcm in suspend mode, trying to resume.\n"); + while ((err = snd_pcm_resume(p->alsa)) == -EAGAIN) + sleep(1); } - if (alsa_can_pause) { - if ((err = snd_pcm_pause(alsa_handler, 0)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm resume error: %s\n", snd_strerror(err)); - return; - } - mp_msg(MSGT_AO,MSGL_V,"alsa-resume: resume supported by hardware\n"); + if (p->can_pause) { + err = snd_pcm_pause(p->alsa, 0); + CHECK_ALSA_ERROR("pcm resume error"); + mp_msg(MSGT_AO, MSGL_V, "alsa-resume: resume supported by hardware\n"); } else { - if ((err = snd_pcm_prepare(alsa_handler)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err)); - return; - } - if (prepause_frames) { - void *silence = calloc(prepause_frames, bytes_per_sample); - play(silence, prepause_frames * bytes_per_sample, 0); + err = snd_pcm_prepare(p->alsa); + CHECK_ALSA_ERROR("pcm prepare error"); + if (p->prepause_frames) { + void *silence = calloc(p->prepause_frames, p->bytes_per_sample); + play(ao, silence, p->prepause_frames * p->bytes_per_sample, 0); free(silence); } } + +alsa_error: ; } /* stop playing and empty buffers (for seeking/pause) */ -static void reset(void) +static void reset(struct ao *ao) { + struct priv *p = ao->priv; int err; - prepause_frames = 0; - delay_before_pause = 0; - if ((err = snd_pcm_drop(alsa_handler)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err)); - return; - } - if ((err = snd_pcm_prepare(alsa_handler)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err)); - return; - } - return; + p->prepause_frames = 0; + p->delay_before_pause = 0; + err = snd_pcm_drop(p->alsa); + CHECK_ALSA_ERROR("pcm prepare error"); + err = snd_pcm_prepare(p->alsa); + CHECK_ALSA_ERROR("pcm prepare error"); + +alsa_error: ; } /* @@ -780,91 +725,115 @@ static void reset(void) returns: number of bytes played modified last at 29.06.02 by jp thanxs for marius <marius@rospot.com> for giving us the light ;) -*/ + */ -static int play(void* data, int len, int flags) +static int play(struct ao *ao, void *data, int len, int flags) { - int num_frames; - snd_pcm_sframes_t res = 0; - if (!(flags & AOPLAY_FINAL_CHUNK)) - len = len / ao_data.outburst * ao_data.outburst; - num_frames = len / bytes_per_sample; + struct priv *p = ao->priv; + int num_frames; + snd_pcm_sframes_t res = 0; + if (!(flags & AOPLAY_FINAL_CHUNK)) + len = len / ao->outburst * ao->outburst; + num_frames = len / p->bytes_per_sample; - //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len); + //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len); - if (!alsa_handler) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Device configuration error."); - return 0; - } + if (!p->alsa) { + mp_tmsg(MSGT_AO, MSGL_ERR, "[AO_ALSA] Device configuration error."); + return 0; + } - if (num_frames == 0) - return 0; + if (num_frames == 0) + return 0; + + do { + res = snd_pcm_writei(p->alsa, data, num_frames); + + if (res == -EINTR) { + /* nothing to do */ + res = 0; + } else if (res == -ESTRPIPE) { /* suspend */ + mp_tmsg(MSGT_AO, MSGL_INFO, + "[AO_ALSA] Pcm in suspend mode, trying to resume.\n"); + while ((res = snd_pcm_resume(p->alsa)) == -EAGAIN) + sleep(1); + } + if (res < 0) { + mp_tmsg(MSGT_AO, MSGL_ERR, "[AO_ALSA] Write error: %s\n", + snd_strerror(res)); + mp_tmsg(MSGT_AO, MSGL_INFO, + "[AO_ALSA] Trying to reset soundcard.\n"); + res = snd_pcm_prepare(p->alsa); + int err = res; + CHECK_ALSA_ERROR("pcm prepare error"); + res = 0; + } + } while (res == 0); + + return res < 0 ? 0 : res * p->bytes_per_sample; - do { - res = snd_pcm_writei(alsa_handler, data, num_frames); - - if (res == -EINTR) { - /* nothing to do */ - res = 0; - } - else if (res == -ESTRPIPE) { /* suspend */ - mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Pcm in suspend mode, trying to resume.\n"); - while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN) - sleep(1); - } - if (res < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Write error: %s\n", snd_strerror(res)); - mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Trying to reset soundcard.\n"); - if ((res = snd_pcm_prepare(alsa_handler)) < 0) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(res)); - break; - } - res = 0; - } - } while (res == 0); - - return res < 0 ? 0 : res * bytes_per_sample; +alsa_error: + return 0; } /* how many byes are free in the buffer */ -static int get_space(void) +static int get_space(struct ao *ao) { + struct priv *p = ao->priv; snd_pcm_status_t *status; - int ret; + int err; snd_pcm_status_alloca(&status); - if ((ret = snd_pcm_status(alsa_handler, status)) < 0) - { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Cannot get pcm status: %s\n", snd_strerror(ret)); - return 0; - } + err = snd_pcm_status(p->alsa, status); + CHECK_ALSA_ERROR("cannot get pcm status"); - unsigned space = snd_pcm_status_get_avail(status) * bytes_per_sample; - if (space > ao_data.buffersize) // Buffer underrun? - space = ao_data.buffersize; + unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample; + if (space > ao->buffersize) // Buffer underrun? + space = ao->buffersize; return space; + +alsa_error: + return 0; } /* delay in seconds between first and last sample in buffer */ -static float get_delay(void) +static float get_delay(struct ao *ao) { - if (alsa_handler) { - snd_pcm_sframes_t delay; + struct priv *p = ao->priv; + if (p->alsa) { + snd_pcm_sframes_t delay; - if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_PAUSED) - return delay_before_pause; + if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED) + return p->delay_before_pause; - if (snd_pcm_delay(alsa_handler, &delay) < 0) - return 0; + if (snd_pcm_delay(p->alsa, &delay) < 0) + return 0; - if (delay < 0) { - /* underrun - move the application pointer forward to catch up */ - snd_pcm_forward(alsa_handler, -delay); - delay = 0; - } - return (float)delay / (float)ao_data.samplerate; - } else { - return 0; - } + if (delay < 0) { + /* underrun - move the application pointer forward to catch up */ + snd_pcm_forward(p->alsa, -delay); + delay = 0; + } + return (float)delay / (float)ao->samplerate; + } else + return 0; } + +const struct ao_driver audio_out_alsa = { + .info = &(const struct ao_info) { + "ALSA-0.9.x-1.x audio output", + "alsa", + "Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>", + "under development" + }, + .init = init, + .uninit = uninit, + .control = control, + .get_space = get_space, + .play = play, + .get_delay = get_delay, + .pause = audio_pause, + .resume = audio_resume, + .reset = reset, +}; diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c index ba8f517c75..54ff1b4915 100644 --- a/audio/out/ao_coreaudio.c +++ b/audio/out/ao_coreaudio.c @@ -412,7 +412,7 @@ static void print_help(void) free(devids); } -static int init(int rate,int channels,int format,int flags) +static int init(int rate,const struct mp_chmap *channels,int format,int flags) { AudioStreamBasicDescription inDesc; AudioComponentDescription desc; @@ -439,7 +439,7 @@ int device_id, display_help = 0; return 0; } - ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, channels, af_fmt2str_short(format), flags); + ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, ao_data.channels.num, af_fmt2str_short(format), flags); ao = calloc(1, sizeof(ao_coreaudio_t)); @@ -499,10 +499,15 @@ int device_id, display_help = 0; // Save selected device id ao->i_selected_dev = devid_def; + struct mp_chmap_sel chmap_sel = {0}; + mp_chmap_sel_add_waveext(&chmap_sel); + if (!ao_chmap_sel_adjust(&ao_data, &ao_data.channels, &chmap_sel)) + goto err_out; + // Build Description for the input format inDesc.mSampleRate=rate; inDesc.mFormatID=ao->b_supports_digital ? kAudioFormat60958AC3 : kAudioFormatLinearPCM; - inDesc.mChannelsPerFrame=channels; + inDesc.mChannelsPerFrame=ao_data.channels.num; inDesc.mBitsPerChannel=af_fmt2bits(format); if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) { @@ -521,7 +526,7 @@ int device_id, display_help = 0; inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian; inDesc.mFramesPerPacket = 1; - ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*channels*(inDesc.mBitsPerChannel/8); + ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*ao_data.channels.num*(inDesc.mBitsPerChannel/8); print_format(MSGL_V, "source:",&inDesc); if (ao->b_supports_digital) @@ -605,7 +610,8 @@ int device_id, display_help = 0; ao->chunk_size = maxFrames;//*inDesc.mBytesPerFrame; ao_data.samplerate = inDesc.mSampleRate; - ao_data.channels = inDesc.mChannelsPerFrame; + if (!ao_chmap_sel_get_def(&ao_data, &chmap_sel, &ao_data.channels, inDesc.mChannelsPerFrame)) + goto err_out2; ao_data.bps = ao_data.samplerate * inDesc.mBytesPerFrame; ao_data.outburst = ao->chunk_size; ao_data.buffersize = ao_data.bps; @@ -837,7 +843,8 @@ static int OpenSPDIF(void) ao->chunk_size = ao->stream_format.mBytesPerPacket; ao_data.samplerate = ao->stream_format.mSampleRate; - ao_data.channels = ao->stream_format.mChannelsPerFrame; + // Applies default ordering; ok because AC3 data is always in mpv internal channel order + mp_chmap_from_channels(&ao_data.channels, ao->stream_format.mChannelsPerFrame); ao_data.bps = ao_data.samplerate * (ao->stream_format.mBytesPerPacket/ao->stream_format.mFramesPerPacket); ao_data.outburst = ao->chunk_size; ao_data.buffersize = ao_data.bps; diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c index 9ad27306bd..967084d3ae 100644 --- a/audio/out/ao_dsound.c +++ b/audio/out/ao_dsound.c @@ -64,26 +64,6 @@ LIBAO_EXTERN(dsound) static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}}; -#define SPEAKER_FRONT_LEFT 0x1 -#define SPEAKER_FRONT_RIGHT 0x2 -#define SPEAKER_FRONT_CENTER 0x4 -#define SPEAKER_LOW_FREQUENCY 0x8 -#define SPEAKER_BACK_LEFT 0x10 -#define SPEAKER_BACK_RIGHT 0x20 -#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 -#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 -#define SPEAKER_BACK_CENTER 0x100 -#define SPEAKER_SIDE_LEFT 0x200 -#define SPEAKER_SIDE_RIGHT 0x400 -#define SPEAKER_TOP_CENTER 0x800 -#define SPEAKER_TOP_FRONT_LEFT 0x1000 -#define SPEAKER_TOP_FRONT_CENTER 0x2000 -#define SPEAKER_TOP_FRONT_RIGHT 0x4000 -#define SPEAKER_TOP_BACK_LEFT 0x8000 -#define SPEAKER_TOP_BACK_CENTER 0x10000 -#define SPEAKER_TOP_BACK_RIGHT 0x20000 -#define SPEAKER_RESERVED 0x80000000 - #if 0 #define DSSPEAKER_HEADPHONE 0x00000001 #define DSSPEAKER_MONO 0x00000002 @@ -107,28 +87,6 @@ typedef struct { } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; #endif -static const int channel_mask[] = { - /* 1 */ SPEAKER_FRONT_CENTER, - /* 2 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, - /* 3 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT - | SPEAKER_LOW_FREQUENCY, - /* 4 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT - | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, - /* 5 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT - | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT - | SPEAKER_LOW_FREQUENCY, - /* 6 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT - | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT - | SPEAKER_LOW_FREQUENCY, - /* 7 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT - | SPEAKER_BACK_LEFT | SPEAKER_BACK_CENTER | SPEAKER_BACK_RIGHT - | SPEAKER_LOW_FREQUENCY, - /* 8 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT - | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT - | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT - | SPEAKER_LOW_FREQUENCY, -}; - static HINSTANCE hdsound_dll = NULL; ///handle to the dll static LPDIRECTSOUND hds = NULL; ///direct sound object static LPDIRECTSOUNDBUFFER hdspribuf = NULL; ///primary direct sound buffer @@ -349,22 +307,9 @@ static int write_buffer(unsigned char *data, int len) if (SUCCEEDED(res)) { if (!AF_FORMAT_IS_AC3(ao_data.format)) { - int sampsize = af_fmt2bits(ao_data.format) / 8; - reorder_channel_copy_nch(data, - AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, - lpvPtr1, - AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT, - ao_data.channels, - dwBytes1 / sampsize, - sampsize); + memcpy(lpvPtr1, data, dwBytes1); if (lpvPtr2 != NULL) - reorder_channel_copy_nch(data + dwBytes1, - AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, - lpvPtr2, - AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT, - ao_data.channels, - dwBytes2 / sampsize, - sampsize); + memcpy(lpvPtr2, (char *)data + dwBytes1, dwBytes2); write_offset+=dwBytes1+dwBytes2; if(write_offset>=buffer_size) @@ -432,7 +377,7 @@ static int control(int cmd, void *arg) \param flags unused \return 1=success 0=fail */ -static int init(int rate, int channels, int format, int flags) +static int init(int rate, const struct mp_chmap *channels, int format, int flags) { int res; if (!InitDirectSound()) return 0; @@ -445,15 +390,14 @@ static int init(int rate, int channels, int format, int flags) DSBUFFERDESC dsbpridesc; DSBUFFERDESC dsbdesc; - //check if the channel count and format is supported in general - if (channels > FF_ARRAY_ELEMS(channel_mask)) { - UninitDirectSound(); - mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n"); - return 0; - } - - if (AF_FORMAT_IS_AC3(format)) + if (AF_FORMAT_IS_AC3(format)) { format = AF_FORMAT_AC3_NE; + } else { + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_waveext(&sel); + if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels)) + return 0; + } switch(format){ case AF_FORMAT_AC3_NE: case AF_FORMAT_S24_LE: @@ -465,25 +409,24 @@ static int init(int rate, int channels, int format, int flags) format=AF_FORMAT_S16_LE; } //fill global ao_data - ao_data.channels = channels; ao_data.samplerate = rate; ao_data.format = format; - ao_data.bps = channels * rate * (af_fmt2bits(format)>>3); + ao_data.bps = ao_data.channels.num * rate * (af_fmt2bits(format)>>3); if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec - mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format)); + mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, ao_data.channels.num, af_fmt2str_short(format)); mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, ao_data.buffersize / ao_data.bps * 1000); //fill waveformatex ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE)); - wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0; - wformat.Format.nChannels = channels; + wformat.Format.cbSize = (ao_data.channels.num > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0; + wformat.Format.nChannels = ao_data.channels.num; wformat.Format.nSamplesPerSec = rate; if (AF_FORMAT_IS_AC3(format)) { wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF; wformat.Format.wBitsPerSample = 16; wformat.Format.nBlockAlign = 4; } else { - wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM; + wformat.Format.wFormatTag = (ao_data.channels.num > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM; wformat.Format.wBitsPerSample = af_fmt2bits(format); wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3); } @@ -503,8 +446,8 @@ static int init(int rate, int channels, int format, int flags) | DSBCAPS_GLOBALFOCUS /** Allows background playing */ | DSBCAPS_CTRLVOLUME; /** volume control enabled */ - if (channels > 2) { - wformat.dwChannelMask = channel_mask[channels - 1]; + if (ao_data.channels.num > 2) { + wformat.dwChannelMask = mp_chmap_to_waveext(&ao_data.channels); wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample; // Needed for 5.1 on emu101k - shit soundblaster diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c index b75122dc17..0f8baeab86 100644 --- a/audio/out/ao_jack.c +++ b/audio/out/ao_jack.c @@ -50,7 +50,7 @@ static const ao_info_t info = LIBAO_EXTERN(jack) //! maximum number of channels supported, avoids lots of mallocs -#define MAX_CHANS 8 +#define MAX_CHANS MP_NUM_CHANNELS static jack_port_t *ports[MAX_CHANS]; static int num_ports; ///< Number of used ports == number of channels static jack_client_t *client; @@ -202,7 +202,8 @@ static void print_help (void) ); } -static int init(int rate, int channels, int format, int flags) { +static int init(int rate, const struct mp_chmap *channels, int format, int flags) +{ const char **matching_ports = NULL; char *port_name = NULL; char *client_name = NULL; @@ -222,10 +223,12 @@ static int init(int rate, int channels, int format, int flags) { print_help(); return 0; } - if (channels > MAX_CHANS) { - mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] Invalid number of channels: %i\n", channels); + + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_waveext(&sel); + if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels)) goto err_out; - } + if (!client_name) { client_name = malloc(40); sprintf(client_name, "mpv [%d]", getpid()); @@ -249,9 +252,9 @@ static int init(int rate, int channels, int format, int flags) { goto err_out; } i = 1; + num_ports = ao_data.channels.num; while (matching_ports[i]) i++; - if (channels > i) channels = i; - num_ports = channels; + if (num_ports > i) num_ports = i; // create out output ports for (i = 0; i < num_ports; i++) { @@ -281,10 +284,12 @@ static int init(int rate, int channels, int format, int flags) { / (float)rate; callback_interval = 0; - ao_data.channels = channels; + if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, num_ports)) + goto err_out; + ao_data.samplerate = rate; ao_data.format = AF_FORMAT_FLOAT_NE; - ao_data.bps = channels * rate * sizeof(float); + ao_data.bps = ao_data.channels.num * rate * sizeof(float); ao_data.buffersize = CHUNK_SIZE * NUM_CHUNKS; ao_data.outburst = CHUNK_SIZE; free(matching_ports); diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index 33139ce866..d8185a8dca 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -101,7 +101,14 @@ static int init(struct ao *ao, char *params) ac->stream->codec->time_base.den = ao->samplerate; ac->stream->codec->sample_rate = ao->samplerate; - ac->stream->codec->channels = ao->channels; + + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_any(&sel); + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + return -1; + mp_chmap_reorder_to_lavc(&ao->channels); + ac->stream->codec->channels = ao->channels.num; + ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels); ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE; @@ -243,36 +250,6 @@ out_takefirst: ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8; - switch (ao->channels) { - case 1: - ac->stream->codec->channel_layout = AV_CH_LAYOUT_MONO; - break; - case 2: - ac->stream->codec->channel_layout = AV_CH_LAYOUT_STEREO; - break; - /* someone please check if these are what mplayer normally assumes - case 3: - ac->stream->codec->channel_layout = AV_CH_LAYOUT_SURROUND; - break; - case 4: - ac->stream->codec->channel_layout = AV_CH_LAYOUT_2_2; - break; - */ - case 5: - ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT0; - break; - case 6: - ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT1; - break; - case 8: - ac->stream->codec->channel_layout = AV_CH_LAYOUT_7POINT1; - break; - default: - mp_msg(MSGT_ENCODE, MSGL_ERR, - "ao-lavc: unknown channel layout; hoping for the best\n"); - break; - } - if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0) return -1; @@ -282,11 +259,12 @@ out_takefirst: if (ac->pcmhack) { ac->aframesize = 16384; // "enough" - ac->buffer_size = ac->aframesize * ac->pcmhack * ao->channels * 2 + 200; + ac->buffer_size = + ac->aframesize * ac->pcmhack * ao->channels.num * 2 + 200; } else { ac->aframesize = ac->stream->codec->frame_size; - ac->buffer_size = ac->aframesize * ac->sample_size * ao->channels * 2 + - 200; + ac->buffer_size = + ac->aframesize * ac->sample_size * ao->channels.num * 2 + 200; } if (ac->buffer_size < FF_MIN_BUFFER_SIZE) ac->buffer_size = FF_MIN_BUFFER_SIZE; @@ -304,10 +282,10 @@ out_takefirst: ac->offset_left = ac->offset; //fill_ao_data: - ao->outburst = ac->aframesize * ac->sample_size * ao->channels * - ac->framecount; + ao->outburst = + ac->aframesize * ac->sample_size * ao->channels.num * ac->framecount; ao->buffersize = ao->outburst * 2; - ao->bps = ao->channels * ao->samplerate * ac->sample_size; + ao->bps = ao->channels.num * ao->samplerate * ac->sample_size; ao->untimed = true; ao->priv = ac; @@ -348,7 +326,7 @@ static void uninit(struct ao *ao, bool cut_audio) // TRICK: append aframesize-1 samples to the end, then play() will // encode all it can size_t extralen = - (ac->aframesize - 1) * ao->channels * ac->sample_size; + (ac->aframesize - 1) * ao->channels.num * ac->sample_size; void *paddingbuf = talloc_size(ao, ao->buffer.len + extralen); memcpy(paddingbuf, ao->buffer.start, ao->buffer.len); fill_with_padding((char *) paddingbuf + ao->buffer.len, @@ -392,12 +370,6 @@ static int encode(struct ao *ao, double apts, void *data) int status, gotpacket; ac->aframecount++; - if (data && (ao->channels == 5 || ao->channels == 6 || ao->channels == 8)) { - reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, - AF_CHANNEL_LAYOUT_LAVC_DEFAULT, - ao->channels, - ac->aframesize * ao->channels, ac->sample_size); - } if (data) ectx->audio_pts_offset = realapts - apts; @@ -411,12 +383,18 @@ static int encode(struct ao *ao, double apts, void *data) frame->nb_samples = ac->aframesize; if (ac->planarize) { - void *data2 = talloc_size(ao, ac->aframesize * ao->channels * ac->sample_size); - reorder_to_planar(data2, data, ac->sample_size, ao->channels, ac->aframesize); + void *data2 = talloc_size(ao, ac->aframesize * ao->channels.num * + ac->sample_size); + reorder_to_planar(data2, data, ac->sample_size, ao->channels.num, + ac->aframesize); data = data2; } - if (avcodec_fill_audio_frame(frame, ao->channels, ac->stream->codec->sample_fmt, data, ac->aframesize * ao->channels * ac->sample_size, 1)) { + size_t audiolen = ac->aframesize * ao->channels.num * ac->sample_size; + if (avcodec_fill_audio_frame(frame, ao->channels.num, + ac->stream->codec->sample_fmt, data, + audiolen, 1)) + { mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error filling\n"); return -1; } @@ -523,7 +501,7 @@ static int play(struct ao *ao, void *data, int len, int flags) double pts = ao->pts; double outpts; - len /= ac->sample_size * ao->channels; + len /= ac->sample_size * ao->channels.num; if (!encode_lavc_start(ectx)) { mp_msg(MSGT_ENCODE, MSGL_WARN, @@ -593,7 +571,7 @@ static int play(struct ao *ao, void *data, int len, int flags) if (ac->offset_left <= -len) { // skip whole frame ac->offset_left += len; - return len * ac->sample_size * ao->channels; + return len * ac->sample_size * ao->channels.num; } else { // skip part of this frame, buffer/encode the rest bufpos -= ac->offset_left; @@ -604,11 +582,11 @@ static int play(struct ao *ao, void *data, int len, int flags) // make a temporary buffer, filled with zeroes at the start // (don't worry, only happens once) - paddingbuf = talloc_size(ac, ac->sample_size * ao->channels * + paddingbuf = talloc_size(ac, ac->sample_size * ao->channels.num * (ac->offset_left + len)); fill_with_padding(paddingbuf, ac->offset_left, ac->sample_size, ac->sample_padding); - data = (char *) paddingbuf + ac->sample_size * ao->channels * + data = (char *) paddingbuf + ac->sample_size * ao->channels.num * ac->offset_left; bufpos -= ac->offset_left; // yes, negative! ptsoffset += ac->offset_left; @@ -651,7 +629,7 @@ static int play(struct ao *ao, void *data, int len, int flags) while (len - bufpos >= ac->aframesize) { encode(ao, outpts + (bufpos + ptsoffset) / (double) ao->samplerate + encode_lavc_getoffset(ectx, ac->stream), - (char *) data + ac->sample_size * bufpos * ao->channels); + (char *) data + ac->sample_size * bufpos * ao->channels.num); bufpos += ac->aframesize; } @@ -667,11 +645,10 @@ static int play(struct ao *ao, void *data, int len, int flags) ectx->next_in_pts = nextpts; } - return bufpos * ac->sample_size * ao->channels; + return bufpos * ac->sample_size * ao->channels.num; } const struct ao_driver audio_out_lavc = { - .is_new = true, .encode = true, .info = &(const struct ao_info) { "audio encoding using libavcodec", diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c index 102f0a7013..53ec2a9a83 100644 --- a/audio/out/ao_null.c +++ b/audio/out/ao_null.c @@ -48,11 +48,17 @@ static int init(struct ao *ao, char *params) { struct priv *priv = talloc_zero(ao, struct priv); ao->priv = priv; + + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_any(&sel); + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + return -1; + int samplesize = af_fmt2bits(ao->format) / 8; - ao->outburst = 256 * ao->channels * samplesize; + ao->outburst = 256 * ao->channels.num * samplesize; // A "buffer" for about 0.2 seconds of audio ao->buffersize = (int)(ao->samplerate * 0.2 / 256 + 1) * ao->outburst; - ao->bps = ao->channels * ao->samplerate * samplesize; + ao->bps = ao->channels.num * ao->samplerate * samplesize; priv->last_time = GetTimer(); return 0; @@ -110,7 +116,6 @@ static float get_delay(struct ao *ao) } const struct ao_driver audio_out_null = { - .is_new = true, .info = &(const struct ao_info) { "Null audio output", "null", diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c index 157cf93ac4..08f8bc1978 100644 --- a/audio/out/ao_openal.c +++ b/audio/out/ao_openal.c @@ -53,7 +53,7 @@ static const ao_info_t info = LIBAO_EXTERN(openal) -#define MAX_CHANS 8 +#define MAX_CHANS MP_NUM_CHANNELS #define NUM_BUF 128 #define CHUNK_SIZE 512 static ALuint buffers[MAX_CHANS][NUM_BUF]; @@ -109,15 +109,29 @@ static void list_devices(void) { } } -static int init(int rate, int channels, int format, int flags) { +struct speaker { + int id; + float pos[3]; +}; + +static const struct speaker speaker_pos[] = { + {MP_SPEAKER_ID_FL, {-1, 0, 0.5}}, + {MP_SPEAKER_ID_FR, { 1, 0, 0.5}}, + {MP_SPEAKER_ID_FC, { 0, 0, 1}}, + {MP_SPEAKER_ID_LFE, { 0, 0, 0.1}}, + {MP_SPEAKER_ID_BL, {-1, 0, -1}}, + {MP_SPEAKER_ID_BR, { 1, 0, -1}}, + {MP_SPEAKER_ID_BC, { 0, 0, -1}}, + {MP_SPEAKER_ID_SL, {-1, 0, 0}}, + {MP_SPEAKER_ID_SR, { 1, 0, 0}}, + {-1}, +}; + +static int init(int rate, const struct mp_chmap *channels, int format, + int flags) +{ float position[3] = {0, 0, 0}; float direction[6] = {0, 0, 1, 0, -1, 0}; - float sppos[MAX_CHANS][3] = { - {-1, 0, 0.5}, {1, 0, 0.5}, - {-1, 0, -1}, {1, 0, -1}, - {0, 0, 1}, {0, 0, 0.1}, - {-1, 0, 0}, {1, 0, 0}, - }; ALCdevice *dev = NULL; ALCcontext *ctx = NULL; ALCint freq = 0; @@ -137,9 +151,22 @@ static int init(int rate, int channels, int format, int flags) { list_devices(); goto err_out; } - if (channels > MAX_CHANS) { - mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Invalid number of channels: %i\n", channels); + struct mp_chmap_sel sel = {0}; + for (i = 0; speaker_pos[i].id != -1; i++) + mp_chmap_sel_add_speaker(&sel, speaker_pos[i].id); + if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels)) goto err_out; + struct speaker speakers[MAX_CHANS]; + for (i = 0; i < ao_data.channels.num; i++) { + speakers[i].id = -1; + for (int n = 0; speaker_pos[n].id >= 0; n++) { + if (speaker_pos[n].id == ao_data.channels.speaker[i]) + speakers[i] = speaker_pos[n]; + } + if (speakers[i].id < 0) { + mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Unknown channel layout\n"); + goto err_out; + } } dev = alcOpenDevice(device); if (!dev) { @@ -150,25 +177,22 @@ static int init(int rate, int channels, int format, int flags) { alcMakeContextCurrent(ctx); alListenerfv(AL_POSITION, position); alListenerfv(AL_ORIENTATION, direction); - alGenSources(channels, sources); - for (i = 0; i < channels; i++) { + alGenSources(ao_data.channels.num, sources); + for (i = 0; i < ao_data.channels.num; i++) { cur_buf[i] = 0; unqueue_buf[i] = 0; alGenBuffers(NUM_BUF, buffers[i]); - alSourcefv(sources[i], AL_POSITION, sppos[i]); + alSourcefv(sources[i], AL_POSITION, speakers[i].pos); alSource3f(sources[i], AL_VELOCITY, 0, 0, 0); } - if (channels == 1) - alSource3f(sources[0], AL_POSITION, 0, 0, 1); - ao_data.channels = channels; alcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq); if (alcGetError(dev) == ALC_NO_ERROR && freq) rate = freq; ao_data.samplerate = rate; ao_data.format = AF_FORMAT_S16_NE; - ao_data.bps = channels * rate * 2; + ao_data.bps = ao_data.channels.num * rate * 2; ao_data.buffersize = CHUNK_SIZE * NUM_BUF; - ao_data.outburst = channels * CHUNK_SIZE; + ao_data.outburst = ao_data.channels.num * CHUNK_SIZE; tmpbuf = malloc(CHUNK_SIZE); free(device); return 1; @@ -200,7 +224,7 @@ static void uninit(int immed) { static void unqueue_buffers(void) { ALint p; int s; - for (s = 0; s < ao_data.channels; s++) { + for (s = 0; s < ao_data.channels.num; s++) { int till_wrap = NUM_BUF - unqueue_buf[s]; alGetSourcei(sources[s], AL_BUFFERS_PROCESSED, &p); if (p >= till_wrap) { @@ -219,7 +243,7 @@ static void unqueue_buffers(void) { * \brief stop playing and empty buffers (for seeking/pause) */ static void reset(void) { - alSourceStopv(ao_data.channels, sources); + alSourceStopv(ao_data.channels.num, sources); unqueue_buffers(); } @@ -227,14 +251,14 @@ static void reset(void) { * \brief stop playing, keep buffers (for pause) */ static void audio_pause(void) { - alSourcePausev(ao_data.channels, sources); + alSourcePausev(ao_data.channels.num, sources); } /** * \brief resume playing, after audio_pause() */ static void audio_resume(void) { - alSourcePlayv(ao_data.channels, sources); + alSourcePlayv(ao_data.channels.num, sources); } static int get_space(void) { @@ -243,7 +267,7 @@ static int get_space(void) { alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued); queued = NUM_BUF - queued - 3; if (queued < 0) return 0; - return queued * CHUNK_SIZE * ao_data.channels; + return queued * CHUNK_SIZE * ao_data.channels.num; } /** @@ -254,22 +278,22 @@ static int play(void *data, int len, int flags) { int i, j, k; int ch; int16_t *d = data; - len /= ao_data.channels * CHUNK_SIZE; + len /= ao_data.channels.num * CHUNK_SIZE; for (i = 0; i < len; i++) { - for (ch = 0; ch < ao_data.channels; ch++) { - for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels) + for (ch = 0; ch < ao_data.channels.num; ch++) { + for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels.num) tmpbuf[j] = d[k]; alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, tmpbuf, CHUNK_SIZE, ao_data.samplerate); alSourceQueueBuffers(sources[ch], 1, &buffers[ch][cur_buf[ch]]); cur_buf[ch] = (cur_buf[ch] + 1) % NUM_BUF; } - d += ao_data.channels * CHUNK_SIZE / 2; + d += ao_data.channels.num * CHUNK_SIZE / 2; } alGetSourcei(sources[0], AL_SOURCE_STATE, &state); if (state != AL_PLAYING) // checked here in case of an underrun - alSourcePlayv(ao_data.channels, sources); - return len * ao_data.channels * CHUNK_SIZE; + alSourcePlayv(ao_data.channels.num, sources); + return len * ao_data.channels.num * CHUNK_SIZE; } static float get_delay(void) { diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c index fa8eccdeea..4ff97b30b6 100644 --- a/audio/out/ao_oss.c +++ b/audio/out/ao_oss.c @@ -221,12 +221,12 @@ static int control(int cmd,void *arg){ // open & setup audio device // return: 1=success 0=fail -static int init(int rate,int channels,int format,int flags){ +static int init(int rate,const struct mp_chmap *channels,int format,int flags){ char *mixer_channels [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; int oss_format; char *mdev = mixer_device, *mchan = mixer_channel; - mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz %d chans %s\n",rate,channels, + mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz %d chans %s\n",rate,ao_data.channels.num, af_fmt2str_short(format)); if (ao_subdevice) { @@ -339,25 +339,31 @@ ac3_retry: mp_msg(MSGT_AO,MSGL_V,"audio_setup: sample format: %s (requested: %s)\n", af_fmt2str_short(ao_data.format), af_fmt2str_short(format)); - ao_data.channels = channels; if(!AF_FORMAT_IS_AC3(format)) { + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_alsa_def(&sel); + if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels)) + return 0; + int reqchannels = ao_data.channels.num; // We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it - if (ao_data.channels > 2) { - if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels) == -1 || - ao_data.channels != channels ) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", channels); + if (reqchannels > 2) { + int nchannels = reqchannels; + if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1 || + nchannels != reqchannels ) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", reqchannels); return 0; } } else { - int c = ao_data.channels-1; + int c = reqchannels-1; if (ioctl (audio_fd, SNDCTL_DSP_STEREO, &c) == -1) { - mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", ao_data.channels); + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", reqchannels); return 0; } - ao_data.channels=c+1; + if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, c + 1)) + return 0; } - mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels, channels); + mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels.num, reqchannels); // set rate ao_data.samplerate=rate; ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate); @@ -403,7 +409,7 @@ ac3_retry: #endif } - ao_data.bps=ao_data.channels; + ao_data.bps=ao_data.channels.num; switch (ao_data.format & AF_FORMAT_BITS_MASK) { case AF_FORMAT_8BIT: break; @@ -459,10 +465,10 @@ static void reset(void){ ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate); ioctl (audio_fd, SNDCTL_DSP_SETFMT, &oss_format); if(!AF_FORMAT_IS_AC3(ao_data.format)) { - if (ao_data.channels > 2) - ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels); + if (ao_data.channels.num > 2) + ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels.num); else { - int c = ao_data.channels-1; + int c = ao_data.channels.num-1; ioctl (audio_fd, SNDCTL_DSP_STEREO, &c); } ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate); diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c index 5b3cc94395..903e89bc8b 100644 --- a/audio/out/ao_pcm.c +++ b/audio/out/ao_pcm.c @@ -69,7 +69,7 @@ static void fput32le(uint32_t val, FILE *fp) static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length) { - bool use_waveex = ao->channels >= 5 && ao->channels <= 8; + bool use_waveex = true; uint16_t fmt = ao->format == AF_FORMAT_FLOAT_LE ? WAV_ID_FLOAT_PCM : WAV_ID_PCM; uint32_t fmt_chunk_size = use_waveex ? 40 : 16; @@ -86,30 +86,17 @@ static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length) fput32le(WAV_ID_FMT, fp); fput32le(fmt_chunk_size, fp); fput16le(use_waveex ? WAV_ID_FORMAT_EXTENSIBLE : fmt, fp); - fput16le(ao->channels, fp); + fput16le(ao->channels.num, fp); fput32le(ao->samplerate, fp); fput32le(ao->bps, fp); - fput16le(ao->channels * (bits / 8), fp); + fput16le(ao->channels.num * (bits / 8), fp); fput16le(bits, fp); if (use_waveex) { // Extension chunk fput16le(22, fp); fput16le(bits, fp); - switch (ao->channels) { - case 5: - fput32le(0x0607, fp); // L R C Lb Rb - break; - case 6: - fput32le(0x060f, fp); // L R C Lb Rb LFE - break; - case 7: - fput32le(0x0727, fp); // L R C Cb Ls Rs LFE - break; - case 8: - fput32le(0x063f, fp); // L R C Lb Rb Ls Rs LFE - break; - } + fput32le(mp_chmap_to_waveext(&ao->channels), fp); // 2 bytes format + 14 bytes guid fput32le(fmt, fp); fput32le(0x00100000, fp); @@ -159,14 +146,19 @@ static int init(struct ao *ao, char *params) } } + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_waveext(&sel); + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + return -1; + ao->outburst = 65536; - ao->bps = ao->channels * ao->samplerate * (af_fmt2bits(ao->format) / 8); + ao->bps = ao->channels.num * ao->samplerate * (af_fmt2bits(ao->format) / 8); mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] File: %s (%s)\n" "PCM: Samplerate: %d Hz Channels: %d Format: %s\n", priv->outputfilename, priv->waveheader ? "WAVE" : "RAW PCM", ao->samplerate, - ao->channels, af_fmt2str_short(ao->format)); + ao->channels.num, af_fmt2str_short(ao->format)); mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] Info: Faster dumping is achieved with -no-video\n" "[AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).\n"); @@ -222,20 +214,12 @@ static int play(struct ao *ao, void *data, int len, int flags) { struct priv *priv = ao->priv; - if (ao->channels == 5 || ao->channels == 6 || ao->channels == 8) { - int frame_size = af_fmt2bits(ao->format) / 8; - len -= len % (frame_size * ao->channels); - reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, - AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT, - ao->channels, len / frame_size, frame_size); - } fwrite(data, len, 1, priv->fp); priv->data_length += len; return len; } const struct ao_driver audio_out_pcm = { - .is_new = true, .info = &(const struct ao_info) { "RAW PCM/WAVE file writer audio output", "pcm", diff --git a/audio/out/ao_portaudio.c b/audio/out/ao_portaudio.c index b0744e8f8a..f84447bdb4 100644 --- a/audio/out/ao_portaudio.c +++ b/audio/out/ao_portaudio.c @@ -273,9 +273,15 @@ static int init(struct ao *ao, char *params) if (pa_device == paNoDevice) goto error_exit; + // The actual channel order probably depends on the platform. + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_waveext_def(&sel); + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + goto error_exit; + PaStreamParameters sp = { .device = pa_device, - .channelCount = ao->channels, + .channelCount = ao->channels.num, .suggestedLatency = Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency, }; @@ -298,7 +304,7 @@ static int init(struct ao *ao, char *params) ao->format = fmt->mp_format; sp.sampleFormat = fmt->pa_format; - priv->framelen = ao->channels * (af_fmt2bits(ao->format) / 8); + priv->framelen = ao->channels.num * (af_fmt2bits(ao->format) / 8); ao->bps = ao->samplerate * priv->framelen; if (!check_pa_ret(Pa_IsFormatSupported(NULL, &sp, ao->samplerate))) @@ -413,7 +419,6 @@ static void resume(struct ao *ao) } const struct ao_driver audio_out_portaudio = { - .is_new = true, .info = &(const struct ao_info) { "PortAudio", "portaudio", diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c index 314ef778bd..15a2b8b0a1 100644 --- a/audio/out/ao_pulse.c +++ b/audio/out/ao_pulse.c @@ -143,6 +143,59 @@ static const struct format_map { {AF_FORMAT_UNKNOWN, 0} }; +static const int speaker_map[][2] = { + {PA_CHANNEL_POSITION_MONO, MP_SPEAKER_ID_FC}, + {PA_CHANNEL_POSITION_FRONT_LEFT, MP_SPEAKER_ID_FL}, + {PA_CHANNEL_POSITION_FRONT_RIGHT, MP_SPEAKER_ID_FR}, + {PA_CHANNEL_POSITION_FRONT_CENTER, MP_SPEAKER_ID_FC}, + {PA_CHANNEL_POSITION_REAR_CENTER, MP_SPEAKER_ID_BC}, + {PA_CHANNEL_POSITION_REAR_LEFT, MP_SPEAKER_ID_BL}, + {PA_CHANNEL_POSITION_REAR_RIGHT, MP_SPEAKER_ID_BR}, + {PA_CHANNEL_POSITION_LFE, MP_SPEAKER_ID_LFE}, + {PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, MP_SPEAKER_ID_FLC}, + {PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, MP_SPEAKER_ID_FRC}, + {PA_CHANNEL_POSITION_SIDE_LEFT, MP_SPEAKER_ID_SL}, + {PA_CHANNEL_POSITION_SIDE_RIGHT, MP_SPEAKER_ID_SR}, + {PA_CHANNEL_POSITION_TOP_CENTER, MP_SPEAKER_ID_TC}, + {PA_CHANNEL_POSITION_TOP_FRONT_LEFT, MP_SPEAKER_ID_TFL}, + {PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, MP_SPEAKER_ID_TFR}, + {PA_CHANNEL_POSITION_TOP_FRONT_CENTER, MP_SPEAKER_ID_TFC}, + {PA_CHANNEL_POSITION_TOP_REAR_LEFT, MP_SPEAKER_ID_TBL}, + {PA_CHANNEL_POSITION_TOP_REAR_RIGHT, MP_SPEAKER_ID_TBR}, + {PA_CHANNEL_POSITION_TOP_REAR_CENTER, MP_SPEAKER_ID_TBC}, + {PA_CHANNEL_POSITION_INVALID, -1} +}; + +static bool chmap_pa_from_mp(pa_channel_map *dst, struct mp_chmap *src) +{ + if (src->num > PA_CHANNELS_MAX) + return false; + dst->channels = src->num; + for (int n = 0; n < src->num; n++) { + int mp_speaker = src->speaker[n]; + int pa_speaker = PA_CHANNEL_POSITION_INVALID; + for (int i = 0; speaker_map[i][1] != -1; i++) { + if (speaker_map[i][1] == mp_speaker) { + pa_speaker = speaker_map[i][0]; + break; + } + } + if (pa_speaker == PA_CHANNEL_POSITION_INVALID) + return false; + dst->map[n] = pa_speaker; + } + return true; +} + +static bool select_chmap(struct ao *ao, pa_channel_map *dst) +{ + struct mp_chmap_sel sel = {0}; + for (int n = 0; speaker_map[n][1] != -1; n++) + mp_chmap_sel_add_speaker(&sel, speaker_map[n][1]); + return ao_chmap_sel_adjust(ao, &sel, &ao->channels) && + chmap_pa_from_mp(dst, &ao->channels); +} + static void uninit(struct ao *ao, bool cut_audio) { struct priv *priv = ao->priv; @@ -209,30 +262,6 @@ static int init(struct ao *ao, char *params) priv->broken_pause = true; } - ss.channels = ao->channels; - ss.rate = ao->samplerate; - - const struct format_map *fmt_map = format_maps; - while (fmt_map->mp_format != ao->format) { - if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { - mp_msg(MSGT_AO, MSGL_V, - "AO: [pulse] Unsupported format, using default\n"); - fmt_map = format_maps; - break; - } - fmt_map++; - } - ao->format = fmt_map->mp_format; - ss.format = fmt_map->pa_format; - - if (!pa_sample_spec_valid(&ss)) { - mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n"); - goto fail; - } - - pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); - ao->bps = pa_bytes_per_second(&ss); - if (!(priv->mainloop = pa_threaded_mainloop_new())) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n"); goto fail; @@ -260,6 +289,32 @@ static int init(struct ao *ao, char *params) if (pa_context_get_state(priv->context) != PA_CONTEXT_READY) goto unlock_and_fail; + ss.channels = ao->channels.num; + ss.rate = ao->samplerate; + + const struct format_map *fmt_map = format_maps; + while (fmt_map->mp_format != ao->format) { + if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { + mp_msg(MSGT_AO, MSGL_V, + "AO: [pulse] Unsupported format, using default\n"); + fmt_map = format_maps; + break; + } + fmt_map++; + } + ao->format = fmt_map->mp_format; + ss.format = fmt_map->pa_format; + + if (!pa_sample_spec_valid(&ss)) { + mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n"); + goto fail; + } + + if (!select_chmap(ao, &map)) + goto fail; + + ao->bps = pa_bytes_per_second(&ss); + if (!(priv->stream = pa_stream_new(priv->context, "audio stream", &ss, &map))) goto unlock_and_fail; @@ -495,7 +550,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) const ao_control_vol_t *vol = arg; struct pa_cvolume volume; - pa_cvolume_reset(&volume, ao->channels); + pa_cvolume_reset(&volume, ao->channels.num); if (volume.channels != 2) pa_cvolume_set(&volume, volume.channels, VOL_MP2PA(vol->left)); else { @@ -533,7 +588,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) } const struct ao_driver audio_out_pulse = { - .is_new = true, .info = &(const struct ao_info) { "PulseAudio audio output", "pulse", diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c index 7722bf19d2..78ea2c63f5 100644 --- a/audio/out/ao_rsound.c +++ b/audio/out/ao_rsound.c @@ -121,8 +121,16 @@ static int init(struct ao *ao, char *params) free(port); } + // Actual channel layout unknown. + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_waveext_def(&sel); + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { + rsd_free(priv->rd); + return -1; + } + rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate); - rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels); + rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels.num); int rsd_format = set_format(ao); rsd_set_param(priv->rd, RSD_FORMAT, &rsd_format); @@ -132,7 +140,7 @@ static int init(struct ao *ao, char *params) return -1; } - ao->bps = ao->channels * ao->samplerate * af_fmt2bits(ao->format) / 8; + ao->bps = ao->channels.num * ao->samplerate * af_fmt2bits(ao->format) / 8; return 0; } @@ -189,7 +197,6 @@ static float get_delay(struct ao *ao) } const struct ao_driver audio_out_rsound = { - .is_new = true, .info = &(const struct ao_info) { .name = "RSound output driver", .short_name = "rsound", diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c index 7cfb1ae1e2..6678cd3bd3 100644 --- a/audio/out/ao_sdl.c +++ b/audio/out/ao_sdl.c @@ -160,6 +160,13 @@ static int init(struct ao *ao, char *params) return -1; } + struct mp_chmap_sel sel = {0}; + mp_chmap_sel_add_waveext_def(&sel); + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { + uninit(ao, true); + return -1; + } + SDL_AudioSpec desired, obtained; int bytes = 0; @@ -185,7 +192,7 @@ static int init(struct ao *ao, char *params) #endif } desired.freq = ao->samplerate; - desired.channels = ao->channels; + desired.channels = ao->channels.num; desired.samples = FFMIN(32768, ceil_power_of_two(ao->samplerate * buflen)); desired.callback = audio_callback; desired.userdata = ao; @@ -236,9 +243,13 @@ static int init(struct ao *ao, char *params) return -1; } + if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) { + uninit(ao, true); + return -1; + } + ao->samplerate = obtained.freq; - ao->channels = obtained.channels; - ao->bps = ao->channels * ao->samplerate * bytes; + ao->bps = ao->channels.num * ao->samplerate * bytes; ao->buffersize = obtained.size * bufcnt; ao->outburst = obtained.size; priv->buffer = av_fifo_alloc(ao->buffersize); @@ -362,7 +373,6 @@ static float get_delay(struct ao *ao) } const struct ao_driver audio_out_sdl = { - .is_new = true, .info = &(const struct ao_info) { "SDL Audio", "sdl", diff --git a/audio/out/audio_out_internal.h b/audio/out/audio_out_internal.h index f3e92dff66..7b863cfd21 100644 --- a/audio/out/audio_out_internal.h +++ b/audio/out/audio_out_internal.h @@ -20,11 +20,12 @@ #define MPLAYER_AUDIO_OUT_INTERNAL_H #include "core/options.h" +#include "ao.h" // prototypes: //static ao_info_t info; static int control(int cmd, void *arg); -static int init(int rate,int channels,int format,int flags); +static int init(int rate,const struct mp_chmap *channels,int format,int flags); static void uninit(int immed); static void reset(void); static int get_space(void); |