summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/modules/alsa/alsa-sink.c2
-rw-r--r--src/modules/alsa/alsa-source.c2
-rw-r--r--src/modules/alsa/alsa-util.c13
-rw-r--r--src/modules/alsa/alsa-util.h2
-rw-r--r--src/modules/alsa/module-alsa-sink.c4
-rw-r--r--src/modules/alsa/module-alsa-source.c4
-rw-r--r--src/modules/module-solaris.c101
-rw-r--r--src/pulse/introspect.h14
-rw-r--r--src/pulsecore/core-util.c34
9 files changed, 107 insertions, 69 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 0296f64e..0dc0e2b3 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1644,7 +1644,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem);
+ pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL));
pa_sink_new_data_init(&data);
data.driver = driver;
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index ef365a21..348cd082 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1496,7 +1496,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem);
+ pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL));
pa_source_new_data_init(&data);
data.driver = driver;
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 2d0ca107..5b5270b8 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1094,7 +1094,8 @@ success:
int pa_alsa_find_mixer_and_elem(
snd_pcm_t *pcm,
snd_mixer_t **_m,
- snd_mixer_elem_t **_e) {
+ snd_mixer_elem_t **_e,
+ const char *control_name) {
int err;
snd_mixer_t *m;
@@ -1146,11 +1147,17 @@ int pa_alsa_find_mixer_and_elem(
switch (snd_pcm_stream(pcm)) {
case SND_PCM_STREAM_PLAYBACK:
- e = pa_alsa_find_elem(m, "Master", "PCM", TRUE);
+ if (control_name)
+ e = pa_alsa_find_elem(m, control_name, NULL, TRUE);
+ else
+ e = pa_alsa_find_elem(m, "Master", "PCM", TRUE);
break;
case SND_PCM_STREAM_CAPTURE:
- e = pa_alsa_find_elem(m, "Capture", "Mic", FALSE);
+ if (control_name)
+ e = pa_alsa_find_elem(m, control_name, NULL, FALSE);
+ else
+ e = pa_alsa_find_elem(m, "Capture", "Mic", FALSE);
break;
default:
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 68496d51..5cad2958 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -54,7 +54,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
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, snd_mixer_t **_m, snd_mixer_elem_t **_e);
+int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name);
typedef struct pa_alsa_profile_info {
pa_channel_map map;
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index c728a446..8e600ab8 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -52,7 +52,8 @@ PA_MODULE_USAGE(
"tsched=<enable system timer based scheduling mode?> "
"tsched_buffer_size=<buffer size when using timer based scheduling> "
"tsched_buffer_watermark=<lower fill watermark> "
- "ignore_dB=<ignore dB information from the device?>");
+ "ignore_dB=<ignore dB information from the device?> "
+ "control=<name of mixer control>");
static const char* const valid_modargs[] = {
"name",
@@ -70,6 +71,7 @@ static const char* const valid_modargs[] = {
"tsched_buffer_size",
"tsched_buffer_watermark",
"ignore_dB",
+ "control",
NULL
};
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 6188019f..e6b27b3d 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -76,7 +76,8 @@ PA_MODULE_USAGE(
"tsched=<enable system timer based scheduling mode?> "
"tsched_buffer_size=<buffer size when using timer based scheduling> "
"tsched_buffer_watermark=<upper fill watermark> "
- "ignore_dB=<ignore dB information from the device?>");
+ "ignore_dB=<ignore dB information from the device?> "
+ "control=<name of mixer control>");
static const char* const valid_modargs[] = {
"name",
@@ -94,6 +95,7 @@ static const char* const valid_modargs[] = {
"tsched_buffer_size",
"tsched_buffer_watermark",
"ignore_dB",
+ "control",
NULL
};
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index e7dfc05e..ecd3ba32 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -75,7 +75,7 @@ PA_MODULE_USAGE(
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
- "buffer_size=<record buffer size> "
+ "buffer_length=<milliseconds> "
"channel_map=<channel map>");
PA_MODULE_LOAD_ONCE(FALSE);
@@ -94,8 +94,7 @@ struct userdata {
uint32_t frame_size;
int32_t buffer_size;
- volatile uint64_t written_bytes, read_bytes;
- pa_mutex *written_bytes_lock;
+ uint64_t written_bytes, read_bytes;
char *device_name;
int mode;
@@ -107,9 +106,8 @@ struct userdata {
uint32_t play_samples_msw, record_samples_msw;
uint32_t prev_playback_samples, prev_record_samples;
- pa_mutex *sample_counter_lock;
- size_t min_request;
+ int32_t minimum_request;
};
static const char* const valid_modargs[] = {
@@ -118,7 +116,7 @@ static const char* const valid_modargs[] = {
"device",
"record",
"playback",
- "buffer_size",
+ "buffer_length",
"format",
"rate",
"channels",
@@ -127,13 +125,9 @@ static const char* const valid_modargs[] = {
};
#define DEFAULT_DEVICE "/dev/audio"
-#define MIN_BUFFER_SIZE (640)
-#define MAX_RENDER_HZ (300)
-/* This render rate limit implies a minimum latency, but without it we waste too much CPU time in the
- * IO thread. The maximum render rate and minimum latency (or minimum buffer size) are unachievable on
- * common hardware anyway. Note that MIN_BUFFER_SIZE * MAX_RENDER_HZ >= 4 * 48000 Bps.
- */
+#define MAX_RENDER_HZ (300)
+/* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */
static uint64_t get_playback_buffered_bytes(struct userdata *u) {
audio_info_t info;
@@ -142,8 +136,6 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
pa_assert(u->sink);
- pa_mutex_lock(u->sample_counter_lock);
-
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
@@ -159,8 +151,6 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
u->prev_playback_samples = info.play.samples;
played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
- pa_mutex_unlock(u->sample_counter_lock);
-
return u->written_bytes - played_bytes;
}
@@ -171,11 +161,9 @@ static pa_usec_t sink_get_latency(struct userdata *u, pa_sample_spec *ss) {
pa_assert(ss);
if (u->fd >= 0) {
- pa_mutex_lock(u->written_bytes_lock);
r = pa_bytes_to_usec(get_playback_buffered_bytes(u), ss);
if (u->memchunk.memblock)
r += pa_bytes_to_usec(u->memchunk.length, ss);
- pa_mutex_unlock(u->written_bytes_lock);
}
return r;
}
@@ -487,7 +475,7 @@ static void sink_set_volume(pa_sink *s) {
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
- info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@@ -523,7 +511,7 @@ static void source_set_volume(pa_source *s) {
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
- info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@@ -580,6 +568,25 @@ static void sink_get_mute(pa_sink *s) {
}
}
+static void process_rewind(struct userdata *u) {
+ size_t rewind_nbytes;
+
+ pa_assert(u);
+
+ /* Figure out how much we shall rewind and reset the counter */
+ rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ if (rewind_nbytes > 0) {
+ pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+ rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes);
+ u->memchunk.length -= rewind_nbytes;
+ pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+ }
+
+ pa_sink_process_rewind(u->sink, rewind_nbytes);
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
unsigned short revents = 0;
@@ -604,10 +611,13 @@ static void thread_func(void *userdata) {
uint64_t buffered_bytes;
if (u->sink->thread_info.rewind_requested)
- pa_sink_process_rewind(u->sink, 0);
+ process_rewind(u);
err = ioctl(u->fd, AUDIO_GETINFO, &info);
- pa_assert(err >= 0);
+ if (err < 0) {
+ pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
if (info.play.error) {
pa_log_debug("buffer under-run!");
@@ -635,7 +645,7 @@ static void thread_func(void *userdata) {
len = u->buffer_size - buffered_bytes;
len -= len % u->frame_size;
- if (len < u->min_request)
+ if (len < (size_t) u->minimum_request)
break;
if (u->memchunk.length < len)
@@ -648,12 +658,16 @@ static void thread_func(void *userdata) {
if (w <= 0) {
switch (errno) {
case EINTR:
- break;
+ continue;
case EAGAIN:
+ /* If the buffer_size is too big, we get EAGAIN. Avoiding that limit by trial and error
+ * is not ideal, but I don't know how to get the system to tell me what the limit is.
+ */
u->buffer_size = u->buffer_size * 18 / 25;
u->buffer_size -= u->buffer_size % u->frame_size;
- u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
+ u->buffer_size = PA_MAX(u->buffer_size, 2 * u->minimum_request);
pa_sink_set_max_request_within_thread(u->sink, u->buffer_size);
+ pa_sink_set_max_rewind_within_thread(u->sink, u->buffer_size);
pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
break;
default:
@@ -663,10 +677,8 @@ static void thread_func(void *userdata) {
} else {
pa_assert(w % u->frame_size == 0);
- pa_mutex_lock(u->written_bytes_lock);
u->written_bytes += w;
u->memchunk.length -= w;
- pa_mutex_unlock(u->written_bytes_lock);
u->memchunk.index += w;
if (u->memchunk.length <= 0) {
@@ -677,9 +689,8 @@ static void thread_func(void *userdata) {
}
pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
- } else {
+ } else
pa_rtpoll_set_timer_disabled(u->rtpoll);
- }
/* Try to read some data and pass it on to the source driver */
@@ -797,6 +808,7 @@ int pa__init(pa_module *m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
+ uint32_t buffer_length_msec;
int fd;
pa_sink_new_data sink_new_data;
pa_source_new_data source_new_data;
@@ -822,8 +834,6 @@ int pa__init(pa_module *m) {
}
u = pa_xnew0(struct userdata, 1);
- u->sample_counter_lock = pa_mutex_new(FALSE, FALSE);
- u->written_bytes_lock = pa_mutex_new(FALSE, FALSE);
/*
* For a process (or several processes) to use the same audio device for both
@@ -839,13 +849,15 @@ int pa__init(pa_module *m) {
}
u->frame_size = pa_frame_size(&ss);
- u->buffer_size = 16384;
- if (pa_modargs_get_value_s32(ma, "buffer_size", &u->buffer_size) < 0) {
- pa_log("failed to parse buffer size argument");
+ u->minimum_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss);
+
+ buffer_length_msec = 100;
+ if (pa_modargs_get_value_u32(ma, "buffer_length", &buffer_length_msec) < 0) {
+ pa_log("failed to parse buffer_length argument");
goto fail;
}
- u->buffer_size -= u->buffer_size % u->frame_size;
- if (u->buffer_size < (int32_t)MIN_BUFFER_SIZE) {
+ u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss);
+ if (u->buffer_size < 2 * u->minimum_request) {
pa_log("supplied buffer size argument is too small");
goto fail;
}
@@ -947,15 +959,17 @@ int pa__init(pa_module *m) {
u->sink->refresh_volume = u->sink->refresh_muted = TRUE;
pa_sink_set_max_request(u->sink, u->buffer_size);
- u->min_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss);
+ pa_sink_set_max_rewind(u->sink, u->buffer_size);
} else
u->sink = NULL;
pa_assert(u->source || u->sink);
u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
- pa_assert(u->sig);
- ioctl(u->fd, I_SETSIG, S_MSG);
+ if (u->sig)
+ ioctl(u->fd, I_SETSIG, S_MSG);
+ else
+ pa_log_warn("Could not register SIGPOLL handler");
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
@@ -1010,8 +1024,10 @@ void pa__done(pa_module *m) {
if (!(u = m->userdata))
return;
- ioctl(u->fd, I_SETSIG, 0);
- pa_signal_free(u->sig);
+ if (u->sig) {
+ ioctl(u->fd, I_SETSIG, 0);
+ pa_signal_free(u->sig);
+ }
if (u->sink)
pa_sink_unlink(u->sink);
@@ -1044,9 +1060,6 @@ void pa__done(pa_module *m) {
if (u->fd >= 0)
close(u->fd);
- pa_mutex_free(u->written_bytes_lock);
- pa_mutex_free(u->sample_counter_lock);
-
pa_xfree(u->device_name);
pa_xfree(u);
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index ec8a2c06..d7ef1c0c 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -344,7 +344,7 @@ typedef struct pa_module_info {
pa_proplist *proplist; /**< Property list \since 0.9.15 */
} pa_module_info;
-/** Callback prototype for pa_context_get_module_info() and firends*/
+/** Callback prototype for pa_context_get_module_info() and friends*/
typedef void (*pa_module_info_cb_t) (pa_context *c, const pa_module_info*i, int eol, void *userdata);
/** Get some information about a module by its index */
@@ -377,7 +377,7 @@ typedef struct pa_client_info {
pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_client_info;
-/** Callback prototype for pa_context_get_client_info() and firends*/
+/** Callback prototype for pa_context_get_client_info() and friends*/
typedef void (*pa_client_info_cb_t) (pa_context *c, const pa_client_info*i, int eol, void *userdata);
/** Get information about a client by its index */
@@ -418,7 +418,7 @@ typedef struct pa_card_info {
pa_proplist *proplist; /**< Property list */
} pa_card_info;
-/** Callback prototype for pa_context_get_card_info() and firends \since 0.9.15 */
+/** Callback prototype for pa_context_get_card_info() and friends \since 0.9.15 */
typedef void (*pa_card_info_cb_t) (pa_context *c, const pa_card_info*i, int eol, void *userdata);
/** Get information about a card by its index \since 0.9.15 */
@@ -460,7 +460,7 @@ typedef struct pa_sink_input_info {
pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_sink_input_info;
-/** Callback prototype for pa_context_get_sink_input_info() and firends*/
+/** Callback prototype for pa_context_get_sink_input_info() and friends*/
typedef void (*pa_sink_input_info_cb_t) (pa_context *c, const pa_sink_input_info *i, int eol, void *userdata);
/** Get some information about a sink input by its index */
@@ -506,7 +506,7 @@ typedef struct pa_source_output_info {
pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_source_output_info;
-/** Callback prototype for pa_context_get_source_output_info() and firends*/
+/** Callback prototype for pa_context_get_source_output_info() and friends*/
typedef void (*pa_source_output_info_cb_t) (pa_context *c, const pa_source_output_info *i, int eol, void *userdata);
/** Get information about a source output by its index */
@@ -571,7 +571,7 @@ typedef struct pa_sample_info {
pa_proplist *proplist; /**< Property list for this sample. \since 0.9.11 */
} pa_sample_info;
-/** Callback prototype for pa_context_get_sample_info_by_name() and firends */
+/** Callback prototype for pa_context_get_sample_info_by_name() and friends */
typedef void (*pa_sample_info_cb_t)(pa_context *c, const pa_sample_info *i, int eol, void *userdata);
/** Get information about a sample by its name */
@@ -606,7 +606,7 @@ typedef struct pa_autoload_info {
const char *argument; /**< Argument string for module */
} pa_autoload_info;
-/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and firends */
+/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and friends */
typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
/** \deprecated Get info about a specific autoload entry. */
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index e5d8a2f4..4b093c0e 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2608,7 +2608,7 @@ char *pa_unescape(char *p) {
}
char *pa_realpath(const char *path) {
- char *r, *t;
+ char *t;
pa_assert(path);
/* We want only abolsute paths */
@@ -2617,17 +2617,31 @@ char *pa_realpath(const char *path) {
return NULL;
}
-#if !defined(__GLIBC__) && !defined(__APPLE__)
-#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
-#endif
+#if defined(__GLIBC__) || defined(__APPLE__)
+ {
+ char *r;
- if (!(r = realpath(path, NULL)))
- return NULL;
+ if (!(r = realpath(path, NULL)))
+ return NULL;
+
+ /* We copy this here in case our pa_xmalloc() is not
+ * implemented on top of libc malloc() */
+ t = pa_xstrdup(r);
+ pa_xfree(r);
+ }
+#elif defined(PATH_MAX)
+ {
+ char *path_buf;
+ path_buf = pa_xmalloc(PATH_MAX);
- /* We copy this here in case our pa_xmalloc() is not implemented
- * on top of libc malloc() */
- t = pa_xstrdup(r);
- pa_xfree(r);
+ if (!(t = realpath(path, path_buf))) {
+ pa_xfree(path_buf);
+ return NULL;
+ }
+ }
+#else
+#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
+#endif
return t;
}