Merge commit 'origin/master' into master-tx

This commit is contained in:
Lennart Poettering 2009-06-17 23:43:50 +02:00
commit 53b87033aa
87 changed files with 7899 additions and 1916 deletions

View file

@ -181,3 +181,10 @@ new messages:
PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED
PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED
### v16, implemented by >= 0.9.15
new messages:
PA_COMMAND_SET_SINK_PORT
PA_COMMAND_SET_SOURCE_PORT

View file

@ -41,7 +41,7 @@ AC_SUBST(PA_MAJORMINORMICRO, pa_major.pa_minor.pa_micro)
AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/]) AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
AC_SUBST(PA_API_VERSION, 12) AC_SUBST(PA_API_VERSION, 12)
AC_SUBST(PA_PROTOCOL_VERSION, 15) AC_SUBST(PA_PROTOCOL_VERSION, 16)
# The stable ABI for client applications, for the version info x:y:z # The stable ABI for client applications, for the version info x:y:z
# always will hold y=z # always will hold y=z

View file

@ -29,6 +29,8 @@ pulsecoreincludedir=$(includedir)/pulsecore
pulseconfdir=$(sysconfdir)/pulse pulseconfdir=$(sysconfdir)/pulse
pulselibexecdir=$(libexecdir)/pulse pulselibexecdir=$(libexecdir)/pulse
xdgautostartdir=$(sysconfdir)/xdg/autostart xdgautostartdir=$(sysconfdir)/xdg/autostart
alsaprofilesetsdir=$(datadir)/alsa-mixer/profile-sets
alsapathsdir=$(datadir)/alsa-mixer/paths
################################### ###################################
# Defines # # Defines #
@ -73,7 +75,9 @@ AM_CFLAGS = \
-DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\" \ -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\" \
-DAO_REQUIRE_CAS \ -DAO_REQUIRE_CAS \
-DPULSE_LOCALEDIR=\"$(pulselocaledir)\" \ -DPULSE_LOCALEDIR=\"$(pulselocaledir)\" \
-DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" -DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" \
-DPA_ALSA_PATHS_DIR=\"$(alsapathsdir)\" \
-DPA_ALSA_PROFILE_SETS_DIR=\"$(alsaprofilesetsdir)\"
AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS) AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS) AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
@ -109,7 +113,23 @@ EXTRA_DIST = \
modules/module-defs.h.m4 \ modules/module-defs.h.m4 \
daemon/pulseaudio.desktop.in \ daemon/pulseaudio.desktop.in \
map-file \ map-file \
daemon/org.pulseaudio.policy.in daemon/org.pulseaudio.policy.in \
modules/alsa/mixer/profile-sets/default.conf \
modules/alsa/mixer/paths/analog-input-aux.conf \
modules/alsa/mixer/paths/analog-input.conf \
modules/alsa/mixer/paths/analog-input.conf.common \
modules/alsa/mixer/paths/analog-input-fm.conf \
modules/alsa/mixer/paths/analog-input-linein.conf \
modules/alsa/mixer/paths/analog-input-mic.conf \
modules/alsa/mixer/paths/analog-input-mic.conf.common \
modules/alsa/mixer/paths/analog-input-mic-line.conf \
modules/alsa/mixer/paths/analog-input-tvtuner.conf \
modules/alsa/mixer/paths/analog-input-video.conf \
modules/alsa/mixer/paths/analog-output.conf \
modules/alsa/mixer/paths/analog-output.conf.common \
modules/alsa/mixer/paths/analog-output-headphones.conf \
modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf \
modules/alsa/mixer/paths/analog-output-mono.conf
pulseconf_DATA = \ pulseconf_DATA = \
default.pa \ default.pa \
@ -1023,6 +1043,27 @@ modlibexec_LTLIBRARIES += \
module-alsa-sink.la \ module-alsa-sink.la \
module-alsa-source.la \ module-alsa-source.la \
module-alsa-card.la module-alsa-card.la
alsaprofilesets_DATA = \
modules/alsa/mixer/profile-sets/default.conf
alsapaths_DATA = \
modules/alsa/mixer/paths/analog-input-aux.conf \
modules/alsa/mixer/paths/analog-input.conf \
modules/alsa/mixer/paths/analog-input.conf.common \
modules/alsa/mixer/paths/analog-input-fm.conf \
modules/alsa/mixer/paths/analog-input-linein.conf \
modules/alsa/mixer/paths/analog-input-mic.conf \
modules/alsa/mixer/paths/analog-input-mic.conf.common \
modules/alsa/mixer/paths/analog-input-mic-line.conf \
modules/alsa/mixer/paths/analog-input-tvtuner.conf \
modules/alsa/mixer/paths/analog-input-video.conf \
modules/alsa/mixer/paths/analog-output.conf \
modules/alsa/mixer/paths/analog-output.conf.common \
modules/alsa/mixer/paths/analog-output-headphones.conf \
modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf \
modules/alsa/mixer/paths/analog-output-mono.conf
endif endif
if HAVE_SOLARIS if HAVE_SOLARIS
@ -1164,8 +1205,8 @@ SYMDEF_FILES = \
modules/bluetooth/module-bluetooth-proximity-symdef.h \ modules/bluetooth/module-bluetooth-proximity-symdef.h \
modules/bluetooth/module-bluetooth-discover-symdef.h \ modules/bluetooth/module-bluetooth-discover-symdef.h \
modules/bluetooth/module-bluetooth-device-symdef.h \ modules/bluetooth/module-bluetooth-device-symdef.h \
modules/module-raop-sink-symdef.h \ modules/raop/module-raop-sink-symdef.h \
modules/module-raop-discover-symdef.h \ modules/raop/module-raop-discover-symdef.h \
modules/gconf/module-gconf-symdef.h \ modules/gconf/module-gconf-symdef.h \
modules/module-position-event-sounds-symdef.h \ modules/module-position-event-sounds-symdef.h \
modules/module-augment-properties-symdef.h \ modules/module-augment-properties-symdef.h \
@ -1346,7 +1387,7 @@ module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore-@PA_MAJORMINORMI
# ALSA # ALSA
libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h
libalsa_util_la_LDFLAGS = -avoid-version libalsa_util_la_LDFLAGS = -avoid-version
libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
@ -1585,11 +1626,11 @@ module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_M
module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
# Apple Airtunes/RAOP # Apple Airtunes/RAOP
module_raop_sink_la_SOURCES = modules/module-raop-sink.c module_raop_sink_la_SOURCES = modules/raop/module-raop-sink.c
module_raop_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_raop_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libraop.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libraop.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_raop_discover_la_SOURCES = modules/module-raop-discover.c module_raop_discover_la_SOURCES = modules/raop/module-raop-discover.c
module_raop_discover_la_LDFLAGS = $(MODULE_LDFLAGS) module_raop_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
module_raop_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_raop_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_raop_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) module_raop_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)

View file

@ -930,6 +930,11 @@ int main(int argc, char *argv[]) {
pa_log_info(_("Running in system mode: %s"), pa_yes_no(pa_in_system_mode())); pa_log_info(_("Running in system mode: %s"), pa_yes_no(pa_in_system_mode()));
if (pa_in_system_mode())
pa_log_warn(_("OK, so you are running PA in system mode. Please note that you most likely shouldn't be doing that.\n"
"If you do it nonetheless then it's your own fault if things don't work as expected.\n"
"Please read http://pulseaudio.org/wiki/WhatIsWrongWithSystemMode for an explanation why system mode is usually a bad idea."));
if (conf->use_pid_file) { if (conf->use_pid_file) {
int z; int z;

View file

@ -28,6 +28,7 @@ pa_channel_map_superset;
pa_channel_map_to_name; pa_channel_map_to_name;
pa_channel_map_to_pretty_name; pa_channel_map_to_pretty_name;
pa_channel_map_valid; pa_channel_map_valid;
pa_channel_position_from_string;
pa_channel_position_to_pretty_string; pa_channel_position_to_pretty_string;
pa_channel_position_to_string; pa_channel_position_to_string;
pa_context_add_autoload; pa_context_add_autoload;
@ -95,10 +96,14 @@ pa_context_set_sink_input_mute;
pa_context_set_sink_input_volume; pa_context_set_sink_input_volume;
pa_context_set_sink_mute_by_index; pa_context_set_sink_mute_by_index;
pa_context_set_sink_mute_by_name; pa_context_set_sink_mute_by_name;
pa_context_set_sink_port_by_index;
pa_context_set_sink_port_by_name;
pa_context_set_sink_volume_by_index; pa_context_set_sink_volume_by_index;
pa_context_set_sink_volume_by_name; pa_context_set_sink_volume_by_name;
pa_context_set_source_mute_by_index; pa_context_set_source_mute_by_index;
pa_context_set_source_mute_by_name; pa_context_set_source_mute_by_name;
pa_context_set_source_port_by_index;
pa_context_set_source_port_by_name;
pa_context_set_source_volume_by_index; pa_context_set_source_volume_by_index;
pa_context_set_source_volume_by_name; pa_context_set_source_volume_by_name;
pa_context_set_state_callback; pa_context_set_state_callback;
@ -264,7 +269,9 @@ pa_stream_writable_size;
pa_stream_write; pa_stream_write;
pa_strerror; pa_strerror;
pa_sw_cvolume_divide; pa_sw_cvolume_divide;
pa_sw_cvolume_divide_scalar;
pa_sw_cvolume_multiply; pa_sw_cvolume_multiply;
pa_sw_cvolume_multiply_scalar;
pa_sw_cvolume_snprint_dB; pa_sw_cvolume_snprint_dB;
pa_sw_volume_divide; pa_sw_volume_divide;
pa_sw_volume_from_dB; pa_sw_volume_from_dB;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,292 @@
#ifndef fooalsamixerhfoo
#define fooalsamixerhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <asoundlib.h>
#include <pulse/sample.h>
#include <pulse/volume.h>
#include <pulse/mainloop-api.h>
#include <pulse/channelmap.h>
#include <pulse/proplist.h>
#include <pulse/volume.h>
#include <pulsecore/llist.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/core.h>
#include <pulsecore/log.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
typedef struct pa_alsa_setting pa_alsa_setting;
typedef struct pa_alsa_option pa_alsa_option;
typedef struct pa_alsa_element pa_alsa_element;
typedef struct pa_alsa_path pa_alsa_path;
typedef struct pa_alsa_path_set pa_alsa_path_set;
typedef struct pa_alsa_mapping pa_alsa_mapping;
typedef struct pa_alsa_profile pa_alsa_profile;
typedef struct pa_alsa_profile_set pa_alsa_profile_set;
typedef struct pa_alsa_port_data pa_alsa_port_data;
#include "alsa-util.h"
typedef enum pa_alsa_switch_use {
PA_ALSA_SWITCH_IGNORE,
PA_ALSA_SWITCH_MUTE, /* make this switch follow mute status */
PA_ALSA_SWITCH_OFF, /* set this switch to 'off' unconditionally */
PA_ALSA_SWITCH_ON, /* set this switch to 'on' unconditionally */
PA_ALSA_SWITCH_SELECT /* allow the user to select switch status through a setting */
} pa_alsa_switch_use_t;
typedef enum pa_alsa_volume_use {
PA_ALSA_VOLUME_IGNORE,
PA_ALSA_VOLUME_MERGE, /* merge this volume slider into the global volume slider */
PA_ALSA_VOLUME_OFF, /* set this volume to minimal unconditionally */
PA_ALSA_VOLUME_ZERO /* set this volume to 0dB unconditionally */
} pa_alsa_volume_use_t;
typedef enum pa_alsa_enumeration_use {
PA_ALSA_ENUMERATION_IGNORE,
PA_ALSA_ENUMERATION_SELECT
} pa_alsa_enumeration_use_t;
typedef enum pa_alsa_required {
PA_ALSA_REQUIRED_IGNORE,
PA_ALSA_REQUIRED_SWITCH,
PA_ALSA_REQUIRED_VOLUME,
PA_ALSA_REQUIRED_ENUMERATION,
PA_ALSA_REQUIRED_ANY
} pa_alsa_required_t;
typedef enum pa_alsa_direction {
PA_ALSA_DIRECTION_ANY,
PA_ALSA_DIRECTION_OUTPUT,
PA_ALSA_DIRECTION_INPUT
} pa_alsa_direction_t;
/* A setting combines a couple of options into a single entity that
* may be selected. Only one setting can be active at the same
* time. */
struct pa_alsa_setting {
pa_alsa_path *path;
PA_LLIST_FIELDS(pa_alsa_setting);
pa_idxset *options;
char *name;
char *description;
unsigned priority;
};
/* An option belongs to an element and refers to one enumeration item
* of the element is an enumeration item, or a switch status if the
* element is a switch item. */
struct pa_alsa_option {
pa_alsa_element *element;
PA_LLIST_FIELDS(pa_alsa_option);
char *alsa_name;
int alsa_idx;
char *name;
char *description;
unsigned priority;
};
/* And element wraps one specific ALSA element. A series of elements *
make up a path (see below). If the element is an enumeration or switch
* element it may includes a list of options. */
struct pa_alsa_element {
pa_alsa_path *path;
PA_LLIST_FIELDS(pa_alsa_element);
char *alsa_name;
pa_alsa_direction_t direction;
pa_alsa_switch_use_t switch_use;
pa_alsa_volume_use_t volume_use;
pa_alsa_enumeration_use_t enumeration_use;
pa_alsa_required_t required;
pa_alsa_required_t required_absent;
pa_bool_t override_map:1;
pa_bool_t direction_try_other:1;
pa_bool_t has_dB:1;
long min_volume, max_volume;
double min_dB, max_dB;
pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
unsigned n_channels;
pa_channel_position_mask_t merged_mask;
PA_LLIST_HEAD(pa_alsa_option, options);
};
/* A path wraps a series of elements into a single entity which can be
* used to control it as if it had a single volume slider, a single
* mute switch and a single list of selectable options. */
struct pa_alsa_path {
pa_alsa_path_set *path_set;
PA_LLIST_FIELDS(pa_alsa_path);
pa_alsa_direction_t direction;
char *name;
char *description;
unsigned priority;
pa_bool_t probed:1;
pa_bool_t supported:1;
pa_bool_t has_mute:1;
pa_bool_t has_volume:1;
pa_bool_t has_dB:1;
long min_volume, max_volume;
double min_dB, max_dB;
/* This is used during parsing only, as a shortcut so that we
* don't have to iterate the list all the time */
pa_alsa_element *last_element;
pa_alsa_option *last_option;
pa_alsa_setting *last_setting;
PA_LLIST_HEAD(pa_alsa_element, elements);
PA_LLIST_HEAD(pa_alsa_setting, settings);
};
/* A path set is simply a set of paths that are applicable to a
* device */
struct pa_alsa_path_set {
PA_LLIST_HEAD(pa_alsa_path, paths);
pa_alsa_direction_t direction;
pa_bool_t probed:1;
/* This is used during parsing only, as a shortcut so that we
* don't have to iterate the list all the time */
pa_alsa_path *last_path;
};
int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m);
void pa_alsa_setting_dump(pa_alsa_setting *s);
void pa_alsa_option_dump(pa_alsa_option *o);
void pa_alsa_element_dump(pa_alsa_element *e);
pa_alsa_path *pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction);
pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction);
int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB);
void pa_alsa_path_dump(pa_alsa_path *p);
int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t *muted);
int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t muted);
int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m);
void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
void pa_alsa_path_free(pa_alsa_path *p);
pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction);
void pa_alsa_path_set_probe(pa_alsa_path_set *s, snd_mixer_t *m, pa_bool_t ignore_dB);
void pa_alsa_path_set_dump(pa_alsa_path_set *s);
void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
void pa_alsa_path_set_free(pa_alsa_path_set *s);
struct pa_alsa_mapping {
pa_alsa_profile_set *profile_set;
char *name;
char *description;
unsigned priority;
pa_alsa_direction_t direction;
pa_channel_map channel_map;
char **device_strings;
char **input_path_names;
char **output_path_names;
char **input_element; /* list of fallbacks */
char **output_element;
unsigned supported;
/* Temporarily used during probing */
snd_pcm_t *input_pcm;
snd_pcm_t *output_pcm;
pa_sink *sink;
pa_source *source;
};
struct pa_alsa_profile {
pa_alsa_profile_set *profile_set;
char *name;
char *description;
unsigned priority;
pa_bool_t supported:1;
char **input_mapping_names;
char **output_mapping_names;
pa_idxset *input_mappings;
pa_idxset *output_mappings;
};
struct pa_alsa_profile_set {
pa_hashmap *mappings;
pa_hashmap *profiles;
pa_bool_t auto_profiles;
pa_bool_t probed:1;
};
void pa_alsa_mapping_dump(pa_alsa_mapping *m);
void pa_alsa_profile_dump(pa_alsa_profile *p);
pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus);
void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss);
void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device);
pa_alsa_fdlist *pa_alsa_fdlist_new(void);
void pa_alsa_fdlist_free(pa_alsa_fdlist *fdl);
int pa_alsa_fdlist_set_mixer(pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
/* Data structure for inclusion in pa_device_port for alsa
* sinks/sources. This contains nothing that needs to be freed
* individually */
struct pa_alsa_port_data {
pa_alsa_path *path;
pa_alsa_setting *setting;
};
void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps);
#endif

View file

@ -80,11 +80,9 @@ struct userdata {
pa_alsa_fdlist *mixer_fdl; pa_alsa_fdlist *mixer_fdl;
snd_mixer_t *mixer_handle; snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem; pa_alsa_path_set *mixer_path_set;
long hw_volume_max, hw_volume_min; pa_alsa_path *mixer_path;
long hw_dB_max, hw_dB_min;
pa_bool_t hw_dB_supported:1;
pa_bool_t mixer_seperate_channels:1;
pa_cvolume hardware_volume; pa_cvolume hardware_volume;
size_t size_t
@ -100,7 +98,8 @@ struct userdata {
unsigned nfragments; unsigned nfragments;
pa_memchunk memchunk; pa_memchunk memchunk;
char *device_name; char *device_name; /* name of the PCM device */
char *control_device; /* name of the control device */
pa_bool_t use_mmap:1, use_tsched:1; pa_bool_t use_mmap:1, use_tsched:1;
@ -991,191 +990,58 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
return 0; return 0;
} }
static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) {
return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) /
(double) (u->hw_volume_max - u->hw_volume_min));
}
static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
long alsa_vol;
alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min))
/ PA_VOLUME_NORM) + u->hw_volume_min;
return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
}
static void sink_get_volume_cb(pa_sink *s) { static void sink_get_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r; pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX]; char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if (u->mixer_seperate_channels) { if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
return;
r.channels = s->sample_spec.channels; /* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
for (i = 0; i < s->sample_spec.channels; i++) {
long alsa_vol;
if (u->hw_dB_supported) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
r.values[i] = from_alsa_volume(u, alsa_vol);
}
}
} else {
long alsa_vol;
if (u->hw_dB_supported) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
}
}
pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
if (!pa_cvolume_equal(&u->hardware_volume, &r)) { if (pa_cvolume_equal(&u->hardware_volume, &r))
return;
s->virtual_volume = u->hardware_volume = r; s->virtual_volume = u->hardware_volume = r;
if (u->hw_dB_supported) { if (u->mixer_path->has_dB) {
pa_cvolume reset; pa_cvolume reset;
/* Hmm, so the hardware volume changed, let's reset our software volume */ /* Hmm, so the hardware volume changed, let's reset our software volume */
pa_cvolume_reset(&reset, s->sample_spec.channels); pa_cvolume_reset(&reset, s->sample_spec.channels);
pa_sink_set_soft_volume(s, &reset); pa_sink_set_soft_volume(s, &reset);
}
} }
return;
fail:
pa_log_error("Unable to read volume: %s", pa_alsa_strerror(err));
} }
static void sink_set_volume_cb(pa_sink *s) { static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r; pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if (u->mixer_seperate_channels) { /* Shift up by the base volume */
pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume);
r.channels = s->sample_spec.channels; if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
return;
for (i = 0; i < s->sample_spec.channels; i++) { /* Shift down by the base volume, so that 0dB becomes maximum volume */
long alsa_vol; pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
pa_volume_t vol;
vol = s->virtual_volume.values[i];
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
alsa_vol = to_alsa_volume(u, vol);
if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
r.values[i] = from_alsa_volume(u, alsa_vol);
}
}
} else {
pa_volume_t vol;
long alsa_vol;
vol = pa_cvolume_max(&s->virtual_volume);
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_playback_dB_all(u->mixer_elem, alsa_vol, 1)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
if ((err = snd_mixer_selem_set_playback_volume_all(u->mixer_elem, alsa_vol)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
}
}
u->hardware_volume = r; u->hardware_volume = r;
if (u->hw_dB_supported) { if (u->mixer_path->has_dB) {
char t[PA_CVOLUME_SNPRINT_MAX];
/* Match exactly what the user requested by software */ /* Match exactly what the user requested by software */
pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
@ -1184,45 +1050,75 @@ static void sink_set_volume_cb(pa_sink *s) {
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
} else } else {
pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
/* We can't match exactly what the user requested, hence let's /* We can't match exactly what the user requested, hence let's
* at least tell the user about it */ * at least tell the user about it */
s->virtual_volume = r; s->virtual_volume = r;
}
return;
fail:
pa_log_error("Unable to set volume: %s", pa_alsa_strerror(err));
} }
static void sink_get_mute_cb(pa_sink *s) { static void sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err, sw = 0; pa_bool_t b;
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) { if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
pa_log_error("Unable to get switch: %s", pa_alsa_strerror(err));
return; return;
}
s->muted = !sw; s->muted = b;
} }
static void sink_set_mute_cb(pa_sink *s) { static void sink_set_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err;
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) { pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted);
pa_log_error("Unable to set switch: %s", pa_alsa_strerror(err)); }
return;
static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
struct userdata *u = s->userdata;
pa_alsa_port_data *data;
pa_assert(u);
pa_assert(p);
pa_assert(u->mixer_handle);
data = PA_DEVICE_PORT_DATA(p);
pa_assert_se(u->mixer_path = data->path);
pa_alsa_path_select(u->mixer_path, u->mixer_handle);
if (u->mixer_path->has_volume && u->mixer_path->has_dB) {
s->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB);
s->n_volume_steps = PA_VOLUME_NORM+1;
if (u->mixer_path->max_dB > 0.0)
pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(s->base_volume));
else
pa_log_info("No particular base volume set, fixing to 0 dB");
} else {
s->base_volume = PA_VOLUME_NORM;
s->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1;
} }
if (data->setting)
pa_alsa_setting_select(data->setting, u->mixer_handle);
if (s->set_mute)
s->set_mute(s);
if (s->set_volume)
s->set_volume(s);
return 0;
} }
static void sink_update_requested_latency_cb(pa_sink *s) { static void sink_update_requested_latency_cb(pa_sink *s) {
@ -1465,77 +1361,127 @@ static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *de
pa_xfree(t); pa_xfree(t);
} }
static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, pa_bool_t ignore_dB) {
if (!mapping && !element)
return;
if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) {
pa_log_info("Failed to find a working mixer device.");
return;
}
if (element) {
if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_OUTPUT)))
goto fail;
if (pa_alsa_path_probe(u->mixer_path, u->mixer_handle, ignore_dB) < 0)
goto fail;
pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
pa_alsa_path_dump(u->mixer_path);
} else {
if (!(u->mixer_path_set = pa_alsa_path_set_new(mapping, PA_ALSA_DIRECTION_OUTPUT)))
goto fail;
pa_alsa_path_set_probe(u->mixer_path_set, u->mixer_handle, ignore_dB);
pa_log_debug("Probed mixer paths:");
pa_alsa_path_set_dump(u->mixer_path_set);
}
return;
fail:
if (u->mixer_path_set) {
pa_alsa_path_set_free(u->mixer_path_set);
u->mixer_path_set = NULL;
} else if (u->mixer_path) {
pa_alsa_path_free(u->mixer_path);
u->mixer_path = NULL;
}
if (u->mixer_handle) {
snd_mixer_close(u->mixer_handle);
u->mixer_handle = NULL;
}
}
static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
pa_assert(u); pa_assert(u);
if (!u->mixer_handle) if (!u->mixer_handle)
return 0; return 0;
pa_assert(u->mixer_elem); if (u->sink->active_port) {
pa_alsa_port_data *data;
if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) { /* We have a list of supported paths, so let's activate the
pa_bool_t suitable = FALSE; * one that has been chosen as active */
if (snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0) data = PA_DEVICE_PORT_DATA(u->sink->active_port);
pa_log_info("Failed to get volume range. Falling back to software volume control."); u->mixer_path = data->path;
else if (u->hw_volume_min >= u->hw_volume_max)
pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
else {
pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
suitable = TRUE;
}
if (suitable) { pa_alsa_path_select(data->path, u->mixer_handle);
if (ignore_dB || snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
pa_log_info("Mixer doesn't support dB information or data is ignored.");
else {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
#endif
if (u->hw_dB_min >= u->hw_dB_max) if (data->setting)
pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0); pa_alsa_setting_select(data->setting, u->mixer_handle);
else {
pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
u->hw_dB_supported = TRUE;
if (u->hw_dB_max > 0) { } else {
u->sink->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume));
} else
pa_log_info("No particular base volume set, fixing to 0 dB");
}
}
if (!u->hw_dB_supported && if (!u->mixer_path && u->mixer_path_set)
u->hw_volume_max - u->hw_volume_min < 3) { u->mixer_path = u->mixer_path_set->paths;
pa_log_info("Device doesn't do dB volume and has less than 4 volume levels. Falling back to software volume control."); if (u->mixer_path) {
suitable = FALSE; /* Hmm, we have only a single path, then let's activate it */
}
}
if (suitable) { pa_alsa_path_select(u->mixer_path, u->mixer_handle);
u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->sink->channel_map, u->mixer_map, TRUE) >= 0;
u->sink->get_volume = sink_get_volume_cb; if (u->mixer_path->settings)
u->sink->set_volume = sink_set_volume_cb; pa_alsa_setting_select(u->mixer_path->settings, u->mixer_handle);
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
if (!u->hw_dB_supported)
u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
} else } else
pa_log_info("Using software volume control."); return 0;
} }
if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) { if (!u->mixer_path->has_volume)
pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
else {
if (u->mixer_path->has_dB) {
pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB);
u->sink->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB);
u->sink->n_volume_steps = PA_VOLUME_NORM+1;
if (u->mixer_path->max_dB > 0.0)
pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume));
else
pa_log_info("No particular base volume set, fixing to 0 dB");
} else {
pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume);
u->sink->base_volume = PA_VOLUME_NORM;
u->sink->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1;
}
u->sink->get_volume = sink_get_volume_cb;
u->sink->set_volume = sink_set_volume_cb;
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->mixer_path->has_dB ? PA_SINK_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
}
if (!u->mixer_path->has_mute) {
pa_log_info("Driver does not support hardware mute control, falling back to software mute control.");
} else {
u->sink->get_mute = sink_get_mute_cb; u->sink->get_mute = sink_get_mute_cb;
u->sink->set_mute = sink_set_mute_cb; u->sink->set_mute = sink_set_mute_cb;
u->sink->flags |= PA_SINK_HW_MUTE_CTRL; u->sink->flags |= PA_SINK_HW_MUTE_CTRL;
} else pa_log_info("Using hardware mute control.");
pa_log_info("Using software mute control."); }
u->mixer_fdl = pa_alsa_fdlist_new(); u->mixer_fdl = pa_alsa_fdlist_new();
@ -1544,13 +1490,15 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
return -1; return -1;
} }
snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback); if (u->mixer_path_set)
snd_mixer_elem_set_callback_private(u->mixer_elem, u); pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
else
pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
return 0; return 0;
} }
pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) { pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
struct userdata *u = NULL; struct userdata *u = NULL;
const char *dev_id = NULL; const char *dev_id = NULL;
@ -1561,7 +1509,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
size_t frame_size; size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_sink_new_data data; pa_sink_new_data data;
char *control_device = NULL; pa_alsa_profile_set *profile_set = NULL;
pa_assert(m); pa_assert(m);
pa_assert(ma); pa_assert(ma);
@ -1646,32 +1594,35 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
b = use_mmap; b = use_mmap;
d = use_tsched; d = use_tsched;
if (profile) { if (mapping) {
if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
pa_log("device_id= not set"); pa_log("device_id= not set");
goto fail; goto fail;
} }
if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile( if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
dev_id, dev_id,
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames, &nfrags, &period_frames, tsched_frames,
&b, &d, profile))) &b, &d, mapping)))
goto fail; goto fail;
} else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
if (!(profile_set = pa_alsa_profile_set_new(NULL, &map)))
goto fail;
if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto( if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
dev_id, dev_id,
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames, &nfrags, &period_frames, tsched_frames,
&b, &d, &profile))) &b, &d, profile_set, &mapping)))
goto fail; goto fail;
@ -1685,7 +1636,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&nfrags, &period_frames, tsched_frames, &nfrags, &period_frames, tsched_frames,
&b, &d, FALSE))) &b, &d, FALSE)))
goto fail; goto fail;
} }
pa_assert(u->device_name); pa_assert(u->device_name);
@ -1696,8 +1646,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail; goto fail;
} }
if (profile) if (mapping)
pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name); pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name);
if (use_mmap && !b) { if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
@ -1723,7 +1673,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
/* ALSA might tweak the sample spec, so recalculate the frame size */ /* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss); frame_size = pa_frame_size(&ss);
pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
pa_sink_new_data_init(&data); pa_sink_new_data_init(&data);
data.driver = driver; data.driver = driver;
@ -1733,23 +1683,21 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map); pa_sink_new_data_set_channel_map(&data, &map);
pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle, u->mixer_elem); pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
if (profile) { if (mapping) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
} }
pa_alsa_init_description(data.proplist); pa_alsa_init_description(data.proplist);
if (control_device) { if (u->control_device)
pa_alsa_init_proplist_ctl(data.proplist, control_device); pa_alsa_init_proplist_ctl(data.proplist, u->control_device);
pa_xfree(control_device);
}
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
pa_log("Invalid properties"); pa_log("Invalid properties");
@ -1757,6 +1705,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail; goto fail;
} }
if (u->mixer_path_set)
pa_alsa_add_ports(&data.ports, u->mixer_path_set);
u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY|(u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0)); u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY|(u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0));
pa_sink_new_data_done(&data); pa_sink_new_data_done(&data);
@ -1768,6 +1719,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
u->sink->parent.process_msg = sink_process_msg; u->sink->parent.process_msg = sink_process_msg;
u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->update_requested_latency = sink_update_requested_latency_cb;
u->sink->set_state = sink_set_state_cb; u->sink->set_state = sink_set_state_cb;
u->sink->set_port = sink_set_port_cb;
u->sink->userdata = u; u->sink->userdata = u;
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
@ -1836,6 +1788,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_sink_put(u->sink); pa_sink_put(u->sink);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return u->sink; return u->sink;
fail: fail:
@ -1843,6 +1798,9 @@ fail:
if (u) if (u)
userdata_free(u); userdata_free(u);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return NULL; return NULL;
} }
@ -1871,17 +1829,22 @@ static void userdata_free(struct userdata *u) {
if (u->rtpoll) if (u->rtpoll)
pa_rtpoll_free(u->rtpoll); pa_rtpoll_free(u->rtpoll);
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
if (u->mixer_handle)
snd_mixer_close(u->mixer_handle);
if (u->pcm_handle) { if (u->pcm_handle) {
snd_pcm_drop(u->pcm_handle); snd_pcm_drop(u->pcm_handle);
snd_pcm_close(u->pcm_handle); snd_pcm_close(u->pcm_handle);
} }
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
if (u->mixer_path_set)
pa_alsa_path_set_free(u->mixer_path_set);
else if (u->mixer_path)
pa_alsa_path_free(u->mixer_path);
if (u->mixer_handle)
snd_mixer_close(u->mixer_handle);
if (u->smoother) if (u->smoother)
pa_smoother_free(u->smoother); pa_smoother_free(u->smoother);
@ -1889,6 +1852,7 @@ static void userdata_free(struct userdata *u) {
monitor_done(u); monitor_done(u);
pa_xfree(u->device_name); pa_xfree(u->device_name);
pa_xfree(u->control_device);
pa_xfree(u); pa_xfree(u);
} }

View file

@ -28,8 +28,9 @@
#include <pulsecore/sink.h> #include <pulsecore/sink.h>
#include "alsa-util.h" #include "alsa-util.h"
#include "alsa-mixer.h"
pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile); pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping);
void pa_alsa_sink_free(pa_sink *s); void pa_alsa_sink_free(pa_sink *s);

View file

@ -28,10 +28,6 @@
#include <asoundlib.h> #include <asoundlib.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulse/util.h> #include <pulse/util.h>
#include <pulse/timeval.h> #include <pulse/timeval.h>
@ -81,11 +77,8 @@ struct userdata {
pa_alsa_fdlist *mixer_fdl; pa_alsa_fdlist *mixer_fdl;
snd_mixer_t *mixer_handle; snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem; pa_alsa_path_set *mixer_path_set;
long hw_volume_max, hw_volume_min; pa_alsa_path *mixer_path;
long hw_dB_max, hw_dB_min;
pa_bool_t hw_dB_supported:1;
pa_bool_t mixer_seperate_channels:1;
pa_cvolume hardware_volume; pa_cvolume hardware_volume;
@ -102,6 +95,7 @@ struct userdata {
unsigned nfragments; unsigned nfragments;
char *device_name; char *device_name;
char *control_device;
pa_bool_t use_mmap:1, use_tsched:1; pa_bool_t use_mmap:1, use_tsched:1;
@ -949,239 +943,135 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
return 0; return 0;
} }
static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) {
return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) /
(double) (u->hw_volume_max - u->hw_volume_min));
}
static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
long alsa_vol;
alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min))
/ PA_VOLUME_NORM) + u->hw_volume_min;
return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
}
static void source_get_volume_cb(pa_source *s) { static void source_get_volume_cb(pa_source *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r; pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX]; char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if (u->mixer_seperate_channels) { if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
return;
r.channels = s->sample_spec.channels; /* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
for (i = 0; i < s->sample_spec.channels; i++) {
long alsa_vol;
if (u->hw_dB_supported) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
r.values[i] = from_alsa_volume(u, alsa_vol);
}
}
} else {
long alsa_vol;
if (u->hw_dB_supported) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
}
}
pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
if (!pa_cvolume_equal(&u->hardware_volume, &r)) { if (pa_cvolume_equal(&u->hardware_volume, &r))
return;
s->virtual_volume = u->hardware_volume = r; s->virtual_volume = u->hardware_volume = r;
if (u->hw_dB_supported) { if (u->mixer_path->has_dB) {
pa_cvolume reset; pa_cvolume reset;
/* Hmm, so the hardware volume changed, let's reset our software volume */ /* Hmm, so the hardware volume changed, let's reset our software volume */
pa_cvolume_reset(&reset, s->sample_spec.channels); pa_cvolume_reset(&reset, s->sample_spec.channels);
pa_source_set_soft_volume(s, &reset); pa_source_set_soft_volume(s, &reset);
}
} }
return;
fail:
pa_log_error("Unable to read volume: %s", pa_alsa_strerror(err));
} }
static void source_set_volume_cb(pa_source *s) { static void source_set_volume_cb(pa_source *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r; pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if (u->mixer_seperate_channels) { /* Shift up by the base volume */
pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume);
r.channels = s->sample_spec.channels; if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
return;
for (i = 0; i < s->sample_spec.channels; i++) { /* Shift down by the base volume, so that 0dB becomes maximum volume */
long alsa_vol; pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
pa_volume_t vol;
vol = s->virtual_volume.values[i];
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
alsa_vol = to_alsa_volume(u, vol);
if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
r.values[i] = from_alsa_volume(u, alsa_vol);
}
}
} else {
pa_volume_t vol;
long alsa_vol;
vol = pa_cvolume_max(&s->virtual_volume);
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_capture_dB_all(u->mixer_elem, alsa_vol, 1)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
if ((err = snd_mixer_selem_set_capture_volume_all(u->mixer_elem, alsa_vol)) < 0)
goto fail;
if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
}
}
u->hardware_volume = r; u->hardware_volume = r;
if (u->hw_dB_supported) { if (u->mixer_path->has_dB) {
char t[PA_CVOLUME_SNPRINT_MAX];
/* Match exactly what the user requested by software */ /* Match exactly what the user requested by software */
pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
} else } else {
pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
/* We can't match exactly what the user requested, hence let's /* We can't match exactly what the user requested, hence let's
* at least tell the user about it */ * at least tell the user about it */
s->virtual_volume = r; s->virtual_volume = r;
}
return;
fail:
pa_log_error("Unable to set volume: %s", pa_alsa_strerror(err));
} }
static void source_get_mute_cb(pa_source *s) { static void source_get_mute_cb(pa_source *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err, sw = 0; pa_bool_t b;
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) { if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
pa_log_error("Unable to get switch: %s", pa_alsa_strerror(err));
return; return;
}
s->muted = !sw; s->muted = b;
} }
static void source_set_mute_cb(pa_source *s) { static void source_set_mute_cb(pa_source *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
int err;
pa_assert(u); pa_assert(u);
pa_assert(u->mixer_elem); pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) { pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted);
pa_log_error("Unable to set switch: %s", pa_alsa_strerror(err)); }
return;
static int source_set_port_cb(pa_source *s, pa_device_port *p) {
struct userdata *u = s->userdata;
pa_alsa_port_data *data;
pa_assert(u);
pa_assert(p);
pa_assert(u->mixer_handle);
data = PA_DEVICE_PORT_DATA(p);
pa_assert_se(u->mixer_path = data->path);
pa_alsa_path_select(u->mixer_path, u->mixer_handle);
if (u->mixer_path->has_volume && u->mixer_path->has_dB) {
s->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB);
s->n_volume_steps = PA_VOLUME_NORM+1;
if (u->mixer_path->max_dB > 0.0)
pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(s->base_volume));
else
pa_log_info("No particular base volume set, fixing to 0 dB");
} else {
s->base_volume = PA_VOLUME_NORM;
s->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1;
} }
if (data->setting)
pa_alsa_setting_select(data->setting, u->mixer_handle);
if (s->set_mute)
s->set_mute(s);
if (s->set_volume)
s->set_volume(s);
return 0;
} }
static void source_update_requested_latency_cb(pa_source *s) { static void source_update_requested_latency_cb(pa_source *s) {
@ -1323,77 +1213,127 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char
pa_xfree(t); pa_xfree(t);
} }
static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, pa_bool_t ignore_dB) {
if (!mapping && !element)
return;
if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) {
pa_log_info("Failed to find a working mixer device.");
return;
}
if (element) {
if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_INPUT)))
goto fail;
if (pa_alsa_path_probe(u->mixer_path, u->mixer_handle, ignore_dB) < 0)
goto fail;
pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
pa_alsa_path_dump(u->mixer_path);
} else {
if (!(u->mixer_path_set = pa_alsa_path_set_new(mapping, PA_ALSA_DIRECTION_INPUT)))
goto fail;
pa_alsa_path_set_probe(u->mixer_path_set, u->mixer_handle, ignore_dB);
pa_log_debug("Probed mixer paths:");
pa_alsa_path_set_dump(u->mixer_path_set);
}
return;
fail:
if (u->mixer_path_set) {
pa_alsa_path_set_free(u->mixer_path_set);
u->mixer_path_set = NULL;
} else if (u->mixer_path) {
pa_alsa_path_free(u->mixer_path);
u->mixer_path = NULL;
}
if (u->mixer_handle) {
snd_mixer_close(u->mixer_handle);
u->mixer_handle = NULL;
}
}
static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
pa_assert(u); pa_assert(u);
if (!u->mixer_handle) if (!u->mixer_handle)
return 0; return 0;
pa_assert(u->mixer_elem); if (u->source->active_port) {
pa_alsa_port_data *data;
if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) { /* We have a list of supported paths, so let's activate the
pa_bool_t suitable = FALSE; * one that has been chosen as active */
if (snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0) data = PA_DEVICE_PORT_DATA(u->source->active_port);
pa_log_info("Failed to get volume range. Falling back to software volume control."); u->mixer_path = data->path;
else if (u->hw_volume_min >= u->hw_volume_max)
pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
else {
pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
suitable = TRUE;
}
if (suitable) { pa_alsa_path_select(data->path, u->mixer_handle);
if (ignore_dB || snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
pa_log_info("Mixer doesn't support dB information or data is ignored.");
else {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
#endif
if (u->hw_dB_min >= u->hw_dB_max) if (data->setting)
pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0); pa_alsa_setting_select(data->setting, u->mixer_handle);
else {
pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
u->hw_dB_supported = TRUE;
if (u->hw_dB_max > 0) { } else {
u->source->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume));
} else
pa_log_info("No particular base volume set, fixing to 0 dB");
}
}
if (!u->hw_dB_supported && if (!u->mixer_path && u->mixer_path_set)
u->hw_volume_max - u->hw_volume_min < 3) { u->mixer_path = u->mixer_path_set->paths;
pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); if (u->mixer_path) {
suitable = FALSE; /* Hmm, we have only a single path, then let's activate it */
}
}
if (suitable) { pa_alsa_path_select(u->mixer_path, u->mixer_handle);
u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->source->channel_map, u->mixer_map, FALSE) >= 0;
u->source->get_volume = source_get_volume_cb; if (u->mixer_path->settings)
u->source->set_volume = source_set_volume_cb; pa_alsa_setting_select(u->mixer_path->settings, u->mixer_handle);
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
if (!u->hw_dB_supported)
u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
} else } else
pa_log_info("Using software volume control."); return 0;
} }
if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) { if (!u->mixer_path->has_volume)
pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
else {
if (u->mixer_path->has_dB) {
pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB);
u->source->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB);
u->source->n_volume_steps = PA_VOLUME_NORM+1;
if (u->mixer_path->max_dB > 0.0)
pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume));
else
pa_log_info("No particular base volume set, fixing to 0 dB");
} else {
pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume);
u->source->base_volume = PA_VOLUME_NORM;
u->source->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1;
}
u->source->get_volume = source_get_volume_cb;
u->source->set_volume = source_set_volume_cb;
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->mixer_path->has_dB ? PA_SOURCE_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
}
if (!u->mixer_path->has_mute) {
pa_log_info("Driver does not support hardware mute control, falling back to software mute control.");
} else {
u->source->get_mute = source_get_mute_cb; u->source->get_mute = source_get_mute_cb;
u->source->set_mute = source_set_mute_cb; u->source->set_mute = source_set_mute_cb;
u->source->flags |= PA_SOURCE_HW_MUTE_CTRL; u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
} else pa_log_info("Using hardware mute control.");
pa_log_info("Using software mute control."); }
u->mixer_fdl = pa_alsa_fdlist_new(); u->mixer_fdl = pa_alsa_fdlist_new();
@ -1402,13 +1342,15 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
return -1; return -1;
} }
snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback); if (u->mixer_path_set)
snd_mixer_elem_set_callback_private(u->mixer_elem, u); pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
else
pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
return 0; return 0;
} }
pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) { pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
struct userdata *u = NULL; struct userdata *u = NULL;
const char *dev_id = NULL; const char *dev_id = NULL;
@ -1419,7 +1361,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
size_t frame_size; size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data; pa_source_new_data data;
char *control_device = NULL; pa_alsa_profile_set *profile_set = NULL;
pa_assert(m); pa_assert(m);
pa_assert(ma); pa_assert(ma);
@ -1480,7 +1422,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->use_tsched = use_tsched; u->use_tsched = use_tsched;
u->rtpoll = pa_rtpoll_new(); u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->alsa_rtpoll_item = NULL;
u->smoother = pa_smoother_new( u->smoother = pa_smoother_new(
DEFAULT_TSCHED_WATERMARK_USEC*2, DEFAULT_TSCHED_WATERMARK_USEC*2,
@ -1504,31 +1445,34 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
b = use_mmap; b = use_mmap;
d = use_tsched; d = use_tsched;
if (profile) { if (mapping) {
if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
pa_log("device_id= not set"); pa_log("device_id= not set");
goto fail; goto fail;
} }
if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile( if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
dev_id, dev_id,
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_CAPTURE, SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames, &nfrags, &period_frames, tsched_frames,
&b, &d, profile))) &b, &d, mapping)))
goto fail; goto fail;
} else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
if (!(profile_set = pa_alsa_profile_set_new(NULL, &map)))
goto fail;
if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto( if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
dev_id, dev_id,
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_CAPTURE, SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames, &nfrags, &period_frames, tsched_frames,
&b, &d, &profile))) &b, &d, profile_set, &mapping)))
goto fail; goto fail;
} else { } else {
@ -1551,8 +1495,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail; goto fail;
} }
if (profile) if (mapping)
pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name); pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name);
if (use_mmap && !b) { if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
@ -1578,7 +1522,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
/* ALSA might tweak the sample spec, so recalculate the frame size */ /* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss); frame_size = pa_frame_size(&ss);
pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
pa_source_new_data_init(&data); pa_source_new_data_init(&data);
data.driver = driver; data.driver = driver;
@ -1588,23 +1532,21 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_source_new_data_set_sample_spec(&data, &ss); pa_source_new_data_set_sample_spec(&data, &ss);
pa_source_new_data_set_channel_map(&data, &map); pa_source_new_data_set_channel_map(&data, &map);
pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle, u->mixer_elem); pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
if (profile) { if (mapping) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
} }
pa_alsa_init_description(data.proplist); pa_alsa_init_description(data.proplist);
if (control_device) { if (u->control_device)
pa_alsa_init_proplist_ctl(data.proplist, control_device); pa_alsa_init_proplist_ctl(data.proplist, u->control_device);
pa_xfree(control_device);
}
if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
pa_log("Invalid properties"); pa_log("Invalid properties");
@ -1612,6 +1554,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail; goto fail;
} }
if (u->mixer_path_set)
pa_alsa_add_ports(&data.ports, u->mixer_path_set);
u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0)); u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0));
pa_source_new_data_done(&data); pa_source_new_data_done(&data);
@ -1623,6 +1568,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->source->parent.process_msg = source_process_msg; u->source->parent.process_msg = source_process_msg;
u->source->update_requested_latency = source_update_requested_latency_cb; u->source->update_requested_latency = source_update_requested_latency_cb;
u->source->set_state = source_set_state_cb; u->source->set_state = source_set_state_cb;
u->source->set_port = source_set_port_cb;
u->source->userdata = u; u->source->userdata = u;
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
@ -1687,6 +1633,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_source_put(u->source); pa_source_put(u->source);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return u->source; return u->source;
fail: fail:
@ -1694,6 +1643,9 @@ fail:
if (u) if (u)
userdata_free(u); userdata_free(u);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return NULL; return NULL;
} }
@ -1719,17 +1671,22 @@ static void userdata_free(struct userdata *u) {
if (u->rtpoll) if (u->rtpoll)
pa_rtpoll_free(u->rtpoll); pa_rtpoll_free(u->rtpoll);
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
if (u->mixer_handle)
snd_mixer_close(u->mixer_handle);
if (u->pcm_handle) { if (u->pcm_handle) {
snd_pcm_drop(u->pcm_handle); snd_pcm_drop(u->pcm_handle);
snd_pcm_close(u->pcm_handle); snd_pcm_close(u->pcm_handle);
} }
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
if (u->mixer_path_set)
pa_alsa_path_set_free(u->mixer_path_set);
else if (u->mixer_path)
pa_alsa_path_free(u->mixer_path);
if (u->mixer_handle)
snd_mixer_close(u->mixer_handle);
if (u->smoother) if (u->smoother)
pa_smoother_free(u->smoother); pa_smoother_free(u->smoother);
@ -1737,6 +1694,7 @@ static void userdata_free(struct userdata *u) {
monitor_done(u); monitor_done(u);
pa_xfree(u->device_name); pa_xfree(u->device_name);
pa_xfree(u->control_device);
pa_xfree(u); pa_xfree(u);
} }

View file

@ -29,7 +29,7 @@
#include "alsa-util.h" #include "alsa-util.h"
pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile); pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping);
void pa_alsa_source_free(pa_source *s); void pa_alsa_source_free(pa_source *s);

File diff suppressed because it is too large Load diff

View file

@ -30,95 +30,86 @@
#include <pulse/mainloop-api.h> #include <pulse/mainloop-api.h>
#include <pulse/channelmap.h> #include <pulse/channelmap.h>
#include <pulse/proplist.h> #include <pulse/proplist.h>
#include <pulse/volume.h>
#include <pulsecore/llist.h>
#include <pulsecore/rtpoll.h> #include <pulsecore/rtpoll.h>
#include <pulsecore/core.h> #include <pulsecore/core.h>
#include <pulsecore/log.h> #include <pulsecore/log.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist; #include "alsa-mixer.h"
struct pa_alsa_fdlist *pa_alsa_fdlist_new(void);
void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl);
int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
int pa_alsa_set_hw_params( int pa_alsa_set_hw_params(
snd_pcm_t *pcm_handle, snd_pcm_t *pcm_handle,
pa_sample_spec *ss, pa_sample_spec *ss, /* modified at return */
uint32_t *periods, uint32_t *periods, /* modified at return */
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, pa_bool_t *use_tsched, /* modified at return */
pa_bool_t require_exact_channel_number); pa_bool_t require_exact_channel_number);
int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min); int pa_alsa_set_sw_params(
snd_pcm_t *pcm,
snd_pcm_uframes_t avail_min);
typedef struct pa_alsa_profile_info { /* Picks a working mapping from the profile set based on the specified ss/map */
pa_channel_map map;
const char *alsa_name;
const char *alsa_name_fallback;
const char *description; /* internationalized */
const char *name;
unsigned priority;
const char *playback_control_name, *playback_control_fallback;
const char *record_control_name, *record_control_fallback;
} pa_alsa_profile_info;
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback);
int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, char **ctl_device, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name, const pa_alsa_profile_info*profile);
void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name);
/* Picks a working profile based on the specified ss/map */
snd_pcm_t *pa_alsa_open_by_device_id_auto( snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id, const char *dev_id,
char **dev, char **dev, /* modified at return */
pa_sample_spec *ss, pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, pa_channel_map* map, /* modified at return */
int mode, int mode,
uint32_t *nfrags, uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, pa_bool_t *use_tsched, /* modified at return */
const pa_alsa_profile_info **profile); pa_alsa_profile_set *ps,
pa_alsa_mapping **mapping); /* modified at return */
/* Uses the specified profile */ /* Uses the specified mapping */
snd_pcm_t *pa_alsa_open_by_device_id_profile( snd_pcm_t *pa_alsa_open_by_device_id_mapping(
const char *dev_id, const char *dev_id,
char **dev, char **dev, /* modified at return */
pa_sample_spec *ss, pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, pa_channel_map* map, /* modified at return */
int mode, int mode,
uint32_t *nfrags, uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, pa_bool_t *use_tsched, /* modified at return */
const pa_alsa_profile_info *profile); pa_alsa_mapping *mapping);
/* Opens the explicit ALSA device */ /* Opens the explicit ALSA device */
snd_pcm_t *pa_alsa_open_by_device_string( snd_pcm_t *pa_alsa_open_by_device_string(
const char *device, const char *dir,
char **dev, char **dev, /* modified at return */
pa_sample_spec *ss, pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, pa_channel_map* map, /* modified at return */
int mode, int mode,
uint32_t *nfrags, uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, pa_bool_t *use_tsched, /* modified at return */
pa_bool_t require_exact_channel_number); pa_bool_t require_exact_channel_number);
int pa_alsa_probe_profiles( /* Opens the explicit ALSA device with a fallback list */
snd_pcm_t *pa_alsa_open_by_template(
char **template,
const char *dev_id, const char *dev_id,
const pa_sample_spec *ss, char **dev, /* modified at return */
void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata), pa_sample_spec *ss, /* modified at return */
void *userdata); pa_channel_map* map, /* modified at return */
int mode,
int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback); uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
pa_bool_t require_exact_channel_number);
void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm); void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm);
void pa_alsa_dump_status(snd_pcm_t *pcm); void pa_alsa_dump_status(snd_pcm_t *pcm);
@ -128,7 +119,8 @@ void pa_alsa_redirect_errors_dec(void);
void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info); void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card); void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card);
void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm, snd_mixer_elem_t *elem); void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm);
void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name);
pa_bool_t pa_alsa_init_description(pa_proplist *p); pa_bool_t pa_alsa_init_description(pa_proplist *p);
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
@ -140,13 +132,11 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_si
int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss); int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss);
char *pa_alsa_get_driver_name(int card); char *pa_alsa_get_driver_name(int card);
char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm); char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);
char *pa_alsa_get_reserve_name(const char *device); char *pa_alsa_get_reserve_name(const char *device);
pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm); pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm);
pa_bool_t pa_alsa_pcm_is_modem(snd_pcm_t *pcm); pa_bool_t pa_alsa_pcm_is_modem(snd_pcm_t *pcm);
const char* pa_alsa_strerror(int errnum); const char* pa_alsa_strerror(int errnum);

View file

@ -0,0 +1,32 @@
# For devices, where we have an Aux element
[General]
priority = 90
name = analog-input
[Element Capture]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
switch = off
volume = off
[Element Line]
switch = off
volume = off
[Element Aux]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Video]
switch = off
volume = off
.include analog-input.conf.common

View file

@ -0,0 +1,44 @@
# For devices where we have an FM element
[General]
priority = 70
name = analog-input-radio
[Element Capture]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
switch = off
volume = off
[Element Line]
switch = off
volume = off
[Element Aux]
switch = off
volume = off
[Element Video]
switch = off
volume = off
[Element Mic/Line]
switch = off
volume = off
[Element TV Tuner]
switch = off
volume = off
[Element FM]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
.include analog-input.conf.common

View file

@ -0,0 +1,43 @@
# For devices, where we have a Line element
[General]
priority = 90
[Element Capture]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
switch = off
volume = off
[Element Line]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Aux]
switch = off
volume = off
[Element Video]
switch = off
volume = off
[Element Mic/Line]
switch = off
volume = off
[Element TV Tuner]
switch = off
volume = off
[Element FM]
switch = off
volume = off
.include analog-input.conf.common

View file

@ -0,0 +1,44 @@
# For devices where we have a Mic/Lineb element
[General]
priority = 90
name = analog-input
[Element Capture]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
switch = off
volume = off
[Element Line]
switch = off
volume = off
[Element Aux]
switch = off
volume = off
[Element Video]
switch = off
volume = off
[Element Mic/Line]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element TV Tuner]
switch = off
volume = off
[Element FM]
switch = off
volume = off
.include analog-input.conf.common

View file

@ -0,0 +1,45 @@
# For devices where we have a Mic element
[General]
priority = 100
name = analog-input-microphone
[Element Capture]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Line]
switch = off
volume = off
[Element Aux]
switch = off
volume = off
[Element Video]
switch = off
volume = off
[Element Mic/Line]
switch = off
volume = off
[Element TV Tuner]
switch = off
volume = off
[Element FM]
switch = off
volume = off
.include analog-input.conf.common
.include analog-input-mic.conf.common

View file

@ -0,0 +1,41 @@
;;; 'Mic Select'
[Element Mic Select]
enumeration = select
[Option Mic Select:Mic1]
name = input-microphone
priority = 20
[Option Mic Select:Mic2]
name = input-microphone
priority = 19
;;; Various Boosts
[Element Mic Boost (+20dB)]
switch = select
[Option Mic Boost (+20dB):on]
name = input-boost-on
[Option Mic Boost (+20dB):off]
name = input-boost-off
[Element Mic Boost]
switch = select
[Option Mic Boost:on]
name = input-boost-on
[Option Mic Boost:off]
name = input-boost-off
[Element Front Mic Boost]
switch = select
[Option Front Mic Boost:on]
name = input-boost-on
[Option Front Mic Boost:off]
name = input-boost-off

View file

@ -0,0 +1,44 @@
# For devices, where we have a TV Tuner element
[General]
priority = 70
name = analog-input-video
[Element Capture]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
switch = off
volume = off
[Element Line]
switch = off
volume = off
[Element Aux]
switch = off
volume = off
[Element Video]
switch = off
volume = off
[Element Mic/Line]
switch = off
volume = off
[Element TV Tuner]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element FM]
switch = off
volume = off
.include analog-input.conf.common

View file

@ -0,0 +1,31 @@
# For devices, where we have a Video element
[General]
priority = 70
[Element Capture]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
switch = off
volume = off
[Element Line]
switch = off
volume = off
[Element Aux]
switch = off
volume = off
[Element Video]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
.include analog-input.conf.common

View file

@ -0,0 +1,35 @@
# A fallback for devices that lack seperate Mic/Line/Aux/Video elements
[General]
priority = 100
[Element Capture]
required = volume
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Mic]
required-absent = any
[Element Line]
required-absent = any
[Element Aux]
required-absent = any
[Element Video]
required-absent = any
[Element Mic/Line]
required-absent = any
[Element TV Tuner]
required-absent = any
[Element FM]
required-absent = any
.include analog-input.conf.common
.include analog-input-mic.conf.common

View file

@ -0,0 +1,239 @@
# Mixer path for PulseAudio's ALSA backend. If multiple options by the
# same id are discovered they will be suffixed with a number to
# distuingish them, in the same order they appear here.
#
# Source selection should use the following names:
#
# input -- If we don't know the exact kind of input
# input-microphone
# input-microphone-internal
# input-microphone-external
# input-linein
# input-video
# input-radio
# input-docking-microphone
# input-docking-linein
# input-docking
#
# We explicitly don't want to wrap the following sources:
#
# CD
# Synth/MIDI
# Phone
# Mix
# Digital/SPDIF
# Master
# PC Speaker
#
;;; 'Input Source Select'
[Element Input Source Select]
enumeration = select
[Option Input Source Select:Input1]
name = input
priority = 10
[Option Input Source Select:Input2]
name = input
priority = 5
;;; 'Input Source'
[Element Input Source]
enumeration = select
[Option Input Source:Mic]
name = input-microphone
priority = 20
[Option Input Source:Microphone]
name = input-microphone
priority = 20
[Option Input Source:Front Mic]
name = input-microphone
priority = 19
[Option Input Source:Front Microphone]
name = input-microphone
priority = 19
[Option Input Source:Line]
name = input-linein
priority = 18
[Option Input Source:Line-In]
name = input-linein
priority = 18
[Option Input Source:Line In]
name = input-linein
priority = 18
;;; ' Capture Source'
[Element Capture Source]
enumeration = select
[Option Capture Source:TV Tuner]
name = input-video
[Option Capture Source:FM]
name = input-radio
[Option Capture Source:Mic/Line]
name = input
[Option Capture Source:Line/Mic]
name = input
[Option Capture Source:Mic]
name = input-microphone
[Option Capture Source:Microphone]
name = input-microphone
[Option Capture Source:Int Mic]
name = input-microphone-internal
[Option Capture Source:Int DMic]
name = input-microphone-internal
[Option Capture Source:Internal Mic]
name = input-microphone-internal
[Option Capture Source:iMic]
name = input-microphone-internal
[Option Capture Source:i-Mic]
name = input-microphone-internal
[Option Capture Source:Internal Microphone]
name = input-microphone-internal
[Option Capture Source:Front Mic]
name = input-microphone
[Option Capture Source:Front Microphone]
name = input-microphone
[Option Capture Source:Rear Mic]
name = input-microphone
[Option Capture Source:Mic1]
name = input-microphone
[Option Capture Source:Mic2]
name = input-microphone
[Option Capture Source:D-Mic]
name = input-microphone
[Option Capture Source:IntMic]
name = input-microphone-internal
[Option Capture Source:ExtMic]
name = input-microphone-external
[Option Capture Source:Ext Mic]
name = input-microphone-external
[Option Capture Source:E-Mic]
name = input-microphone-external
[Option Capture Source:e-Mic]
name = input-microphone-external
[Option Capture Source:LineIn]
name = input-linein
[Option Capture Source:Analog]
name = input
[Option Capture Source:Line]
name = input-linein
[Option Capture Source:Line-In]
name = input-linein
[Option Capture Source:Line In]
name = input-linein
[Option Capture Source:Video]
name = input-video
[Option Capture Source:Aux]
name = input
[Option Capture Source:Aux0]
name = input
[Option Capture Source:Aux1]
name = input
[Option Capture Source:Aux2]
name = input
[Option Capture Source:Aux3]
name = input
[Option Capture Source:AUX IN]
name = input
[Option Capture Source:Aux In]
name = input
[Option Capture Source:AOUT]
name = input
[Option Capture Source:AUX]
name = input
[Option Capture Source:Cam Mic]
name = input-microphone
[Option Capture Source:Digital Mic]
name = input-microphone
[Option Capture Source:Digital Mic 1]
name = input-microphone
[Option Capture Source:Digital Mic 2]
name = input-microphone
[Option Capture Source:Analog Inputs]
name = input
[Option Capture Source:Unknown1]
name = input
[Option Capture Source:Unknown2]
name = input
[Option Capture Source:Docking-Station]
name = input-docking
[Option Capture Source:Dock Mic]
name = input-docking-microphone
;;; Various Boosts
[Element Capture Boost]
switch = select
[Option Capture Boost:on]
name = input-boost-on
[Option Capture Boost:off]
name = input-boost-off
[Element Auto Gain Control]
switch = select
[Option Auto Gain Control:on]
name = input-agc-on
[Option Auto Gain Control:off]
name = input-agc-off

View file

@ -0,0 +1,53 @@
# Path for mixers that have a Headphone slider
[General]
priority = 90
[Element Hardware Master]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Master]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Master Mono]
switch = off
volume = off
[Element Headphone]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Front]
switch = off
volume = off
[Element Rear]
switch = off
volume = off
[Element Sourround]
switch = off
volume = off
[Element Side]
switch = off
volume = off
[Element Center]
switch = off
volume = off
[Element LFE]
switch = off
volume = off
.include analog-output.conf.common

View file

@ -0,0 +1,54 @@
# Intended for usage in laptops that have a seperate LFE speaker
# connected to the Master mono connector
[General]
priority = 40
[Element Hardware Master]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Master]
switch = mute
volume = merge
override-map.1 = all-no-lfe
override-map.2 = all-left,all-right
[Element Master Mono]
required = any
switch = mute
volume = merge
override-map.1 = lfe
override-map.2 = lfe,lfe
[Element Headphone]
switch = off
volume = off
[Element Front]
switch = off
volume = off
[Element Rear]
switch = off
volume = off
[Element Sourround]
switch = off
volume = off
[Element Side]
switch = off
volume = off
[Element Center]
switch = off
volume = off
[Element LFE]
switch = off
volume = off
.include analog-output.conf.common

View file

@ -0,0 +1,51 @@
# Intended for usage on boards that have a seperate Mono output plug.
[General]
priority = 50
[Element Hardware Master]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Master]
switch = off
volume = off
[Element Master Mono]
required = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Headphone]
switch = off
volume = off
[Element Front]
switch = off
volume = off
[Element Rear]
switch = off
volume = off
[Element Sourround]
switch = off
volume = off
[Element Side]
switch = off
volume = off
[Element Center]
switch = off
volume = off
[Element LFE]
switch = off
volume = off
.include analog-output.conf.common

View file

@ -0,0 +1,62 @@
# Intended for the 'default' output
[General]
priority = 100
[Element Hardware Master]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Master]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Master Mono]
switch = off
volume = off
[Element Headphone]
switch = off
volume = off
[Element Front]
switch = mute
volume = merge
override-map.1 = all-front
override-map.2 = front-left,front-right
[Element Rear]
switch = mute
volume = merge
override-map.1 = all-rear
override-map.2 = rear-left,rear-right
[Element Surround]
switch = mute
volume = merge
override-map.1 = all-rear
override-map.2 = rear-left,rear-right
[Element Side]
switch = mute
volume = merge
override-map.1 = all-side
override-map.2 = side-left,side-right
[Element Center]
switch = mute
volume = merge
override-map.1 = all-center
override-map.2 = all-center,all-center
[Element LFE]
switch = mute
volume = merge
override-map.1 = lfe
override-map.2 = lfe,lfe
.include analog-output.conf.common

View file

@ -0,0 +1,40 @@
# Common part of all paths
# [General]
# priority = ...
# description = ...
#
# [Option ...:...]
# name = ...
# priority = ...
#
# [Element ...]
# required = ignore | switch | volume | enumeration | any
# required-absent = ignore | switch | volume
#
# switch = ignore | mute | off | on | select
# volume = ignore | merge | off | zero
# enumeration = ignore | select
#
# direction = playback | capture
# direction-try-other = no | yes
#
# override-map.1 = ...
# override-map.2 = ...
[Element PCM]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element External Amplifier]
switch = select
[Option External Amplifier:on]
name = output-amplifier-on
priority = 0
[Option External Amplifier:off]
name = output-amplifier-off
priority = 10

View file

@ -0,0 +1,105 @@
# Profile definitions for PulseAudio's ALSA backend
#
# [Mapping id]
# device-strings = ...
# channel-map = ...
# description = ...
# paths-input = ...
# paths-output = ...
# element-input = ...
# element-output = ...
# priority = ...
# direction = any | input | output
#
# [Profile id]
# input-mappings = ...
# output-mappings = ...
# description = ...
# priority = ...
# skip-probe = no | yes
[General]
auto-profiles = yes
[Mapping analog-mono]
device-strings = hw:%f
channel-map = mono
paths-output = analog-output analog-output-headphones analog-output-mono analog-output-lfe-on-mono
paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line
priority = 1
[Mapping analog-stereo]
device-strings = front:%f hw:%f
channel-map = left,right
paths-output = analog-output analog-output-headphones analog-output-mono analog-output-lfe-on-mono
paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line
priority = 10
[Mapping analog-surround-40]
device-strings = surround40:%f
channel-map = front-left,front-right,rear-left,rear-right
paths-output = analog-output analog-output-lfe-on-mono
priority = 7
direction = output
[Mapping analog-surround-41]
device-strings = surround41:%f
channel-map = front-left,front-right,rear-left,rear-right,lfe
paths-output = analog-output analog-output-lfe-on-mono
priority = 8
direction = output
[Mapping analog-surround-50]
device-strings = surround50:%f
channel-map = front-left,front-right,rear-left,rear-right,front-center
paths-output = analog-output analog-output-lfe-on-mono
priority = 7
direction = output
[Mapping analog-surround-51]
device-strings = surround51:%f
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
paths-output = analog-output analog-output-lfe-on-mono
priority = 8
direction = output
[Mapping analog-surround-71]
device-strings = surround71:%f
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
description = Analog Surround 7.1
paths-output = analog-output analog-output-lfe-on-mono
priority = 7
direction = output
[Mapping iec958-stereo]
device-strings = iec958:%f
channel-map = left,right
priority = 5
[Mapping iec958-surround-40]
device-strings = iec958:%f
channel-map = front-left,front-right,rear-left,rear-right
priority = 1
[Mapping iec958-ac3-surround-40]
device-strings = a52:%f
channel-map = front-left,front-right,rear-left,rear-right
priority = 2
direction = output
[Mapping iec958-ac3-surround-51]
device-strings = a52:%f
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 3
direction = output
[Mapping hdmi-stereo]
device-strings = hdmi:%f
channel-map = left,right
priority = 4
direction = output
#[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo]
#description = Foobar
#output-mappings = analog-stereo iec958-stereo
#input-mappings = analog-stereo

View file

@ -0,0 +1,150 @@
Simple mixer control 'Master',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 29 [94%] [-3.00dB] [on]
Front Right: Playback 29 [94%] [-3.00dB] [on]
Simple mixer control 'Master Mono',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'PCM',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 23 [74%] [0.00dB] [on]
Front Right: Playback 23 [74%] [0.00dB] [on]
Simple mixer control 'Surround',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-46.50dB] [off]
Front Right: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Surround Jack Mode',0
Capabilities: enum
Items: 'Shared' 'Independent'
Item0: 'Shared'
Simple mixer control 'Center',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'LFE',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Line',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'CD',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-34.50dB] [off]
Front Left: Capture [on]
Front Right: Capture [on]
Simple mixer control 'Mic Boost (+20dB)',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'Mic Select',0
Capabilities: enum
Items: 'Mic1' 'Mic2'
Item0: 'Mic1'
Simple mixer control 'Video',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Phone',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 31 [100%] [12.00dB] [off]
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'IEC958',0
Capabilities: pswitch pswitch-joined cswitch cswitch-joined
Playback channels: Mono
Capture channels: Mono
Mono: Playback [off] Capture [off]
Simple mixer control 'IEC958 Playback AC97-SPSA',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 3
Mono: 0 [0%]
Simple mixer control 'IEC958 Playback Source',0
Capabilities: enum
Items: 'PCM' 'Analog In' 'IEC958 In'
Item0: 'PCM'
Simple mixer control 'PC Speaker',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 15
Mono: Playback 0 [0%] [-45.00dB] [on]
Simple mixer control 'Aux',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [on] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [on] Capture [off]
Simple mixer control 'Mono Output Select',0
Capabilities: enum
Items: 'Mix' 'Mic'
Item0: 'Mix'
Simple mixer control 'Capture',0
Capabilities: cvolume cswitch cswitch-joined
Capture channels: Front Left - Front Right
Limits: Capture 0 - 15
Front Left: Capture 12 [80%] [18.00dB] [on]
Front Right: Capture 12 [80%] [18.00dB] [on]
Simple mixer control 'Mix',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Mix Mono',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Channel Mode',0
Capabilities: enum
Items: '2ch' '4ch' '6ch'
Item0: '2ch'
Simple mixer control 'Duplicate Front',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'External Amplifier',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]

View file

@ -0,0 +1,24 @@
Simple mixer control 'FM',0
Capabilities: cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Capture channels: Mono
Mono: Capture [off]
Simple mixer control 'Mic/Line',0
Capabilities: cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Capture channels: Mono
Mono: Capture [off]
Simple mixer control 'Capture',0
Capabilities: cvolume cvolume-joined
Capture channels: Mono
Limits: Capture 0 - 15
Mono: Capture 13 [87%]
Simple mixer control 'Capture Boost',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]
Simple mixer control 'TV Tuner',0
Capabilities: cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Capture channels: Mono
Mono: Capture [on]

View file

@ -0,0 +1,135 @@
Simple mixer control 'Master',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 63
Mono:
Front Left: Playback 63 [100%] [0.00dB] [on]
Front Right: Playback 63 [100%] [0.00dB] [on]
Simple mixer control 'Master Mono',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Headphone',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-46.50dB] [off]
Front Right: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control '3D Control - Center',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 15
Mono: 0 [0%]
Simple mixer control '3D Control - Depth',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 15
Mono: 0 [0%]
Simple mixer control '3D Control - Switch',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'PCM',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 23 [74%] [0.00dB] [on]
Front Right: Playback 23 [74%] [0.00dB] [on]
Simple mixer control 'Line',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [on]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [on]
Simple mixer control 'CD',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 23 [74%] [0.00dB] [on]
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Mic Boost (+20dB)',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'Mic Select',0
Capabilities: enum
Items: 'Mic1' 'Mic2'
Item0: 'Mic1'
Simple mixer control 'Video',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Phone',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-34.50dB] [off]
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'IEC958',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'PC Speaker',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 15
Mono: Playback 0 [0%] [-45.00dB] [off]
Simple mixer control 'Aux',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Mono Output Select',0
Capabilities: enum
Items: 'Mix' 'Mic'
Item0: 'Mic'
Simple mixer control 'Capture',0
Capabilities: cvolume cswitch cswitch-joined
Capture channels: Front Left - Front Right
Limits: Capture 0 - 15
Front Left: Capture 15 [100%] [22.50dB] [on]
Front Right: Capture 15 [100%] [22.50dB] [on]
Simple mixer control 'Mix',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Mix Mono',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'External Amplifier',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]

View file

@ -0,0 +1,4 @@
Simple mixer control 'IEC958',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]

View file

@ -0,0 +1,62 @@
Simple mixer control 'Master',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 63
Mono:
Front Left: Playback 63 [100%] [3.00dB] [on]
Front Right: Playback 63 [100%] [3.00dB] [on]
Simple mixer control 'PCM',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 23 [74%] [0.00dB] [on]
Front Right: Playback 23 [74%] [0.00dB] [on]
Simple mixer control 'CD',0
Capabilities: pvolume pswitch cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Mono
Limits: Playback 0 - 31
Mono: Capture [off]
Front Left: Playback 0 [0%] [-34.50dB] [off]
Front Right: Playback 0 [0%] [-34.50dB] [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pswitch cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Mono
Limits: Playback 0 - 31
Mono: Capture [on]
Front Left: Playback 0 [0%] [-34.50dB] [off]
Front Right: Playback 0 [0%] [-34.50dB] [off]
Simple mixer control 'Mic Boost',0
Capabilities: volume
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: 0 - 3
Front Left: 0 [0%]
Front Right: 0 [0%]
Simple mixer control 'IEC958',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 Default PCM',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 Playback Source',0
Capabilities: enum
Items: 'PCM' 'ADC'
Item0: 'PCM'
Simple mixer control 'Capture',0
Capabilities: cvolume cswitch
Capture channels: Front Left - Front Right
Limits: Capture 0 - 15
Front Left: Capture 0 [0%] [0.00dB] [on]
Front Right: Capture 0 [0%] [0.00dB] [on]
Simple mixer control 'Mix',0
Capabilities: cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Capture channels: Mono
Mono: Capture [off]

View file

@ -0,0 +1,113 @@
Simple mixer control 'Master',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 64
Mono: Playback 64 [100%] [0.00dB] [on]
Simple mixer control 'Headphone',0
Capabilities: pswitch
Playback channels: Front Left - Front Right
Mono:
Front Left: Playback [on]
Front Right: Playback [on]
Simple mixer control 'PCM',0
Capabilities: pvolume
Playback channels: Front Left - Front Right
Limits: Playback 0 - 255
Mono:
Front Left: Playback 255 [100%] [0.00dB]
Front Right: Playback 255 [100%] [0.00dB]
Simple mixer control 'Front',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 64
Mono:
Front Left: Playback 44 [69%] [-20.00dB] [on]
Front Right: Playback 44 [69%] [-20.00dB] [on]
Simple mixer control 'Front Mic',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-34.50dB] [off]
Front Right: Playback 0 [0%] [-34.50dB] [off]
Simple mixer control 'Front Mic Boost',0
Capabilities: volume
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: 0 - 3
Front Left: 0 [0%]
Front Right: 0 [0%]
Simple mixer control 'Surround',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 64
Mono:
Front Left: Playback 0 [0%] [-64.00dB] [on]
Front Right: Playback 0 [0%] [-64.00dB] [on]
Simple mixer control 'Center',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 64
Mono: Playback 0 [0%] [-64.00dB] [on]
Simple mixer control 'LFE',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 64
Mono: Playback 0 [0%] [-64.00dB] [on]
Simple mixer control 'Side',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 64
Mono:
Front Left: Playback 0 [0%] [-64.00dB] [on]
Front Right: Playback 0 [0%] [-64.00dB] [on]
Simple mixer control 'Line',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-34.50dB] [off]
Front Right: Playback 0 [0%] [-34.50dB] [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-34.50dB] [off]
Front Right: Playback 0 [0%] [-34.50dB] [off]
Simple mixer control 'Mic Boost',0
Capabilities: volume
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: 0 - 3
Front Left: 0 [0%]
Front Right: 0 [0%]
Simple mixer control 'IEC958',0
Capabilities: pswitch pswitch-joined cswitch cswitch-joined
Playback channels: Mono
Capture channels: Mono
Mono: Playback [on] Capture [on]
Simple mixer control 'IEC958 Default PCM',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]
Simple mixer control 'Capture',0
Capabilities: cvolume cswitch
Capture channels: Front Left - Front Right
Limits: Capture 0 - 46
Front Left: Capture 23 [50%] [7.00dB] [on]
Front Right: Capture 23 [50%] [7.00dB] [on]
Simple mixer control 'Capture',1
Capabilities: cvolume cswitch
Capture channels: Front Left - Front Right
Limits: Capture 0 - 46
Front Left: Capture 0 [0%] [-16.00dB] [off]
Front Right: Capture 0 [0%] [-16.00dB] [off]
Simple mixer control 'Input Source',0
Capabilities: cenum
Items: 'Mic' 'Front Mic' 'Line'
Item0: 'Mic'
Simple mixer control 'Input Source',1
Capabilities: cenum
Items: 'Mic' 'Front Mic' 'Line'
Item0: 'Mic'

View file

@ -0,0 +1,128 @@
Simple mixer control 'Master',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 63
Mono:
Front Left: Playback 44 [70%] [-28.50dB] [on]
Front Right: Playback 60 [95%] [-4.50dB] [on]
Simple mixer control 'Master Mono',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 17 [55%] [-21.00dB] [on]
Simple mixer control '3D Control - Center',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 15
Mono: 0 [0%]
Simple mixer control '3D Control - Depth',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 15
Mono: 0 [0%]
Simple mixer control '3D Control - Switch',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'PCM',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 9 [29%] [-21.00dB] [on]
Front Right: Playback 9 [29%] [-21.00dB] [on]
Simple mixer control 'PCM Out Path & Mute',0
Capabilities: enum
Items: 'pre 3D' 'post 3D'
Item0: 'pre 3D'
Simple mixer control 'Line',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'CD',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 9 [29%] [-21.00dB] [on] Capture [off]
Front Right: Playback 9 [29%] [-21.00dB] [on] Capture [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-34.50dB] [off]
Front Left: Capture [on]
Front Right: Capture [on]
Simple mixer control 'Mic Boost (+20dB)',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'Mic Select',0
Capabilities: enum
Items: 'Mic1' 'Mic2'
Item0: 'Mic1'
Simple mixer control 'Video',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Phone',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-34.50dB] [off]
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'PC Speaker',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 15
Mono: Playback 8 [53%] [-21.00dB] [on]
Simple mixer control 'Aux',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Mono Output Select',0
Capabilities: enum
Items: 'Mix' 'Mic'
Item0: 'Mix'
Simple mixer control 'Capture',0
Capabilities: cvolume cswitch cswitch-joined
Capture channels: Front Left - Front Right
Limits: Capture 0 - 15
Front Left: Capture 13 [87%] [19.50dB] [on]
Front Right: Capture 13 [87%] [19.50dB] [on]
Simple mixer control 'Mix',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Mix Mono',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'External Amplifier',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]

View file

@ -0,0 +1,27 @@
Simple mixer control 'Bass',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 48
Mono: 22 [46%]
Simple mixer control 'Bass Boost',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'Treble',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 48
Mono: 25 [52%]
Simple mixer control 'PCM',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 44
Mono:
Front Left: Playback 10 [23%] [-31.00dB] [on]
Front Right: Playback 10 [23%] [-31.00dB] [on]
Simple mixer control 'Auto Gain Control',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]

View file

@ -0,0 +1,37 @@
Simple mixer control 'Master',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 255
Mono: Playback 105 [41%] [-28.97dB] [on]
Simple mixer control 'Line',0
Capabilities: pvolume cvolume pswitch pswitch-joined cswitch cswitch-joined
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 255 Capture 0 - 128
Front Left: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [off]
Front Right: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pvolume-joined cvolume cvolume-joined pswitch pswitch-joined cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Mono
Limits: Playback 0 - 255 Capture 0 - 128
Mono: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [on]
Simple mixer control 'Mic Capture',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 In',0
Capabilities: cswitch cswitch-joined
Capture channels: Mono
Mono: Capture [off]
Simple mixer control 'Input 1',0
Capabilities: cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Capture channels: Mono
Mono: Capture [off]
Simple mixer control 'Input 2',0
Capabilities: cswitch cswitch-joined cswitch-exclusive
Capture exclusive group: 0
Capture channels: Mono
Mono: Capture [off]

View file

@ -0,0 +1,5 @@
Simple mixer control 'Mic',0
Capabilities: cvolume cvolume-joined cswitch cswitch-joined
Capture channels: Mono
Limits: Capture 0 - 3072
Mono: Capture 1536 [50%] [23.00dB] [on]

View file

@ -0,0 +1,211 @@
Simple mixer control 'Master',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 31 [100%] [0.00dB] [on]
Front Right: Playback 31 [100%] [0.00dB] [on]
Simple mixer control 'Master Mono',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Master Surround',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-46.50dB] [off]
Front Right: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Headphone Jack Sense',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'PCM',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 23 [74%] [0.00dB] [on]
Front Right: Playback 23 [74%] [0.00dB] [on]
Simple mixer control 'Surround',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-46.50dB] [off]
Front Right: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Surround Jack Mode',0
Capabilities: enum
Items: 'Shared' 'Independent'
Item0: 'Shared'
Simple mixer control 'Center',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 31 [100%] [0.00dB] [off]
Simple mixer control 'LFE',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Line',0
Capabilities: pvolume pswitch cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Line Jack Sense',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'CD',0
Capabilities: pvolume pswitch cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-34.50dB] [off]
Front Left: Capture [on]
Front Right: Capture [on]
Simple mixer control 'Mic Boost (+20dB)',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'Mic Select',0
Capabilities: enum
Items: 'Mic1' 'Mic2'
Item0: 'Mic1'
Simple mixer control 'Video',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Phone',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Mono
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-34.50dB] [off]
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'IEC958',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 Output',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 Playback AC97-SPSA',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 3
Mono: 3 [100%]
Simple mixer control 'IEC958 Playback Source',0
Capabilities: enum
Items: 'AC-Link' 'A/D Converter'
Item0: 'AC-Link'
Simple mixer control 'Aux',0
Capabilities: pvolume pswitch cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Capture',0
Capabilities: cvolume cswitch
Capture channels: Front Left - Front Right
Limits: Capture 0 - 15
Front Left: Capture 0 [0%] [0.00dB] [on]
Front Right: Capture 0 [0%] [0.00dB] [on]
Simple mixer control 'Mix',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Mix Mono',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Channel Mode',0
Capabilities: enum
Items: '2ch' '4ch' '6ch'
Item0: '2ch'
Simple mixer control 'Downmix',0
Capabilities: enum
Items: 'Off' '6 -> 4' '6 -> 2'
Item0: 'Off'
Simple mixer control 'Exchange Front/Surround',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'External Amplifier',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]
Simple mixer control 'High Pass Filter Enable',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'Input Source Select',0
Capabilities: enum
Items: 'Input1' 'Input2'
Item0: 'Input1'
Simple mixer control 'Input Source Select',1
Capabilities: enum
Items: 'Input1' 'Input2'
Item0: 'Input1'
Simple mixer control 'Spread Front to Surround and Center/LFE',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'VIA DXS',0
Capabilities: pvolume
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 31 [100%] [-48.00dB]
Front Right: Playback 31 [100%] [-48.00dB]
Simple mixer control 'VIA DXS',1
Capabilities: pvolume
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 31 [100%] [-48.00dB]
Front Right: Playback 31 [100%] [-48.00dB]
Simple mixer control 'VIA DXS',2
Capabilities: pvolume
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 31 [100%] [-48.00dB]
Front Right: Playback 31 [100%] [-48.00dB]
Simple mixer control 'VIA DXS',3
Capabilities: pvolume
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 31 [100%] [-48.00dB]
Front Right: Playback 31 [100%] [-48.00dB]
Simple mixer control 'V_REFOUT Enable',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]

View file

@ -0,0 +1,160 @@
Simple mixer control 'Master',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 0 [0%] [-46.50dB] [off]
Front Right: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'PCM',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 31 [100%] [-48.00dB] [off]
Front Right: Playback 31 [100%] [-48.00dB] [off]
Simple mixer control 'Surround',0
Capabilities: pswitch
Playback channels: Front Left - Front Right
Mono:
Front Left: Playback [off]
Front Right: Playback [off]
Simple mixer control 'Surround Jack Mode',0
Capabilities: enum
Items: 'Shared' 'Independent'
Item0: 'Shared'
Simple mixer control 'Center',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 31 [100%] [0.00dB] [off]
Simple mixer control 'LFE',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 31
Mono: Playback 0 [0%] [-46.50dB] [off]
Simple mixer control 'Line',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'CD',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Mic',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [on]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [on]
Simple mixer control 'Mic Boost (+20dB)',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'Mic Select',0
Capabilities: enum
Items: 'Mic1' 'Mic2'
Item0: 'Mic1'
Simple mixer control 'Video',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Phone',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'IEC958',0
Capabilities: pswitch pswitch-joined cswitch cswitch-joined
Playback channels: Mono
Capture channels: Mono
Mono: Playback [off] Capture [off]
Simple mixer control 'IEC958 Capture Monitor',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 Capture Valid',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 Output',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control 'IEC958 Playback AC97-SPSA',0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 - 3
Mono: 3 [100%]
Simple mixer control 'IEC958 Playback Source',0
Capabilities: enum
Items: 'AC-Link' 'ADC' 'SPDIF-In'
Item0: 'AC-Link'
Simple mixer control 'PC Speaker',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 15
Mono: Playback 0 [0%] [-45.00dB] [off]
Simple mixer control 'Aux',0
Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
Capture exclusive group: 0
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31
Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
Simple mixer control 'Mono Output Select',0
Capabilities: enum
Items: 'Mix' 'Mic'
Item0: 'Mix'
Simple mixer control 'Capture',0
Capabilities: cvolume cswitch cswitch-joined
Capture channels: Front Left - Front Right
Limits: Capture 0 - 15
Front Left: Capture 0 [0%] [0.00dB] [on]
Front Right: Capture 0 [0%] [0.00dB] [on]
Simple mixer control 'Mix',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Mix Mono',0
Capabilities: cswitch cswitch-exclusive
Capture exclusive group: 0
Capture channels: Front Left - Front Right
Front Left: Capture [off]
Front Right: Capture [off]
Simple mixer control 'Channel Mode',0
Capabilities: enum
Items: '2ch' '4ch' '6ch'
Item0: '2ch'
Simple mixer control 'DAC Clock Source',0
Capabilities: enum
Items: 'AC-Link' 'SPDIF-In' 'Both'
Item0: 'AC-Link'
Simple mixer control 'External Amplifier',0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [on]
Simple mixer control 'Input Source Select',0
Capabilities: enum
Items: 'Input1' 'Input2'
Item0: 'Input1'
Simple mixer control 'Input Source Select',1
Capabilities: enum
Items: 'Input1' 'Input2'
Item0: 'Input1'

View file

@ -32,6 +32,10 @@
#include <modules/reserve-wrap.h> #include <modules/reserve-wrap.h>
#ifdef HAVE_UDEV
#include <modules/udev-util.h>
#endif
#include "alsa-util.h" #include "alsa-util.h"
#include "alsa-sink.h" #include "alsa-sink.h"
#include "alsa-source.h" #include "alsa-source.h"
@ -92,81 +96,53 @@ struct userdata {
char *device_id; char *device_id;
pa_card *card; pa_card *card;
pa_sink *sink;
pa_source *source;
pa_modargs *modargs; pa_modargs *modargs;
pa_hashmap *profiles; pa_alsa_profile_set *profile_set;
}; };
struct profile_data { struct profile_data {
const pa_alsa_profile_info *sink_profile, *source_profile; pa_alsa_profile *profile;
}; };
static void enumerate_cb( static void add_profiles(struct userdata *u, pa_hashmap *h) {
const pa_alsa_profile_info *sink, pa_alsa_profile *ap;
const pa_alsa_profile_info *source, void *state;
void *userdata) {
struct userdata *u = userdata; pa_assert(u);
char *t, *n; pa_assert(h);
pa_card_profile *p;
struct profile_data *d;
unsigned bonus = 0;
if (sink && source) { PA_HASHMAP_FOREACH(ap, u->profile_set->profiles, state) {
n = pa_sprintf_malloc("output-%s+input-%s", sink->name, source->name); struct profile_data *d;
t = pa_sprintf_malloc(_("Output %s + Input %s"), sink->description, _(source->description)); pa_card_profile *cp;
} else if (sink) { pa_alsa_mapping *m;
n = pa_sprintf_malloc("output-%s", sink->name); uint32_t idx;
t = pa_sprintf_malloc(_("Output %s"), _(sink->description));
} else { cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data));
pa_assert(source); cp->priority = ap->priority;
n = pa_sprintf_malloc("input-%s", source->name);
t = pa_sprintf_malloc(_("Input %s"), _(source->description)); if (ap->output_mappings) {
cp->n_sinks = pa_idxset_size(ap->output_mappings);
PA_IDXSET_FOREACH(m, ap->output_mappings, idx)
if (m->channel_map.channels > cp->max_sink_channels)
cp->max_sink_channels = m->channel_map.channels;
}
if (ap->input_mappings) {
cp->n_sources = pa_idxset_size(ap->input_mappings);
PA_IDXSET_FOREACH(m, ap->input_mappings, idx)
if (m->channel_map.channels > cp->max_source_channels)
cp->max_source_channels = m->channel_map.channels;
}
d = PA_CARD_PROFILE_DATA(cp);
d->profile = ap;
pa_hashmap_put(h, cp->name, cp);
} }
if (sink) {
if (pa_channel_map_equal(&sink->map, &u->core->default_channel_map))
bonus += 50000;
else if (sink->map.channels == u->core->default_channel_map.channels)
bonus += 40000;
}
if (source) {
if (pa_channel_map_equal(&source->map, &u->core->default_channel_map))
bonus += 30000;
else if (source->map.channels == u->core->default_channel_map.channels)
bonus += 20000;
}
pa_log_info("Found profile '%s'", t);
p = pa_card_profile_new(n, t, sizeof(struct profile_data));
pa_xfree(t);
pa_xfree(n);
p->priority =
(sink ? sink->priority : 0) * 100 +
(source ? source->priority : 0) +
bonus;
p->n_sinks = !!sink;
p->n_sources = !!source;
if (sink)
p->max_sink_channels = sink->map.channels;
if (source)
p->max_source_channels = source->map.channels;
d = PA_CARD_PROFILE_DATA(p);
d->sink_profile = sink;
d->source_profile = source;
pa_hashmap_put(u->profiles, p->name, p);
} }
static void add_disabled_profile(pa_hashmap *profiles) { static void add_disabled_profile(pa_hashmap *profiles) {
@ -176,7 +152,7 @@ static void add_disabled_profile(pa_hashmap *profiles) {
p = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data)); p = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data));
d = PA_CARD_PROFILE_DATA(p); d = PA_CARD_PROFILE_DATA(p);
d->sink_profile = d->source_profile = NULL; d->profile = NULL;
pa_hashmap_put(profiles, p->name, p); pa_hashmap_put(profiles, p->name, p);
} }
@ -184,6 +160,9 @@ static void add_disabled_profile(pa_hashmap *profiles) {
static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
struct userdata *u; struct userdata *u;
struct profile_data *nd, *od; struct profile_data *nd, *od;
uint32_t idx;
pa_alsa_mapping *am;
pa_queue *sink_inputs = NULL, *source_outputs = NULL;
pa_assert(c); pa_assert(c);
pa_assert(new_profile); pa_assert(new_profile);
@ -192,67 +171,85 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
nd = PA_CARD_PROFILE_DATA(new_profile); nd = PA_CARD_PROFILE_DATA(new_profile);
od = PA_CARD_PROFILE_DATA(c->active_profile); od = PA_CARD_PROFILE_DATA(c->active_profile);
if (od->sink_profile != nd->sink_profile) { if (od->profile && od->profile->output_mappings)
pa_queue *inputs = NULL; PA_IDXSET_FOREACH(am, od->profile->output_mappings, idx) {
if (!am->sink)
continue;
if (u->sink) { if (nd->profile &&
if (nd->sink_profile) nd->profile->output_mappings &&
inputs = pa_sink_move_all_start(u->sink); pa_idxset_get_by_data(nd->profile->output_mappings, am, NULL))
continue;
pa_alsa_sink_free(u->sink); sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs);
u->sink = NULL; pa_alsa_sink_free(am->sink);
am->sink = NULL;
} }
if (nd->sink_profile) { if (od->profile && od->profile->input_mappings)
u->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, nd->sink_profile); PA_IDXSET_FOREACH(am, od->profile->input_mappings, idx) {
if (!am->source)
continue;
if (inputs) { if (nd->profile &&
if (u->sink) nd->profile->input_mappings &&
pa_sink_move_all_finish(u->sink, inputs, FALSE); pa_idxset_get_by_data(nd->profile->input_mappings, am, NULL))
else continue;
pa_sink_move_all_fail(inputs);
source_outputs = pa_source_move_all_start(am->source, source_outputs);
pa_alsa_source_free(am->source);
am->source = NULL;
}
if (nd->profile && nd->profile->output_mappings)
PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) {
if (!am->sink)
am->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, am);
if (sink_inputs && am->sink) {
pa_sink_move_all_finish(am->sink, sink_inputs, FALSE);
sink_inputs = NULL;
} }
} }
}
if (od->source_profile != nd->source_profile) { if (nd->profile && nd->profile->input_mappings)
pa_queue *outputs = NULL; PA_IDXSET_FOREACH(am, nd->profile->input_mappings, idx) {
if (u->source) { if (!am->source)
if (nd->source_profile) am->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, am);
outputs = pa_source_move_all_start(u->source);
pa_alsa_source_free(u->source); if (source_outputs && am->source) {
u->source = NULL; pa_source_move_all_finish(am->source, source_outputs, FALSE);
} source_outputs = NULL;
if (nd->source_profile) {
u->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, nd->source_profile);
if (outputs) {
if (u->source)
pa_source_move_all_finish(u->source, outputs, FALSE);
else
pa_source_move_all_fail(outputs);
} }
} }
}
if (sink_inputs)
pa_sink_move_all_fail(sink_inputs);
if (source_outputs)
pa_source_move_all_fail(source_outputs);
return 0; return 0;
} }
static void init_profile(struct userdata *u) { static void init_profile(struct userdata *u) {
uint32_t idx;
pa_alsa_mapping *am;
struct profile_data *d; struct profile_data *d;
pa_assert(u); pa_assert(u);
d = PA_CARD_PROFILE_DATA(u->card->active_profile); d = PA_CARD_PROFILE_DATA(u->card->active_profile);
if (d->sink_profile) if (d->profile && d->profile->output_mappings)
u->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, d->sink_profile); PA_IDXSET_FOREACH(am, d->profile->output_mappings, idx)
am->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, am);
if (d->source_profile) if (d->profile && d->profile->input_mappings)
u->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, d->source_profile); PA_IDXSET_FOREACH(am, d->profile->input_mappings, idx)
am->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, am);
} }
static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) { static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) {
@ -286,9 +283,9 @@ int pa__init(pa_module *m) {
pa_modargs *ma; pa_modargs *ma;
int alsa_card_index; int alsa_card_index;
struct userdata *u; struct userdata *u;
char rname[32];
pa_reserve_wrapper *reserve = NULL; pa_reserve_wrapper *reserve = NULL;
const char *description; const char *description;
char *fn = NULL;
pa_alsa_redirect_errors_inc(); pa_alsa_redirect_errors_inc();
snd_config_update_free_global(); snd_config_update_free_global();
@ -300,13 +297,10 @@ int pa__init(pa_module *m) {
goto fail; goto fail;
} }
m->userdata = u = pa_xnew(struct userdata, 1); m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core; u->core = m->core;
u->module = m; u->module = m;
u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID)); u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
u->card = NULL;
u->sink = NULL;
u->source = NULL;
u->modargs = ma; u->modargs = ma;
if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) { if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
@ -314,16 +308,36 @@ int pa__init(pa_module *m) {
goto fail; goto fail;
} }
pa_snprintf(rname, sizeof(rname), "Audio%i", alsa_card_index); if (!pa_in_system_mode()) {
char *rname;
if (!pa_in_system_mode()) if ((rname = pa_alsa_get_reserve_name(u->device_id))) {
if (!(reserve = pa_reserve_wrapper_get(m->core, rname))) reserve = pa_reserve_wrapper_get(m->core, rname);
goto fail; pa_xfree(rname);
if (!reserve)
goto fail;
}
}
#ifdef HAVE_UDEV
fn = pa_udev_get_property(alsa_card_index, "PULSE_PROFILE_SET");
#endif
u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
pa_xfree(fn);
if (!u->profile_set)
goto fail;
pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec);
pa_card_new_data_init(&data); pa_card_new_data_init(&data);
data.driver = __FILE__; data.driver = __FILE__;
data.module = m; data.module = m;
pa_alsa_init_proplist_card(m->core, data.proplist, alsa_card_index); pa_alsa_init_proplist_card(m->core, data.proplist, alsa_card_index);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id);
pa_alsa_init_description(data.proplist); pa_alsa_init_description(data.proplist);
set_card_name(&data, ma, u->device_id); set_card_name(&data, ma, u->device_id);
@ -332,11 +346,8 @@ int pa__init(pa_module *m) {
if ((description = pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION))) if ((description = pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION)))
pa_reserve_wrapper_set_application_device_name(reserve, description); pa_reserve_wrapper_set_application_device_name(reserve, description);
u->profiles = data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, u) < 0) { add_profiles(u, data.profiles);
pa_card_new_data_done(&data);
goto fail;
}
if (pa_hashmap_isempty(data.profiles)) { if (pa_hashmap_isempty(data.profiles)) {
pa_log("Failed to find a working profile."); pa_log("Failed to find a working profile.");
@ -379,13 +390,22 @@ fail:
int pa__get_n_used(pa_module *m) { int pa__get_n_used(pa_module *m) {
struct userdata *u; struct userdata *u;
int n = 0;
uint32_t idx;
pa_sink *sink;
pa_source *source;
pa_assert(m); pa_assert(m);
pa_assert_se(u = m->userdata); pa_assert_se(u = m->userdata);
pa_assert(u->card);
return PA_IDXSET_FOREACH(sink, u->card->sinks, idx)
(u->sink ? pa_sink_linked_by(u->sink) : 0) + n += pa_sink_linked_by(sink);
(u->source ? pa_source_linked_by(u->source) : 0);
PA_IDXSET_FOREACH(source, u->card->sources, idx)
n += pa_source_linked_by(source);
return n;
} }
void pa__done(pa_module*m) { void pa__done(pa_module*m) {
@ -396,11 +416,19 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata)) if (!(u = m->userdata))
goto finish; goto finish;
if (u->sink) if (u->card && u->card->sinks) {
pa_alsa_sink_free(u->sink); pa_sink *s;
if (u->source) while ((s = pa_idxset_steal_first(u->card->sinks, NULL)))
pa_alsa_source_free(u->source); pa_alsa_sink_free(s);
}
if (u->card && u->card->sources) {
pa_source *s;
while ((s = pa_idxset_steal_first(u->card->sources, NULL)))
pa_alsa_source_free(s);
}
if (u->card) if (u->card)
pa_card_free(u->card); pa_card_free(u->card);
@ -408,6 +436,9 @@ void pa__done(pa_module*m) {
if (u->modargs) if (u->modargs)
pa_modargs_free(u->modargs); pa_modargs_free(u->modargs);
if (u->profile_set)
pa_alsa_profile_set_free(u->profile_set);
pa_xfree(u->device_id); pa_xfree(u->device_id);
pa_xfree(u); pa_xfree(u);

View file

@ -1443,12 +1443,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) { if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_sink_volume_changed(u->sink, &v); pa_sink_volume_changed(u->sink, &v, TRUE);
} else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) { } else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_source_volume_changed(u->source, &v); pa_source_volume_changed(u->source, &v, TRUE);
} }
} }
} }
@ -1622,6 +1622,8 @@ static int add_sink(struct userdata *u) {
data.module = u->module; data.module = u->module;
pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
if (u->profile == PROFILE_HSP)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
data.card = u->card; data.card = u->card;
data.name = get_name("sink", u->modargs, u->address, &b); data.name = get_name("sink", u->modargs, u->address, &b);
data.namereg_fail = b; data.namereg_fail = b;
@ -1680,6 +1682,8 @@ static int add_source(struct userdata *u) {
data.module = u->module; data.module = u->module;
pa_source_new_data_set_sample_spec(&data, &u->sample_spec); pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "hsp"); pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "hsp");
if (u->profile == PROFILE_HSP)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
data.card = u->card; data.card = u->card;
data.name = get_name("source", u->modargs, u->address, &b); data.name = get_name("source", u->modargs, u->address, &b);
data.namereg_fail = b; data.namereg_fail = b;
@ -1916,7 +1920,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
if (!(device = pa_bluetooth_discovery_get_by_path(u->discovery, u->path))) { if (!(device = pa_bluetooth_discovery_get_by_path(u->discovery, u->path))) {
pa_log_error("Failed to get device object."); pa_log_error("Failed to get device object.");
return -1; return -PA_ERR_IO;
} }
/* The state signal is sent by bluez, so it is racy to check /* The state signal is sent by bluez, so it is racy to check
@ -1926,15 +1930,15 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
module will be unloaded. */ module will be unloaded. */
if (device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) { if (device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) {
pa_log_warn("HSP is not connected, refused to switch profile"); pa_log_warn("HSP is not connected, refused to switch profile");
return -1; return -PA_ERR_IO;
} }
else if (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) { else if (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) {
pa_log_warn("A2DP is not connected, refused to switch profile"); pa_log_warn("A2DP is not connected, refused to switch profile");
return -1; return -PA_ERR_IO;
} }
if (u->sink) { if (u->sink) {
inputs = pa_sink_move_all_start(u->sink); inputs = pa_sink_move_all_start(u->sink, NULL);
#ifdef NOKIA #ifdef NOKIA
if (!USE_SCO_OVER_PCM(u)) if (!USE_SCO_OVER_PCM(u))
#endif #endif
@ -1942,7 +1946,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
} }
if (u->source) { if (u->source) {
outputs = pa_source_move_all_start(u->source); outputs = pa_source_move_all_start(u->source, NULL);
#ifdef NOKIA #ifdef NOKIA
if (!USE_SCO_OVER_PCM(u)) if (!USE_SCO_OVER_PCM(u))
#endif #endif

View file

@ -109,7 +109,7 @@ static void update_volume(struct userdata *u) {
} }
pa_log_info("Found %u BT devices, unmuting.", u->n_found); pa_log_info("Found %u BT devices, unmuting.", u->n_found);
pa_sink_set_mute(s, FALSE); pa_sink_set_mute(s, FALSE, FALSE);
} else if (!u->muted && (u->n_found+u->n_unknown) <= 0) { } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
pa_sink *s; pa_sink *s;
@ -122,7 +122,7 @@ static void update_volume(struct userdata *u) {
} }
pa_log_info("No BT devices found, muting."); pa_log_info("No BT devices found, muting.");
pa_sink_set_mute(s, TRUE); pa_sink_set_mute(s, TRUE, FALSE);
} else } else
pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown); pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);

View file

@ -27,6 +27,7 @@
#endif #endif
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulse/i18n.h>
#include <pulsecore/core-error.h> #include <pulsecore/core-error.h>
#include <pulsecore/namereg.h> #include <pulsecore/namereg.h>
@ -45,20 +46,20 @@
#include "ladspa.h" #include "ladspa.h"
PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Virtual LADSPA sink"); PA_MODULE_DESCRIPTION(_("Virtual LADSPA sink"));
PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE( PA_MODULE_USAGE(
"sink_name=<name for the sink> " _("sink_name=<name for the sink> "
"sink_properties=<properties for the sink> " "sink_properties=<properties for the sink> "
"master=<name of sink to remap> " "master=<name of sink to filter> "
"format=<sample format> " "format=<sample format> "
"rate=<sample rate> " "rate=<sample rate> "
"channels=<number of channels> " "channels=<number of channels> "
"channel_map=<channel map> " "channel_map=<channel map> "
"plugin=<ladspa plugin name> " "plugin=<ladspa plugin name> "
"label=<ladspa plugin label> " "label=<ladspa plugin label> "
"control=<comma seperated list of input control values>"); "control=<comma seperated list of input control values>"));
#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)

View file

@ -112,7 +112,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
volchange = RESET; volchange = RESET;
if (volchange == INVALID) if (volchange == INVALID)
pa_log_warn("Recieved unknown IR code '%s'", name); pa_log_warn("Received unknown IR code '%s'", name);
else { else {
pa_sink *s; pa_sink *s;
@ -133,7 +133,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MAX; cv.values[i] = PA_VOLUME_MAX;
} }
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break; break;
case DOWN: case DOWN:
@ -144,20 +144,20 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MUTED; cv.values[i] = PA_VOLUME_MUTED;
} }
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break; break;
case MUTE: case MUTE:
pa_sink_set_mute(s, TRUE); pa_sink_set_mute(s, TRUE, TRUE);
break; break;
case RESET: case RESET:
pa_sink_set_mute(s, FALSE); pa_sink_set_mute(s, FALSE, TRUE);
break; break;
case MUTE_TOGGLE: case MUTE_TOGGLE:
pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE)); pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE);
break; break;
case INVALID: case INVALID:

View file

@ -115,7 +115,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MAX; cv.values[i] = PA_VOLUME_MAX;
} }
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break; break;
case DOWN: case DOWN:
@ -126,12 +126,12 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MUTED; cv.values[i] = PA_VOLUME_MUTED;
} }
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break; break;
case MUTE_TOGGLE: case MUTE_TOGGLE:
pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE)); pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE);
break; break;
case INVALID: case INVALID:

View file

@ -730,7 +730,7 @@ static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
} }
if (channel != u->channel) { if (channel != u->channel) {
pa_log("Recieved data for invalid channel"); pa_log("Received data for invalid channel");
goto fail; goto fail;
} }
@ -1157,10 +1157,10 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_cvolume_equal(&volume, &u->sink->virtual_volume)) pa_cvolume_equal(&volume, &u->sink->virtual_volume))
return; return;
pa_sink_volume_changed(u->sink, &volume); pa_sink_volume_changed(u->sink, &volume, FALSE);
if (u->version >= 11) if (u->version >= 11)
pa_sink_mute_changed(u->sink, mute); pa_sink_mute_changed(u->sink, mute, FALSE);
return; return;
@ -1675,7 +1675,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_assert(u); pa_assert(u);
if (channel != u->channel) { if (channel != u->channel) {
pa_log("Recieved memory block on bad channel."); pa_log("Received memory block on bad channel.");
pa_module_unload_request(u->module, TRUE); pa_module_unload_request(u->module, TRUE);
return; return;
} }

View file

@ -583,6 +583,7 @@ int pa__init(pa_module*m) {
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_sample_spec(&data, &ss);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music");
if ((desc = pa_modargs_get_value(ma, "description", NULL))) if ((desc = pa_modargs_get_value(ma, "description", NULL)))
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, desc); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, desc);
else else

View file

@ -62,7 +62,7 @@
#include "sap.h" #include "sap.h"
PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Recieve data from a network via RTP/SAP/SDP"); PA_MODULE_DESCRIPTION("Receive data from a network via RTP/SAP/SDP");
PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE( PA_MODULE_USAGE(

View file

@ -58,7 +58,7 @@ static int read_id(struct udev_device *d, const char *n) {
return u; return u;
} }
int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) { int pa_udev_get_info(int card_idx, pa_proplist *p) {
int r = -1; int r = -1;
struct udev *udev; struct udev *udev;
struct udev_device *card = NULL; struct udev_device *card = NULL;
@ -66,7 +66,6 @@ int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) {
const char *v; const char *v;
int id; int id;
pa_assert(core);
pa_assert(p); pa_assert(p);
pa_assert(card_idx >= 0); pa_assert(card_idx >= 0);
@ -84,6 +83,19 @@ int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) {
goto finish; goto finish;
} }
if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH))
if (((v = udev_device_get_property_value(card, "ID_PATH")) && *v) ||
(v = udev_device_get_devpath(card)))
pa_proplist_sets(p, PA_PROP_DEVICE_BUS_PATH, v);
if (!pa_proplist_contains(p, "sysfs.path"))
if ((v = udev_device_get_devpath(card)))
pa_proplist_sets(p, "sysfs.path", v);
if (!pa_proplist_contains(p, "udev.id"))
if ((v = udev_device_get_property_value(card, "ID_ID")) && *v)
pa_proplist_sets(p, "udev.id", v);
if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS)) if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS))
if ((v = udev_device_get_property_value(card, "ID_BUS")) && *v) if ((v = udev_device_get_property_value(card, "ID_BUS")) && *v)
pa_proplist_sets(p, PA_PROP_DEVICE_BUS, v); pa_proplist_sets(p, PA_PROP_DEVICE_BUS, v);
@ -114,15 +126,15 @@ int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) {
if ((v = udev_device_get_property_value(card, "ID_SERIAL")) && *v) if ((v = udev_device_get_property_value(card, "ID_SERIAL")) && *v)
pa_proplist_sets(p, PA_PROP_DEVICE_SERIAL, v); pa_proplist_sets(p, PA_PROP_DEVICE_SERIAL, v);
if (!pa_proplist_contains(p, PA_PROP_DEVICE_CLASS))
if ((v = udev_device_get_property_value(card, "SOUND_CLASS")) && *v)
pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, v);
if (!pa_proplist_contains(p, PA_PROP_DEVICE_FORM_FACTOR)) if (!pa_proplist_contains(p, PA_PROP_DEVICE_FORM_FACTOR))
if ((v = udev_device_get_property_value(card, "SOUND_FORM_FACTOR")) && *v) if ((v = udev_device_get_property_value(card, "SOUND_FORM_FACTOR")) && *v)
pa_proplist_sets(p, PA_PROP_DEVICE_FORM_FACTOR, v); pa_proplist_sets(p, PA_PROP_DEVICE_FORM_FACTOR, v);
if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH)) /* This is normaly not set by the udev rules but may be useful to
if ((v = udev_device_get_devpath(card)))
pa_proplist_sets(p, PA_PROP_DEVICE_BUS_PATH, v);
/* This is normaly not set by th udev rules but may be useful to
* allow administrators to overwrite the device description.*/ * allow administrators to overwrite the device description.*/
if (!pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION)) if (!pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
if ((v = udev_device_get_property_value(card, "SOUND_DESCRIPTION")) && *v) if ((v = udev_device_get_property_value(card, "SOUND_DESCRIPTION")) && *v)
@ -140,3 +152,40 @@ finish:
return r; return r;
} }
char* pa_udev_get_property(int card_idx, const char *name) {
struct udev *udev;
struct udev_device *card = NULL;
char *t, *r = NULL;
const char *v;
pa_assert(card_idx >= 0);
pa_assert(name);
if (!(udev = udev_new())) {
pa_log_error("Failed to allocate udev context.");
goto finish;
}
t = pa_sprintf_malloc("%s/class/sound/card%i", udev_get_sys_path(udev), card_idx);
card = udev_device_new_from_syspath(udev, t);
pa_xfree(t);
if (!card) {
pa_log_error("Failed to get card object.");
goto finish;
}
if ((v = udev_device_get_property_value(card, name)) && *v)
r = pa_xstrdup(v);
finish:
if (card)
udev_device_unref(card);
if (udev)
udev_unref(udev);
return r;
}

View file

@ -25,6 +25,7 @@
#include <pulsecore/core.h> #include <pulsecore/core.h>
int pa_udev_get_info(pa_core *core, pa_proplist *p, int card); int pa_udev_get_info(int card_idx, pa_proplist *p);
char* pa_udev_get_property(int card_idx, const char *name);
#endif #endif

View file

@ -30,9 +30,11 @@
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulse/i18n.h> #include <pulse/i18n.h>
#include <pulsecore/core-util.h> #include <pulsecore/core-util.h>
#include <pulsecore/macro.h> #include <pulsecore/macro.h>
#include <pulsecore/bitset.h> #include <pulsecore/bitset.h>
#include <pulsecore/sample-util.h>
#include "channelmap.h" #include "channelmap.h"
@ -491,6 +493,27 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
return s; return s;
} }
pa_channel_position_t pa_channel_position_from_string(const char *p) {
pa_channel_position_t i;
pa_assert(p);
/* Some special aliases */
if (pa_streq(p, "left"))
return PA_CHANNEL_POSITION_LEFT;
else if (pa_streq(p, "right"))
return PA_CHANNEL_POSITION_RIGHT;
else if (pa_streq(p, "center"))
return PA_CHANNEL_POSITION_CENTER;
else if (pa_streq(p, "subwoofer"))
return PA_CHANNEL_POSITION_SUBWOOFER;
for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
if (pa_streq(p, table[i]))
return i;
return PA_CHANNEL_POSITION_INVALID;
}
pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
const char *state; const char *state;
pa_channel_map map; pa_channel_map map;
@ -559,36 +582,19 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
map.channels = 0; map.channels = 0;
while ((p = pa_split(s, ",", &state))) { while ((p = pa_split(s, ",", &state))) {
pa_channel_position_t f;
if (map.channels >= PA_CHANNELS_MAX) { if (map.channels >= PA_CHANNELS_MAX) {
pa_xfree(p); pa_xfree(p);
return NULL; return NULL;
} }
/* Some special aliases */ if ((f = pa_channel_position_from_string(p)) == PA_CHANNEL_POSITION_INVALID) {
if (pa_streq(p, "left")) pa_xfree(p);
map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT; return NULL;
else if (pa_streq(p, "right"))
map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT;
else if (pa_streq(p, "center"))
map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER;
else if (pa_streq(p, "subwoofer"))
map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER;
else {
pa_channel_position_t i;
for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
if (strcmp(p, table[i]) == 0) {
map.map[map.channels++] = i;
break;
}
if (i >= PA_CHANNEL_POSITION_MAX) {
pa_xfree(p);
return NULL;
}
} }
map.map[map.channels++] = f;
pa_xfree(p); pa_xfree(p);
} }
@ -627,8 +633,7 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s
} }
int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
pa_bitset_t in_a[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; pa_channel_position_mask_t am, bm;
unsigned i;
pa_assert(a); pa_assert(a);
pa_assert(b); pa_assert(b);
@ -636,98 +641,36 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
pa_return_val_if_fail(pa_channel_map_valid(a), 0); pa_return_val_if_fail(pa_channel_map_valid(a), 0);
pa_return_val_if_fail(pa_channel_map_valid(b), 0); pa_return_val_if_fail(pa_channel_map_valid(b), 0);
memset(in_a, 0, sizeof(in_a)); am = pa_channel_map_mask(a);
bm = pa_channel_map_mask(b);
for (i = 0; i < a->channels; i++) return (bm & am) == bm;
pa_bitset_set(in_a, a->map[i], TRUE);
for (i = 0; i < b->channels; i++)
if (!pa_bitset_get(in_a, b->map[i]))
return 0;
return 1;
} }
int pa_channel_map_can_balance(const pa_channel_map *map) { int pa_channel_map_can_balance(const pa_channel_map *map) {
unsigned c; pa_channel_position_mask_t m;
pa_bool_t left = FALSE, right = FALSE;
pa_assert(map); pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0); pa_return_val_if_fail(pa_channel_map_valid(map), 0);
for (c = 0; c < map->channels; c++) { m = pa_channel_map_mask(map);
switch (map->map[c]) { return
case PA_CHANNEL_POSITION_LEFT: (PA_CHANNEL_POSITION_MASK_LEFT & m) &&
case PA_CHANNEL_POSITION_REAR_LEFT: (PA_CHANNEL_POSITION_MASK_RIGHT & m);
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
case PA_CHANNEL_POSITION_SIDE_LEFT:
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
left = TRUE;
break;
case PA_CHANNEL_POSITION_RIGHT:
case PA_CHANNEL_POSITION_REAR_RIGHT:
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
case PA_CHANNEL_POSITION_SIDE_RIGHT:
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
right = TRUE;
break;
default:
;
}
if (left && right)
return 1;
}
return 0;
} }
int pa_channel_map_can_fade(const pa_channel_map *map) { int pa_channel_map_can_fade(const pa_channel_map *map) {
unsigned c; pa_channel_position_mask_t m;
pa_bool_t front = FALSE, rear = FALSE;
pa_assert(map); pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0); pa_return_val_if_fail(pa_channel_map_valid(map), 0);
for (c = 0; c < map->channels; c++) { m = pa_channel_map_mask(map);
switch (map->map[c]) { return
case PA_CHANNEL_POSITION_FRONT_LEFT: (PA_CHANNEL_POSITION_MASK_FRONT & m) &&
case PA_CHANNEL_POSITION_FRONT_RIGHT: (PA_CHANNEL_POSITION_MASK_REAR & m);
case PA_CHANNEL_POSITION_FRONT_CENTER:
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
case PA_CHANNEL_POSITION_TOP_FRONT_CENTER:
front = TRUE;
break;
case PA_CHANNEL_POSITION_REAR_LEFT:
case PA_CHANNEL_POSITION_REAR_RIGHT:
case PA_CHANNEL_POSITION_REAR_CENTER:
case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
case PA_CHANNEL_POSITION_TOP_REAR_CENTER:
rear = TRUE;
break;
default:
;
}
if (front && rear)
return 1;
}
return 0;
} }
const char* pa_channel_map_to_name(const pa_channel_map *map) { const char* pa_channel_map_to_name(const pa_channel_map *map) {

View file

@ -209,7 +209,7 @@ typedef enum pa_channel_position {
typedef uint64_t pa_channel_position_mask_t; typedef uint64_t pa_channel_position_mask_t;
/** Makes a bit mask from a channel position. \since 0.9.16 */ /** Makes a bit mask from a channel position. \since 0.9.16 */
#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1 << (f))) #define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
/** A list of channel mapping definitions for pa_channel_map_init_auto() */ /** A list of channel mapping definitions for pa_channel_map_init_auto() */
typedef enum pa_channel_map_def { typedef enum pa_channel_map_def {
@ -282,6 +282,9 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels,
/** Return a text label for the specified channel position */ /** Return a text label for the specified channel position */
const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE; const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE;
/* The inverse of pa_channel_position_to_string(). \since 0.9.16 */
pa_channel_position_t pa_channel_position_from_string(const char *s) PA_GCC_PURE;
/** Return a human readable text label for the specified channel position. \since 0.9.7 */ /** Return a human readable text label for the specified channel position. \since 0.9.7 */
const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos); const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);

View file

@ -393,6 +393,7 @@ enum {
PA_ERR_OBSOLETE, /**< Obsolete functionality. \since 0.9.15 */ PA_ERR_OBSOLETE, /**< Obsolete functionality. \since 0.9.15 */
PA_ERR_NOTIMPLEMENTED, /**< Missing implementation. \since 0.9.15 */ PA_ERR_NOTIMPLEMENTED, /**< Missing implementation. \since 0.9.15 */
PA_ERR_FORKED, /**< The caller forked without calling execve() and tried to reuse the context. \since 0.9.15 */ PA_ERR_FORKED, /**< The caller forked without calling execve() and tried to reuse the context. \since 0.9.15 */
PA_ERR_IO, /**< An IO error happened. \since 0.9.16 */
PA_ERR_MAX /**< Not really an error but the first invalid error code */ PA_ERR_MAX /**< Not really an error but the first invalid error code */
}; };

View file

@ -49,7 +49,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, uint32_t t
pa_assert(o); pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1); pa_assert(PA_REFCNT_VALUE(o) >= 1);
memset(&i, 0, sizeof(i)); pa_zero(i);
if (!o->context) if (!o->context)
goto finish; goto finish;
@ -93,7 +93,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,
pa_assert(o); pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1); pa_assert(PA_REFCNT_VALUE(o) >= 1);
memset(&i, 0, sizeof(i)); pa_zero(i);
if (!o->context) if (!o->context)
goto finish; goto finish;
@ -161,8 +161,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
pa_bool_t mute; pa_bool_t mute;
uint32_t flags; uint32_t flags;
uint32_t state; uint32_t state;
uint32_t j;
const char *ap = NULL;
memset(&i, 0, sizeof(i)); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
i.base_volume = PA_VOLUME_NORM; i.base_volume = PA_VOLUME_NORM;
i.n_volume_steps = PA_VOLUME_NORM+1; i.n_volume_steps = PA_VOLUME_NORM+1;
@ -190,13 +192,53 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
(pa_tagstruct_get_volume(t, &i.base_volume) < 0 || (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
pa_tagstruct_getu32(t, &state) < 0 || pa_tagstruct_getu32(t, &state) < 0 ||
pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 || pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 ||
pa_tagstruct_getu32(t, &i.card) < 0))) { pa_tagstruct_getu32(t, &i.card) < 0)) ||
(o->context->version >= 16 &&
(pa_tagstruct_getu32(t, &i.n_ports)))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist); pa_proplist_free(i.proplist);
goto finish; goto finish;
} }
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports);
for (j = 0; j < i.n_ports; j++) {
if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 ||
pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports);
pa_xfree(i.ports[0]);
pa_proplist_free(i.proplist);
goto finish;
}
i.ports[j] = &i.ports[0][j];
}
i.ports[j] = NULL;
}
if (pa_tagstruct_gets(t, &ap) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
if (ap) {
for (j = 0; j < i.n_ports; j++)
if (pa_streq(i.ports[j]->name, ap)) {
i.active_port = i.ports[j];
break;
}
}
i.mute = (int) mute; i.mute = (int) mute;
i.flags = (pa_sink_flags_t) flags; i.flags = (pa_sink_flags_t) flags;
i.state = (pa_sink_state_t) state; i.state = (pa_sink_state_t) state;
@ -271,6 +313,56 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name,
return o; return o;
} }
pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_PORT, &tag);
pa_tagstruct_putu32(t, idx);
pa_tagstruct_puts(t, NULL);
pa_tagstruct_puts(t, port);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char *name, const char*port, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_PORT, &tag);
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, name);
pa_tagstruct_puts(t, port);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
/*** Source info ***/ /*** Source info ***/
static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@ -296,8 +388,10 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
pa_bool_t mute; pa_bool_t mute;
uint32_t flags; uint32_t flags;
uint32_t state; uint32_t state;
unsigned j;
const char *ap;
memset(&i, 0, sizeof(i)); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
i.base_volume = PA_VOLUME_NORM; i.base_volume = PA_VOLUME_NORM;
i.n_volume_steps = PA_VOLUME_NORM+1; i.n_volume_steps = PA_VOLUME_NORM+1;
@ -325,13 +419,53 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
(pa_tagstruct_get_volume(t, &i.base_volume) < 0 || (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
pa_tagstruct_getu32(t, &state) < 0 || pa_tagstruct_getu32(t, &state) < 0 ||
pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 || pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 ||
pa_tagstruct_getu32(t, &i.card) < 0))) { pa_tagstruct_getu32(t, &i.card) < 0)) ||
(o->context->version >= 16 &&
(pa_tagstruct_getu32(t, &i.n_ports)))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist); pa_proplist_free(i.proplist);
goto finish; goto finish;
} }
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports);
for (j = 0; j < i.n_ports; j++) {
if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 ||
pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
i.ports[j] = &i.ports[0][j];
}
i.ports[j] = NULL;
}
if (pa_tagstruct_gets(t, &ap) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
if (ap) {
for (j = 0; j < i.n_ports; j++)
if (pa_streq(i.ports[j]->name, ap)) {
i.active_port = i.ports[j];
break;
}
}
i.mute = (int) mute; i.mute = (int) mute;
i.flags = (pa_source_flags_t) flags; i.flags = (pa_source_flags_t) flags;
i.state = (pa_source_state_t) state; i.state = (pa_source_state_t) state;
@ -406,6 +540,56 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name
return o; return o;
} }
pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_PORT, &tag);
pa_tagstruct_putu32(t, idx);
pa_tagstruct_puts(t, NULL);
pa_tagstruct_puts(t, port);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char *name, const char*port, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_PORT, &tag);
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, name);
pa_tagstruct_puts(t, port);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
/*** Client info ***/ /*** Client info ***/
static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@ -429,7 +613,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) { while (!pa_tagstruct_eof(t)) {
pa_client_info i; pa_client_info i;
memset(&i, 0, sizeof(i)); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 || if (pa_tagstruct_getu32(t, &i.index) < 0 ||
@ -514,7 +698,7 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
uint32_t j; uint32_t j;
const char*ap; const char*ap;
memset(&i, 0, sizeof(i)); pa_zero(i);
if (pa_tagstruct_getu32(t, &i.index) < 0 || if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.name) < 0 ||
@ -527,7 +711,7 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
} }
if (i.n_profiles > 0) { if (i.n_profiles > 0) {
i.profiles = pa_xnew(pa_card_profile_info, i.n_profiles+1); i.profiles = pa_xnew0(pa_card_profile_info, i.n_profiles+1);
for (j = 0; j < i.n_profiles; j++) { for (j = 0; j < i.n_profiles; j++) {
@ -715,7 +899,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
pa_module_info i; pa_module_info i;
pa_bool_t auto_unload = FALSE; pa_bool_t auto_unload = FALSE;
memset(&i, 0, sizeof(i)); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 || if (pa_tagstruct_getu32(t, &i.index) < 0 ||
@ -800,7 +984,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_sink_input_info i; pa_sink_input_info i;
pa_bool_t mute = FALSE; pa_bool_t mute = FALSE;
memset(&i, 0, sizeof(i)); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 || if (pa_tagstruct_getu32(t, &i.index) < 0 ||
@ -894,7 +1078,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
while (!pa_tagstruct_eof(t)) { while (!pa_tagstruct_eof(t)) {
pa_source_output_info i; pa_source_output_info i;
memset(&i, 0, sizeof(i)); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 || if (pa_tagstruct_getu32(t, &i.index) < 0 ||
@ -1236,7 +1420,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_sample_info i; pa_sample_info i;
pa_bool_t lazy = FALSE; pa_bool_t lazy = FALSE;
memset(&i, 0, sizeof(i)); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 || if (pa_tagstruct_getu32(t, &i.index) < 0 ||

View file

@ -193,6 +193,15 @@ PA_C_DECL_BEGIN
/** @{ \name Sinks */ /** @{ \name Sinks */
/** Stores information about a specific port of a sink. Please
* note that this structure can be extended as part of evolutionary
* API updates at any time in any new release. \since 0.9.16 */
typedef struct pa_sink_port_info {
const char *name; /**< Name of this port */
const char *description; /**< Description of this port */
uint32_t priority; /**< The higher this value is the more useful this port is as a default */
} pa_sink_port_info;
/** Stores information about sinks. Please note that this structure /** Stores information about sinks. Please note that this structure
* can be extended as part of evolutionary API updates at any time in * can be extended as part of evolutionary API updates at any time in
* any new release. */ * any new release. */
@ -216,6 +225,9 @@ typedef struct pa_sink_info {
pa_sink_state_t state; /**< State \since 0.9.15 */ pa_sink_state_t state; /**< State \since 0.9.15 */
uint32_t n_volume_steps; /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */ uint32_t n_volume_steps; /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */
uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */ uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */
uint32_t n_ports; /**< Number of entries in port array \since 0.9.16 */
pa_sink_port_info** ports; /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16 */
pa_sink_port_info* active_port; /**< Pointer to active port in the array, or NULL \since 0.9.16 */
} pa_sink_info; } pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */ /** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@ -248,10 +260,25 @@ pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_na
/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */ /** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */
pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
/** Change the profile of a sink. \since 0.9.16 */
pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata);
/** Change the profile of a sink. \since 0.9.15 */
pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata);
/** @} */ /** @} */
/** @{ \name Sources */ /** @{ \name Sources */
/** Stores information about a specific port of a source. Please
* note that this structure can be extended as part of evolutionary
* API updates at any time in any new release. \since 0.9.16 */
typedef struct pa_source_port_info {
const char *name; /**< Name of this port */
const char *description; /**< Description of this port */
uint32_t priority; /**< The higher this value is the more useful this port is as a default */
} pa_source_port_info;
/** Stores information about sources. Please note that this structure /** Stores information about sources. Please note that this structure
* can be extended as part of evolutionary API updates at any time in * can be extended as part of evolutionary API updates at any time in
* any new release. */ * any new release. */
@ -275,6 +302,9 @@ typedef struct pa_source_info {
pa_source_state_t state; /**< State \since 0.9.15 */ pa_source_state_t state; /**< State \since 0.9.15 */
uint32_t n_volume_steps; /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */ uint32_t n_volume_steps; /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */
uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */ uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */
uint32_t n_ports; /**< Number of entries in port array \since 0.9.16 */
pa_source_port_info** ports; /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16 */
pa_source_port_info* active_port; /**< Pointer to active port in the array, or NULL \since 0.9.16 */
} pa_source_info; } pa_source_info;
/** Callback prototype for pa_context_get_source_info_by_name() and friends */ /** Callback prototype for pa_context_get_source_info_by_name() and friends */
@ -301,6 +331,12 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i
/** Set the mute switch of a source device specified by its name */ /** Set the mute switch of a source device specified by its name */
pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
/** Change the profile of a source. \since 0.9.16 */
pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata);
/** Change the profile of a source. \since 0.9.15 */
pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata);
/** @} */ /** @} */
/** @{ \name Server */ /** @{ \name Server */

View file

@ -206,6 +206,9 @@ PA_C_DECL_BEGIN
/** For devices: profile identifier for the profile this devices is in. e.g. "analog-stereo", "analog-surround-40", "iec958-stereo", ...*/ /** For devices: profile identifier for the profile this devices is in. e.g. "analog-stereo", "analog-surround-40", "iec958-stereo", ...*/
#define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name" #define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name"
/** For devices: intended use. A comma seperated list of roles (see PA_PROP_MEDIA_ROLE) this device is particularly well suited for, due to latency, quality or form factor. \since 0.9.16 */
#define PA_PROP_DEVICE_INTENDED_ROLES "device.intended_roles"
/** For devices: human readable one-line description of the profile this device is in. e.g. "Analog Stereo", ... */ /** For devices: human readable one-line description of the profile this device is in. e.g. "Analog Stereo", ... */
#define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description" #define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description"

View file

@ -64,8 +64,8 @@ const char* pa_get_library_version(void);
* newer than the specified. \since 0.9.16 */ * newer than the specified. \since 0.9.16 */
#define PA_CHECK_VERSION(major,minor,micro) \ #define PA_CHECK_VERSION(major,minor,micro) \
((PA_MAJOR > (major)) || \ ((PA_MAJOR > (major)) || \
(PA_MAJOR == (major) && CA_MINOR > (minor)) || \ (PA_MAJOR == (major) && PA_MINOR > (minor)) || \
(PA_MAJOR == (major) && CA_MINOR == (minor) && CA_MICRO >= (micro))) (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
PA_C_DECL_END PA_C_DECL_END

View file

@ -27,8 +27,10 @@
#include <string.h> #include <string.h>
#include <pulse/i18n.h> #include <pulse/i18n.h>
#include <pulsecore/core-util.h> #include <pulsecore/core-util.h>
#include <pulsecore/macro.h> #include <pulsecore/macro.h>
#include <pulsecore/sample-util.h>
#include "volume.h" #include "volume.h"
@ -344,7 +346,7 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const
pa_return_val_if_fail(pa_cvolume_valid(a), NULL); pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL); pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) for (i = 0; i < a->channels && i < b->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]); dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]);
dest->channels = (uint8_t) i; dest->channels = (uint8_t) i;
@ -352,6 +354,22 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const
return dest; return dest;
} }
pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i; unsigned i;
@ -362,7 +380,7 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa
pa_return_val_if_fail(pa_cvolume_valid(a), NULL); pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL); pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) for (i = 0; i < a->channels && i < b->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]); dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]);
dest->channels = (uint8_t) i; dest->channels = (uint8_t) i;
@ -370,6 +388,22 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa
return dest; return dest;
} }
pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
int pa_cvolume_valid(const pa_cvolume *v) { int pa_cvolume_valid(const pa_cvolume *v) {
unsigned c; unsigned c;
@ -386,65 +420,27 @@ int pa_cvolume_valid(const pa_cvolume *v) {
} }
static pa_bool_t on_left(pa_channel_position_t p) { static pa_bool_t on_left(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT);
return
p == PA_CHANNEL_POSITION_FRONT_LEFT ||
p == PA_CHANNEL_POSITION_REAR_LEFT ||
p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
p == PA_CHANNEL_POSITION_SIDE_LEFT ||
p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
} }
static pa_bool_t on_right(pa_channel_position_t p) { static pa_bool_t on_right(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT);
return
p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
p == PA_CHANNEL_POSITION_REAR_RIGHT ||
p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
} }
static pa_bool_t on_center(pa_channel_position_t p) { static pa_bool_t on_center(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER);
return
p == PA_CHANNEL_POSITION_FRONT_CENTER ||
p == PA_CHANNEL_POSITION_REAR_CENTER ||
p == PA_CHANNEL_POSITION_TOP_CENTER ||
p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
} }
static pa_bool_t on_lfe(pa_channel_position_t p) { static pa_bool_t on_lfe(pa_channel_position_t p) {
return p == PA_CHANNEL_POSITION_LFE;
return
p == PA_CHANNEL_POSITION_LFE;
} }
static pa_bool_t on_front(pa_channel_position_t p) { static pa_bool_t on_front(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT);
return
p == PA_CHANNEL_POSITION_FRONT_LEFT ||
p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
p == PA_CHANNEL_POSITION_FRONT_CENTER ||
p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
} }
static pa_bool_t on_rear(pa_channel_position_t p) { static pa_bool_t on_rear(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR);
return
p == PA_CHANNEL_POSITION_REAR_LEFT ||
p == PA_CHANNEL_POSITION_REAR_RIGHT ||
p == PA_CHANNEL_POSITION_REAR_CENTER ||
p == PA_CHANNEL_POSITION_TOP_REAR_LEFT ||
p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT ||
p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
} }
pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) { pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) {

View file

@ -216,16 +216,26 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
* *dest. This is only valid for software volumes! */ * *dest. This is only valid for software volumes! */
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
/** Multiply a per-channel volume with a scalar volume and return the
* result in *dest. This is only valid for software volumes! \since
* 0.9.16 */
pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b);
/** Divide two volume specifications, return the result. This uses /** Divide two volume specifications, return the result. This uses
* PA_VOLUME_NORM as neutral element of division. This is only valid * PA_VOLUME_NORM as neutral element of division. This is only valid
* for software volumes! If a division by zero is tried the result * for software volumes! If a division by zero is tried the result
* will be 0. \since 0.9.13 */ * will be 0. \since 0.9.13 */
pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
/** Multiply to per-channel volumes and return the result in /** Divide two per-channel volumes and return the result in
* *dest. This is only valid for software volumes! \since 0.9.13 */ * *dest. This is only valid for software volumes! \since 0.9.13 */
pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
/** Divide a per-channel volume by a scalar volume and return the
* result in *dest. This is only valid for software volumes! \since
* 0.9.16 */
pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b);
/** Convert a decibel value to a volume (amplitude, not power). This is only valid for software volumes! */ /** Convert a decibel value to a volume (amplitude, not power). This is only valid for software volumes! */
pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST; pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;

View file

@ -88,9 +88,20 @@ static inline void* _pa_xnewdup_internal(const void *p, size_t n, size_t k) {
return pa_xmemdup(p, n*k); return pa_xmemdup(p, n*k);
} }
/** Same as pa_xnew() but set the memory to zero */ /** Same as pa_xnew() but duplicate the specified data */
#define pa_xnewdup(type, p, n) ((type*) _pa_xnewdup_internal((p), (n), sizeof(type))) #define pa_xnewdup(type, p, n) ((type*) _pa_xnewdup_internal((p), (n), sizeof(type)))
/** Internal helper for pa_xrenew() */
static void* _pa_xrenew_internal(void *p, size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(2,3);
static inline void* _pa_xrenew_internal(void *p, size_t n, size_t k) {
assert(n < INT_MAX/k);
return pa_xrealloc(p, n*k);
}
/** Reallocate n new structures of the specified type. */
#define pa_xrenew(type, p, n) ((type*) _pa_xrenew_internal(p, (n), sizeof(type)))
PA_C_DECL_END PA_C_DECL_END
#endif #endif

View file

@ -148,15 +148,12 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
c->save_profile = data->save_profile; c->save_profile = data->save_profile;
if (!c->active_profile && c->profiles) { if (!c->active_profile && c->profiles) {
void *state = NULL; void *state;
pa_card_profile *p; pa_card_profile *p;
while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) { PA_HASHMAP_FOREACH(p, c->profiles, state)
if (!c->active_profile || if (!c->active_profile || p->priority > c->active_profile->priority)
p->priority > c->active_profile->priority)
c->active_profile = p; c->active_profile = p;
}
} }
c->userdata = NULL; c->userdata = NULL;
@ -164,6 +161,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
pa_device_init_description(c->proplist); pa_device_init_description(c->proplist);
pa_device_init_icon(c->proplist, TRUE); pa_device_init_icon(c->proplist, TRUE);
pa_device_init_intended_roles(c->proplist);
pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0); pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0);
@ -176,7 +174,6 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
void pa_card_free(pa_card *c) { void pa_card_free(pa_card *c) {
pa_core *core; pa_core *core;
pa_card_profile *profile;
pa_assert(c); pa_assert(c);
pa_assert(c->core); pa_assert(c->core);
@ -199,8 +196,10 @@ void pa_card_free(pa_card *c) {
pa_idxset_free(c->sources, NULL, NULL); pa_idxset_free(c->sources, NULL, NULL);
if (c->profiles) { if (c->profiles) {
while ((profile = pa_hashmap_steal_first(c->profiles))) pa_card_profile *p;
pa_card_profile_free(profile);
while ((p = pa_hashmap_steal_first(c->profiles)))
pa_card_profile_free(p);
pa_hashmap_free(c->profiles, NULL, NULL); pa_hashmap_free(c->profiles, NULL, NULL);
} }
@ -213,26 +212,27 @@ void pa_card_free(pa_card *c) {
int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) { int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) {
pa_card_profile *profile; pa_card_profile *profile;
int r;
pa_assert(c); pa_assert(c);
if (!c->set_profile) { if (!c->set_profile) {
pa_log_warn("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name); pa_log_debug("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
return -1; return -PA_ERR_NOTIMPLEMENTED;
} }
if (!c->profiles) if (!c->profiles)
return -1; return -PA_ERR_NOENTITY;
if (!(profile = pa_hashmap_get(c->profiles, name))) if (!(profile = pa_hashmap_get(c->profiles, name)))
return -1; return -PA_ERR_NOENTITY;
if (c->active_profile == profile) { if (c->active_profile == profile) {
c->save_profile = c->save_profile || save; c->save_profile = c->save_profile || save;
return 0; return 0;
} }
if (c->set_profile(c, profile) < 0) if ((r = c->set_profile(c, profile)) < 0)
return -1; return r;
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
@ -253,11 +253,19 @@ int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_assert(c); pa_assert(c);
pa_assert(cause != 0); pa_assert(cause != 0);
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
ret -= pa_sink_suspend(sink, suspend, cause) < 0; int r;
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
ret -= pa_source_suspend(source, suspend, cause) < 0; ret = r;
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
int r;
if ((r = pa_source_suspend(source, suspend, cause)) < 0)
ret = r;
}
return ret; return ret;
} }

View file

@ -63,7 +63,7 @@ struct pa_card {
pa_hashmap *profiles; pa_hashmap *profiles;
pa_card_profile *active_profile; pa_card_profile *active_profile;
pa_bool_t save_profile; pa_bool_t save_profile:1;
void *userdata; void *userdata;
@ -72,9 +72,8 @@ struct pa_card {
typedef struct pa_card_new_data { typedef struct pa_card_new_data {
char *name; char *name;
char *description;
pa_proplist *proplist; pa_proplist *proplist;
const char *driver; const char *driver;
pa_module *module; pa_module *module;

View file

@ -125,6 +125,8 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */ /* A method table for all available commands */
@ -176,10 +178,12 @@ static const struct command commands[] = {
{ "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3}, { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
{ "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2}, { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
{ "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3}, { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3},
{ "set-sink-port", pa_cli_command_sink_port, "Change the port of a sink (args: index, name)", 3},
{ "set-source-port", pa_cli_command_source_port, "Change the port of a source (args: index, name)", 3},
{ "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2}, { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
{ "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2}, { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
{ "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2}, { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
{ "set-log-backtrace", pa_cli_command_log_backtrace, "Show bakctrace in log messages (args: frames)", 2}, { "set-log-backtrace", pa_cli_command_log_backtrace, "Show backtrace in log messages (args: frames)", 2},
{ NULL, NULL, NULL, 0 } { NULL, NULL, NULL, 0 }
}; };
@ -526,7 +530,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
} }
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE); pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE);
return 0; return 0;
} }
@ -604,7 +608,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
} }
pa_cvolume_set(&cvolume, source->sample_spec.channels, volume); pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
pa_source_set_volume(source, &cvolume); pa_source_set_volume(source, &cvolume, TRUE);
return 0; return 0;
} }
@ -638,7 +642,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1; return -1;
} }
pa_sink_set_mute(sink, mute); pa_sink_set_mute(sink, mute, TRUE);
return 0; return 0;
} }
@ -672,7 +676,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1; return -1;
} }
pa_source_set_mute(source, mute); pa_source_set_mute(source, mute, TRUE);
return 0; return 0;
} }
@ -1476,6 +1480,70 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0; return 0;
} }
static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *p;
pa_sink *sink;
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
if (!(p = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
if (pa_sink_set_port(sink, p, TRUE) < 0) {
pa_strbuf_printf(buf, "Failed to set sink port to '%s'.\n", p);
return -1;
}
return 0;
}
static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *p;
pa_source *source;
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
}
if (!(p = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
return -1;
}
if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
pa_strbuf_puts(buf, "No source found by this name or index.\n");
return -1;
}
if (pa_source_set_port(source, p, TRUE) < 0) {
pa_strbuf_printf(buf, "Failed to set source port to '%s'.\n", p);
return -1;
}
return 0;
}
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m; pa_module *m;
pa_sink *sink; pa_sink *sink;

View file

@ -139,11 +139,10 @@ char *pa_card_list_to_string(pa_core *c) {
if (card->profiles) { if (card->profiles) {
pa_card_profile *p; pa_card_profile *p;
void *state = NULL; void *state;
pa_strbuf_puts(s, "\tprofiles:\n"); pa_strbuf_puts(s, "\tprofiles:\n");
PA_HASHMAP_FOREACH(p, card->profiles, state)
while ((p = pa_hashmap_iterate(card->profiles, &state, NULL)))
pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority); pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
} }
@ -307,6 +306,22 @@ char *pa_sink_list_to_string(pa_core *c) {
t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t"); t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t");
pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t); pa_xfree(t);
if (sink->ports) {
pa_device_port *p;
void *state;
pa_strbuf_puts(s, "\tports:\n");
PA_HASHMAP_FOREACH(p, sink->ports, state)
pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
}
if (sink->active_port)
pa_strbuf_printf(
s,
"\tactive port: <%s>\n",
sink->active_port->name);
} }
return pa_strbuf_tostring_free(s); return pa_strbuf_tostring_free(s);
@ -412,6 +427,21 @@ char *pa_source_list_to_string(pa_core *c) {
t = pa_proplist_to_string_sep(source->proplist, "\n\t\t"); t = pa_proplist_to_string_sep(source->proplist, "\n\t\t");
pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t); pa_xfree(t);
if (source->ports) {
pa_device_port *p;
void *state;
pa_strbuf_puts(s, "\tports:\n");
PA_HASHMAP_FOREACH(p, source->ports, state)
pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
}
if (source->active_port)
pa_strbuf_printf(
s,
"\tactive port: <%s>\n",
source->active_port->name);
} }
return pa_strbuf_tostring_free(s); return pa_strbuf_tostring_free(s);

View file

@ -40,19 +40,35 @@
#define COMMENTS "#;\n" #define COMMENTS "#;\n"
/* Run the user supplied parser for an assignment */ /* Run the user supplied parser for an assignment */
static int next_assignment(const char *filename, unsigned line, const char *section, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { static int next_assignment(
const char *filename,
unsigned line,
const char *section,
const pa_config_item *t,
const char *lvalue,
const char *rvalue,
void *userdata) {
pa_assert(filename); pa_assert(filename);
pa_assert(t); pa_assert(t);
pa_assert(lvalue); pa_assert(lvalue);
pa_assert(rvalue); pa_assert(rvalue);
for (; t->parse; t++) for (; t->parse; t++) {
if (!t->lvalue ||
(pa_streq(lvalue, t->lvalue) &&
((!section && !t->section) || pa_streq(section, t->section))))
return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strnull(section)); if (t->lvalue && !pa_streq(lvalue, t->lvalue))
continue;
if (t->section && !section)
continue;
if (t->section && !pa_streq(section, t->section))
continue;
return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
}
pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strna(section));
return -1; return -1;
} }
@ -96,6 +112,25 @@ static int parse_line(const char *filename, unsigned line, char **section, const
if (!*b) if (!*b)
return 0; return 0;
if (pa_startswith(b, ".include ")) {
char *path, *fn;
int r;
fn = strip(b+9);
if (!pa_is_path_absolute(fn)) {
const char *k;
if ((k = strrchr(filename, '/'))) {
char *dir = pa_xstrndup(filename, k-filename);
fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn);
pa_xfree(dir);
}
}
r = pa_config_parse(fn, NULL, t, userdata);
pa_xfree(path);
return r;
}
if (*b == '[') { if (*b == '[') {
size_t k; size_t k;
@ -135,6 +170,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
if (!f && !(f = fopen(filename, "r"))) { if (!f && !(f = fopen(filename, "r"))) {
if (errno == ENOENT) { if (errno == ENOENT) {
pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
r = 0; r = 0;
goto finish; goto finish;
} }

View file

@ -2732,3 +2732,48 @@ void pa_disable_sigpipe(void) {
} }
#endif #endif
} }
void pa_xfreev(void**a) {
void **p;
if (!a)
return;
for (p = a; *p; p++)
pa_xfree(*p);
pa_xfree(a);
}
char **pa_split_spaces_strv(const char *s) {
char **t, *e;
unsigned i = 0, n = 8;
const char *state = NULL;
t = pa_xnew(char*, n);
while ((e = pa_split_spaces(s, &state))) {
t[i++] = e;
if (i >= n) {
n *= 2;
t = pa_xrenew(char*, t, n);
}
}
if (i <= 0) {
pa_xfree(t);
return NULL;
}
t[i] = NULL;
return t;
}
char* pa_maybe_prefix_path(const char *path, const char *prefix) {
pa_assert(path);
if (pa_is_path_absolute(path))
return pa_xstrdup(path);
return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path);
}

View file

@ -229,4 +229,14 @@ char *pa_realpath(const char *path);
void pa_disable_sigpipe(void); void pa_disable_sigpipe(void);
void pa_xfreev(void**a);
static inline void pa_xstrfreev(char **a) {
pa_xfreev((void**) a);
}
char **pa_split_spaces_strv(const char *s);
char* pa_maybe_prefix_path(const char *path, const char *prefix);
#endif #endif

View file

@ -71,10 +71,13 @@ pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
/* We include the host identifier in the file name because gdbm /* We include the host identifier in the file name because gdbm
* files are CPU dependant, and we don't want things to go wrong * files are CPU dependant, and we don't want things to go wrong
* if we are on a multiarch system. */ * if we are on a multiarch system. */
path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn); path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn);
errno = 0; errno = 0;
f = gdbm_open((char*) path, 0, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL);
/* We need to set the block size explicitly here, since otherwise
* gdbm takes the native block size of the underlying file system
* which might be incredibly large. */
f = gdbm_open((char*) path, 1024, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL);
if (f) if (f)
pa_log_debug("Opened GDBM database '%s'", path); pa_log_debug("Opened GDBM database '%s'", path);

View file

@ -237,6 +237,39 @@ at_end:
return NULL; return NULL;
} }
void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) {
struct hashmap_entry *e;
pa_assert(h);
pa_assert(state);
if (*state == (void*) -1)
goto at_beginning;
if (!*state && !h->iterate_list_tail)
goto at_beginning;
e = *state ? *state : h->iterate_list_tail;
if (e->iterate_previous)
*state = e->iterate_previous;
else
*state = (void*) -1;
if (key)
*key = e->key;
return e->value;
at_beginning:
*state = (void *) -1;
if (key)
*key = NULL;
return NULL;
}
void* pa_hashmap_first(pa_hashmap *h) { void* pa_hashmap_first(pa_hashmap *h) {
pa_assert(h); pa_assert(h);
@ -246,6 +279,15 @@ void* pa_hashmap_first(pa_hashmap *h) {
return h->iterate_list_head->value; return h->iterate_list_head->value;
} }
void* pa_hashmap_last(pa_hashmap *h) {
pa_assert(h);
if (!h->iterate_list_tail)
return NULL;
return h->iterate_list_tail->value;
}
void* pa_hashmap_steal_first(pa_hashmap *h) { void* pa_hashmap_steal_first(pa_hashmap *h) {
void *data; void *data;

View file

@ -26,7 +26,8 @@
/* Simple Implementation of a hash table. Memory management is the /* Simple Implementation of a hash table. Memory management is the
* user's job. It's a good idea to have the key pointer point to a * user's job. It's a good idea to have the key pointer point to a
* string in the value data. */ * string in the value data. The insertion order is preserved when
* iterating. */
typedef struct pa_hashmap pa_hashmap; typedef struct pa_hashmap pa_hashmap;
@ -59,14 +60,24 @@ pa_bool_t pa_hashmap_isempty(pa_hashmap *h);
returned. */ returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
/* Same as pa_hashmap_iterate() but goes backwards */
void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key);
/* Remove the oldest entry in the hashmap and return it */ /* Remove the oldest entry in the hashmap and return it */
void *pa_hashmap_steal_first(pa_hashmap *h); void *pa_hashmap_steal_first(pa_hashmap *h);
/* Return the oldest entry in the hashmap */ /* Return the oldest entry in the hashmap */
void* pa_hashmap_first(pa_hashmap *h); void* pa_hashmap_first(pa_hashmap *h);
/* Return the newest entry in the hashmap */
void* pa_hashmap_last(pa_hashmap *h);
/* A macro to ease iteration through all entries */ /* A macro to ease iteration through all entries */
#define PA_HASHMAP_FOREACH(e, h, state) \ #define PA_HASHMAP_FOREACH(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL)) for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL))
/* A macro to ease iteration through all entries, backwards */
#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL))
#endif #endif

View file

@ -453,3 +453,17 @@ pa_bool_t pa_idxset_isempty(pa_idxset *s) {
return s->n_entries == 0; return s->n_entries == 0;
} }
pa_idxset *pa_idxset_copy(pa_idxset *s) {
pa_idxset *copy;
struct idxset_entry *i;
pa_assert(s);
copy = pa_idxset_new(s->hash_func, s->compare_func);
for (i = s->iterate_list_head; i; i = i->iterate_next)
pa_idxset_put(copy, i->data, NULL);
return copy;
}

View file

@ -103,6 +103,9 @@ unsigned pa_idxset_size(pa_idxset*s);
/* Return TRUE of the idxset is empty */ /* Return TRUE of the idxset is empty */
pa_bool_t pa_idxset_isempty(pa_idxset *s); pa_bool_t pa_idxset_isempty(pa_idxset *s);
/* Duplicate the idxset. This will not copy the actual indexes */
pa_idxset *pa_idxset_copy(pa_idxset *s);
/* A macro to ease iteration through all entries */ /* A macro to ease iteration through all entries */
#define PA_IDXSET_FOREACH(e, s, idx) \ #define PA_IDXSET_FOREACH(e, s, idx) \
for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx))) for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx)))

View file

@ -165,6 +165,10 @@ enum {
PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED, PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
/* Supported since protocol v16 (0.9.16) */
PA_COMMAND_SET_SINK_PORT,
PA_COMMAND_SET_SOURCE_PORT,
PA_COMMAND_MAX PA_COMMAND_MAX
}; };

View file

@ -304,7 +304,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
if (command >= PA_COMMAND_MAX || !(p = command_names[command])) if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
pa_snprintf((char*) (p = t), sizeof(t), "%u", command); pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
pa_log("[%p] Recieved opcode <%s>", pd, p); pa_log("[%p] Received opcode <%s>", pd, p);
} }
#endif #endif
@ -325,7 +325,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
(*c)(pd, command, tag, ts, userdata); (*c)(pd, command, tag, ts, userdata);
} else { } else {
pa_log("Recieved unsupported command %u", command); pa_log("Received unsupported command %u", command);
goto finish; goto finish;
} }

View file

@ -284,6 +284,7 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL, [PA_COMMAND_ERROR] = NULL,
@ -380,6 +381,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile, [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
[PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
[PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
[PA_COMMAND_EXTENSION] = command_extension [PA_COMMAND_EXTENSION] = command_extension
}; };
@ -2841,6 +2845,23 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
pa_tagstruct_putu32(t, sink->n_volume_steps); pa_tagstruct_putu32(t, sink->n_volume_steps);
pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX); pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
} }
if (c->version >= 16) {
pa_tagstruct_putu32(t, sink->ports ? pa_hashmap_size(sink->ports) : 0);
if (sink->ports) {
void *state;
pa_device_port *p;
PA_HASHMAP_FOREACH(p, sink->ports, state) {
pa_tagstruct_puts(t, p->name);
pa_tagstruct_puts(t, p->description);
pa_tagstruct_putu32(t, p->priority);
}
}
pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
}
} }
static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) { static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
@ -2881,6 +2902,24 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
pa_tagstruct_putu32(t, source->n_volume_steps); pa_tagstruct_putu32(t, source->n_volume_steps);
pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX); pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
} }
if (c->version >= 16) {
pa_tagstruct_putu32(t, source->ports ? pa_hashmap_size(source->ports) : 0);
if (source->ports) {
void *state;
pa_device_port *p;
PA_HASHMAP_FOREACH(p, source->ports, state) {
pa_tagstruct_puts(t, p->name);
pa_tagstruct_puts(t, p->description);
pa_tagstruct_putu32(t, p->priority);
}
}
pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
}
} }
static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) { static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
@ -3328,9 +3367,9 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY); CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink) if (sink)
pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE); pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE);
else if (source) else if (source)
pa_source_set_volume(source, &volume); pa_source_set_volume(source, &volume, TRUE);
else if (si) else if (si)
pa_sink_input_set_volume(si, &volume, TRUE, TRUE); pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
@ -3400,9 +3439,9 @@ static void command_set_mute(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY); CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink) if (sink)
pa_sink_set_mute(sink, mute); pa_sink_set_mute(sink, mute, TRUE);
else if (source) else if (source)
pa_source_set_mute(source, mute); pa_source_set_mute(source, mute, TRUE);
else if (si) else if (si)
pa_sink_input_set_mute(si, mute, TRUE); pa_sink_input_set_mute(si, mute, TRUE);
@ -4195,6 +4234,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
uint32_t idx = PA_INVALID_INDEX; uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL, *profile = NULL; const char *name = NULL, *profile = NULL;
pa_card *card = NULL; pa_card *card = NULL;
int ret;
pa_native_connection_assert_ref(c); pa_native_connection_assert_ref(c);
pa_assert(t); pa_assert(t);
@ -4220,14 +4260,72 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY); CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
if (pa_card_set_profile(card, profile, TRUE) < 0) { if ((ret = pa_card_set_profile(card, profile, TRUE)) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); pa_pstream_send_error(c->pstream, tag, -ret);
return; return;
} }
pa_pstream_send_simple_ack(c->pstream, tag); pa_pstream_send_simple_ack(c->pstream, tag);
} }
static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL, *port = NULL;
int ret;
pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_gets(t, &port) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
if (command == PA_COMMAND_SET_SINK_PORT) {
pa_sink *sink;
if (idx != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
else
sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
if ((ret = pa_sink_set_port(sink, port, TRUE)) < 0) {
pa_pstream_send_error(c->pstream, tag, -ret);
return;
}
} else {
pa_source *source;
pa_assert(command = PA_COMMAND_SET_SOURCE_PORT);
if (idx != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
else
source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
if ((ret = pa_source_set_port(source, port, TRUE)) < 0) {
pa_pstream_send_error(c->pstream, tag, -ret);
return;
}
}
pa_pstream_send_simple_ack(c->pstream, tag);
}
/*** pstream callbacks ***/ /*** pstream callbacks ***/
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) { static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {

View file

@ -684,7 +684,7 @@ static int do_read(pa_pstream *p) {
flags = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]); flags = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) { if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) {
pa_log_warn("Recieved SHM frame on a socket where SHM is disabled."); pa_log_warn("Received SHM frame on a socket where SHM is disabled.");
return -1; return -1;
} }
@ -714,7 +714,7 @@ static int do_read(pa_pstream *p) {
length = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]); length = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]);
if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) { if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) {
pa_log_warn("Recieved invalid frame size: %lu", (unsigned long) length); pa_log_warn("Received invalid frame size: %lu", (unsigned long) length);
return -1; return -1;
} }
@ -743,7 +743,7 @@ static int do_read(pa_pstream *p) {
if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) { if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) {
if (length != sizeof(p->read.shm_info)) { if (length != sizeof(p->read.shm_info)) {
pa_log_warn("Recieved SHM memblock frame with Invalid frame length."); pa_log_warn("Received SHM memblock frame with Invalid frame length.");
return -1; return -1;
} }
@ -758,7 +758,7 @@ static int do_read(pa_pstream *p) {
p->read.data = NULL; p->read.data = NULL;
} else { } else {
pa_log_warn("Recieved memblock frame with invalid flags value."); pa_log_warn("Received memblock frame with invalid flags value.");
return -1; return -1;
} }
} }

View file

@ -1182,7 +1182,7 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
case PA_SAMPLE_S24LE: case PA_SAMPLE_S24LE:
case PA_SAMPLE_S24BE: case PA_SAMPLE_S24BE:
case PA_SAMPLE_S24_32LE: case PA_SAMPLE_S24_32LE:
case PA_SAMPLE_S24_32RE: case PA_SAMPLE_S24_32BE:
case PA_SAMPLE_FLOAT32LE: case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE: case PA_SAMPLE_FLOAT32BE:
cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0); cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);

View file

@ -25,6 +25,7 @@
#include <pulse/sample.h> #include <pulse/sample.h>
#include <pulse/volume.h> #include <pulse/volume.h>
#include <pulse/channelmap.h>
#include <pulsecore/memblock.h> #include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h> #include <pulsecore/memchunk.h>
@ -85,4 +86,62 @@ void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn);
void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq); void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq);
#define PA_CHANNEL_POSITION_MASK_LEFT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
#define PA_CHANNEL_POSITION_MASK_RIGHT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
#define PA_CHANNEL_POSITION_MASK_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_FRONT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
#define PA_CHANNEL_POSITION_MASK_REAR \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
#define PA_CHANNEL_POSITION_MASK_TOP \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_ALL \
((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
#endif #endif

View file

@ -442,7 +442,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) { if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume; pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume); pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
} }
if (i->sink->asyncmsgq) if (i->sink->asyncmsgq)
@ -520,7 +520,7 @@ void pa_sink_input_put(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) { if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume; pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume); pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
} else } else
pa_sink_input_set_relative_volume(i, &i->virtual_volume); pa_sink_input_set_relative_volume(i, &i->virtual_volume);
@ -900,7 +900,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
* volumes and update the flat volume of the sink */ * volumes and update the flat volume of the sink */
pa_sink_update_flat_volume(i->sink, &new_volume); pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE); pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE);
} else { } else {
@ -1159,7 +1159,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
/* We might need to update the sink's volume if we are in flat /* We might need to update the sink's volume if we are in flat
* volume mode. */ * volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume); pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
} }
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
@ -1252,7 +1252,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* We might need to update the sink's volume if we are in flat volume mode. */ /* We might need to update the sink's volume if we are in flat volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume); pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
} }
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);

View file

@ -100,11 +100,51 @@ void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
data->muted = !!mute; data->muted = !!mute;
} }
void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port) {
pa_assert(data);
pa_xfree(data->active_port);
data->active_port = pa_xstrdup(port);
}
void pa_sink_new_data_done(pa_sink_new_data *data) { void pa_sink_new_data_done(pa_sink_new_data *data) {
pa_assert(data); pa_assert(data);
pa_xfree(data->name);
pa_proplist_free(data->proplist); pa_proplist_free(data->proplist);
if (data->ports) {
pa_device_port *p;
while ((p = pa_hashmap_steal_first(data->ports)))
pa_device_port_free(p);
pa_hashmap_free(data->ports, NULL, NULL);
}
pa_xfree(data->name);
pa_xfree(data->active_port);
}
pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra) {
pa_device_port *p;
pa_assert(name);
p = pa_xmalloc(PA_ALIGN(sizeof(pa_device_port)) + extra);
p->name = pa_xstrdup(name);
p->description = pa_xstrdup(description);
p->priority = 0;
return p;
}
void pa_device_port_free(pa_device_port *p) {
pa_assert(p);
pa_xfree(p->name);
pa_xfree(p->description);
pa_xfree(p);
} }
/* Called from main context */ /* Called from main context */
@ -118,6 +158,7 @@ static void reset_callbacks(pa_sink *s) {
s->set_mute = NULL; s->set_mute = NULL;
s->request_rewind = NULL; s->request_rewind = NULL;
s->update_requested_latency = NULL; s->update_requested_latency = NULL;
s->set_port = NULL;
} }
/* Called from main context */ /* Called from main context */
@ -152,6 +193,8 @@ pa_sink* pa_sink_new(
return NULL; return NULL;
} }
/* FIXME, need to free s here on failure */
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
@ -177,6 +220,7 @@ pa_sink* pa_sink_new(
pa_device_init_description(data->proplist); pa_device_init_description(data->proplist);
pa_device_init_icon(data->proplist, TRUE); pa_device_init_icon(data->proplist, TRUE);
pa_device_init_intended_roles(data->proplist);
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) { if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s); pa_xfree(s);
@ -218,6 +262,30 @@ pa_sink* pa_sink_new(
s->asyncmsgq = NULL; s->asyncmsgq = NULL;
s->rtpoll = NULL; s->rtpoll = NULL;
/* As a minor optimization we just steal the list instead of
* copying it here */
s->ports = data->ports;
data->ports = NULL;
s->active_port = NULL;
s->save_port = FALSE;
if (data->active_port && s->ports)
if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
s->save_port = data->save_port;
if (!s->active_port && s->ports) {
void *state;
pa_device_port *p;
PA_HASHMAP_FOREACH(p, s->ports, state)
if (!s->active_port || p->priority > s->active_port->priority)
s->active_port = p;
}
s->save_volume = data->save_volume;
s->save_muted = data->save_muted;
pa_silence_memchunk_get( pa_silence_memchunk_get(
&core->silence_cache, &core->silence_cache,
core->mempool, core->mempool,
@ -466,6 +534,15 @@ static void sink_free(pa_object *o) {
if (s->proplist) if (s->proplist)
pa_proplist_free(s->proplist); pa_proplist_free(s->proplist);
if (s->ports) {
pa_device_port *p;
while ((p = pa_hashmap_steal_first(s->ports)))
pa_device_port_free(p);
pa_hashmap_free(s->ports, NULL, NULL);
}
pa_xfree(s); pa_xfree(s);
} }
@ -484,6 +561,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
s->rtpoll = p; s->rtpoll = p;
if (s->monitor_source) if (s->monitor_source)
pa_source_set_rtpoll(s->monitor_source, p); pa_source_set_rtpoll(s->monitor_source, p);
} }
@ -525,15 +603,15 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
} }
/* Called from main context */ /* Called from main context */
pa_queue *pa_sink_move_all_start(pa_sink *s) { pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
pa_queue *q;
pa_sink_input *i, *n; pa_sink_input *i, *n;
uint32_t idx; uint32_t idx;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state)); pa_assert(PA_SINK_IS_LINKED(s->state));
q = pa_queue_new(); if (!q)
q = pa_queue_new();
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) { for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)); n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
@ -1236,7 +1314,7 @@ void pa_sink_propagate_flat_volume(pa_sink *s) {
} }
/* Called from main thread */ /* Called from main thread */
void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference) { void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save) {
pa_bool_t virtual_volume_changed; pa_bool_t virtual_volume_changed;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
@ -1247,6 +1325,7 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume); virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
s->virtual_volume = *volume; s->virtual_volume = *volume;
s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
if (become_reference) if (become_reference)
s->reference_volume = s->virtual_volume; s->reference_volume = s->virtual_volume;
@ -1317,15 +1396,17 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_boo
} }
/* Called from main thread */ /* Called from main thread */
void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) { void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
/* The sink implementor may call this if the volume changed to make sure everyone is notified */ /* The sink implementor may call this if the volume changed to make sure everyone is notified */
if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
if (pa_cvolume_equal(&s->virtual_volume, new_volume)) s->save_volume = s->save_volume || save;
return; return;
}
s->reference_volume = s->virtual_volume = *new_volume; s->reference_volume = s->virtual_volume = *new_volume;
s->save_volume = save;
if (s->flags & PA_SINK_FLAT_VOLUME) if (s->flags & PA_SINK_FLAT_VOLUME)
pa_sink_propagate_flat_volume(s); pa_sink_propagate_flat_volume(s);
@ -1334,7 +1415,7 @@ void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
} }
/* Called from main thread */ /* Called from main thread */
void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted; pa_bool_t old_muted;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
@ -1342,6 +1423,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
old_muted = s->muted; old_muted = s->muted;
s->muted = mute; s->muted = mute;
s->save_muted = (old_muted == s->muted && s->save_muted) || save;
if (s->set_mute) if (s->set_mute)
s->set_mute(s); s->set_mute(s);
@ -1377,15 +1459,19 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
} }
/* Called from main thread */ /* Called from main thread */
void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) { void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
/* The sink implementor may call this if the volume changed to make sure everyone is notified */ /* The sink implementor may call this if the volume changed to make sure everyone is notified */
if (s->muted == new_muted) if (s->muted == new_muted) {
s->save_muted = s->save_muted || save;
return; return;
}
s->muted = new_muted; s->muted = new_muted;
s->save_muted = save;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
} }
@ -1483,7 +1569,7 @@ unsigned pa_sink_check_suspend(pa_sink *s) {
ret = 0; ret = 0;
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { PA_IDXSET_FOREACH(i, s->inputs, idx) {
pa_sink_input_state_t st; pa_sink_input_state_t st;
st = pa_sink_input_get_state(i); st = pa_sink_input_get_state(i);
@ -2193,6 +2279,41 @@ size_t pa_sink_get_max_request(pa_sink *s) {
return r; return r;
} }
/* Called from main context */
int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
pa_device_port *port;
pa_assert(s);
if (!s->set_port) {
pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
return -PA_ERR_NOTIMPLEMENTED;
}
if (!s->ports)
return -PA_ERR_NOENTITY;
if (!(port = pa_hashmap_get(s->ports, name)))
return -PA_ERR_NOENTITY;
if (s->active_port == port) {
s->save_port = s->save_port || save;
return 0;
}
if ((s->set_port(s, port)) < 0)
return -PA_ERR_NOENTITY;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name);
s->active_port = port;
s->save_port = save;
return 0;
}
/* Called from main context */ /* Called from main context */
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) { pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
const char *ff, *c, *t = NULL, *s = "", *profile, *bus; const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
@ -2287,3 +2408,19 @@ pa_bool_t pa_device_init_description(pa_proplist *p) {
return FALSE; return FALSE;
} }
pa_bool_t pa_device_init_intended_roles(pa_proplist *p) {
const char *s;
pa_assert(p);
if (pa_proplist_contains(p, PA_PROP_DEVICE_INTENDED_ROLES))
return TRUE;
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
if (pa_streq(s, "handset") || pa_streq(s, "hands-free")) {
pa_proplist_sets(p, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
return TRUE;
}
return FALSE;
}

View file

@ -24,6 +24,7 @@
***/ ***/
typedef struct pa_sink pa_sink; typedef struct pa_sink pa_sink;
typedef struct pa_device_port pa_device_port;
#include <inttypes.h> #include <inttypes.h>
@ -49,11 +50,23 @@ static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED; return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
} }
struct pa_device_port {
char *name;
char *description;
unsigned priority;
/* .. followed by some implementation specific data */
};
#define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port))))
struct pa_sink { struct pa_sink {
pa_msgobject parent; pa_msgobject parent;
uint32_t index; uint32_t index;
pa_core *core; pa_core *core;
pa_sink_state_t state; pa_sink_state_t state;
pa_sink_flags_t flags; pa_sink_flags_t flags;
pa_suspend_cause_t suspend_cause; pa_suspend_cause_t suspend_cause;
@ -83,6 +96,9 @@ struct pa_sink {
pa_bool_t refresh_volume:1; pa_bool_t refresh_volume:1;
pa_bool_t refresh_muted:1; pa_bool_t refresh_muted:1;
pa_bool_t save_port:1;
pa_bool_t save_volume:1;
pa_bool_t save_muted:1;
pa_asyncmsgq *asyncmsgq; pa_asyncmsgq *asyncmsgq;
pa_rtpoll *rtpoll; pa_rtpoll *rtpoll;
@ -91,6 +107,9 @@ struct pa_sink {
pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */ pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
pa_hashmap *ports;
pa_device_port *active_port;
/* Called when the main loop requests a state change. Called from /* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be * main loop context. If returns -1 the state change will be
* inhibited */ * inhibited */
@ -126,6 +145,10 @@ struct pa_sink {
* thread context. */ * thread context. */
void (*update_requested_latency)(pa_sink *s); /* dito */ void (*update_requested_latency)(pa_sink *s); /* dito */
/* Called whenever the port shall be changed. Called from main
* thread. */
int (*set_port)(pa_sink *s, pa_device_port *port); /* dito */
/* Contains copies of the above data so that the real-time worker /* Contains copies of the above data so that the real-time worker
* thread can work without access locking */ * thread can work without access locking */
struct { struct {
@ -192,6 +215,9 @@ typedef struct pa_sink_new_data {
pa_module *module; pa_module *module;
pa_card *card; pa_card *card;
pa_hashmap *ports;
char *active_port;
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; pa_channel_map channel_map;
pa_cvolume volume; pa_cvolume volume;
@ -203,6 +229,10 @@ typedef struct pa_sink_new_data {
pa_bool_t muted_is_set:1; pa_bool_t muted_is_set:1;
pa_bool_t namereg_fail:1; pa_bool_t namereg_fail:1;
pa_bool_t save_port:1;
pa_bool_t save_volume:1;
pa_bool_t save_muted:1;
} pa_sink_new_data; } pa_sink_new_data;
pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data); pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
@ -211,6 +241,7 @@ void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_sp
void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map); void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume); void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute); void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port);
void pa_sink_new_data_done(pa_sink_new_data *data); void pa_sink_new_data_done(pa_sink_new_data *data);
/*** To be called exclusively by the sink driver, from main context */ /*** To be called exclusively by the sink driver, from main context */
@ -236,11 +267,12 @@ void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s); void pa_sink_attach(pa_sink *s);
void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume); void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save);
void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted); void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save);
pa_bool_t pa_device_init_description(pa_proplist *p); pa_bool_t pa_device_init_description(pa_proplist *p);
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink); pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink);
pa_bool_t pa_device_init_intended_roles(pa_proplist *p);
/**** May be called by everyone, from main context */ /**** May be called by everyone, from main context */
@ -259,21 +291,23 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume); void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
void pa_sink_propagate_flat_volume(pa_sink *s); void pa_sink_propagate_flat_volume(pa_sink *s);
void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference); void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference); const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute); void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh); pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p); pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save);
unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */ unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */ unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_sink_get_state(s) ((s)->state) #define pa_sink_get_state(s) ((s)->state)
/* Moves all inputs away, and stores them in pa_queue */ /* Moves all inputs away, and stores them in pa_queue */
pa_queue *pa_sink_move_all_start(pa_sink *s); pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q);
void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save); void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
void pa_sink_move_all_fail(pa_queue *q); void pa_sink_move_all_fail(pa_queue *q);
@ -306,4 +340,7 @@ void pa_sink_invalidate_requested_latency(pa_sink *s);
pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s); pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra);
void pa_device_port_free(pa_device_port *p);
#endif #endif

View file

@ -93,11 +93,29 @@ void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
data->muted = !!mute; data->muted = !!mute;
} }
void pa_source_new_data_set_port(pa_source_new_data *data, const char *port) {
pa_assert(data);
pa_xfree(data->active_port);
data->active_port = pa_xstrdup(port);
}
void pa_source_new_data_done(pa_source_new_data *data) { void pa_source_new_data_done(pa_source_new_data *data) {
pa_assert(data); pa_assert(data);
pa_xfree(data->name);
pa_proplist_free(data->proplist); pa_proplist_free(data->proplist);
if (data->ports) {
pa_device_port *p;
while ((p = pa_hashmap_steal_first(data->ports)))
pa_device_port_free(p);
pa_hashmap_free(data->ports, NULL, NULL);
}
pa_xfree(data->name);
pa_xfree(data->active_port);
} }
/* Called from main context */ /* Called from main context */
@ -110,6 +128,7 @@ static void reset_callbacks(pa_source *s) {
s->get_mute = NULL; s->get_mute = NULL;
s->set_mute = NULL; s->set_mute = NULL;
s->update_requested_latency = NULL; s->update_requested_latency = NULL;
s->set_port = NULL;
} }
/* Called from main context */ /* Called from main context */
@ -142,6 +161,8 @@ pa_source* pa_source_new(
return NULL; return NULL;
} }
/* FIXME, need to free s here on failure */
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
@ -167,6 +188,7 @@ pa_source* pa_source_new(
pa_device_init_description(data->proplist); pa_device_init_description(data->proplist);
pa_device_init_icon(data->proplist, FALSE); pa_device_init_icon(data->proplist, FALSE);
pa_device_init_intended_roles(data->proplist);
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) { if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
pa_xfree(s); pa_xfree(s);
@ -209,6 +231,30 @@ pa_source* pa_source_new(
s->asyncmsgq = NULL; s->asyncmsgq = NULL;
s->rtpoll = NULL; s->rtpoll = NULL;
/* As a minor optimization we just steal the list instead of
* copying it here */
s->ports = data->ports;
data->ports = NULL;
s->active_port = NULL;
s->save_port = FALSE;
if (data->active_port && s->ports)
if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
s->save_port = data->save_port;
if (!s->active_port && s->ports) {
void *state;
pa_device_port *p;
PA_HASHMAP_FOREACH(p, s->ports, state)
if (!s->active_port || p->priority > s->active_port->priority)
s->active_port = p;
}
s->save_volume = data->save_volume;
s->save_muted = data->save_muted;
pa_silence_memchunk_get( pa_silence_memchunk_get(
&core->silence_cache, &core->silence_cache,
core->mempool, core->mempool,
@ -399,6 +445,15 @@ static void source_free(pa_object *o) {
if (s->proplist) if (s->proplist)
pa_proplist_free(s->proplist); pa_proplist_free(s->proplist);
if (s->ports) {
pa_device_port *p;
while ((p = pa_hashmap_steal_first(s->ports)))
pa_device_port_free(p);
pa_hashmap_free(s->ports, NULL, NULL);
}
pa_xfree(s); pa_xfree(s);
} }
@ -471,15 +526,15 @@ int pa_source_sync_suspend(pa_source *s) {
} }
/* Called from main context */ /* Called from main context */
pa_queue *pa_source_move_all_start(pa_source *s) { pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) {
pa_queue *q;
pa_source_output *o, *n; pa_source_output *o, *n;
uint32_t idx; uint32_t idx;
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_assert(PA_SOURCE_IS_LINKED(s->state));
q = pa_queue_new(); if (!q)
q = pa_queue_new();
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) { for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)); n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
@ -666,7 +721,7 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
} }
/* Called from main thread */ /* Called from main thread */
void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save) {
pa_cvolume old_virtual_volume; pa_cvolume old_virtual_volume;
pa_bool_t virtual_volume_changed; pa_bool_t virtual_volume_changed;
@ -679,6 +734,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
old_virtual_volume = s->virtual_volume; old_virtual_volume = s->virtual_volume;
s->virtual_volume = *volume; s->virtual_volume = *volume;
virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume); virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
if (s->set_volume) { if (s->set_volume) {
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
@ -724,20 +780,24 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
} }
/* Called from main thread */ /* Called from main thread */
void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) { void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save) {
pa_source_assert_ref(s); pa_source_assert_ref(s);
/* The source implementor may call this if the volume changed to make sure everyone is notified */ /* The source implementor may call this if the volume changed to make sure everyone is notified */
if (pa_cvolume_equal(&s->virtual_volume, new_volume)) if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
s->save_volume = s->save_volume || save;
return; return;
}
s->virtual_volume = *new_volume; s->virtual_volume = *new_volume;
s->save_volume = save;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
} }
/* Called from main thread */ /* Called from main thread */
void pa_source_set_mute(pa_source *s, pa_bool_t mute) { void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted; pa_bool_t old_muted;
pa_source_assert_ref(s); pa_source_assert_ref(s);
@ -745,6 +805,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
old_muted = s->muted; old_muted = s->muted;
s->muted = mute; s->muted = mute;
s->save_muted = (old_muted == s->muted && s->save_muted) || save;
if (s->set_mute) if (s->set_mute)
s->set_mute(s); s->set_mute(s);
@ -780,15 +841,19 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
} }
/* Called from main thread */ /* Called from main thread */
void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) { void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) {
pa_source_assert_ref(s); pa_source_assert_ref(s);
/* The source implementor may call this if the mute state changed to make sure everyone is notified */ /* The source implementor may call this if the mute state changed to make sure everyone is notified */
if (s->muted == new_muted) if (s->muted == new_muted) {
s->save_muted = s->save_muted || save;
return; return;
}
s->muted = new_muted; s->muted = new_muted;
s->save_muted = save;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
} }
@ -865,7 +930,7 @@ unsigned pa_source_check_suspend(pa_source *s) {
ret = 0; ret = 0;
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx))) { PA_IDXSET_FOREACH(o, s->outputs, idx) {
pa_source_output_state_t st; pa_source_output_state_t st;
st = pa_source_output_get_state(o); st = pa_source_output_get_state(o);
@ -1322,3 +1387,38 @@ size_t pa_source_get_max_rewind(pa_source *s) {
return r; return r;
} }
/* Called from main context */
int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
pa_device_port *port;
pa_assert(s);
if (!s->set_port) {
pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
return -PA_ERR_NOTIMPLEMENTED;
}
if (!s->ports)
return -PA_ERR_NOENTITY;
if (!(port = pa_hashmap_get(s->ports, name)))
return -PA_ERR_NOENTITY;
if (s->active_port == port) {
s->save_port = s->save_port || save;
return 0;
}
if ((s->set_port(s, port)) < 0)
return -PA_ERR_NOENTITY;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name);
s->active_port = port;
s->save_port = save;
return 0;
}

View file

@ -56,6 +56,7 @@ struct pa_source {
uint32_t index; uint32_t index;
pa_core *core; pa_core *core;
pa_source_state_t state; pa_source_state_t state;
pa_source_flags_t flags; pa_source_flags_t flags;
pa_suspend_cause_t suspend_cause; pa_suspend_cause_t suspend_cause;
@ -83,6 +84,10 @@ struct pa_source {
pa_bool_t refresh_volume:1; pa_bool_t refresh_volume:1;
pa_bool_t refresh_muted:1; pa_bool_t refresh_muted:1;
pa_bool_t save_port:1;
pa_bool_t save_volume:1;
pa_bool_t save_muted:1;
pa_asyncmsgq *asyncmsgq; pa_asyncmsgq *asyncmsgq;
pa_rtpoll *rtpoll; pa_rtpoll *rtpoll;
@ -90,6 +95,9 @@ struct pa_source {
pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */ pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
pa_hashmap *ports;
pa_device_port *active_port;
/* Called when the main loop requests a state change. Called from /* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be * main loop context. If returns -1 the state change will be
* inhibited */ * inhibited */
@ -121,6 +129,10 @@ struct pa_source {
* thread context. */ * thread context. */
void (*update_requested_latency)(pa_source *s); /* dito */ void (*update_requested_latency)(pa_source *s); /* dito */
/* Called whenever the port shall be changed. Called from main
* thread. */
int (*set_port)(pa_source *s, pa_device_port *port); /*dito */
/* Contains copies of the above data so that the real-time worker /* Contains copies of the above data so that the real-time worker
* thread can work without access locking */ * thread can work without access locking */
struct { struct {
@ -174,6 +186,9 @@ typedef struct pa_source_new_data {
pa_module *module; pa_module *module;
pa_card *card; pa_card *card;
pa_hashmap *ports;
char *active_port;
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; pa_channel_map channel_map;
pa_cvolume volume; pa_cvolume volume;
@ -185,6 +200,10 @@ typedef struct pa_source_new_data {
pa_bool_t channel_map_is_set:1; pa_bool_t channel_map_is_set:1;
pa_bool_t namereg_fail:1; pa_bool_t namereg_fail:1;
pa_bool_t save_port:1;
pa_bool_t save_volume:1;
pa_bool_t save_muted:1;
} pa_source_new_data; } pa_source_new_data;
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data); pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
@ -193,6 +212,7 @@ void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sampl
void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map); void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume); void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute); void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
void pa_source_new_data_set_port(pa_source_new_data *data, const char *port);
void pa_source_new_data_done(pa_source_new_data *data); void pa_source_new_data_done(pa_source_new_data *data);
/*** To be called exclusively by the source driver, from main context */ /*** To be called exclusively by the source driver, from main context */
@ -217,8 +237,8 @@ void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s); void pa_source_attach(pa_source *s);
void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume); void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save);
void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted); void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save);
int pa_source_sync_suspend(pa_source *s); int pa_source_sync_suspend(pa_source *s);
@ -235,20 +255,22 @@ int pa_source_update_status(pa_source*s);
int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause); int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause);
int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause); int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save);
const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh); const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
void pa_source_set_mute(pa_source *source, pa_bool_t mute); void pa_source_set_mute(pa_source *source, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh); pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p); pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save);
unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */ unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */ unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_source_get_state(s) ((pa_source_state_t) (s)->state) #define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
/* Moves all inputs away, and stores them in pa_queue */ /* Moves all inputs away, and stores them in pa_queue */
pa_queue *pa_source_move_all_start(pa_source *s); pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q);
void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save); void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save);
void pa_source_move_all_fail(pa_queue *q); void pa_source_move_all_fail(pa_queue *q);

View file

@ -49,8 +49,21 @@
static pa_context *context = NULL; static pa_context *context = NULL;
static pa_mainloop_api *mainloop_api = NULL; static pa_mainloop_api *mainloop_api = NULL;
static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL, *card_name = NULL, *profile_name = NULL; static char
static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX; *device = NULL,
*sample_name = NULL,
*sink_name = NULL,
*source_name = NULL,
*module_name = NULL,
*module_args = NULL,
*card_name = NULL,
*profile_name = NULL,
*port_name = NULL;
static uint32_t
sink_input_idx = PA_INVALID_INDEX,
source_output_idx = PA_INVALID_INDEX;
static uint32_t module_index; static uint32_t module_index;
static pa_bool_t suspend; static pa_bool_t suspend;
@ -80,7 +93,9 @@ static enum {
UNLOAD_MODULE, UNLOAD_MODULE,
SUSPEND_SINK, SUSPEND_SINK,
SUSPEND_SOURCE, SUSPEND_SOURCE,
SET_CARD_PROFILE SET_CARD_PROFILE,
SET_SINK_PORT,
SET_SOURCE_PORT
} action = NONE; } action = NONE;
static void quit(int ret) { static void quit(int ret) {
@ -239,6 +254,18 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
pa_xfree(pl); pa_xfree(pl);
if (i->ports) {
pa_sink_port_info **p;
printf(_("\tPorts:\n"));
for (p = i->ports; *p; p++)
printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
}
if (i->active_port)
printf(_("\tActive Port: %s\n"),
i->active_port->name);
} }
static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) { static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
@ -319,6 +346,18 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
pa_xfree(pl); pa_xfree(pl);
if (i->ports) {
pa_source_port_info **p;
printf(_("\tPorts:\n"));
for (p = i->ports; *p; p++)
printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
}
if (i->active_port)
printf(_("\tActive Port: %s\n"),
i->active_port->name);
} }
static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) { static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
@ -753,6 +792,14 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL)); pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
break; break;
case SET_SINK_PORT:
pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
break;
case SET_SOURCE_PORT:
pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
break;
default: default:
pa_assert_not_reached(); pa_assert_not_reached();
} }
@ -788,12 +835,14 @@ static void help(const char *argv0) {
"%s [options] unload-module ID\n" "%s [options] unload-module ID\n"
"%s [options] suspend-sink [SINK] 1|0\n" "%s [options] suspend-sink [SINK] 1|0\n"
"%s [options] suspend-source [SOURCE] 1|0\n" "%s [options] suspend-source [SOURCE] 1|0\n"
"%s [options] set-card-profile [CARD] [PROFILE] \n\n" "%s [options] set-card-profile [CARD] [PROFILE] \n"
"%s [options] set-sink-port [SINK] [PORT] \n"
"%s [options] set-source-port [SOURCE] [PORT] \n\n"
" -h, --help Show this help\n" " -h, --help Show this help\n"
" --version Show version\n\n" " --version Show version\n\n"
" -s, --server=SERVER The name of the server to connect to\n" " -s, --server=SERVER The name of the server to connect to\n"
" -n, --client-name=NAME How to call this client on the server\n"), " -n, --client-name=NAME How to call this client on the server\n"),
argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0); argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
} }
enum { enum {
@ -1017,6 +1066,28 @@ int main(int argc, char *argv[]) {
card_name = pa_xstrdup(argv[optind+1]); card_name = pa_xstrdup(argv[optind+1]);
profile_name = pa_xstrdup(argv[optind+2]); profile_name = pa_xstrdup(argv[optind+2]);
} else if (pa_streq(argv[optind], "set-sink-port")) {
action = SET_SINK_PORT;
if (argc != optind+3) {
pa_log(_("You have to specify a sink name/index and a port name\n"));
goto quit;
}
sink_name = pa_xstrdup(argv[optind+1]);
port_name = pa_xstrdup(argv[optind+2]);
} else if (pa_streq(argv[optind], "set-source-port")) {
action = SET_SOURCE_PORT;
if (argc != optind+3) {
pa_log(_("You have to specify a source name/index and a port name\n"));
goto quit;
}
source_name = pa_xstrdup(argv[optind+1]);
port_name = pa_xstrdup(argv[optind+2]);
} else if (pa_streq(argv[optind], "help")) { } else if (pa_streq(argv[optind], "help")) {
help(bn); help(bn);
ret = 0; ret = 0;