From 7e2afffb81ab8b495d4f769858a855c2df2c0610 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Jul 2009 22:38:38 +0200 Subject: alsa: deal properly with IO functions asking us to write 0 bytes --- src/modules/alsa/alsa-sink.c | 6 +++++- src/modules/alsa/alsa-source.c | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 0cde694c..e7925902 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -494,6 +494,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size) frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size; + if (frames == 0) + break; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -631,7 +634,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames); pa_memblock_release(u->memchunk.memblock); - pa_assert(frames != 0); + if (frames == 0) + break; if (PA_UNLIKELY(frames < 0)) { diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index a6760e1e..41bb768b 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -473,6 +473,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size) frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size; + if (frames == 0) + break; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -599,7 +602,10 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames); pa_memblock_release(chunk.memblock); - pa_assert(frames != 0); + if (frames == 0) { + pa_memblock_unref(chunk.memblock); + break; + } if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); -- cgit From e3b0ce57e0f44790bd75412778cce8129e3945eb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Jul 2009 22:48:54 +0200 Subject: udev: don't fail if /dev/snd is not available right-away --- src/modules/module-udev-detect.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 1ad6fa2d..c8ec2bf9 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -65,6 +65,8 @@ static const char* const valid_modargs[] = { NULL }; +static int setup_inotify(struct userdata *u); + static void device_free(struct device *d) { pa_assert(d); @@ -117,6 +119,9 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { pa_assert(u); pa_assert(dev); + /* Maybe /dev/snd is now available? */ + setup_inotify(u); + path = udev_device_get_devpath(dev); if ((d = pa_hashmap_get(u->devices, path))) { @@ -262,7 +267,7 @@ static void inotify_cb( } buf; struct userdata *u = userdata; static int type = 0; - pa_bool_t verify = FALSE; + pa_bool_t verify = FALSE, deleted = FALSE; for (;;) { ssize_t r; @@ -279,6 +284,9 @@ static void inotify_cb( if ((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC")) verify = TRUE; + + if ((buf.e.mask & (IN_DELETE_SELF|IN_MOVE_SELF))) + deleted = TRUE; } if (verify) { @@ -291,11 +299,14 @@ static void inotify_cb( verify_access(u, d); } - return; + if (!deleted) + return; fail: - a->io_free(u->inotify_io); - u->inotify_io = NULL; + if (u->inotify_io) { + a->io_free(u->inotify_io); + u->inotify_io = NULL; + } if (u->inotify_fd >= 0) { pa_close(u->inotify_fd); @@ -307,17 +318,28 @@ static int setup_inotify(struct userdata *u) { char *dev_snd; int r; + if (u->inotify_fd >= 0) + return 0; + 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); + r = inotify_add_watch(u->inotify_fd, dev_snd, IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF); pa_xfree(dev_snd); if (r < 0) { - pa_log("inotify_add_watch() failed: %s", pa_cstrerror(errno)); + int saved_errno = errno; + + pa_close(u->inotify_fd); + u->inotify_fd = -1; + + if (saved_errno == ENOENT) + return 0; + + pa_log("inotify_add_watch() failed: %s", pa_cstrerror(saved_errno)); return -1; } -- cgit From 1160cad9c3f87659e810ebdb9c4a20626e8a4eed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Jul 2009 18:44:26 +0200 Subject: alsa: control 'Speaker' element as well --- src/modules/alsa/mixer/paths/analog-output-headphones.conf | 4 ++++ src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf | 6 ++++++ src/modules/alsa/mixer/paths/analog-output-mono.conf | 6 ++++++ src/modules/alsa/mixer/paths/analog-output.conf | 6 ++++++ 4 files changed, 22 insertions(+) (limited to 'src/modules') diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf index c018e0eb..691cb3f2 100644 --- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf +++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf @@ -44,6 +44,10 @@ volume = merge override-map.1 = all override-map.2 = all-left,all-right +[Element Speaker] +switch = off +volume = off + [Element Front] switch = off volume = off diff --git a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf index 7a267890..2db976a5 100644 --- a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf @@ -45,6 +45,12 @@ override-map.2 = lfe,lfe switch = off volume = off +[Element Speaker] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Front] switch = off volume = off diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf index f6cb9f8a..a58cc970 100644 --- a/src/modules/alsa/mixer/paths/analog-output-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf @@ -42,6 +42,12 @@ override-map.2 = all-left,all-right switch = off volume = off +[Element Speaker] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Front] switch = off volume = off diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf index ea108aaf..b412a437 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf +++ b/src/modules/alsa/mixer/paths/analog-output.conf @@ -41,6 +41,12 @@ volume = off switch = off volume = off +[Element Speaker] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Front] switch = mute volume = merge -- cgit From ac38c4d89845237cd5d19c29d8d3ef55f0374dca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Jul 2009 18:49:28 +0200 Subject: build-sys: add a couple of stub Makefiles --- src/modules/alsa/mixer/Makefile | 1 + src/modules/alsa/mixer/paths/Makefile | 1 + src/modules/alsa/mixer/profile-sets/Makefile | 1 + 3 files changed, 3 insertions(+) create mode 120000 src/modules/alsa/mixer/Makefile create mode 120000 src/modules/alsa/mixer/paths/Makefile create mode 120000 src/modules/alsa/mixer/profile-sets/Makefile (limited to 'src/modules') diff --git a/src/modules/alsa/mixer/Makefile b/src/modules/alsa/mixer/Makefile new file mode 120000 index 00000000..b4955194 --- /dev/null +++ b/src/modules/alsa/mixer/Makefile @@ -0,0 +1 @@ +../../../pulse/Makefile \ No newline at end of file diff --git a/src/modules/alsa/mixer/paths/Makefile b/src/modules/alsa/mixer/paths/Makefile new file mode 120000 index 00000000..dc23aaa2 --- /dev/null +++ b/src/modules/alsa/mixer/paths/Makefile @@ -0,0 +1 @@ +../../../../pulse/Makefile \ No newline at end of file diff --git a/src/modules/alsa/mixer/profile-sets/Makefile b/src/modules/alsa/mixer/profile-sets/Makefile new file mode 120000 index 00000000..dc23aaa2 --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/Makefile @@ -0,0 +1 @@ +../../../../pulse/Makefile \ No newline at end of file -- cgit From c325b93c01c5973db659aead6b66f753aa719168 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Jul 2009 19:24:26 +0200 Subject: alsa: don't reset volume/mute when selecting path --- src/modules/alsa/alsa-mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index a5515e1b..6a0b4ab7 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -940,7 +940,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { PA_LLIST_FOREACH(e, p->elements) { switch (e->switch_use) { - case PA_ALSA_SWITCH_MUTE: case PA_ALSA_SWITCH_OFF: r = element_set_switch(e, m, FALSE); break; @@ -949,6 +948,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { r = element_set_switch(e, m, TRUE); break; + case PA_ALSA_SWITCH_MUTE: case PA_ALSA_SWITCH_IGNORE: case PA_ALSA_SWITCH_SELECT: r = 0; @@ -960,7 +960,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { switch (e->volume_use) { case PA_ALSA_VOLUME_OFF: - case PA_ALSA_VOLUME_MERGE: r = element_mute_volume(e, m); break; @@ -968,6 +967,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { r = element_zero_volume(e, m); break; + case PA_ALSA_VOLUME_MERGE: case PA_ALSA_VOLUME_IGNORE: r = 0; break; -- cgit From 18433c19b690432179e9a0ed83eff611f5cecc67 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Jul 2009 13:45:59 +0200 Subject: alsa: handle correctly if alsa returns us 0 or EAGAIN on snd_pcm_mmap_begin if we didn't call snd_pcm_avail immediately before --- src/modules/alsa/alsa-sink.c | 22 ++++++++++++++++++---- src/modules/alsa/alsa-source.c | 28 +++++++++++++++++++++------- 2 files changed, 39 insertions(+), 11 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index e7925902..46562cbd 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -401,6 +401,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle snd_pcm_sframes_t n; size_t n_bytes; int r; + pa_bool_t after_avail = TRUE; /* First we determine how many samples are missing to fill the * buffer up to 100% */ @@ -484,6 +485,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { + if (!after_avail && err == -EAGAIN) + break; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; @@ -494,9 +498,12 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size) frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size; - if (frames == 0) + if (!after_avail && frames == 0) break; + pa_assert(frames > 0); + after_avail = FALSE; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -617,6 +624,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle for (;;) { snd_pcm_sframes_t frames; void *p; + pa_bool_t after_avail = TRUE; /* pa_log_debug("%lu frames to write", (unsigned long) frames); */ @@ -634,17 +642,23 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames); pa_memblock_release(u->memchunk.memblock); - if (frames == 0) - break; - if (PA_UNLIKELY(frames < 0)) { + if (!after_avail && (int) frames == -EAGAIN) + break; + if ((r = try_recover(u, "snd_pcm_writei", (int) frames)) == 0) continue; return r; } + if (!after_avail && frames == 0) + break; + + pa_assert(frames > 0); + after_avail = FALSE; + u->memchunk.index += (size_t) frames * u->frame_size; u->memchunk.length -= (size_t) frames * u->frame_size; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 41bb768b..8a1fbe5f 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -391,6 +391,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled snd_pcm_sframes_t n; size_t n_bytes; int r; + pa_bool_t after_avail = TRUE; if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { @@ -463,6 +464,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) { + if (!after_avail && err == -EAGAIN) + break; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; @@ -473,9 +477,12 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size) frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size; - if (frames == 0) + if (!after_avail && frames == 0) break; + pa_assert(frames > 0); + after_avail = FALSE; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -542,6 +549,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled snd_pcm_sframes_t n; size_t n_bytes; int r; + pa_bool_t after_avail = TRUE; if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { @@ -602,20 +610,26 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames); pa_memblock_release(chunk.memblock); - if (frames == 0) { - pa_memblock_unref(chunk.memblock); - break; - } - if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); - if ((r = try_recover(u, "snd_pcm_readi", (int) (frames))) == 0) + if (!after_avail && (int) frames == -EAGAIN) + break; + + if ((r = try_recover(u, "snd_pcm_readi", (int) frames)) == 0) continue; return r; } + if (!after_avail && frames == 0) { + pa_memblock_unref(chunk.memblock); + break; + } + + pa_assert(frames > 0); + after_avail = FALSE; + chunk.index = 0; chunk.length = (size_t) frames * u->frame_size; -- cgit From 2bbdf631f8ef1029558be19bcf8a9176a5932b8b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Jul 2009 13:58:22 +0200 Subject: udev: explain what happened when inotify_add_watch() returned ENOSPC, rhbz #513571 --- src/modules/module-udev-detect.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index c8ec2bf9..11de1ccb 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -336,8 +336,18 @@ static int setup_inotify(struct userdata *u) { pa_close(u->inotify_fd); u->inotify_fd = -1; - if (saved_errno == ENOENT) + if (saved_errno == ENOENT) { + pa_log_debug("/dev/snd/ is apparently not existing yet, retrying to create inotify watch later."); return 0; + } + + if (saved_errno == ENOSPC) { + pa_log("You apparently ran out of inotify watches, probably because Tracker/Beagle took them all away. " + "I wished people would do their homework first and fix inotify before using it for watching whole " + "directory trees which is something the current inotify is certainly not useful for. " + "Please make sure to drop the Tracker/Beagle guys a line complaining about their broken use of inotify."); + return 0; + } pa_log("inotify_add_watch() failed: %s", pa_cstrerror(saved_errno)); return -1; -- cgit From 5efb07281d5be1cddaed5b0610325fedd7f599ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Jul 2009 20:13:52 +0200 Subject: alsa: throw timing data away after device resume --- src/modules/alsa/alsa-sink.c | 6 +++++- src/modules/alsa/alsa-source.c | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 46562cbd..7fc602be 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -899,9 +899,13 @@ static int unsuspend(struct userdata *u) { if (build_pollfd(u) < 0) goto fail; + u->write_count = 0; + pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); + u->first = TRUE; u->since_start = 0; + pa_log_info("Resumed successfully..."); return 0; @@ -1204,7 +1208,7 @@ static int process_rewind(struct userdata *u) { if (rewind_nbytes <= 0) pa_log_info("Tried rewind, but was apparently not possible."); else { - u->write_count -= out_frames * u->frame_size; + u->write_count -= rewind_nbytes; pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); pa_sink_process_rewind(u->sink, rewind_nbytes); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 8a1fbe5f..ed9c1480 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -854,7 +854,9 @@ 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_now(), TRUE); + + u->read_count = 0; + pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); pa_log_info("Resumed successfully..."); -- cgit From c14f6c179fb3a52219f2796d20a1c6f5648aa8a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 31 Jul 2009 00:50:19 +0200 Subject: tunnel: don't assert on misaligned reads, closes #597 and rhbz #496310 --- src/modules/module-tunnel.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index d1153829..f788f660 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef TUNNEL_SINK #include "module-tunnel-sink-symdef.h" @@ -194,6 +195,7 @@ struct userdata { #else char *source_name; pa_source *source; + pa_mcalign *mcalign; #endif pa_auth_cookie *auth_cookie; @@ -614,14 +616,23 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off return 0; } - case SOURCE_MESSAGE_POST: + case SOURCE_MESSAGE_POST: { + pa_memchunk c; - if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) - pa_source_post(u->source, chunk); + pa_mcalign_push(u->mcalign, chunk); - u->counter += (int64_t) chunk->length; + while (pa_mcalign_pop(u->mcalign, &c) >= 0) { + + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) + pa_source_post(u->source, &c); + + pa_memblock_unref(c.memblock); + + u->counter += (int64_t) c.length; + } return 0; + } case SOURCE_MESSAGE_REMOTE_SUSPEND: @@ -1937,6 +1948,8 @@ int pa__init(pa_module*m) { pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); + + u->mcalign = pa_mcalign_new(pa_frame_size(&u->source->sample_spec)); #endif pa_xfree(dn); @@ -2030,6 +2043,11 @@ void pa__done(pa_module*m) { if (u->time_event) u->core->mainloop->time_free(u->time_event); +#ifndef TUNNEL_SINK + if (u->mcalign) + pa_mcalign_free(u->mcalign); +#endif + #ifdef TUNNEL_SINK pa_xfree(u->sink_name); #else -- cgit From 39aa1cf94d43efaa80571b3f9d3dc56297f2f5be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 31 Jul 2009 02:07:24 +0200 Subject: alsa: revert to first set number of periods, then set buffer size Apparently some ALSA drivers aren't happy with getting the buffer size configured first followed the period size. So swap the order again and document this for future reference so that we don't turn that around again. --- src/modules/alsa/alsa-util.c | 55 +++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 1f3e5dcd..a47a8958 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -233,14 +233,16 @@ int pa_alsa_set_hw_params( goto finish; } - if (_period_size && tsched_size && _periods) { + if (_period_size > 0 && tsched_size > 0 && _periods > 0) { + snd_pcm_uframes_t buffer_size; + unsigned int p; /* Adjust the buffer sizes, if we didn't get the rate we were asking for */ _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); if (_use_tsched) { - snd_pcm_uframes_t buffer_size = 0; + buffer_size = 0; if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0) pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); @@ -251,32 +253,33 @@ int pa_alsa_set_hw_params( _periods = 1; } - if (_period_size > 0 && _periods > 0) { - snd_pcm_uframes_t buffer_size; - - buffer_size = _periods * _period_size; - - if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) - pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); - } - - if (_periods > 0) { - - /* First we pass 0 as direction to get exactly what we - * asked for. That this is necessary is presumably a bug - * in ALSA. All in all this is mostly a hint to ALSA, so - * we don't care if this fails. */ - - dir = 0; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) { - dir = 1; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) { - dir = -1; - if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) - pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); - } + /* Some ALSA drivers really don't like if we set the buffer + * size first and the number of periods second. (which would + * make a lot more sense to me) So, follow this rule and + * adjust the periods first and the buffer size second */ + + /* First we pass 0 as direction to get exactly what we + * asked for. That this is necessary is presumably a bug + * in ALSA. All in all this is mostly a hint to ALSA, so + * we don't care if this fails. */ + + p = _periods; + dir = 0; + if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { + p = _periods; + dir = 1; + if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { + p = _periods; + dir = -1; + if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir)) < 0) + pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); } } + + /* Now set the buffer size */ + buffer_size = _periods * _period_size; + if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) + pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); } if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) -- cgit From 0113e7282cc5c0131f9f681b84459fcdf2e806c4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 31 Jul 2009 23:13:21 +0200 Subject: hal: add option to initialize all subdevices of an OSS device Patch from 'cmt', closes ticket #544 --- src/modules/module-hal-detect.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 658b3e55..79758b92 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -57,12 +57,14 @@ PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); #if defined(HAVE_ALSA) && defined(HAVE_OSS) PA_MODULE_USAGE("api= " - "tsched="); + "tsched=" + "subdevs="); #elif defined(HAVE_ALSA) PA_MODULE_USAGE("api= " "tsched="); #elif defined(HAVE_OSS) -PA_MODULE_USAGE("api="); +PA_MODULE_USAGE("api=" + "subdevs="); #endif PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!"); @@ -82,6 +84,9 @@ struct userdata { #ifdef HAVE_ALSA pa_bool_t use_tsched; #endif +#ifdef HAVE_OSS + pa_bool_t init_subdevs; +#endif }; #define CAPABILITY_ALSA "alsa" @@ -91,6 +96,9 @@ static const char* const valid_modargs[] = { "api", #ifdef HAVE_ALSA "tsched", +#endif +#ifdef HAVE_OSS + "subdevs", #endif NULL }; @@ -264,7 +272,7 @@ fail: #ifdef HAVE_OSS -static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) { +static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi, pa_bool_t init_subdevices) { char *class = NULL, *dev = NULL, *e; int device; pa_bool_t r = FALSE; @@ -294,7 +302,7 @@ static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) /* We only care for the main device */ device = libhal_device_get_property_int(context, udi, "oss.device", &error); - if (dbus_error_is_set(&error) || device != 0) + if (dbus_error_is_set(&error) || (device != 0 && init_subdevices == FALSE)) goto finish; r = TRUE; @@ -324,7 +332,7 @@ static int hal_device_load_oss(struct userdata *u, const char *udi, struct devic pa_assert(d); /* We only care for OSS PCM devices */ - if (!hal_oss_device_is_pcm(u->context, udi)) + if (!hal_oss_device_is_pcm(u->context, udi, u->init_subdevs)) goto fail; /* We store only one entry per card, hence we look for the originating device */ @@ -763,6 +771,13 @@ int pa__init(pa_module*m) { goto fail; } +#ifdef HAVE_OSS + if (pa_modargs_get_value_boolean(ma, "subdevs", &u->init_subdevs) < 0) { + pa_log("Failed to parse subdevs argument."); + goto fail; + } +#endif + if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) { pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message); goto fail; -- cgit From e5c2256e360df3662295b8cc0594568e425e8679 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 1 Aug 2009 01:59:58 +0200 Subject: pipe: replace PIPE_BUF macro pa pa_pipe_buf call This should help portability to platforms that lack PIPE_BUF. Based on a patch from Samuel Thibault. See ticket #546 --- src/modules/module-pipe-sink.c | 6 +++--- src/modules/module-pipe-source.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index 8a7dc846..9c169327 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -122,7 +122,7 @@ static int process_render(struct userdata *u) { pa_assert(u); if (u->memchunk.length <= 0) - pa_sink_render(u->sink, PIPE_BUF, &u->memchunk); + pa_sink_render(u->sink, pa_pipe_buf(u->fd), &u->memchunk); pa_assert(u->memchunk.length > 0); @@ -299,8 +299,8 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_max_request(u->sink, PIPE_BUF); - pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(PIPE_BUF, &u->sink->sample_spec)); + pa_sink_set_max_request(u->sink, pa_pipe_buf(u->fd)); + pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(pa_pipe_buf(u->fd), &u->sink->sample_spec)); u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index e5609fb5..49104f8d 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -142,7 +142,7 @@ static void thread_func(void *userdata) { void *p; if (!u->memchunk.memblock) { - u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF); + u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd)); u->memchunk.index = u->memchunk.length = 0; } -- cgit From 34f31f666e2a2c301e0735e48e48da44ff503acc Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sun, 2 Aug 2009 17:52:05 +0200 Subject: Recover stream when it's suspended upon rewind Error from snd_pcm_rewind() might mean we just woke up from suspend and didn't have a chance to try to recover the stream since we didn't write to it in between. Call try_recover() in such cases. Note that for this to work kernel must return ESTRPIPE instead of EBADF for rewind/forward attempts on suspended streams, so that snd_pcm_recover() can recognize it should snd_pcm_resume() the stream. This is not the case yet (2.6.31-rc5), patch is available. Signed-off-by: Lubomir Rintel --- src/modules/alsa/alsa-sink.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 7fc602be..2a734e35 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -340,6 +340,9 @@ static int try_recover(struct userdata *u, const char *call, int err) { if (err == -EPIPE) pa_log_debug("%s: Buffer underrun!", call); + if (err == -EBADFD) + pa_log_debug("%s: Stream suspended!", call); + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) { pa_log("%s: %s", call, pa_alsa_strerror(err)); return -1; @@ -1199,8 +1202,11 @@ static int process_rewind(struct userdata *u) { pa_log_debug("before: %lu", (unsigned long) in_frames); if ((out_frames = snd_pcm_rewind(u->pcm_handle, (snd_pcm_uframes_t) in_frames)) < 0) { pa_log("snd_pcm_rewind() failed: %s", pa_alsa_strerror((int) out_frames)); - return -1; + if (try_recover(u, "process_rewind", out_frames) < 0) + return -1; + out_frames = 0; } + pa_log_debug("after: %lu", (unsigned long) out_frames); rewind_nbytes = (size_t) out_frames * u->frame_size; -- cgit From 9b2534b6d0667589f69bf36702f9b998841d3d80 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 4 Aug 2009 00:23:43 +0200 Subject: alsa: properly treat ESTRPIPE as system suspend --- src/modules/alsa/alsa-sink.c | 4 ++-- src/modules/alsa/alsa-source.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 2a734e35..1c38430f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -340,8 +340,8 @@ static int try_recover(struct userdata *u, const char *call, int err) { if (err == -EPIPE) pa_log_debug("%s: Buffer underrun!", call); - if (err == -EBADFD) - pa_log_debug("%s: Stream suspended!", call); + if (err == -ESTRPIPE) + pa_log_debug("%s: System suspended!", call); if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) { pa_log("%s: %s", call, pa_alsa_strerror(err)); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index ed9c1480..9a51f857 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -333,6 +333,9 @@ static int try_recover(struct userdata *u, const char *call, int err) { if (err == -EPIPE) pa_log_debug("%s: Buffer overrun!", call); + if (err == -ESTRPIPE) + pa_log_debug("%s: System suspended!", call); + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) { pa_log("%s: %s", call, pa_alsa_strerror(err)); return -1; -- cgit From 51b3899348bf29dd88b56691aeea9f57895dfd14 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Aug 2009 23:55:06 +0200 Subject: core: save volume/mute changes coming from the hardware automatically Volume changes coming from the lower layers are most likely changes triggered by the user, so let's save them automatically. --- src/modules/bluetooth/module-bluetooth-device.c | 4 ++-- src/modules/module-tunnel.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 0560ef32..e682997f 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1446,12 +1446,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, TRUE); + pa_sink_volume_changed(u->sink, &v); } 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, TRUE); + pa_source_volume_changed(u->source, &v); } } } diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index f788f660..eaccea4e 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -1165,10 +1165,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, FALSE); + pa_sink_volume_changed(u->sink, &volume); if (u->version >= 11) - pa_sink_mute_changed(u->sink, mute, FALSE); + pa_sink_mute_changed(u->sink, mute); return; -- cgit From 7d4916379bbf05384ad199004949cc220822aa5f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Aug 2009 23:58:45 +0200 Subject: ladspa/remap: make sure we process all requested rewinds unconditionally In some situations a rewind request travelling downstream might be optimized away on its way and an upstream rewind processing might never come back. Hence, call _process_rewind() before each _render()just to make sure we processed them all. --- src/modules/module-ladspa-sink.c | 3 +++ src/modules/module-remap-sink.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 21f4a8f1..b26330c8 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -178,6 +178,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; + /* Hmm, process any rewind request that might be queued up */ + pa_sink_process_rewind(u->sink, 0); + while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 119f5b9f..0b7b9b8f 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -148,6 +148,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; + /* Hmm, process any rewind request that might be queued up */ + pa_sink_process_rewind(u->sink, 0); + pa_sink_render(u->sink, nbytes, chunk); return 0; } -- cgit From 4c1511500759c7701b407227e907c0e5c8e38763 Mon Sep 17 00:00:00 2001 From: Diego Elio 'Flameeyes' Pettenò Date: Sat, 8 Aug 2009 01:53:15 +0200 Subject: Split OSS support in output and wrapper. Since Fedora does not enable OSS output support at all, but still uses padsp, and in Gentoo we could also make use of padsp without OSS output support, split the two things in two parameters, although they both check for sys/soundcard.h once. --- src/modules/module-detect.c | 4 ++-- src/modules/module-hal-detect.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c index 18479df3..956fe4c5 100644 --- a/src/modules/module-detect.c +++ b/src/modules/module-detect.c @@ -119,7 +119,7 @@ static int detect_alsa(pa_core *c, int just_one) { } #endif -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT static int detect_oss(pa_core *c, int just_one) { FILE *f; int n = 0, b = 0; @@ -240,7 +240,7 @@ int pa__init(pa_module*m) { #ifdef HAVE_ALSA if ((n = detect_alsa(m->core, just_one)) <= 0) #endif -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT if ((n = detect_oss(m->core, just_one)) <= 0) #endif #ifdef HAVE_SOLARIS diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 79758b92..b5b2aaf0 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -55,14 +55,14 @@ PA_MODULE_AUTHOR("Shahms King"); PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); -#if defined(HAVE_ALSA) && defined(HAVE_OSS) +#if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT) PA_MODULE_USAGE("api= " "tsched=" "subdevs="); #elif defined(HAVE_ALSA) PA_MODULE_USAGE("api= " "tsched="); -#elif defined(HAVE_OSS) +#elif defined(HAVE_OSS_OUTPUT) PA_MODULE_USAGE("api=" "subdevs="); #endif @@ -84,7 +84,7 @@ struct userdata { #ifdef HAVE_ALSA pa_bool_t use_tsched; #endif -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT pa_bool_t init_subdevs; #endif }; @@ -97,7 +97,7 @@ static const char* const valid_modargs[] = { #ifdef HAVE_ALSA "tsched", #endif -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT "subdevs", #endif NULL @@ -270,7 +270,7 @@ fail: #endif -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi, pa_bool_t init_subdevices) { char *class = NULL, *dev = NULL, *e; @@ -402,7 +402,7 @@ static struct device* hal_device_add(struct userdata *u, const char *udi) { if (pa_streq(u->capability, CAPABILITY_ALSA)) r = hal_device_load_alsa(u, udi, d); #endif -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT if (pa_streq(u->capability, CAPABILITY_OSS)) r = hal_device_load_oss(u, udi, d); #endif @@ -761,7 +761,7 @@ int pa__init(pa_module*m) { api = pa_modargs_get_value(ma, "api", "oss"); #endif -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT if (pa_streq(api, "oss")) u->capability = CAPABILITY_OSS; #endif @@ -771,7 +771,7 @@ int pa__init(pa_module*m) { goto fail; } -#ifdef HAVE_OSS +#ifdef HAVE_OSS_OUTPUT if (pa_modargs_get_value_boolean(ma, "subdevs", &u->init_subdevs) < 0) { pa_log("Failed to parse subdevs argument."); goto fail; -- cgit From c904f97e2d77bac298130de55621fa2498b5aa51 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 11 Aug 2009 23:04:35 +0200 Subject: hal: add stub module that loads module-udev-detect instead of module-hal-detect This adds module-hal-detect-compat.c which when enabled will be compiled into a module module-hal-detect which simply loads module-udev-detect. The purpose of this is to allow easy upgrading without breaking default.pa. Distributions are recommended to enable this to easy upgrades from HAL versions of PA to udev versions. --- src/modules/module-hal-detect-compat.c | 84 ++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/modules/module-hal-detect-compat.c (limited to 'src/modules') diff --git a/src/modules/module-hal-detect-compat.c b/src/modules/module-hal-detect-compat.c new file mode 100644 index 00000000..14cf8143 --- /dev/null +++ b/src/modules/module-hal-detect-compat.c @@ -0,0 +1,84 @@ +/*** + 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 +#endif + +#include + +#include +#include +#include +#include + +#include "module-hal-detect-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Compatibility module"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!"); + +static const char* const valid_modargs[] = { + "api", + "tsched", + "subdevices", + NULL, +}; + +int pa__init(pa_module*m) { + pa_modargs *ma = NULL; + pa_bool_t tsched = TRUE; + pa_module *n; + char *t; + + 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, "tsched", &tsched) < 0) { + pa_log("tsched= expects boolean arguments"); + goto fail; + } + + pa_log_warn("We will now load module-udev-detect. Please make sure to remove module-hal-detect from your configuration."); + + t = pa_sprintf_malloc("tsched=%s", pa_yes_no(tsched)); + n = pa_module_load(m->core, "module-udev-detect", t); + pa_xfree(t); + + if (n) + pa_module_unload_request(m, TRUE); + + pa_modargs_free(ma); + + return n ? 0 : -1; + +fail: + if (ma) + pa_modargs_free(ma); + + return -1; +} -- cgit From c117febbe4a92864a7214da2dd8a5c9dae432925 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 11 Aug 2009 23:18:01 +0200 Subject: hal: replace subdevs= parameter by subdevices= parameter Abbreviating tsched like this was bad enough, so let's not add another option here. --- src/modules/module-hal-detect.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index b5b2aaf0..ec370d61 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -58,13 +58,13 @@ PA_MODULE_LOAD_ONCE(TRUE); #if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT) PA_MODULE_USAGE("api= " "tsched=" - "subdevs="); + "subdevices="); #elif defined(HAVE_ALSA) PA_MODULE_USAGE("api= " "tsched="); #elif defined(HAVE_OSS_OUTPUT) PA_MODULE_USAGE("api=" - "subdevs="); + "subdevices="); #endif PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!"); @@ -98,7 +98,7 @@ static const char* const valid_modargs[] = { "tsched", #endif #ifdef HAVE_OSS_OUTPUT - "subdevs", + "subdevices", #endif NULL }; @@ -772,8 +772,8 @@ int pa__init(pa_module*m) { } #ifdef HAVE_OSS_OUTPUT - if (pa_modargs_get_value_boolean(ma, "subdevs", &u->init_subdevs) < 0) { - pa_log("Failed to parse subdevs argument."); + if (pa_modargs_get_value_boolean(ma, "subdevices", &u->init_subdevs) < 0) { + pa_log("Failed to parse subdevices= argument."); goto fail; } #endif -- cgit From d27e26dca0b492b1906b42b2d0f7decd38ed8ae3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 11 Aug 2009 23:19:28 +0200 Subject: volume-restore: forward module load return value of stream-restore back to caller --- src/modules/module-volume-restore.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 91da598e..6e484eae 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -48,6 +48,7 @@ static const char* const valid_modargs[] = { int pa__init(pa_module*m) { pa_modargs *ma = NULL; pa_bool_t restore_device = TRUE, restore_volume = TRUE; + pa_module *n; char *t; pa_assert(m); @@ -66,13 +67,15 @@ int pa__init(pa_module*m) { pa_log_warn("We will now load module-stream-restore. Please make sure to remove module-volume-restore from your configuration."); t = pa_sprintf_malloc("restore_volume=%s restore_device=%s", pa_yes_no(restore_volume), pa_yes_no(restore_device)); - pa_module_load(m->core, "module-stream-restore", t); + n = pa_module_load(m->core, "module-stream-restore", t); pa_xfree(t); - pa_module_unload_request(m, TRUE); + if (n) + pa_module_unload_request(m, TRUE); pa_modargs_free(ma); - return 0; + + return n ? 0 : -1; fail: if (ma) -- cgit From 462cdf44b7fe36768c836c90761f6b8153290517 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 12 Aug 2009 00:53:38 +0200 Subject: alsa: adjust priority bonus of mappings that match the configured default channel map We need to make sure that having both input and output weighs more for selecting the default profile than a channel map that matches the default channel map has. https://bugzilla.redhat.com/show_bug.cgi?id=496320 --- src/modules/alsa/alsa-mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 6a0b4ab7..a4c2ee0f 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -2838,9 +2838,9 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) { if (bonus) { if (pa_channel_map_equal(&m->channel_map, bonus)) - m->priority += 5000; + m->priority += 50; else if (m->channel_map.channels == bonus->channels) - m->priority += 4000; + m->priority += 30; } return 0; -- cgit From e8340345f6a102cd03b6676576bcd3879ead7aad Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 12 Aug 2009 00:56:10 +0200 Subject: alsa: enable ext. amplifier by default --- src/modules/alsa/mixer/paths/analog-output.conf.common | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common index cc1185f4..3c6ce803 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf.common +++ b/src/modules/alsa/mixer/paths/analog-output.conf.common @@ -104,8 +104,8 @@ switch = select [Option External Amplifier:on] name = output-amplifier-on -priority = 0 +priority = 10 [Option External Amplifier:off] name = output-amplifier-off -priority = 10 +priority = 0 -- cgit From 7891f964e4a1858ccae744ddff5d33b78f00b4d2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 14 Aug 2009 23:55:32 +0200 Subject: module-stream-restore: don't fiddle with sinks/sources/streams that are not fully set up yet --- src/modules/module-stream-restore.c | 112 +++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 26 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 8c0bb6b0..727a5275 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -102,15 +102,16 @@ struct userdata { pa_idxset *subscribed; }; -#define ENTRY_VERSION 2 +#define ENTRY_VERSION 3 struct entry { uint8_t version; - pa_bool_t muted_valid:1, volume_valid:1, device_valid:1; + pa_bool_t muted_valid:1, volume_valid:1, device_valid:1, card_valid:1; pa_bool_t muted:1; pa_channel_map channel_map; pa_cvolume volume; char device[PA_NAME_MAX]; + char card[PA_NAME_MAX]; } PA_GCC_PACKED; enum { @@ -196,11 +197,21 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; } + if (!memchr(e->card, 0, sizeof(e->card))) { + pa_log_warn("Database contains entry for stream %s with missing NUL byte in card name", name); + goto fail; + } + if (e->device_valid && !pa_namereg_is_valid_name(e->device)) { pa_log_warn("Invalid device name stored in database for stream %s", name); goto fail; } + if (e->card_valid && !pa_namereg_is_valid_name(e->card)) { + pa_log_warn("Invalid card name stored in database for stream %s", name); + goto fail; + } + if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) { pa_log_warn("Invalid channel map stored in database for stream %s", name); goto fail; @@ -252,6 +263,10 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { (a->device_valid && strncmp(a->device, b->device, sizeof(a->device)))) return FALSE; + if (a->card_valid != b->card_valid || + (a->card_valid && strncmp(a->card, b->card, sizeof(a->card)))) + return FALSE; + if (a->muted_valid != b->muted_valid || (a->muted_valid && (a->muted != b->muted))) return FALSE; @@ -308,6 +323,11 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (sink_input->save_sink) { pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device)); entry.device_valid = TRUE; + + if (sink_input->sink->card) { + pa_strlcpy(entry.card, sink_input->sink->card->name, sizeof(entry.card)); + entry.card_valid = TRUE; + } } } else { @@ -327,6 +347,11 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (source_output->save_source) { pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device)); entry.device_valid = source_output->save_source; + + if (source_output->source->card) { + pa_strlcpy(entry.card, source_output->source->card->name, sizeof(entry.card)); + entry.card_valid = TRUE; + } } } @@ -368,19 +393,28 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n if (!(name = get_name(new_data->proplist, "sink-input"))) return PA_HOOK_OK; - if ((e = read_entry(u, name))) { + if (new_data->sink) + pa_log_debug("Not restoring device for stream %s, because already set.", name); + else if ((e = read_entry(u, name))) { + pa_sink *s = NULL; - if (e->device_valid) { - pa_sink *s; + if (e->device_valid) + s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK); - if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) { - if (!new_data->sink) { - pa_log_info("Restoring device for stream %s.", name); - new_data->sink = s; - new_data->save_sink = TRUE; - } else - pa_log_debug("Not restoring device for stream %s, because already set.", name); - } + if (!s && e->card_valid) { + pa_card *card; + + if ((card = pa_namereg_get(c, e->card, PA_NAMEREG_CARD))) + s = pa_idxset_first(card->sinks, NULL); + } + + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s))) { + pa_log_info("Restoring device for stream %s.", name); + new_data->sink = s; + new_data->save_sink = TRUE; } pa_xfree(e); @@ -455,18 +489,28 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou if (!(name = get_name(new_data->proplist, "source-output"))) return PA_HOOK_OK; - if ((e = read_entry(u, name))) { - pa_source *s; + if (new_data->source) + pa_log_debug("Not restoring device for stream %s, because already set", name); + else if ((e = read_entry(u, name))) { + pa_source *s = NULL; - if (e->device_valid) { - if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) { - if (!new_data->source) { - pa_log_info("Restoring device for stream %s.", name); - new_data->source = s; - new_data->save_source = TRUE; - } else - pa_log_debug("Not restoring device for stream %s, because already set", name); - } + if (e->device_valid) + s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE); + + if (!s && e->card_valid) { + pa_card *card; + + if ((card = pa_namereg_get(c, e->card, PA_NAMEREG_CARD))) + s = pa_idxset_first(card->sources, NULL); + } + + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (s && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) { + pa_log_info("Restoring device for stream %s.", name); + new_data->source = s; + new_data->save_source = TRUE; } pa_xfree(e); @@ -496,6 +540,12 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct if (si->save_sink) continue; + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) + continue; + if (!(name = get_name(si->proplist, "sink-input"))) continue; @@ -534,6 +584,12 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, if (so->direct_on_input) continue; + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so))) + continue; + if (!(name = get_name(so->proplist, "source-input"))) continue; @@ -575,7 +631,9 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str if (e->device_valid) { pa_sink *d; - if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && d != sink) + if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && + d != sink && + PA_SINK_IS_LINKED(pa_sink_get_state(d))) pa_sink_input_move_to(si, d, TRUE); } @@ -613,7 +671,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc if (e->device_valid) { pa_source *d; - if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && d != source) + if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && + d != source && + PA_SOURCE_IS_LINKED(pa_source_get_state(d))) pa_source_output_move_to(so, d, TRUE); } -- cgit From fd1266c666f62f1c19bff6c1ab3397300e25ffed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:15:18 +0200 Subject: rescure-stream: handle failed moves as well as dying sinks/sources --- src/modules/module-rescue-streams.c | 173 ++++++++++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 37 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index c23feceb..82f693f9 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -45,13 +45,43 @@ static const char* const valid_modargs[] = { }; struct userdata { - pa_hook_slot *sink_slot, *source_slot; + pa_hook_slot + *sink_unlink_slot, + *source_unlink_slot, + *sink_input_move_fail_slot, + *source_output_move_fail_slot; }; -static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { +static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) { + pa_sink *target, *def; + uint32_t idx; + + pa_assert(c); + pa_assert(i); + + def = pa_namereg_get_default_sink(c); + + if (def && def != skip && pa_sink_input_may_move_to(i, def)) + return def; + + PA_IDXSET_FOREACH(target, c->sinks, idx) { + if (target == def) + continue; + + if (target == skip) + continue; + + if (pa_sink_input_may_move_to(i, target)) + return target; + } + + pa_log_debug("No evacuation sink found."); + return NULL; +} + +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { pa_sink_input *i; uint32_t idx; - pa_sink *target; pa_assert(c); pa_assert(sink); @@ -65,21 +95,12 @@ 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_default_sink(c)) || target == sink) { - - PA_IDXSET_FOREACH(target, c->sinks, idx) - if (target != sink) - break; - - if (!target) { - pa_log_debug("No evacuation sink found."); - return PA_HOOK_OK; - } - } + PA_IDXSET_FOREACH(i, sink->inputs, idx) { + pa_sink *target; - pa_assert(target != sink); + if (!(target = find_evacuation_sink(c, i, sink))) + continue; - PA_IDXSET_FOREACH(i, sink->inputs, idx) { if (pa_sink_input_move_to(i, target, FALSE) < 0) pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name); @@ -91,9 +112,63 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user return PA_HOOK_OK; } -static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void* userdata) { +static pa_hook_result_t sink_input_move_fail_hook_callback(pa_core *c, pa_sink_input *i, void *userdata) { + pa_sink *target; + + pa_assert(c); + pa_assert(i); + + /* 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 (!(target = find_evacuation_sink(c, i, NULL))) + return PA_HOOK_OK; + + if (pa_sink_input_finish_move(i, target, FALSE) < 0) { + pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index, + pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name); + return PA_HOOK_OK; + + } else { + pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, + pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name); + return PA_HOOK_STOP; + } +} + +static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_source *skip) { + pa_source *target, *def; + uint32_t idx; + + pa_assert(c); + pa_assert(o); + + def = pa_namereg_get_default_source(c); + + if (def && def != skip && pa_source_output_may_move_to(o, def)) + return def; + + PA_IDXSET_FOREACH(target, c->sources, idx) { + if (target == def) + continue; + + if (target == skip) + continue; + + if (!target->monitor_of != !skip->monitor_of) + continue; + + if (pa_source_output_may_move_to(o, target)) + return target; + } + + pa_log_debug("No evacuation source found."); + return NULL; +} + +static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) { pa_source_output *o; - pa_source *target; uint32_t idx; pa_assert(c); @@ -108,21 +183,12 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void return PA_HOOK_OK; } - 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) - break; - - if (!target) { - pa_log_info("No evacuation source found."); - return PA_HOOK_OK; - } - } + PA_IDXSET_FOREACH(o, source->outputs, idx) { + pa_source *target; - pa_assert(target != source); + if (!(target = find_evacuation_source(c, o, source))) + continue; - PA_IDXSET_FOREACH(o, source->outputs, idx) { if (pa_source_output_move_to(o, target, FALSE) < 0) pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name); @@ -134,6 +200,31 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void return PA_HOOK_OK; } +static pa_hook_result_t source_output_move_fail_hook_callback(pa_core *c, pa_source_output *i, void *userdata) { + pa_source *target; + + pa_assert(c); + pa_assert(i); + + /* 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 (!(target = find_evacuation_source(c, i, NULL))) + return PA_HOOK_OK; + + if (pa_source_output_finish_move(i, target, FALSE) < 0) { + pa_log_info("Failed to move source input %u \"%s\" to %s.", i->index, + pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name); + return PA_HOOK_OK; + + } else { + pa_log_info("Sucessfully moved source input %u \"%s\" to %s.", i->index, + pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name); + return PA_HOOK_STOP; + } +} + int pa__init(pa_module*m) { pa_modargs *ma; struct userdata *u; @@ -148,8 +239,11 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew(struct userdata, 1); /* 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); + u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_unlink_hook_callback, u); + u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_unlink_hook_callback, u); + + u->sink_input_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_input_move_fail_hook_callback, u); + u->source_output_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) source_output_move_fail_hook_callback, u); pa_modargs_free(ma); return 0; @@ -163,10 +257,15 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->sink_slot) - pa_hook_slot_free(u->sink_slot); - if (u->source_slot) - pa_hook_slot_free(u->source_slot); + if (u->sink_unlink_slot) + pa_hook_slot_free(u->sink_unlink_slot); + if (u->source_unlink_slot) + pa_hook_slot_free(u->source_unlink_slot); + + if (u->sink_input_move_fail_slot) + pa_hook_slot_free(u->sink_input_move_fail_slot); + if (u->source_output_move_fail_slot) + pa_hook_slot_free(u->source_output_move_fail_slot); pa_xfree(u); } -- cgit From 58d441f7ea7994d5a0e8bc5397e2986707eb466b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:16:25 +0200 Subject: log: place more rate limit invocations --- src/modules/alsa/alsa-sink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 1c38430f..a91b4b8a 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1292,7 +1292,8 @@ static void thread_func(void *userdata) { * we have filled the buffer at least once * completely.*/ - pa_log_debug("Cutting sleep time for the initial iterations by half."); + if (pa_log_ratelimit()) + pa_log_debug("Cutting sleep time for the initial iterations by half."); sleep_usec /= 2; } -- cgit From 4eb59fb90e474a81f2d626bc4fc7db083fafed7a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:26:00 +0200 Subject: core: move rtpoll to thread_info sub structure --- src/modules/module-combine.c | 4 ++-- src/modules/rtp/module-rtp-recv.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 16de6890..92716552 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -455,12 +455,12 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_assert(!o->inq_rtpoll_item_read && !o->outq_rtpoll_item_write); o->inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( - i->sink->rtpoll, + i->sink->thread_info.rtpoll, PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */ o->inq); o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( - i->sink->rtpoll, + i->sink->thread_info.rtpoll, PA_RTPOLL_EARLY, o->outq); } diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index 5caf8272..c195c045 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -361,7 +361,7 @@ static void sink_input_attach(pa_sink_input *i) { pa_assert_se(s = i->userdata); pa_assert(!s->rtpoll_item); - s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1); + s->rtpoll_item = pa_rtpoll_item_new(i->sink->thread_info.rtpoll, PA_RTPOLL_LATE, 1); p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL); p->fd = s->rtp_context.fd; -- cgit From 350a2bc846559bb274ba70f928bb42a9472050bf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:48:14 +0200 Subject: core: make fixed latency dynamically changeable This of course makes the name 'fixed' a bit of a misnomer. However the definitions are now like this: fixed latency: the latency may change during runtime, but is solely controlled by the backend, the client has no influence. dynamic latency: the latency may change during runtime, influenced by the requests of the clients. i.e. fixed vs. dynamic is from the perspective of the client. --- src/modules/bluetooth/module-bluetooth-device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index e682997f..93b14a12 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -881,7 +881,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse *((pa_usec_t*) data) = wi > ri ? wi - ri : 0; } - *((pa_usec_t*) data) += u->sink->fixed_latency; + *((pa_usec_t*) data) += u->sink->thread_info.fixed_latency; return 0; } } @@ -943,7 +943,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off 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; + *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->thread_info.fixed_latency; return 0; } -- cgit From fb5205daac937e98736db1448fe7c8d84f3e78c4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:57:36 +0200 Subject: remap: unify argument order with other modules --- src/modules/module-remap-sink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 0b7b9b8f..45f4e2af 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -1,7 +1,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2008 Lennart Poettering + Copyright 2004-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 @@ -48,8 +48,8 @@ PA_MODULE_USAGE( "master= " "master_channel_map= " "format= " - "channels= " "rate= " + "channels= " "channel_map= " "remix="); -- cgit From c44f518eb9d36fd73ada6d49d51bbb6de389e7b7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:58:19 +0200 Subject: ladspa: move LADSPA_Data size check to compile time --- src/modules/module-ladspa-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index b26330c8..24334995 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -395,7 +395,7 @@ int pa__init(pa_module*m) { pa_assert(m); - pa_assert(sizeof(LADSPA_Data) == sizeof(float)); + pa_assert_cc(sizeof(LADSPA_Data) == sizeof(float)); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); -- cgit From 1b3848ebd768632f8ca8baedeb53feaf381847c4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:59:26 +0200 Subject: module-remap: allow moving of sink, forward fixed latency This is a bigger change reworking a number of things: - We now allow moving of the remap sink betwween backend sinks like any other stream. - We forward the fixed latency parameter of the underlying sinks the same way as the dynamic latency. --- src/modules/module-remap-sink.c | 134 +++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 58 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 45f4e2af..7b4c9bbb 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -54,10 +54,9 @@ PA_MODULE_USAGE( "remix="); struct userdata { - pa_core *core; pa_module *module; - pa_sink *sink, *master; + pa_sink *sink; pa_sink_input *sink_input; }; @@ -80,19 +79,24 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch (code) { - case PA_SINK_MESSAGE_GET_LATENCY: { - pa_usec_t usec = 0; + case PA_SINK_MESSAGE_GET_LATENCY: - /* Get the latency of the master sink */ - if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) - usec = 0; + /* The sink is _put() before the sink input is, so let's + * make sure we don't access it yet */ + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { + *((pa_usec_t*) data) = 0; + return 0; + } - /* Add the latency internal to our sink input on top */ - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); + *((pa_usec_t*) data) = + /* Get the latency of the master sink */ + pa_sink_get_latency_within_thread(u->sink_input->sink) + + + /* Add the latency internal to our sink input on top */ + pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); - *((pa_usec_t*) data) = usec; return 0; - } } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -105,12 +109,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - if (PA_SINK_IS_LINKED(state) && - u->sink_input && - PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) - - pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + if (!PA_SINK_IS_LINKED(state) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return 0; + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); return 0; } @@ -121,6 +124,10 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE); } @@ -131,6 +138,10 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; + /* Just hand this one over to the master sink */ pa_sink_input_set_requested_latency_within_thread( u->sink_input, @@ -145,9 +156,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return -1; - /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); @@ -163,9 +171,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return; - if (u->sink->thread_info.rewind_nbytes > 0) { amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes); u->sink->thread_info.rewind_nbytes = 0; @@ -181,9 +186,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -194,9 +196,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_sink_set_max_request_within_thread(u->sink, nbytes); } @@ -207,24 +206,28 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ -static void sink_input_detach_cb(pa_sink_input *i) { +static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_sink_detach_within_thread(u->sink); - pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); } @@ -235,14 +238,13 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll); + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); + pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i)); + pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i)); - pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); - pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -252,14 +254,18 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_unlink(u->sink); + /* The order here matters! We first kill the sink input, followed + * by the sink. That means the sink callbacks must be protected + * against an unconnected sink input! */ pa_sink_input_unlink(u->sink_input); + pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - u->sink = NULL; pa_sink_input_unref(u->sink_input); u->sink_input = NULL; + pa_sink_unref(u->sink); + u->sink = NULL; + pa_module_unload_request(u->module, TRUE); } @@ -289,6 +295,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { return u->sink != dest; } +/* Called from main context */ +static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); +} + int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -339,12 +355,8 @@ int pa__init(pa_module*m) { } u = pa_xnew0(struct userdata, 1); - u->core = m->core; u->module = m; m->userdata = u; - u->master = master; - u->sink = NULL; - u->sink_input = NULL; /* Create sink */ pa_sink_new_data_init(&sink_data); @@ -365,7 +377,7 @@ int pa__init(pa_module*m) { goto fail; } - u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); + u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -380,19 +392,18 @@ int pa__init(pa_module*m) { u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); - pa_sink_set_rtpoll(u->sink, master->rtpoll); /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; - sink_input_data.sink = u->master; + sink_input_data.sink = master; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX)); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, (remix ? 0 : PA_SINK_INPUT_NO_REMIX)); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) @@ -403,11 +414,13 @@ int pa__init(pa_module*m) { u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; + u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; + u->sink_input->moving = sink_input_moving_cb; u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -443,15 +456,20 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->sink) { - pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - } + /* See comments in sink_input_kill_cb() above regarding + * destruction order! */ - if (u->sink_input) { + if (u->sink_input) pa_sink_input_unlink(u->sink_input); + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->sink_input) pa_sink_input_unref(u->sink_input); - } + + if (u->sink) + pa_sink_unref(u->sink); pa_xfree(u); } -- cgit From 763866280adf3bd50463b0e316af7a7c4fa5aaf9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 01:01:52 +0200 Subject: module-ladspa: allow moving of sink, forward fixed latency --- src/modules/module-ladspa-sink.c | 136 ++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 58 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 24334995..e838be3e 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -64,10 +64,9 @@ PA_MODULE_USAGE( #define MEMBLOCKQ_MAXLENGTH (16*1024*1024) struct userdata { - pa_core *core; pa_module *module; - pa_sink *sink, *master; + pa_sink *sink; pa_sink_input *sink_input; const LADSPA_Descriptor *descriptor; @@ -105,19 +104,26 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch (code) { - case PA_SINK_MESSAGE_GET_LATENCY: { - pa_usec_t usec = 0; + case PA_SINK_MESSAGE_GET_LATENCY: - /* Get the latency of the master sink */ - if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) - usec = 0; + /* The sink is _put() before the sink input is, so let's + * make sure we don't access it in that time. Also, the + * sink input is first shut down, the sink second. */ + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { + *((pa_usec_t*) data) = 0; + return 0; + } + + *((pa_usec_t*) data) = - /* Add the latency internal to our sink input on top */ - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); + /* Get the latency of the master sink */ + pa_sink_get_latency_within_thread(u->sink_input->sink) + + + /* Add the latency internal to our sink input on top */ + pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); - *((pa_usec_t*) data) = usec; return 0; - } } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -130,12 +136,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - if (PA_SINK_IS_LINKED(state) && - u->sink_input && - PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) - - pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + if (!PA_SINK_IS_LINKED(state) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return 0; + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); return 0; } @@ -146,6 +151,10 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; + /* Just hand this one over to the master sink */ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE); } @@ -157,6 +166,10 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; + /* Just hand this one over to the master sink */ pa_sink_input_set_requested_latency_within_thread( u->sink_input, @@ -175,9 +188,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return -1; - /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); @@ -228,9 +238,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return; - if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; @@ -266,9 +273,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_memblockq_set_maxrewind(u->memblockq, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -280,9 +284,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_sink_set_max_request_within_thread(u->sink, nbytes); } @@ -293,24 +294,28 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ -static void sink_input_detach_cb(pa_sink_input *i) { +static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_sink_detach_within_thread(u->sink); - pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); } @@ -321,14 +326,13 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll); + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); + pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i)); + pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i)); - pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); - pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -338,14 +342,18 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_unlink(u->sink); + /* The order here matters! We first kill the sink input, followed + * by the sink. That means the sink callbacks must be protected + * against an unconnected sink input! */ pa_sink_input_unlink(u->sink_input); + pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - u->sink = NULL; pa_sink_input_unref(u->sink_input); u->sink_input = NULL; + pa_sink_unref(u->sink); + u->sink = NULL; + pa_module_unload_request(u->module, TRUE); } @@ -375,6 +383,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { return u->sink != dest; } +/* Called from main context */ +static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); +} + int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -428,12 +446,8 @@ int pa__init(pa_module*m) { cdata = pa_modargs_get_value(ma, "control", NULL); u = pa_xnew0(struct userdata, 1); - u->core = m->core; u->module = m; m->userdata = u; - u->master = master; - u->sink = NULL; - u->sink_input = NULL; u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL); if (!(e = getenv("LADSPA_PATH"))) @@ -717,7 +731,7 @@ int pa__init(pa_module*m) { goto fail; } - u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); + u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -732,19 +746,18 @@ int pa__init(pa_module*m) { u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); - pa_sink_set_rtpoll(u->sink, master->rtpoll); /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; - sink_input_data.sink = u->master; + sink_input_data.sink = master; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) @@ -755,11 +768,13 @@ int pa__init(pa_module*m) { u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; + u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; + u->sink_input->moving = sink_input_moving_cb; u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -800,15 +815,20 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->sink) { - pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - } + /* See comments in sink_input_kill_cb() above regarding + * destruction order! */ - if (u->sink_input) { + if (u->sink_input) pa_sink_input_unlink(u->sink_input); + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->sink_input) pa_sink_input_unref(u->sink_input); - } + + if (u->sink) + pa_sink_unref(u->sink); for (c = 0; c < u->channels; c++) if (u->handle[c]) { -- cgit From a5b2dee03c08b72f4b7d27d9c7ac304d98e0513c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 01:02:16 +0200 Subject: ladspa: name sink after human readable plugin name, not the id string --- src/modules/module-ladspa-sink.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index e838be3e..3c6e3495 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -711,11 +711,10 @@ int pa__init(pa_module*m) { sink_data.module = m; if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name); - sink_data.namereg_fail = FALSE; pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &map); z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", d->Name, z ? z : master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin); -- cgit From 8947d6551586d239be206f90adca2f6dace667a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 01:04:21 +0200 Subject: combine: drop adjust_timestamp variable because it is unused --- src/modules/module-combine.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 92716552..325b8988 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -125,8 +125,6 @@ struct userdata { pa_resample_method_t resample_method; - struct timeval adjust_timestamp; - pa_usec_t block_usec; pa_idxset* outputs; /* managed in main context */ @@ -833,14 +831,11 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { pa_assert(sink); pa_assert(u->sink); - o = pa_xnew(struct output, 1); + o = pa_xnew0(struct output, 1); o->userdata = u; o->inq = pa_asyncmsgq_new(0); o->outq = pa_asyncmsgq_new(0); - o->inq_rtpoll_item_write = o->inq_rtpoll_item_read = NULL; - o->outq_rtpoll_item_write = o->outq_rtpoll_item_read = NULL; o->sink = sink; - o->sink_input = NULL; o->memblockq = pa_memblockq_new( 0, MEMBLOCKQ_MAXLENGTH, @@ -1029,18 +1024,14 @@ int pa__init(pa_module*m) { } } - m->userdata = u = pa_xnew(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - u->sink = NULL; - u->time_event = NULL; u->adjust_time = DEFAULT_ADJUST_TIME; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - u->thread = NULL; u->resample_method = resample_method; u->outputs = pa_idxset_new(NULL, NULL); - memset(&u->adjust_timestamp, 0, sizeof(u->adjust_timestamp)); u->sink_put_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL; PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs); pa_atomic_store(&u->thread_info.running, FALSE); @@ -1095,7 +1086,6 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output"); } - u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); pa_sink_new_data_done(&data); -- cgit From e1f3f5e0bf3d788ff69d24cd40c465eaaf6e9385 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 01:07:37 +0200 Subject: combine: big rework --- src/modules/module-combine.c | 471 ++++++++++++++++++++++++------------------- 1 file changed, 266 insertions(+), 205 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 325b8988..04c0d4db 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -92,6 +92,8 @@ struct output { pa_sink *sink; pa_sink_input *sink_input; + pa_bool_t ignore_state_change; + pa_asyncmsgq *inq, /* Message queue from the sink thread to this sink input */ *outq; /* Message queue from this sink input to the sink thread */ pa_rtpoll_item *inq_rtpoll_item_read, *inq_rtpoll_item_write; @@ -99,9 +101,12 @@ struct output { pa_memblockq *memblockq; + /* For communication of the stream latencies to the main thread */ pa_usec_t total_latency; + /* For coomunication of the stream parameters to the sink thread */ pa_atomic_t max_request; + pa_atomic_t requested_latency; PA_LLIST_FIELDS(struct output); }; @@ -144,13 +149,16 @@ enum { SINK_MESSAGE_REMOVE_OUTPUT, SINK_MESSAGE_NEED, SINK_MESSAGE_UPDATE_LATENCY, - SINK_MESSAGE_UPDATE_MAX_REQUEST + SINK_MESSAGE_UPDATE_MAX_REQUEST, + SINK_MESSAGE_UPDATE_REQUESTED_LATENCY }; enum { SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX, }; +static void output_disable(struct output *o); +static void output_enable(struct output *o); static void output_free(struct output *o); static int output_create_sink_input(struct output *o); @@ -170,7 +178,7 @@ static void adjust_rates(struct userdata *u) { if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink))) return; - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { + PA_IDXSET_FOREACH(o, u->outputs, idx) { pa_usec_t sink_latency; if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) @@ -187,6 +195,8 @@ static void adjust_rates(struct userdata *u) { avg_total_latency += o->total_latency; n++; + + pa_log_debug("[%s] total=%0.2fms sink=%0.2fms ", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC, (double) sink_latency / PA_USEC_PER_MSEC); } if (min_total_latency == (pa_usec_t) -1) @@ -201,7 +211,7 @@ static void adjust_rates(struct userdata *u) { base_rate = u->sink->sample_spec.rate; - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { + PA_IDXSET_FOREACH(o, u->outputs, idx) { uint32_t r = base_rate; if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) @@ -213,10 +223,10 @@ static void adjust_rates(struct userdata *u) { r += (uint32_t) ((((double) (o->total_latency - target_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC); if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) { - pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r); + pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->sink->name, base_rate, r); pa_sink_input_set_rate(o->sink_input, base_rate); } else { - pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), r, (double) r / base_rate, (float) o->total_latency); + pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", o->sink_input->sink->name, r, (double) r / base_rate, (float) o->total_latency); pa_sink_input_set_rate(o->sink_input, r); } } @@ -353,18 +363,15 @@ static void render_memblock(struct userdata *u, struct output *o, size_t length) u->thread_info.counter += chunk.length; /* OK, let's send this data to the other threads */ - for (j = u->thread_info.active_outputs; j; j = j->next) - - /* Send to other outputs, which are not the requesting - * one */ + PA_LLIST_FOREACH(j, u->thread_info.active_outputs) { + if (j == o) + continue; - if (j != o) - pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL); + pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL); + } /* And place it directly into the requesting output's queue */ - if (o) - pa_memblockq_push_align(o->memblockq, &chunk); - + pa_memblockq_push_align(o->memblockq, &chunk); pa_memblock_unref(chunk.memblock); } } @@ -400,10 +407,18 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* If necessary, get some new data */ request_memblock(o, nbytes); + /* pa_log("%s q size is %u + %u (%u/%u)", */ + /* i->sink->name, */ + /* pa_memblockq_get_nblocks(o->memblockq), */ + /* pa_memblockq_get_nblocks(i->thread_info.render_memblockq), */ + /* pa_memblockq_get_maxrewind(o->memblockq), */ + /* pa_memblockq_get_maxrewind(i->thread_info.render_memblockq)); */ + if (pa_memblockq_peek(o->memblockq, chunk) < 0) return -1; pa_memblockq_drop(o->memblockq, chunk->length); + return 0; } @@ -438,13 +453,35 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { return; pa_atomic_store(&o->max_request, (int) nbytes); - pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL); } +/* Called from thread context */ +static void sink_input_update_sink_requested_latency_cb(pa_sink_input *i) { + struct output *o; + pa_usec_t c; + + pa_assert(i); + + pa_sink_input_assert_ref(i); + pa_assert_se(o = i->userdata); + + c = pa_sink_get_requested_latency_within_thread(i->sink); + + if (c == (pa_usec_t) -1) + c = i->sink->thread_info.max_latency; + + if (pa_atomic_load(&o->requested_latency) == (int) c) + return; + + pa_atomic_store(&o->requested_latency, (int) c); + pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_REQUESTED_LATENCY, NULL, 0, NULL, NULL); +} + /* Called from I/O thread context */ static void sink_input_attach_cb(pa_sink_input *i) { struct output *o; + pa_usec_t c; pa_sink_input_assert_ref(i); pa_assert_se(o = i->userdata); @@ -461,6 +498,16 @@ static void sink_input_attach_cb(pa_sink_input *i) { i->sink->thread_info.rtpoll, PA_RTPOLL_EARLY, o->outq); + + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); + + pa_atomic_store(&o->max_request, (int) pa_sink_input_get_max_request(i)); + + c = pa_sink_get_requested_latency_within_thread(i->sink); + pa_atomic_store(&o->requested_latency, (int) (c == (pa_usec_t) -1 ? 0 : c)); + + pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL); + pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_REQUESTED_LATENCY, NULL, 0, NULL, NULL); } /* Called from I/O thread context */ @@ -470,14 +517,15 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(o = i->userdata); - /* Shut down the queue from the sink thread to us */ - pa_assert(o->inq_rtpoll_item_read && o->outq_rtpoll_item_write); - - pa_rtpoll_item_free(o->inq_rtpoll_item_read); - o->inq_rtpoll_item_read = NULL; + if (o->inq_rtpoll_item_read) { + pa_rtpoll_item_free(o->inq_rtpoll_item_read); + o->inq_rtpoll_item_read = NULL; + } - pa_rtpoll_item_free(o->outq_rtpoll_item_write); - o->outq_rtpoll_item_write = NULL; + if (o->outq_rtpoll_item_write) { + pa_rtpoll_item_free(o->outq_rtpoll_item_write); + o->outq_rtpoll_item_write = NULL; + } } /* Called from main context */ @@ -491,20 +539,6 @@ static void sink_input_kill_cb(pa_sink_input *i) { output_free(o); } -/* Called from IO thread context */ -static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { - struct userdata *u; - - pa_sink_input_assert_ref(i); - pa_assert_se(u = i->userdata); - - /* If we are added for the first time, ask for a rewinding so that - * we are heard right-away. */ - if (PA_SINK_INPUT_IS_LINKED(state) && - i->thread_info.state == PA_SINK_INPUT_INIT) - pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); -} - /* Called from thread context */ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct output *o = PA_SINK_INPUT(obj)->userdata; @@ -534,37 +568,6 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64 return pa_sink_input_process_msg(obj, code, data, offset, chunk); } -/* Called from main context */ -static void disable_output(struct output *o) { - pa_assert(o); - - if (!o->sink_input) - return; - - pa_sink_input_unlink(o->sink_input); - pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); - pa_sink_input_unref(o->sink_input); - o->sink_input = NULL; -} - -/* Called from main context */ -static void enable_output(struct output *o) { - pa_assert(o); - - if (o->sink_input) - return; - - if (output_create_sink_input(o) >= 0) { - - pa_memblockq_flush_write(o->memblockq); - - pa_sink_input_put(o->sink_input); - - if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink))) - pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); - } -} - /* Called from main context */ static void suspend(struct userdata *u) { struct output *o; @@ -573,8 +576,8 @@ static void suspend(struct userdata *u) { pa_assert(u); /* Let's suspend by unlinking all streams */ - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) - disable_output(o); + PA_IDXSET_FOREACH(o, u->outputs, idx) + output_disable(o); pa_log_info("Device suspended..."); } @@ -587,13 +590,8 @@ static void unsuspend(struct userdata *u) { pa_assert(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_SUSPEND_IDLE); - - if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) - enable_output(o); - } + PA_IDXSET_FOREACH(o, u->outputs, idx) + output_enable(o); pa_log_info("Resumed successfully..."); } @@ -637,7 +635,13 @@ static void update_max_request(struct userdata *u) { size_t max_request = 0; struct output *o; - for (o = u->thread_info.active_outputs; o; o = o->next) { + pa_assert(u); + pa_sink_assert_io_context(u->sink); + + /* Collects the max_request values of all streams and sets the + * largest one locally */ + + PA_LLIST_FOREACH(o, u->thread_info.active_outputs) { size_t mr = (size_t) pa_atomic_load(&o->max_request); if (mr > max_request) @@ -650,6 +654,67 @@ static void update_max_request(struct userdata *u) { pa_sink_set_max_request_within_thread(u->sink, max_request); } +/* Called from IO context */ +static void update_fixed_latency(struct userdata *u) { + pa_usec_t fixed_latency = 0; + struct output *o; + + pa_assert(u); + pa_sink_assert_io_context(u->sink); + + /* Collects the requested_latency values of all streams and sets + * the largest one as fixed_latency locally */ + + PA_LLIST_FOREACH(o, u->thread_info.active_outputs) { + pa_usec_t rl = (size_t) pa_atomic_load(&o->requested_latency); + + if (rl > fixed_latency) + fixed_latency = rl; + } + + if (fixed_latency <= 0) + fixed_latency = u->block_usec; + + pa_sink_set_fixed_latency_within_thread(u->sink, fixed_latency); +} + +/* Called from thread context of the io thread */ +static void output_add_within_thread(struct output *o) { + pa_assert(o); + pa_sink_assert_io_context(o->sink); + + PA_LLIST_PREPEND(struct output, o->userdata->thread_info.active_outputs, o); + + pa_assert(!o->outq_rtpoll_item_read && !o->inq_rtpoll_item_write); + + o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( + o->userdata->rtpoll, + PA_RTPOLL_EARLY-1, /* This item is very important */ + o->outq); + o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( + o->userdata->rtpoll, + PA_RTPOLL_EARLY, + o->inq); +} + +/* Called from thread context of the io thread */ +static void output_remove_within_thread(struct output *o) { + pa_assert(o); + pa_sink_assert_io_context(o->sink); + + PA_LLIST_REMOVE(struct output, o->userdata->thread_info.active_outputs, o); + + if (o->outq_rtpoll_item_read) { + pa_rtpoll_item_free(o->outq_rtpoll_item_read); + o->outq_rtpoll_item_read = NULL; + } + + if (o->inq_rtpoll_item_write) { + pa_rtpoll_item_free(o->inq_rtpoll_item_write); + o->inq_rtpoll_item_write = NULL; + } +} + /* Called from thread context of the io thread */ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; @@ -682,42 +747,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse return 0; } - case SINK_MESSAGE_ADD_OUTPUT: { - struct output *op = data; - - PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, op); - - pa_assert(!op->outq_rtpoll_item_read && !op->inq_rtpoll_item_write); - - op->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( - u->rtpoll, - PA_RTPOLL_EARLY-1, /* This item is very important */ - op->outq); - op->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( - u->rtpoll, - PA_RTPOLL_EARLY, - op->inq); - + case SINK_MESSAGE_ADD_OUTPUT: + output_add_within_thread(data); update_max_request(u); + update_fixed_latency(u); return 0; - } - - case SINK_MESSAGE_REMOVE_OUTPUT: { - struct output *op = data; - - PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op); - - pa_assert(op->outq_rtpoll_item_read && op->inq_rtpoll_item_write); - - pa_rtpoll_item_free(op->outq_rtpoll_item_read); - op->outq_rtpoll_item_read = NULL; - - pa_rtpoll_item_free(op->inq_rtpoll_item_write); - op->inq_rtpoll_item_write = NULL; + case SINK_MESSAGE_REMOVE_OUTPUT: + output_remove_within_thread(data); update_max_request(u); + update_fixed_latency(u); return 0; - } case SINK_MESSAGE_NEED: render_memblock(u, (struct output*) data, (size_t) offset); @@ -739,10 +779,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse } case SINK_MESSAGE_UPDATE_MAX_REQUEST: - update_max_request(u); break; - } + + case SINK_MESSAGE_UPDATE_REQUESTED_LATENCY: + update_fixed_latency(u); + break; +} return pa_sink_process_msg(o, code, data, offset, chunk); } @@ -765,7 +808,7 @@ static void update_description(struct userdata *u) { t = pa_xstrdup("Simultaneous output to"); - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { + PA_IDXSET_FOREACH(o, u->outputs, idx) { char *e; if (first) { @@ -800,7 +843,7 @@ static int output_create_sink_input(struct output *o) { data.module = o->userdata->module; data.resample_method = o->userdata->resample_method; - pa_sink_input_new(&o->sink_input, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new(&o->sink_input, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE|PA_SINK_INPUT_NO_CREATE_ON_SUSPEND); pa_sink_input_new_data_done(&data); @@ -810,9 +853,9 @@ static int output_create_sink_input(struct output *o) { o->sink_input->parent.process_msg = sink_input_process_msg; o->sink_input->pop = sink_input_pop_cb; o->sink_input->process_rewind = sink_input_process_rewind_cb; - o->sink_input->state_change = sink_input_state_change_cb; o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; o->sink_input->update_max_request = sink_input_update_max_request_cb; + o->sink_input->update_sink_requested_latency = sink_input_update_sink_requested_latency_cb; o->sink_input->attach = sink_input_attach_cb; o->sink_input->detach = sink_input_detach_cb; o->sink_input->kill = sink_input_kill_cb; @@ -823,9 +866,9 @@ static int output_create_sink_input(struct output *o) { return 0; } +/* Called from main context */ static struct output *output_new(struct userdata *u, pa_sink *sink) { struct output *o; - pa_sink_state_t state; pa_assert(u); pa_assert(sink); @@ -845,84 +888,135 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { 0, 0, NULL); - pa_atomic_store(&o->max_request, 0); - PA_LLIST_INIT(struct output, o); pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0); + update_description(u); - state = pa_sink_get_state(u->sink); - - if (state != PA_SINK_INIT) - pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); - else { - /* If the sink is not yet started, we need to do the activation ourselves */ - PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o); - - o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( - u->rtpoll, - PA_RTPOLL_EARLY-1, /* This item is very important */ - o->outq); - o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( - u->rtpoll, - PA_RTPOLL_EARLY, - o->inq); - } + return o; +} - if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) { - pa_sink_suspend(sink, FALSE, PA_SUSPEND_IDLE); +/* Called from main context */ +static void output_free(struct output *o) { + pa_assert(o); - if (PA_SINK_IS_OPENED(pa_sink_get_state(sink))) - if (output_create_sink_input(o) < 0) - goto fail; - } + output_disable(o); - update_description(u); + pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL)); + update_description(o->userdata); - return o; + if (o->inq_rtpoll_item_read) + pa_rtpoll_item_free(o->inq_rtpoll_item_read); + if (o->inq_rtpoll_item_write) + pa_rtpoll_item_free(o->inq_rtpoll_item_write); -fail: + if (o->outq_rtpoll_item_read) + pa_rtpoll_item_free(o->outq_rtpoll_item_read); + if (o->outq_rtpoll_item_write) + pa_rtpoll_item_free(o->outq_rtpoll_item_write); - if (o) { - pa_idxset_remove_by_data(u->outputs, o, NULL); + if (o->inq) + pa_asyncmsgq_unref(o->inq); - if (o->sink_input) { - pa_sink_input_unlink(o->sink_input); - pa_sink_input_unref(o->sink_input); - } + if (o->outq) + pa_asyncmsgq_unref(o->outq); + + if (o->memblockq) + pa_memblockq_free(o->memblockq); - if (o->memblockq) - pa_memblockq_free(o->memblockq); + pa_xfree(o); +} - if (o->inq) - pa_asyncmsgq_unref(o->inq); +/* Called from main context */ +static void output_enable(struct output *o) { + pa_assert(o); + + if (o->sink_input) + return; + + /* This might cause the sink to be resumed. The state change hook + * of the sink might hence be called from here, which might then + * cause us to be called in a loop. Make sure that state changes + * for this output don't cause this loop by setting a flag here */ + o->ignore_state_change = TRUE; + + if (output_create_sink_input(o) >= 0) { - if (o->outq) - pa_asyncmsgq_unref(o->outq); + if (pa_sink_get_state(o->sink) != PA_SINK_INIT) { - pa_xfree(o); + /* First we register the output. That means that the sink + * will start to pass data to this output. */ + pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); + + /* Then we enable the sink input. That means that the sink + * is now asked for new data. */ + pa_sink_input_put(o->sink_input); + + } else + /* Hmm the sink is not yet started, do things right here */ + output_add_within_thread(o); } - return NULL; + o->ignore_state_change = FALSE; } +/* Called from main context */ +static void output_disable(struct output *o) { + pa_assert(o); + + if (!o->sink_input) + return; + + /* First we disable the sink input. That means that the sink is + * not asked for new data anymore */ + pa_sink_input_unlink(o->sink_input); + + /* Then we unregister the output. That means that the sink doesn't + * pass any further data to this output */ + pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); + + /* Now dellocate the stream */ + pa_sink_input_unref(o->sink_input); + o->sink_input = NULL; + + /* Finally, drop all queued data */ + pa_memblockq_flush_write(o->memblockq); + pa_asyncmsgq_flush(o->inq, FALSE); + pa_asyncmsgq_flush(o->outq, FALSE); +} + +/* Called from main context */ +static void output_verify(struct output *o) { + pa_assert(o); + + if (PA_SINK_IS_OPENED(pa_sink_get_state(o->userdata->sink))) + output_enable(o); + else + output_disable(o); +} + +/* Called from main context */ static pa_bool_t is_suitable_sink(struct userdata *u, pa_sink *s) { const char *t; pa_sink_assert_ref(s); + if (s == u->sink) + return FALSE; + if (!(s->flags & PA_SINK_HARDWARE)) return FALSE; - if (s == u->sink) + if (!(s->flags & PA_SINK_LATENCY)) return FALSE; if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS))) - if (strcmp(t, "sound")) + if (!pa_streq(t, "sound")) return FALSE; return TRUE; } +/* Called from main context */ static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { struct output *o; @@ -935,18 +1029,17 @@ static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata return PA_HOOK_OK; pa_log_info("Configuring new sink: %s", s->name); - if (!(o = output_new(u, s))) { pa_log("Failed to create sink input on sink '%s'.", s->name); return PA_HOOK_OK; } - if (o->sink_input) - pa_sink_input_put(o->sink_input); + output_verify(o); return PA_HOOK_OK; } +/* Called from main context */ static struct output* find_output(struct userdata *u, pa_sink *s) { struct output *o; uint32_t idx; @@ -957,13 +1050,14 @@ static struct output* find_output(struct userdata *u, pa_sink *s) { if (u->sink == s) return NULL; - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) + PA_IDXSET_FOREACH(o, u->outputs, idx) if (o->sink == s) return o; return NULL; } +/* Called from main context */ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { struct output *o; @@ -975,26 +1069,25 @@ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userd return PA_HOOK_OK; pa_log_info("Unconfiguring sink: %s", s->name); - output_free(o); return PA_HOOK_OK; } +/* Called from main context */ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { struct output *o; - pa_sink_state_t state; if (!(o = find_output(u, s))) return PA_HOOK_OK; - state = pa_sink_get_state(s); - - if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) - enable_output(o); + /* This state change might be triggered because we are creating a + * stream here, in that case we don't want to create it a second + * time here and enter a loop */ + if (o->ignore_state_change) + return PA_HOOK_OK; - if (state == PA_SINK_SUSPENDED && o->sink_input) - disable_output(o); + output_verify(o); return PA_HOOK_OK; } @@ -1139,7 +1232,7 @@ int pa__init(pa_module*m) { /* We're in automatic mode, we add every sink that matches our needs */ - for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) { + PA_IDXSET_FOREACH(s, m->core->sinks, idx) { if (!is_suitable_sink(u, s)) continue; @@ -1164,9 +1257,8 @@ int pa__init(pa_module*m) { /* Activate the sink and the sink inputs */ pa_sink_put(u->sink); - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) - if (o->sink_input) - pa_sink_input_put(o->sink_input); + PA_IDXSET_FOREACH(o, u->outputs, idx) + output_verify(o); 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); @@ -1185,37 +1277,6 @@ fail: return -1; } -static void output_free(struct output *o) { - pa_assert(o); - - disable_output(o); - - pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL)); - - update_description(o->userdata); - - if (o->inq_rtpoll_item_read) - pa_rtpoll_item_free(o->inq_rtpoll_item_read); - if (o->inq_rtpoll_item_write) - pa_rtpoll_item_free(o->inq_rtpoll_item_write); - - if (o->outq_rtpoll_item_read) - pa_rtpoll_item_free(o->outq_rtpoll_item_read); - if (o->outq_rtpoll_item_write) - pa_rtpoll_item_free(o->outq_rtpoll_item_write); - - if (o->inq) - pa_asyncmsgq_unref(o->inq); - - if (o->outq) - pa_asyncmsgq_unref(o->outq); - - if (o->memblockq) - pa_memblockq_free(o->memblockq); - - pa_xfree(o); -} - void pa__done(pa_module*m) { struct userdata *u; struct output *o; -- cgit From 1eeddd84d2ff2482dd4a6d2dd43dc8a315ba72a4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 01:16:57 +0200 Subject: combine: warn when the latency of a stream gets too high --- src/modules/module-combine.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 04c0d4db..155b928a 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -197,6 +197,9 @@ static void adjust_rates(struct userdata *u) { n++; pa_log_debug("[%s] total=%0.2fms sink=%0.2fms ", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC, (double) sink_latency / PA_USEC_PER_MSEC); + + if (o->total_latency > 10*PA_USEC_PER_SEC) + pa_log_warn("[%s] Total latency of output is very high (%0.2fms), most likely the audio timing in one of your drivers is broken.", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC); } if (min_total_latency == (pa_usec_t) -1) -- cgit From 61105df13b423b100394639cf7850856efbad767 Mon Sep 17 00:00:00 2001 From: Maarten Bosmans Date: Sat, 15 Aug 2009 01:35:53 +0200 Subject: combine: determine sample parameters of combined sink from underlying sinks http://pulseaudio.org/ticket/521 --- src/modules/module-combine.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 155b928a..582cbce1 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -1152,6 +1152,55 @@ int pa__init(pa_module*m) { ss = m->core->default_sample_spec; map = m->core->default_channel_map; + + /* Check the specified slave sinks for sample_spec and channel_map to use for the combined sink */ + if (!u->automatic) { + const char*split_state = NULL; + char *n = NULL; + pa_sample_spec slaves_spec; + pa_channel_map slaves_map; + pa_bool_t is_first_slave = TRUE; + + while ((n = pa_split(slaves, ",", &split_state))) { + pa_sink *slave_sink; + + if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK))) { + pa_log("Invalid slave sink '%s'", n); + pa_xfree(n); + goto fail; + } + + pa_xfree(n); + + if (is_first_slave) { + slaves_spec = slave_sink->sample_spec; + slaves_map = slave_sink->channel_map; + is_first_slave = FALSE; + } else { + if (slaves_spec.format != slave_sink->sample_spec.format) + slaves_spec.format = PA_SAMPLE_INVALID; + + if (slaves_spec.rate < slave_sink->sample_spec.rate) + slaves_spec.rate = slave_sink->sample_spec.rate; + + if (!pa_channel_map_equal(&slaves_map, &slave_sink->channel_map)) + slaves_spec.channels = 0; + } + } + + if (!is_first_slave) { + if (slaves_spec.format != PA_SAMPLE_INVALID) + ss.format = slaves_spec.format; + + ss.rate = slaves_spec.rate; + + if (slaves_spec.channels > 0) { + map = slaves_map; + ss.channels = slaves_map.channels; + } + } + } + if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) { pa_log("Invalid sample specification."); goto fail; -- cgit From fd32fee4488b1e06d0a64079485aa77f0c87164b Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 6 Jul 2009 17:50:51 +0300 Subject: bluetooth: don't call pa_sink_render with 0 bytes request --- src/modules/bluetooth/module-bluetooth-device.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 93b14a12..d9831854 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1319,18 +1319,21 @@ static void thread_func(void *userdata) { if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) { pa_usec_t skip_usec; uint64_t skip_bytes; - pa_memchunk tmp; skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC; skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec); - pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream", - (unsigned long long) skip_usec, - (unsigned long long) skip_bytes); + if (skip_bytes > 0) { + pa_memchunk tmp; - pa_sink_render_full(u->sink, skip_bytes, &tmp); - pa_memblock_unref(tmp.memblock); - u->write_index += skip_bytes; + pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream", + (unsigned long long) skip_usec, + (unsigned long long) skip_bytes); + + pa_sink_render_full(u->sink, skip_bytes, &tmp); + pa_memblock_unref(tmp.memblock); + u->write_index += skip_bytes; + } } do_write = 1; -- cgit From 3ecb80e19ab9aa678b7db6a5068553d956d707fa Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 20 Jul 2009 13:44:27 +0300 Subject: bluetooth: fix typo with service_{read,write}_type --- src/modules/bluetooth/module-bluetooth-device.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index d9831854..5f119c7a 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1739,7 +1739,8 @@ static void shutdown_bt(struct userdata *u) { if (u->service_fd >= 0) { pa_close(u->service_fd); u->service_fd = -1; - u->service_write_type = u->service_write_type = 0; + u->service_write_type = 0; + u->service_read_type = 0; } if (u->write_memchunk.memblock) { @@ -1755,7 +1756,8 @@ static int init_bt(struct userdata *u) { shutdown_bt(u); u->stream_write_type = 0; - u->service_write_type = u->service_write_type = 0; + u->service_write_type = 0; + u->service_read_type = 0; if ((u->service_fd = bt_audio_service_open()) < 0) { pa_log_error("Couldn't connect to bluetooth audio service"); -- cgit From e5b08a81d23d94c668fdfabc4c6c196e14640869 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 03:42:16 +0200 Subject: ladspa/remap: sync latency flags from master sink when moving between sinks --- src/modules/module-ladspa-sink.c | 1 + src/modules/module-remap-sink.c | 1 + 2 files changed, 2 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 3c6e3495..f2d53d00 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -391,6 +391,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_assert_se(u = i->userdata); pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); } int pa__init(pa_module*m) { diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 7b4c9bbb..0b4fdc9b 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -303,6 +303,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_assert_se(u = i->userdata); pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); } int pa__init(pa_module*m) { -- cgit From 5c90cf2d6a148d5450b4d05edfc98d32ae83a854 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 22:24:13 +0200 Subject: bluetooth: drop PA_BT_AUDIO_STATE_LAST since it is unused and we normally call that _MAX anyway --- src/modules/bluetooth/bluetooth-util.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h index 265caf40..f15f2170 100644 --- a/src/modules/bluetooth/bluetooth-util.h +++ b/src/modules/bluetooth/bluetooth-util.h @@ -59,8 +59,7 @@ typedef enum pa_bt_audio_state { PA_BT_AUDIO_STATE_DISCONNECTED, PA_BT_AUDIO_STATE_CONNECTING, PA_BT_AUDIO_STATE_CONNECTED, - PA_BT_AUDIO_STATE_PLAYING, - PA_BT_AUDIO_STATE_LAST + PA_BT_AUDIO_STATE_PLAYING } pa_bt_audio_state_t; struct pa_bluetooth_device { -- cgit From fa52a91b1a1d89e0a99faeea821d3e1a3597eb9a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 22:25:21 +0200 Subject: bluetooth: recognize only those BT devices that implement both the Audio and either AudioSink or Headset interfaces --- src/modules/bluetooth/bluetooth-util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index 66e1c31e..d5806b96 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -122,9 +122,9 @@ static pa_bool_t device_is_audio(pa_bluetooth_device *d) { return d->device_info_valid && - (d->audio_state != PA_BT_AUDIO_STATE_INVALID || - d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID || - d->headset_state != PA_BT_AUDIO_STATE_INVALID); + (d->audio_state != PA_BT_AUDIO_STATE_INVALID && + (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID || + d->headset_state != PA_BT_AUDIO_STATE_INVALID)); } static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) { -- cgit From de4968cdded8d78fe2e59b4487a21937b843c570 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 22:25:53 +0200 Subject: bluetooth: ask first for Headset and AudioSink properties, followed by Audio --- src/modules/bluetooth/bluetooth-util.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index d5806b96..16c29248 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -226,10 +226,6 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device node = uuid_new(value); PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node); - /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */ - pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties")); - send_and_add_to_pending(y, d, m, get_properties_reply); - /* Vudentz said the interfaces are here when the UUIDs are announced */ if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) { pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset", "GetProperties")); @@ -239,6 +235,10 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device send_and_add_to_pending(y, d, m, get_properties_reply); } + /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */ + pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties")); + send_and_add_to_pending(y, d, m, get_properties_reply); + if (!dbus_message_iter_next(&ai)) break; } -- cgit From 2a39663ab61614982c52e244bde596dcc1a08f37 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2009 00:43:22 +0200 Subject: bluetooth: move installation of mq's earlier to avoid context asserts to be triggered --- src/modules/bluetooth/module-bluetooth-device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 5f119c7a..d6321fc4 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1262,11 +1262,11 @@ static void thread_func(void *userdata) { if (u->core->realtime_scheduling) pa_make_realtime(u->core->realtime_priority); + pa_thread_mq_install(&u->thread_mq); + if (start_stream_fd(u) < 0) goto fail; - pa_thread_mq_install(&u->thread_mq); - for (;;) { struct pollfd *pollfd; int ret; -- cgit From 4c29ba9c332dd682ced5ed668aede16aa5861128 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2009 00:45:23 +0200 Subject: modules: add various checks to avoid selecting objects that are not linked or in another unclear state --- src/modules/module-intended-roles.c | 54 +++++++++++++++++++++++++++++++------ src/modules/module-rescue-streams.c | 6 +++++ src/modules/module-stream-restore.c | 18 +++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index c697209a..b9924dfd 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -127,6 +127,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n if (s == def) continue; + if (!PA_SINK_IS_LINKED(pa_sink_get_state(s))) + continue; + if (role_match(s->proplist, role)) { new_data->sink = s; new_data->save_sink = FALSE; @@ -173,6 +176,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou if (s == def) continue; + if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s))) + continue; + if (role_match(s->proplist, role)) { new_data->source = s; new_data->save_source = FALSE; @@ -201,6 +207,17 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct if (si->save_sink) continue; + /* Skip this if it is already in the process of being moved + * anyway */ + if (!si->sink) + continue; + + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) + continue; + if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) continue; @@ -237,6 +254,17 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, if (so->direct_on_input) continue; + /* Skip this if it is already in the process of being moved + * anyway */ + if (!so->source) + continue; + + /* It might happen that a stream and a source are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so))) + continue; + if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) continue; @@ -275,24 +303,28 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str uint32_t jdx; pa_sink *d; + if (!si->sink) + continue; + 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; - } + if (def != sink && role_match(def->proplist, role)) + if (pa_sink_input_move_to(si, def, FALSE) >= 0) + 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; - } + if (!PA_SINK_IS_LINKED(pa_sink_get_state(d))) + continue; + + if (role_match(d->proplist, role)) + if (pa_sink_input_move_to(si, d, FALSE) >= 0) + break; } } @@ -325,6 +357,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc if (so->direct_on_input) continue; + if (!so->source) + continue; + if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) continue; @@ -339,6 +374,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc if (d == def || d == source) continue; + if (!PA_SOURCE_IS_LINKED(pa_source_get_state(d))) + continue; + if (role_match(d->proplist, role) && !source->monitor_of == !d->monitor_of) { pa_source_output_move_to(so, d, FALSE); break; diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index 82f693f9..722d84b2 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -71,6 +71,9 @@ static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip if (target == skip) continue; + if (!PA_SINK_IS_LINKED(pa_sink_get_state(target))) + continue; + if (pa_sink_input_may_move_to(i, target)) return target; } @@ -159,6 +162,9 @@ static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_sou if (!target->monitor_of != !skip->monitor_of) continue; + if (!PA_SOURCE_IS_LINKED(pa_source_get_state(target))) + continue; + if (pa_source_output_may_move_to(o, target)) return target; } diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 727a5275..e560bd28 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -540,6 +540,11 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct if (si->save_sink) continue; + /* Skip this if it is already in the process of being moved + * anyway */ + if (!si->sink) + continue; + /* It might happen that a stream and a sink are set up at the same time, in which case we want to make sure we don't interfere with that */ @@ -584,6 +589,10 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, if (so->direct_on_input) continue; + /* Skip this if it is already in the process of being moved anyway */ + if (!so->source) + continue; + /* It might happen that a stream and a sink are set up at the same time, in which case we want to make sure we don't interfere with that */ @@ -623,6 +632,9 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str char *name; struct entry *e; + if (!si->sink) + continue; + if (!(name = get_name(si->proplist, "sink-input"))) continue; @@ -663,6 +675,12 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc char *name; struct entry *e; + if (so->direct_on_input) + continue; + + if (!so->source) + continue; + if (!(name = get_name(so->proplist, "source-output"))) continue; -- cgit From c579cb56e941e759b71b393eae1bc98d5deb8a1b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 17 Aug 2009 03:41:14 +0200 Subject: reserver: update from upstream git --- src/modules/reserve-monitor.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'src/modules') diff --git a/src/modules/reserve-monitor.c b/src/modules/reserve-monitor.c index 13ecde2b..97cb9b9e 100644 --- a/src/modules/reserve-monitor.c +++ b/src/modules/reserve-monitor.c @@ -38,6 +38,7 @@ struct rm_monitor { char *device_name; char *service_name; + char *match; DBusConnection *connection; @@ -51,6 +52,13 @@ struct rm_monitor { #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1." +#define SERVICE_FILTER \ + "type='signal'," \ + "sender='" DBUS_SERVICE_DBUS "'," \ + "interface='" DBUS_INTERFACE_DBUS "'," \ + "member='NameOwnerChanged'," \ + "arg0='%s'" + static DBusHandlerResult filter_handler( DBusConnection *c, DBusMessage *s, @@ -175,11 +183,13 @@ int rm_watch( m->filtering = 1; - dbus_bus_add_match(m->connection, - "type='signal'," - "sender='" DBUS_SERVICE_DBUS "'," - "interface='" DBUS_INTERFACE_DBUS "'," - "member='NameOwnerChanged'", error); + if (!(m->match = malloc(sizeof(SERVICE_FILTER) - 2 + strlen(m->service_name)))) { + r = -ENOMEM; + goto fail; + } + + sprintf(m->match, SERVICE_FILTER, m->service_name); + dbus_bus_add_match(m->connection, m->match, error); if (dbus_error_is_set(error)) { r = -EIO; @@ -220,10 +230,8 @@ void rm_release(rm_monitor *m) { if (m->matching) dbus_bus_remove_match( m->connection, - "type='signal'," - "sender='" DBUS_SERVICE_DBUS "'," - "interface='" DBUS_INTERFACE_DBUS "'," - "member='NameOwnerChanged'", NULL); + m->match, + NULL); if (m->filtering) dbus_connection_remove_filter( @@ -233,6 +241,7 @@ void rm_release(rm_monitor *m) { free(m->device_name); free(m->service_name); + free(m->match); if (m->connection) dbus_connection_unref(m->connection); -- cgit From 90a07435d3a1c5660ebb4bac571267a62187ea2e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 17 Aug 2009 03:51:17 +0200 Subject: bluetooth: make NameOwnerChanged filter matches more focussed --- src/modules/bluetooth/bluetooth-util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index 16c29248..b8eb5c9d 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -758,7 +758,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { if (pa_dbus_add_matches( pa_dbus_connection_get(y->connection), &err, - "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'", + "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg='org.bluez'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", @@ -809,7 +809,7 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { if (y->connection) { pa_dbus_remove_matches(pa_dbus_connection_get(y->connection), - "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'", + "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg='org.bluez'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", -- cgit From 319d187972f792568e37af92726b3f25e708cbbc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 17 Aug 2009 19:14:35 +0200 Subject: bluetooth: fix match syntax --- src/modules/bluetooth/bluetooth-util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index b8eb5c9d..f576823d 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -758,7 +758,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { if (pa_dbus_add_matches( pa_dbus_connection_get(y->connection), &err, - "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg='org.bluez'", + "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", @@ -809,7 +809,7 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { if (y->connection) { pa_dbus_remove_matches(pa_dbus_connection_get(y->connection), - "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg='org.bluez'", + "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", -- cgit From 2223a9f9384ca76691f85d0faf4cdd72924f0976 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 00:59:23 +0200 Subject: dbus: never return DBUS_HANDLER_RESULT_HANDLED in filter callbacks, since other callbacks might stell want to have the messages --- src/modules/module-console-kit.c | 2 -- src/modules/module-hal-detect.c | 3 --- 2 files changed, 5 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c index a666073c..103f5c48 100644 --- a/src/modules/module-console-kit.c +++ b/src/modules/module-console-kit.c @@ -187,7 +187,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo } add_session(u, path); - return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) { @@ -202,7 +201,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo } remove_session(u, path); - return DBUS_HANDLER_RESULT_HANDLED; } finish: diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index ec370d61..6034d0ee 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -623,8 +623,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo } - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) { /* We use this message to avoid a dirty race condition when we get an ACLAdded message before the previously owning PA @@ -668,7 +666,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo /* Yes, we don't check the UDI for validity, but hopefully HAL will */ device_added_cb(u->context, udi); - return DBUS_HANDLER_RESULT_HANDLED; } finish: -- cgit From a69b7294145e7dfed6ede8e3d8aa01d7e8509142 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 01:02:32 +0200 Subject: voltest: extend test to verify correctness of _multiply() and _divide() --- src/modules/reserve-monitor.c | 24 +----------------------- src/modules/reserve.c | 25 +------------------------ 2 files changed, 2 insertions(+), 47 deletions(-) (limited to 'src/modules') diff --git a/src/modules/reserve-monitor.c b/src/modules/reserve-monitor.c index 97cb9b9e..ab453e61 100644 --- a/src/modules/reserve-monitor.c +++ b/src/modules/reserve-monitor.c @@ -64,7 +64,6 @@ static DBusHandlerResult filter_handler( DBusMessage *s, void *userdata) { - DBusMessage *reply; rm_monitor *m; DBusError error; @@ -105,31 +104,10 @@ static DBusHandlerResult filter_handler( } } - 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; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } int rm_watch( diff --git a/src/modules/reserve.c b/src/modules/reserve.c index 5597f177..b4c168cf 100644 --- a/src/modules/reserve.c +++ b/src/modules/reserve.c @@ -291,7 +291,6 @@ static DBusHandlerResult filter_handler( DBusMessage *m, void *userdata) { - DBusMessage *reply; rd_device *d; DBusError error; @@ -323,35 +322,13 @@ static DBusHandlerResult filter_handler( rd_release(d); } - return DBUS_HANDLER_RESULT_HANDLED; } } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - invalid: - if (!(reply = dbus_message_new_error( - m, - 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; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -- cgit From 24e582808c18d6866d8c10f8f0320b1af0ab758b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 01:35:43 +0200 Subject: source: rework volume handling - drop the 'virtual_' prefix from s->virtual_volume since we don't distuingish between reference and real volumes for sources - introduce an accuracy for source volumes: if the hardware can control the volume "close enough" don't necessarily adjust the rest in software unless it is beyond a certain threshold. This should save a little bit of CPU at the expensive of a bit of accuracy in volume handling. - other minor cleanups --- src/modules/alsa/alsa-source.c | 37 ++++++++++++++++--------- src/modules/bluetooth/module-bluetooth-device.c | 4 +-- src/modules/oss/module-oss.c | 8 +++--- 3 files changed, 30 insertions(+), 19 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 9a51f857..7da37553 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -65,6 +65,8 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ +#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) + struct userdata { pa_core *core; pa_module *module; @@ -987,15 +989,11 @@ static void source_get_volume_cb(pa_source *s) { if (pa_cvolume_equal(&u->hardware_volume, &r)) return; - s->virtual_volume = u->hardware_volume = r; - - if (u->mixer_path->has_dB) { - pa_cvolume reset; + s->volume = u->hardware_volume = r; - /* 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 */ + if (u->mixer_path->has_dB) + pa_source_set_soft_volume(s, NULL); } static void source_set_volume_cb(pa_source *s) { @@ -1008,7 +1006,7 @@ static void source_set_volume_cb(pa_source *s) { pa_assert(u->mixer_handle); /* Shift up by the base volume */ - pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); + pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume); if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1019,13 +1017,26 @@ static void source_set_volume_cb(pa_source *s) { u->hardware_volume = r; if (u->mixer_path->has_dB) { + pa_cvolume new_soft_volume; + pa_bool_t accurate_enough; /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); + pa_sw_cvolume_divide(&new_soft_volume, &s->volume, &u->hardware_volume); + + /* If the adjustment to do in software is only minimal we + * can skip it. That saves us CPU at the expense of a bit of + * accuracy */ + accurate_enough = + (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) && + (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY)); - pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume), + pa_yes_no(accurate_enough)); + + if (!accurate_enough) + s->soft_volume = new_soft_volume; } else { pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); @@ -1033,7 +1044,7 @@ static void source_set_volume_cb(pa_source *s) { /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ - s->virtual_volume = r; + s->volume = r; } } diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index d6321fc4..395ec838 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1500,12 +1500,12 @@ static void source_set_volume_cb(pa_source *s) { if (u->profile != PROFILE_HSP) return; - gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM; + gain = (pa_cvolume_max(&s->volume) * 15) / PA_VOLUME_NORM; if (gain > 15) gain = 15; - pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); + pa_cvolume_set(&s->volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain")); pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c index c44b882b..0848d43b 100644 --- a/src/modules/oss/module-oss.c +++ b/src/modules/oss/module-oss.c @@ -848,11 +848,11 @@ static void source_get_volume(pa_source *s) { pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); if (u->mixer_devmask & SOUND_MASK_IGAIN) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume) >= 0) return; if (u->mixer_devmask & SOUND_MASK_RECLEV) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume) >= 0) return; pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); @@ -866,11 +866,11 @@ static void source_set_volume(pa_source *s) { pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); if (u->mixer_devmask & SOUND_MASK_IGAIN) - if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume) >= 0) return; if (u->mixer_devmask & SOUND_MASK_RECLEV) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume) >= 0) return; pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); -- cgit From d6f598ab3e1cdb71dc3b408592d06bba23f53a71 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 02:29:59 +0200 Subject: udev: allow passing of ignore_dB= parameter to alsa modules --- src/modules/alsa/alsa-sink.c | 37 ++++++++++++++++--------- src/modules/bluetooth/module-bluetooth-device.c | 4 +-- src/modules/module-lirc.c | 4 +-- src/modules/module-udev-detect.c | 27 ++++++++++++++---- 4 files changed, 49 insertions(+), 23 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index a91b4b8a..12538368 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -68,6 +68,8 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ +#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ + struct userdata { pa_core *core; pa_module *module; @@ -1034,15 +1036,11 @@ static void sink_get_volume_cb(pa_sink *s) { if (pa_cvolume_equal(&u->hardware_volume, &r)) return; - s->virtual_volume = u->hardware_volume = r; - - if (u->mixer_path->has_dB) { - pa_cvolume reset; + s->real_volume = u->hardware_volume = r; - /* 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 */ + if (u->mixer_path->has_dB) + pa_sink_set_soft_volume(s, NULL); } static void sink_set_volume_cb(pa_sink *s) { @@ -1055,7 +1053,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_assert(u->mixer_handle); /* Shift up by the base volume */ - pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); + pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1066,13 +1064,26 @@ static void sink_set_volume_cb(pa_sink *s) { u->hardware_volume = r; if (u->mixer_path->has_dB) { + pa_cvolume new_soft_volume; + pa_bool_t accurate_enough; /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); + pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume); + + /* If the adjustment to do in software is only minimal we + * can skip it. That saves us CPU at the expense of a bit of + * accuracy */ + accurate_enough = + (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) && + (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY)); - pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->real_volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume), + pa_yes_no(accurate_enough)); + + if (!accurate_enough) + s->soft_volume = new_soft_volume; } else { pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); @@ -1080,7 +1091,7 @@ static void sink_set_volume_cb(pa_sink *s) { /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ - s->virtual_volume = r; + s->real_volume = r; } } diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 395ec838..4e23862c 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1476,12 +1476,12 @@ static void sink_set_volume_cb(pa_sink *s) { if (u->profile != PROFILE_HSP) return; - gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM; + gain = (pa_cvolume_max(&s->real_volume) * 15) / PA_VOLUME_NORM; if (gain > 15) gain = 15; - pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); + pa_cvolume_set(&s->real_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetSpeakerGain")); pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index 06efeb8f..2bb8014d 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -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, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case DOWN: @@ -144,7 +144,7 @@ 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, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case MUTE: diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 11de1ccb..0b30fd54 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -39,6 +39,9 @@ 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); +PA_MODULE_USAGE( + "tsched= " + "ignore_dB="); struct device { char *path; @@ -50,7 +53,9 @@ struct device { struct userdata { pa_core *core; pa_hashmap *devices; - pa_bool_t use_tsched; + + pa_bool_t use_tsched:1; + pa_bool_t ignore_dB:1; struct udev* udev; struct udev_monitor *monitor; @@ -62,6 +67,7 @@ struct userdata { static const char* const valid_modargs[] = { "tsched", + "ignore_dB", NULL }; @@ -140,12 +146,14 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { args = pa_sprintf_malloc("device_id=\"%s\" " "name=\"%s\" " "card_name=\"%s\" " - "tsched=%i " + "tsched=%s " + "ignore_dB=%s " "card_properties=\"module-udev-detect.discovered=1\"", path_get_card_id(path), n, card_name, - (int) u->use_tsched); + pa_yes_no(u->use_tsched), + pa_yes_no(u->ignore_dB)); pa_log_debug("Loading module-alsa-card with arguments '%s'", args); m = pa_module_load(u->core, "module-alsa-card", args); @@ -364,6 +372,7 @@ int pa__init(pa_module *m) { struct udev_enumerate *enumerate = NULL; struct udev_list_entry *item = NULL, *first = NULL; int fd; + pa_bool_t use_tsched = TRUE, ignore_dB = FALSE; pa_assert(m); @@ -375,13 +384,19 @@ int pa__init(pa_module *m) { 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."); + if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) { + pa_log("Failed to parse tsched= argument."); + goto fail; + } + u->use_tsched = use_tsched; + + if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) { + pa_log("Failed to parse ignore_dB= argument."); goto fail; } + u->ignore_dB = ignore_dB; if (!(u->udev = udev_new())) { pa_log("Failed to initialize udev library."); -- cgit From 5207e191424675df74059aaf30f9b1292a05cb5d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 02:37:35 +0200 Subject: match: document how broken the module-match logic is --- src/modules/module-match.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-match.c b/src/modules/module-match.c index 625f2a8b..14e01127 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -243,6 +243,9 @@ int pa__init(pa_module*m) { if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0) goto fail; + /* FIXME: Doing this asynchronously is just broken. This needs to + * use a hook! */ + u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u); pa_modargs_free(ma); -- cgit From 8c31974f56ebbbfc1a4978150026acf77c32689e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 02:55:02 +0200 Subject: sink: volume handling rework, new flat volume logic - We now implement a logic where the sink maintains two distinct volumes: the 'reference' volume which is shown to the users, and the 'real' volume, which is configured to the hardware. The latter is configured to the max of all streams. Volume changes on sinks are propagated back to the streams proportional to the reference volume change. Volume changes on sink inputs are forwarded to the sink by 'pushing' the volume if necessary. This renames the old 'virtual_volume' to 'real_volume'. The 'reference_volume' is now the one exposed to users. By this logic the sink volume visible to the user, will always be the "upper" boundary for everything that is played. Saved/restored stream volumes are measured relative to this boundary, the factor here is always < 1.0. - introduce accuracy for sink volumes, similar to the accuracy we already have for source volumes. - other cleanups. --- src/modules/alsa/alsa-sink.c | 2 +- src/modules/module-device-restore.c | 2 +- src/modules/module-lirc.c | 2 +- src/modules/module-match.c | 2 +- src/modules/module-mmkbd-evdev.c | 6 +++--- src/modules/module-tunnel.c | 4 ++-- src/modules/oss/module-oss.c | 8 ++++---- src/modules/raop/module-raop-sink.c | 6 +++--- 8 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 12538368..e3707ae7 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1009,7 +1009,7 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { return 0; if (mask & SND_CTL_EVENT_MASK_VALUE) { - pa_sink_get_volume(u->sink, TRUE, FALSE); + pa_sink_get_volume(u->sink, TRUE); pa_sink_get_mute(u->sink, TRUE); } diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 120b762c..da6c9666 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -218,7 +218,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (sink->save_volume) { entry.channel_map = sink->channel_map; - entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE); + entry.volume = *pa_sink_get_volume(sink, FALSE); entry.volume_valid = TRUE; } diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index 2bb8014d..fdfdc797 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -120,7 +120,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event pa_log("Failed to get sink '%s'", u->sink_name); else { int i; - pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE); + pa_cvolume cv = *pa_sink_get_volume(s, FALSE); #define DELTA (PA_VOLUME_NORM/20) diff --git a/src/modules/module-match.c b/src/modules/module-match.c index 14e01127..0bd781d2 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -216,7 +216,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v pa_cvolume cv; pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume); pa_cvolume_set(&cv, si->sample_spec.channels, r->volume); - pa_sink_input_set_volume(si, &cv, TRUE, TRUE); + pa_sink_input_set_volume(si, &cv, TRUE, FALSE); } } } diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index b30fae51..7be48700 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -102,7 +102,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event pa_log("Failed to get sink '%s'", u->sink_name); else { int i; - pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE); + pa_cvolume cv = *pa_sink_get_volume(s, FALSE); #define DELTA (PA_VOLUME_NORM/20) @@ -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, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case DOWN: @@ -126,7 +126,7 @@ 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, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case MUTE_TOGGLE: diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index eaccea4e..5ccb81d0 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -1162,7 +1162,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag pa_assert(u->sink); if ((u->version < 11 || !!mute == !!u->sink->muted) && - pa_cvolume_equal(&volume, &u->sink->virtual_volume)) + pa_cvolume_equal(&volume, &u->sink->real_volume)) return; pa_sink_volume_changed(u->sink, &volume); @@ -1763,7 +1763,7 @@ static void sink_set_volume(pa_sink *sink) { pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME); pa_tagstruct_putu32(t, tag = u->ctag++); pa_tagstruct_putu32(t, u->device_index); - pa_tagstruct_put_cvolume(t, &sink->virtual_volume); + pa_tagstruct_put_cvolume(t, &sink->real_volume); pa_pstream_send_tagstruct(u->pstream, t); } diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c index 0848d43b..71536260 100644 --- a/src/modules/oss/module-oss.c +++ b/src/modules/oss/module-oss.c @@ -812,11 +812,11 @@ static void sink_get_volume(pa_sink *s) { pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM)); if (u->mixer_devmask & SOUND_MASK_VOLUME) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->real_volume) >= 0) return; if (u->mixer_devmask & SOUND_MASK_PCM) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->real_volume) >= 0) return; pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); @@ -830,11 +830,11 @@ static void sink_set_volume(pa_sink *s) { pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM)); if (u->mixer_devmask & SOUND_MASK_VOLUME) - if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->real_volume) >= 0) return; if (u->mixer_devmask & SOUND_MASK_PCM) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->real_volume) >= 0) return; pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c index 9699132d..ac48ab10 100644 --- a/src/modules/raop/module-raop-sink.c +++ b/src/modules/raop/module-raop-sink.c @@ -283,15 +283,15 @@ static void sink_set_volume_cb(pa_sink *s) { /* Calculate the max volume of all channels. We'll use this as our (single) volume on the APEX device and emulate any variation in channel volumes in software */ - v = pa_cvolume_max(&s->virtual_volume); + v = pa_cvolume_max(&s->real_volume); /* Create a pa_cvolume version of our single value */ pa_cvolume_set(&hw, s->sample_spec.channels, v); /* Perform any software manipulation of the volume needed */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw); + pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw); - pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->real_volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw)); pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); -- cgit From 2f54798b1afdbe684dd1e5ba6d39a807c6afc8d8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 03:37:29 +0200 Subject: mmkbd: replace manual code by pa_cvolume_{inc|dec}() --- src/modules/module-mmkbd-evdev.c | 43 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index 7be48700..516bf413 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -65,6 +65,8 @@ struct userdata { pa_module *module; }; +#define DELTA (PA_VOLUME_NORM/20) + static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) { struct userdata *u = userdata; @@ -85,14 +87,27 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event } if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) { - enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID; + enum { + INVALID, + UP, + DOWN, + MUTE_TOGGLE + } volchange = INVALID; pa_log_debug("Key code=%u, value=%u", ev.code, ev.value); switch (ev.code) { - case KEY_VOLUMEDOWN: volchange = DOWN; break; - case KEY_VOLUMEUP: volchange = UP; break; - case KEY_MUTE: volchange = MUTE_TOGGLE; break; + case KEY_VOLUMEDOWN: + volchange = DOWN; + break; + + case KEY_VOLUMEUP: + volchange = UP; + break; + + case KEY_MUTE: + volchange = MUTE_TOGGLE; + break; } if (volchange != INVALID) { @@ -101,36 +116,20 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) pa_log("Failed to get sink '%s'", u->sink_name); else { - int i; pa_cvolume cv = *pa_sink_get_volume(s, FALSE); -#define DELTA (PA_VOLUME_NORM/20) - switch (volchange) { case UP: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] < PA_VOLUME_MAX - DELTA) - cv.values[i] += DELTA; - else - cv.values[i] = PA_VOLUME_MAX; - } - + pa_cvolume_inc(&cv, DELTA); pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case DOWN: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] > DELTA) - cv.values[i] -= DELTA; - else - cv.values[i] = PA_VOLUME_MUTED; - } - + pa_cvolume_dec(&cv, DELTA); pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE); break; -- cgit From 7af3833b7110c08d3717edb6018350d390e9d492 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 03:37:42 +0200 Subject: lirc: replace manual code by pa_cvolume_{inc|dec}() --- src/modules/module-lirc.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index fdfdc797..d0e902f6 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -63,6 +63,8 @@ struct userdata { float mute_toggle_save; }; +#define DELTA (PA_VOLUME_NORM/20) + static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) { struct userdata *u = userdata; char *name = NULL, *code = NULL; @@ -119,31 +121,16 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) pa_log("Failed to get sink '%s'", u->sink_name); else { - int i; pa_cvolume cv = *pa_sink_get_volume(s, FALSE); -#define DELTA (PA_VOLUME_NORM/20) - switch (volchange) { case UP: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] < PA_VOLUME_MAX - DELTA) - cv.values[i] += DELTA; - else - cv.values[i] = PA_VOLUME_MAX; - } - + pa_cvolume_inc(&cv, DELTA); pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case DOWN: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] > DELTA) - cv.values[i] -= DELTA; - else - cv.values[i] = PA_VOLUME_MUTED; - } - + pa_cvolume_dec(&cv, DELTA); pa_sink_set_volume(s, &cv, TRUE, TRUE); break; @@ -156,7 +143,6 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event break; case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE); break; -- cgit From c1b6a87b27b569cda135da05b53cc98aa9ca37cb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 20 Aug 2009 13:40:27 +0200 Subject: alsa-sink: reduce the amount of smoother updates Exponentially increase the amount of time between smoother updates. We start with a 2ms interval and increase up to 200ms intervals. Smoother updates and the resulting linear regression take a fair amount of CPU so we want to reduce the amount of updates. --- src/modules/alsa/alsa-sink.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index e3707ae7..c3694729 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -68,6 +68,9 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update inteval */ + #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ struct userdata { @@ -115,6 +118,8 @@ struct userdata { pa_smoother *smoother; uint64_t write_count; uint64_t since_start; + pa_usec_t smoother_interval; + pa_usec_t last_smoother_update; pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; @@ -723,17 +728,27 @@ static void update_smoother(struct userdata *u) { now1 = pa_timespec_load(&htstamp); } + /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ + if (now1 <= 0) + now1 = pa_rtclock_now(); + + /* check if the time since the last update is bigger than the interval */ + if (u->last_smoother_update > 0) { + if (u->last_smoother_update + u->smoother_interval > now1) + return; + } + position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size); if (PA_UNLIKELY(position < 0)) position = 0; - /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ - if (now1 <= 0) - now1 = pa_rtclock_now(); - now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec); + u->last_smoother_update = now1; + /* exponentially increase the update interval up to the MAX limit */ + u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL); + pa_smoother_put(u->smoother, now1, now2); } @@ -906,6 +921,8 @@ static int unsuspend(struct userdata *u) { u->write_count = 0; pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; + u->last_smoother_update = 0; u->first = TRUE; u->since_start = 0; @@ -1622,6 +1639,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca 5, pa_rtclock_now(), TRUE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; dev_id = pa_modargs_get_value( ma, "device_id", -- cgit From a562978509674f37f3cc7d5d5d1002f52a59654d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 02:59:26 +0200 Subject: ladspa: forward volume changes from ladspa sink to stream and hence via flat volume logic to master sink --- src/modules/module-ladspa-sink.c | 72 +++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index f2d53d00..233f90c4 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -99,7 +99,7 @@ static const char* const valid_modargs[] = { }; /* Called from I/O thread context */ -static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { @@ -130,7 +130,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse } /* Called from main context */ -static int sink_set_state(pa_sink *s, pa_sink_state_t state) { +static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) { struct userdata *u; pa_sink_assert_ref(s); @@ -145,7 +145,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { } /* Called from I/O thread context */ -static void sink_request_rewind(pa_sink *s) { +static void sink_request_rewind_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); @@ -160,7 +160,7 @@ static void sink_request_rewind(pa_sink *s) { } /* Called from I/O thread context */ -static void sink_update_requested_latency(pa_sink *s) { +static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); @@ -176,6 +176,34 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } +/* Called from main context */ +static void sink_set_volume_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return; + + pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE); +} + +/* Called from main context */ +static void sink_set_mute_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return; + + pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); +} + /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; @@ -394,6 +422,26 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); } +/* Called from main context */ +static void sink_input_volume_changed_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_volume_changed(u->sink, &i->volume); +} + +/* Called from main context */ +static void sink_input_mute_changed_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_mute_changed(u->sink, i->muted); +} + int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -731,7 +779,9 @@ int pa__init(pa_module*m) { goto fail; } - u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); + u->sink = pa_sink_new(m->core, &sink_data, + PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| + (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -739,10 +789,12 @@ int pa__init(pa_module*m) { goto fail; } - u->sink->parent.process_msg = sink_process_msg; - u->sink->set_state = sink_set_state; - u->sink->update_requested_latency = sink_update_requested_latency; - u->sink->request_rewind = sink_request_rewind; + u->sink->parent.process_msg = sink_process_msg_cb; + u->sink->set_state = sink_set_state_cb; + u->sink->update_requested_latency = sink_update_requested_latency_cb; + u->sink->request_rewind = sink_request_rewind_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->set_mute = sink_set_mute_cb; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); @@ -775,6 +827,8 @@ int pa__init(pa_module*m) { u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; u->sink_input->moving = sink_input_moving_cb; + u->sink_input->volume_changed = sink_input_volume_changed_cb; + u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; pa_sink_put(u->sink); -- cgit From 8a2a6b2004cd299467de1955f7f99e25033faa63 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 03:43:53 +0200 Subject: adjust various data/library paths automatically if we are run from a build tree --- src/modules/alsa/alsa-mixer.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index a4c2ee0f..61c92cd0 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -929,7 +929,7 @@ static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) { int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { pa_alsa_element *e; - int r; + int r = 0; pa_assert(m); pa_assert(p); @@ -1849,7 +1849,12 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) items[1].data = &p->description; items[2].data = &p->name; - fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR); + fn = pa_maybe_prefix_path(fname, +#if defined(__linux__) && !defined(__OPTIMIZE__) + pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" : +#endif + PA_ALSA_PATHS_DIR); + r = pa_config_parse(fn, NULL, items, p); pa_xfree(fn); @@ -3110,7 +3115,12 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel if (!fname) fname = "default.conf"; - fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR); + fn = pa_maybe_prefix_path(fname, +#if defined(__linux__) && !defined(__OPTIMIZE__) + pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" : +#endif + PA_ALSA_PROFILE_SETS_DIR); + r = pa_config_parse(fn, NULL, items, ps); pa_xfree(fn); -- cgit From ac056191410b67466b429720778fe87279d9912a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 03:45:17 +0200 Subject: combine: quieten gcc a bit --- src/modules/module-combine.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 582cbce1..e90ef11c 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -1161,6 +1161,8 @@ int pa__init(pa_module*m) { pa_channel_map slaves_map; pa_bool_t is_first_slave = TRUE; + pa_sample_spec_init(&slaves_spec); + while ((n = pa_split(slaves, ",", &split_state))) { pa_sink *slave_sink; -- cgit From fe9a577cf2799c0864a05a08c785161ba3738d88 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 03:45:58 +0200 Subject: alsa: leave headphone jack enabled in normal mixer paths --- src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf | 7 +++++-- src/modules/alsa/mixer/paths/analog-output-mono.conf | 7 +++++-- src/modules/alsa/mixer/paths/analog-output.conf | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf index 2db976a5..3457d4a2 100644 --- a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf @@ -41,9 +41,12 @@ volume = merge override-map.1 = lfe override-map.2 = lfe,lfe +; This profile path is intended to control the speaker, not the +; headphones. But it should not hurt if we leave the headphone jack +; enabled nonetheless. [Element Headphone] -switch = off -volume = off +switch = mute +volume = zero [Element Speaker] switch = mute diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf index a58cc970..dc270cfe 100644 --- a/src/modules/alsa/mixer/paths/analog-output-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf @@ -38,9 +38,12 @@ volume = merge override-map.1 = all override-map.2 = all-left,all-right +; This profile path is intended to control the speaker, not the +; headphones. But it should not hurt if we leave the headphone jack +; enabled nonetheless. [Element Headphone] -switch = off -volume = off +switch = mute +volume = zero [Element Speaker] switch = mute diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf index b412a437..f71a05a1 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf +++ b/src/modules/alsa/mixer/paths/analog-output.conf @@ -37,9 +37,12 @@ override-map.2 = all-left,all-right switch = off volume = off +; This profile path is intended to control the speaker, not the +; headphones. But it should not hurt if we leave the headphone jack +; enabled nonetheless. [Element Headphone] -switch = off -volume = off +switch = mute +volume = zero [Element Speaker] switch = mute -- cgit From 5317e35543ab208a416cc662e2a6a88899a96704 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 14:55:05 +0200 Subject: udev: when a device appears that we cannot access right-away try again later on inotify --- src/modules/module-udev-detect.c | 91 +++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 34 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 0b30fd54..22ce8c3c 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -47,6 +47,7 @@ struct device { char *path; pa_bool_t accessible; char *card_name; + char *args; uint32_t module; }; @@ -78,6 +79,7 @@ static void device_free(struct device *d) { pa_xfree(d->path); pa_xfree(d->card_name); + pa_xfree(d->args); pa_xfree(d); } @@ -103,22 +105,43 @@ static void verify_access(struct userdata *u, struct device *d) { 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)); + d->accessible = access(cd, R_OK|W_OK) >= 0; pa_xfree(cd); - pa_card_suspend(card, !d->accessible, PA_SUSPEND_SESSION); + pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible)); + + if (d->module == PA_INVALID_INDEX) { + + /* If we not loaded, try to load */ + + if (d->accessible) { + pa_module *m; + + pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); + m = pa_module_load(u->core, "module-alsa-card", d->args); + + if (m) { + d->module = m->index; + pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name); + } else + pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name); + } + + } else { + + /* If we are already loaded update suspend status with + * accessible boolean */ + + if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) + 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; @@ -135,44 +158,41 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { return; } + d = pa_xnew0(struct device, 1); + d->path = pa_xstrdup(path); + d->accessible = TRUE; + d->module = PA_INVALID_INDEX; + 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); + d->card_name = pa_sprintf_malloc("alsa_card.%s", n); + d->args = pa_sprintf_malloc("device_id=\"%s\" " + "name=\"%s\" " + "card_name=\"%s\" " + "tsched=%s " + "ignore_dB=%s " + "card_properties=\"module-udev-detect.discovered=1\"", + path_get_card_id(path), + n, + d->card_name, + pa_yes_no(u->use_tsched), + pa_yes_no(u->ignore_dB)); + pa_xfree(n); - card_name = pa_sprintf_malloc("alsa_card.%s", n); - args = pa_sprintf_malloc("device_id=\"%s\" " - "name=\"%s\" " - "card_name=\"%s\" " - "tsched=%s " - "ignore_dB=%s " - "card_properties=\"module-udev-detect.discovered=1\"", - path_get_card_id(path), - n, - card_name, - pa_yes_no(u->use_tsched), - pa_yes_no(u->ignore_dB)); - - pa_log_debug("Loading module-alsa-card with arguments '%s'", args); - m = pa_module_load(u->core, "module-alsa-card", args); - pa_xfree(args); + pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); + m = pa_module_load(u->core, "module-alsa-card", d->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); + pa_log_info("Card %s (%s) added and module loaded.", path, d->card_name); } else - pa_xfree(card_name); + pa_log_info("Card %s (%s) added but failed to load module.", path, d->card_name); - pa_xfree(n); + pa_hashmap_put(u->devices, d->path, d); } static void remove_card(struct userdata *u, struct udev_device *dev) { @@ -185,7 +205,10 @@ static void remove_card(struct userdata *u, struct udev_device *dev) { return; pa_log_info("Card %s removed.", d->path); - pa_module_unload_request_by_index(u->core, d->module, TRUE); + + if (d->module != PA_INVALID_INDEX) + pa_module_unload_request_by_index(u->core, d->module, TRUE); + device_free(d); } -- cgit From 14c27c7ade403683e06705e45b9a3df28102a909 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 22:05:27 +0200 Subject: gconf: use correct path for gconf-helper tool when running from build tree --- src/modules/gconf/module-gconf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/modules') diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c index c01ebbf6..85523b39 100644 --- a/src/modules/gconf/module-gconf.c +++ b/src/modules/gconf/module-gconf.c @@ -52,9 +52,6 @@ PA_MODULE_LOAD_ONCE(TRUE); #define MAX_MODULES 10 #define BUF_MAX 2048 -/* #undef PA_GCONF_HELPER */ -/* #define PA_GCONF_HELPER "/home/lennart/projects/pulseaudio/src/gconf-helper" */ - struct module_item { char *name; char *args; @@ -343,7 +340,11 @@ int pa__init(pa_module*m) { u->io_event = NULL; u->buf_fill = 0; - if ((u->fd = pa_start_child_for_read(PA_GCONF_HELPER, NULL, &u->pid)) < 0) + if ((u->fd = pa_start_child_for_read( +#if defined(__linux__) && !defined(__OPTIMIZE__) + pa_run_from_build_tree() ? PA_BUILDDIR "/.libs/gconf-helper" : +#endif + PA_GCONF_HELPER, NULL, &u->pid)) < 0) goto fail; u->io_event = m->core->mainloop->io_new( -- cgit From d06680afe88d14a46ce8a4541d43d514a225732f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 22:06:23 +0200 Subject: udev: always verify access before loading modules --- src/modules/module-udev-detect.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 22ce8c3c..2c7e7dc3 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -142,7 +142,6 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { struct device *d; const char *path; const char *t; - pa_module *m; char *n; pa_assert(u); @@ -183,16 +182,9 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { pa_yes_no(u->ignore_dB)); pa_xfree(n); - pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); - m = pa_module_load(u->core, "module-alsa-card", d->args); - - if (m) { - d->module = m->index; - pa_log_info("Card %s (%s) added and module loaded.", path, d->card_name); - } else - pa_log_info("Card %s (%s) added but failed to load module.", path, d->card_name); - pa_hashmap_put(u->devices, d->path, d); + + verify_access(u, d); } static void remove_card(struct userdata *u, struct udev_device *dev) { @@ -472,7 +464,7 @@ int pa__init(pa_module *m) { udev_enumerate_unref(enumerate); - pa_log_info("Loaded %u modules.", pa_hashmap_size(u->devices)); + pa_log_info("Found %u cards.", pa_hashmap_size(u->devices)); pa_modargs_free(ma); -- cgit From 4ec701aa21c51a0a0c1dd60bd94ee4af1c1d1343 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 22:11:38 +0200 Subject: udev: don't access string after free() --- src/modules/module-udev-detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 2c7e7dc3..f2e7b0df 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -107,9 +107,9 @@ static void verify_access(struct userdata *u, struct device *d) { cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path)); d->accessible = access(cd, R_OK|W_OK) >= 0; - pa_xfree(cd); pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible)); + pa_xfree(cd); if (d->module == PA_INVALID_INDEX) { -- cgit From 2d0120485c60e0eacc81d1e28e2993559350211b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 22:49:23 +0200 Subject: udev: watch for both ACL changes and processes closing devices --- src/modules/module-udev-detect.c | 63 ++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 12 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index f2e7b0df..2fcbe216 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -45,7 +45,8 @@ PA_MODULE_USAGE( struct device { char *path; - pa_bool_t accessible; + pa_bool_t accessible:1; + pa_bool_t need_verify:1; char *card_name; char *args; uint32_t module; @@ -277,6 +278,34 @@ fail: u->udev_io = NULL; } +static pa_bool_t pcm_node_belongs_to_device( + struct device *d, + const char *node) { + + char *cd; + pa_bool_t b; + + cd = pa_sprintf_malloc("pcmC%sD", path_get_card_id(d->path)); + b = pa_startswith(node, cd); + pa_xfree(cd); + + return b; +} + +static pa_bool_t control_node_belongs_to_device( + struct device *d, + const char *node) { + + char *cd; + pa_bool_t b; + + cd = pa_sprintf_malloc("controlC%s", path_get_card_id(d->path)); + b = pa_streq(node, cd); + pa_xfree(cd); + + return b; +} + static void inotify_cb( pa_mainloop_api*a, pa_io_event* e, @@ -290,7 +319,9 @@ static void inotify_cb( } buf; struct userdata *u = userdata; static int type = 0; - pa_bool_t verify = FALSE, deleted = FALSE; + pa_bool_t deleted = FALSE; + struct device *d; + void *state; for (;;) { ssize_t r; @@ -305,22 +336,30 @@ static void inotify_cb( goto fail; } - if ((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC")) - verify = TRUE; + /* From udev we get the guarantee that the control + * device's ACL is changes last. To avoid races when ACLs + * are changed we hence watch only the control device */ + if (((buf.e.mask & IN_ATTRIB) && pa_startswith(buf.e.name, "controlC"))) + PA_HASHMAP_FOREACH(d, u->devices, state) + if (control_node_belongs_to_device(d, buf.e.name)) + d->need_verify = TRUE; + + /* ALSA doesn't really give us any guarantee on the closing + * order, so let's simply hope */ + if (((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC"))) + PA_HASHMAP_FOREACH(d, u->devices, state) + if (pcm_node_belongs_to_device(d, buf.e.name)) + d->need_verify = TRUE; if ((buf.e.mask & (IN_DELETE_SELF|IN_MOVE_SELF))) deleted = TRUE; } - if (verify) { - struct device *d; - void *state; - - pa_log_debug("Verifying access."); - - PA_HASHMAP_FOREACH(d, u->devices, state) + PA_HASHMAP_FOREACH(d, u->devices, state) + if (d->need_verify) { + d->need_verify = FALSE; verify_access(u, d); - } + } if (!deleted) return; -- cgit From 066e160bbd095afe79fc7ea79fbc88b8746960cc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 23:24:40 +0200 Subject: udev: tell inotify to actually inform us about ACL changes --- src/modules/module-udev-detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 2fcbe216..1d67c0cc 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -389,7 +389,7 @@ static int setup_inotify(struct userdata *u) { } 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|IN_DELETE_SELF|IN_MOVE_SELF); + r = inotify_add_watch(u->inotify_fd, dev_snd, IN_ATTRIB|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF); pa_xfree(dev_snd); if (r < 0) { -- cgit From 44c7aa55e25334901769b82355c12dee91cb3629 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Fri, 21 Aug 2009 13:15:38 +1000 Subject: Solaris: build fixes (resent) Fix bit rot due to recent flat volume changes. --- src/modules/module-solaris.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 0920d25e..2c878c20 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -479,7 +479,7 @@ static void sink_set_volume(pa_sink *s) { if (u->fd >= 0) { AUDIO_INITINFO(&info); - info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; + info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; assert(info.play.gain <= AUDIO_MAX_GAIN); if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { @@ -501,8 +501,7 @@ static void sink_get_volume(pa_sink *s) { if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); else - pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels, - info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); + pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); } } @@ -515,7 +514,7 @@ static void source_set_volume(pa_source *s) { if (u->fd >= 0) { AUDIO_INITINFO(&info); - info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; + info.play.gain = pa_cvolume_max(&s->volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; assert(info.play.gain <= AUDIO_MAX_GAIN); if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { @@ -537,8 +536,7 @@ static void source_get_volume(pa_source *s) { if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); else - pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels, - info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); + pa_cvolume_set(&s->volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN); } } @@ -797,7 +795,7 @@ static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void pa_log_debug("caught signal"); if (u->sink) { - pa_sink_get_volume(u->sink, TRUE, FALSE); + pa_sink_get_volume(u->sink, TRUE); pa_sink_get_mute(u->sink, TRUE); } -- cgit From 87d2dded9b90331943a6c7b9d8d9b1ac100b6689 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Fri, 21 Aug 2009 13:17:03 +1000 Subject: Solaris: use smoother (resent) Make use of the smoother, just in case. --- src/modules/module-solaris.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 2c878c20..3bf7c4b0 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -60,6 +60,7 @@ #include #include #include +#include #include "module-solaris-symdef.h" @@ -110,6 +111,8 @@ struct userdata { uint32_t prev_playback_samples, prev_record_samples; int32_t minimum_request; + + pa_smoother *smoother; }; static const char* const valid_modargs[] = { @@ -145,7 +148,12 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) { /* Handle wrap-around of the device's sample counter, which is a uint_32. */ if (u->prev_playback_samples > info.play.samples) { - /* Unfortunately info.play.samples can sometimes go backwards, even before it wraps! */ + /* + * Unfortunately info.play.samples can sometimes go backwards, even before it wraps! + * The bug seems to be absent on Solaris x86 nv117 with audio810 driver, at least on this (UP) machine. + * The bug is present on a different (SMP) machine running Solaris x86 nv103 with audioens driver. + * An earlier revision of this file mentions the same bug independently (unknown configuration). + */ if (u->prev_playback_samples + info.play.samples < 240000) { ++u->play_samples_msw; } else { @@ -155,6 +163,8 @@ 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_smoother_put(u->smoother, pa_rtclock_now(), pa_bytes_to_usec(played_bytes, &u->sink->sample_spec)); + return u->written_bytes - played_bytes; } @@ -387,6 +397,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); + pa_smoother_pause(u->smoother, pa_rtclock_now()); + if (!u->source || u->source_suspended) { if (suspend(u) < 0) return -1; @@ -398,6 +410,8 @@ 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_now(), TRUE); + if (!u->source || u->source_suspended) { if (unsuspend(u) < 0) return -1; @@ -604,11 +618,13 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); + pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); + for (;;) { /* Render some data and write it to the dsp */ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) { - pa_usec_t xtime0; + pa_usec_t xtime0, ysleep_interval, xsleep_interval; uint64_t buffered_bytes; if (u->sink->thread_info.rewind_requested) @@ -627,6 +643,8 @@ static void thread_func(void *userdata) { info.play.error = 0; if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); + + pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); } for (;;) { @@ -689,7 +707,9 @@ 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)); + ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec); + xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval); + pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval)); } else pa_rtpoll_set_timer_disabled(u->rtpoll); @@ -836,6 +856,9 @@ int pa__init(pa_module *m) { u = pa_xnew0(struct userdata, 1); + if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, TRUE, TRUE, 10, pa_rtclock_now(), TRUE))) + goto fail; + /* * For a process (or several processes) to use the same audio device for both * record and playback at the same time, the device's mixer must be enabled. @@ -1073,6 +1096,9 @@ void pa__done(pa_module *m) { if (u->fd >= 0) close(u->fd); + if (u->smoother) + pa_smoother_free(u->smoother); + pa_xfree(u->device_name); pa_xfree(u); -- cgit From 601fb63b0160d3d76083d07dcc1201a123031915 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Fri, 21 Aug 2009 13:18:40 +1000 Subject: Solaris: fixed latency (resent) Set a fixed latency based on the given buffer size, which is constrained to the 128 KB limit on buffered writes. Also fix an error path. --- src/modules/module-solaris.c | 56 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 3bf7c4b0..71f14071 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -136,6 +136,9 @@ static const char* const valid_modargs[] = { #define MAX_RENDER_HZ (300) /* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */ +#define MAX_BUFFER_SIZE (128 * 1024) +/* An attempt to buffer more than 128 KB causes write() to fail with errno == EAGAIN. */ + static uint64_t get_playback_buffered_bytes(struct userdata *u) { audio_info_t info; uint64_t played_bytes; @@ -651,6 +654,7 @@ static void thread_func(void *userdata) { void *p; ssize_t w; size_t len; + int write_type = 1; /* * Since we cannot modify the size of the output buffer we fake it @@ -668,38 +672,31 @@ static void thread_func(void *userdata) { break; if (u->memchunk.length < len) - pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk); + pa_sink_render(u->sink, len - u->memchunk.length, &u->memchunk); + + len = PA_MIN(u->memchunk.length, len); p = pa_memblock_acquire(u->memchunk.memblock); - w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL); + w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type); pa_memblock_release(u->memchunk.memblock); if (w <= 0) { - switch (errno) { - case EINTR: - 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, 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: - pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno)); - goto fail; + if (errno == EINTR) { + continue; + } else if (errno == EAGAIN) { + /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */ + pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes); + break; + } else { + pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno)); + goto fail; } } else { pa_assert(w % u->frame_size == 0); u->written_bytes += w; - u->memchunk.length -= w; - u->memchunk.index += w; + u->memchunk.length -= w; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); @@ -830,7 +827,7 @@ int pa__init(pa_module *m) { pa_channel_map map; pa_modargs *ma = NULL; uint32_t buffer_length_msec; - int fd; + int fd = -1; pa_sink_new_data sink_new_data; pa_source_new_data source_new_data; char const *name; @@ -882,7 +879,13 @@ int pa__init(pa_module *m) { } 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"); + pa_log("buffer_length argument cannot be smaller than %u", + (unsigned)(pa_bytes_to_usec(2 * u->minimum_request, &ss) / 1000)); + goto fail; + } + if (u->buffer_size > MAX_BUFFER_SIZE) { + pa_log("buffer_length argument cannot be greater than %u", + (unsigned)(pa_bytes_to_usec(MAX_BUFFER_SIZE, &ss) / 1000)); goto fail; } @@ -945,6 +948,7 @@ int pa__init(pa_module *m) { pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); + pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->source->sample_spec)); u->source->get_volume = source_get_volume; u->source->set_volume = source_set_volume; @@ -987,15 +991,15 @@ int pa__init(pa_module *m) { pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); + pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec)); + pa_sink_set_max_request(u->sink, u->buffer_size); + pa_sink_set_max_rewind(u->sink, u->buffer_size); u->sink->get_volume = sink_get_volume; u->sink->set_volume = sink_set_volume; u->sink->get_mute = sink_get_mute; u->sink->set_mute = sink_set_mute; u->sink->refresh_volume = u->sink->refresh_muted = TRUE; - - pa_sink_set_max_request(u->sink, u->buffer_size); - pa_sink_set_max_rewind(u->sink, u->buffer_size); } else u->sink = NULL; -- cgit From 5b0683d6cd103a7a91bc2e88bcc9f77750d10c25 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 22 Aug 2009 04:03:31 +0200 Subject: ladspa/remap: handle failing stream moves properly --- src/modules/module-ladspa-sink.c | 7 +++++-- src/modules/module-remap-sink.c | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 233f90c4..aa28f7fd 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -418,8 +418,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); - pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + if (dest) { + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + } else + pa_sink_set_asyncmsgq(u->sink, NULL); } /* Called from main context */ diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 0b4fdc9b..becff55c 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -302,8 +302,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); - pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + if (dest) { + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + } else + pa_sink_set_asyncmsgq(u->sink, NULL); } int pa__init(pa_module*m) { -- cgit From 560da5b0a1386c2a158d69b6ba0ef99c0f03bbf4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 22 Aug 2009 21:59:17 +0200 Subject: udev: process all inotify events queued up, not just the first one in the queue --- src/modules/module-udev-detect.c | 56 ++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 17 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 1d67c0cc..55136348 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -325,6 +325,7 @@ static void inotify_cb( for (;;) { ssize_t r; + struct inotify_event *event; pa_zero(buf); if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) { @@ -336,23 +337,44 @@ static void inotify_cb( goto fail; } - /* From udev we get the guarantee that the control - * device's ACL is changes last. To avoid races when ACLs - * are changed we hence watch only the control device */ - if (((buf.e.mask & IN_ATTRIB) && pa_startswith(buf.e.name, "controlC"))) - PA_HASHMAP_FOREACH(d, u->devices, state) - if (control_node_belongs_to_device(d, buf.e.name)) - d->need_verify = TRUE; - - /* ALSA doesn't really give us any guarantee on the closing - * order, so let's simply hope */ - if (((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC"))) - PA_HASHMAP_FOREACH(d, u->devices, state) - if (pcm_node_belongs_to_device(d, buf.e.name)) - d->need_verify = TRUE; - - if ((buf.e.mask & (IN_DELETE_SELF|IN_MOVE_SELF))) - deleted = TRUE; + event = &buf.e; + while (r > 0) { + size_t len; + + if ((size_t) r < sizeof(struct inotify_event)) { + pa_log("read() too short."); + goto fail; + } + + len = sizeof(struct inotify_event) + event->len; + + if ((size_t) r < len) { + pa_log("Payload missing."); + goto fail; + } + + /* From udev we get the guarantee that the control + * device's ACL is changed last. To avoid races when ACLs + * are changed we hence watch only the control device */ + if (((event->mask & IN_ATTRIB) && pa_startswith(event->name, "controlC"))) + PA_HASHMAP_FOREACH(d, u->devices, state) + if (control_node_belongs_to_device(d, event->name)) + d->need_verify = TRUE; + + /* ALSA doesn't really give us any guarantee on the closing + * order, so let's simply hope */ + if (((event->mask & IN_CLOSE_WRITE) && pa_startswith(event->name, "pcmC"))) + PA_HASHMAP_FOREACH(d, u->devices, state) + if (pcm_node_belongs_to_device(d, event->name)) + d->need_verify = TRUE; + + /* /dev/snd/ might have been removed */ + if ((event->mask & (IN_DELETE_SELF|IN_MOVE_SELF))) + deleted = TRUE; + + event = (struct inotify_event*) ((uint8_t*) event + len); + r -= len; + } } PA_HASHMAP_FOREACH(d, u->devices, state) -- cgit From d6fb8d10819bebc1cee203de7330cceeafde9fed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 22 Aug 2009 23:10:45 +0200 Subject: udev: check busy status of alsa cards before loading alsa modules and hence initiating profile probing --- src/modules/module-udev-detect.c | 149 ++++++++++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 17 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 55136348..b41b9c0f 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -45,8 +46,7 @@ PA_MODULE_USAGE( struct device { char *path; - pa_bool_t accessible:1; - pa_bool_t need_verify:1; + pa_bool_t need_verify; char *card_name; char *args; uint32_t module; @@ -99,34 +99,150 @@ static const char *path_get_card_id(const char *path) { return e + 5; } +static pa_bool_t is_card_busy(const char *id) { + char *card_path = NULL, *pcm_path = NULL, *sub_status = NULL; + DIR *card_dir = NULL, *pcm_dir = NULL; + FILE *status_file = NULL; + size_t len; + struct dirent *space = NULL, *de; + pa_bool_t busy = FALSE; + int r; + + pa_assert(id); + + card_path = pa_sprintf_malloc("/proc/asound/card%s", id); + + if (!(card_dir = opendir(card_path))) { + pa_log_warn("Failed to open %s: %s", card_path, pa_cstrerror(errno)); + goto fail; + } + + len = offsetof(struct dirent, d_name) + fpathconf(dirfd(card_dir), _PC_NAME_MAX) + 1; + space = pa_xmalloc(len); + + for (;;) { + de = NULL; + + if ((r = readdir_r(card_dir, space, &de)) != 0) { + pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r)); + goto fail; + } + + if (!de) + break; + + if (!pa_startswith(de->d_name, "pcm")) + continue; + + pa_xfree(pcm_path); + pcm_path = pa_sprintf_malloc("%s/%s", card_path, de->d_name); + + if (pcm_dir) + closedir(pcm_dir); + + if (!(pcm_dir = opendir(pcm_path))) { + pa_log_warn("Failed to open %s: %s", pcm_path, pa_cstrerror(errno)); + continue; + } + + for (;;) { + char line[32]; + + if ((r = readdir_r(pcm_dir, space, &de)) != 0) { + pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r)); + goto fail; + } + + if (!de) + break; + + if (!pa_startswith(de->d_name, "sub")) + continue; + + pa_xfree(sub_status); + sub_status = pa_sprintf_malloc("%s/%s/status", pcm_path, de->d_name); + + if (status_file) + fclose(status_file); + + if (!(status_file = fopen(sub_status, "r"))) { + pa_log_warn("Failed to open %s: %s", sub_status, pa_cstrerror(errno)); + continue; + } + + if (!(fgets(line, sizeof(line)-1, status_file))) { + pa_log_warn("Failed to read from %s: %s", sub_status, pa_cstrerror(errno)); + continue; + } + + if (!pa_streq(line, "closed\n")) { + busy = TRUE; + break; + } + } + } + +fail: + + pa_xfree(card_path); + pa_xfree(pcm_path); + pa_xfree(sub_status); + pa_xfree(space); + + if (card_dir) + closedir(card_dir); + + if (pcm_dir) + closedir(pcm_dir); + + if (status_file) + fclose(status_file); + + return busy; +} + static void verify_access(struct userdata *u, struct device *d) { char *cd; pa_card *card; + pa_bool_t accessible; pa_assert(u); pa_assert(d); cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path)); - d->accessible = access(cd, R_OK|W_OK) >= 0; + accessible = access(cd, R_OK|W_OK) >= 0; + pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible)); - pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible)); pa_xfree(cd); if (d->module == PA_INVALID_INDEX) { - /* If we not loaded, try to load */ + /* If we are not loaded, try to load */ - if (d->accessible) { + if (accessible) { pa_module *m; - - pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); - m = pa_module_load(u->core, "module-alsa-card", d->args); - - if (m) { - d->module = m->index; - pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name); - } else - pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name); + pa_bool_t busy; + + /* Check if any of the PCM devices that belong to this + * card are currently busy. If they are, don't try to load + * right now, to make sure the probing phase can + * successfully complete. When the current user of the + * device closes it we will get another notification via + * inotify and can then recheck. */ + + busy = is_card_busy(path_get_card_id(d->path)); + pa_log_debug("%s is busy: %s", d->path, pa_yes_no(busy)); + + if (!busy) { + pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); + m = pa_module_load(u->core, "module-alsa-card", d->args); + + if (m) { + d->module = m->index; + pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name); + } else + pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name); + } } } else { @@ -135,7 +251,7 @@ static void verify_access(struct userdata *u, struct device *d) { * accessible boolean */ if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) - pa_card_suspend(card, !d->accessible, PA_SUSPEND_SESSION); + pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION); } } @@ -160,7 +276,6 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { d = pa_xnew0(struct device, 1); d->path = pa_xstrdup(path); - d->accessible = TRUE; d->module = PA_INVALID_INDEX; if (!(t = udev_device_get_property_value(dev, "PULSE_NAME"))) -- cgit From 80c693730365c1a375a5c0e781f38e7f165b37bf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2009 22:34:42 +0200 Subject: alsa: increase interval between smoother updates exponentially for alsa sources, following the scheme for sinks --- src/modules/alsa/alsa-sink.c | 8 +++----- src/modules/alsa/alsa-source.c | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index c3694729..b99ed782 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -733,10 +733,9 @@ static void update_smoother(struct userdata *u) { now1 = pa_rtclock_now(); /* check if the time since the last update is bigger than the interval */ - if (u->last_smoother_update > 0) { + if (u->last_smoother_update > 0) if (u->last_smoother_update + u->smoother_interval > now1) return; - } position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size); @@ -745,11 +744,11 @@ static void update_smoother(struct userdata *u) { now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec); + pa_smoother_put(u->smoother, now1, now2); + u->last_smoother_update = now1; /* exponentially increase the update interval up to the MAX limit */ u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL); - - pa_smoother_put(u->smoother, now1, now2); } static pa_usec_t sink_get_latency(struct userdata *u) { @@ -927,7 +926,6 @@ static int unsuspend(struct userdata *u) { u->first = TRUE; u->since_start = 0; - pa_log_info("Resumed successfully..."); return 0; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 7da37553..336027a2 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -65,6 +65,9 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */ + #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) struct userdata { @@ -108,6 +111,8 @@ struct userdata { pa_smoother *smoother; uint64_t read_count; + pa_usec_t smoother_interval; + pa_usec_t last_smoother_update; pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; @@ -691,15 +696,23 @@ static void update_smoother(struct userdata *u) { now1 = pa_timespec_load(&htstamp); } - position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size); - /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ if (now1 <= 0) now1 = pa_rtclock_now(); + /* check if the time since the last update is bigger than the interval */ + if (u->last_smoother_update > 0) + if (u->last_smoother_update + u->smoother_interval > now1) + return; + + position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size); now2 = pa_bytes_to_usec(position, &u->source->sample_spec); pa_smoother_put(u->smoother, now1, now2); + + u->last_smoother_update = now1; + /* exponentially increase the update interval up to the MAX limit */ + u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL); } static pa_usec_t source_get_latency(struct userdata *u) { @@ -862,6 +875,8 @@ static int unsuspend(struct userdata *u) { u->read_count = 0; pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; + u->last_smoother_update = 0; pa_log_info("Resumed successfully..."); @@ -1469,6 +1484,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p 5, pa_rtclock_now(), FALSE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; dev_id = pa_modargs_get_value( ma, "device_id", -- cgit From 050a3a99e1d151b4f55c89f82073ef33f3399646 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2009 03:26:56 +0200 Subject: alsa: automatically decrease watermark after a time of stability --- src/modules/alsa/alsa-sink.c | 146 +++++++++++++++++++++++++++++++---------- src/modules/alsa/alsa-source.c | 145 ++++++++++++++++++++++++++++++---------- 2 files changed, 223 insertions(+), 68 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index b99ed782..07d53880 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -62,14 +62,21 @@ /* #define DEBUG_TIMING */ #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s -- Overall buffer size */ -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms -- Fill up when only this much is left in the buffer */ -#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */ -#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ -#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ -#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */ -#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update inteval */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s -- Overall buffer size */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms -- Fill up when only this much is left in the buffer */ + +#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */ +#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms -- When everything's great, decrease watermark by this */ +#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s -- How long after a drop out recheck if things are good now */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms -- If the buffer level ever below this theshold, increase the watermark */ +#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this theshold in the verification time, decrease the watermark */ + +#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ +#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ + +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update inteval */ #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ @@ -99,7 +106,12 @@ struct userdata { hwbuf_unused, min_sleep, min_wakeup, - watermark_step; + watermark_inc_step, + watermark_dec_step, + watermark_inc_threshold, + watermark_dec_threshold; + + pa_usec_t watermark_dec_not_before; unsigned nfragments; pa_memchunk memchunk; @@ -248,6 +260,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; max_use_2 = pa_frame_align(max_use/2, &u->sink->sample_spec); @@ -262,6 +275,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) { static void fix_tsched_watermark(struct userdata *u) { size_t max_use; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; @@ -272,7 +286,7 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = u->min_wakeup; } -static void adjust_after_underrun(struct userdata *u) { +static void increase_watermark(struct userdata *u) { size_t old_watermark; pa_usec_t old_min_latency, new_min_latency; @@ -281,31 +295,64 @@ static void adjust_after_underrun(struct userdata *u) { /* First, just try to increase the watermark */ old_watermark = u->tsched_watermark; - u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step); + u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step); fix_tsched_watermark(u); if (old_watermark != u->tsched_watermark) { - pa_log_notice("Increasing wakeup watermark to %0.2f ms", - (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + pa_log_info("Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); return; } /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */ old_min_latency = u->sink->thread_info.min_latency; - new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC); + new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC); new_min_latency = PA_MIN(new_min_latency, u->sink->thread_info.max_latency); if (old_min_latency != new_min_latency) { - pa_log_notice("Increasing minimal latency to %0.2f ms", - (double) new_min_latency / PA_USEC_PER_MSEC); + pa_log_info("Increasing minimal latency to %0.2f ms", + (double) new_min_latency / PA_USEC_PER_MSEC); pa_sink_set_latency_range_within_thread(u->sink, new_min_latency, u->sink->thread_info.max_latency); - return; } /* When we reach this we're officialy fucked! */ } +static void decrease_watermark(struct userdata *u) { + size_t old_watermark; + pa_usec_t now; + + pa_assert(u); + pa_assert(u->use_tsched); + + now = pa_rtclock_now(); + + if (u->watermark_dec_not_before <= 0) + goto restart; + + if (u->watermark_dec_not_before > now) + return; + + old_watermark = u->tsched_watermark; + + if (u->tsched_watermark < u->watermark_dec_step) + u->tsched_watermark = u->tsched_watermark / 2; + else + u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step); + + fix_tsched_watermark(u); + + if (old_watermark != u->tsched_watermark) + pa_log_info("Decreasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + + /* We don't change the latency range*/ + +restart: + u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC; +} + static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) { pa_usec_t usec, wm; @@ -313,6 +360,7 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p pa_assert(process_usec); pa_assert(u); + pa_assert(u->use_tsched); usec = pa_sink_get_requested_latency_within_thread(u->sink); @@ -360,7 +408,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { return 0; } -static size_t check_left_to_play(struct userdata *u, size_t n_bytes) { +static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_play; /* We use <= instead of < for this check here because an underrun @@ -368,34 +416,55 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes) { * it is removed from the buffer. This is particularly important * when block transfer is used. */ - if (n_bytes <= u->hwbuf_size) { + if (n_bytes <= u->hwbuf_size) left_to_play = u->hwbuf_size - n_bytes; + else { + + /* We got a dropout. What a mess! */ + left_to_play = 0; #ifdef DEBUG_TIMING - pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + PA_DEBUG_TRAP; #endif - } else { - left_to_play = 0; + if (!u->first && !u->after_rewind) + if (pa_log_ratelimit()) + pa_log_info("Underrun!"); + } #ifdef DEBUG_TIMING - PA_DEBUG_TRAP; + pa_log_debug("%0.2f ms left to play; inc threshold = %0.2f ms; dec threshold = %0.2f ms", + (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(u->watermark_inc_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(u->watermark_dec_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC); #endif + if (u->use_tsched) { + pa_bool_t reset_not_before = TRUE; + if (!u->first && !u->after_rewind) { + if (left_to_play < u->watermark_inc_threshold) + increase_watermark(u); + else if (left_to_play > u->watermark_dec_threshold) { + reset_not_before = FALSE; - if (pa_log_ratelimit()) - pa_log_info("Underrun!"); + /* We decrease the watermark only if have actually + * been woken up by a timeout. If something else woke + * us up it's too easy to fulfill the deadlines... */ - if (u->use_tsched) - adjust_after_underrun(u); + if (on_timeout) + decrease_watermark(u); + } } + + if (reset_not_before) + u->watermark_dec_not_before = 0; } return left_to_play; } -static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) { pa_bool_t work_done = TRUE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_play; @@ -430,7 +499,8 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle pa_log_debug("avail: %lu", (unsigned long) n_bytes); #endif - left_to_play = check_left_to_play(u, n_bytes); + left_to_play = check_left_to_play(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) @@ -565,7 +635,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle return work_done ? 1 : 0; } -static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) { pa_bool_t work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_play; @@ -591,7 +661,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle } n_bytes = (size_t) n * u->frame_size; - left_to_play = check_left_to_play(u, n_bytes); + left_to_play = check_left_to_play(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) @@ -1278,15 +1349,16 @@ static void thread_func(void *userdata) { if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { int work_done; pa_usec_t sleep_usec = 0; + pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll); if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) if (process_rewind(u) < 0) goto fail; if (u->use_mmap) - work_done = mmap_write(u, &sleep_usec, revents & POLLOUT); + work_done = mmap_write(u, &sleep_usec, revents & POLLOUT, on_timeout); else - work_done = unix_write(u, &sleep_usec, revents & POLLOUT); + work_done = unix_write(u, &sleep_usec, revents & POLLOUT, on_timeout); if (work_done < 0) goto fail; @@ -1787,7 +1859,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; - u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec); pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels); pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", @@ -1798,7 +1869,13 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_sink_set_max_rewind(u->sink, u->hwbuf_size); if (u->use_tsched) { - u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->sink->sample_spec); + u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec); + + u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->sink->sample_spec); + u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->sink->sample_spec); + + u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->sink->sample_spec); + u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->sink->sample_spec); fix_min_sleep_wakeup(u); fix_tsched_watermark(u); @@ -1812,6 +1889,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca } else pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss)); + reserve_update(u); if (update_sw_params(u) < 0) diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 336027a2..165b2e3b 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -59,14 +59,22 @@ /* #define DEBUG_TIMING */ #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ -#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ -#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */ -#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ + +#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ +#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms */ +#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */ +#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ + +#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ +#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ + +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */ #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) @@ -96,7 +104,12 @@ struct userdata { hwbuf_unused, min_sleep, min_wakeup, - watermark_step; + watermark_inc_step, + watermark_dec_step, + watermark_inc_threshold, + watermark_dec_threshold; + + pa_usec_t watermark_dec_not_before; unsigned nfragments; @@ -241,6 +254,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) { static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; max_use_2 = pa_frame_align(max_use/2, &u->source->sample_spec); @@ -255,6 +269,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) { static void fix_tsched_watermark(struct userdata *u) { size_t max_use; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; @@ -265,7 +280,7 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = u->min_wakeup; } -static void adjust_after_overrun(struct userdata *u) { +static void increase_watermark(struct userdata *u) { size_t old_watermark; pa_usec_t old_min_latency, new_min_latency; @@ -274,36 +289,72 @@ static void adjust_after_overrun(struct userdata *u) { /* First, just try to increase the watermark */ old_watermark = u->tsched_watermark; - u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step); - + u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step); fix_tsched_watermark(u); if (old_watermark != u->tsched_watermark) { - pa_log_notice("Increasing wakeup watermark to %0.2f ms", - (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + pa_log_info("Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); return; } /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */ old_min_latency = u->source->thread_info.min_latency; - new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC); + new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC); new_min_latency = PA_MIN(new_min_latency, u->source->thread_info.max_latency); if (old_min_latency != new_min_latency) { - pa_log_notice("Increasing minimal latency to %0.2f ms", - (double) new_min_latency / PA_USEC_PER_MSEC); + pa_log_info("Increasing minimal latency to %0.2f ms", + (double) new_min_latency / PA_USEC_PER_MSEC); pa_source_set_latency_range_within_thread(u->source, new_min_latency, u->source->thread_info.max_latency); - return; } /* When we reach this we're officialy fucked! */ } +static void decrease_watermark(struct userdata *u) { + size_t old_watermark; + pa_usec_t now; + + pa_assert(u); + pa_assert(u->use_tsched); + + now = pa_rtclock_now(); + + if (u->watermark_dec_not_before <= 0) + goto restart; + + if (u->watermark_dec_not_before > now) + return; + + old_watermark = u->tsched_watermark; + + if (u->tsched_watermark < u->watermark_dec_step) + u->tsched_watermark = u->tsched_watermark / 2; + else + u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step); + + fix_tsched_watermark(u); + + if (old_watermark != u->tsched_watermark) + pa_log_info("Decreasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + + /* We don't change the latency range*/ + +restart: + u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC; +} + static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) { pa_usec_t wm, usec; + pa_assert(sleep_usec); + pa_assert(process_usec); + pa_assert(u); + pa_assert(u->use_tsched); usec = pa_source_get_requested_latency_within_thread(u->source); @@ -352,7 +403,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { return 0; } -static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { +static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_record; size_t rec_space = u->hwbuf_size - u->hwbuf_unused; @@ -361,14 +412,11 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { * it is removed from the buffer. This is particularly important * when block transfer is used. */ - if (n_bytes <= rec_space) { + if (n_bytes <= rec_space) left_to_record = rec_space - n_bytes; + else { -#ifdef DEBUG_TIMING - pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); -#endif - - } else { + /* We got a dropout. What a mess! */ left_to_record = 0; #ifdef DEBUG_TIMING @@ -377,15 +425,36 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { if (pa_log_ratelimit()) pa_log_info("Overrun!"); + } - if (u->use_tsched) - adjust_after_overrun(u); +#ifdef DEBUG_TIMING + pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); +#endif + + if (u->use_tsched) { + pa_bool_t reset_not_before = TRUE; + + if (left_to_record < u->watermark_inc_threshold) + increase_watermark(u); + else if (left_to_record > u->watermark_dec_threshold) { + reset_not_before = FALSE; + + /* We decrease the watermark only if have actually been + * woken up by a timeout. If something else woke us up + * it's too easy to fulfill the deadlines... */ + + if (on_timeout) + decrease_watermark(u); + } + + if (reset_not_before) + u->watermark_dec_not_before = 0; } return left_to_record; } -static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) { pa_bool_t work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; @@ -417,7 +486,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled pa_log_debug("avail: %lu", (unsigned long) n_bytes); #endif - left_to_record = check_left_to_record(u, n_bytes); + left_to_record = check_left_to_record(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) if (!polled && @@ -543,7 +613,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled return work_done ? 1 : 0; } -static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) { int work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; @@ -570,7 +640,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled } n_bytes = (size_t) n * u->frame_size; - left_to_record = check_left_to_record(u, n_bytes); + left_to_record = check_left_to_record(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) if (!polled && @@ -1158,11 +1229,12 @@ static void thread_func(void *userdata) { if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { int work_done; pa_usec_t sleep_usec = 0; + pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll); if (u->use_mmap) - work_done = mmap_read(u, &sleep_usec, revents & POLLIN); + work_done = mmap_read(u, &sleep_usec, revents & POLLIN, on_timeout); else - work_done = unix_read(u, &sleep_usec, revents & POLLIN); + work_done = unix_read(u, &sleep_usec, revents & POLLIN, on_timeout); if (work_done < 0) goto fail; @@ -1632,7 +1704,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; - u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", @@ -1640,7 +1711,13 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); if (u->use_tsched) { - u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->source->sample_spec); + u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); + + u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec); + u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec); + + u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec); + u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec); fix_min_sleep_wakeup(u); fix_tsched_watermark(u); -- cgit From 65c3e6576c635c06103fe44a4987b36de81db7a9 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Thu, 30 Jul 2009 03:18:15 -0300 Subject: bluetooth: handle bluetooth source --- src/modules/bluetooth/module-bluetooth-device.c | 181 ++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 13 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 4e23862c..b8a88042 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1,7 +1,7 @@ /*** This file is part of PulseAudio. - Copyright 2008 Joao Paulo Rechi Vita + Copyright 2008-2009 Joao Paulo Rechi Vita PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -129,6 +129,7 @@ struct hsp_info { enum profile { PROFILE_A2DP, + PROFILE_A2DP_SOURCE, PROFILE_HSP, PROFILE_OFF }; @@ -178,6 +179,7 @@ struct userdata { }; #define FIXED_LATENCY_PLAYBACK_A2DP (25*PA_USEC_PER_MSEC) +#define FIXED_LATENCY_RECORD_A2DP (25*PA_USEC_PER_MSEC) #define FIXED_LATENCY_PLAYBACK_HSP (125*PA_USEC_PER_MSEC) #define FIXED_LATENCY_RECORD_HSP (25*PA_USEC_PER_MSEC) @@ -307,7 +309,7 @@ static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capa pa_log_debug("Payload size is %lu %lu", (unsigned long) bytes_left, (unsigned long) sizeof(*codec)); - if ((u->profile == PROFILE_A2DP && codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) || + if (((u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) && codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) || (u->profile == PROFILE_HSP && codec->transport != BT_CAPABILITIES_TRANSPORT_SCO)) { pa_log_error("Got capabilities for wrong codec."); return -1; @@ -340,6 +342,26 @@ static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capa pa_assert(codec->type == BT_A2DP_SBC_SINK); + if (codec->configured && seid == 0) + return codec->seid; + + memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities)); + + } else if (u->profile == PROFILE_A2DP_SOURCE) { + + while (bytes_left > 0) { + if ((codec->type == BT_A2DP_SBC_SOURCE) && !codec->lock) + break; + + bytes_left -= codec->length; + codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length); + } + + if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities)) + return -1; + + pa_assert(codec->type == BT_A2DP_SBC_SOURCE); + if (codec->configured && seid == 0) return codec->seid; @@ -368,7 +390,7 @@ static int get_caps(struct userdata *u, uint8_t seid) { msg.getcaps_req.seid = seid; pa_strlcpy(msg.getcaps_req.object, u->path, sizeof(msg.getcaps_req.object)); - if (u->profile == PROFILE_A2DP) + if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP; else { pa_assert(u->profile == PROFILE_HSP); @@ -451,7 +473,7 @@ static int setup_a2dp(struct userdata *u) { }; pa_assert(u); - pa_assert(u->profile == PROFILE_A2DP); + pa_assert(u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE); cap = &u->a2dp.sbc_capabilities; @@ -652,8 +674,8 @@ static int set_conf(struct userdata *u) { msg.open_req.h.length = sizeof(msg.open_req); pa_strlcpy(msg.open_req.object, u->path, sizeof(msg.open_req.object)); - msg.open_req.seid = u->profile == PROFILE_A2DP ? u->a2dp.sbc_capabilities.capability.seid : BT_A2DP_SEID_RANGE + 1; - msg.open_req.lock = u->profile == PROFILE_A2DP ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK; + msg.open_req.seid = (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) ? u->a2dp.sbc_capabilities.capability.seid : BT_A2DP_SEID_RANGE + 1; + msg.open_req.lock = (u->profile == PROFILE_A2DP) ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK; if (service_send(u, &msg.open_req.h) < 0) return -1; @@ -661,7 +683,7 @@ static int set_conf(struct userdata *u) { if (service_expect(u, &msg.open_rsp.h, sizeof(msg), BT_OPEN, sizeof(msg.open_rsp)) < 0) return -1; - if (u->profile == PROFILE_A2DP ) { + if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { u->sample_spec.format = PA_SAMPLE_S16LE; if (setup_a2dp(u) < 0) @@ -679,7 +701,7 @@ static int set_conf(struct userdata *u) { msg.setconf_req.h.name = BT_SET_CONFIGURATION; msg.setconf_req.h.length = sizeof(msg.setconf_req); - if (u->profile == PROFILE_A2DP) { + if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities, sizeof(u->a2dp.sbc_capabilities)); } else { msg.setconf_req.codec.transport = BT_CAPABILITIES_TRANSPORT_SCO; @@ -697,7 +719,7 @@ static int set_conf(struct userdata *u) { u->link_mtu = msg.setconf_rsp.link_mtu; /* setup SBC encoder now we agree on parameters */ - if (u->profile == PROFILE_A2DP) { + if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { setup_sbc(&u->a2dp); u->block_size = @@ -1250,6 +1272,119 @@ static int a2dp_process_render(struct userdata *u) { return ret; } +static int a2dp_process_push(struct userdata *u) { + int ret = 0; + pa_memchunk memchunk; + + pa_assert(u); + pa_assert(u->profile == PROFILE_A2DP_SOURCE); + pa_assert(u->source); + pa_assert(u->read_smoother); + + memchunk.memblock = pa_memblock_new(u->core->mempool, u->block_size); + memchunk.index = memchunk.length = 0; + + for (;;) { + pa_bool_t found_tstamp = FALSE; + pa_usec_t tstamp; + struct a2dp_info *a2dp; + struct rtp_header *header; + struct rtp_payload *payload; + const void *p; + void *d; + ssize_t l; + size_t to_write, to_decode; + unsigned frame_count; + + a2dp_prepare_buffer(u); + + a2dp = &u->a2dp; + header = a2dp->buffer; + payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header)); + + l = pa_read(u->stream_fd, a2dp->buffer, a2dp->buffer_size, &u->stream_write_type); + + if (l <= 0) { + + if (l < 0 && errno == EINTR) + /* Retry right away if we got interrupted */ + continue; + + else if (l < 0 && errno == EAGAIN) + /* Hmm, apparently the socket was not readable, give up for now. */ + break; + + pa_log_error("Failed to read data from socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); + ret = -1; + break; + } + + pa_assert((size_t) l <= a2dp->buffer_size); + + u->read_index += (uint64_t) l; + + /* TODO: get timestamp from rtp */ + if (!found_tstamp) { + /* pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); */ + tstamp = pa_rtclock_now(); + } + + pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); + pa_smoother_resume(u->read_smoother, tstamp, TRUE); + + p = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload); + to_decode = l - sizeof(*header) - sizeof(*payload); + + d = pa_memblock_acquire(memchunk.memblock); + to_write = memchunk.length = pa_memblock_get_length(memchunk.memblock); + + while (PA_LIKELY(to_decode > 0 && to_write > 0)) { + size_t written; + ssize_t decoded; + + decoded = sbc_decode(&a2dp->sbc, + p, to_decode, + d, to_write, + &written); + + if (PA_UNLIKELY(decoded <= 0)) { + pa_log_error("SBC decoding error (%li)", (long) decoded); + pa_memblock_release(memchunk.memblock); + pa_memblock_unref(memchunk.memblock); + return -1; + } + +/* pa_log_debug("SBC: decoded: %lu; written: %lu", (unsigned long) decoded, (unsigned long) written); */ +/* pa_log_debug("SBC: frame_length: %lu; codesize: %lu", (unsigned long) a2dp->frame_length, (unsigned long) a2dp->codesize); */ + + pa_assert_fp((size_t) decoded <= to_decode); + pa_assert_fp((size_t) decoded == a2dp->frame_length); + + pa_assert_fp((size_t) written <= to_write); + pa_assert_fp((size_t) written == a2dp->codesize); + + p = (const uint8_t*) p + decoded; + to_decode -= decoded; + + d = (uint8_t*) d + written; + to_write -= written; + + frame_count++; + } + + pa_memblock_release(memchunk.memblock); + + pa_source_post(u->source, &memchunk); + + ret = 1; + break; + } + + pa_memblock_unref(memchunk.memblock); + + return ret; +} + static void thread_func(void *userdata) { struct userdata *u = userdata; unsigned do_write = 0; @@ -1285,7 +1420,12 @@ static void thread_func(void *userdata) { if (pollfd && (pollfd->revents & POLLIN)) { int n_read; - if ((n_read = hsp_process_push(u)) < 0) + if (u->profile == PROFILE_HSP) + n_read = hsp_process_push(u); + else + n_read = a2dp_process_push(u); + + if (n_read < 0) goto fail; /* We just read something, so we are supposed to write something, too */ @@ -1687,7 +1827,7 @@ static int add_source(struct userdata *u) { data.driver = __FILE__; 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"); + pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP_SOURCE ? "a2dp_source" : "hsp"); if (u->profile == PROFILE_HSP) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); data.card = u->card; @@ -1712,7 +1852,7 @@ static int add_source(struct userdata *u) { u->source->parent.process_msg = source_process_msg; pa_source_set_fixed_latency(u->source, - (/* u->profile == PROFILE_A2DP ? FIXED_LATENCY_RECORD_A2DP : */ FIXED_LATENCY_RECORD_HSP) + + (u->profile == PROFILE_A2DP_SOURCE ? FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_HSP) + pa_bytes_to_usec(u->block_size, &u->sample_spec)); } @@ -1809,7 +1949,8 @@ static int init_profile(struct userdata *u) { if (add_sink(u) < 0) r = -1; - if (u->profile == PROFILE_HSP) + if (u->profile == PROFILE_HSP || + u->profile == PROFILE_A2DP_SOURCE) if (add_source(u) < 0) r = -1; @@ -2050,6 +2191,20 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) { pa_hashmap_put(data.profiles, p->name, p); } + if (pa_bluetooth_uuid_has(device->uuids, A2DP_SOURCE_UUID)) { + p = pa_card_profile_new("a2dp_source", _("High Fidelity Capture (A2DP)"), sizeof(enum profile)); + p->priority = 10; + p->n_sinks = 0; + p->n_sources = 1; + p->max_sink_channels = 0; + p->max_source_channels = 2; + + d = PA_CARD_PROFILE_DATA(p); + *d = PROFILE_A2DP_SOURCE; + + pa_hashmap_put(data.profiles, p->name, p); + } + if (pa_bluetooth_uuid_has(device->uuids, HSP_HS_UUID) || pa_bluetooth_uuid_has(device->uuids, HFP_HS_UUID)) { p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile)); -- cgit From 2772521698caa4ffe7c0c2de174e9d901d7752fd Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Fri, 24 Jul 2009 21:44:36 -0300 Subject: bluetooth: add discover of bluetooth sources --- src/modules/bluetooth/bluetooth-util.c | 26 +++++++++++++++++------ src/modules/bluetooth/bluetooth-util.h | 7 ++++-- src/modules/bluetooth/module-bluetooth-discover.c | 8 +++++-- 3 files changed, 31 insertions(+), 10 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index f576823d..d0c89aab 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -1,7 +1,7 @@ /*** This file is part of PulseAudio. - Copyright 2008 Joao Paulo Rechi Vita + Copyright 2008-2009 Joao Paulo Rechi Vita PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -95,6 +95,7 @@ static pa_bluetooth_device* device_new(const char *path) { d->audio_state = PA_BT_AUDIO_STATE_INVALID; d->audio_sink_state = PA_BT_AUDIO_STATE_INVALID; + d->audio_source_state = PA_BT_AUDIO_STATE_INVALID; d->headset_state = PA_BT_AUDIO_STATE_INVALID; return d; @@ -124,6 +125,7 @@ static pa_bool_t device_is_audio(pa_bluetooth_device *d) { d->device_info_valid && (d->audio_state != PA_BT_AUDIO_STATE_INVALID && (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID || + d->audio_source_state != PA_BT_AUDIO_STATE_INVALID || d->headset_state != PA_BT_AUDIO_STATE_INVALID)); } @@ -233,6 +235,9 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device } else if (strcasecmp(A2DP_SINK_UUID, value) == 0) { pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSink", "GetProperties")); send_and_add_to_pending(y, d, m, get_properties_reply); + } else if (strcasecmp(A2DP_SOURCE_UUID, value) == 0) { + pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSource", "GetProperties")); + send_and_add_to_pending(y, d, m, get_properties_reply); } /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */ @@ -278,7 +283,7 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessa dbus_message_iter_recurse(i, &variant_i); -/* pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|Headset}.%s", key); */ +/* pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|AudioSource|Headset}.%s", key); */ switch (dbus_message_iter_get_arg_type(&variant_i)) { @@ -390,6 +395,9 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) { } else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) { if (parse_audio_property(y, &d->audio_sink_state, &dict_i) < 0) goto finish; + } else if (dbus_message_has_interface(p->message, "org.bluez.AudioSource")) { + if (parse_audio_property(y, &d->audio_source_state, &dict_i) < 0) + goto finish; } } @@ -440,8 +448,8 @@ static void found_device(pa_bluetooth_discovery *y, const char* path) { pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties")); send_and_add_to_pending(y, d, m, get_properties_reply); - /* Before we read the other properties (Audio, AudioSink, Headset) we wait - * that the UUID is read */ + /* Before we read the other properties (Audio, AudioSink, AudioSource, + * Headset) we wait that the UUID is read */ } static void list_devices_reply(DBusPendingCall *pending, void *userdata) { @@ -616,6 +624,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us } else if (dbus_message_is_signal(m, "org.bluez.Audio", "PropertyChanged") || dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") || dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") || + dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged") || dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) { pa_bluetooth_device *d; @@ -643,6 +652,9 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us } else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) { if (parse_audio_property(y, &d->audio_sink_state, &arg_i) < 0) goto fail; + } else if (dbus_message_has_interface(m, "org.bluez.AudioSource")) { + if (parse_audio_property(y, &d->audio_source_state, &arg_i) < 0) + goto fail; } run_callback(y, d, FALSE); @@ -765,7 +777,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", - "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) { + "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", NULL) < 0) { pa_log("Failed to add D-Bus matches: %s", err.message); goto fail; } @@ -817,7 +830,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", - "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL); + "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", NULL); dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y); diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h index f15f2170..e2a0c3d5 100644 --- a/src/modules/bluetooth/bluetooth-util.h +++ b/src/modules/bluetooth/bluetooth-util.h @@ -4,7 +4,7 @@ /*** This file is part of PulseAudio. - Copyright 2008 Joao Paulo Rechi Vita + Copyright 2008-2009 Joao Paulo Rechi Vita PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -53,7 +53,7 @@ struct pa_bluetooth_uuid { PA_LLIST_FIELDS(pa_bluetooth_uuid); }; -/* This enum is shared among Audio, Headset, and AudioSink, although not all values are acceptable in all profiles */ +/* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */ typedef enum pa_bt_audio_state { PA_BT_AUDIO_STATE_INVALID = -1, PA_BT_AUDIO_STATE_DISCONNECTED, @@ -84,6 +84,9 @@ struct pa_bluetooth_device { /* AudioSink state */ pa_bt_audio_state_t audio_sink_state; + /* AudioSource state */ + pa_bt_audio_state_t audio_source_state; + /* Headset state */ pa_bt_audio_state_t headset_state; }; diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index 788fee00..7571e48a 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -1,7 +1,7 @@ /*** This file is part of PulseAudio. - Copyright 2008 Joao Paulo Rechi Vita + Copyright 2008-2009 Joao Paulo Rechi Vita PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -83,8 +83,9 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const mi = pa_hashmap_get(u->hashmap, d->path); + pa_log("dead: %d, device_connected: %d, audio_state: %d, audio_source_state: %d", d->dead, d->device_connected, d->audio_state, d->audio_source_state); if (!d->dead && - d->device_connected > 0 && d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED) { + d->device_connected > 0 && (d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED || d->audio_source_state >= PA_BT_AUDIO_STATE_CONNECTED)) { if (!mi) { pa_module *m = NULL; @@ -116,6 +117,9 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const } #endif + if (d->audio_source_state >= PA_BT_AUDIO_STATE_CONNECTED) + args = pa_sprintf_malloc("%s profile=\"a2dp_source\"", args); + pa_log_debug("Loading module-bluetooth-device %s", args); m = pa_module_load(u->module->core, "module-bluetooth-device", args); pa_xfree(args); -- cgit From 419b071a1eaf96870021b894f837fbf3da74ca80 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2009 16:35:19 +0200 Subject: detect: recommend module-udev-detect instead of module-hal-detect --- src/modules/module-detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c index 956fe4c5..b1f24e15 100644 --- a/src/modules/module-detect.c +++ b/src/modules/module-detect.c @@ -50,7 +50,7 @@ PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_USAGE("just-one="); -PA_MODULE_DEPRECATED("Please use module-hal-detect instead of module-detect!"); +PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-detect!"); static const char* const valid_modargs[] = { "just-one", -- cgit From 457b973ba69d1a417d438b8716389464eb62e049 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Tue, 25 Aug 2009 03:47:59 +1000 Subject: Solaris: debug my latest enbugging, take 2 Prevent partially played memchunks from getting lost. If the sink has a memblock, don't leak it when rewinding. --- src/modules/module-solaris.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 71f14071..b0d4db43 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -600,6 +600,10 @@ static void process_rewind(struct userdata *u) { 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; + if (u->memchunk.length <= 0 && u->memchunk.memblock) { + pa_memblock_unref(u->memchunk.memblock); + pa_memchunk_reset(&u->memchunk); + } pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); } @@ -671,8 +675,8 @@ static void thread_func(void *userdata) { if (len < (size_t) u->minimum_request) break; - if (u->memchunk.length < len) - pa_sink_render(u->sink, len - u->memchunk.length, &u->memchunk); + if (!u->memchunk.length) + pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk); len = PA_MIN(u->memchunk.length, len); -- cgit From ea4b65b8e0202755dddf20cd5a46dafea86bb29c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Aug 2009 01:42:17 +0200 Subject: loopback: add loopback module for direct connections of sinks and sources --- src/modules/module-loopback.c | 706 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 706 insertions(+) create mode 100644 src/modules/module-loopback.c (limited to 'src/modules') diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c new file mode 100644 index 00000000..8b22fa1a --- /dev/null +++ b/src/modules/module-loopback.c @@ -0,0 +1,706 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Intel Corporation + Contributor: Pierre-Louis Bossart + + 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 +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "module-loopback-symdef.h" + +PA_MODULE_AUTHOR("Pierre-Louis Bossart"); +PA_MODULE_DESCRIPTION("Loopback from source to sink"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( + "source= " + "sink= " + "adjust_time= " + "latency_msec= " + "format= " + "rate= " + "channels= " + "channel_map="); + +#define DEFAULT_LATENCY_MSEC 200 + +#define MEMBLOCKQ_MAXLENGTH (1024*1024*16) + +#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC) + +struct userdata { + pa_core *core; + pa_module *module; + + pa_sink_input *sink_input; + pa_source_output *source_output; + + pa_asyncmsgq *asyncmsgq; + pa_memblockq *memblockq; + + pa_rtpoll_item *rtpoll_item_read, *rtpoll_item_write; + + pa_time_event *time_event; + pa_usec_t adjust_time; + + int64_t recv_counter; + int64_t send_counter; + + size_t skip; + pa_usec_t latency; + + pa_bool_t in_pop; + size_t min_memblockq_length; + + struct { + int64_t send_counter; + size_t source_output_buffer; + pa_usec_t source_latency; + + int64_t recv_counter; + size_t sink_input_buffer; + pa_usec_t sink_latency; + + size_t min_memblockq_length; + size_t max_request; + } latency_snapshot; +}; + +static const char* const valid_modargs[] = { + "source", + "sink", + "latency", + "format", + "rate", + "channels", + "channel_map", + NULL, +}; + +enum { + SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX, + SINK_INPUT_MESSAGE_REWIND, + SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, + SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED +}; + +enum { + SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT +}; + +/* Called from main context */ +static void teardown(struct userdata *u) { + pa_assert(u); + pa_assert_ctl_context(); + + if (u->sink_input) + pa_sink_input_unlink(u->sink_input); + + if (u->source_output) + pa_source_output_unlink(u->source_output); + + if (u->sink_input) { + pa_sink_input_unref(u->sink_input); + u->sink_input = NULL; + } + + if (u->source_output) { + pa_source_output_unref(u->source_output); + u->source_output = NULL; + } +} + +/* Called from main context */ +static void adjust_rates(struct userdata *u) { + size_t buffer, fs; + uint32_t old_rate, base_rate, new_rate; + pa_usec_t buffer_latency; + + pa_assert(u); + pa_assert_ctl_context(); + + pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); + pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); + + buffer = + u->latency_snapshot.sink_input_buffer + + u->latency_snapshot.source_output_buffer; + + if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter) + buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter); + else + buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter)); + + buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec); + + pa_log_info("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", + (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, + (double) buffer_latency / PA_USEC_PER_MSEC, + (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, + ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC); + + pa_log_info("Should buffer %lu bytes, buffered at minimum %lu bytes", + u->latency_snapshot.max_request*2, + u->latency_snapshot.min_memblockq_length); + + fs = pa_frame_size(&u->sink_input->sample_spec); + old_rate = u->sink_input->sample_spec.rate; + base_rate = u->source_output->sample_spec.rate; + + if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2) + new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time; + else + new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time; + + pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate); + + pa_sink_input_set_rate(u->sink_input, new_rate); + + pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); +} + +/* Called from main context */ +static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) { + struct userdata *u = userdata; + + pa_assert(u); + pa_assert(a); + pa_assert(u->time_event == e); + + adjust_rates(u); +} + +/* Called from input thread context */ +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { + struct userdata *u; + pa_memchunk copy; + + pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); + pa_assert_se(u = o->userdata); + + if (u->skip > chunk->length) { + u->skip -= chunk->length; + return; + } + + if (u->skip > 0) { + copy = *chunk; + copy.index += u->skip; + copy.length -= u->skip; + u->skip = 0; + + chunk = © + } + + pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, chunk, NULL); + u->send_counter += (int64_t) chunk->length; +} + +/* Called from input thread context */ +static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) { + struct userdata *u; + + pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); + pa_assert_se(u = o->userdata); + + pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL); + u->send_counter -= (int64_t) nbytes; +} + +/* Called from output thread context */ +static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { + struct userdata *u = PA_SOURCE_OUTPUT(obj)->userdata; + + switch (code) { + + case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT: { + size_t length; + + length = pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq); + + u->latency_snapshot.send_counter = u->send_counter; + u->latency_snapshot.source_output_buffer = u->source_output->thread_info.resampler ? pa_resampler_result(u->source_output->thread_info.resampler, length) : length; + u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source); + + return 0; + } + } + + return pa_source_output_process_msg(obj, code, data, offset, chunk); +} + +/* Called from output thread context */ +static void source_output_attach_cb(pa_source_output *o) { + struct userdata *u; + + pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); + pa_assert_se(u = o->userdata); + + u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( + o->source->thread_info.rtpoll, + PA_RTPOLL_LATE, + u->asyncmsgq); +} + +/* Called from output thread context */ +static void source_output_detach_cb(pa_source_output *o) { + struct userdata *u; + + pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); + pa_assert_se(u = o->userdata); + + if (u->rtpoll_item_write) { + pa_rtpoll_item_free(u->rtpoll_item_write); + u->rtpoll_item_write = NULL; + } +} + +/* Called from output thread context */ +static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) { + struct userdata *u; + + pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); + pa_assert_se(u = o->userdata); + + if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) { + + u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source), + u->latency), + &o->sample_spec); + + pa_log_info("Skipping %lu bytes", (unsigned long) u->skip); + } +} + +/* Called from main thread */ +static void source_output_kill_cb(pa_source_output *o) { + struct userdata *u; + + pa_source_output_assert_ref(o); + pa_assert_ctl_context(); + pa_assert_se(u = o->userdata); + + teardown(u); + pa_module_unload_request(u->module, TRUE); +} + +/* Called from output thread context */ +static void update_min_memblockq_length(struct userdata *u) { + size_t length; + + pa_assert(u); + pa_sink_input_assert_io_context(u->sink_input); + + length = pa_memblockq_get_length(u->memblockq); + + if (u->min_memblockq_length == (size_t) -1 || + length < u->min_memblockq_length) + u->min_memblockq_length = length; +} + +/* Called from output thread context */ +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_sink_input_assert_io_context(i); + pa_assert_se(u = i->userdata); + pa_assert(chunk); + + u->in_pop = TRUE; + while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0) + ; + u->in_pop = FALSE; + + if (pa_memblockq_peek(u->memblockq, chunk) < 0) { + pa_log_info("Coud not peek into queue"); + return -1; + } + + chunk->length = PA_MIN(chunk->length, nbytes); + pa_memblockq_drop(u->memblockq, chunk->length); + + update_min_memblockq_length(u); + + return 0; +} + +/* Called from output thread context */ +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_sink_input_assert_io_context(i); + pa_assert_se(u = i->userdata); + + pa_memblockq_rewind(u->memblockq, nbytes); +} + +/* Called from output thread context */ +static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { + struct userdata *u = PA_SINK_INPUT(obj)->userdata; + + switch (code) { + + case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { + pa_usec_t *r = data; + + pa_sink_input_assert_io_context(u->sink_input); + + *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec); + + /* Fall through, the default handler will add in the extra + * latency added by the resampler */ + break; + } + + case SINK_INPUT_MESSAGE_POST: + + pa_sink_input_assert_io_context(u->sink_input); + + if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state)) + pa_memblockq_push_align(u->memblockq, chunk); + else + pa_memblockq_flush_write(u->memblockq); + + update_min_memblockq_length(u); + + /* Is this the end of an underrun? Then let's start things + * right-away */ + if (!u->in_pop && + u->sink_input->thread_info.underrun_for > 0 && + pa_memblockq_is_readable(u->memblockq)) { + + pa_log_debug("Requesting rewind due to end of underrun."); + pa_sink_input_request_rewind(u->sink_input, + (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for), + FALSE, TRUE, FALSE); + } + + u->recv_counter += (int64_t) chunk->length; + + return 0; + + case SINK_INPUT_MESSAGE_REWIND: + + pa_sink_input_assert_io_context(u->sink_input); + + if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state)) + pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE); + else + pa_memblockq_flush_write(u->memblockq); + + u->recv_counter -= offset; + + update_min_memblockq_length(u); + + return 0; + + case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: { + size_t length; + + update_min_memblockq_length(u); + + length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq); + + u->latency_snapshot.recv_counter = u->recv_counter; + u->latency_snapshot.sink_input_buffer = + pa_memblockq_get_length(u->memblockq) + + (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length); + u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink); + + u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input); + + u->latency_snapshot.min_memblockq_length = u->min_memblockq_length; + u->min_memblockq_length = (size_t) -1; + + return 0; + } + + case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: { + /* This message is sent from the IO thread to the main + * thread! So don't be confused. All the user cases above + * are executed in thread context, but this one is not! */ + + pa_assert_ctl_context(); + + adjust_rates(u); + return 0; + } + } + + return pa_sink_input_process_msg(obj, code, data, offset, chunk); +} + +/* Called from output thread context */ +static void sink_input_attach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_sink_input_assert_io_context(i); + pa_assert_se(u = i->userdata); + + u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( + i->sink->thread_info.rtpoll, + PA_RTPOLL_LATE, + u->asyncmsgq); + + pa_memblockq_set_prebuf(u->memblockq, pa_sink_input_get_max_request(i)*2); + pa_memblockq_set_maxrewind(u->memblockq, pa_sink_input_get_max_rewind(i)); + + u->min_memblockq_length = (size_t) -1; +} + +/* Called from output thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_sink_input_assert_io_context(i); + pa_assert_se(u = i->userdata); + + if (u->rtpoll_item_read) { + pa_rtpoll_item_free(u->rtpoll_item_read); + u->rtpoll_item_read = NULL; + } +} + +/* Called from output thread context */ +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_sink_input_assert_io_context(i); + pa_assert_se(u = i->userdata); + + pa_memblockq_set_maxrewind(u->memblockq, nbytes); +} + +/* Called from output thread context */ +static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_sink_input_assert_io_context(i); + pa_assert_se(u = i->userdata); + + pa_memblockq_set_prebuf(u->memblockq, nbytes*2); + pa_log_info("Max request changed"); + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED, NULL, 0, NULL, NULL); +} + +/* Called from main thread */ +static void sink_input_kill_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert_se(u = i->userdata); + + teardown(u); + pa_module_unload_request(u->module, TRUE); +} + +int pa__init(pa_module *m) { + pa_modargs *ma = NULL; + struct userdata *u; + pa_sink *sink; + pa_sink_input_new_data sink_input_data; + pa_source *source; + pa_source_output_new_data source_output_data; + uint32_t latency_msec; + pa_sample_spec ss; + pa_channel_map map; + pa_memchunk silence; + uint32_t adjust_time_sec; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + if (!(source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) { + pa_log("No such source."); + goto fail; + } + + if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) { + pa_log("No such sink."); + goto fail; + } + + ss = sink->sample_spec; + map = sink->channel_map; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Invalid sample format specification or channel map"); + goto fail; + } + + latency_msec = DEFAULT_LATENCY_MSEC; + if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) { + pa_log("Invalid latency specification"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC; + + adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC; + if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) { + pa_log("Failed to parse adjust_time value"); + goto fail; + } + + if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC) + u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC; + else + u->adjust_time = DEFAULT_ADJUST_TIME_USEC; + + pa_sink_input_new_data_init(&sink_input_data); + sink_input_data.driver = __FILE__; + sink_input_data.module = m; + sink_input_data.sink = sink; + pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)); + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); + pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); + pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); + + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_VARIABLE_RATE); + pa_sink_input_new_data_done(&sink_input_data); + + if (!u->sink_input) + goto fail; + + u->sink_input->parent.process_msg = sink_input_process_msg_cb; + u->sink_input->pop = sink_input_pop_cb; + u->sink_input->process_rewind = sink_input_process_rewind_cb; + u->sink_input->kill = sink_input_kill_cb; + u->sink_input->attach = sink_input_attach_cb; + u->sink_input->detach = sink_input_detach_cb; + u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; + u->sink_input->update_max_request = sink_input_update_max_request_cb; + u->sink_input->userdata = u; + + pa_sink_input_set_requested_latency(u->sink_input, u->latency/3); + + pa_source_output_new_data_init(&source_output_data); + source_output_data.driver = __FILE__; + source_output_data.module = m; + source_output_data.source = source; + pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); + pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); + pa_source_output_new_data_set_sample_spec(&source_output_data, &ss); + pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); + + pa_source_output_new(&u->source_output, m->core, &source_output_data, 0); + pa_source_output_new_data_done(&source_output_data); + + if (!u->source_output) + goto fail; + + u->source_output->parent.process_msg = source_output_process_msg_cb; + u->source_output->push = source_output_push_cb; + u->source_output->process_rewind = source_output_process_rewind_cb; + u->source_output->kill = source_output_kill_cb; + u->source_output->attach = source_output_attach_cb; + u->source_output->detach = source_output_detach_cb; + u->source_output->state_change = source_output_state_change_cb; + u->source_output->userdata = u; + + pa_source_output_set_requested_latency(u->source_output, u->latency/3); + + pa_sink_input_get_silence(u->sink_input, &silence); + u->memblockq = pa_memblockq_new( + 0, /* idx */ + MEMBLOCKQ_MAXLENGTH, /* maxlength */ + MEMBLOCKQ_MAXLENGTH, /* tlength */ + pa_frame_size(&ss), /* base */ + 0, /* prebuf */ + 0, /* minreq */ + 0, /* maxrewind */ + &silence); /* silence frame */ + pa_memblock_unref(silence.memblock); + + u->asyncmsgq = pa_asyncmsgq_new(0); + + pa_sink_input_put(u->sink_input); + pa_source_output_put(u->source_output); + + if (u->adjust_time > 0) + u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u); + + pa_modargs_free(ma); + return 0; + +fail: + 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; + + teardown(u); + + if (u->memblockq) + pa_memblockq_free(u->memblockq); + + if (u->asyncmsgq) + pa_asyncmsgq_unref(u->asyncmsgq); + + if (u->time_event) + u->core->mainloop->time_free(u->time_event); + + pa_xfree(u); +} -- cgit From 99d3e6b744c2a45af1e8e2cf7219e8c6908fca94 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Aug 2009 01:51:29 +0200 Subject: combine: store adjust time in usec --- src/modules/module-combine.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index e90ef11c..4a57ce2d 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -58,7 +58,7 @@ PA_MODULE_USAGE( "sink_name= " "sink_properties= " "slaves= " - "adjust_time= " + "adjust_time= " "resample_method= " "format= " "rate= " @@ -69,7 +69,7 @@ PA_MODULE_USAGE( #define MEMBLOCKQ_MAXLENGTH (1024*1024*16) -#define DEFAULT_ADJUST_TIME 10 +#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC) #define BLOCK_USEC (PA_USEC_PER_MSEC * 200) @@ -91,7 +91,6 @@ struct output { pa_sink *sink; pa_sink_input *sink_input; - pa_bool_t ignore_state_change; pa_asyncmsgq *inq, /* Message queue from the sink thread to this sink input */ @@ -121,7 +120,7 @@ struct userdata { pa_rtpoll *rtpoll; pa_time_event *time_event; - uint32_t adjust_time; + pa_usec_t adjust_time; pa_bool_t automatic; pa_bool_t auto_desc; @@ -221,9 +220,9 @@ static void adjust_rates(struct userdata *u) { continue; if (o->total_latency < target_latency) - r -= (uint32_t) ((((double) (target_latency - o->total_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC); + r -= (uint32_t) ((((double) (target_latency - o->total_latency))/(double)u->adjust_time)*(double)r); else if (o->total_latency > target_latency) - r += (uint32_t) ((((double) (o->total_latency - target_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC); + r += (uint32_t) ((((double) (o->total_latency - target_latency))/(double)u->adjust_time)*(double)r); if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) { pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->sink->name, base_rate, r); @@ -246,7 +245,7 @@ static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct tim adjust_rates(u); - pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time * PA_USEC_PER_SEC); + pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time); } static void process_render_null(struct userdata *u, pa_usec_t now) { @@ -1105,6 +1104,7 @@ int pa__init(pa_module*m) { struct output *o; uint32_t idx; pa_sink_new_data data; + uint32_t adjust_time_sec; pa_assert(m); @@ -1123,16 +1123,10 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - u->adjust_time = DEFAULT_ADJUST_TIME; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->resample_method = resample_method; u->outputs = pa_idxset_new(NULL, NULL); - u->sink_put_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL; - PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs); - pa_atomic_store(&u->thread_info.running, FALSE); - u->thread_info.in_null_mode = FALSE; - u->thread_info.counter = 0; u->thread_info.smoother = pa_smoother_new( PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, @@ -1142,11 +1136,17 @@ int pa__init(pa_module*m) { 0, FALSE); - if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) { + adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC; + if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) { pa_log("Failed to parse adjust_time value"); goto fail; } + if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC) + u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC; + else + u->adjust_time = DEFAULT_ADJUST_TIME_USEC; + slaves = pa_modargs_get_value(ma, "slaves", NULL); u->automatic = !slaves; @@ -1315,7 +1315,7 @@ int pa__init(pa_module*m) { output_verify(o); 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); + u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u); pa_modargs_free(ma); -- cgit From d909f593894cef45040ce2135368edd4a1861ee7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Aug 2009 01:52:02 +0200 Subject: loopback: make sure a monitor can't be looped back to its sink --- src/modules/module-loopback.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 8b22fa1a..ac7029ae 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -322,6 +322,17 @@ static void source_output_kill_cb(pa_source_output *o) { pa_module_unload_request(u->module, TRUE); } +/* Called from main thread */ +static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) { + struct userdata *u; + + pa_source_output_assert_ref(o); + pa_assert_ctl_context(); + pa_assert_se(u = o->userdata); + + return dest != u->sink_input->sink->monitor_source; +} + /* Called from output thread context */ static void update_min_memblockq_length(struct userdata *u) { size_t length; @@ -539,6 +550,20 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_module_unload_request(u->module, TRUE); } +/* Called from main thread */ +static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert_se(u = i->userdata); + + if (!u->source_output->source->monitor_of) + return TRUE; + + return dest != u->source_output->source->monitor_of; +} + int pa__init(pa_module *m) { pa_modargs *ma = NULL; struct userdata *u; @@ -621,6 +646,7 @@ int pa__init(pa_module *m) { u->sink_input->detach = sink_input_detach_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; + u->sink_input->may_move_to = sink_input_may_move_to_cb; u->sink_input->userdata = u; pa_sink_input_set_requested_latency(u->sink_input, u->latency/3); @@ -647,6 +673,7 @@ int pa__init(pa_module *m) { u->source_output->attach = source_output_attach_cb; u->source_output->detach = source_output_detach_cb; u->source_output->state_change = source_output_state_change_cb; + u->source_output->may_move_to = source_output_may_move_to_cb; u->source_output->userdata = u; pa_source_output_set_requested_latency(u->source_output, u->latency/3); -- cgit From 4614412f94eef8ed65076c6fc76b463547f8f072 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Aug 2009 02:17:37 +0200 Subject: loopback: update description and icons when moving loopback streams --- src/modules/module-loopback.c | 54 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index ac7029ae..d9edbac0 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -333,6 +333,26 @@ static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *de return dest != u->sink_input->sink->monitor_source; } +/* Called from main thread */ +static void source_output_moving_cb(pa_source_output *o, pa_source *dest) { + pa_proplist *p; + const char *n; + struct userdata *u; + + pa_source_output_assert_ref(o); + pa_assert_ctl_context(); + pa_assert_se(u = o->userdata); + + p = pa_proplist_new(); + pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); + + if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) + pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n); + + pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p); + pa_proplist_free(p); +} + /* Called from output thread context */ static void update_min_memblockq_length(struct userdata *u) { size_t length; @@ -550,6 +570,26 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_module_unload_request(u->module, TRUE); } +/* Called from main thread */ +static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + pa_proplist *p; + const char *n; + + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert_se(u = i->userdata); + + p = pa_proplist_new(); + pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); + + if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) + pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n); + + pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p); + pa_proplist_free(p); +} + /* Called from main thread */ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; @@ -576,6 +616,7 @@ int pa__init(pa_module *m) { pa_channel_map map; pa_memchunk silence; uint32_t adjust_time_sec; + const char *n; pa_assert(m); @@ -627,7 +668,11 @@ int pa__init(pa_module *m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = sink; - pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)); + + pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback of %s", + pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))); + if ((n = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME))) + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME, n); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); @@ -647,6 +692,7 @@ int pa__init(pa_module *m) { u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; + u->sink_input->moving = sink_input_moving_cb; u->sink_input->userdata = u; pa_sink_input_set_requested_latency(u->sink_input, u->latency/3); @@ -655,7 +701,10 @@ int pa__init(pa_module *m) { source_output_data.driver = __FILE__; source_output_data.module = m; source_output_data.source = source; - pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); + pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s", + pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); + if ((n = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME))) + pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME, n); pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_source_output_new_data_set_sample_spec(&source_output_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); @@ -674,6 +723,7 @@ int pa__init(pa_module *m) { u->source_output->detach = source_output_detach_cb; u->source_output->state_change = source_output_state_change_cb; u->source_output->may_move_to = source_output_may_move_to_cb; + u->source_output->moving = source_output_moving_cb; u->source_output->userdata = u; pa_source_output_set_requested_latency(u->source_output, u->latency/3); -- cgit From 368c3e3f904c7dba5ba8ba4b17d76ba49b40c6ae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Aug 2009 20:05:19 +0200 Subject: loopback: quieten gcc on 32bit --- src/modules/module-loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index d9edbac0..6e4f66d5 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -172,7 +172,7 @@ static void adjust_rates(struct userdata *u) { (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC); - pa_log_info("Should buffer %lu bytes, buffered at minimum %lu bytes", + pa_log_info("Should buffer %zu bytes, buffered at minimum %zu bytes", u->latency_snapshot.max_request*2, u->latency_snapshot.min_memblockq_length); -- cgit From c372b52a77b20b454c49d5368a7d1afbc1a23188 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 27 Aug 2009 00:05:54 +0200 Subject: protocol-native: print more volume change debug messages to easy tracking down of feedback loops --- src/modules/module-stream-restore.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/modules') diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index e560bd28..d6e3c153 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -934,6 +934,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio data.data = &entry; data.size = sizeof(entry); + pa_log_debug("Client %s changes entry %s.", + pa_strnull(pa_proplist_gets(pa_native_connection_get_client(c)->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)), + name); + if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) if (apply_immediately) apply_entry(u, name, &entry); -- cgit From 8169a6a6c921215c1353e8a34fccbdc4e2e20440 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 27 Aug 2009 11:29:49 -0300 Subject: Handle DisconnectRequested in bluetooth module. --- src/modules/bluetooth/bluetooth-util.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index d0c89aab..f8c5b778 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -662,6 +662,21 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } else if (dbus_message_is_signal(m, "org.bluez.Device", "DisconnectRequested")) { + pa_bluetooth_device *d; + + if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) { + /* Device will disconnect in 2 sec */ + d->audio_state = PA_BT_AUDIO_STATE_DISCONNECTED; + d->audio_sink_state = PA_BT_AUDIO_STATE_DISCONNECTED; + d->audio_source_state = PA_BT_AUDIO_STATE_DISCONNECTED; + d->headset_state = PA_BT_AUDIO_STATE_DISCONNECTED; + + run_callback(y, d, FALSE); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } else if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) { const char *name, *old_owner, *new_owner; @@ -775,6 +790,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.Device',member='DisconnectRequested'", "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", @@ -828,6 +844,7 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.Device',member='DisconnectRequested'", "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", -- cgit From 63f3dc0bdc789fa26a7a6e05f0127411c74ff143 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 28 Aug 2009 17:07:08 +0200 Subject: bluetooth: remove left-over debug line --- src/modules/bluetooth/module-bluetooth-discover.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index 7571e48a..02fd4240 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -83,7 +83,6 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const mi = pa_hashmap_get(u->hashmap, d->path); - pa_log("dead: %d, device_connected: %d, audio_state: %d, audio_source_state: %d", d->dead, d->device_connected, d->audio_state, d->audio_source_state); if (!d->dead && d->device_connected > 0 && (d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED || d->audio_source_state >= PA_BT_AUDIO_STATE_CONNECTED)) { -- cgit From 4e8562c1f1cf5ee134043747feea093ded469be4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 28 Aug 2009 15:16:52 +0300 Subject: raop: Fix memory leak The return memory of the pa_strbuf_tostring_free() should be freed. pa_headerlist_puts() saves its own copy. --- src/modules/rtp/rtsp_client.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c index 72d304e8..ba657f74 100644 --- a/src/modules/rtp/rtsp_client.c +++ b/src/modules/rtp/rtsp_client.c @@ -214,11 +214,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) { /* End of headers */ /* We will have a header left from our looping iteration, so add it in :) */ if (c->last_header) { + char *tmp = pa_strbuf_tostring_free(c->header_buffer); /* This is not a continuation header so let's dump it into our proplist */ - pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer)); + pa_headerlist_puts(c->response_headers, c->last_header, tmp); + pa_xfree(tmp); pa_xfree(c->last_header); c->last_header = NULL; - c->header_buffer= NULL; + c->header_buffer = NULL; } pa_log_debug("Full response received. Dispatching"); @@ -240,9 +242,11 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) { } if (c->last_header) { + char *tmp = pa_strbuf_tostring_free(c->header_buffer); /* This is not a continuation header so let's dump the full header/value into our proplist */ - pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer)); + pa_headerlist_puts(c->response_headers, c->last_header, tmp); + pa_xfree(tmp); pa_xfree(c->last_header); c->last_header = NULL; c->header_buffer = NULL; -- cgit From 84eb6614eb3a4c72d9c529948aff8ffd4c534e19 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 28 Aug 2009 23:24:09 +0200 Subject: core: move 'flags' field into 'pa_sink_input_new_data' structure so that hooks can access it --- src/modules/module-combine.c | 3 ++- src/modules/module-ladspa-sink.c | 2 +- src/modules/module-loopback.c | 5 +++-- src/modules/module-remap-sink.c | 3 ++- src/modules/module-sine.c | 2 +- src/modules/rtp/module-rtp-recv.c | 3 ++- src/modules/rtp/module-rtp-send.c | 3 ++- 7 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 4a57ce2d..a186c899 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -844,8 +844,9 @@ static int output_create_sink_input(struct output *o) { pa_sink_input_new_data_set_channel_map(&data, &o->userdata->sink->channel_map); data.module = o->userdata->module; data.resample_method = o->userdata->resample_method; + data.flags = PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE|PA_SINK_INPUT_NO_CREATE_ON_SUSPEND; - pa_sink_input_new(&o->sink_input, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE|PA_SINK_INPUT_NO_CREATE_ON_SUSPEND); + pa_sink_input_new(&o->sink_input, o->userdata->core, &data); pa_sink_input_new_data_done(&data); diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index aa28f7fd..933fb182 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -812,7 +812,7 @@ int pa__init(pa_module*m) { pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 6e4f66d5..29c3ddab 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -676,8 +676,9 @@ int pa__init(pa_module *m) { pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); + sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE; - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_VARIABLE_RATE); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) @@ -709,7 +710,7 @@ int pa__init(pa_module *m) { pa_source_output_new_data_set_sample_spec(&source_output_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); - pa_source_output_new(&u->source_output, m->core, &source_output_data, 0); + pa_source_output_new(&u->source_output, m->core, &source_output_data); pa_source_output_new_data_done(&source_output_data); if (!u->source_output) diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index becff55c..6cfd0d15 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -406,8 +406,9 @@ int pa__init(pa_module*m) { pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map); + sink_input_data.flags = (remix ? 0 : PA_SINK_INPUT_NO_REMIX); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, (remix ? 0 : PA_SINK_INPUT_NO_REMIX)); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index 0be1d722..69b20028 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -163,7 +163,7 @@ int pa__init(pa_module*m) { pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency); pa_sink_input_new_data_set_sample_spec(&data, &ss); - pa_sink_input_new(&u->sink_input, m->core, &data, 0); + pa_sink_input_new(&u->sink_input, m->core, &data); pa_sink_input_new_data_done(&data); if (!u->sink_input) diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index c195c045..1a05f57d 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -501,8 +501,9 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in pa_proplist_setf(data.proplist, "rtp.payload", "%u", (unsigned) sdp_info->payload); data.module = u->module; pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec); + data.flags = PA_SINK_INPUT_VARIABLE_RATE; - pa_sink_input_new(&s->sink_input, u->module->core, &data, PA_SINK_INPUT_VARIABLE_RATE); + pa_sink_input_new(&s->sink_input, u->module->core, &data); pa_sink_input_new_data_done(&data); if (!s->sink_input) { diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index f147364d..8e1cfe36 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -330,8 +330,9 @@ int pa__init(pa_module*m) { data.source = s; pa_source_output_new_data_set_sample_spec(&data, &ss); pa_source_output_new_data_set_channel_map(&data, &cm); + data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; - pa_source_output_new(&o, m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND); + pa_source_output_new(&o, m->core, &data); pa_source_output_new_data_done(&data); if (!o) { -- cgit From f0290413726f98a5a59bb7f1e70cf516f123b8bd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 28 Aug 2009 23:24:37 +0200 Subject: suspend-on-idle: don't resume devices for corked streams --- src/modules/module-suspend-on-idle.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index 70a7b049..7adaa0b1 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -145,6 +145,9 @@ static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_ pa_assert(data); pa_assert(u); + if (data->flags & PA_SINK_INPUT_START_CORKED) + return PA_HOOK_OK; + if ((d = pa_hashmap_get(u->device_infos, data->sink))) resume(d); @@ -158,6 +161,9 @@ static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_outpu pa_assert(data); pa_assert(u); + if (data->flags & PA_SOURCE_OUTPUT_START_CORKED) + return PA_HOOK_OK; + if (data->source->monitor_of) d = pa_hashmap_get(u->device_infos, data->source->monitor_of); else @@ -226,11 +232,16 @@ static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input static pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { struct device_info *d; + pa_sink_input_state_t state; pa_assert(c); pa_sink_input_assert_ref(s); pa_assert(u); + state = pa_sink_input_get_state(s); + if (state != PA_SINK_INPUT_RUNNING && state != PA_SINK_INPUT_DRAINED) + return PA_HOOK_OK; + if ((d = pa_hashmap_get(u->device_infos, s->sink))) resume(d); @@ -265,6 +276,9 @@ static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_ pa_source_output_assert_ref(s); pa_assert(u); + if (pa_source_output_get_state(s) != PA_SOURCE_OUTPUT_RUNNING) + return PA_HOOK_OK; + if (s->source->monitor_of) d = pa_hashmap_get(u->device_infos, s->source->monitor_of); else @@ -279,6 +293,7 @@ static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_ static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { struct device_info *d; pa_sink_input_state_t state; + pa_assert(c); pa_sink_input_assert_ref(s); pa_assert(u); @@ -292,15 +307,11 @@ static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_inp } static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { - pa_source_output_state_t state; - pa_assert(c); pa_source_output_assert_ref(s); pa_assert(u); - state = pa_source_output_get_state(s); - - if (state == PA_SOURCE_OUTPUT_RUNNING) { + if (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_RUNNING) { struct device_info *d; if (s->source->monitor_of) @@ -387,22 +398,17 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s pa_sink *s = PA_SINK(o); pa_sink_state_t state = pa_sink_get_state(s); - if (pa_sink_check_suspend(s) <= 0) { - + if (pa_sink_check_suspend(s) <= 0) if (PA_SINK_IS_OPENED(state)) restart(d); - } - } else if (pa_source_isinstance(o)) { pa_source *s = PA_SOURCE(o); pa_source_state_t state = pa_source_get_state(s); - if (pa_source_check_suspend(s) <= 0) { - + if (pa_source_check_suspend(s) <= 0) if (PA_SOURCE_IS_OPENED(state)) restart(d); - } } return PA_HOOK_OK; -- cgit From 6b6d14626d5ae8b49edd1abe8ecc2432262358c9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Sep 2009 04:54:18 +0200 Subject: alsa: distuingish real underruns from left_to_play=0 --- src/modules/alsa/alsa-sink.c | 4 +++- src/modules/alsa/alsa-source.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 07d53880..afea8e08 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -410,6 +410,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_play; + pa_bool_t underrun = FALSE; /* We use <= instead of < for this check here because an underrun * only happens after the last sample was processed, not already when @@ -422,6 +423,7 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t o /* We got a dropout. What a mess! */ left_to_play = 0; + underrun = TRUE; #ifdef DEBUG_TIMING PA_DEBUG_TRAP; @@ -443,7 +445,7 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t o pa_bool_t reset_not_before = TRUE; if (!u->first && !u->after_rewind) { - if (left_to_play < u->watermark_inc_threshold) + if (underrun || left_to_play < u->watermark_inc_threshold) increase_watermark(u); else if (left_to_play > u->watermark_dec_threshold) { reset_not_before = FALSE; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 165b2e3b..643566b5 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -406,6 +406,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_record; size_t rec_space = u->hwbuf_size - u->hwbuf_unused; + pa_bool_t overrun = FALSE; /* We use <= instead of < for this check here because an overrun * only happens after the last sample was processed, not already when @@ -418,6 +419,7 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t /* We got a dropout. What a mess! */ left_to_record = 0; + overrun = TRUE; #ifdef DEBUG_TIMING PA_DEBUG_TRAP; @@ -434,7 +436,7 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t if (u->use_tsched) { pa_bool_t reset_not_before = TRUE; - if (left_to_record < u->watermark_inc_threshold) + if (overrun || left_to_record < u->watermark_inc_threshold) increase_watermark(u); else if (left_to_record > u->watermark_dec_threshold) { reset_not_before = FALSE; -- cgit From 46b9ca7fd5747d1ddbb7f2d18d2b572ddb785dc6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Sep 2009 04:55:05 +0200 Subject: alsa: by default increase watermarks only on real underruns, don't try to be smart --- src/modules/alsa/alsa-sink.c | 5 ++++- src/modules/alsa/alsa-source.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index afea8e08..195bdf6e 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -69,9 +69,12 @@ #define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */ #define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms -- When everything's great, decrease watermark by this */ #define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s -- How long after a drop out recheck if things are good now */ -#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms -- If the buffer level ever below this theshold, increase the watermark */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (0*PA_USEC_PER_MSEC) /* 0ms -- If the buffer level ever below this theshold, increase the watermark */ #define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this theshold in the verification time, decrease the watermark */ +/* Note that TSCHED_WATERMARK_INC_THRESHOLD_USEC == 0 means tht we + * will increase the watermark only if we hit a real underrun. */ + #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 643566b5..f42d3542 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -66,7 +66,7 @@ #define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ #define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms */ #define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s */ -#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (0*PA_USEC_PER_MSEC) /* 0ms */ #define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */ #define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -- cgit From b245b547025945aeca3717f987db3dd152559138 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Sep 2009 06:06:04 +0200 Subject: ladspa,remap: make description of sink follow moves --- src/modules/module-ladspa-sink.c | 25 ++++++++++++++++++++++--- src/modules/module-remap-sink.c | 24 +++++++++++++++++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 933fb182..994c778f 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -82,6 +82,8 @@ struct userdata { LADSPA_Data control_out; pa_memblockq *memblockq; + + pa_bool_t auto_desc; }; static const char* const valid_modargs[] = { @@ -423,6 +425,19 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); } else pa_sink_set_asyncmsgq(u->sink, NULL); + + if (u->auto_desc && dest) { + const char *z; + pa_proplist *pl; + + pl = pa_proplist_new(); + z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", + pa_proplist_gets(u->sink->proplist, "device.ladspa.name"), z ? z : dest->name); + + pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl); + pa_proplist_free(pl); + } } /* Called from main context */ @@ -451,7 +466,6 @@ int pa__init(pa_module*m) { pa_channel_map map; pa_modargs *ma; char *t; - const char *z; pa_sink *master; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; @@ -765,8 +779,6 @@ int pa__init(pa_module*m) { sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name); pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &map); - z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", d->Name, z ? z : master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin); @@ -782,6 +794,13 @@ int pa__init(pa_module*m) { goto fail; } + if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { + const char *z; + + z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", d->Name, z ? z : master->name); + } + u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 6cfd0d15..43748bd0 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -58,6 +58,8 @@ struct userdata { pa_sink *sink; pa_sink_input *sink_input; + + pa_bool_t auto_desc; }; static const char* const valid_modargs[] = { @@ -307,6 +309,18 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); } else pa_sink_set_asyncmsgq(u->sink, NULL); + + if (u->auto_desc && dest) { + const char *k; + pa_proplist *pl; + + pl = pa_proplist_new(); + k = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : dest->name); + + pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl); + pa_proplist_free(pl); + } } int pa__init(pa_module*m) { @@ -314,7 +328,6 @@ int pa__init(pa_module*m) { pa_sample_spec ss; pa_channel_map sink_map, stream_map; pa_modargs *ma; - const char *k; pa_sink *master; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; @@ -370,8 +383,6 @@ int pa__init(pa_module*m) { sink_data.name = pa_sprintf_malloc("%s.remapped", master->name); pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &sink_map); - k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); @@ -381,6 +392,13 @@ int pa__init(pa_module*m) { goto fail; } + if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { + const char *k; + + k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name); + } + u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); pa_sink_new_data_done(&sink_data); -- cgit From 8cd635bc614834c13d0f1c586d472b4a52b98664 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 3 Sep 2009 04:45:55 +0200 Subject: alsa: add more input sources to path set --- src/modules/alsa/mixer/paths/analog-input.conf.common | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/modules') diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common index 6728a6ae..87af38b3 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input.conf.common @@ -78,6 +78,10 @@ priority = 19 name = input-microphone priority = 19 +[Option Input Source:Internal Mic] +name = input-microphone +priority = 19 + [Option Input Source:Line] name = input-linein priority = 18 @@ -90,6 +94,10 @@ priority = 18 name = input-linein priority = 18 +[Option Input Source:Docking-Station] +name = input-docking +priority = 17 + ;;; ' Capture Source' [Element Capture Source] -- cgit From 0ad6a574a1eb206f40a1eed3abab8b05a1b6f6ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 4 Sep 2009 02:44:43 +0200 Subject: null: make name of null sink translatable --- src/modules/module-null-sink.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 36c50b05..74a2ebb1 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -51,7 +52,7 @@ #include "module-null-sink-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("Clocked NULL sink"); +PA_MODULE_DESCRIPTION(_("Clocked NULL sink")); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( @@ -287,7 +288,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_sink_new_data_set_channel_map(&data, &map); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output")); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", _("Null Output"))); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { -- cgit From 5daecea4dd4e0962e90e24a4ff594e2fa6a624a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 4 Sep 2009 02:46:43 +0200 Subject: always-sink: rename null sink created to 'dummy sink' and make it translatable --- src/modules/module-always-sink.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c index aee1c650..3d7de9c6 100644 --- a/src/modules/module-always-sink.c +++ b/src/modules/module-always-sink.c @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -35,7 +36,7 @@ #include "module-always-sink-symdef.h" PA_MODULE_AUTHOR("Colin Guthrie"); -PA_MODULE_DESCRIPTION("Always keeps at least one sink loaded even if it's a null one"); +PA_MODULE_DESCRIPTION(_("Always keeps at least one sink loaded even if it's a null one")); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_USAGE( @@ -78,7 +79,8 @@ static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* u->ignore = TRUE; - t = pa_sprintf_malloc("sink_name=%s", u->sink_name); + t = pa_sprintf_malloc("sink_name=%s sink_properties='device.description=\"%s\"'", u->sink_name, + _("Dummy Output")); m = pa_module_load(c, "module-null-sink", t); u->null_module = m ? m->index : PA_INVALID_INDEX; pa_xfree(t); -- cgit From f5046759cdd72daf5ba3b31c9dfc7b8d5be6bc9b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Sep 2009 23:46:23 +0200 Subject: llvm-clang-analyzer: drop a few unnecessary assignments and other trivial fixes --- src/modules/alsa/alsa-mixer.c | 2 +- src/modules/alsa/alsa-sink.c | 3 +-- src/modules/alsa/alsa-source.c | 3 +-- src/modules/alsa/alsa-util.c | 2 +- src/modules/module-hal-detect.c | 4 +--- src/modules/module-tunnel.c | 15 ++++++--------- src/modules/raop/base64.c | 1 - src/modules/x11/module-x11-publish.c | 2 +- 8 files changed, 12 insertions(+), 20 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 61c92cd0..9eb7b462 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -2889,7 +2889,7 @@ static void profile_set_add_auto_pair( else name = pa_sprintf_malloc("input:%s", n->name); - if ((p = pa_hashmap_get(ps->profiles, name))) { + if (pa_hashmap_get(ps->profiles, name)) { pa_xfree(name); return; } diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 195bdf6e..63a3e906 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1638,7 +1638,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark; + uint32_t nfrags, frag_size, tsched_size, tsched_watermark; snd_pcm_uframes_t period_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; @@ -1673,7 +1673,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } - hwbuf_size = frag_size * nfrags; period_frames = frag_size/frame_size; tsched_frames = tsched_size/frame_size; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index f42d3542..64f89806 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1483,7 +1483,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark; + uint32_t nfrags, frag_size, tsched_size, tsched_watermark; snd_pcm_uframes_t period_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; @@ -1518,7 +1518,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } - hwbuf_size = frag_size * nfrags; period_frames = frag_size/frame_size; tsched_frames = tsched_size/frame_size; diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index a47a8958..4d75c63c 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -900,7 +900,7 @@ void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) { snd_ctl_card_info_alloca(&info); if ((err = snd_ctl_open(&ctl, name, 0)) < 0) { - pa_log_warn("Error opening low-level control device '%s'", name); + pa_log_warn("Error opening low-level control device '%s': %s", name, snd_strerror(err)); return; } diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 6034d0ee..18519131 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -435,9 +435,7 @@ static int hal_device_add_all(struct userdata *u) { int i; for (i = 0; i < n; i++) { - struct device *d; - - if ((d = hal_device_add(u, udis[i]))) { + if (hal_device_add(u, udis[i])) { count++; pa_log_debug("Loaded device %s", udis[i]); } else diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 5ccb81d0..af4b8b2a 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -332,7 +332,7 @@ static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; - uint32_t channel, maxlength, tlength, fragsize, prebuf, minreq; + uint32_t channel, maxlength, tlength = 0, fragsize, prebuf, minreq; pa_usec_t usec; pa_assert(pd); @@ -1097,7 +1097,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag uint32_t idx, owner_module, client, sink; pa_usec_t buffer_usec, sink_usec; const char *name, *driver, *resample_method; - pa_bool_t mute; + pa_bool_t mute = FALSE; pa_sample_spec sample_spec; pa_channel_map channel_map; pa_cvolume volume; @@ -1345,12 +1345,11 @@ static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32 /* Called from main context */ static void start_subscribe(struct userdata *u) { pa_tagstruct *t; - uint32_t tag; pa_assert(u); t = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE); - pa_tagstruct_putu32(t, tag = u->ctag++); + pa_tagstruct_putu32(t, u->ctag++); pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER| #ifdef TUNNEL_SINK PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK @@ -1526,7 +1525,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t reply = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); - pa_tagstruct_putu32(reply, tag = u->ctag++); + pa_tagstruct_putu32(reply, u->ctag++); if (u->version >= 13) { pa_proplist *pl; @@ -1753,7 +1752,6 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata static void sink_set_volume(pa_sink *sink) { struct userdata *u; pa_tagstruct *t; - uint32_t tag; pa_assert(sink); u = sink->userdata; @@ -1761,7 +1759,7 @@ static void sink_set_volume(pa_sink *sink) { t = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME); - pa_tagstruct_putu32(t, tag = u->ctag++); + pa_tagstruct_putu32(t, u->ctag++); pa_tagstruct_putu32(t, u->device_index); pa_tagstruct_put_cvolume(t, &sink->real_volume); pa_pstream_send_tagstruct(u->pstream, t); @@ -1771,7 +1769,6 @@ static void sink_set_volume(pa_sink *sink) { static void sink_set_mute(pa_sink *sink) { struct userdata *u; pa_tagstruct *t; - uint32_t tag; pa_assert(sink); u = sink->userdata; @@ -1782,7 +1779,7 @@ static void sink_set_mute(pa_sink *sink) { t = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE); - pa_tagstruct_putu32(t, tag = u->ctag++); + pa_tagstruct_putu32(t, u->ctag++); pa_tagstruct_putu32(t, u->device_index); pa_tagstruct_put_boolean(t, !!sink->muted); pa_pstream_send_tagstruct(u->pstream, t); diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c index e1cbed02..5b061034 100644 --- a/src/modules/raop/base64.c +++ b/src/modules/raop/base64.c @@ -57,7 +57,6 @@ int pa_base64_encode(const void *data, int size, char **str) p = s = pa_xnew(char, size * 4 / 3 + 4); q = (const unsigned char *) data; - i = 0; for (i = 0; i < size;) { c = q[i++]; c *= 256; diff --git a/src/modules/x11/module-x11-publish.c b/src/modules/x11/module-x11-publish.c index 2c7fdc12..7ee1b6da 100644 --- a/src/modules/x11/module-x11-publish.c +++ b/src/modules/x11/module-x11-publish.c @@ -90,7 +90,7 @@ static void publish_servers(struct userdata *u, pa_strlist *l) { l = pa_strlist_reverse(l); s = pa_strlist_tostring(l); - l = pa_strlist_reverse(l); + pa_strlist_reverse(l); pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER", s); pa_xfree(s); -- cgit From 382eced35de93cba8098610f3990c16ae35b6ea0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Sep 2009 23:47:23 +0200 Subject: alsa-sink: init after_avail earlier (llvm-clang-analyzer) --- src/modules/alsa/alsa-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 63a3e906..883b32a7 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -656,6 +656,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle snd_pcm_sframes_t n; size_t n_bytes; int r; + pa_bool_t after_avail = TRUE; if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { @@ -710,7 +711,6 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle for (;;) { snd_pcm_sframes_t frames; void *p; - pa_bool_t after_avail = TRUE; /* pa_log_debug("%lu frames to write", (unsigned long) frames); */ -- cgit From 1380f18e52db48f61fd8582c59e52281728f22b7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Sep 2009 23:48:12 +0200 Subject: blueooth: actually honour 'room' variable (llvm-clang-analyzer) --- src/modules/bluetooth/module-bluetooth-device.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index b8a88042..4592fca1 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -221,9 +221,7 @@ static int service_recv(struct userdata *u, bt_audio_msg_header_t *msg, size_t r pa_assert(u); pa_assert(u->service_fd >= 0); pa_assert(msg); - - if (room <= 0) - room = BT_SUGGESTED_BUFFER_SIZE; + pa_assert(room >= sizeof(*msg)); pa_log_debug("Trying to receive message from audio service..."); @@ -236,6 +234,11 @@ static int service_recv(struct userdata *u, bt_audio_msg_header_t *msg, size_t r return -1; } + if (msg->length > room) { + pa_log_error("Not enough room."); + return -1; + } + /* Secondly, read the payload */ if (msg->length > sizeof(*msg)) { -- cgit From 157ec797dd53e1fb3d14f44c991064b0a2dee8e2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Sep 2009 23:48:47 +0200 Subject: hal: check properly for failure of libhal_find_device_by_capability() (llvm-clang-analyzer) --- src/modules/hal-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/hal-util.c b/src/modules/hal-util.c index e2a2d8d7..2d59f51d 100644 --- a/src/modules/hal-util.c +++ b/src/modules/hal-util.c @@ -65,7 +65,7 @@ int pa_hal_get_info(pa_core *core, pa_proplist *p, int card) { goto finish; } - if (!(udis = libhal_find_device_by_capability(hal, "sound", &n, &error)) < 0) { + if (!(udis = libhal_find_device_by_capability(hal, "sound", &n, &error))) { pa_log_error("Couldn't find devices: %s: %s", error.name, error.message); goto finish; } -- cgit From f3be47f1e03b5970281527d95c7ee26de2fc6dde Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Sep 2009 23:49:10 +0200 Subject: rtsp: document that rtsp_exec() needs fixing (llvm-clang-analyzer) --- src/modules/rtp/rtsp_client.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/modules') diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c index ba657f74..915c1072 100644 --- a/src/modules/rtp/rtsp_client.c +++ b/src/modules/rtp/rtsp_client.c @@ -456,6 +456,8 @@ static int rtsp_exec(pa_rtsp_client* c, const char* cmd, l = pa_iochannel_write(c->io, hdrs, strlen(hdrs)); pa_xfree(hdrs); + /* FIXME: this is broken, not necessarily all bytes are written */ + return 0; } -- cgit From c2f1994968e71f0f0a6c1f44bd8ec40edf1b6434 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 02:41:58 +0200 Subject: udev: ratelimit device initializations --- src/modules/module-udev-detect.c | 47 +++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index b41b9c0f..1b1e9c1a 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -29,10 +29,13 @@ #include #include +#include + #include #include #include #include +#include #include "module-udev-detect-symdef.h" @@ -50,6 +53,7 @@ struct device { char *card_name; char *args; uint32_t module; + pa_ratelimit ratelimit; }; struct userdata { @@ -110,6 +114,9 @@ static pa_bool_t is_card_busy(const char *id) { pa_assert(id); + /* This simply uses /proc/asound/card.../pcm.../sub.../status to + * check whether there is still a process using this audio device. */ + card_path = pa_sprintf_malloc("/proc/asound/card%s", id); if (!(card_dir = opendir(card_path))) { @@ -234,14 +241,41 @@ static void verify_access(struct userdata *u, struct device *d) { pa_log_debug("%s is busy: %s", d->path, pa_yes_no(busy)); if (!busy) { - pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); - m = pa_module_load(u->core, "module-alsa-card", d->args); - if (m) { - d->module = m->index; - pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name); + /* So, why do we rate limit here? It's certainly ugly, + * but there seems to be no other way. Problem is + * this: if we are unable to configure/probe an audio + * device after opening it we will close it again and + * the module initialization will fail. This will then + * cause an inotify event on the device node which + * will be forwarded to us. We then try to reopen the + * audio device again, practically entering a busy + * loop. + * + * A clean fix would be if we would be able to ignore + * our own inotify close events. However, inotify + * lacks such functionality. Also, during probing of + * the device we cannot really distuingish between + * other processes causing EBUSY or ourselves, which + * means we have no way to figure out if the probing + * during opening was canceled by a "try again" + * failure or a "fatal" failure. */ + + if (pa_ratelimit_test(&d->ratelimit)) { + pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args); + m = pa_module_load(u->core, "module-alsa-card", d->args); + + if (m) { + d->module = m->index; + pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name); + } else + pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name); } else - pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name); + pa_log_warn("Tried to configure %s (%s) more often than %u times in %llus", + d->path, + d->card_name, + d->ratelimit.burst, + (long long unsigned) (d->ratelimit.interval / PA_USEC_PER_SEC)); } } @@ -277,6 +311,7 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { d = pa_xnew0(struct device, 1); d->path = pa_xstrdup(path); d->module = PA_INVALID_INDEX; + PA_INIT_RATELIMIT(d->ratelimit, 10*PA_USEC_PER_SEC, 5); if (!(t = udev_device_get_property_value(dev, "PULSE_NAME"))) if (!(t = udev_device_get_property_value(dev, "ID_ID"))) -- cgit From 557c4295107dc7374c850b0bd5331dd35e8fdd0f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 04:28:52 +0200 Subject: alsa: rework buffer/period configuration - As discussed on alsa-devel it's probably better to initialize the buffer size first, followed by the period size. If that fails try the other way round. If that fails try to configure only buffer size. If that fails try to configure only period size. Finally, try to configure neither. - Don't require integral periods anymore. Both of these changes should help improving compatibility with various weirder sound devices, such as TV cards. --- src/modules/alsa/alsa-sink.c | 44 ++++---- src/modules/alsa/alsa-source.c | 45 ++++---- src/modules/alsa/alsa-util.c | 248 +++++++++++++++++++++++++++-------------- src/modules/alsa/alsa-util.h | 10 +- 4 files changed, 217 insertions(+), 130 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 883b32a7..c4ff10de 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -116,7 +116,6 @@ struct userdata { pa_usec_t watermark_dec_not_before; - unsigned nfragments; pa_memchunk memchunk; char *device_name; /* name of the PCM device */ @@ -943,8 +942,7 @@ static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; pa_bool_t b, d; - unsigned nfrags; - snd_pcm_uframes_t period_size; + snd_pcm_uframes_t period_size, buffer_size; pa_assert(u); pa_assert(!u->pcm_handle); @@ -961,12 +959,12 @@ static int unsuspend(struct userdata *u) { } ss = u->sink->sample_spec; - nfrags = u->nfragments; period_size = u->fragment_size / u->frame_size; + buffer_size = u->hwbuf_size / u->frame_size; b = u->use_mmap; d = u->use_tsched; - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) { pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err)); goto fail; } @@ -981,10 +979,11 @@ static int unsuspend(struct userdata *u) { goto fail; } - if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { - pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", - (unsigned long) u->nfragments, (unsigned long) u->fragment_size, - (unsigned long) nfrags, period_size * u->frame_size); + if (period_size*u->frame_size != u->fragment_size || + buffer_size*u->frame_size != u->hwbuf_size) { + pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)", + (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size, + (unsigned long) (buffer_size*u->fragment_size), (unsigned long) (period_size*u->frame_size)); goto fail; } @@ -1638,8 +1637,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, frag_size, tsched_size, tsched_watermark; - snd_pcm_uframes_t period_frames, tsched_frames; + uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark; + snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_sink_new_data data; @@ -1673,7 +1672,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } + buffer_size = nfrags * frag_size; + period_frames = frag_size/frame_size; + buffer_frames = buffer_size/frame_size; tsched_frames = tsched_size/frame_size; if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { @@ -1740,7 +1742,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, mapping))) goto fail; @@ -1755,7 +1757,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, profile_set, &mapping))) goto fail; @@ -1767,7 +1769,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, FALSE))) goto fail; } @@ -1819,7 +1821,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca 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_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); @@ -1860,13 +1862,15 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_sink_set_rtpoll(u->sink, u->rtpoll); u->frame_size = frame_size; - u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); - u->nfragments = nfrags; - u->hwbuf_size = u->fragment_size * nfrags; + u->fragment_size = frag_size = (size_t) (period_frames * frame_size); + u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size); pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels); - pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", - nfrags, (long unsigned) u->fragment_size, + pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)", + (double) u->hwbuf_size / (double) u->fragment_size, + (long unsigned) u->fragment_size, + (double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC, + (long unsigned) u->hwbuf_size, (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); pa_sink_set_max_request(u->sink, u->hwbuf_size); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 64f89806..5f89d88c 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -111,8 +111,6 @@ struct userdata { pa_usec_t watermark_dec_not_before; - unsigned nfragments; - char *device_name; char *control_device; @@ -891,8 +889,7 @@ static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; pa_bool_t b, d; - unsigned nfrags; - snd_pcm_uframes_t period_size; + snd_pcm_uframes_t period_size, buffer_size; pa_assert(u); pa_assert(!u->pcm_handle); @@ -909,12 +906,12 @@ static int unsuspend(struct userdata *u) { } ss = u->source->sample_spec; - nfrags = u->nfragments; period_size = u->fragment_size / u->frame_size; + buffer_size = u->hwbuf_size / u->frame_size; b = u->use_mmap; d = u->use_tsched; - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) { pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err)); goto fail; } @@ -929,10 +926,11 @@ static int unsuspend(struct userdata *u) { goto fail; } - if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { - pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", - (unsigned long) u->nfragments, (unsigned long) u->fragment_size, - (unsigned long) nfrags, period_size * u->frame_size); + if (period_size*u->frame_size != u->fragment_size || + buffer_size*u->frame_size != u->hwbuf_size) { + pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)", + (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size, + (unsigned long) (buffer_size*u->fragment_size), (unsigned long) (period_size*u->frame_size)); goto fail; } @@ -1483,8 +1481,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, frag_size, tsched_size, tsched_watermark; - snd_pcm_uframes_t period_frames, tsched_frames; + uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark; + snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_source_new_data data; @@ -1518,7 +1516,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } + buffer_size = nfrags * frag_size; + period_frames = frag_size/frame_size; + buffer_frames = buffer_size/frame_size; tsched_frames = tsched_size/frame_size; if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { @@ -1584,7 +1585,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, mapping))) goto fail; @@ -1598,7 +1599,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, profile_set, &mapping))) goto fail; @@ -1609,7 +1610,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, FALSE))) goto fail; } @@ -1661,7 +1662,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p 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_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); @@ -1702,13 +1703,15 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_source_set_rtpoll(u->source, u->rtpoll); u->frame_size = frame_size; - u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); - u->nfragments = nfrags; - u->hwbuf_size = u->fragment_size * nfrags; + u->fragment_size = frag_size = (size_t) (period_frames * frame_size); + u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size); pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); - pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", - nfrags, (long unsigned) u->fragment_size, + pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)", + (double) u->hwbuf_size / (double) u->fragment_size, + (long unsigned) u->fragment_size, + (double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC, + (long unsigned) u->hwbuf_size, (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); if (u->use_tsched) { diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 4d75c63c..91474527 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -93,6 +93,7 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s int ret; pa_assert(pcm_handle); + pa_assert(hwparams); pa_assert(f); if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0) @@ -148,33 +149,71 @@ try_auto: return -1; } +static int set_period_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) { + snd_pcm_uframes_t s; + int d, ret; + + pa_assert(pcm_handle); + pa_assert(hwparams); + + s = size; + d = 0; + if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) { + s = size; + d = -1; + if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) { + s = size; + d = 1; + if ((ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d)) < 0) { + pa_log_info("snd_pcm_hw_params_set_period_size_near() failed: %s", pa_alsa_strerror(ret)); + return ret; + } + } + } + + return 0; +} + +static int set_buffer_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) { + int ret; + + pa_assert(pcm_handle); + pa_assert(hwparams); + + if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &size)) < 0) { + pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); + return ret; + } + + return 0; +} + /* Set the hardware parameters of the given ALSA device. Returns the * selected fragment settings in *period and *period_size */ int pa_alsa_set_hw_params( snd_pcm_t *pcm_handle, pa_sample_spec *ss, - uint32_t *periods, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, pa_bool_t require_exact_channel_number) { int ret = -1; + snd_pcm_hw_params_t *hwparams, *hwparams_copy; + int dir; snd_pcm_uframes_t _period_size = period_size ? *period_size : 0; - unsigned int _periods = periods ? *periods : 0; - unsigned int r = ss->rate; - unsigned int c = ss->channels; - pa_sample_format_t f = ss->format; - snd_pcm_hw_params_t *hwparams; + snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0; pa_bool_t _use_mmap = use_mmap && *use_mmap; pa_bool_t _use_tsched = use_tsched && *use_tsched; - int dir; + pa_sample_spec _ss = *ss; pa_assert(pcm_handle); pa_assert(ss); snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_hw_params_alloca(&hwparams_copy); if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret)); @@ -208,114 +247,140 @@ int pa_alsa_set_hw_params( if (!_use_mmap) _use_tsched = FALSE; - if ((ret = set_format(pcm_handle, hwparams, &f)) < 0) + if ((ret = set_format(pcm_handle, hwparams, &_ss.format)) < 0) goto finish; - if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) { + if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &_ss.rate, NULL)) < 0) { pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret)); goto finish; } if (require_exact_channel_number) { - if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", c, pa_alsa_strerror(ret)); + if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, _ss.channels)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret)); goto finish; } } else { + unsigned int c = _ss.channels; + if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", c, pa_alsa_strerror(ret)); + pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret)); goto finish; } - } - if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_periods_integer() failed: %s", pa_alsa_strerror(ret)); - goto finish; + _ss.channels = c; } - if (_period_size > 0 && tsched_size > 0 && _periods > 0) { - snd_pcm_uframes_t buffer_size; - unsigned int p; + if (_use_tsched && tsched_size > 0) { + _buffer_size = pa_convert_size(tsched_size, ss, &_ss); + _period_size = _buffer_size; + } else { + _period_size = pa_convert_size(_period_size, ss, &_ss); + _buffer_size = pa_convert_size(_buffer_size, ss, &_ss); + } - /* Adjust the buffer sizes, if we didn't get the rate we were asking for */ - _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); - tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); + if (_buffer_size > 0 || _period_size > 0) { + snd_pcm_uframes_t max_frames = 0; - if (_use_tsched) { - buffer_size = 0; + if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &max_frames)) < 0) + pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); + else + pa_log_debug("Maximum hw buffer size is %lu ms", (long unsigned) max_frames * PA_MSEC_PER_SEC / _ss.rate); - if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0) - pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); - else - pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); + /* Some ALSA drivers really don't like if we set the buffer + * size first and the number of periods second. (which would + * make a lot more sense to me) So, try a few combinations + * before we give up. */ + + if (_buffer_size > 0 && _period_size > 0) { + snd_pcm_hw_params_copy(hwparams_copy, hwparams); + + /* First try: set buffer size first, followed by period size */ + if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 && + set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set buffer size first, period size second."); + goto success; + } - _period_size = tsched_size; - _periods = 1; + /* Second try: set period size first, followed by buffer size */ + if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 && + set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set period size first, buffer size second."); + goto success; + } } - /* Some ALSA drivers really don't like if we set the buffer - * size first and the number of periods second. (which would - * make a lot more sense to me) So, follow this rule and - * adjust the periods first and the buffer size second */ - - /* First we pass 0 as direction to get exactly what we - * asked for. That this is necessary is presumably a bug - * in ALSA. All in all this is mostly a hint to ALSA, so - * we don't care if this fails. */ - - p = _periods; - dir = 0; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { - p = _periods; - dir = 1; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { - p = _periods; - dir = -1; - if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir)) < 0) - pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); + if (_buffer_size > 0) { + snd_pcm_hw_params_copy(hwparams_copy, hwparams); + + /* Third try: set only buffer size */ + if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set only buffer size second."); + goto success; } } - /* Now set the buffer size */ - buffer_size = _periods * _period_size; - if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) - pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); + if (_period_size > 0) { + snd_pcm_hw_params_copy(hwparams_copy, hwparams); + + /* Fourth try: set only period size */ + if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set only period size second."); + goto success; + } + } } - if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) + pa_log_debug("Set neither period nor buffer size."); + + /* Last chance, set nothing */ + if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + pa_log_info("snd_pcm_hw_params failed: %s", pa_alsa_strerror(ret)); goto finish; + } + +success: - if (ss->rate != r) - pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r); + if (ss->rate != _ss.rate) + pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, _ss.rate); - if (ss->channels != c) - pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c); + if (ss->channels != _ss.channels) + pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, _ss.channels); - if (ss->format != f) - pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f)); + if (ss->format != _ss.format) + pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(_ss.format)); if ((ret = snd_pcm_prepare(pcm_handle)) < 0) { pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret)); goto finish; } + if ((ret = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) { + pa_log_info("snd_pcm_hw_params_current() failed: %s", pa_alsa_strerror(ret)); + goto finish; + } + if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || - (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) { - pa_log_info("snd_pcm_hw_params_get_period{s|_size}() failed: %s", pa_alsa_strerror(ret)); + (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) { + pa_log_info("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", pa_alsa_strerror(ret)); goto finish; } /* If the sample rate deviates too much, we need to resample */ - if (r < ss->rate*.95 || r > ss->rate*1.05) - ss->rate = r; - ss->channels = (uint8_t) c; - ss->format = f; + if (_ss.rate < ss->rate*.95 || _ss.rate > ss->rate*1.05) + ss->rate = _ss.rate; + ss->channels = _ss.channels; + ss->format = _ss.format; - pa_assert(_periods > 0); pa_assert(_period_size > 0); + pa_assert(_buffer_size > 0); - if (periods) - *periods = _periods; + if (buffer_size) + *buffer_size = _buffer_size; if (period_size) *period_size = _period_size; @@ -393,8 +458,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -410,8 +475,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( pa_assert(dev); pa_assert(ss); pa_assert(map); - pa_assert(nfrags); - pa_assert(period_size); pa_assert(ps); /* First we try to find a device string with a superset of the @@ -433,8 +496,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( ss, map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, @@ -460,8 +523,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( ss, map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, @@ -478,7 +541,18 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( /* OK, we didn't find any good device, so let's try the raw hw: stuff */ d = pa_sprintf_malloc("hw:%s", dev_id); pa_log_debug("Trying %s as last resort...", d); - pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE); + pcm_handle = pa_alsa_open_by_device_string( + d, + dev, + ss, + map, + mode, + period_size, + buffer_size, + tsched_size, + use_mmap, + use_tsched, + FALSE); pa_xfree(d); if (pcm_handle && mapping) @@ -493,8 +567,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -508,8 +582,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( pa_assert(dev); pa_assert(ss); pa_assert(map); - pa_assert(nfrags); - pa_assert(period_size); pa_assert(m); try_ss.channels = m->channel_map.channels; @@ -524,8 +596,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( &try_ss, &try_map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, @@ -547,8 +619,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -579,7 +651,15 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_log_debug("Managed to open %s", d); - if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) { + if ((err = pa_alsa_set_hw_params( + pcm_handle, + ss, + period_size, + buffer_size, + tsched_size, + use_mmap, + use_tsched, + require_exact_channel_number)) < 0) { if (!reformat) { reformat = TRUE; @@ -632,8 +712,8 @@ snd_pcm_t *pa_alsa_open_by_template( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -653,8 +733,8 @@ snd_pcm_t *pa_alsa_open_by_template( ss, map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 830a922e..265cd28c 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -42,8 +42,8 @@ int pa_alsa_set_hw_params( snd_pcm_t *pcm_handle, 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 *buffer_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 */ @@ -60,8 +60,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( 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 *buffer_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 */ @@ -75,8 +75,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( 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 *buffer_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 */ @@ -89,8 +89,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( 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 *buffer_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 */ @@ -104,8 +104,8 @@ snd_pcm_t *pa_alsa_open_by_template( 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 *buffer_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 */ -- cgit From 84ade2140ed44bf241eda494d0970390e48b21d3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 04:44:51 +0200 Subject: alsa: pass SND_PCM_NONBLOCK when opening device during unsuspend, the same way we do it for initial opening --- src/modules/alsa/alsa-sink.c | 2 +- src/modules/alsa/alsa-source.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index c4ff10de..7fe77831 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -950,7 +950,7 @@ static int unsuspend(struct userdata *u) { pa_log_info("Trying resume..."); if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, - /*SND_PCM_NONBLOCK|*/ + SND_PCM_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| SND_PCM_NO_AUTO_FORMAT)) < 0) { diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 5f89d88c..a7f2a018 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -897,7 +897,7 @@ static int unsuspend(struct userdata *u) { pa_log_info("Trying resume..."); if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, - /*SND_PCM_NONBLOCK|*/ + SND_PCM_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| SND_PCM_NO_AUTO_FORMAT)) < 0) { -- cgit From 8364b959b452a2b9e3f230705feb176a1fa6de06 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 23:56:51 +0200 Subject: alsa: when probing for profiles configure buffer/period sizes since some broken drivers apparently need that --- src/modules/alsa/alsa-mixer.c | 23 ++++++++++++++++++++--- src/modules/alsa/alsa-mixer.h | 2 +- src/modules/alsa/module-alsa-card.c | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 9eb7b462..685169b9 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -3145,7 +3145,13 @@ fail: return NULL; } -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_probe( + pa_alsa_profile_set *ps, + const char *dev_id, + const pa_sample_spec *ss, + unsigned default_n_fragments, + unsigned default_fragment_size_msec) { + void *state; pa_alsa_profile *p, *last = NULL; pa_alsa_mapping *m; @@ -3160,6 +3166,7 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons PA_HASHMAP_FOREACH(p, ps->profiles, state) { pa_sample_spec try_ss; pa_channel_map try_map; + snd_pcm_uframes_t try_period_size, try_buffer_size; uint32_t idx; /* Is this already marked that it is supported? (i.e. from the config file) */ @@ -3213,13 +3220,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons try_ss = *ss; try_ss.channels = try_map.channels; + try_period_size = + pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) / + pa_frame_size(&try_ss); + try_buffer_size = default_n_fragments * try_period_size; + if (!(m ->output_pcm = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, SND_PCM_STREAM_PLAYBACK, - NULL, NULL, 0, NULL, NULL, + &try_period_size, &try_buffer_size, 0, NULL, NULL, TRUE))) { p->supported = FALSE; break; @@ -3237,13 +3249,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons try_ss = *ss; try_ss.channels = try_map.channels; + try_period_size = + pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) / + pa_frame_size(&try_ss); + try_buffer_size = default_n_fragments * try_period_size; + if (!(m ->input_pcm = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, SND_PCM_STREAM_CAPTURE, - NULL, NULL, 0, NULL, NULL, + &try_period_size, &try_buffer_size, 0, NULL, NULL, TRUE))) { p->supported = FALSE; break; diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 76788183..a0d4fcbe 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -269,7 +269,7 @@ 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_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec); void pa_alsa_profile_set_free(pa_alsa_profile_set *s); void pa_alsa_profile_set_dump(pa_alsa_profile_set *s); diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index 55f6a6e2..6bea33d7 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -329,7 +329,7 @@ int pa__init(pa_module *m) { if (!u->profile_set) goto fail; - pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec); + pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec); pa_card_new_data_init(&data); data.driver = __FILE__; -- cgit From d5f43bd4c6a7eecff7bc0c4ff1be9152b33cb1e0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 23:57:49 +0200 Subject: alsa: disable tsched for software devices before we configure the buffer metrics so that we don't accidently set a buffer size that is suitable for tsched where we don't use tsched --- src/modules/alsa/alsa-sink.c | 5 ----- src/modules/alsa/alsa-source.c | 5 ----- src/modules/alsa/alsa-util.c | 5 ++++- 3 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 7fe77831..76cbe46f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1795,11 +1795,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca u->use_tsched = use_tsched = FALSE; } - if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) { - pa_log_info("Device is not a hardware device, disabling timer-based scheduling."); - u->use_tsched = use_tsched = FALSE; - } - if (u->use_mmap) pa_log_info("Successfully enabled mmap() mode."); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index a7f2a018..88f2d8ae 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1636,11 +1636,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->use_tsched = use_tsched = FALSE; } - if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) { - pa_log_info("Device is not a hardware device, disabling timer-based scheduling."); - u->use_tsched = use_tsched = FALSE; - } - if (u->use_mmap) pa_log_info("Successfully enabled mmap() mode."); diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 91474527..f934285a 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -189,7 +189,7 @@ static int set_buffer_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, } /* Set the hardware parameters of the given ALSA device. Returns the - * selected fragment settings in *period and *period_size */ + * selected fragment settings in *buffer_size and *period_size. If tsched mode can be enabled */ int pa_alsa_set_hw_params( snd_pcm_t *pcm_handle, pa_sample_spec *ss, @@ -247,6 +247,9 @@ int pa_alsa_set_hw_params( if (!_use_mmap) _use_tsched = FALSE; + if (!pa_alsa_pcm_is_hw(pcm_handle)) + _use_tsched = FALSE; + if ((ret = set_format(pcm_handle, hwparams, &_ss.format)) < 0) goto finish; -- cgit From bb36bb4bbe1ee01fb95debdca79d9769851a06da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Sep 2009 01:21:46 +0200 Subject: alsa: properly convert sample buffer sizes --- src/modules/alsa/alsa-util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index f934285a..56d60dfb 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -275,11 +275,11 @@ int pa_alsa_set_hw_params( } if (_use_tsched && tsched_size > 0) { - _buffer_size = pa_convert_size(tsched_size, ss, &_ss); + _buffer_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * _ss.rate) / ss->rate); _period_size = _buffer_size; } else { - _period_size = pa_convert_size(_period_size, ss, &_ss); - _buffer_size = pa_convert_size(_buffer_size, ss, &_ss); + _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * _ss.rate) / ss->rate); + _buffer_size = (snd_pcm_uframes_t) (((uint64_t) _buffer_size * _ss.rate) / ss->rate); } if (_buffer_size > 0 || _period_size > 0) { -- cgit From 80b44574765738532bc688e5d6fa5b40dc61402b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Sep 2009 01:22:10 +0200 Subject: alsa: properly report suspension error codes --- src/modules/alsa/alsa-sink.c | 23 ++++++++++++++--------- src/modules/alsa/alsa-source.c | 22 +++++++++++++--------- 2 files changed, 27 insertions(+), 18 deletions(-) (limited to 'src/modules') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 76cbe46f..22e88b4a 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1011,7 +1011,7 @@ fail: u->pcm_handle = NULL; } - return -1; + return -PA_ERR_IO; } /* Called from IO context */ @@ -1035,28 +1035,33 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { - case PA_SINK_SUSPENDED: + case PA_SINK_SUSPENDED: { + int r; + pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); - if (suspend(u) < 0) - return -1; + if ((r = suspend(u)) < 0) + return r; break; + } case PA_SINK_IDLE: - case PA_SINK_RUNNING: + case PA_SINK_RUNNING: { + int r; if (u->sink->thread_info.state == PA_SINK_INIT) { if (build_pollfd(u) < 0) - return -1; + return -PA_ERR_IO; } if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { - if (unsuspend(u) < 0) - return -1; + if ((r = unsuspend(u)) < 0) + return r; } break; + } case PA_SINK_UNLINKED: case PA_SINK_INIT: @@ -1084,7 +1089,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state) { reserve_done(u); else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state)) if (reserve_init(u, u->device_name) < 0) - return -1; + return -PA_ERR_BUSY; return 0; } diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 88f2d8ae..fa3ac0aa 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -959,7 +959,7 @@ fail: u->pcm_handle = NULL; } - return -1; + return -PA_ERR_IO; } static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { @@ -982,30 +982,34 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { - case PA_SOURCE_SUSPENDED: + case PA_SOURCE_SUSPENDED: { + int r; pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state)); - if (suspend(u) < 0) - return -1; + if ((r = suspend(u)) < 0) + return r; break; + } case PA_SOURCE_IDLE: - case PA_SOURCE_RUNNING: + case PA_SOURCE_RUNNING: { + int r; if (u->source->thread_info.state == PA_SOURCE_INIT) { if (build_pollfd(u) < 0) - return -1; + return -PA_ERR_IO; snd_pcm_start(u->pcm_handle); } if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) { - if (unsuspend(u) < 0) - return -1; + if ((r = unsuspend(u)) < 0) + return r; } break; + } case PA_SOURCE_UNLINKED: case PA_SOURCE_INIT: @@ -1033,7 +1037,7 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) { reserve_done(u); else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state)) if (reserve_init(u, u->device_name) < 0) - return -1; + return -PA_ERR_BUSY; return 0; } -- cgit From 6fa2445e1131c9c42b685451fd47d79ae2884871 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Sep 2009 02:32:21 +0200 Subject: position-event-sounds: honour window position if set, position both vertically and horizontally --- src/modules/module-position-event-sounds.c | 55 ++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 11 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c index e191ec33..c3329f88 100644 --- a/src/modules/module-position-event-sounds.c +++ b/src/modules/module-position-event-sounds.c @@ -57,34 +57,67 @@ struct userdata { pa_hook_slot *sink_input_fixate_hook_slot; }; +static int parse_pos(const char *pos, double *f) { + + if (pa_atod(pos, f) < 0) { + pa_log_warn("Failed to parse hpos/vpos property '%s'.", pos); + return -1; + } + + if (*f < 0.0 || *f > 1.0) { + pa_log_warn("Property hpos/vpos out of range %0.2f", *f); + return -1; + } + + return 0; +} + static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) { - const char *hpos; + const char *hpos, *vpos, *role; double f; char t[PA_CVOLUME_SNPRINT_MAX]; pa_cvolume v; pa_assert(data); - if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS))) + if (!(role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE))) return PA_HOOK_OK; - if (pa_atod(hpos, &f) < 0) { - pa_log_warn("Failed to parse "PA_PROP_EVENT_MOUSE_HPOS" property '%s'.", hpos); + if (!pa_streq(role, "event")) return PA_HOOK_OK; - } - if (f < 0.0 || f > 1.0) { - pa_log_warn("Property "PA_PROP_EVENT_MOUSE_HPOS" out of range %0.2f", f); + if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS))) + hpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_HPOS); + + if (!(vpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_VPOS))) + vpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_VPOS); + + if (!hpos && !vpos) return PA_HOOK_OK; + + pa_cvolume_reset(&v, data->sample_spec.channels); + + if (hpos) { + if (parse_pos(hpos, &f) < 0) + return PA_HOOK_OK; + + if (pa_channel_map_can_balance(&data->channel_map)) { + pa_log_debug("Positioning event sound '%s' horizontally at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); + pa_cvolume_set_balance(&v, &data->channel_map, f*2.0-1.0); + } } - pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); + if (vpos) { + if (parse_pos(vpos, &f) < 0) + return PA_HOOK_OK; - pa_cvolume_reset(&v, data->sample_spec.channels); - pa_cvolume_set_balance(&v, &data->channel_map, f*2.0-1.0); + if (pa_channel_map_can_fade(&data->channel_map)) { + pa_log_debug("Positioning event sound '%s' vertically at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); + pa_cvolume_set_fade(&v, &data->channel_map, f*2.0-1.0); + } + } pa_log_debug("Final volume factor %s.", pa_cvolume_snprint(t, sizeof(t), &v)); - pa_sink_input_new_data_apply_volume_factor(data, &v); return PA_HOOK_OK; -- cgit From 180ef1eebdfbdf0220af1fb5e4bf43e348207cde Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Sep 2009 03:27:38 +0200 Subject: position-event-sounds: apply volume factor after, not before resampling --- src/modules/module-position-event-sounds.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/modules') diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c index c3329f88..fa8f73d8 100644 --- a/src/modules/module-position-event-sounds.c +++ b/src/modules/module-position-event-sounds.c @@ -95,15 +95,15 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i if (!hpos && !vpos) return PA_HOOK_OK; - pa_cvolume_reset(&v, data->sample_spec.channels); + pa_cvolume_reset(&v, data->sink->sample_spec.channels); if (hpos) { if (parse_pos(hpos, &f) < 0) return PA_HOOK_OK; - if (pa_channel_map_can_balance(&data->channel_map)) { + if (pa_channel_map_can_balance(&data->sink->channel_map)) { pa_log_debug("Positioning event sound '%s' horizontally at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); - pa_cvolume_set_balance(&v, &data->channel_map, f*2.0-1.0); + pa_cvolume_set_balance(&v, &data->sink->channel_map, f*2.0-1.0); } } @@ -111,14 +111,14 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i if (parse_pos(vpos, &f) < 0) return PA_HOOK_OK; - if (pa_channel_map_can_fade(&data->channel_map)) { + if (pa_channel_map_can_fade(&data->sink->channel_map)) { pa_log_debug("Positioning event sound '%s' vertically at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); - pa_cvolume_set_fade(&v, &data->channel_map, f*2.0-1.0); + pa_cvolume_set_fade(&v, &data->sink->channel_map, f*2.0-1.0); } } pa_log_debug("Final volume factor %s.", pa_cvolume_snprint(t, sizeof(t), &v)); - pa_sink_input_new_data_apply_volume_factor(data, &v); + pa_sink_input_new_data_apply_volume_factor_sink(data, &v); return PA_HOOK_OK; } -- cgit