Merge branch 'master' into dbus-work

Conflicts:
	src/daemon/daemon-conf.c
	src/daemon/daemon-conf.h
	src/daemon/main.c
	src/pulsecore/dbus-util.h
This commit is contained in:
Tanu Kaskinen 2009-06-29 18:35:06 +03:00
commit 0bc538b08c
207 changed files with 33341 additions and 18718 deletions

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

@ -32,16 +32,18 @@
#include <valgrind/memcheck.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/timeval.h>
#include <pulse/i18n.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/log.h>
@ -50,7 +52,6 @@
#include <pulsecore/core-error.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/time-smoother.h>
#include <modules/reserve-wrap.h>
@ -80,11 +81,9 @@ struct userdata {
pa_alsa_fdlist *mixer_fdl;
snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem;
long hw_volume_max, hw_volume_min;
long hw_dB_max, hw_dB_min;
pa_bool_t hw_dB_supported:1;
pa_bool_t mixer_seperate_channels:1;
pa_alsa_path_set *mixer_path_set;
pa_alsa_path *mixer_path;
pa_cvolume hardware_volume;
size_t
@ -100,7 +99,8 @@ struct userdata {
unsigned nfragments;
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;
@ -116,6 +116,8 @@ struct userdata {
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
pa_reserve_monitor_wrapper *monitor;
pa_hook_slot *monitor_slot;
};
static void userdata_free(struct userdata *u);
@ -124,7 +126,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u
pa_assert(r);
pa_assert(u);
if (pa_sink_suspend(u->sink, TRUE) < 0)
if (pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_APPLICATION) < 0)
return PA_HOOK_CANCEL;
return PA_HOOK_OK;
@ -185,6 +187,57 @@ static int reserve_init(struct userdata *u, const char *dname) {
return 0;
}
static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
pa_bool_t b;
pa_assert(w);
pa_assert(u);
b = PA_PTR_TO_UINT(busy) && !u->reserve;
pa_sink_suspend(u->sink, b, PA_SUSPEND_APPLICATION);
return PA_HOOK_OK;
}
static void monitor_done(struct userdata *u) {
pa_assert(u);
if (u->monitor_slot) {
pa_hook_slot_free(u->monitor_slot);
u->monitor_slot = NULL;
}
if (u->monitor) {
pa_reserve_monitor_wrapper_unref(u->monitor);
u->monitor = NULL;
}
}
static int reserve_monitor_init(struct userdata *u, const char *dname) {
char *rname;
pa_assert(u);
pa_assert(dname);
if (pa_in_system_mode())
return 0;
/* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
pa_xfree(rname);
if (!(u->monitor))
return -1;
pa_assert(!u->monitor_slot);
u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u);
return 0;
}
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
@ -655,7 +708,7 @@ static void update_smoother(struct userdata *u) {
/* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
if (now1 <= 0)
now1 = pa_rtclock_usec();
now1 = pa_rtclock_now();
now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec);
@ -669,7 +722,7 @@ static pa_usec_t sink_get_latency(struct userdata *u) {
pa_assert(u);
now1 = pa_rtclock_usec();
now1 = pa_rtclock_now();
now2 = pa_smoother_get(u->smoother, now1);
delay = (int64_t) pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now2;
@ -700,7 +753,7 @@ static int suspend(struct userdata *u) {
pa_assert(u);
pa_assert(u->pcm_handle);
pa_smoother_pause(u->smoother, pa_rtclock_usec());
pa_smoother_pause(u->smoother, pa_rtclock_now());
/* Let's suspend -- we don't call snd_pcm_drain() here since that might
* take awfully long with our long buffer sizes today. */
@ -786,7 +839,6 @@ static int unsuspend(struct userdata *u) {
pa_log_info("Trying resume...");
snd_config_update_free_global();
if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK,
/*SND_PCM_NONBLOCK|*/
SND_PCM_NO_AUTO_RESAMPLE|
@ -938,191 +990,58 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
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) {
struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
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;
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));
}
}
/* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
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) {
pa_cvolume reset;
if (u->mixer_path->has_dB) {
pa_cvolume reset;
/* Hmm, so the hardware volume changed, let's reset our software volume */
pa_cvolume_reset(&reset, s->sample_spec.channels);
pa_sink_set_soft_volume(s, &reset);
}
/* Hmm, so the hardware volume changed, let's reset our software volume */
pa_cvolume_reset(&reset, s->sample_spec.channels);
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) {
struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
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++) {
long alsa_vol;
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));
}
}
/* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
u->hardware_volume = r;
if (u->hw_dB_supported) {
char t[PA_CVOLUME_SNPRINT_MAX];
if (u->mixer_path->has_dB) {
/* Match exactly what the user requested by software */
pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
@ -1131,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("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
* at least tell the user about it */
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) {
struct userdata *u = s->userdata;
int err, sw;
pa_bool_t b;
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) {
pa_log_error("Unable to get switch: %s", pa_alsa_strerror(err));
if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
return;
}
s->muted = !sw;
s->muted = b;
}
static void sink_set_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
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_log_error("Unable to set switch: %s", pa_alsa_strerror(err));
return;
pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted);
}
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) {
@ -1264,7 +1213,6 @@ static void thread_func(void *userdata) {
pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;
@ -1298,7 +1246,7 @@ static void thread_func(void *userdata) {
pa_log_info("Starting playback.");
snd_pcm_start(u->pcm_handle);
pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
}
update_smoother(u);
@ -1327,7 +1275,7 @@ static void thread_func(void *userdata) {
/* Convert from the sound card time domain to the
* system time domain */
cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec);
/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
@ -1386,7 +1334,7 @@ finish:
pa_log_debug("Thread shutting down");
}
static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name, pa_alsa_mapping *mapping) {
const char *n;
char *t;
@ -1407,82 +1355,136 @@ static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *de
data->namereg_fail = FALSE;
}
t = pa_sprintf_malloc("alsa_output.%s", n);
if (mapping)
t = pa_sprintf_malloc("alsa_output.%s.%s", n, mapping->name);
else
t = pa_sprintf_malloc("alsa_output.%s", n);
pa_sink_new_data_set_name(data, 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) {
pa_assert(u);
if (!u->mixer_handle)
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)) {
pa_bool_t suitable = FALSE;
/* We have a list of supported paths, so let's activate the
* 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)
pa_log_info("Failed to get volume range. Falling back to software volume control.");
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;
}
data = PA_DEVICE_PORT_DATA(u->sink->active_port);
u->mixer_path = data->path;
if (suitable) {
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
pa_alsa_path_select(data->path, u->mixer_handle);
if (u->hw_dB_min >= u->hw_dB_max)
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);
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 (data->setting)
pa_alsa_setting_select(data->setting, u->mixer_handle);
if (u->hw_dB_max > 0) {
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");
}
}
} else {
if (!u->hw_dB_supported &&
u->hw_volume_max - u->hw_volume_min < 3) {
if (!u->mixer_path && u->mixer_path_set)
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.");
suitable = FALSE;
}
}
if (u->mixer_path) {
/* Hmm, we have only a single path, then let's activate it */
if (suitable) {
u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->sink->channel_map, u->mixer_map, TRUE) >= 0;
pa_alsa_path_select(u->mixer_path, u->mixer_handle);
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->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;
if (u->mixer_path->settings)
pa_alsa_setting_select(u->mixer_path->settings, u->mixer_handle);
} 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->set_mute = sink_set_mute_cb;
u->sink->flags |= PA_SINK_HW_MUTE_CTRL;
} else
pa_log_info("Using software mute control.");
pa_log_info("Using hardware mute control.");
}
u->mixer_fdl = pa_alsa_fdlist_new();
@ -1491,13 +1493,15 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
return -1;
}
snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
snd_mixer_elem_set_callback_private(u->mixer_elem, u);
if (u->mixer_path_set)
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;
}
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;
const char *dev_id = NULL;
@ -1508,7 +1512,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_sink_new_data data;
char *control_device = NULL;
pa_alsa_profile_set *profile_set = NULL;
pa_assert(m);
pa_assert(ma);
@ -1577,43 +1581,51 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
TRUE,
TRUE,
5,
pa_rtclock_usec(),
pa_rtclock_now(),
TRUE);
if (reserve_init(u, pa_modargs_get_value(
ma, "device_id",
pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0)
dev_id = pa_modargs_get_value(
ma, "device_id",
pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
if (reserve_init(u, dev_id) < 0)
goto fail;
if (reserve_monitor_init(u, dev_id) < 0)
goto fail;
b = use_mmap;
d = use_tsched;
if (profile) {
if (mapping) {
if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
pa_log("device_id= not set");
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,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames,
&b, &d, profile)))
&b, &d, mapping)))
goto fail;
} 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(
dev_id,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames,
&b, &d, &profile)))
&b, &d, profile_set, &mapping)))
goto fail;
@ -1627,7 +1639,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&nfrags, &period_frames, tsched_frames,
&b, &d, FALSE)))
goto fail;
}
pa_assert(u->device_name);
@ -1638,8 +1649,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail;
}
if (profile)
pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
if (mapping)
pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name);
if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
@ -1665,33 +1676,31 @@ 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 */
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);
data.driver = driver;
data.module = m;
data.card = card;
set_sink_name(&data, ma, dev_id, u->device_name);
set_sink_name(&data, ma, dev_id, u->device_name, mapping);
pa_sink_new_data_set_sample_spec(&data, &ss);
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_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_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
if (profile) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
if (mapping) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
}
pa_alsa_init_description(data.proplist);
if (control_device) {
pa_alsa_init_proplist_ctl(data.proplist, control_device);
pa_xfree(control_device);
}
if (u->control_device)
pa_alsa_init_proplist_ctl(data.proplist, u->control_device);
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
pa_log("Invalid properties");
@ -1699,6 +1708,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
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));
pa_sink_new_data_done(&data);
@ -1710,6 +1722,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->update_requested_latency = sink_update_requested_latency_cb;
u->sink->set_state = sink_set_state_cb;
u->sink->set_port = sink_set_port_cb;
u->sink->userdata = u;
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
@ -1778,6 +1791,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_sink_put(u->sink);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return u->sink;
fail:
@ -1785,6 +1801,9 @@ fail:
if (u)
userdata_free(u);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return NULL;
}
@ -1813,23 +1832,30 @@ static void userdata_free(struct userdata *u) {
if (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) {
snd_pcm_drop(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)
pa_smoother_free(u->smoother);
reserve_done(u);
monitor_done(u);
pa_xfree(u->device_name);
pa_xfree(u->control_device);
pa_xfree(u);
}

View file

@ -28,8 +28,9 @@
#include <pulsecore/sink.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);

View file

@ -28,14 +28,11 @@
#include <asoundlib.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/timeval.h>
#include <pulse/i18n.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core.h>
@ -43,6 +40,7 @@
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/log.h>
@ -52,7 +50,6 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
#include <modules/reserve-wrap.h>
@ -81,11 +78,8 @@ struct userdata {
pa_alsa_fdlist *mixer_fdl;
snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem;
long hw_volume_max, hw_volume_min;
long hw_dB_max, hw_dB_min;
pa_bool_t hw_dB_supported:1;
pa_bool_t mixer_seperate_channels:1;
pa_alsa_path_set *mixer_path_set;
pa_alsa_path *mixer_path;
pa_cvolume hardware_volume;
@ -102,6 +96,7 @@ struct userdata {
unsigned nfragments;
char *device_name;
char *control_device;
pa_bool_t use_mmap:1, use_tsched:1;
@ -114,6 +109,8 @@ struct userdata {
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
pa_reserve_monitor_wrapper *monitor;
pa_hook_slot *monitor_slot;
};
static void userdata_free(struct userdata *u);
@ -122,7 +119,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u
pa_assert(r);
pa_assert(u);
if (pa_source_suspend(u->source, TRUE) < 0)
if (pa_source_suspend(u->source, TRUE, PA_SUSPEND_APPLICATION) < 0)
return PA_HOOK_CANCEL;
return PA_HOOK_OK;
@ -183,6 +180,57 @@ static int reserve_init(struct userdata *u, const char *dname) {
return 0;
}
static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
pa_bool_t b;
pa_assert(w);
pa_assert(u);
b = PA_PTR_TO_UINT(busy) && !u->reserve;
pa_source_suspend(u->source, b, PA_SUSPEND_APPLICATION);
return PA_HOOK_OK;
}
static void monitor_done(struct userdata *u) {
pa_assert(u);
if (u->monitor_slot) {
pa_hook_slot_free(u->monitor_slot);
u->monitor_slot = NULL;
}
if (u->monitor) {
pa_reserve_monitor_wrapper_unref(u->monitor);
u->monitor = NULL;
}
}
static int reserve_monitor_init(struct userdata *u, const char *dname) {
char *rname;
pa_assert(u);
pa_assert(dname);
if (pa_in_system_mode())
return 0;
/* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
pa_xfree(rname);
if (!(u->monitor))
return -1;
pa_assert(!u->monitor_slot);
u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u);
return 0;
}
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
pa_assert(u);
@ -622,7 +670,7 @@ static void update_smoother(struct userdata *u) {
/* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
if (now1 <= 0)
now1 = pa_rtclock_usec();
now1 = pa_rtclock_now();
now2 = pa_bytes_to_usec(position, &u->source->sample_spec);
@ -635,7 +683,7 @@ static pa_usec_t source_get_latency(struct userdata *u) {
pa_assert(u);
now1 = pa_rtclock_usec();
now1 = pa_rtclock_now();
now2 = pa_smoother_get(u->smoother, now1);
delay = (int64_t) now2 - (int64_t) pa_bytes_to_usec(u->read_count, &u->source->sample_spec);
@ -660,7 +708,7 @@ static int suspend(struct userdata *u) {
pa_assert(u);
pa_assert(u->pcm_handle);
pa_smoother_pause(u->smoother, pa_rtclock_usec());
pa_smoother_pause(u->smoother, pa_rtclock_now());
/* Let's suspend */
snd_pcm_close(u->pcm_handle);
@ -740,8 +788,6 @@ static int unsuspend(struct userdata *u) {
pa_log_info("Trying resume...");
snd_config_update_free_global();
if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE,
/*SND_PCM_NONBLOCK|*/
SND_PCM_NO_AUTO_RESAMPLE|
@ -788,7 +834,7 @@ static int unsuspend(struct userdata *u) {
/* FIXME: We need to reload the volume somehow */
snd_pcm_start(u->pcm_handle);
pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
pa_log_info("Resumed successfully...");
@ -896,239 +942,135 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
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) {
struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
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;
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));
}
}
/* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
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) {
pa_cvolume reset;
if (u->mixer_path->has_dB) {
pa_cvolume reset;
/* Hmm, so the hardware volume changed, let's reset our software volume */
pa_cvolume_reset(&reset, s->sample_spec.channels);
pa_source_set_soft_volume(s, &reset);
}
/* Hmm, so the hardware volume changed, let's reset our software volume */
pa_cvolume_reset(&reset, s->sample_spec.channels);
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) {
struct userdata *u = s->userdata;
int err;
unsigned i;
pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
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++) {
long alsa_vol;
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));
}
}
/* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
u->hardware_volume = r;
if (u->hw_dB_supported) {
char t[PA_CVOLUME_SNPRINT_MAX];
if (u->mixer_path->has_dB) {
/* Match exactly what the user requested by software */
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("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));
} 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
* at least tell the user about it */
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) {
struct userdata *u = s->userdata;
int err, sw;
pa_bool_t b;
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) {
pa_log_error("Unable to get switch: %s", pa_alsa_strerror(err));
if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
return;
}
s->muted = !sw;
s->muted = b;
}
static void source_set_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
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_log_error("Unable to set switch: %s", pa_alsa_strerror(err));
return;
pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted);
}
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) {
@ -1153,7 +1095,6 @@ static void thread_func(void *userdata) {
pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;
@ -1190,7 +1131,7 @@ static void thread_func(void *userdata) {
/* Convert from the sound card time domain to the
* system time domain */
cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec);
/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
@ -1244,7 +1185,7 @@ finish:
pa_log_debug("Thread shutting down");
}
static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name, pa_alsa_mapping *mapping) {
const char *n;
char *t;
@ -1265,82 +1206,136 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char
data->namereg_fail = FALSE;
}
t = pa_sprintf_malloc("alsa_input.%s", n);
if (mapping)
t = pa_sprintf_malloc("alsa_input.%s.%s", n, mapping->name);
else
t = pa_sprintf_malloc("alsa_input.%s", n);
pa_source_new_data_set_name(data, 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) {
pa_assert(u);
if (!u->mixer_handle)
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)) {
pa_bool_t suitable = FALSE;
/* We have a list of supported paths, so let's activate the
* 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)
pa_log_info("Failed to get volume range. Falling back to software volume control.");
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;
}
data = PA_DEVICE_PORT_DATA(u->source->active_port);
u->mixer_path = data->path;
if (suitable) {
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
pa_alsa_path_select(data->path, u->mixer_handle);
if (u->hw_dB_min >= u->hw_dB_max)
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);
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 (data->setting)
pa_alsa_setting_select(data->setting, u->mixer_handle);
if (u->hw_dB_max > 0) {
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");
}
}
} else {
if (!u->hw_dB_supported &&
u->hw_volume_max - u->hw_volume_min < 3) {
if (!u->mixer_path && u->mixer_path_set)
u->mixer_path = u->mixer_path_set->paths;
pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
suitable = FALSE;
}
}
if (u->mixer_path) {
/* Hmm, we have only a single path, then let's activate it */
if (suitable) {
u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->source->channel_map, u->mixer_map, FALSE) >= 0;
pa_alsa_path_select(u->mixer_path, u->mixer_handle);
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->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;
if (u->mixer_path->settings)
pa_alsa_setting_select(u->mixer_path->settings, u->mixer_handle);
} 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->set_mute = source_set_mute_cb;
u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
} else
pa_log_info("Using software mute control.");
pa_log_info("Using hardware mute control.");
}
u->mixer_fdl = pa_alsa_fdlist_new();
@ -1349,13 +1344,15 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
return -1;
}
snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
snd_mixer_elem_set_callback_private(u->mixer_elem, u);
if (u->mixer_path_set)
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;
}
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;
const char *dev_id = NULL;
@ -1366,7 +1363,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data;
char *control_device = NULL;
pa_alsa_profile_set *profile_set = NULL;
pa_assert(m);
pa_assert(ma);
@ -1427,7 +1424,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->use_tsched = use_tsched;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->alsa_rtpoll_item = NULL;
u->smoother = pa_smoother_new(
DEFAULT_TSCHED_WATERMARK_USEC*2,
@ -1435,42 +1431,50 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
TRUE,
TRUE,
5,
pa_rtclock_usec(),
pa_rtclock_now(),
FALSE);
if (reserve_init(u, pa_modargs_get_value(
ma, "device_id",
pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0)
dev_id = pa_modargs_get_value(
ma, "device_id",
pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
if (reserve_init(u, dev_id) < 0)
goto fail;
if (reserve_monitor_init(u, dev_id) < 0)
goto fail;
b = use_mmap;
d = use_tsched;
if (profile) {
if (mapping) {
if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
pa_log("device_id= not set");
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,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames,
&b, &d, profile)))
&b, &d, mapping)))
goto fail;
} 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(
dev_id,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames,
&b, &d, &profile)))
&b, &d, profile_set, &mapping)))
goto fail;
} else {
@ -1493,8 +1497,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail;
}
if (profile)
pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
if (mapping)
pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name);
if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
@ -1520,33 +1524,31 @@ 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 */
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);
data.driver = driver;
data.module = m;
data.card = card;
set_source_name(&data, ma, dev_id, u->device_name);
set_source_name(&data, ma, dev_id, u->device_name, mapping);
pa_source_new_data_set_sample_spec(&data, &ss);
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_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_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
if (profile) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
if (mapping) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
}
pa_alsa_init_description(data.proplist);
if (control_device) {
pa_alsa_init_proplist_ctl(data.proplist, control_device);
pa_xfree(control_device);
}
if (u->control_device)
pa_alsa_init_proplist_ctl(data.proplist, u->control_device);
if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
pa_log("Invalid properties");
@ -1554,6 +1556,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
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));
pa_source_new_data_done(&data);
@ -1565,6 +1570,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->update_requested_latency = source_update_requested_latency_cb;
u->source->set_state = source_set_state_cb;
u->source->set_port = source_set_port_cb;
u->source->userdata = u;
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
@ -1629,6 +1635,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_source_put(u->source);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return u->source;
fail:
@ -1636,6 +1645,9 @@ fail:
if (u)
userdata_free(u);
if (profile_set)
pa_alsa_profile_set_free(profile_set);
return NULL;
}
@ -1661,23 +1673,30 @@ static void userdata_free(struct userdata *u) {
if (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) {
snd_pcm_drop(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)
pa_smoother_free(u->smoother);
reserve_done(u);
monitor_done(u);
pa_xfree(u->device_name);
pa_xfree(u->control_device);
pa_xfree(u);
}

View file

@ -29,7 +29,7 @@
#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);

File diff suppressed because it is too large Load diff

View file

@ -30,105 +30,97 @@
#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;
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);
#include "alsa-mixer.h"
int pa_alsa_set_hw_params(
snd_pcm_t *pcm_handle,
pa_sample_spec *ss,
uint32_t *periods,
snd_pcm_uframes_t *period_size,
pa_sample_spec *ss, /* modified at return */
uint32_t *periods, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
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 {
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 */
/* Picks a working mapping from the profile set based on the specified ss/map */
snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id,
char **dev,
pa_sample_spec *ss,
pa_channel_map* map,
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
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,
pa_bool_t *use_tsched,
const pa_alsa_profile_info **profile);
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
pa_alsa_profile_set *ps,
pa_alsa_mapping **mapping); /* modified at return */
/* Uses the specified profile */
snd_pcm_t *pa_alsa_open_by_device_id_profile(
/* Uses the specified mapping */
snd_pcm_t *pa_alsa_open_by_device_id_mapping(
const char *dev_id,
char **dev,
pa_sample_spec *ss,
pa_channel_map* map,
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
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,
pa_bool_t *use_tsched,
const pa_alsa_profile_info *profile);
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
pa_alsa_mapping *mapping);
/* Opens the explicit ALSA device */
snd_pcm_t *pa_alsa_open_by_device_string(
const char *device,
char **dev,
pa_sample_spec *ss,
pa_channel_map* map,
const char *dir,
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
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,
pa_bool_t *use_tsched,
pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */
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 pa_sample_spec *ss,
void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata),
void *userdata);
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);
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
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_status(snd_pcm_t *pcm);
void pa_alsa_redirect_errors_inc(void);
void pa_alsa_redirect_errors_dec(void);
void pa_alsa_refcnt_inc(void);
void pa_alsa_refcnt_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_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);
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);
char *pa_alsa_get_driver_name(int card);
char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);
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_modem(snd_pcm_t *pcm);
const char* pa_alsa_strerror(int errnum);

View file

@ -0,0 +1,62 @@
# This file is part of PulseAudio.
#
# 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.
; For devices where an 'Aux' element exists
;
; See analog-output.conf.common for an explanation on the directives
[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
[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,62 @@
# This file is part of PulseAudio.
#
# 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.
; For devices where an 'FM' element exists
;
; See analog-output.conf.common for an explanation on the directives
[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,61 @@
# This file is part of PulseAudio.
#
# 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.
; For devices where a 'Line' element exists
;
; See analog-output.conf.common for an explanation on the directives
[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,63 @@
# This file is part of PulseAudio.
#
# 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.
; For devices where a 'Mic/Line' element exists
;
; See analog-output.conf.common for an explanation on the directives
[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
.include analog-input-mic.conf.common

View file

@ -0,0 +1,63 @@
# This file is part of PulseAudio.
#
# 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.
; For devices where a 'Mic' element exists
;
; See analog-output.conf.common for an explanation on the directives
[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,63 @@
# This file is part of PulseAudio.
#
# 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.
; Common element for all microphone inputs
;
; See analog-output.conf.common for an explanation on the directives
;;; '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
volume = merge
[Option Mic Boost (+20dB):on]
name = input-boost-on
[Option Mic Boost (+20dB):off]
name = input-boost-off
[Element Mic Boost]
switch = select
volume = merge
[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,62 @@
# This file is part of PulseAudio.
#
# 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.
; For devices where a 'TV Tuner' element exists
;
; See analog-output.conf.common for an explanation on the directives
[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,61 @@
# This file is part of PulseAudio.
#
# 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.
; For devices where a 'Video' element exists
;
; See analog-output.conf.common for an explanation on the directives
[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
[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,54 @@
# This file is part of PulseAudio.
#
# 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.
; A fallback for devices that lack seperate Mic/Line/Aux/Video/TV
; Tuner/FM elements
;
; See analog-output.conf.common for an explanation on the directives
[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,257 @@
# This file is part of PulseAudio.
#
# 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.
; Mixer path for PulseAudio's ALSA backend, common elements for all
; input paths. 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
;
; See analog-output.conf.common for an explanation on the directives
;;; '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,71 @@
# This file is part of PulseAudio.
#
# 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.
; Path for mixers that have a 'Headphone' control
;
; See analog-output.conf.common for an explanation on the directives
[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,72 @@
# This file is part of PulseAudio.
#
# 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.
; Intended for usage in laptops that have a seperate LFE speaker
; connected to the Master mono connector
;
; See analog-output.conf.common for an explanation on the directives
[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,69 @@
# This file is part of PulseAudio.
#
# 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.
; Intended for usage on boards that have a seperate Mono output plug.
;
; See analog-output.conf.common for an explanation on the directives
[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,80 @@
# This file is part of PulseAudio.
#
# 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.
; Intended for the 'default' output
;
; See analog-output.conf.common for an explanation on the directives
[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,111 @@
# This file is part of PulseAudio.
#
# 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.
; Common part of all paths
; So here's generally how mixer paths are used by PA: PA goes through
; a mixer path file from top to bottom and checks if a mixer element
; described therein exists. If so it is added to the list of mixer
; elements PA will control, keeping the order it read them in. If a
; mixer element described here has set the required= or
; required-absent= directives a path might not be accepted as valid
; and is ignored in its entirety (see below). However usually if a
; element listed here is missing this one element is ignored but not
; the entire path.
;
; When a device shall be muted/unmuted *all* elements listed in a path
; file with "switch = mute" will be toggled.
;
; When a device shall change its volume, PA will got through the list
; of all elements with "volume = merge" and set the volume on the
; first element. If that element does not support dB volumes, this is
; where the story ends. If it does support dB volumes, PA divides the
; requested volume by the volume that was set on this element, and
; then go on to the next element with "volume = merge" and then set
; that there, and so on. That way the first volume element in the
; path will be the one that does the 'biggest' part of the overall
; volume adjustment, with the remaining elements usually being set to
; some value next to 0dB. This logic makes sure we get the full range
; over all volume sliders and a very high granularity of volumes
; already in hardware.
;
; All switches and enumerations set to "select" are exposed via the
; "port" functionality of sinks/sources. Basically every possible
; switch setting and every possible enumeration setting will be
; combined and made into a "port". So make sure you don't list too
; many switches/enums for exposing, because the number of ports might
; rise exponentially.
;
; Only one path can be selected at a time. All paths that are valid
; for an audio device will be exposed as "port" for the sink/source.
; [General]
; priority = ... # Priority for this path
; description = ...
;
; [Option ...:...] # For each option of an enumeration or switch element
; # that shall be exposed as a sink/source port. Needs to
; # be named after the Element, followed by a colon, followed
; # by the option name, resp. on/off if the element is a switch.
; name = ... # Logical name to use in the path identifier
; priority = ... # Priority if this is made into a device port
;
; [Element ...] # For each element that we shall control
; required = ignore | switch | volume | enumeration | any # If set, require this element to be of this kind and available,
; # otherwise don't consider this path valid for the card
; required-absent = ignore | switch | volume # If set, require this element to not be of this kind and not
; # available, otherwise don't consider this path valid for the card
;
; switch = ignore | mute | off | on | select # What to do with this switch: ignore it, make it follow mute status,
; # always set it to off, always to on, or make it selectable as port.
; # If set to 'select' you need to define an Option section for on
; # and off
; volume = ignore | merge | off | zero # What to do with this volume: ignore it, merge it into the device
; # volume slider, always set it to the lowest value possible, or always
; # set it to 0 dB (for whatever that means)
; enumeration = ignore | select # What to do with this enumeration, ignore it or make it selectable
; # via device ports. If set to 'select' you need to define an Option section
; # for each of the items you want to expose
; direction = playback | capture # Is this relevant only for playback or capture? If not set this will implicitly be
; # set the direction of the PCM device is opened as. Generally this doesn't need to be set
; # unless you have a broken driver that has playback controls marked for capture or vice
; # versa
; direction-try-other = no | yes # If the element does not supported what is requested, try the other direction, too?
;
; override-map.1 = ... # Override the channel mask of the mixer control if the control only exposes a single channel
; override-map.2 = ... # Override the channel masks of the mixer control if the control only exposes two channels
; # Override maps should list for each element channel which high-level channels it controls via a
; # channel mask. A channel mask may either be the name of a single channel, or the words "all-left",
; # "all-right", "all-center", "all-front", "all-rear", and "all" to encode a specific subset of
; # channels in a mask
[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,26 @@
# do not edit this file, it will be overwritten on update
# This file is part of PulseAudio.
#
# 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.
SUBSYSTEM!="sound", GOTO="pulseaudio_end"
ACTION!="change", GOTO="pulseaudio_end"
KERNEL!="card*", GOTO="pulseaudio_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_PROFILE_SET}="native-instruments-audio8dj.conf"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="0839", ENV{PULSE_PROFILE_SET}="native-instruments-audio4dj.conf"
LABEL="pulseaudio_end"

View file

@ -0,0 +1,144 @@
# This file is part of PulseAudio.
#
# 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.
; Default profile definitions for the ALSA backend of PulseAudio. This
; is used as fallback for all cards that have no special mapping
; assigned. (and should be good enough for the vast majority of
; cards). Use the udev property PULSE_PROFILE_SET to assign a
; different profile set than this one to a device. So what is this
; about? Simply, what we do here is map ALSA devices to how they are
; exposed in PA. We say which ALSA device string to use to open a
; device, which channel mapping to use then, and which mixer path to
; use. This is encoded in a 'mapping'. Multiple of these mappings can
; be bound together in a 'profile' which is then directly exposed in
; the UI as a card profile. Each mapping assigned to a profile will
; result in one sink/source to be created if the profile is selected
; for the card.
; [General]
; auto-profiles = no | yes # Instead of defining all profiles manually, autogenerate
; # them by combining every input mapping with every output mapping.
;
; [Mapping id]
; device-strings = ... # ALSA device string. %f will be replaced by the card identifier.
; channel-map = ... # Channel mapping to use for this device
; description = ...
; paths-input = ... # A list of mixer paths to use. Every path in this list will be probed.
; # If multiple are found to be working they will be available as device ports
; paths-output = ...
; element-input = ... # Instead of configuring a full mixer path simply configure a single
; # mixer element for volume/mute handling
; element-output = ...
; priority = ...
; direction = any | input | output # Only useful for?
;
; [Profile id]
; input-mappings = ... # Lists mappings for sources on this profile, those mapping must be
; # defined in this file too
; output-mappings = ... # Lists mappings for sinks on this profile, those mappings must be
; # defined in this file too
; description = ...
; priority = ... # Numeric value to deduce priority for this profile
; skip-probe = no | yes # Skip probing for availability? If this is yes then this profile
; # will be assumed as working without probing. Makes initialization
; # a bit faster but only works if the card is really known well.
[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
; An example for defining multiple-sink profiles
#[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,91 @@
# This file is part of PulseAudio.
#
# 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.
; Native Instruments Audio 4 DJ
;
; This card has two stereo pairs of input and two stereo pairs of
; output, named channels A and B. Channel B has an additional
; Headphone connector.
;
; We knowingly only define a subset of the theoretically possible
; mapping combinations as profiles here.
;
; See default.conf for an explanation on the directives used here.
[General]
auto-profiles = no
[Mapping analog-stereo-a]
description = Analog Stereo Channel A
device-strings = hw:%f,0,0
channel-map = left,right
[Mapping analog-stereo-b-output]
description = Analog Stereo Channel B (Headphones)
device-strings = hw:%f,0,1
channel-map = left,right
direction = output
[Mapping analog-stereo-b-input]
description = Analog Stereo Channel B
device-strings = hw:%f,0,1
channel-map = left,right
direction = input
[Profile output:analog-stereo-all+input:analog-stereo-all]
description = Analog Stereo Duplex Channels A, B (Headphones)
output-mappings = analog-stereo-a analog-stereo-b-output
input-mappings = analog-stereo-a analog-stereo-b-input
priority = 100
skip-probe = yes
[Profile output:analog-stereo-a+input:analog-stereo-a]
description = Analog Stereo Duplex Channel A
output-mappings = analog-stereo-a
input-mappings = analog-stereo-a
priority = 40
skip-probe = yes
[Profile output:analog-stereo-b+input:analog-stereo-b]
description = Analog Stereo Duplex Channel B (Headphones)
output-mappings = analog-stereo-b-output
input-mappings = analog-stereo-b-input
priority = 50
skip-probe = yes
[Profile output:analog-stereo-a]
description = Analog Stereo Output Channel A
output-mappings = analog-stereo-a
priority = 5
skip-probe = yes
[Profile output:analog-stereo-b]
description = Analog Stereo Output Channel B (Headphones)
output-mappings = analog-stereo-b-output
priority = 6
skip-probe = yes
[Profile input:analog-stereo-a]
description = Analog Stereo Input Channel A
input-mappings = analog-stereo-a
priority = 2
skip-probe = yes
[Profile input:analog-stereo-b]
description = Analog Stereo Input Channel B
input-mappings = analog-stereo-b-input
priority = 1
skip-probe = yes

View file

@ -0,0 +1,162 @@
# This file is part of PulseAudio.
#
# 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.
; Native Instruments Audio 8 DJ
;
; This card has four stereo pairs of input and four stereo pairs of
; output, named channels A to D. Channel C has an additional Mic/Line
; connector, channel D an additional Headphone connector.
;
; We knowingly only define a subset of the theoretically possible
; mapping combinations as profiles here.
;
; See default.conf for an explanation on the directives used here.
[General]
auto-profiles = no
[Mapping analog-stereo-a]
description = Analog Stereo Channel A
device-strings = hw:%f,0,0
channel-map = left,right
[Mapping analog-stereo-b]
description = Analog Stereo Channel B
device-strings = hw:%f,0,1
channel-map = left,right
# Since we want to set a different description for channel C's/D's input
# and output we define two seperate mappings for them
[Mapping analog-stereo-c-output]
description = Analog Stereo Channel C
device-strings = hw:%f,0,2
channel-map = left,right
direction = output
[Mapping analog-stereo-c-input]
description = Analog Stereo Channel C (Line/Mic)
device-strings = hw:%f,0,2
channel-map = left,right
direction = input
[Mapping analog-stereo-d-output]
description = Analog Stereo Channel D (Headphones)
device-strings = hw:%f,0,3
channel-map = left,right
direction = output
[Mapping analog-stereo-d-input]
description = Analog Stereo Channel D
device-strings = hw:%f,0,3
channel-map = left,right
direction = input
[Profile output:analog-stereo-all+input:analog-stereo-all]
description = Analog Stereo Duplex Channels A, B, C (Line/Mic), D (Headphones)
output-mappings = analog-stereo-a analog-stereo-b analog-stereo-c-output analog-stereo-d-output
input-mappings = analog-stereo-a analog-stereo-b analog-stereo-c-input analog-stereo-d-input
priority = 100
skip-probe = yes
[Profile output:analog-stereo-d+input:analog-stereo-c]
description = Analog Stereo Channel D (Headphones) Output, Channel C (Line/Mic) Input
output-mappings = analog-stereo-d-output
input-mappings = analog-stereo-c-input
priority = 90
skip-probe = yes
[Profile output:analog-stereo-c-d+input:analog-stereo-c-d]
description = Analog Stereo Duplex Channels C (Line/Mic), D (Line/Mic)
output-mappings = analog-stereo-c-output analog-stereo-d-output
input-mappings = analog-stereo-c-input analog-stereo-d-input
priority = 80
skip-probe = yes
[Profile output:analog-stereo-a+input:analog-stereo-a]
description = Analog Stereo Duplex Channel A
output-mappings = analog-stereo-a
input-mappings = analog-stereo-a
priority = 50
skip-probe = yes
[Profile output:analog-stereo-b+input:analog-stereo-b]
description = Analog Stereo Duplex Channel B
output-mappings = analog-stereo-b
input-mappings = analog-stereo-b
priority = 40
skip-probe = yes
[Profile output:analog-stereo-c+input:analog-stereo-c]
description = Analog Stereo Duplex Channel C (Line/Mic)
output-mappings = analog-stereo-c-output
input-mappings = analog-stereo-c-input
priority = 60
skip-probe = yes
[Profile output:analog-stereo-d+input:analog-stereo-d]
description = Analog Stereo Duplex Channel D (Headphones)
output-mappings = analog-stereo-d-output
input-mappings = analog-stereo-d-input
priority = 70
skip-probe = yes
[Profile output:analog-stereo-a]
description = Analog Stereo Output Channel A
output-mappings = analog-stereo-a
priority = 6
skip-probe = yes
[Profile output:analog-stereo-b]
description = Analog Stereo Output Channel B
output-mappings = analog-stereo-b
priority = 5
skip-probe = yes
[Profile output:analog-stereo-c]
description = Analog Stereo Output Channel C
output-mappings = analog-stereo-c-output
priority = 7
skip-probe = yes
[Profile output:analog-stereo-d]
description = Analog Stereo Output Channel D (Headphones)
output-mappings = analog-stereo-d-output
priority = 8
skip-probe = yes
[Profile input:analog-stereo-a]
description = Analog Stereo Input Channel A
input-mappings = analog-stereo-a
priority = 2
skip-probe = yes
[Profile input:analog-stereo-b]
description = Analog Stereo Input Channel B
input-mappings = analog-stereo-b
priority = 1
skip-probe = yes
[Profile input:analog-stereo-c]
description = Analog Stereo Input Channel C (Line/Mic)
input-mappings = analog-stereo-c-input
priority = 4
skip-probe = yes
[Profile input:analog-stereo-d]
description = Analog Stereo Input Channel D
input-mappings = analog-stereo-d-input
priority = 3
skip-probe = yes

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>
#ifdef HAVE_UDEV
#include <modules/udev-util.h>
#endif
#include "alsa-util.h"
#include "alsa-sink.h"
#include "alsa-source.h"
@ -92,81 +96,53 @@ struct userdata {
char *device_id;
pa_card *card;
pa_sink *sink;
pa_source *source;
pa_modargs *modargs;
pa_hashmap *profiles;
pa_alsa_profile_set *profile_set;
};
struct profile_data {
const pa_alsa_profile_info *sink_profile, *source_profile;
pa_alsa_profile *profile;
};
static void enumerate_cb(
const pa_alsa_profile_info *sink,
const pa_alsa_profile_info *source,
void *userdata) {
static void add_profiles(struct userdata *u, pa_hashmap *h) {
pa_alsa_profile *ap;
void *state;
struct userdata *u = userdata;
char *t, *n;
pa_card_profile *p;
struct profile_data *d;
unsigned bonus = 0;
pa_assert(u);
pa_assert(h);
if (sink && source) {
n = pa_sprintf_malloc("output-%s+input-%s", sink->name, source->name);
t = pa_sprintf_malloc(_("Output %s + Input %s"), sink->description, _(source->description));
} else if (sink) {
n = pa_sprintf_malloc("output-%s", sink->name);
t = pa_sprintf_malloc(_("Output %s"), _(sink->description));
} else {
pa_assert(source);
n = pa_sprintf_malloc("input-%s", source->name);
t = pa_sprintf_malloc(_("Input %s"), _(source->description));
PA_HASHMAP_FOREACH(ap, u->profile_set->profiles, state) {
struct profile_data *d;
pa_card_profile *cp;
pa_alsa_mapping *m;
uint32_t idx;
cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data));
cp->priority = ap->priority;
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) {
@ -176,7 +152,7 @@ static void add_disabled_profile(pa_hashmap *profiles) {
p = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data));
d = PA_CARD_PROFILE_DATA(p);
d->sink_profile = d->source_profile = NULL;
d->profile = NULL;
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) {
struct userdata *u;
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(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);
od = PA_CARD_PROFILE_DATA(c->active_profile);
if (od->sink_profile != nd->sink_profile) {
pa_queue *inputs = NULL;
if (od->profile && od->profile->output_mappings)
PA_IDXSET_FOREACH(am, od->profile->output_mappings, idx) {
if (!am->sink)
continue;
if (u->sink) {
if (nd->sink_profile)
inputs = pa_sink_move_all_start(u->sink);
if (nd->profile &&
nd->profile->output_mappings &&
pa_idxset_get_by_data(nd->profile->output_mappings, am, NULL))
continue;
pa_alsa_sink_free(u->sink);
u->sink = NULL;
sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs);
pa_alsa_sink_free(am->sink);
am->sink = NULL;
}
if (nd->sink_profile) {
u->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, nd->sink_profile);
if (od->profile && od->profile->input_mappings)
PA_IDXSET_FOREACH(am, od->profile->input_mappings, idx) {
if (!am->source)
continue;
if (inputs) {
if (u->sink)
pa_sink_move_all_finish(u->sink, inputs, FALSE);
else
pa_sink_move_all_fail(inputs);
if (nd->profile &&
nd->profile->input_mappings &&
pa_idxset_get_by_data(nd->profile->input_mappings, am, NULL))
continue;
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) {
pa_queue *outputs = NULL;
if (nd->profile && nd->profile->input_mappings)
PA_IDXSET_FOREACH(am, nd->profile->input_mappings, idx) {
if (u->source) {
if (nd->source_profile)
outputs = pa_source_move_all_start(u->source);
if (!am->source)
am->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, am);
pa_alsa_source_free(u->source);
u->source = 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 (source_outputs && am->source) {
pa_source_move_all_finish(am->source, source_outputs, FALSE);
source_outputs = NULL;
}
}
}
if (sink_inputs)
pa_sink_move_all_fail(sink_inputs);
if (source_outputs)
pa_source_move_all_fail(source_outputs);
return 0;
}
static void init_profile(struct userdata *u) {
uint32_t idx;
pa_alsa_mapping *am;
struct profile_data *d;
pa_assert(u);
d = PA_CARD_PROFILE_DATA(u->card->active_profile);
if (d->sink_profile)
u->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, d->sink_profile);
if (d->profile && d->profile->output_mappings)
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)
u->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, d->source_profile);
if (d->profile && d->profile->input_mappings)
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) {
@ -286,12 +283,11 @@ int pa__init(pa_module *m) {
pa_modargs *ma;
int alsa_card_index;
struct userdata *u;
char rname[32];
pa_reserve_wrapper *reserve = NULL;
const char *description;
char *fn = NULL;
pa_alsa_redirect_errors_inc();
snd_config_update_free_global();
pa_alsa_refcnt_inc();
pa_assert(m);
@ -300,13 +296,10 @@ int pa__init(pa_module *m) {
goto fail;
}
m->userdata = u = pa_xnew(struct userdata, 1);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
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;
if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
@ -314,16 +307,36 @@ int pa__init(pa_module *m) {
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 (!(reserve = pa_reserve_wrapper_get(m->core, rname)))
goto fail;
if ((rname = pa_alsa_get_reserve_name(u->device_id))) {
reserve = pa_reserve_wrapper_get(m->core, rname);
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);
data.driver = __FILE__;
data.module = m;
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_alsa_init_description(data.proplist);
set_card_name(&data, ma, u->device_id);
@ -332,11 +345,8 @@ int pa__init(pa_module *m) {
if ((description = pa_proplist_gets(data.proplist, PA_PROP_DEVICE_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);
if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, u) < 0) {
pa_card_new_data_done(&data);
goto fail;
}
data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
add_profiles(u, data.profiles);
if (pa_hashmap_isempty(data.profiles)) {
pa_log("Failed to find a working profile.");
@ -379,13 +389,22 @@ fail:
int pa__get_n_used(pa_module *m) {
struct userdata *u;
int n = 0;
uint32_t idx;
pa_sink *sink;
pa_source *source;
pa_assert(m);
pa_assert_se(u = m->userdata);
pa_assert(u->card);
return
(u->sink ? pa_sink_linked_by(u->sink) : 0) +
(u->source ? pa_source_linked_by(u->source) : 0);
PA_IDXSET_FOREACH(sink, u->card->sinks, idx)
n += pa_sink_linked_by(sink);
PA_IDXSET_FOREACH(source, u->card->sources, idx)
n += pa_source_linked_by(source);
return n;
}
void pa__done(pa_module*m) {
@ -396,11 +415,19 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
goto finish;
if (u->sink)
pa_alsa_sink_free(u->sink);
if (u->card && u->card->sinks) {
pa_sink *s;
if (u->source)
pa_alsa_source_free(u->source);
while ((s = pa_idxset_steal_first(u->card->sinks, NULL)))
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)
pa_card_free(u->card);
@ -408,10 +435,12 @@ void pa__done(pa_module*m) {
if (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);
finish:
snd_config_update_free_global();
pa_alsa_redirect_errors_dec();
pa_alsa_refcnt_dec();
}

View file

@ -82,8 +82,7 @@ int pa__init(pa_module*m) {
pa_assert(m);
pa_alsa_redirect_errors_inc();
snd_config_update_free_global();
pa_alsa_refcnt_inc();
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@ -124,6 +123,5 @@ void pa__done(pa_module*m) {
if ((sink = m->userdata))
pa_alsa_sink_free(sink);
snd_config_update_free_global();
pa_alsa_redirect_errors_dec();
pa_alsa_refcnt_dec();
}

View file

@ -37,6 +37,7 @@
#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
#include <pulsecore/memchunk.h>
@ -51,7 +52,6 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
#include "alsa-util.h"
#include "alsa-source.h"
@ -106,8 +106,7 @@ int pa__init(pa_module*m) {
pa_assert(m);
pa_alsa_redirect_errors_inc();
snd_config_update_free_global();
pa_alsa_refcnt_inc();
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@ -148,6 +147,5 @@ void pa__done(pa_module*m) {
if ((source = m->userdata))
pa_alsa_source_free(source);
snd_config_update_free_global();
pa_alsa_redirect_errors_dec();
pa_alsa_refcnt_dec();
}

View file

@ -30,13 +30,15 @@
#include <linux/sockios.h>
#include <arpa/inet.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/sample.h>
#include <pulse/i18n.h>
#include <pulse/rtclock.h>
#include <pulse/sample.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/socket-util.h>
@ -44,7 +46,6 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/namereg.h>
#include <pulsecore/dbus-shared.h>
@ -773,7 +774,7 @@ static int start_stream_fd(struct userdata *u) {
TRUE,
TRUE,
10,
pa_rtclock_usec(),
pa_rtclock_now(),
TRUE);
return 0;
@ -867,14 +868,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
if (u->read_smoother) {
pa_usec_t wi, ri;
ri = pa_smoother_get(u->read_smoother, pa_rtclock_usec());
ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
wi = pa_bytes_to_usec(u->write_index + u->block_size, &u->sample_spec);
*((pa_usec_t*) data) = wi > ri ? wi - ri : 0;
} else {
pa_usec_t ri, wi;
ri = pa_rtclock_usec() - u->started_at;
ri = pa_rtclock_now() - u->started_at;
wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
*((pa_usec_t*) data) = wi > ri ? wi - ri : 0;
@ -912,7 +913,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
stop_stream_fd(u);
if (u->read_smoother)
pa_smoother_pause(u->read_smoother, pa_rtclock_usec());
pa_smoother_pause(u->read_smoother, pa_rtclock_now());
break;
case PA_SOURCE_IDLE:
@ -939,7 +940,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
case PA_SOURCE_MESSAGE_GET_LATENCY: {
pa_usec_t wi, ri;
wi = pa_smoother_get(u->read_smoother, pa_rtclock_usec());
wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
*((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->fixed_latency;
@ -1086,7 +1087,7 @@ static int hsp_process_push(struct userdata *u) {
if (!found_tstamp) {
pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");
tstamp = pa_rtclock_usec();
tstamp = pa_rtclock_now();
}
pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
@ -1265,7 +1266,6 @@ static void thread_func(void *userdata) {
goto fail;
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
struct pollfd *pollfd;
@ -1309,7 +1309,7 @@ static void thread_func(void *userdata) {
/* Hmm, there is no input stream we could synchronize
* to. So let's do things by time */
time_passed = pa_rtclock_usec() - u->started_at;
time_passed = pa_rtclock_now() - u->started_at;
audio_sent = pa_bytes_to_usec(u->write_index, &u->sample_spec);
if (audio_sent <= time_passed) {
@ -1341,7 +1341,7 @@ static void thread_func(void *userdata) {
int n_written;
if (u->write_index <= 0)
u->started_at = pa_rtclock_usec();
u->started_at = pa_rtclock_now();
if (u->profile == PROFILE_A2DP) {
if ((n_written = a2dp_process_render(u)) < 0)
@ -1361,7 +1361,7 @@ static void thread_func(void *userdata) {
/* Hmm, there is no input stream we could synchronize
* to. So let's estimate when we need to wake up the latest */
time_passed = pa_rtclock_usec() - u->started_at;
time_passed = pa_rtclock_now() - u->started_at;
next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec);
sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
@ -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")) {
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")) {
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;
pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
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.name = get_name("sink", u->modargs, u->address, &b);
data.namereg_fail = b;
@ -1680,6 +1682,8 @@ static int add_source(struct userdata *u) {
data.module = u->module;
pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
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.name = get_name("source", u->modargs, u->address, &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))) {
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
@ -1926,15 +1930,15 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
module will be unloaded. */
if (device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) {
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) {
pa_log_warn("A2DP is not connected, refused to switch profile");
return -1;
return -PA_ERR_IO;
}
if (u->sink) {
inputs = pa_sink_move_all_start(u->sink);
inputs = pa_sink_move_all_start(u->sink, NULL);
#ifdef NOKIA
if (!USE_SCO_OVER_PCM(u))
#endif
@ -1942,7 +1946,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
}
if (u->source) {
outputs = pa_source_move_all_start(u->source);
outputs = pa_source_move_all_start(u->source, NULL);
#ifdef NOKIA
if (!USE_SCO_OVER_PCM(u))
#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_sink_set_mute(s, FALSE);
pa_sink_set_mute(s, FALSE, FALSE);
} else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
pa_sink *s;
@ -122,7 +122,7 @@ static void update_volume(struct userdata *u) {
}
pa_log_info("No BT devices found, muting.");
pa_sink_set_mute(s, TRUE);
pa_sink_set_mute(s, TRUE, FALSE);
} else
pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);

View file

@ -225,7 +225,6 @@ static void thread_func(void *userdata) {
pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;

View file

@ -196,7 +196,6 @@ static void thread_func(void *userdata) {
pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;

View file

@ -58,6 +58,7 @@ struct rule {
char *process_name;
char *application_name;
char *icon_name;
char *role;
pa_proplist *proplist;
};
@ -72,12 +73,21 @@ static void rule_free(struct rule *r) {
pa_xfree(r->process_name);
pa_xfree(r->application_name);
pa_xfree(r->icon_name);
pa_xfree(r->role);
if (r->proplist)
pa_proplist_free(r->proplist);
pa_xfree(r);
}
static int parse_properties(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
static int parse_properties(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
struct rule *r = userdata;
pa_proplist *n;
@ -93,11 +103,56 @@ static int parse_properties(const char *filename, unsigned line, const char *sec
return 0;
}
static int check_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
static int parse_categories(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
struct rule *r = userdata;
const char *state = NULL;
char *c;
while ((c = pa_split(rvalue, ";", &state))) {
if (pa_streq(c, "Game")) {
pa_xfree(r->role);
r->role = pa_xstrdup("game");
} else if (pa_streq(c, "Telephony")) {
pa_xfree(r->role);
r->role = pa_xstrdup("phone");
}
pa_xfree(c);
}
return 0;
}
static int check_type(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
return pa_streq(rvalue, "Application") ? 0 : -1;
}
static int catch_all(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
static int catch_all(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
return 0;
}
@ -109,6 +164,7 @@ static void update_rule(struct rule *r) {
{ "Icon", pa_config_parse_string, NULL, "Desktop Entry" },
{ "Type", check_type, NULL, "Desktop Entry" },
{ "X-PulseAudio-Properties", parse_properties, NULL, "Desktop Entry" },
{ "Categories", parse_categories, NULL, "Desktop Entry" },
{ NULL, catch_all, NULL, NULL },
{ NULL, NULL, NULL, NULL },
};
@ -131,7 +187,8 @@ static void update_rule(struct rule *r) {
r->mtime = st.st_mtime;
pa_xfree(r->application_name);
pa_xfree(r->icon_name);
r->application_name = r->icon_name = NULL;
pa_xfree(r->role);
r->application_name = r->icon_name = r->role = NULL;
if (r->proplist)
pa_proplist_clear(r->proplist);
@ -151,6 +208,9 @@ static void apply_rule(struct rule *r, pa_proplist *p) {
if (!r->good)
return;
if (r->proplist)
pa_proplist_update(p, PA_UPDATE_MERGE, r->proplist);
if (r->icon_name)
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_ICON_NAME))
pa_proplist_sets(p, PA_PROP_APPLICATION_ICON_NAME, r->icon_name);
@ -164,8 +224,9 @@ static void apply_rule(struct rule *r, pa_proplist *p) {
pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, r->application_name);
}
if (r->proplist)
pa_proplist_update(p, PA_UPDATE_MERGE, r->proplist);
if (r->role)
if (!pa_proplist_contains(p, PA_PROP_MEDIA_ROLE))
pa_proplist_sets(p, PA_PROP_MEDIA_ROLE, r->role);
}
static void make_room(pa_hashmap *cache) {

View file

@ -35,6 +35,7 @@
#include <pulse/volume.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/rtclock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
@ -53,7 +54,7 @@ PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
#define SAVE_INTERVAL 10
#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
static const char* const valid_modargs[] = {
NULL
@ -75,12 +76,11 @@ struct entry {
char profile[PA_NAME_MAX];
} PA_GCC_PACKED ;
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
pa_assert(a);
pa_assert(e);
pa_assert(tv);
pa_assert(u);
pa_assert(e == u->save_time_event);
@ -132,14 +132,10 @@ fail:
}
static void trigger_save(struct userdata *u) {
struct timeval tv;
if (u->save_time_event)
return;
pa_gettimeofday(&tv);
tv.tv_sec += SAVE_INTERVAL;
u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
}
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
@ -197,8 +193,9 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
if ((e = read_entry(u, new_data->name)) && e->profile[0]) {
if (!new_data->active_profile) {
pa_card_new_data_set_profile(new_data, e->profile);
pa_log_info("Restoring profile for card %s.", new_data->name);
pa_card_new_data_set_profile(new_data, e->profile);
new_data->save_profile = TRUE;
} else
pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
@ -222,11 +219,9 @@ int pa__init(pa_module*m) {
goto fail;
}
m->userdata = u = pa_xnew(struct userdata, 1);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
u->save_time_event = NULL;
u->database = NULL;
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CARD, subscribe_callback, u);

View file

@ -26,6 +26,7 @@
#include <stdio.h>
#include <errno.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@ -36,6 +37,7 @@
#include <pulsecore/sink-input.h>
#include <pulsecore/memblockq.h>
#include <pulsecore/log.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
@ -43,7 +45,6 @@
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/time-smoother.h>
@ -224,9 +225,8 @@ static void adjust_rates(struct userdata *u) {
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL);
}
static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
struct timeval n;
pa_assert(u);
pa_assert(a);
@ -234,9 +234,7 @@ static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct time
adjust_rates(u);
pa_gettimeofday(&n);
n.tv_sec += (time_t) u->adjust_time;
u->sink->core->mainloop->time_restart(e, &n);
pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time * PA_USEC_PER_SEC);
}
static void process_render_null(struct userdata *u, pa_usec_t now) {
@ -280,9 +278,8 @@ static void thread_func(void *userdata) {
pa_make_realtime(u->core->realtime_priority+1);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
u->thread_info.timestamp = pa_rtclock_usec();
u->thread_info.timestamp = pa_rtclock_now();
u->thread_info.in_null_mode = FALSE;
for (;;) {
@ -296,7 +293,7 @@ static void thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && !u->thread_info.active_outputs) {
pa_usec_t now;
now = pa_rtclock_usec();
now = pa_rtclock_now();
if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now)
process_render_null(u, now);
@ -593,7 +590,7 @@ static void unsuspend(struct userdata *u) {
/* Let's resume */
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
pa_sink_suspend(o->sink, FALSE);
pa_sink_suspend(o->sink, FALSE, PA_SUSPEND_IDLE);
if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
enable_output(o);
@ -664,16 +661,16 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
pa_atomic_store(&u->thread_info.running, PA_PTR_TO_UINT(data) == PA_SINK_RUNNING);
if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED)
pa_smoother_pause(u->thread_info.smoother, pa_rtclock_usec());
pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
else
pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec(), TRUE);
pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), TRUE);
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t x, y, c, *delay = data;
x = pa_rtclock_usec();
x = pa_rtclock_now();
y = pa_smoother_get(u->thread_info.smoother, x);
c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
@ -730,7 +727,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case SINK_MESSAGE_UPDATE_LATENCY: {
pa_usec_t x, y, latency = (pa_usec_t) offset;
x = pa_rtclock_usec();
x = pa_rtclock_now();
y = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
if (y > latency)
@ -873,7 +870,7 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
}
if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) {
pa_sink_suspend(sink, FALSE);
pa_sink_suspend(sink, FALSE, PA_SUSPEND_IDLE);
if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
if (output_create_sink_input(o) < 0)
@ -1170,12 +1167,8 @@ int pa__init(pa_module*m) {
if (o->sink_input)
pa_sink_input_put(o->sink_input);
if (u->adjust_time > 0) {
struct timeval tv;
pa_gettimeofday(&tv);
tv.tv_sec += (time_t) u->adjust_time;
u->time_event = m->core->mainloop->time_new(m->core->mainloop, &tv, time_callback, u);
}
if (u->adjust_time > 0)
u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time * PA_USEC_PER_SEC, time_callback, u);
pa_modargs_free(ma);

View file

@ -157,7 +157,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne
c = pa_xnew(struct connection, 1);
c->server = s;
c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, new_connection);
c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection);
c->client = client;
c->client->kill = client_kill_cb;

View file

@ -26,6 +26,7 @@
#include <errno.h>
#include <stdio.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
@ -42,7 +43,7 @@ PA_MODULE_DESCRIPTION("Automatically restore the default sink and source");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
#define DEFAULT_SAVE_INTERVAL 5
#define SAVE_INTERVAL (5 * PA_USEC_PER_SEC)
struct userdata {
pa_core *core;
@ -127,7 +128,7 @@ static void save(struct userdata *u) {
u->modified = FALSE;
}
static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
pa_assert(u);
@ -146,12 +147,8 @@ static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t id
u->modified = TRUE;
if (!u->time_event) {
struct timeval tv;
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
}
if (!u->time_event)
u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, time_cb, u);
}
int pa__init(pa_module *m) {

View file

@ -35,6 +35,7 @@
#include <pulse/volume.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/rtclock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
@ -54,14 +55,16 @@ PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_USAGE(
"restore_port=<Save/restore port?> "
"restore_volume=<Save/restore volumes?> "
"restore_muted=<Save/restore muted states?>");
#define SAVE_INTERVAL 10
#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
static const char* const valid_modargs[] = {
"restore_volume",
"restore_muted",
"restore_port",
NULL
};
@ -70,30 +73,34 @@ struct userdata {
pa_module *module;
pa_subscription *subscription;
pa_hook_slot
*sink_new_hook_slot,
*sink_fixate_hook_slot,
*source_new_hook_slot,
*source_fixate_hook_slot;
pa_time_event *save_time_event;
pa_database *database;
pa_bool_t restore_volume:1;
pa_bool_t restore_muted:1;
pa_bool_t restore_port:1;
};
#define ENTRY_VERSION 1
#define ENTRY_VERSION 2
struct entry {
uint8_t version;
pa_bool_t muted_valid:1, volume_valid:1, port_valid:1;
pa_bool_t muted:1;
pa_channel_map channel_map;
pa_cvolume volume;
char port[PA_NAME_MAX];
} PA_GCC_PACKED;
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
pa_assert(a);
pa_assert(e);
pa_assert(tv);
pa_assert(u);
pa_assert(e == u->save_time_event);
@ -131,17 +138,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
}
if (!(pa_cvolume_valid(&e->volume))) {
pa_log_warn("Invalid volume stored in database for device %s", name);
if (!memchr(e->port, 0, sizeof(e->port))) {
pa_log_warn("Database contains entry for device %s with missing NUL byte in port name", name);
goto fail;
}
if (!(pa_channel_map_valid(&e->channel_map))) {
if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
pa_log_warn("Invalid channel map stored in database for device %s", name);
goto fail;
}
if (e->volume.channels != e->channel_map.channels) {
if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
pa_log_warn("Volume and channel map don't match in database entry for device %s", name);
goto fail;
}
@ -155,14 +162,29 @@ fail:
}
static void trigger_save(struct userdata *u) {
struct timeval tv;
if (u->save_time_event)
return;
pa_gettimeofday(&tv);
tv.tv_sec += SAVE_INTERVAL;
u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
}
static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
pa_cvolume t;
if (a->port_valid != b->port_valid ||
(a->port_valid && strncmp(a->port, b->port, sizeof(a->port))))
return FALSE;
if (a->muted_valid != b->muted_valid ||
(a->muted_valid && (a->muted != b->muted)))
return FALSE;
t = b->volume;
if (a->volume_valid != b->volume_valid ||
(a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
return FALSE;
return TRUE;
}
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
@ -190,9 +212,25 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
name = pa_sprintf_malloc("sink:%s", sink->name);
entry.channel_map = sink->channel_map;
entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE);
entry.muted = pa_sink_get_mute(sink, FALSE);
if ((old = read_entry(u, name)))
entry = *old;
if (sink->save_volume) {
entry.channel_map = sink->channel_map;
entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE);
entry.volume_valid = TRUE;
}
if (sink->save_muted) {
entry.muted = pa_sink_get_mute(sink, FALSE);
entry.muted_valid = TRUE;
}
if (sink->save_port) {
pa_strlcpy(entry.port, sink->active_port ? sink->active_port->name : "", sizeof(entry.port));
entry.port_valid = TRUE;
}
} else {
pa_source *source;
@ -203,16 +241,30 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
name = pa_sprintf_malloc("source:%s", source->name);
entry.channel_map = source->channel_map;
entry.volume = *pa_source_get_volume(source, FALSE);
entry.muted = pa_source_get_mute(source, FALSE);
if ((old = read_entry(u, name)))
entry = *old;
if (source->save_volume) {
entry.channel_map = source->channel_map;
entry.volume = *pa_source_get_volume(source, FALSE);
entry.volume_valid = TRUE;
}
if (source->save_muted) {
entry.muted = pa_source_get_mute(source, FALSE);
entry.muted_valid = TRUE;
}
if (source->save_port) {
pa_strlcpy(entry.port, source->active_port ? source->active_port->name : "", sizeof(entry.port));
entry.port_valid = TRUE;
}
}
if ((old = read_entry(u, name))) {
if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) &&
!old->muted == !entry.muted) {
if (old) {
if (entries_equal(old, &entry)) {
pa_xfree(old);
pa_xfree(name);
return;
@ -227,7 +279,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
data.data = &entry;
data.size = sizeof(entry);
pa_log_info("Storing volume/mute for device %s.", name);
pa_log_info("Storing volume/mute/port for device %s.", name);
pa_database_set(u->database, &key, &data, TRUE);
@ -236,31 +288,71 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
trigger_save(u);
}
static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
char *name;
struct entry *e;
pa_assert(c);
pa_assert(new_data);
pa_assert(u);
pa_assert(u->restore_port);
name = pa_sprintf_malloc("sink:%s", new_data->name);
if ((e = read_entry(u, name))) {
if (u->restore_volume) {
if (!new_data->volume_is_set) {
pa_log_info("Restoring volume for sink %s.", new_data->name);
pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
if (e->port_valid) {
if (!new_data->active_port) {
pa_log_info("Restoring port for sink %s.", name);
pa_sink_new_data_set_port(new_data, e->port);
new_data->save_port = TRUE;
} else
pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
pa_log_debug("Not restoring port for sink %s, because already set.", name);
}
if (u->restore_muted) {
pa_xfree(e);
}
pa_xfree(name);
return PA_HOOK_OK;
}
static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
char *name;
struct entry *e;
pa_assert(c);
pa_assert(new_data);
pa_assert(u);
pa_assert(u->restore_volume || u->restore_muted);
name = pa_sprintf_malloc("sink:%s", new_data->name);
if ((e = read_entry(u, name))) {
if (u->restore_volume && e->volume_valid) {
if (!new_data->volume_is_set) {
pa_cvolume v;
pa_log_info("Restoring volume for sink %s.", new_data->name);
v = e->volume;
pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
pa_sink_new_data_set_volume(new_data, &v);
new_data->save_volume = TRUE;
} else
pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
}
if (u->restore_muted && e->muted_valid) {
if (!new_data->muted_is_set) {
pa_log_info("Restoring mute state for sink %s.", new_data->name);
pa_sink_new_data_set_muted(new_data, e->muted);
new_data->save_muted = TRUE;
} else
pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
}
@ -273,30 +365,71 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
return PA_HOOK_OK;
}
static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
char *name;
struct entry *e;
pa_assert(c);
pa_assert(new_data);
pa_assert(u);
pa_assert(u->restore_port);
name = pa_sprintf_malloc("source:%s", new_data->name);
if ((e = read_entry(u, name))) {
if (e->port_valid) {
if (!new_data->active_port) {
pa_log_info("Restoring port for source %s.", name);
pa_source_new_data_set_port(new_data, e->port);
new_data->save_port = TRUE;
} else
pa_log_debug("Not restoring port for source %s, because already set.", name);
}
pa_xfree(e);
}
pa_xfree(name);
return PA_HOOK_OK;
}
static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
char *name;
struct entry *e;
pa_assert(c);
pa_assert(new_data);
pa_assert(u);
pa_assert(u->restore_volume || u->restore_muted);
name = pa_sprintf_malloc("source:%s", new_data->name);
if ((e = read_entry(u, name))) {
if (u->restore_volume) {
if (u->restore_volume && e->volume_valid) {
if (!new_data->volume_is_set) {
pa_cvolume v;
pa_log_info("Restoring volume for source %s.", new_data->name);
pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
v = e->volume;
pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
pa_source_new_data_set_volume(new_data, &v);
new_data->save_volume = TRUE;
} else
pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
}
if (u->restore_muted) {
if (u->restore_muted && e->muted_valid) {
if (!new_data->muted_is_set) {
pa_log_info("Restoring mute state for source %s.", new_data->name);
pa_source_new_data_set_muted(new_data, e->muted);
new_data->save_muted = TRUE;
} else
pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
}
@ -316,7 +449,7 @@ int pa__init(pa_module*m) {
pa_sink *sink;
pa_source *source;
uint32_t idx;
pa_bool_t restore_volume = TRUE, restore_muted = TRUE;
pa_bool_t restore_volume = TRUE, restore_muted = TRUE, restore_port = TRUE;
pa_assert(m);
@ -326,24 +459,29 @@ int pa__init(pa_module*m) {
}
if (pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0) {
pa_log("restore_volume= and restore_muted= expect boolean arguments");
pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
pa_modargs_get_value_boolean(ma, "restore_port", &restore_port) < 0) {
pa_log("restore_port=, restore_volume= and restore_muted= expect boolean arguments");
goto fail;
}
if (!restore_muted && !restore_volume)
pa_log_warn("Neither restoring volume nor restoring muted enabled!");
if (!restore_muted && !restore_volume && !restore_port)
pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
m->userdata = u = pa_xnew(struct userdata, 1);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
u->save_time_event = NULL;
u->restore_volume = restore_volume;
u->restore_muted = restore_muted;
u->database = NULL;
u->restore_port = restore_port;
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
if (restore_port) {
u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
}
if (restore_muted || restore_volume) {
u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u);
u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
@ -394,6 +532,10 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->sink_fixate_hook_slot);
if (u->source_fixate_hook_slot)
pa_hook_slot_free(u->source_fixate_hook_slot);
if (u->sink_new_hook_slot)
pa_hook_slot_free(u->sink_new_hook_slot);
if (u->source_new_hook_slot)
pa_hook_slot_free(u->source_new_hook_slot);
if (u->save_time_event)
u->core->mainloop->time_free(u->save_time_event);

View file

@ -41,13 +41,15 @@
#include <linux/sockios.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
@ -57,7 +59,6 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/thread.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/socket-util.h>
#include "module-esound-sink-symdef.h"
@ -145,14 +146,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
pa_smoother_pause(u->smoother, pa_rtclock_usec());
pa_smoother_pause(u->smoother, pa_rtclock_now());
break;
case PA_SINK_IDLE:
case PA_SINK_RUNNING:
if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
break;
@ -167,7 +168,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t w, r;
r = pa_smoother_get(u->smoother, pa_rtclock_usec());
r = pa_smoother_get(u->smoother, pa_rtclock_now());
w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
@ -200,9 +201,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
for (;;) {
int ret;
@ -295,7 +295,7 @@ static void thread_func(void *userdata) {
else
usec = 0;
pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
}
/* Hmm, nothing to do. Let's sleep */
@ -608,7 +608,7 @@ int pa__init(pa_module*m) {
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
if (!(u->client = pa_socket_client_new_string(u->core->mainloop, espeaker, ESD_DEFAULT_PORT))) {
if (!(u->client = pa_socket_client_new_string(u->core->mainloop, TRUE, espeaker, ESD_DEFAULT_PORT))) {
pa_log("Failed to connect to server.");
goto fail;
}

View file

@ -64,6 +64,7 @@ PA_MODULE_USAGE("api=<alsa> "
#elif defined(HAVE_OSS)
PA_MODULE_USAGE("api=<oss>");
#endif
PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
struct device {
char *udi, *originating_udi;
@ -232,7 +233,7 @@ static int hal_device_load_alsa(struct userdata *u, const char *udi, struct devi
goto fail;
card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi));
args = pa_sprintf_malloc("device_id=%u name=%s card_name=%s tsched=%i", card, strip_udi(originating_udi), card_name, (int) u->use_tsched);
args = pa_sprintf_malloc("device_id=%u name=\"%s\" card_name=\"%s\" tsched=%i card_properties=\"module-hal-detect.discovered=1\"", card, strip_udi(originating_udi), card_name, (int) u->use_tsched);
pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
m = pa_module_load(u->core, "module-alsa-card", args);
@ -567,7 +568,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
pa_sink *sink;
if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0;
pa_bool_t success = pa_sink_suspend(sink, suspend, PA_SUSPEND_SESSION) >= 0;
if (!success && !suspend)
d->acl_race_fix = TRUE; /* resume failed, let's try again */
@ -580,7 +581,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
pa_source *source;
if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
pa_bool_t success = pa_source_suspend(source, suspend) >= 0;
pa_bool_t success = pa_source_suspend(source, suspend, PA_SUSPEND_SESSION) >= 0;
if (!success && !suspend)
d->acl_race_fix = TRUE; /* resume failed, let's try again */
@ -593,7 +594,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
pa_card *card;
if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
pa_bool_t success = pa_card_suspend(card, suspend) >= 0;
pa_bool_t success = pa_card_suspend(card, suspend, PA_SUSPEND_SESSION) >= 0;
if (!success && !suspend)
d->acl_race_fix = TRUE; /* resume failed, let's try again */
@ -637,21 +638,21 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
pa_sink *sink;
if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
pa_sink_suspend(sink, FALSE);
pa_sink_suspend(sink, FALSE, PA_SUSPEND_SESSION);
}
if (d->source_name) {
pa_source *source;
if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
pa_source_suspend(source, FALSE);
pa_source_suspend(source, FALSE, PA_SUSPEND_SESSION);
}
if (d->card_name) {
pa_card *card;
if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
pa_card_suspend(card, FALSE);
pa_card_suspend(card, FALSE, PA_SUSPEND_SESSION);
}
}

View file

@ -0,0 +1,428 @@
/***
This file is part of PulseAudio.
Copyright 2009 Lennart Poettering
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.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/volume.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
#include "module-intended-roles-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Automatically set device of streams based of intended roles of devices");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_USAGE(
"on_hotplug=<When new device becomes available, recheck streams?> "
"on_rescue=<When device becomes unavailable, recheck streams?>");
static const char* const valid_modargs[] = {
"on_hotplug",
"on_rescue",
NULL
};
struct userdata {
pa_core *core;
pa_module *module;
pa_hook_slot
*sink_input_new_hook_slot,
*source_output_new_hook_slot,
*sink_put_hook_slot,
*source_put_hook_slot,
*sink_unlink_hook_slot,
*source_unlink_hook_slot;
pa_bool_t on_hotplug:1;
pa_bool_t on_rescue:1;
};
static pa_bool_t role_match(pa_proplist *proplist, const char *role) {
const char *ir;
char *r;
const char *state = NULL;
if (!(ir = pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES)))
return FALSE;
while ((r = pa_split_spaces(ir, &state))) {
if (pa_streq(role, r)) {
pa_xfree(r);
return TRUE;
}
pa_xfree(r);
}
return FALSE;
}
static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
const char *role;
pa_sink *s, *def;
uint32_t idx;
pa_assert(c);
pa_assert(new_data);
pa_assert(u);
if (!new_data->proplist) {
pa_log_debug("New stream lacks property data.");
return PA_HOOK_OK;
}
if (new_data->sink) {
pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
return PA_HOOK_OK;
}
if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
return PA_HOOK_OK;
}
/* Prefer the default sink over any other sink, just in case... */
if ((def = pa_namereg_get_default_sink(c)))
if (role_match(def->proplist, role)) {
new_data->sink = def;
new_data->save_sink = FALSE;
return PA_HOOK_OK;
}
PA_IDXSET_FOREACH(s, c->sinks, idx) {
if (s == def)
continue;
if (role_match(s->proplist, role)) {
new_data->sink = s;
new_data->save_sink = FALSE;
return PA_HOOK_OK;
}
}
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
const char *role;
pa_source *s, *def;
uint32_t idx;
pa_assert(c);
pa_assert(new_data);
pa_assert(u);
if (!new_data->proplist) {
pa_log_debug("New stream lacks property data.");
return PA_HOOK_OK;
}
if (new_data->source) {
pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
return PA_HOOK_OK;
}
if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
return PA_HOOK_OK;
}
/* Prefer the default source over any other source, just in case... */
if ((def = pa_namereg_get_default_source(c)))
if (role_match(def->proplist, role)) {
new_data->source = def;
new_data->save_source = FALSE;
return PA_HOOK_OK;
}
PA_IDXSET_FOREACH(s, c->sources, idx) {
if (s == def)
continue;
if (role_match(s->proplist, role)) {
new_data->source = s;
new_data->save_source = FALSE;
return PA_HOOK_OK;
}
}
return PA_HOOK_OK;
}
static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
pa_sink_input *si;
uint32_t idx;
pa_assert(c);
pa_assert(sink);
pa_assert(u);
pa_assert(u->on_hotplug);
PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
const char *role;
if (si->sink == sink)
continue;
if (si->save_sink)
continue;
if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
continue;
if (role_match(si->sink->proplist, role))
continue;
if (!role_match(sink->proplist, role))
continue;
pa_sink_input_move_to(si, sink, FALSE);
}
return PA_HOOK_OK;
}
static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
pa_source_output *so;
uint32_t idx;
pa_assert(c);
pa_assert(source);
pa_assert(u);
pa_assert(u->on_hotplug);
PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
const char *role;
if (so->source == source)
continue;
if (so->save_source)
continue;
if (so->direct_on_input)
continue;
if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
continue;
if (role_match(so->source->proplist, role))
continue;
if (!role_match(source->proplist, role))
continue;
pa_source_output_move_to(so, source, FALSE);
}
return PA_HOOK_OK;
}
static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
pa_sink_input *si;
uint32_t idx;
pa_sink *def;
pa_assert(c);
pa_assert(sink);
pa_assert(u);
pa_assert(u->on_rescue);
/* There's no point in doing anything if the core is shut down anyway */
if (c->state == PA_CORE_SHUTDOWN)
return PA_HOOK_OK;
/* If there not default sink, then there is no sink at all */
if (!(def = pa_namereg_get_default_sink(c)))
return PA_HOOK_OK;
PA_IDXSET_FOREACH(si, sink->inputs, idx) {
const char *role;
uint32_t jdx;
pa_sink *d;
if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
continue;
/* Would the default sink fit? If so, let's use it */
if (def != sink && role_match(def->proplist, role)) {
pa_sink_input_move_to(si, def, FALSE);
continue;
}
/* Try to find some other fitting sink */
PA_IDXSET_FOREACH(d, c->sinks, jdx) {
if (d == def || d == sink)
continue;
if (role_match(d->proplist, role)) {
pa_sink_input_move_to(si, d, FALSE);
break;
}
}
}
return PA_HOOK_OK;
}
static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
pa_source_output *so;
uint32_t idx;
pa_source *def;
pa_assert(c);
pa_assert(source);
pa_assert(u);
pa_assert(u->on_rescue);
/* There's no point in doing anything if the core is shut down anyway */
if (c->state == PA_CORE_SHUTDOWN)
return PA_HOOK_OK;
/* If there not default source, then there is no source at all */
if (!(def = pa_namereg_get_default_source(c)))
return PA_HOOK_OK;
PA_IDXSET_FOREACH(so, source->outputs, idx) {
const char *role;
uint32_t jdx;
pa_source *d;
if (so->direct_on_input)
continue;
if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
continue;
/* Would the default source fit? If so, let's use it */
if (def != source && role_match(def->proplist, role) && !source->monitor_of == !def->monitor_of) {
pa_source_output_move_to(so, def, FALSE);
continue;
}
/* Try to find some other fitting source */
PA_IDXSET_FOREACH(d, c->sources, jdx) {
if (d == def || d == source)
continue;
if (role_match(d->proplist, role) && !source->monitor_of == !d->monitor_of) {
pa_source_output_move_to(so, d, FALSE);
break;
}
}
}
return PA_HOOK_OK;
}
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
pa_log("on_hotplug= and on_rescue= expect boolean arguments");
goto fail;
}
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
u->on_hotplug = on_hotplug;
u->on_rescue = on_rescue;
/* A little bit later than module-stream-restore */
u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) sink_input_new_hook_callback, u);
u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) source_output_new_hook_callback, u);
if (on_hotplug) {
/* A little bit later than module-stream-restore */
u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u);
u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) source_put_hook_callback, u);
}
if (on_rescue) {
/* A little bit later than module-stream-restore, a little bit earlier than module-rescue-streams, ... */
u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_unlink_hook_callback, u);
u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) source_unlink_hook_callback, u);
}
pa_modargs_free(ma);
return 0;
fail:
pa__done(m);
if (ma)
pa_modargs_free(ma);
return -1;
}
void pa__done(pa_module*m) {
struct userdata* u;
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->sink_input_new_hook_slot)
pa_hook_slot_free(u->sink_input_new_hook_slot);
if (u->source_output_new_hook_slot)
pa_hook_slot_free(u->source_output_new_hook_slot);
if (u->sink_put_hook_slot)
pa_hook_slot_free(u->sink_put_hook_slot);
if (u->source_put_hook_slot)
pa_hook_slot_free(u->source_put_hook_slot);
if (u->sink_unlink_hook_slot)
pa_hook_slot_free(u->sink_unlink_hook_slot);
if (u->source_unlink_hook_slot)
pa_hook_slot_free(u->source_unlink_hook_slot);
pa_xfree(u);
}

View file

@ -27,6 +27,7 @@
#endif
#include <pulse/xmalloc.h>
#include <pulse/i18n.h>
#include <pulsecore/core-error.h>
#include <pulsecore/namereg.h>
@ -45,20 +46,20 @@
#include "ladspa.h"
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Virtual LADSPA sink");
PA_MODULE_DESCRIPTION(_("Virtual LADSPA sink"));
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"sink_properties=<properties for the sink> "
"master=<name of sink to remap> "
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
"channel_map=<channel map> "
"plugin=<ladspa plugin name> "
"label=<ladspa plugin label> "
"control=<comma seperated list of input control values>");
_("sink_name=<name for the sink> "
"sink_properties=<properties for the sink> "
"master=<name of sink to filter> "
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
"channel_map=<channel map> "
"plugin=<ladspa plugin name> "
"label=<ladspa plugin label> "
"control=<comma seperated list of input control values>"));
#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;
if (volchange == INVALID)
pa_log_warn("Recieved unknown IR code '%s'", name);
pa_log_warn("Received unknown IR code '%s'", name);
else {
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;
}
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE);
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break;
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;
}
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE);
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break;
case MUTE:
pa_sink_set_mute(s, TRUE);
pa_sink_set_mute(s, TRUE, TRUE);
break;
case RESET:
pa_sink_set_mute(s, FALSE);
pa_sink_set_mute(s, FALSE, TRUE);
break;
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;
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;
}
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE);
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break;
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;
}
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE);
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
break;
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;
case INVALID:

View file

@ -32,12 +32,14 @@
#include <unistd.h>
#include <limits.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/macro.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/modargs.h>
@ -45,7 +47,6 @@
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/rtclock.h>
#include "module-null-sink-symdef.h"
@ -101,14 +102,14 @@ static int sink_process_msg(
case PA_SINK_MESSAGE_SET_STATE:
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
u->timestamp = pa_rtclock_usec();
u->timestamp = pa_rtclock_now();
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t now;
now = pa_rtclock_usec();
now = pa_rtclock_now();
*((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0ULL;
return 0;
@ -208,9 +209,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
u->timestamp = pa_rtclock_usec();
u->timestamp = pa_rtclock_now();
for (;;) {
int ret;
@ -219,7 +219,7 @@ static void thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
pa_usec_t now;
now = pa_rtclock_usec();
now = pa_rtclock_now();
if (u->sink->thread_info.rewind_requested) {
if (u->sink->thread_info.rewind_nbytes > 0)

View file

@ -170,7 +170,6 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
struct pollfd *pollfd;

View file

@ -129,7 +129,6 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;

View file

@ -65,14 +65,14 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
return PA_HOOK_OK;
}
if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)) || target == sink) {
if (!(target = pa_namereg_get_default_sink(c)) || target == sink) {
PA_IDXSET_FOREACH(target, c->sinks, idx)
if (target != sink)
break;
if (!target) {
pa_log_info("No evacuation sink found.");
pa_log_debug("No evacuation sink found.");
return PA_HOOK_OK;
}
}
@ -108,7 +108,7 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
return PA_HOOK_OK;
}
if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE)) || target == source) {
if (!(target = pa_namereg_get_default_source(c)) || target == source) {
PA_IDXSET_FOREACH(target, c->sources, idx)
if (target != source && !target->monitor_of == !source->monitor_of)
@ -146,8 +146,10 @@ int pa__init(pa_module*m) {
}
m->userdata = u = pa_xnew(struct userdata, 1);
u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_hook_callback, NULL);
u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_hook_callback, NULL);
/* A little bit later than module-stream-restore, module-intended-roles... */
u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_hook_callback, u);
u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_hook_callback, u);
pa_modargs_free(ma);
return 0;

View file

@ -34,19 +34,20 @@
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <pulse/xmalloc.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/rtclock.h>
#include "module-sine-source-symdef.h"
@ -101,14 +102,14 @@ static int source_process_msg(
case PA_SOURCE_MESSAGE_SET_STATE:
if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
u->timestamp = pa_rtclock_usec();
u->timestamp = pa_rtclock_now();
break;
case PA_SOURCE_MESSAGE_GET_LATENCY: {
pa_usec_t now, left_to_fill;
now = pa_rtclock_usec();
now = pa_rtclock_now();
left_to_fill = u->timestamp > now ? u->timestamp - now : 0ULL;
*((pa_usec_t*) data) = u->block_usec > left_to_fill ? u->block_usec - left_to_fill : 0ULL;
@ -166,9 +167,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
u->timestamp = pa_rtclock_usec();
u->timestamp = pa_rtclock_now();
for (;;) {
int ret;
@ -176,7 +176,7 @@ static void thread_func(void *userdata) {
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
pa_usec_t now;
now = pa_rtclock_usec();
now = pa_rtclock_now();
if (u->timestamp <= now)
process_render(u, now);

View file

@ -641,7 +641,7 @@ static void thread_func(void *userdata) {
* Since we cannot modify the size of the output buffer we fake it
* by not filling it more than u->buffer_size.
*/
xtime0 = pa_rtclock_usec();
xtime0 = pa_rtclock_now();
buffered_bytes = get_playback_buffered_bytes(u);
if (buffered_bytes >= (uint64_t)u->buffer_size)
break;

View file

@ -35,6 +35,7 @@
#include <pulse/volume.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/rtclock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
@ -59,15 +60,19 @@ PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_USAGE(
"restore_device=<Save/restore sinks/sources?> "
"restore_volume=<Save/restore volumes?> "
"restore_muted=<Save/restore muted states?>");
"restore_muted=<Save/restore muted states?> "
"on_hotplug=<When new device becomes available, recheck streams?> "
"on_rescue=<When device becomes unavailable, recheck streams?>");
#define SAVE_INTERVAL 10
#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
#define IDENTIFICATION_PROPERTY "module-stream-restore.id"
static const char* const valid_modargs[] = {
"restore_device",
"restore_volume",
"restore_muted",
"on_hotplug",
"on_rescue",
NULL
};
@ -79,6 +84,10 @@ struct userdata {
*sink_input_new_hook_slot,
*sink_input_fixate_hook_slot,
*source_output_new_hook_slot,
*sink_put_hook_slot,
*source_put_hook_slot,
*sink_unlink_hook_slot,
*source_unlink_hook_slot,
*connection_unlink_hook_slot;
pa_time_event *save_time_event;
pa_database* database;
@ -86,6 +95,8 @@ struct userdata {
pa_bool_t restore_device:1;
pa_bool_t restore_volume:1;
pa_bool_t restore_muted:1;
pa_bool_t on_hotplug:1;
pa_bool_t on_rescue:1;
pa_native_protocol *protocol;
pa_idxset *subscribed;
@ -111,12 +122,11 @@ enum {
SUBCOMMAND_EVENT
};
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
pa_assert(a);
pa_assert(e);
pa_assert(tv);
pa_assert(u);
pa_assert(e == u->save_time_event);
@ -210,7 +220,6 @@ fail:
}
static void trigger_save(struct userdata *u) {
struct timeval tv;
pa_native_connection *c;
uint32_t idx;
@ -230,9 +239,7 @@ static void trigger_save(struct userdata *u) {
if (u->save_time_event)
return;
pa_gettimeofday(&tv);
tv.tv_sec += SAVE_INTERVAL;
u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
}
static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
@ -353,18 +360,18 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
char *name;
struct entry *e;
pa_assert(c);
pa_assert(new_data);
if (!u->restore_device)
return PA_HOOK_OK;
pa_assert(u);
pa_assert(u->restore_device);
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
if ((e = read_entry(u, name))) {
pa_sink *s;
if (e->device_valid) {
pa_sink *s;
if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
if (!new_data->sink) {
@ -372,7 +379,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
new_data->sink = s;
new_data->save_sink = TRUE;
} else
pa_log_info("Not restore device for stream %s, because already set.", name);
pa_log_debug("Not restoring device for stream %s, because already set.", name);
}
}
@ -388,10 +395,10 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
char *name;
struct entry *e;
pa_assert(c);
pa_assert(new_data);
if (!u->restore_volume && !u->restore_muted)
return PA_HOOK_OK;
pa_assert(u);
pa_assert(u->restore_volume || u->restore_muted);
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
@ -404,12 +411,13 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
pa_cvolume v;
pa_log_info("Restoring volume for sink input %s.", name);
v = e->volume;
pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
pa_sink_input_new_data_set_volume(new_data, &v);
new_data->volume_is_absolute = FALSE;
new_data->save_volume = FALSE;
new_data->save_volume = TRUE;
} else
pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
}
@ -436,10 +444,10 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
char *name;
struct entry *e;
pa_assert(c);
pa_assert(new_data);
if (!u->restore_device)
return PA_HOOK_OK;
pa_assert(u);
pa_assert(u->restore_device);
if (new_data->direct_on_input)
return PA_HOOK_OK;
@ -457,7 +465,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
new_data->source = s;
new_data->save_source = TRUE;
} else
pa_log_info("Not restoring device for stream %s, because already set", name);
pa_log_debug("Not restoring device for stream %s, because already set", name);
}
}
@ -469,6 +477,155 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
return PA_HOOK_OK;
}
static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
pa_sink_input *si;
uint32_t idx;
pa_assert(c);
pa_assert(sink);
pa_assert(u);
pa_assert(u->on_hotplug && u->restore_device);
PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
char *name;
struct entry *e;
if (si->sink == sink)
continue;
if (si->save_sink)
continue;
if (!(name = get_name(si->proplist, "sink-input")))
continue;
if ((e = read_entry(u, name))) {
if (e->device_valid && pa_streq(e->device, sink->name))
pa_sink_input_move_to(si, sink, TRUE);
pa_xfree(e);
}
pa_xfree(name);
}
return PA_HOOK_OK;
}
static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
pa_source_output *so;
uint32_t idx;
pa_assert(c);
pa_assert(source);
pa_assert(u);
pa_assert(u->on_hotplug && u->restore_device);
PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
char *name;
struct entry *e;
if (so->source == source)
continue;
if (so->save_source)
continue;
if (so->direct_on_input)
continue;
if (!(name = get_name(so->proplist, "source-input")))
continue;
if ((e = read_entry(u, name))) {
if (e->device_valid && pa_streq(e->device, source->name))
pa_source_output_move_to(so, source, TRUE);
pa_xfree(e);
}
pa_xfree(name);
}
return PA_HOOK_OK;
}
static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
pa_sink_input *si;
uint32_t idx;
pa_assert(c);
pa_assert(sink);
pa_assert(u);
pa_assert(u->on_rescue && u->restore_device);
/* There's no point in doing anything if the core is shut down anyway */
if (c->state == PA_CORE_SHUTDOWN)
return PA_HOOK_OK;
PA_IDXSET_FOREACH(si, sink->inputs, idx) {
char *name;
struct entry *e;
if (!(name = get_name(si->proplist, "sink-input")))
continue;
if ((e = read_entry(u, name))) {
if (e->device_valid) {
pa_sink *d;
if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && d != sink)
pa_sink_input_move_to(si, d, TRUE);
}
pa_xfree(e);
}
pa_xfree(name);
}
return PA_HOOK_OK;
}
static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
pa_source_output *so;
uint32_t idx;
pa_assert(c);
pa_assert(source);
pa_assert(u);
pa_assert(u->on_rescue && u->restore_device);
/* There's no point in doing anything if the core is shut down anyway */
if (c->state == PA_CORE_SHUTDOWN)
return PA_HOOK_OK;
PA_IDXSET_FOREACH(so, source->outputs, idx) {
char *name;
struct entry *e;
if (!(name = get_name(so->proplist, "source-output")))
continue;
if ((e = read_entry(u, name))) {
if (e->device_valid) {
pa_source *d;
if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && d != source)
pa_source_output_move_to(so, d, TRUE);
}
pa_xfree(e);
}
pa_xfree(name);
}
return PA_HOOK_OK;
}
#define EXT_VERSION 1
static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
@ -503,7 +660,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
if (u->restore_muted && e->muted_valid) {
pa_log_info("Restoring mute state for sink input %s.", name);
pa_sink_input_set_mute(si, e->muted, TRUE);
pa_sink_input_set_mute(si, e->muted, FALSE);
}
if (u->restore_device &&
@ -511,7 +668,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
(s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
pa_log_info("Restoring device for stream %s.", name);
pa_sink_input_move_to(si, s, TRUE);
pa_sink_input_move_to(si, s, FALSE);
}
}
@ -533,7 +690,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
(s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
pa_log_info("Restoring device for stream %s.", name);
pa_source_output_move_to(so, s, TRUE);
pa_source_output_move_to(so, s, FALSE);
}
}
}
@ -774,7 +931,7 @@ int pa__init(pa_module*m) {
pa_sink_input *si;
pa_source_output *so;
uint32_t idx;
pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE;
pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE;
pa_assert(m);
@ -785,22 +942,24 @@ int pa__init(pa_module*m) {
if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0) {
pa_log("restore_device=, restore_volume= and restore_muted= expect boolean arguments");
pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
pa_log("restore_device=, restore_volume=, restore_muted=, on_hotplug= and on_rescue= expect boolean arguments");
goto fail;
}
if (!restore_muted && !restore_volume && !restore_device)
pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring device enabled!");
m->userdata = u = pa_xnew(struct userdata, 1);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
u->save_time_event = NULL;
u->restore_device = restore_device;
u->restore_volume = restore_volume;
u->restore_muted = restore_muted;
u->database = NULL;
u->on_hotplug = on_hotplug;
u->on_rescue = on_rescue;
u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
u->protocol = pa_native_protocol_get(m->core);
@ -811,17 +970,27 @@ int pa__init(pa_module*m) {
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
if (restore_device) {
/* A little bit earlier than module-intended-roles ... */
u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
}
if (restore_device && on_hotplug) {
/* A little bit earlier than module-intended-roles ... */
u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_callback, u);
u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_put_hook_callback, u);
}
if (restore_device && on_rescue) {
/* A little bit earlier than module-intended-roles, module-rescue-streams, ... */
u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_hook_callback, u);
u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_hook_callback, u);
}
if (restore_volume || restore_muted)
u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
fname = pa_state_path("stream-volumes", TRUE);
if (!fname)
if (!(fname = pa_state_path("stream-volumes", TRUE)))
goto fail;
if (!(u->database = pa_database_open(fname, TRUE))) {
@ -833,10 +1002,10 @@ int pa__init(pa_module*m) {
pa_log_info("Sucessfully opened database file '%s'.", fname);
pa_xfree(fname);
for (si = pa_idxset_first(m->core->sink_inputs, &idx); si; si = pa_idxset_next(m->core->sink_inputs, &idx))
PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
for (so = pa_idxset_first(m->core->source_outputs, &idx); so; so = pa_idxset_next(m->core->source_outputs, &idx))
PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
pa_modargs_free(ma);
@ -869,6 +1038,16 @@ void pa__done(pa_module*m) {
if (u->source_output_new_hook_slot)
pa_hook_slot_free(u->source_output_new_hook_slot);
if (u->sink_put_hook_slot)
pa_hook_slot_free(u->sink_put_hook_slot);
if (u->source_put_hook_slot)
pa_hook_slot_free(u->source_put_hook_slot);
if (u->sink_unlink_hook_slot)
pa_hook_slot_free(u->sink_unlink_hook_slot);
if (u->source_unlink_hook_slot)
pa_hook_slot_free(u->source_unlink_hook_slot);
if (u->connection_unlink_hook_slot)
pa_hook_slot_free(u->connection_unlink_hook_slot);

View file

@ -25,6 +25,7 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/rtclock.h>
#include <pulsecore/core.h>
#include <pulsecore/core-util.h>
@ -75,45 +76,43 @@ struct device_info {
struct userdata *userdata;
pa_sink *sink;
pa_source *source;
struct timeval last_use;
pa_usec_t last_use;
pa_time_event *time_event;
};
static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
struct device_info *d = userdata;
pa_assert(d);
d->userdata->core->mainloop->time_restart(d->time_event, NULL);
if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && !(d->sink->suspend_cause & PA_SUSPEND_IDLE)) {
pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
pa_sink_suspend(d->sink, TRUE);
pa_sink_suspend(d->sink, TRUE, PA_SUSPEND_IDLE);
}
if (d->source && pa_source_check_suspend(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) {
pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
pa_source_suspend(d->source, TRUE);
pa_source_suspend(d->source, TRUE, PA_SUSPEND_IDLE);
}
}
static void restart(struct device_info *d) {
struct timeval tv;
pa_usec_t now;
const char *s;
uint32_t timeout;
pa_assert(d);
pa_assert(d->sink || d->source);
pa_gettimeofday(&tv);
d->last_use = tv;
d->last_use = now = pa_rtclock_now();
s = pa_proplist_gets(d->sink ? d->sink->proplist : d->source->proplist, "module-suspend-on-idle.timeout");
if (!s || pa_atou(s, &timeout) < 0)
timeout = d->userdata->timeout;
timeout = d->userdata->timeout;
pa_timeval_add(&tv, timeout * PA_USEC_PER_SEC);
d->userdata->core->mainloop->time_restart(d->time_event, &tv);
pa_core_rttime_restart(d->userdata->core, d->time_event, now + timeout * PA_USEC_PER_SEC);
if (d->sink)
pa_log_debug("Sink %s becomes idle, timeout in %u seconds.", d->sink->name, timeout);
@ -127,13 +126,13 @@ static void resume(struct device_info *d) {
d->userdata->core->mainloop->time_restart(d->time_event, NULL);
if (d->sink) {
pa_sink_suspend(d->sink, FALSE);
pa_sink_suspend(d->sink, FALSE, PA_SUSPEND_IDLE);
pa_log_debug("Sink %s becomes busy.", d->sink->name);
}
if (d->source) {
pa_source_suspend(d->source, FALSE);
pa_source_suspend(d->source, FALSE, PA_SUSPEND_IDLE);
pa_log_debug("Source %s becomes busy.", d->source->name);
}
@ -338,7 +337,7 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user
d->userdata = u;
d->source = source ? pa_source_ref(source) : NULL;
d->sink = sink ? pa_sink_ref(sink) : NULL;
d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);
d->time_event = pa_core_rttime_new(c, PA_USEC_INVALID, timeout_cb, d);
pa_hashmap_put(u->device_infos, o, d);
if ((d->sink && pa_sink_check_suspend(d->sink) <= 0) ||

View file

@ -31,6 +31,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/version.h>
@ -50,7 +51,7 @@
#include <pulsecore/time-smoother.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/proplist-util.h>
#include <pulsecore/auth-cookie.h>
@ -112,7 +113,7 @@ static const char* const valid_modargs[] = {
#define DEFAULT_TIMEOUT 5
#define LATENCY_INTERVAL 10
#define LATENCY_INTERVAL (10*PA_USEC_PER_SEC)
#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
@ -395,7 +396,7 @@ static void check_smoother_status(struct userdata *u, pa_bool_t past) {
pa_assert(u);
x = pa_rtclock_usec();
x = pa_rtclock_now();
/* Correct by the time the requested issued needs to travel to the
* other side. This is a valid thread-safe access, because the
@ -500,7 +501,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
pa_usec_t yl, yr, *usec = data;
yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
yr = pa_smoother_get(u->smoother, pa_rtclock_now());
*usec = yl > yr ? yl - yr : 0;
return 0;
@ -533,7 +534,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
else
y = 0;
pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
pa_smoother_put(u->smoother, pa_rtclock_now(), y);
/* We can access this freely here, since the main thread is waiting for us */
u->thread_transport_usec = u->transport_usec;
@ -607,7 +608,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
pa_usec_t yr, yl, *usec = data;
yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
yr = pa_smoother_get(u->smoother, pa_rtclock_now());
*usec = yr > yl ? yr - yl : 0;
return 0;
@ -633,7 +634,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
y += (pa_usec_t) offset;
pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
pa_smoother_put(u->smoother, pa_rtclock_now(), y);
/* We can access this freely here, since the main thread is waiting for us */
u->thread_transport_usec = u->transport_usec;
@ -683,7 +684,6 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;
@ -730,7 +730,7 @@ static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
}
if (channel != u->channel) {
pa_log("Recieved data for invalid channel");
pa_log("Received data for invalid channel");
goto fail;
}
@ -878,9 +878,8 @@ static void request_latency(struct userdata *u) {
}
/* Called from main context */
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
struct timeval ntv;
pa_assert(m);
pa_assert(e);
@ -888,9 +887,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct
request_latency(u);
pa_gettimeofday(&ntv);
ntv.tv_sec += LATENCY_INTERVAL;
m->time_restart(e, &ntv);
pa_core_rttime_restart(u->core, e, pa_rtclock_now() + LATENCY_INTERVAL);
}
/* Called from main context */
@ -1157,10 +1154,10 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_cvolume_equal(&volume, &u->sink->virtual_volume))
return;
pa_sink_volume_changed(u->sink, &volume);
pa_sink_volume_changed(u->sink, &volume, FALSE);
if (u->version >= 11)
pa_sink_mute_changed(u->sink, mute);
pa_sink_mute_changed(u->sink, mute, FALSE);
return;
@ -1357,7 +1354,6 @@ static void start_subscribe(struct userdata *u) {
/* Called from main context */
static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
struct timeval ntv;
#ifdef TUNNEL_SINK
uint32_t bytes;
#endif
@ -1439,9 +1435,7 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t
request_info(u);
pa_assert(!u->time_event);
pa_gettimeofday(&ntv);
ntv.tv_sec += LATENCY_INTERVAL;
u->time_event = u->core->mainloop->time_new(u->core->mainloop, &ntv, timeout_callback, u);
u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + LATENCY_INTERVAL, timeout_callback, u);
request_latency(u);
@ -1675,7 +1669,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_assert(u);
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);
return;
}
@ -1706,7 +1700,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
}
u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->mempool);
u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX);
u->pdispatch = pa_pdispatch_new(u->core->mainloop, TRUE, command_table, PA_COMMAND_MAX);
pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u);
@ -1825,7 +1819,7 @@ int pa__init(pa_module*m) {
TRUE,
TRUE,
10,
pa_rtclock_usec(),
pa_rtclock_now(),
FALSE);
u->ctag = 1;
u->device_index = u->channel = PA_INVALID_INDEX;
@ -1853,7 +1847,7 @@ int pa__init(pa_module*m) {
goto fail;
}
if (!(u->client = pa_socket_client_new_string(m->core->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
if (!(u->client = pa_socket_client_new_string(m->core->mainloop, TRUE, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
pa_log("Failed to connect to server '%s'", u->server_name);
goto fail;
}

View file

@ -0,0 +1,457 @@
/***
This file is part of PulseAudio.
Copyright 2009 Lennart Poettering
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.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <limits.h>
#include <sys/inotify.h>
#include <libudev.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/namereg.h>
#include "module-udev-detect-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
struct device {
char *path;
pa_bool_t accessible;
char *card_name;
uint32_t module;
};
struct userdata {
pa_core *core;
pa_hashmap *devices;
pa_bool_t use_tsched;
struct udev* udev;
struct udev_monitor *monitor;
pa_io_event *udev_io;
int inotify_fd;
pa_io_event *inotify_io;
};
static const char* const valid_modargs[] = {
"tsched",
NULL
};
static void device_free(struct device *d) {
pa_assert(d);
pa_xfree(d->path);
pa_xfree(d->card_name);
pa_xfree(d);
}
static const char *path_get_card_id(const char *path) {
const char *e;
if (!path)
return NULL;
if (!(e = strrchr(path, '/')))
return NULL;
if (!pa_startswith(e, "/card"))
return NULL;
return e + 5;
}
static void verify_access(struct userdata *u, struct device *d) {
char *cd;
pa_card *card;
pa_assert(u);
pa_assert(d);
if (!(card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
return;
cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path));
d->accessible = access(cd, W_OK) >= 0;
pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible));
pa_xfree(cd);
pa_card_suspend(card, !d->accessible, PA_SUSPEND_SESSION);
}
static void card_changed(struct userdata *u, struct udev_device *dev) {
struct device *d;
const char *path;
const char *t;
char *card_name, *args;
pa_module *m;
char *n;
pa_assert(u);
pa_assert(dev);
path = udev_device_get_devpath(dev);
if ((d = pa_hashmap_get(u->devices, path))) {
verify_access(u, d);
return;
}
if (!(t = udev_device_get_property_value(dev, "PULSE_NAME")))
if (!(t = udev_device_get_property_value(dev, "ID_ID")))
if (!(t = udev_device_get_property_value(dev, "ID_PATH")))
t = path_get_card_id(path);
n = pa_namereg_make_valid_name(t);
card_name = pa_sprintf_malloc("alsa_card.%s", n);
args = pa_sprintf_malloc("device_id=\"%s\" "
"name=\"%s\" "
"card_name=\"%s\" "
"tsched=%i "
"card_properties=\"module-udev-detect.discovered=1\"",
path_get_card_id(path),
n,
card_name,
(int) u->use_tsched);
pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
m = pa_module_load(u->core, "module-alsa-card", args);
pa_xfree(args);
if (m) {
pa_log_info("Card %s (%s) added.", path, n);
d = pa_xnew(struct device, 1);
d->path = pa_xstrdup(path);
d->card_name = card_name;
d->module = m->index;
d->accessible = TRUE;
pa_hashmap_put(u->devices, d->path, d);
} else
pa_xfree(card_name);
pa_xfree(n);
}
static void remove_card(struct userdata *u, struct udev_device *dev) {
struct device *d;
pa_assert(u);
pa_assert(dev);
if (!(d = pa_hashmap_remove(u->devices, udev_device_get_devpath(dev))))
return;
pa_log_info("Card %s removed.", d->path);
pa_module_unload_request_by_index(u->core, d->module, TRUE);
device_free(d);
}
static void process_device(struct userdata *u, struct udev_device *dev) {
const char *action, *ff;
pa_assert(u);
pa_assert(dev);
if (udev_device_get_property_value(dev, "PULSE_IGNORE")) {
pa_log_debug("Ignoring %s, because marked so.", udev_device_get_devpath(dev));
return;
}
if ((ff = udev_device_get_property_value(dev, "SOUND_FORM_FACTOR")) &&
pa_streq(ff, "modem")) {
pa_log_debug("Ignoring %s, because it is a modem.", udev_device_get_devpath(dev));
return;
}
action = udev_device_get_action(dev);
if (action && pa_streq(action, "remove"))
remove_card(u, dev);
else if ((!action || pa_streq(action, "change")) &&
udev_device_get_property_value(dev, "SOUND_INITIALIZED"))
card_changed(u, dev);
/* For an explanation why we don't look for 'add' events here
* have a look into /lib/udev/rules.d/78-sound-card.rules! */
}
static void process_path(struct userdata *u, const char *path) {
struct udev_device *dev;
if (!path_get_card_id(path))
return;
if (!(dev = udev_device_new_from_syspath(u->udev, path))) {
pa_log("Failed to get udev device object from udev.");
return;
}
process_device(u, dev);
udev_device_unref(dev);
}
static void monitor_cb(
pa_mainloop_api*a,
pa_io_event* e,
int fd,
pa_io_event_flags_t events,
void *userdata) {
struct userdata *u = userdata;
struct udev_device *dev;
pa_assert(a);
if (!(dev = udev_monitor_receive_device(u->monitor))) {
pa_log("Failed to get udev device object from monitor.");
goto fail;
}
if (!path_get_card_id(udev_device_get_devpath(dev)))
return;
process_device(u, dev);
udev_device_unref(dev);
return;
fail:
a->io_free(u->udev_io);
u->udev_io = NULL;
}
static void inotify_cb(
pa_mainloop_api*a,
pa_io_event* e,
int fd,
pa_io_event_flags_t events,
void *userdata) {
struct {
struct inotify_event e;
char name[NAME_MAX];
} buf;
struct userdata *u = userdata;
static int type = 0;
pa_bool_t verify = FALSE;
for (;;) {
ssize_t r;
pa_zero(buf);
if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) {
if (r < 0 && errno == EAGAIN)
break;
pa_log("read() from inotify failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
goto fail;
}
if ((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC"))
verify = TRUE;
}
if (verify) {
struct device *d;
void *state;
pa_log_debug("Verifying access.");
PA_HASHMAP_FOREACH(d, u->devices, state)
verify_access(u, d);
}
return;
fail:
a->io_free(u->inotify_io);
u->inotify_io = NULL;
if (u->inotify_fd >= 0) {
pa_close(u->inotify_fd);
u->inotify_fd = -1;
}
}
static int setup_inotify(struct userdata *u) {
char *dev_snd;
int r;
if ((u->inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
pa_log("inotify_init1() failed: %s", pa_cstrerror(errno));
return -1;
}
dev_snd = pa_sprintf_malloc("%s/snd", udev_get_dev_path(u->udev));
r = inotify_add_watch(u->inotify_fd, dev_snd, IN_CLOSE_WRITE);
pa_xfree(dev_snd);
if (r < 0) {
pa_log("inotify_add_watch() failed: %s", pa_cstrerror(errno));
return -1;
}
pa_assert_se(u->inotify_io = u->core->mainloop->io_new(u->core->mainloop, u->inotify_fd, PA_IO_EVENT_INPUT, inotify_cb, u));
return 0;
}
int pa__init(pa_module *m) {
struct userdata *u = NULL;
pa_modargs *ma;
struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
int fd;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
u->use_tsched = TRUE;
u->inotify_fd = -1;
if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
pa_log("Failed to parse tsched argument.");
goto fail;
}
if (!(u->udev = udev_new())) {
pa_log("Failed to initialize udev library.");
goto fail;
}
if (setup_inotify(u) < 0)
goto fail;
if (!(u->monitor = udev_monitor_new_from_netlink(u->udev, "udev"))) {
pa_log("Failed to initialize monitor.");
goto fail;
}
errno = 0;
if (udev_monitor_enable_receiving(u->monitor) < 0) {
pa_log("Failed to enable monitor: %s", pa_cstrerror(errno));
if (errno == EPERM)
pa_log_info("Most likely your kernel is simply too old and "
"allows only priviliged processes to listen to device events. "
"Please upgrade your kernel to at least 2.6.30.");
goto fail;
}
if ((fd = udev_monitor_get_fd(u->monitor)) < 0) {
pa_log("Failed to get udev monitor fd.");
goto fail;
}
pa_assert_se(u->udev_io = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, monitor_cb, u));
if (!(enumerate = udev_enumerate_new(u->udev))) {
pa_log("Failed to initialize udev enumerator.");
goto fail;
}
if (udev_enumerate_add_match_subsystem(enumerate, "sound") < 0) {
pa_log("Failed to match to subsystem.");
goto fail;
}
if (udev_enumerate_scan_devices(enumerate) < 0) {
pa_log("Failed to scan for devices.");
goto fail;
}
first = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(item, first)
process_path(u, udev_list_entry_get_name(item));
udev_enumerate_unref(enumerate);
pa_log_info("Loaded %u modules.", pa_hashmap_size(u->devices));
pa_modargs_free(ma);
return 0;
fail:
if (enumerate)
udev_enumerate_unref(enumerate);
if (ma)
pa_modargs_free(ma);
pa__done(m);
return -1;
}
void pa__done(pa_module *m) {
struct userdata *u;
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->udev_io)
m->core->mainloop->io_free(u->udev_io);
if (u->monitor)
udev_monitor_unref(u->monitor);
if (u->udev)
udev_unref(u->udev);
if (u->inotify_io)
m->core->mainloop->io_free(u->inotify_io);
if (u->inotify_fd >= 0)
pa_close(u->inotify_fd);
if (u->devices) {
struct device *d;
while ((d = pa_hashmap_steal_first(u->devices)))
device_free(d);
pa_hashmap_free(u->devices, NULL, NULL);
}
pa_xfree(u);
}

View file

@ -256,7 +256,7 @@ static void poll_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *t
pa_gettimeofday(&ntv);
pa_timeval_add(&ntv, u->poll_timeout);
a->time_restart(e, &ntv);
a->rtclock_time_restart(e, &ntv);
}
static void defer_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
@ -549,7 +549,7 @@ int pa__init(pa_core *c, pa_module*m) {
pa_gettimeofday(&tv);
pa_timeval_add(&tv, u->poll_timeout);
u->event = c->mainloop->time_new(c->mainloop, &tv, poll_cb, u);
u->event = c->mainloop->rtclock_time_new(c->mainloop, &tv, poll_cb, u);
assert(u->event);
u->defer = c->mainloop->defer_new(c->mainloop, defer_cb, u);

View file

@ -889,7 +889,6 @@ static void thread_func(void *userdata) {
pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;

View file

@ -42,13 +42,15 @@
#include <linux/sockios.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
@ -57,7 +59,6 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/thread.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/socket-util.h>
#include "module-raop-sink-symdef.h"
@ -181,7 +182,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
pa_smoother_pause(u->smoother, pa_rtclock_usec());
pa_smoother_pause(u->smoother, pa_rtclock_now());
/* Issue a FLUSH if we are connected */
if (u->fd >= 0) {
@ -193,7 +194,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_RUNNING:
if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
/* The connection can be closed when idle, so check to
see if we need to reestablish it */
@ -216,7 +217,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t w, r;
r = pa_smoother_get(u->smoother, pa_rtclock_usec());
r = pa_smoother_get(u->smoother, pa_rtclock_now());
w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
@ -323,9 +324,8 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
/* Create a chunk of memory that is our encoded silence sample. */
pa_memchunk_reset(&silence);
@ -465,7 +465,7 @@ static void thread_func(void *userdata) {
else
usec = 0;
pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
}
/* Hmm, nothing to do. Let's sleep */
@ -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_sample_spec(&data, &ss);
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)))
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, desc);
else

View file

@ -331,7 +331,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
uint32_t port = pa_rtsp_serverport(c->rtsp);
pa_log_debug("RAOP: RECORDED");
if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, c->host, port))) {
if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, TRUE, c->host, port))) {
pa_log("failed to connect to server '%s:%d'", c->host, port);
return;
}

View file

@ -0,0 +1,259 @@
/***
Copyright 2009 Lennart Poettering
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
***/
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "reserve-monitor.h"
struct rm_monitor {
int ref;
char *device_name;
char *service_name;
DBusConnection *connection;
unsigned busy:1;
unsigned filtering:1;
unsigned matching:1;
rm_change_cb_t change_cb;
void *userdata;
};
#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
static DBusHandlerResult filter_handler(
DBusConnection *c,
DBusMessage *s,
void *userdata) {
DBusMessage *reply;
rm_monitor *m;
DBusError error;
dbus_error_init(&error);
m = userdata;
assert(m->ref >= 1);
if (dbus_message_is_signal(s, "org.freedesktop.DBus", "NameOwnerChanged")) {
const char *name, *old, *new;
if (!dbus_message_get_args(
s,
&error,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &old,
DBUS_TYPE_STRING, &new,
DBUS_TYPE_INVALID))
goto invalid;
if (strcmp(name, m->service_name) == 0) {
m->busy = !!(new && *new);
if (m->change_cb) {
m->ref++;
m->change_cb(m);
rm_release(m);
}
}
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
invalid:
if (!(reply = dbus_message_new_error(
s,
DBUS_ERROR_INVALID_ARGS,
"Invalid arguments")))
goto oom;
if (!dbus_connection_send(c, reply, NULL))
goto oom;
dbus_message_unref(reply);
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_HANDLED;
oom:
if (reply)
dbus_message_unref(reply);
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
int rm_watch(
rm_monitor **_m,
DBusConnection *connection,
const char*device_name,
rm_change_cb_t change_cb,
DBusError *error) {
rm_monitor *m = NULL;
int r;
DBusError _error;
if (!error)
error = &_error;
dbus_error_init(error);
if (!_m)
return -EINVAL;
if (!connection)
return -EINVAL;
if (!device_name)
return -EINVAL;
if (!(m = calloc(sizeof(rm_monitor), 1)))
return -ENOMEM;
m->ref = 1;
if (!(m->device_name = strdup(device_name))) {
r = -ENOMEM;
goto fail;
}
m->connection = dbus_connection_ref(connection);
m->change_cb = change_cb;
if (!(m->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
r = -ENOMEM;
goto fail;
}
sprintf(m->service_name, SERVICE_PREFIX "%s", m->device_name);
if (!(dbus_connection_add_filter(m->connection, filter_handler, m, NULL))) {
r = -ENOMEM;
goto fail;
}
m->filtering = 1;
dbus_bus_add_match(m->connection,
"type='signal',"
"sender='" DBUS_SERVICE_DBUS "',"
"interface='" DBUS_INTERFACE_DBUS "',"
"member='NameOwnerChanged'", error);
if (dbus_error_is_set(error)) {
r = -EIO;
goto fail;
}
m->matching = 1;
m->busy = dbus_bus_name_has_owner(m->connection, m->service_name, error);
if (dbus_error_is_set(error)) {
r = -EIO;
goto fail;
}
*_m = m;
return 0;
fail:
if (&_error == error)
dbus_error_free(&_error);
if (m)
rm_release(m);
return r;
}
void rm_release(rm_monitor *m) {
if (!m)
return;
assert(m->ref > 0);
if (--m->ref > 0)
return;
if (m->matching)
dbus_bus_remove_match(
m->connection,
"type='signal',"
"sender='" DBUS_SERVICE_DBUS "',"
"interface='" DBUS_INTERFACE_DBUS "',"
"member='NameOwnerChanged'", NULL);
if (m->filtering)
dbus_connection_remove_filter(
m->connection,
filter_handler,
m);
free(m->device_name);
free(m->service_name);
if (m->connection)
dbus_connection_unref(m->connection);
free(m);
}
int rm_busy(rm_monitor *m) {
if (!m)
return -EINVAL;
assert(m->ref > 0);
return m->busy;
}
void rm_set_userdata(rm_monitor *m, void *userdata) {
if (!m)
return;
assert(m->ref > 0);
m->userdata = userdata;
}
void* rm_get_userdata(rm_monitor *m) {
if (!m)
return NULL;
assert(m->ref > 0);
return m->userdata;
}

View file

@ -0,0 +1,62 @@
#ifndef fooreservemonitorhfoo
#define fooreservemonitorhfoo
/***
Copyright 2009 Lennart Poettering
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
***/
#include <dbus/dbus.h>
#include <inttypes.h>
typedef struct rm_monitor rm_monitor;
/* Prototype for a function that is called whenever the reservation
* device of a device changes. Use rm_monitor_busy() to find out the
* new state.*/
typedef void (*rm_change_cb_t)(rm_monitor *m);
/* Creates a monitor for watching the lock status of a device. Returns
* 0 on success, a negative errno style return value on error. The
* DBus error might be set as well if the error was caused D-Bus. */
int rm_watch(
rm_monitor **m, /* On success a pointer to the newly allocated rm_device object will be filled in here */
DBusConnection *connection, /* Session bus (when D-Bus learns about user busses we should switchg to user busses) */
const char *device_name, /* The device to monitor, e.g. "Audio0" */
rm_change_cb_t change_cb, /* Will be called whenever the lock status changes. May be NULL */
DBusError *error); /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */
/* Free a rm_monitor object */
void rm_release(rm_monitor *m);
/* Checks whether the device is currently reserved, and returns 1
* then, 0 if not, negative errno style error code value on error. */
int rm_busy(rm_monitor *m);
/* Attach a userdata pointer to an rm_monitor */
void rm_set_userdata(rm_monitor *m, void *userdata);
/* Query the userdata pointer from an rm_monitor. Returns NULL if no
* userdata was set. */
void* rm_get_userdata(rm_monitor *m);
#endif

View file

@ -35,6 +35,7 @@
#ifdef HAVE_DBUS
#include <pulsecore/dbus-shared.h>
#include "reserve.h"
#include "reserve-monitor.h"
#endif
#include "reserve-wrap.h"
@ -50,6 +51,17 @@ struct pa_reserve_wrapper {
#endif
};
struct pa_reserve_monitor_wrapper {
PA_REFCNT_DECLARE;
pa_core *core;
pa_hook hook;
char *shared_name;
#ifdef HAVE_DBUS
pa_dbus_connection *connection;
struct rm_monitor *monitor;
#endif
};
static void reserve_wrapper_free(pa_reserve_wrapper *r) {
pa_assert(r);
@ -83,7 +95,7 @@ static int request_cb(rd_device *d, int forced) {
PA_REFCNT_INC(r);
k = pa_hook_fire(&r->hook, PA_INT_TO_PTR(forced));
pa_log_debug("Device unlock has been requested and %s.", k < 0 ? "failed" : "succeeded");
pa_log_debug("Device unlock of %s has been requested and %s.", r->shared_name, k < 0 ? "failed" : "succeeded");
pa_reserve_wrapper_unref(r);
@ -191,3 +203,138 @@ void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const
rd_set_application_device_name(r->device, name);
#endif
}
static void reserve_monitor_wrapper_free(pa_reserve_monitor_wrapper *w) {
pa_assert(w);
#ifdef HAVE_DBUS
if (w->monitor)
rm_release(w->monitor);
if (w->connection)
pa_dbus_connection_unref(w->connection);
#endif
pa_hook_done(&w->hook);
if (w->shared_name) {
pa_assert_se(pa_shared_remove(w->core, w->shared_name) >= 0);
pa_xfree(w->shared_name);
}
pa_xfree(w);
}
#ifdef HAVE_DBUS
static void change_cb(rm_monitor *m) {
pa_reserve_monitor_wrapper *w;
int k;
pa_assert(m);
pa_assert_se(w = rm_get_userdata(m));
pa_assert(PA_REFCNT_VALUE(w) >= 1);
PA_REFCNT_INC(w);
if ((k = rm_busy(w->monitor)) < 0)
return;
pa_hook_fire(&w->hook, PA_INT_TO_PTR(!!k));
pa_log_debug("Device lock status of %s changed: %s", w->shared_name, k ? "busy" : "not busy");
pa_reserve_monitor_wrapper_unref(w);
}
#endif
pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name) {
pa_reserve_monitor_wrapper *w;
int k;
char *t;
#ifdef HAVE_DBUS
DBusError error;
dbus_error_init(&error);
#endif
pa_assert(c);
pa_assert(device_name);
t = pa_sprintf_malloc("reserve-monitor-wrapper@%s", device_name);
if ((w = pa_shared_get(c, t))) {
pa_xfree(t);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
PA_REFCNT_INC(w);
return w;
}
w = pa_xnew0(pa_reserve_monitor_wrapper, 1);
PA_REFCNT_INIT(w);
w->core = c;
pa_hook_init(&w->hook, w);
w->shared_name = t;
pa_assert_se(pa_shared_set(c, w->shared_name, w) >= 0);
#ifdef HAVE_DBUS
if (!(w->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
pa_log_warn("Unable to contact D-Bus session bus: %s: %s", error.name, error.message);
/* We don't treat this as error here because we want allow PA
* to run even when no session bus is available. */
return w;
}
if ((k = rm_watch(
&w->monitor,
pa_dbus_connection_get(w->connection),
device_name,
change_cb,
NULL)) < 0) {
pa_log_warn("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k));
goto fail;
}
pa_log_debug("Successfully create reservation lock monitor for device '%s'", device_name);
rm_set_userdata(w->monitor, w);
return w;
fail:
dbus_error_free(&error);
reserve_monitor_wrapper_free(w);
return NULL;
#else
return w;
#endif
}
void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *w) {
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
if (PA_REFCNT_DEC(w) > 0)
return;
reserve_monitor_wrapper_free(w);
}
pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *w) {
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
return &w->hook;
}
pa_bool_t pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *w) {
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
return rm_busy(w->monitor) > 0;
}

View file

@ -28,11 +28,18 @@
typedef struct pa_reserve_wrapper pa_reserve_wrapper;
pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name);
void pa_reserve_wrapper_unref(pa_reserve_wrapper *r);
pa_hook* pa_reserve_wrapper_hook(pa_reserve_wrapper *r);
void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const char *name);
typedef struct pa_reserve_monitor_wrapper pa_reserve_monitor_wrapper;
pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name);
void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *m);
pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *m);
pa_bool_t pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *m);
#endif

View file

@ -43,16 +43,15 @@ struct rd_device {
DBusConnection *connection;
int owning:1;
int registered:1;
int filtering:1;
int gave_up:1;
unsigned owning:1;
unsigned registered:1;
unsigned filtering:1;
unsigned gave_up:1;
rd_request_cb_t request_cb;
void *userdata;
};
#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
#define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
@ -297,6 +296,7 @@ static DBusHandlerResult filter_handler(
dbus_error_init(&error);
d = userdata;
assert(d->ref >= 1);
if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) {
const char *name;
@ -560,7 +560,7 @@ void rd_release(
assert(d->ref > 0);
if (--d->ref)
if (--d->ref > 0)
return;
@ -575,17 +575,11 @@ void rd_release(
d->connection,
d->object_path);
if (d->owning) {
DBusError error;
dbus_error_init(&error);
if (d->owning)
dbus_bus_release_name(
d->connection,
d->service_name,
&error);
dbus_error_free(&error);
}
NULL);
free(d->device_name);
free(d->application_name);

View file

@ -45,7 +45,7 @@ typedef int (*rd_request_cb_t)(
* the error was caused D-Bus. */
int rd_acquire(
rd_device **d, /* On success a pointer to the newly allocated rd_device object will be filled in here */
DBusConnection *connection,
DBusConnection *connection, /* Session bus (when D-Bus learns about user busses we should switchg to user busses) */
const char *device_name, /* The device to lock, e.g. "Audio0" */
const char *application_name, /* A human readable name of the application, e.g. "PulseAudio Sound Server" */
int32_t priority, /* The priority for this application. If unsure use 0 */

View file

@ -33,6 +33,7 @@
#include <unistd.h>
#include <poll.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@ -43,13 +44,13 @@
#include <pulsecore/sink-input.h>
#include <pulsecore/memblockq.h>
#include <pulsecore/log.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/atomic.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/atomic.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/socket-util.h>
@ -62,7 +63,7 @@
#include "sap.h"
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_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
@ -112,6 +113,7 @@ struct session {
struct userdata {
pa_module *module;
pa_core *core;
pa_sap_context sap_context;
pa_io_event* sap_event;
@ -193,7 +195,7 @@ static void sink_input_suspend_within_thread(pa_sink_input* i, pa_bool_t b) {
pa_assert_se(s = i->userdata);
if (b) {
pa_smoother_pause(s->smoother, pa_rtclock_usec());
pa_smoother_pause(s->smoother, pa_rtclock_now());
pa_memblockq_flush_read(s->memblockq);
} else
s->first_packet = FALSE;
@ -621,15 +623,13 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
}
}
static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *ptv, void *userdata) {
static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
struct session *s, *n;
struct userdata *u = userdata;
struct timeval now;
struct timeval tv;
pa_assert(m);
pa_assert(t);
pa_assert(ptv);
pa_assert(u);
pa_rtclock_get(&now);
@ -647,9 +647,7 @@ static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const str
}
/* Restart timer */
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEATH_TIMEOUT*PA_USEC_PER_SEC);
m->time_restart(t, &tv);
pa_core_rttime_restart(u->module->core, t, pa_rtclock_now() + DEATH_TIMEOUT * PA_USEC_PER_SEC);
}
int pa__init(pa_module*m) {
@ -663,7 +661,6 @@ int pa__init(pa_module*m) {
socklen_t salen;
const char *sap_address;
int fd = -1;
struct timeval tv;
pa_assert(m);
@ -696,6 +693,7 @@ int pa__init(pa_module*m) {
m->userdata = u = pa_xnew(struct userdata, 1);
u->module = m;
u->core = m->core;
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
@ -705,9 +703,7 @@ int pa__init(pa_module*m) {
u->n_sessions = 0;
u->by_origin = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEATH_TIMEOUT * PA_USEC_PER_SEC);
u->check_death_event = m->core->mainloop->time_new(m->core->mainloop, &tv, check_death_event_cb, u);
u->check_death_event = pa_core_rttime_new(m->core, pa_rtclock_now() + DEATH_TIMEOUT * PA_USEC_PER_SEC, check_death_event_cb, u);
pa_modargs_free(ma);

View file

@ -31,6 +31,7 @@
#include <string.h>
#include <unistd.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/xmalloc.h>
@ -77,7 +78,7 @@ PA_MODULE_USAGE(
#define DEFAULT_DESTINATION "224.0.0.56"
#define MEMBLOCKQ_MAXLENGTH (1024*170)
#define DEFAULT_MTU 1280
#define SAP_INTERVAL 5
#define SAP_INTERVAL (5*PA_USEC_PER_SEC)
static const char* const valid_modargs[] = {
"source",
@ -151,18 +152,14 @@ static void source_output_kill(pa_source_output* o) {
static void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
struct timeval next;
pa_assert(m);
pa_assert(t);
pa_assert(tv);
pa_assert(u);
pa_sap_send(&u->sap_context, 0);
pa_gettimeofday(&next);
pa_timeval_add(&next, SAP_INTERVAL * PA_USEC_PER_SEC);
m->time_restart(t, &next);
pa_core_rttime_restart(u->module->core, t, pa_rtclock_now() + SAP_INTERVAL);
}
int pa__init(pa_module*m) {
@ -186,7 +183,6 @@ int pa__init(pa_module*m) {
char *p;
int r, j;
socklen_t k;
struct timeval tv;
char hn[128], *n;
pa_bool_t loop = FALSE;
pa_source_output_new_data data;
@ -347,8 +343,8 @@ int pa__init(pa_module*m) {
o->push = source_output_push;
o->kill = source_output_kill;
pa_log_info("Configured source latency of %lu ms.",
pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC);
pa_log_info("Configured source latency of %llu ms.",
(unsigned long long) pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC);
m->userdata = o->userdata = u = pa_xnew(struct userdata, 1);
u->module = m;
@ -395,9 +391,7 @@ int pa__init(pa_module*m) {
pa_sap_send(&u->sap_context, 0);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, SAP_INTERVAL * PA_USEC_PER_SEC);
u->sap_event = m->core->mainloop->time_new(m->core->mainloop, &tv, sap_event_cb, u);
u->sap_event = pa_core_rttime_new(m->core, pa_rtclock_now() + SAP_INTERVAL, sap_event_cb, u);
pa_source_output_put(u->source_output);

View file

@ -333,7 +333,7 @@ int pa_rtsp_connect(pa_rtsp_client *c) {
pa_xfree(c->session);
c->session = NULL;
if (!(c->sc = pa_socket_client_new_string(c->mainloop, c->hostname, c->port))) {
if (!(c->sc = pa_socket_client_new_string(c->mainloop, TRUE, c->hostname, c->port))) {
pa_log("failed to connect to server '%s:%d'", c->hostname, c->port);
return -1;
}

View file

@ -58,7 +58,7 @@ static int read_id(struct udev_device *d, const char *n) {
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;
struct udev *udev;
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;
int id;
pa_assert(core);
pa_assert(p);
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;
}
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 ((v = udev_device_get_property_value(card, "ID_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)
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 ((v = udev_device_get_property_value(card, "SOUND_FORM_FACTOR")) && *v)
pa_proplist_sets(p, PA_PROP_DEVICE_FORM_FACTOR, v);
if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH))
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
/* This is normaly not set by the udev rules but may be useful to
* allow administrators to overwrite the device description.*/
if (!pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
if ((v = udev_device_get_property_value(card, "SOUND_DESCRIPTION")) && *v)
@ -140,3 +152,40 @@ finish:
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>
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