Re: [pulseaudio-discuss] [PATCH v2 4/5] pass pa_suspend_cause_t to set_state_in_io_thread() callbacks

2018-03-16 Thread Georg Chini

On 13.03.2018 18:40, Tanu Kaskinen wrote:

The suspend cause isn't yet used by any of the callbacks. The alsa sink
and source will use it to sync the mixer when the SESSION suspend cause
is removed. Currently the syncing is done in pa_sink/source_suspend(),
and I want to change that, because pa_sink/source_suspend() shouldn't
have any alsa specific code.
---
  src/modules/alsa/alsa-sink.c |  7 +++-
  src/modules/alsa/alsa-source.c   |  7 +++-
  src/modules/bluetooth/module-bluez4-device.c |  4 +-
  src/modules/bluetooth/module-bluez5-device.c |  4 +-
  src/modules/echo-cancel/module-echo-cancel.c |  2 +-
  src/modules/module-combine-sink.c|  7 +++-
  src/modules/module-equalizer-sink.c  |  2 +-
  src/modules/module-esound-sink.c |  7 +++-
  src/modules/module-ladspa-sink.c |  2 +-
  src/modules/module-null-sink.c   |  2 +-
  src/modules/module-null-source.c |  2 +-
  src/modules/module-pipe-sink.c   |  2 +-
  src/modules/module-remap-sink.c  |  2 +-
  src/modules/module-sine-source.c |  2 +-
  src/modules/module-solaris.c | 14 ++-
  src/modules/module-tunnel-sink-new.c |  7 +++-
  src/modules/module-tunnel-source-new.c   |  7 +++-
  src/modules/module-virtual-sink.c|  2 +-
  src/modules/module-virtual-surround-sink.c   |  2 +-
  src/modules/oss/module-oss.c | 14 ++-
  src/modules/raop/raop-sink.c |  7 +++-
  src/pulsecore/sink.c | 57 +---
  src/pulsecore/sink.h |  2 +-
  src/pulsecore/source.c   | 57 +---
  src/pulsecore/source.h   |  2 +-
  25 files changed, 168 insertions(+), 55 deletions(-)



...

  
  static void pa_sink_volume_change_push(pa_sink *s);

@@ -429,19 +434,47 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t 
state, pa_suspend_cause_t
   * current approach of not setting any suspend cause works well enough. */
  
  if (s->set_state_in_main_thread) {

-ret = s->set_state_in_main_thread(s, state, suspend_cause);
-/* set_state_in_main_thread() is allowed to fail only when resuming. */
-pa_assert(ret >= 0 || resuming);
+if ((ret = s->set_state_in_main_thread(s, state, suspend_cause)) < 0) {
+/* set_state_in_main_thread() is allowed to fail only when 
resuming. */
+pa_assert(resuming);
+
+/* If resuming fails, we set the state to SUSPENDED and
+ * suspend_cause to 0. */
+state = PA_SINK_SUSPENDED;
+suspend_cause = 0;
+state_changed = state != s->state;
+suspend_cause_changed = suspend_cause != s->suspend_cause;
+suspending = PA_SINK_IS_OPENED(s->state);
+resuming = false;
+
+if (!state_changed && !suspend_cause_changed)
+return ret;
+}
  }


I think the above is not correct. When set_state_in_main_thread() fails,
the state of the sink will be SUSPENDED before and after the call. We
know it was suspended before and if resume fails it will still be suspended
after the call. So if the (failing) set_state() forgot to set the state back
to SUSPENDED, we should just do so. Because we don't have a state
change, it does not make sense to send notifications if the handler
falsely set the state to IDLE or RUNNING. This leaves only suspend
changes for further processing. Same comment applies for the source
side.

  
-if (ret >= 0 && s->asyncmsgq && state_changed)

-if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), 
PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
+if (s->asyncmsgq) {
+struct set_state_data data = { .state = state, .suspend_cause = 
suspend_cause };
+
+if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), 
PA_SINK_MESSAGE_SET_STATE, &data, 0, NULL)) < 0) {
  /* SET_STATE is allowed to fail only when resuming. */
  pa_assert(resuming);
  
  if (s->set_state_in_main_thread)

  s->set_state_in_main_thread(s, PA_SINK_SUSPENDED, 0);
+
+/* If resuming fails, we set the state to SUSPENDED and
+ * suspend_cause to 0. */
+state = PA_SINK_SUSPENDED;
+suspend_cause = 0;
+state_changed = state != s->state;
+suspend_cause_changed = suspend_cause != s->suspend_cause;
+suspending = PA_SINK_IS_OPENED(s->state);
+resuming = false;
+
+if (!state_changed && !suspend_cause_changed)
+return ret;
  }
+}


The same applies here, only we should call set_state_in_main_thread() again
with state=SUSPENDED and suspend_cause=0. Again, the same is valid for
the source side.

Otherwise looks good.
___

Re: [pulseaudio-discuss] [PATCH v2 5/5] fix a call to pa_sink_suspend() from an incorrect thread

2018-03-16 Thread Georg Chini

On 13.03.2018 18:40, Tanu Kaskinen wrote:

The alsa sink calls pa_sink_suspend() from the set_port() callback.
pa_sink_suspend() can only be called from the main thread, but the
set_port() callback was often called from the IO thread. That caused an
assertion to be hit in pa_sink_suspend() when switching ports.

Another issue was that pa_sink_suspend() called the set_port() callback,
and if the callback calls pa_sink_suspend() again recursively, nothing
good can be expected from that, so the thread mismatch was not the only
problem.

This patch moves the mixer syncing logic out of pa_sink/source_suspend()
to be handled internally by the alsa sink/source. This removes the
recursive pa_sink_suspend() call. This also removes the need to have the
mixer_dirty flag in pa_sink/source, so the flag and the
pa_sink/source_set_mixer_dirty() functions can be removed.

This patch also changes the threading rules of set_port(). Previously it
was called sometimes from the main thread and sometimes from the IO
thread. Now it's always called from the main thread. When deferred
volumes are used, the alsa sink and source still have to update the
mixer from the IO thread when switching ports, but the thread
synchronization is now handled internally by the alsa sink and source.
The SET_PORT messages are not needed any more and can be removed.

BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=104761
---
  src/modules/alsa/alsa-sink.c   | 94 +-
  src/modules/alsa/alsa-source.c | 94 +-
  src/pulsecore/sink.c   | 52 +--
  src/pulsecore/sink.h   |  7 +---
  src/pulsecore/source.c | 52 +--
  src/pulsecore/source.h |  7 +---
  6 files changed, 156 insertions(+), 150 deletions(-)


LGTM
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v2 4/5] pass pa_suspend_cause_t to set_state_in_io_thread() callbacks

2018-03-16 Thread Georg Chini

On 16.03.2018 19:26, Tanu Kaskinen wrote:

On Fri, 2018-03-16 at 11:15 +0100, Georg Chini wrote:

On 13.03.2018 18:40, Tanu Kaskinen wrote:

The suspend cause isn't yet used by any of the callbacks. The alsa sink
and source will use it to sync the mixer when the SESSION suspend cause
is removed. Currently the syncing is done in pa_sink/source_suspend(),
and I want to change that, because pa_sink/source_suspend() shouldn't
have any alsa specific code.
---
   src/modules/alsa/alsa-sink.c |  7 +++-
   src/modules/alsa/alsa-source.c   |  7 +++-
   src/modules/bluetooth/module-bluez4-device.c |  4 +-
   src/modules/bluetooth/module-bluez5-device.c |  4 +-
   src/modules/echo-cancel/module-echo-cancel.c |  2 +-
   src/modules/module-combine-sink.c|  7 +++-
   src/modules/module-equalizer-sink.c  |  2 +-
   src/modules/module-esound-sink.c |  7 +++-
   src/modules/module-ladspa-sink.c |  2 +-
   src/modules/module-null-sink.c   |  2 +-
   src/modules/module-null-source.c |  2 +-
   src/modules/module-pipe-sink.c   |  2 +-
   src/modules/module-remap-sink.c  |  2 +-
   src/modules/module-sine-source.c |  2 +-
   src/modules/module-solaris.c | 14 ++-
   src/modules/module-tunnel-sink-new.c |  7 +++-
   src/modules/module-tunnel-source-new.c   |  7 +++-
   src/modules/module-virtual-sink.c|  2 +-
   src/modules/module-virtual-surround-sink.c   |  2 +-
   src/modules/oss/module-oss.c | 14 ++-
   src/modules/raop/raop-sink.c |  7 +++-
   src/pulsecore/sink.c | 57 
+---
   src/pulsecore/sink.h |  2 +-
   src/pulsecore/source.c   | 57 
+---
   src/pulsecore/source.h   |  2 +-
   25 files changed, 168 insertions(+), 55 deletions(-)


...

   
   static void pa_sink_volume_change_push(pa_sink *s);

@@ -429,19 +434,47 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t 
state, pa_suspend_cause_t
* current approach of not setting any suspend cause works well enough. 
*/
   
   if (s->set_state_in_main_thread) {

-ret = s->set_state_in_main_thread(s, state, suspend_cause);
-/* set_state_in_main_thread() is allowed to fail only when resuming. */
-pa_assert(ret >= 0 || resuming);
+if ((ret = s->set_state_in_main_thread(s, state, suspend_cause)) < 0) {
+/* set_state_in_main_thread() is allowed to fail only when 
resuming. */
+pa_assert(resuming);
+
+/* If resuming fails, we set the state to SUSPENDED and
+ * suspend_cause to 0. */
+state = PA_SINK_SUSPENDED;
+suspend_cause = 0;
+state_changed = state != s->state;
+suspend_cause_changed = suspend_cause != s->suspend_cause;
+suspending = PA_SINK_IS_OPENED(s->state);
+resuming = false;
+
+if (!state_changed && !suspend_cause_changed)
+return ret;
+}
   }

I think the above is not correct. When set_state_in_main_thread() fails,
the state of the sink will be SUSPENDED before and after the call. We
know it was suspended before and if resume fails it will still be suspended
after the call. So if the (failing) set_state() forgot to set the state back
to SUSPENDED, we should just do so.

The set_state_in_io_thread() callback is never responsible for setting
the state, and as you explained, the state isn't changing, so there
isn't anything to set anyway.


Because we don't have a state
change, it does not make sense to send notifications if the handler
falsely set the state to IDLE or RUNNING. This leaves only suspend
changes for further processing. Same comment applies for the source
side.

Since the state isn't changing, I should change this

+state = PA_SINK_SUSPENDED;
+suspend_cause = 0;
+state_changed = state != s->state;
+suspend_cause_changed = suspend_cause != s->suspend_cause;
+suspending = PA_SINK_IS_OPENED(s->state);
+resuming = false;

to this:

+suspend_cause = 0;
+state_changed = false;
+suspend_cause_changed = suspend_cause != s->suspend_cause;
+resuming = false;

Yes, that should be OK.
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v3 0/2] State change refactoring, port change crash fix

2018-03-20 Thread Georg Chini

On 19.03.2018 22:11, Tanu Kaskinen wrote:

Here are the last two patches that are required for fixing
https://bugs.freedesktop.org/show_bug.cgi?id=104761

Changes in v3:
  - I simplified the variable assignments in sink/source_set_state()
after a failed resume.
  - I reduced the "!state_changed && !suspend_cause_changed" checks after
a failed to just "!suspend_cause_changed", because state_changed is
always false. I added a comment related to this too.

There are no changes in the second patch.

Tanu Kaskinen (2):
   pass pa_suspend_cause_t to set_state_in_io_thread() callbacks
   fix a call to pa_sink_suspend() from an incorrect thread

  src/modules/alsa/alsa-sink.c |  99 -
  src/modules/alsa/alsa-source.c   |  99 -
  src/modules/bluetooth/module-bluez4-device.c |   4 +-
  src/modules/bluetooth/module-bluez5-device.c |   4 +-
  src/modules/echo-cancel/module-echo-cancel.c |   2 +-
  src/modules/module-combine-sink.c|   7 +-
  src/modules/module-equalizer-sink.c  |   2 +-
  src/modules/module-esound-sink.c |   7 +-
  src/modules/module-ladspa-sink.c |   2 +-
  src/modules/module-null-sink.c   |   2 +-
  src/modules/module-null-source.c |   2 +-
  src/modules/module-pipe-sink.c   |   2 +-
  src/modules/module-remap-sink.c  |   2 +-
  src/modules/module-sine-source.c |   2 +-
  src/modules/module-solaris.c |  14 +++-
  src/modules/module-tunnel-sink-new.c |   7 +-
  src/modules/module-tunnel-source-new.c   |   7 +-
  src/modules/module-virtual-sink.c|   2 +-
  src/modules/module-virtual-surround-sink.c   |   2 +-
  src/modules/oss/module-oss.c |  14 +++-
  src/modules/raop/raop-sink.c |   7 +-
  src/pulsecore/sink.c | 107 +++
  src/pulsecore/sink.h |   9 +--
  src/pulsecore/source.c   | 107 +++
  src/pulsecore/source.h   |   9 +--
  25 files changed, 322 insertions(+), 199 deletions(-)


Both look OK.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] Bluetooth HSP handling on pulseaudio

2018-03-22 Thread Georg Chini

On 22.03.2018 13:00, David Woodhouse wrote:

RING and CKPD are ignored by PulseAudio, so you can't currently get
notifications about those, unfortunately.

Hm... if I wanted to get notifications about those, how would I do so?

I've just written a plugin for Pidgin which works with USB headsets and
synchronises their mute/offhook status with calls correctly:
http://git.infradead.org/users/dwmw2/pidgin-headset.git

I can now hang up a call by pressing the headset button, and the
headset's "mute" button interoperates correctly with the server-side /
software mute.

I'd like to extend that to work with Bluetooth headsets too.

I think I can probably work out the mute part, since we *do* process
the AT+VGM= input from the headset. That should be visible somehow in
the conference GStreamer pipeline, I expect. But as you say, CKPD is
not handled and there's no way to handle RING either.

How would we connect those up?


And... if those are exposed through PulseAudio, does it make sense for
the USB headsets to be handled by PulseAudio too? What I do now is a
bit of a hack and I'm just finding *any* old headset device and using
it to control the call, even if it isn't the device that's actually
handing the audio. If PulseAudio were to automatically open the HID
associated with a given USB audio device and expose the mute/ring/hook
stuff in the same way as Bluetooth headsets, actually associated with
the correct device, that might be a useful approach?



I had written a patch for the BT headset button, but it was rejected
at the time. See
https://patchwork.freedesktop.org/patch/143572/

Maybe we can pick this up again, when we are able to send signals
via the native protocol as well. (There are patches for sending signals
which are not yet reviewed.)
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] Bluetooth HSP handling on pulseaudio

2018-03-22 Thread Georg Chini

On 22.03.2018 13:51, David Woodhouse wrote:

On Thu, 2018-03-22 at 13:16 +0100, Georg Chini wrote:

I had written a patch for the BT headset button, but it was rejected
at the time. See https://patchwork.freedesktop.org/patch/143572/

Maybe we can pick this up again, when we are able to send signals
via the native protocol as well. (There are patches for sending
signals which are not yet reviewed.)

Thanks; I hadn't seen that.

I'm inclined to agree that a random D-Bus signal that isn't associated
with the specific PA device isn't quite the right way to do this.

I see you asked, "could you please outline how it would be done using
the native API?" — I think that is basically what I was asking.

Ultimately for me this probably ends up with pulsesink/src GStreamer
elements emitting GStreamer messages when the headset events happen,
and having a GStreamer-based interface for the controls too. I don't
much *care* how it works inside PA, or indeed have enough context to
have a strong opinion.

I should probably start with the mute part, which looks like it *might*
already be functional, and take it from there...


As I said, meanwhile there exist patches to send signals via the
native API but they are not yet reviewed.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-03-24 Thread Georg Chini

On 24.03.2018 18:13, Tanu Kaskinen wrote:

On Mon, 2018-03-05 at 08:49 +0100, Georg Chini wrote:

The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid lags
after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
  src/modules/bluetooth/module-bluez5-device.c | 275 ++-
  1 file changed, 182 insertions(+), 93 deletions(-)

Thanks! Reading the new code caused less trouble than I recall the
bluetooth thread_func() previously having caused, so the changes are
good.

There are some issues pointed out below.


Thanks for the review.




  /* Run from I/O thread */
@@ -852,8 +892,10 @@ static void setup_stream(struct userdata *u) {
  
  pa_log_debug("Stream properly set up, we're ready to roll!");
  
-if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)

+if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
  a2dp_set_bitpool(u, u->sbc_info.max_bitpool);
+update_buffer_size(u);

This is redundant, a2dp_set_bitpool() calls update_buffer_size() by
itself.


It is not redundant. If the bit pools are equal, a2dp_set_bitpool() returns
immediately without calling update_buffer_size(). Therefore the call is
needed here.




+}
  
  u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);

  pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
@@ -861,7 +903,7 @@ static void setup_stream(struct userdata *u) {
  pollfd->events = pollfd->revents = 0;
  
  u->read_index = u->write_index = 0;

-u->started_at = 0;
+u->started_at = pa_rtclock_now();

This seems unnecessary. write_block() resets started_at anyway when the
first write is done.

Hmm... Now I noticed that u->started_at is used before the first write.
But it seems wrong to set u->started_at twice using (slightly)
different values. u->started_at is used in this code before the first
write:

 time_passed = pa_rtclock_now() - u->started_at;

I think before the first write time_passed should be set to exactly 0.


We have to discard audio that accumulated during the startup
time of the device to make sure audio is in sync. There will always
be some bytes dropped at the start. So it is correct to set the
initial time twice.



  
+/* A new block needs to be sent. */

  if (audio_sent <= time_passed) {
-pa_usec_t audio_to_send = time_passed - audio_sent;
+size_t bytes_to_send = pa_usec_to_bytes(time_passed - 
audio_sent, &u->sample_spec);
  
-/* Never try to catch up for more than 100ms */

-if (u->write_index > 0 && audio_to_send > 
MAX_PLAYBACK_CATCH_UP_USEC) {
-pa_usec_t skip_usec;
+/* There are more than two blocks that need to be 
written.
+ * We cannot catch up, therefore discard everything 
older
+ * than two block sizes. */

It seems that "cannot catch up" is not always true. It's probably true
that usually the buffer has room for only two blocks, but it's possible
that the buffer size is large enough for more blocks. Suggested
wording:

"There are more than two blocks that need to be written. Usually the
socket buffer has room for only two blocks, but even if there's more
room, let's not bother trying to catch up more than two blocks. We'll
discard everything older than two block sizes."


"Catch up" means catch up with the accumulated audio. We cannot play
faster, so everything we do not discard here will cause a permanent delay.
This has nothing to do with buffer size.


+ if ((result = write_block(u)) < 0)

  goto fail;
-}
-
-if (n_written == 0)
-pa_log("Broken kernel: we got EAGAIN on write() after 
POLLOUT!");
  
-do_write -= n_written;

-writable = false;
-}
+blocks_to_write -= result;
+writable = false;
+have_written = true;

Shouldn't have_written be set to true only if result > 0? If we got
EAGAIN, then no write actually happened.


It needs to be set anyway, because we will try to send the same block
again on the next iteration if we got EAGAIN. If have_written is false, the
thread will not be woken up by POLLOUT.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-03-25 Thread Georg Chini

On 25.03.2018 08:34, Tanu Kaskinen wrote:

On Sat, 2018-03-24 at 19:27 +0100, Georg Chini wrote:

On 24.03.2018 18:13, Tanu Kaskinen wrote:

On Mon, 2018-03-05 at 08:49 +0100, Georg Chini wrote:

The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid lags
after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
   src/modules/bluetooth/module-bluez5-device.c | 275 
++-
   1 file changed, 182 insertions(+), 93 deletions(-)

Thanks! Reading the new code caused less trouble than I recall the
bluetooth thread_func() previously having caused, so the changes are
good.

There are some issues pointed out below.

+}
   
   u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);

   pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
@@ -861,7 +903,7 @@ static void setup_stream(struct userdata *u) {
   pollfd->events = pollfd->revents = 0;
   
   u->read_index = u->write_index = 0;

-u->started_at = 0;
+u->started_at = pa_rtclock_now();

This seems unnecessary. write_block() resets started_at anyway when the
first write is done.

Hmm... Now I noticed that u->started_at is used before the first write.
But it seems wrong to set u->started_at twice using (slightly)
different values. u->started_at is used in this code before the first
write:

  time_passed = pa_rtclock_now() - u->started_at;

I think before the first write time_passed should be set to exactly 0.

We have to discard audio that accumulated during the startup
time of the device to make sure audio is in sync. There will always
be some bytes dropped at the start. So it is correct to set the
initial time twice.

Dropped? I don't see how that happens. In the first iteration
time_passed is some small value, likely less than one block. audio_sent
is zero. Skipping is done only if (time_passed - audio_sent) is more
than two blocks.


It definitely is more than two blocks.



If the delay is large for some reason, then skipping might happen, but
that's not good. A long startup delay shouldn't cause audio to be
dropped.


Why shouldn't it? If audio accumulated, it needs to be dropped
to achieve initial sync.


AV sync only requires accurate latency reporting, and the GET_LATENCY
handler uses u->started_at in its calculations. The delay between
setup_stream() where started_at is first set and the first
write_block() call doesn't affect the latency at all (and if it did,
GET_LATENCY would not take that latency into account). Consider this
thought experiment: if the startup delay is 10 seconds for some reason,
surely the end latency is still much less 10 seconds (even without
dropping any audio).


Don't understand. If the startup delay is 10 seconds and audio is 
accumulating
during that time, the delay will always be 10 seconds. How should it 
ever change?
So without dropping (and without resetting start time) A/V Sync does not 
work.




   
+/* A new block needs to be sent. */

   if (audio_sent <= time_passed) {
-pa_usec_t audio_to_send = time_passed - audio_sent;
+size_t bytes_to_send = pa_usec_to_bytes(time_passed - 
audio_sent, &u->sample_spec);
   
-/* Never try to catch up for more than 100ms */

-if (u->write_index > 0 && audio_to_send > 
MAX_PLAYBACK_CATCH_UP_USEC) {
-pa_usec_t skip_usec;
+/* There are more than two blocks that need to be 
written.
+ * We cannot catch up, therefore discard everything 
older
+ * than two block sizes. */

It seems that "cannot catch up" is not always true. It's probably true
that usually the buffer has room for only two blocks, but it's possible
that the buffer size is large enough for more blocks. Suggested
wording:

"There are more than two blocks that need to be written. Usually the
socket buffer has room for only two blocks, but even if there's more
room, let's not bother trying to catch up more than two blocks. We'll
discard everything older than two block sizes."

"Catch up" means catch up with the accumulated audio. We cannot play
faster, so everything we do not discard here will cause a permanent delay.
This has nothing to do with buffer size.

Ok. Since I misunderstood the comment, maybe make it more elaborate:

"There are more than two blocks that need to be written. It seems that
the socket has not been accepting data fast enough (could be due to
hiccups in the wi

Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-03-25 Thread Georg Chini

On 25.03.2018 09:44, Georg Chini wrote:

On 25.03.2018 08:34, Tanu Kaskinen wrote:

On Sat, 2018-03-24 at 19:27 +0100, Georg Chini wrote:

On 24.03.2018 18:13, Tanu Kaskinen wrote:

On Mon, 2018-03-05 at 08:49 +0100, Georg Chini wrote:
The rewrite of the thread function does not change functionality 
much,

most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to 
avoid lags
after temporary connection drops, following the proof-of-concept 
patch

posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
   src/modules/bluetooth/module-bluez5-device.c | 275 
++-

   1 file changed, 182 insertions(+), 93 deletions(-)

Thanks! Reading the new code caused less trouble than I recall the
bluetooth thread_func() previously having caused, so the changes are
good.

There are some issues pointed out below.

+    }
      u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, 
PA_RTPOLL_NEVER, 1);

   pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
@@ -861,7 +903,7 @@ static void setup_stream(struct userdata *u) {
   pollfd->events = pollfd->revents = 0;
      u->read_index = u->write_index = 0;
-    u->started_at = 0;
+    u->started_at = pa_rtclock_now();
This seems unnecessary. write_block() resets started_at anyway when 
the

first write is done.

Hmm... Now I noticed that u->started_at is used before the first 
write.

But it seems wrong to set u->started_at twice using (slightly)
different values. u->started_at is used in this code before the first
write:

  time_passed = pa_rtclock_now() - u->started_at;

I think before the first write time_passed should be set to exactly 0.

We have to discard audio that accumulated during the startup
time of the device to make sure audio is in sync. There will always
be some bytes dropped at the start. So it is correct to set the
initial time twice.

Dropped? I don't see how that happens. In the first iteration
time_passed is some small value, likely less than one block. audio_sent
is zero. Skipping is done only if (time_passed - audio_sent) is more
than two blocks.


It definitely is more than two blocks.



If the delay is large for some reason, then skipping might happen, but
that's not good. A long startup delay shouldn't cause audio to be
dropped.


Why shouldn't it? If audio accumulated, it needs to be dropped
to achieve initial sync.


AV sync only requires accurate latency reporting, and the GET_LATENCY
handler uses u->started_at in its calculations. The delay between
setup_stream() where started_at is first set and the first
write_block() call doesn't affect the latency at all (and if it did,
GET_LATENCY would not take that latency into account). Consider this
thought experiment: if the startup delay is 10 seconds for some reason,
surely the end latency is still much less 10 seconds (even without
dropping any audio).


Don't understand. If the startup delay is 10 seconds and audio is 
accumulating
during that time, the delay will always be 10 seconds. How should it 
ever change?
So without dropping (and without resetting start time) A/V Sync does 
not work. 

Maybe some additional explanation helps:

Consider you are watching a video and switch from your internal sound card
to your BT headset. At some point, the thread function will be set up 
and audio
will start to accumulate. On the first iteration of the thread function 
nothing
happens (and nothing is dropped), it will simply fall through and then 
wait for

POLLOUT. This can take some time and the video is continuing. Now, when
POLLOUT is set, the audio that was sent before needs to be dropped, 
otherwise

A/V sync will be lost.
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-03-29 Thread Georg Chini

On 25.03.2018 15:48, Tanu Kaskinen wrote:

On Sun, 2018-03-25 at 14:36 +0200, Georg Chini wrote:

On 25.03.2018 09:44, Georg Chini wrote:

On 25.03.2018 08:34, Tanu Kaskinen wrote:

On Sat, 2018-03-24 at 19:27 +0100, Georg Chini wrote:

On 24.03.2018 18:13, Tanu Kaskinen wrote:

On Mon, 2018-03-05 at 08:49 +0100, Georg Chini wrote:

The rewrite of the thread function does not change functionality
much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to
avoid lags
after temporary connection drops, following the proof-of-concept
patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
src/modules/bluetooth/module-bluez5-device.c | 275
++-
1 file changed, 182 insertions(+), 93 deletions(-)

Thanks! Reading the new code caused less trouble than I recall the
bluetooth thread_func() previously having caused, so the changes are
good.

There are some issues pointed out below.

+}
   u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll,
PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
@@ -861,7 +903,7 @@ static void setup_stream(struct userdata *u) {
pollfd->events = pollfd->revents = 0;
   u->read_index = u->write_index = 0;
-u->started_at = 0;
+u->started_at = pa_rtclock_now();

This seems unnecessary. write_block() resets started_at anyway when
the
first write is done.

Hmm... Now I noticed that u->started_at is used before the first
write.
But it seems wrong to set u->started_at twice using (slightly)
different values. u->started_at is used in this code before the first
write:

   time_passed = pa_rtclock_now() - u->started_at;

I think before the first write time_passed should be set to exactly 0.

We have to discard audio that accumulated during the startup
time of the device to make sure audio is in sync. There will always
be some bytes dropped at the start. So it is correct to set the
initial time twice.

Dropped? I don't see how that happens. In the first iteration
time_passed is some small value, likely less than one block. audio_sent
is zero. Skipping is done only if (time_passed - audio_sent) is more
than two blocks.

It definitely is more than two blocks.


If the delay is large for some reason, then skipping might happen, but
that's not good. A long startup delay shouldn't cause audio to be
dropped.

Why shouldn't it? If audio accumulated, it needs to be dropped
to achieve initial sync.


AV sync only requires accurate latency reporting, and the GET_LATENCY
handler uses u->started_at in its calculations. The delay between
setup_stream() where started_at is first set and the first
write_block() call doesn't affect the latency at all (and if it did,
GET_LATENCY would not take that latency into account). Consider this
thought experiment: if the startup delay is 10 seconds for some reason,
surely the end latency is still much less 10 seconds (even without
dropping any audio).

Don't understand. If the startup delay is 10 seconds and audio is
accumulating
during that time, the delay will always be 10 seconds. How should it
ever change?
So without dropping (and without resetting start time) A/V Sync does
not work.

Maybe some additional explanation helps:

Consider you are watching a video and switch from your internal sound card
to your BT headset. At some point, the thread function will be set up
and audio
will start to accumulate. On the first iteration of the thread function
nothing
happens (and nothing is dropped), it will simply fall through and then
wait for
POLLOUT. This can take some time and the video is continuing. Now, when
POLLOUT is set, the audio that was sent before needs to be dropped,
otherwise
A/V sync will be lost.

Where do you think the audio accumulates? If waiting for POLLOUT takes
time, then playback will just stall. The video player won't keep
writing more audio if old audio isn't being consumed.

Dropping audio when switching devices during video playback perhaps
isn't a big problem, but dropping audio when starting to play something
is a significant problem. Event sounds may end up not being heard at
all, and when starting to play music, the beginning of a song is lost.


Well, you are again right. I tested it here and it does not seem to
make a difference in the initial sync. I will send a new patch and
ask the user who had massive initial sync problems to re-test.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-03-29 Thread Georg Chini

On 24.03.2018 18:13, Tanu Kaskinen wrote:

On Mon, 2018-03-05 at 08:49 +0100, Georg Chini wrote:

The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid lags
after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
  src/modules/bluetooth/module-bluez5-device.c | 275 ++-
  1 file changed, 182 insertions(+), 93 deletions(-)

Thanks! Reading the new code caused less trouble than I recall the
bluetooth thread_func() previously having caused, so the changes are
good.

There are some issues pointed out below.


diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index 7970dda7..149d1708 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -56,7 +56,6 @@ PA_MODULE_LOAD_ONCE(false);
  PA_MODULE_USAGE("path="
  "autodetect_mtu=");
  
-#define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)

  #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
  #define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)
  #define FIXED_LATENCY_RECORD_A2DP   (25 * PA_USEC_PER_MSEC)
@@ -660,6 +659,38 @@ static int a2dp_process_push(struct userdata *u) {
  return ret;
  }
  
+static void update_buffer_size(struct userdata *u) {

+int old_bufsize;
+socklen_t len = sizeof(int);
+int ret;
+
+ret = getsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &old_bufsize, &len);

In case the type of old_bufsize changes (which it will never do, but
what if it does anyway) then the calculation of len has to be
separately updated. Therefore it would be safer to pass
sizeof(new_bufsize) here and get rid of the len variable.


Unfortunately the getsockopt() call takes a pointer to the len variable
as argument, therefore I cannot pass sizeof(old_bufsize) directly. I'll
leave this as it is.
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-03-29 Thread Georg Chini
The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid lags
after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
Changes in v2:
 - fix issues pointed out by Tanu
 - set writable to false for HSP only if a block really needs to be written

 src/modules/bluetooth/module-bluez5-device.c | 289 ++-
 1 file changed, 191 insertions(+), 98 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index c3acc1dc..dac1eb2a 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -56,9 +56,8 @@ PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE("path="
 "autodetect_mtu=");
 
-#define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
-#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_PLAYBACK_SCO  (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_RECORD_A2DP   (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_RECORD_SCO(25 * PA_USEC_PER_MSEC)
 
@@ -660,6 +659,38 @@ static int a2dp_process_push(struct userdata *u) {
 return ret;
 }
 
+static void update_buffer_size(struct userdata *u) {
+int old_bufsize;
+socklen_t len = sizeof(int);
+int ret;
+
+ret = getsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &old_bufsize, &len);
+if (ret == -1) {
+pa_log_warn("Changing bluetooth buffer size: Failed to 
getsockopt(SO_SNDBUF): %s", pa_cstrerror(errno));
+} else {
+int new_bufsize;
+
+/* Set send buffer size as small as possible. The minimum value is 
1024 according to the
+ * socket man page. The data is written to the socket in chunks of 
write_block_size, so
+ * there should at least be room for two chunks in the buffer. 
Generally, write_block_size
+ * is larger than 512. If not, use the next multiple of 
write_block_size which is larger
+ * than 1024. */
+new_bufsize = 2 * u->write_block_size;
+if (new_bufsize < 1024)
+new_bufsize = (1024 / u->write_block_size + 1) * 
u->write_block_size;
+
+/* The kernel internally doubles the buffer size that was set by 
setsockopt and getsockopt
+ * returns the doubled value. */
+if (new_bufsize != old_bufsize / 2) {
+ret = setsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, 
&new_bufsize, len);
+if (ret == -1)
+pa_log_warn("Changing bluetooth buffer size: Failed to change 
from %d to %d: %s", old_bufsize / 2, new_bufsize, pa_cstrerror(errno));
+else
+pa_log_info("Changing bluetooth buffer size: Changed from %d 
to %d", old_bufsize / 2, new_bufsize);
+}
+}
+}
+
 /* Run from I/O thread */
 static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) {
 struct sbc_info *sbc_info;
@@ -694,6 +725,15 @@ static void a2dp_set_bitpool(struct userdata *u, uint8_t 
bitpool) {
 pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
 pa_sink_set_fixed_latency_within_thread(u->sink,
 FIXED_LATENCY_PLAYBACK_A2DP + 
pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+
+/* If there is still data in the memchunk, we have to discard it
+ * because the write_block_size may have changed. */
+if (u->write_memchunk.memblock) {
+pa_memblock_unref(u->write_memchunk.memblock);
+pa_memchunk_reset(&u->write_memchunk);
+}
+
+update_buffer_size(u);
 }
 
 /* Run from I/O thread */
@@ -852,8 +892,10 @@ static void setup_stream(struct userdata *u) {
 
 pa_log_debug("Stream properly set up, we're ready to roll!");
 
-if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
 a2dp_set_bitpool(u, u->sbc_info.max_bitpool);
+update_buffer_size(u);
+}
 
 u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
@@ -1068,12 +1110,12 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 switch (code) {
 
 case PA_SINK_MESSAGE_GET_LATENCY: {
-int64_t wi, ri;
+int64_t wi = 0, ri = 0;
 
 if (u->read_smoother) {
 ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 wi = pa_bytes_to_usec(u->write_index + u->write_block_size, 
&u->sample_spec);
-} else {
+} else if (u->started_at) {
 ri = pa_rtclock_now() - u->started_at;
 wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
 }
@@ -1415,12 +1457,32 @@ stat

Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-03-29 Thread Georg Chini

On 29.03.2018 15:33, Georg Chini wrote:

The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid lags
after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
Changes in v2:
  - fix issues pointed out by Tanu
  - set writable to false for HSP only if a block really needs to be written


Sorry, should have been v3 instead of v2.
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH] alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()

2018-04-02 Thread Georg Chini
The current code does not call snd_pcm_status_set_audio_htstamp_config()
to configure the way timestamps are updated in ALSA. This leads to
incorrect time stamps in the status object returned by snd_pcm_status(),
so the computed latencies are wrong.

This patch uses snd_pcm_status_set_audio_htstamp_config() to set the
ALSA report_delay flag to 1 before the call to snd_pcm_status(). With
this, time stamps are updated as expected.
---
 src/modules/alsa/alsa-util.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 61fb4903..b91a0e98 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,6 +1187,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
 size_t abs_k;
 int err;
 snd_pcm_sframes_t avail = 0;
+snd_pcm_audio_tstamp_config_t tstamp_config;
 
 pa_assert(pcm);
 pa_assert(delay);
@@ -1200,6 +1201,12 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
  * avail, delay and timestamp values in a single kernel call to improve
  * timer-based scheduling */
 
+/* The time stamp configuration needs to be set so that the
+ * ALSA code will use the internal delay reported by the driver */
+tstamp_config.type_requested = 1; /* ALSA default time stamp type */
+tstamp_config.report_delay = 1;
+snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+
 if ((err = snd_pcm_status(pcm, status)) < 0)
 return err;
 
-- 
2.16.2

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()

2018-04-02 Thread Georg Chini

On 02.04.2018 21:35, Pierre-Louis Bossart wrote:



On 04/02/2018 07:54 AM, Georg Chini wrote:

The current code does not call snd_pcm_status_set_audio_htstamp_config()
to configure the way timestamps are updated in ALSA. This leads to
incorrect time stamps in the status object returned by snd_pcm_status(),
so the computed latencies are wrong.

This patch uses snd_pcm_status_set_audio_htstamp_config() to set the
ALSA report_delay flag to 1 before the call to snd_pcm_status(). With
this, time stamps are updated as expected.
---
  src/modules/alsa/alsa-util.c | 7 +++
  1 file changed, 7 insertions(+)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 61fb4903..b91a0e98 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,6 +1187,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, 
snd_pcm_status_t *status, snd_pcm_sframes

  size_t abs_k;
  int err;
  snd_pcm_sframes_t avail = 0;
+    snd_pcm_audio_tstamp_config_t tstamp_config;
    pa_assert(pcm);
  pa_assert(delay);
@@ -1200,6 +1201,12 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, 
snd_pcm_status_t *status, snd_pcm_sframes
   * avail, delay and timestamp values in a single kernel call to 
improve

   * timer-based scheduling */
  +    /* The time stamp configuration needs to be set so that the
+ * ALSA code will use the internal delay reported by the driver */
+    tstamp_config.type_requested = 1; /* ALSA default time stamp 
type */

+    tstamp_config.report_delay = 1;
+    snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+
are you sure it's necessary or is this possibly a misunderstanding of 
what audio_tstamps are?


this command is only for the audio timestamp, and to the best of my 
knowledge you are not using the results using one of the 
snd_pcm_status_get_audio_htstamp_* commands


the typical usage (see alsa-lib/test/audio_time.c) is this:

    snd_pcm_status_set_audio_htstamp_config(status, audio_tstamp_config);

    if ((err = snd_pcm_status(handle, status)) < 0) {
        printf("Stream status error: %s\n", snd_strerror(err));
        exit(0);
    }
    snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp);
    snd_pcm_status_get_htstamp(status, timestamp);
    snd_pcm_status_get_audio_htstamp(status, audio_timestamp);
    snd_pcm_status_get_audio_htstamp_report(status, audio_tstamp_report);

if you are not using the _get_audio_hstamp() then the config has 
essentially no effect, and the delay is available separately in the 
status command as before.



  if ((err = snd_pcm_status(pcm, status)) < 0)
  return err;


See this bug report, why it is needed:

https://bugzilla.kernel.org/show_bug.cgi?id=199235

It finally turned out that there was not a bug but just the flag missing.
We are using  snd_pcm_status_get_htstamp() with the time smoother
to calculate sink/source latency.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()

2018-04-03 Thread Georg Chini

On 04.04.2018 00:35, Pierre-Louis Bossart wrote:

On 4/2/18 3:14 PM, Georg Chini wrote:

On 02.04.2018 21:35, Pierre-Louis Bossart wrote:



On 04/02/2018 07:54 AM, Georg Chini wrote:
The current code does not call 
snd_pcm_status_set_audio_htstamp_config()

to configure the way timestamps are updated in ALSA. This leads to
incorrect time stamps in the status object returned by 
snd_pcm_status(),

so the computed latencies are wrong.

This patch uses snd_pcm_status_set_audio_htstamp_config() to set the
ALSA report_delay flag to 1 before the call to snd_pcm_status(). With
this, time stamps are updated as expected.
---
  src/modules/alsa/alsa-util.c | 7 +++
  1 file changed, 7 insertions(+)

diff --git a/src/modules/alsa/alsa-util.c 
b/src/modules/alsa/alsa-util.c

index 61fb4903..b91a0e98 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,6 +1187,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, 
snd_pcm_status_t *status, snd_pcm_sframes

  size_t abs_k;
  int err;
  snd_pcm_sframes_t avail = 0;
+    snd_pcm_audio_tstamp_config_t tstamp_config;
    pa_assert(pcm);
  pa_assert(delay);
@@ -1200,6 +1201,12 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, 
snd_pcm_status_t *status, snd_pcm_sframes
   * avail, delay and timestamp values in a single kernel call 
to improve

   * timer-based scheduling */
  +    /* The time stamp configuration needs to be set so that the
+ * ALSA code will use the internal delay reported by the 
driver */
+    tstamp_config.type_requested = 1; /* ALSA default time stamp 
type */

+    tstamp_config.report_delay = 1;
+    snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+
are you sure it's necessary or is this possibly a misunderstanding 
of what audio_tstamps are?


this command is only for the audio timestamp, and to the best of my 
knowledge you are not using the results using one of the 
snd_pcm_status_get_audio_htstamp_* commands


the typical usage (see alsa-lib/test/audio_time.c) is this:

    snd_pcm_status_set_audio_htstamp_config(status, 
audio_tstamp_config);


    if ((err = snd_pcm_status(handle, status)) < 0) {
        printf("Stream status error: %s\n", snd_strerror(err));
        exit(0);
    }
    snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp);
    snd_pcm_status_get_htstamp(status, timestamp);
    snd_pcm_status_get_audio_htstamp(status, audio_timestamp);
    snd_pcm_status_get_audio_htstamp_report(status, 
audio_tstamp_report);


if you are not using the _get_audio_hstamp() then the config has 
essentially no effect, and the delay is available separately in the 
status command as before.



  if ((err = snd_pcm_status(pcm, status)) < 0)
  return err;


See this bug report, why it is needed:

https://bugzilla.kernel.org/show_bug.cgi?id=199235

It finally turned out that there was not a bug but just the flag 
missing.

We are using  snd_pcm_status_get_htstamp() with the time smoother
to calculate sink/source latency.


Humm, that looks more like a bug in the fix (20e3f9 'ALSA: pcm: update 
tstamp only in audio_tstamp changed'). I don't think we intended that 
changes in the way the audio_tstamp is calculated (with or without 
delay) would impact when the system timestamp is updated. I am pretty 
sure we only wanted to update the timestamp when the hw_ptr changed 
with this fix so as to go back to the traditional behavior before 
kernel 4.1.


Can you check if just using tstamp_config.type_requested = 1; isn't 
enough for PulseAudio? If not, we have two conflicting desires on when 
the system timestamp should be updated.




It isn't enough. For the time stamp to be updated you need to have 
report_delay set.


___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()

2018-04-03 Thread Georg Chini

On 04.04.2018 08:01, Takashi Iwai wrote:

On Wed, 04 Apr 2018 00:35:24 +0200,
Pierre-Louis Bossart wrote:

On 4/2/18 3:14 PM, Georg Chini wrote:

On 02.04.2018 21:35, Pierre-Louis Bossart wrote:


On 04/02/2018 07:54 AM, Georg Chini wrote:

The current code does not call snd_pcm_status_set_audio_htstamp_config()
to configure the way timestamps are updated in ALSA. This leads to
incorrect time stamps in the status object returned by snd_pcm_status(),
so the computed latencies are wrong.

This patch uses snd_pcm_status_set_audio_htstamp_config() to set the
ALSA report_delay flag to 1 before the call to snd_pcm_status(). With
this, time stamps are updated as expected.
---
   src/modules/alsa/alsa-util.c | 7 +++
   1 file changed, 7 insertions(+)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 61fb4903..b91a0e98 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,6 +1187,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm,
snd_pcm_status_t *status, snd_pcm_sframes
   size_t abs_k;
   int err;
   snd_pcm_sframes_t avail = 0;
+    snd_pcm_audio_tstamp_config_t tstamp_config;
     pa_assert(pcm);
   pa_assert(delay);
@@ -1200,6 +1201,12 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm,
snd_pcm_status_t *status, snd_pcm_sframes
    * avail, delay and timestamp values in a single kernel call
to improve
    * timer-based scheduling */
   +    /* The time stamp configuration needs to be set so that the
+ * ALSA code will use the internal delay reported by the driver */
+    tstamp_config.type_requested = 1; /* ALSA default time stamp
type */
+    tstamp_config.report_delay = 1;
+    snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+

are you sure it's necessary or is this possibly a misunderstanding
of what audio_tstamps are?

this command is only for the audio timestamp, and to the best of my
knowledge you are not using the results using one of the
snd_pcm_status_get_audio_htstamp_* commands

the typical usage (see alsa-lib/test/audio_time.c) is this:

     snd_pcm_status_set_audio_htstamp_config(status, audio_tstamp_config);

     if ((err = snd_pcm_status(handle, status)) < 0) {
         printf("Stream status error: %s\n", snd_strerror(err));
         exit(0);
     }
     snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp);
     snd_pcm_status_get_htstamp(status, timestamp);
     snd_pcm_status_get_audio_htstamp(status, audio_timestamp);
     snd_pcm_status_get_audio_htstamp_report(status, audio_tstamp_report);

if you are not using the _get_audio_hstamp() then the config has
essentially no effect, and the delay is available separately in the
status command as before.


   if ((err = snd_pcm_status(pcm, status)) < 0)
   return err;

See this bug report, why it is needed:

https://bugzilla.kernel.org/show_bug.cgi?id=199235

It finally turned out that there was not a bug but just the flag missing.
We are using  snd_pcm_status_get_htstamp() with the time smoother
to calculate sink/source latency.

Humm, that looks more like a bug in the fix (20e3f9 'ALSA: pcm: update
tstamp only in audio_tstamp changed'). I don't think we intended that
changes in the way the audio_tstamp is calculated (with or without
delay) would impact when the system timestamp is updated. I am pretty
sure we only wanted to update the timestamp when the hw_ptr changed
with this fix so as to go back to the traditional behavior before
kernel 4.1.

The fact here is that hwptr still remains same but only the delay
changes.  As the prior-4.1 traditional behavior, the timestamp isn't
updated in such a case.  Before the commit, the timestamp is always
updated no matter whether hwptr is updated or not, and it broke
applications.

So, the question is how we should look at the timestamp.  The fix is
based on the assumption that tstamp is always coupled with
audio_tstamp, where the latter calculation depends on the tstamp
config flag.

OTOH, if we take rather audio_tstamp always coupled with the
hwptr+delay, we may fix the code in a different way, too.  But this
would need to remember the last delay, and moreover, I don't think
it's better interpretation -- if we read tstamp in that way, what the
heck audio_tstamp is for?


thanks,

Takashi


As already said in the bug discussion, I think the right way to fix
the issue is to exclude runtime->delay from the delay estimation
when report_delay is not set. What caused the problems for me
was that the delay snapshot was taken at another time than the
time stamp indicated. snd_pcm_status_get_delay() includes
the delay, while audio time stamp does not and time stamp is
kept at the time of the last audio time stamp update. So the
values are inconsistent which renders the returned delay unusable
if combined with the time stamp.

If the returned delay is also kept at the value corresponding to the
time stamp, it would be all consis

Re: [pulseaudio-discuss] [PATCH v10 1/3] pipe-source: implement autosuspend behavior

2018-04-09 Thread Georg Chini

On 20.03.2018 13:27, Raman Shyshniou wrote:

Currently the pipe-source will remain running even if no
writer is connected and therefore no data is produced.
This patch prevents this by auto-suspending source
when all writers are disconnected.


The series looks nearly good now, only two small comments remaining.


@@ -102,16 +154,11 @@ static int source_process_msg(
  switch (code) {
  
  case PA_SOURCE_MESSAGE_GET_LATENCY: {

-size_t n = 0;
+pa_usec_t now;
  
-#ifdef FIONREAD

-int l;
+now = pa_rtclock_now();
+*((int64_t*) data) = (int64_t)now - (int64_t)u->timestamp;
  
-if (ioctl(u->fd, FIONREAD, &l) >= 0 && l > 0)

-n = (size_t) l;
-#endif
-
-*((int64_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
  return 0;
  }
  }

It is possible, that the memchunk contains some data you have not posted
when you are resuming from auto suspend. So you have to add the data in
the memchunk to the latency.
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v10 2/3] pipe-source: respect frame boundaries

2018-04-09 Thread Georg Chini

On 20.03.2018 13:27, Raman Shyshniou wrote:

Currently pipe-source doesn't track frame boundaries while posting
data. This can lead to assertion failure in source-output for any
sample specs except s8/u8 mono. The simplest way to reproduce is
to write s24le format to pipe:
$ pacat -r -d null --format=s24le > /tmp/music.input

E: [pipe-source] source-output.c: Assertion
'pa_frame_aligned(chunk->length, &o->source->sample_spec)' failed at
pulsecore/source-output.c:738, function pa_source_output_push().
---
  src/modules/module-pipe-source.c | 52 
  1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index c069dd4..4fd515b 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -202,6 +202,7 @@ static void thread_func(void *userdata) {
  struct userdata *u = userdata;
  struct pollfd *pollfd;
  int read_type = 0;
+size_t fs;
  
  pa_assert(u);
  
@@ -209,6 +210,8 @@ static void thread_func(void *userdata) {
  
  pa_thread_mq_install(&u->thread_mq);
  
+fs = pa_frame_size(&u->source->sample_spec);

+
  u->memchunk.memblock = pa_memblock_new(u->core->mempool, u->pipe_size);
  u->timestamp = pa_rtclock_now();
  
@@ -229,7 +232,7 @@ static void thread_func(void *userdata) {

  pa_assert(u->memchunk.length == 0);
  
  p = pa_memblock_acquire(u->memchunk.memblock);

-l = pa_read(u->fd, p, pa_memblock_get_length(u->memchunk.memblock), 
&read_type);
+l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, 
pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
  pa_memblock_release(u->memchunk.memblock);
  
  if (l < 0) {

@@ -252,6 +255,7 @@ static void thread_func(void *userdata) {
  }
  
  u->writer_connected = false;

+u->memchunk.index = 0;
  }
  
  } else {

@@ -269,21 +273,59 @@ static void thread_func(void *userdata) {
  
  /* Post data to source */

  if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+size_t total_len = u->memchunk.index + u->memchunk.length;
+size_t frames = total_len / fs;
+size_t tail = total_len % fs;
  
  if (u->writer_connected && u->corkfd >= 0) {

  pa_assert_se(pa_close(u->corkfd) == 0);
  u->corkfd = -1;
  }
  
-if (u->memchunk.length > 0) {

+if (frames > 0) {
+pa_memblock *new_memblock;
+
+new_memblock = pa_memblock_new(u->core->mempool, u->pipe_size);
+
+u->memchunk.index = 0;
+u->memchunk.length = frames * fs;
+
+if (tail > 0) {
+void *src, *dst;
+
+dst = pa_memblock_acquire(new_memblock);
+src = pa_memblock_acquire(u->memchunk.memblock);
+
+memcpy(dst, (uint8_t *) src + u->memchunk.length, tail);
+
+pa_memblock_release(u->memchunk.memblock);
+pa_memblock_release(new_memblock);
+}
+
  pa_source_post(u->source, &u->memchunk);
  pa_memblock_unref(u->memchunk.memblock);
-u->memchunk.memblock = pa_memblock_new(u->core->mempool, 
u->pipe_size);
-u->memchunk.length = 0;
+u->memchunk.memblock = new_memblock;
+}
+
+u->memchunk.index = tail;
+u->memchunk.length = 0;
+
+} else if (u->suspended_by_user) {
+size_t total_len = u->memchunk.index + u->memchunk.length;
+size_t frames = total_len / fs;
+size_t tail = total_len % fs;


You can move the three lines above outside the if-block because you
are doing the same thing in both branches.


+
+if (frames > 0 && tail > 0) {
+void *p;
+
+p = pa_memblock_acquire(u->memchunk.memblock);
+memmove(p, (uint8_t *) p + frames * fs, tail);
+pa_memblock_release(u->memchunk.memblock);
  }
  
-} else if (u->suspended_by_user)

+u->memchunk.index = tail;
  u->memchunk.length = 0;
+}
  
  pollfd->events = u->memchunk.length ? 0 : POLLIN;
  



___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] man: unify pactl and pacmd suspend command documentation

2018-04-09 Thread Georg Chini

On 08.04.2018 12:23, Tanu Kaskinen wrote:

The suspend-sink and suspend-source documentation for pacmd was quite
terse, so I copied the more complete documentation from pactl. I
couldn't resist doing some other minor edits along the way too.

Bug-link: https://bugs.freedesktop.org/show_bug.cgi?id=105907
---
  man/pactl.1.xml.in| 32 
  man/pulse-cli-syntax.5.xml.in | 15 ---
  2 files changed, 28 insertions(+), 19 deletions(-)

diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
index 39569b6bb..ee8fd51ea 100644
--- a/man/pactl.1.xml.in
+++ b/man/pactl.1.xml.in
@@ -129,27 +129,27 @@ License along with PulseAudio; if not, see 
.
  
  
  

-  suspend-sink SINK 1|0
+  suspend-sink SINK true|false
Suspend or resume the specified sink (which may be
-  specified either by its symbolic name, or by its numeric index),
-  depending whether 1 (suspend) or 0 (resume) is passed as last
-  argument. Suspending a sink will pause all playback. Depending
-  on the module implementing the sink this might have the effect
-  that the underlying device is closed, making it available for
-  other applications to use. The exact behaviour depends on the
-  module.
+specified either by its name or index), depending whether true
+(suspend) or false (resume) is passed as last argument. Suspending
+a sink will pause all playback. Depending on the module implementing
+the sink this might have the effect that the underlying device is
+closed, making it available for other applications to use. The exact
+behaviour depends on the module.
+  
  
  
  

-  suspend-source SOURCE 1|0
+  suspend-source SOURCE true|false
Suspend or resume the specified source (which may be
-  specified either by its symbolic name, or by its numeric index),
-  depending whether 1 (suspend) or 0 (resume) is passed as last
-  argument. Suspending a source will pause all
-  capturing. Depending on the module implementing the source this
-  might have the effect that the underlying device is closed,
-  making it available for other applications to use. The exact
-  behaviour depends on the module.
+specified either by its name or index), depending whether true
+(suspend) or false (resume) is passed as last argument. Suspending
+a source will pause all capturing. Depending on the module implementing
+the source this might have the effect that the underlying device is
+closed, making it available for other applications to use. The exact
+behaviour depends on the module.
+  
  
  
  

diff --git a/man/pulse-cli-syntax.5.xml.in b/man/pulse-cli-syntax.5.xml.in
index 0a0fabaf0..42cd73a63 100644
--- a/man/pulse-cli-syntax.5.xml.in
+++ b/man/pulse-cli-syntax.5.xml.in
@@ -163,9 +163,18 @@ License along with PulseAudio; if not, see 
.
  
  
  

-  suspend-sink|suspend-source index|name 
boolean
-  Suspend (i.e. disconnect from the underlying hardware) a sink
-  (resp. source).
+  suspend-sink|suspend-source name|index
+true|false
+  
+  Suspend or resume the specified sink or source (which may be
+specified either by its name or index), depending whether true
+(suspend) or false (resume) is passed as last argument. Suspending
+a sink will pause all playback and suspending a source will pause all
+capturing. Depending on the module implementing the sink or source this
+might have the effect that the underlying device is closed, making it
+available for other applications to use. The exact behaviour depends on
+the module.
+  
  
  
  


LGTM

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH] null-source: fix multiple bugs

2018-04-09 Thread Georg Chini
The current null-source implementation has several bugs:

1) The latency reported is the negative of the correct latency.
2) The memchunk passed to pa_source_post() is not initialized
with silence.
3) In PA_SOURCE_MESSAGE_SET_STATE the timestamp is always set
when the source transitions to RUNNING state. This should only
happen when the source transitions from SUSPENDED to RUNNING
but also if it changes from SUSPENDED to IDLE.
4) The timing of the thread function is incorrect. It always
uses u->latency_time, regardless of the specified source
latency.
5) The latency_time argument seems pointless because the source
is defined with dynamic latency.

This patch fixes the issues by
1) inverting the sign of the reported latency,
2) initializing the memchunk with silence,
3) changing the logic in PA_SOURCE_MESSAGE_SET_STATE so that
the timestamp is set when needed,
4) using u->block_usec instead of u->latency_time for setting
the rtpoll timer and checking if the timer has elapsed,
5) removing the latency_time option.
---
 src/modules/module-null-source.c | 43 
 1 file changed, 22 insertions(+), 21 deletions(-)

diff --git a/src/modules/module-null-source.c b/src/modules/module-null-source.c
index 0e4c8d2f..e3aba5dc 100644
--- a/src/modules/module-null-source.c
+++ b/src/modules/module-null-source.c
@@ -51,12 +51,11 @@ PA_MODULE_USAGE(
 "rate= "
 "source_name= "
 "channel_map= "
-"description= "
-"latency_time=");
+"description= ");
 
 #define DEFAULT_SOURCE_NAME "source.null"
-#define DEFAULT_LATENCY_TIME 20
 #define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
+#define MIN_LATENCY_USEC (500)
 
 struct userdata {
 pa_core *core;
@@ -71,7 +70,6 @@ struct userdata {
 
 pa_usec_t block_usec;
 pa_usec_t timestamp;
-pa_usec_t latency_time;
 };
 
 static const char* const valid_modargs[] = {
@@ -81,7 +79,6 @@ static const char* const valid_modargs[] = {
 "source_name",
 "channel_map",
 "description",
-"latency_time",
 NULL
 };
 
@@ -93,7 +90,7 @@ static int source_process_msg(pa_msgobject *o, int code, void 
*data, int64_t off
 pa_usec_t now;
 
 now = pa_rtclock_now();
-*((int64_t*) data) = (int64_t)u->timestamp - (int64_t)now;
+*((int64_t*) data) = (int64_t)now - (int64_t)u->timestamp;
 
 return 0;
 }
@@ -109,8 +106,10 @@ static int source_set_state_in_io_thread_cb(pa_source *s, 
pa_source_state_t new_
 pa_assert(s);
 pa_assert_se(u = s->userdata);
 
-if (new_state == PA_SOURCE_RUNNING)
-u->timestamp = pa_rtclock_now();
+if (s->thread_info.state == PA_SOURCE_SUSPENDED || s->thread_info.state == 
PA_SOURCE_INIT) {
+if (PA_SOURCE_IS_OPENED(new_state))
+u->timestamp = pa_rtclock_now();
+}
 
 return 0;
 }
@@ -123,10 +122,14 @@ static void source_update_requested_latency_cb(pa_source 
*s) {
 pa_assert(u);
 
 u->block_usec = pa_source_get_requested_latency_within_thread(s);
+if (u->block_usec == (pa_usec_t)-1)
+u->block_usec = u->source->thread_info.max_latency;
 }
 
 static void thread_func(void *userdata) {
 struct userdata *u = userdata;
+bool timer_elapsed = false;
+size_t max_block_size;
 
 pa_assert(u);
 
@@ -134,6 +137,7 @@ static void thread_func(void *userdata) {
 
 pa_thread_mq_install(&u->thread_mq);
 
+max_block_size = 
pa_frame_align(pa_mempool_block_size_max(u->core->mempool), 
&u->source->sample_spec);
 u->timestamp = pa_rtclock_now();
 
 for (;;) {
@@ -146,17 +150,20 @@ static void thread_func(void *userdata) {
 
 now = pa_rtclock_now();
 
-if ((chunk.length = pa_usec_to_bytes(now - u->timestamp, 
&u->source->sample_spec)) > 0) {
+if (timer_elapsed && (chunk.length = pa_usec_to_bytes(now - 
u->timestamp, &u->source->sample_spec)) > 0) {
+
+chunk.length = PA_MIN(max_block_size, chunk.length);
 
-chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) 
-1); /* or chunk.length? */
+chunk.memblock = pa_memblock_new(u->core->mempool, 
chunk.length);
 chunk.index = 0;
+pa_silence_memchunk(&chunk, &u->source->sample_spec);
 pa_source_post(u->source, &chunk);
 pa_memblock_unref(chunk.memblock);
 
-u->timestamp = now;
+u->timestamp += pa_bytes_to_usec(chunk.length, 
&u->source->sample_spec);
 }
 
-pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + 
u->latency_time * PA_USEC_PER_MSEC);
+pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + 
u->block_usec);
 } else
 pa_rtpoll_set_timer_disabled(u->rtpoll);
 
@@ -164,6 +171,8 @@ static void thread_func(void *userdata) {
 if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
 goto fail;
 
+timer_elapsed = pa_rtpoll_timer_elapsed(

[pulseaudio-discuss] [PATCH] bluez5-device: Fix memory leak in sco_process_render()

2018-04-09 Thread Georg Chini
sco_process_render does not unref the memblock when it encounters an error.

This patch fixes the issue. It also changes the return value to 1 in the case
of EAGAIN. Because the data was already rendered and cannot be re-sent, we
have to discard the block.
---
 src/modules/bluetooth/module-bluez5-device.c | 12 +---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index 95d288ef..b81c233c 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -282,9 +282,13 @@ static int sco_process_render(struct userdata *u) {
 if (errno == EINTR)
 /* Retry right away if we got interrupted */
 continue;
-else if (errno == EAGAIN)
-/* Hmm, apparently the socket was not writable, give up for now */
-return 0;
+
+pa_memblock_unref(memchunk.memblock);
+
+if (errno == EAGAIN)
+/* Hmm, apparently the socket was not writable, give up for now.
+ * Because the data was already rendered, let's discard the block. 
*/
+return 1;
 
 pa_log_error("Failed to write data to SCO socket: %s", 
pa_cstrerror(errno));
 return -1;
@@ -296,6 +300,8 @@ static int sco_process_render(struct userdata *u) {
 pa_log_error("Wrote memory block to socket only partially! %llu 
written, wanted to write %llu.",
 (unsigned long long) l,
 (unsigned long long) memchunk.length);
+
+pa_memblock_unref(memchunk.memblock);
 return -1;
 }
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH] bluez5-device, bluez5-discover: add hsp_source_buffer_msec argument

2018-04-09 Thread Georg Chini
Currently the PA bluetooth code calls source_push() for each HSP source packet.
The default HSP MTU is 48 bytes, this means that source_push() is called every
3ms, which leads to increased CPU load especially on embedded systems.

This patch adds a hsp_source_buffer_msec argument to module-bluez5-discover and
module-bluez5-device. The argument gives the number of milliseconds of audio 
which
are buffered, before source_push() is called. The value can range from 0 to 
100ms,
and is rounded down to the next multiple of the MTU size. Therefore a value of 
less
than 2*MTU time corresponds to the original behavior.
---
 src/modules/bluetooth/module-bluetooth-discover.c |  1 +
 src/modules/bluetooth/module-bluez5-device.c  | 68 +--
 src/modules/bluetooth/module-bluez5-discover.c| 14 -
 3 files changed, 64 insertions(+), 19 deletions(-)

diff --git a/src/modules/bluetooth/module-bluetooth-discover.c 
b/src/modules/bluetooth/module-bluetooth-discover.c
index 63195d3e..14c0a38f 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -32,6 +32,7 @@ PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
 "headset=ofono|native|auto (bluez 5 only)"
 "autodetect_mtu= (bluez 5 only)"
+"hsp_source_buffer_msec=<0 - 100> (bluez5 only)"
 );
 
 struct userdata {
diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index b81c233c..d40bbb0c 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -54,7 +54,8 @@ PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and 
source");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE("path="
-"autodetect_mtu=");
+"autodetect_mtu="
+"hsp_source_buffer_msec=<0 - 100>");
 
 #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_PLAYBACK_SCO  (25 * PA_USEC_PER_MSEC)
@@ -68,6 +69,7 @@ PA_MODULE_USAGE("path="
 static const char* const valid_modargs[] = {
 "path",
 "autodetect_mtu",
+"hsp_source_buffer_msec",
 NULL
 };
 
@@ -123,6 +125,9 @@ struct userdata {
 pa_card *card;
 pa_sink *sink;
 pa_source *source;
+uint32_t source_buffer_usec;
+uint32_t source_buffered_blocks;
+pa_memchunk source_buffer;
 pa_bluetooth_profile_t profile;
 char *output_port_name;
 char *input_port_name;
@@ -314,10 +319,10 @@ static int sco_process_render(struct userdata *u) {
 /* Run from IO thread */
 static int sco_process_push(struct userdata *u) {
 ssize_t l;
-pa_memchunk memchunk;
 struct cmsghdr *cm;
 struct msghdr m;
 bool found_tstamp = false;
+uint32_t max_blocks;
 pa_usec_t tstamp = 0;
 
 pa_assert(u);
@@ -326,11 +331,17 @@ static int sco_process_push(struct userdata *u) {
 pa_assert(u->source);
 pa_assert(u->read_smoother);
 
-memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
-memchunk.index = memchunk.length = 0;
+max_blocks = u->source_buffer_usec / pa_bytes_to_usec(u->read_block_size, 
&u->source->sample_spec);
+if (max_blocks == 0)
+max_blocks = 1;
+
+if (!u->source_buffer.memblock) {
+u->source_buffer.memblock = pa_memblock_new(u->core->mempool, 
max_blocks * u->read_block_size);
+u->source_buffer.index = u->source_buffer.length = 0;
+}
 
 for (;;) {
-void *p;
+uint8_t *p;
 uint8_t aux[1024];
 struct iovec iov;
 
@@ -343,11 +354,11 @@ static int sco_process_push(struct userdata *u) {
 m.msg_control = aux;
 m.msg_controllen = sizeof(aux);
 
-p = pa_memblock_acquire(memchunk.memblock);
-iov.iov_base = p;
-iov.iov_len = pa_memblock_get_length(memchunk.memblock);
+p = pa_memblock_acquire(u->source_buffer.memblock);
+iov.iov_base = p + u->source_buffer.index;
+iov.iov_len = u->read_block_size;
 l = recvmsg(u->stream_fd, &m, 0);
-pa_memblock_release(memchunk.memblock);
+pa_memblock_release(u->source_buffer.memblock);
 
 if (l > 0)
 break;
@@ -356,8 +367,6 @@ static int sco_process_push(struct userdata *u) {
 /* Retry right away if we got interrupted */
 continue;
 
-pa_memblock_unref(memchunk.memblock);
-
 if (l < 0 && errno == EAGAIN)
 /* Hmm, apparently the socket was not readable, give up for now. */
 return 0;
@@ -366,7 +375,7 @@ static int sco_process_push(struct userdata *u) {
 return -1;
 }
 
-pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock));
+pa_assert((size_t) l <= u->read_block_size);
 
 /* In some rare occasions, we might receive packets of a very strange
  * size. This could potentially be possible if the SCO packet was
@@ -376,11 +385,10 @@ static int sco_process_push(str

[pulseaudio-discuss] [PATCH 2/6] combine-sink: Add rate controller

2018-04-09 Thread Georg Chini
This patch adds a rate controller similar to the one used in module-loopback
to limit step size and maximum deviation from the base rate. Rate changes
are handled more smoothly by the controller. The patch has not much impact
on the behavior of the module, except that there is less rate hunting.
---
 src/modules/module-combine-sink.c | 53 ++-
 1 file changed, 36 insertions(+), 17 deletions(-)

diff --git a/src/modules/module-combine-sink.c 
b/src/modules/module-combine-sink.c
index 75677fb0..7c111246 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -200,6 +200,36 @@ static void output_enable(struct output *o);
 static void output_free(struct output *o);
 static int output_create_sink_input(struct output *o);
 
+/* rate controller, called from main context
+ * - maximum deviation from base rate is less than 1%
+ * - controller step size is limited to 2.01‰
+ * - exhibits hunting with USB or Bluetooth devices
+ */
+static uint32_t rate_controller(
+struct output *o,
+uint32_t base_rate, uint32_t old_rate,
+int32_t latency_difference_usec) {
+
+double new_rate, new_rate_1, new_rate_2;
+double min_cycles_1, min_cycles_2;
+
+/* Calculate next rate that is not more than 2‰ away from the last rate */
+min_cycles_1 = (double)abs(latency_difference_usec) / 
o->userdata->adjust_time / 0.002 + 1;
+new_rate_1 = old_rate + base_rate * (double)latency_difference_usec / 
min_cycles_1 / o->userdata->adjust_time;
+
+/* Calculate best rate to correct the current latency offset, limit at
+ * 1% difference from base_rate */
+min_cycles_2 = (double)abs(latency_difference_usec) / 
o->userdata->adjust_time / 0.01 + 1;
+new_rate_2 = (double)base_rate * (1.0 + (double)latency_difference_usec / 
min_cycles_2 / o->userdata->adjust_time);
+
+/* Choose the rate that is nearer to base_rate */
+new_rate = new_rate_2;
+if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate))
+new_rate = new_rate_1;
+
+return (uint32_t)(new_rate + 0.5);
+}
+
 static void adjust_rates(struct userdata *u) {
 struct output *o;
 struct sink_snapshot rdata;
@@ -298,29 +328,18 @@ static void adjust_rates(struct userdata *u) {
 
 base_rate = u->sink->sample_spec.rate;
 
+/* Calculate and set rates for the sink inputs. */
 PA_IDXSET_FOREACH(o, u->outputs, idx) {
-uint32_t new_rate = base_rate;
-uint32_t current_rate;
+uint32_t new_rate;
+int32_t latency_difference;
 
 if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
 continue;
 
-current_rate = o->sink_input->sample_spec.rate;
-
-if (o->total_latency != target_latency)
-new_rate += (uint32_t) (((double) o->total_latency - (double) 
target_latency) / (double) u->adjust_time * (double) new_rate);
+latency_difference = (int64_t)o->total_latency - 
(int64_t)target_latency;
+new_rate = rate_controller(o, base_rate, 
o->sink_input->sample_spec.rate, latency_difference);
 
-if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) 
(base_rate*1.25)) {
-pa_log_warn("[%s] sample rates too different, not adjusting (%u 
vs. %u).", o->sink_input->sink->name, base_rate, new_rate);
-new_rate = base_rate;
-} else {
-/* Do the adjustment in small steps; 2‰ can be considered 
inaudible */
-if (new_rate < (uint32_t) (current_rate*0.998) || new_rate > 
(uint32_t) (current_rate*1.002)) {
-pa_log_info("[%s] new rate of %u Hz not within 2‰ of %u Hz, 
forcing smaller adjustment", o->sink_input->sink->name, new_rate, current_rate);
-new_rate = PA_CLAMP(new_rate, (uint32_t) (current_rate*0.998), 
(uint32_t) (current_rate*1.002));
-}
-pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f.", 
o->sink_input->sink->name, new_rate, (double) new_rate / base_rate);
-}
+pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f.", 
o->sink_input->sink->name, new_rate, (double) new_rate / base_rate);
 pa_sink_input_set_rate(o->sink_input, new_rate);
 }
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 1/6] combine-sink: Fix latency calculations

2018-04-09 Thread Georg Chini
Currently module-combine-sink uses only a rough estimate of the current
slave sink latencies to calculate the rate for the various sink inputs.
This leads to very inexact and unstable latency reports for the virtual
sink.

This patch fixes the issue by introducing latency snapshots like they
are used in module-loopback. It also changes the definition of the
target latency to ensure that there is always one sink which uses the
base rate.
---
 src/modules/module-combine-sink.c | 176 +++---
 1 file changed, 145 insertions(+), 31 deletions(-)

diff --git a/src/modules/module-combine-sink.c 
b/src/modules/module-combine-sink.c
index f7649a36..75677fb0 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -115,6 +115,14 @@ struct output {
 
 /* For communication of the stream latencies to the main thread */
 pa_usec_t total_latency;
+struct {
+pa_usec_t timestamp;
+pa_usec_t sink_latency;
+size_t output_memblockq_size;
+uint64_t receive_counter;
+} latency_snapshot;
+
+uint64_t receive_counter;
 
 /* For communication of the stream parameters to the sink thread */
 pa_atomic_t max_request;
@@ -158,21 +166,33 @@ struct userdata {
 bool in_null_mode;
 pa_smoother *smoother;
 uint64_t counter;
+
+uint64_t snapshot_counter;
+pa_usec_t snapshot_time;
+
+pa_usec_t render_timestamp;
 } thread_info;
 };
 
+struct sink_snapshot {
+pa_usec_t timestamp;
+uint64_t send_counter;
+};
+
 enum {
 SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX,
 SINK_MESSAGE_REMOVE_OUTPUT,
 SINK_MESSAGE_NEED,
 SINK_MESSAGE_UPDATE_LATENCY,
 SINK_MESSAGE_UPDATE_MAX_REQUEST,
-SINK_MESSAGE_UPDATE_LATENCY_RANGE
+SINK_MESSAGE_UPDATE_LATENCY_RANGE,
+SINK_MESSAGE_GET_SNAPSHOT
 };
 
 enum {
 SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
-SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY
+SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
+SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
 };
 
 static void output_disable(struct output *o);
@@ -182,10 +202,16 @@ static int output_create_sink_input(struct output *o);
 
 static void adjust_rates(struct userdata *u) {
 struct output *o;
-pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, 
target_latency, avg_total_latency = 0;
+struct sink_snapshot rdata;
+pa_usec_t avg_total_latency = 0;
+pa_usec_t target_latency = 0;
+pa_usec_t max_sink_latency = 0;
+pa_usec_t min_total_latency = (pa_usec_t)-1;
 uint32_t base_rate;
 uint32_t idx;
 unsigned n = 0;
+pa_usec_t now;
+struct output *o_max;
 
 pa_assert(u);
 pa_sink_assert_ref(u->sink);
@@ -193,42 +219,82 @@ static void adjust_rates(struct userdata *u) {
 if (pa_idxset_size(u->outputs) <= 0)
 return;
 
-if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
+if (pa_sink_get_state(u->sink) != PA_SINK_RUNNING)
+return;
+
+/* Get sink snapshot */
+pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), 
SINK_MESSAGE_GET_SNAPSHOT, &rdata, 0, NULL);
+
+/* The sink snapshot time is the time when the last data was rendered.
+ * Latency is calculated for that point in time. */
+now = rdata.timestamp;
+
+/* Sink snapshot is not yet valid. */
+if (!now)
 return;
 
 PA_IDXSET_FOREACH(o, u->outputs, idx) {
-pa_usec_t sink_latency;
+pa_usec_t snapshot_latency;
+int64_t time_difference;
 
 if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
 continue;
 
-o->total_latency = pa_sink_input_get_latency(o->sink_input, 
&sink_latency);
-o->total_latency += sink_latency;
-
-if (sink_latency > max_sink_latency)
-max_sink_latency = sink_latency;
-
-if (min_total_latency == (pa_usec_t) -1 || o->total_latency < 
min_total_latency)
+/* The difference may become negative, because it is probable, that 
the last
+ * render time was before the sink input snapshot. In this case, the 
sink
+ * had some more latency at the render time, so subtracting the value 
still
+ * gives the right result. */
+time_difference = (int64_t)now - 
(int64_t)o->latency_snapshot.timestamp;
+
+/* Latency at sink snapshot time is sink input snapshot latency minus 
time
+ * passed between the two snapshots. */
+snapshot_latency = o->latency_snapshot.sink_latency
+   + 
pa_bytes_to_usec(o->latency_snapshot.output_memblockq_size, 
&o->sink_input->sample_spec)
+   - time_difference;
+
+/* Add the data that was sent between taking the sink input snapshot
+ * and the sink snapshot. */
+snapshot_latency += pa_bytes_to_usec(rdata.send_counter - 
o->latency_snapshot.receive_counter, &o->sink_input->sample_spec);
+
+/* This is the cu

[pulseaudio-discuss] [PATCH 3/6] combine-sink: Improve initial latency reports

2018-04-09 Thread Georg Chini
Currently, it takes one adjust time before the smoother is updated after an
unsuspend. Before the first update, the smoother will not be aware of the
slave sink latencies, leading to incorrect latency reports.

This patch moves the first smoother update to one latency time after the
sink was unsuspended, thereby improving initial latency reports. This
only partially resolves the problem because the smoother takes multiple
updates to adapt to the slave sink latencies.
---
 src/modules/module-combine-sink.c | 10 +++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/modules/module-combine-sink.c 
b/src/modules/module-combine-sink.c
index 7c111246..559f6575 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -788,9 +788,6 @@ static void unsuspend(struct userdata *u) {
 PA_IDXSET_FOREACH(o, u->outputs, idx)
 output_enable(o);
 
-if (!u->time_event && u->adjust_time > 0)
-u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + 
u->adjust_time, time_callback, u);
-
 pa_log_info("Resumed successfully...");
 }
 
@@ -822,6 +819,13 @@ static int sink_set_state_in_main_thread_cb(pa_sink *sink, 
pa_sink_state_t state
 if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED)
 unsuspend(u);
 
+/* The first smoother update should be done early, otherwise the 
smoother will
+ * not be aware of the slave sink latencies and report far too 
small values.
+ * This is especially important if after an unsuspend the sink 
runs on a different
+ * latency than before. */
+if (state == PA_SINK_RUNNING && !u->time_event && u->adjust_time > 
0)
+u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + 
pa_sink_get_requested_latency(u->sink), time_callback, u);
+
 break;
 
 case PA_SINK_UNLINKED:
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 0/6] combine-sink, tunnel-sink: Latency fixes

2018-04-09 Thread Georg Chini
This series fixes the latency calculations for module-tunnel-sink and
module-combine-sink. In some situations, both modules did not take into
account all components that contribute to the latency which lead to
jumps in the reported values.

Additionally, module-tunnel used a fixed latency of 250ms which seems
unnecessary high. The fixed latency can now be configured.

Default adjust times are updated to reflect the increased reliability
of the latency reports.

Georg Chini (6):
  combine-sink: Fix latency calculations
  combine-sink: Add rate controller
  combine-sink: Improve initial latency reports
  combine-sink: Use configured resampler, reduce update time to 1s
  tunnel: Fix latency calculations
  tunnel: Make fixed latency configurable

 src/modules/module-combine-sink.c | 242 ++
 src/modules/module-tunnel.c   |  75 +---
 2 files changed, 247 insertions(+), 70 deletions(-)

-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 6/6] tunnel: Make fixed latency configurable

2018-04-09 Thread Georg Chini
Currently, module-tunnel uses the default fixed latency of 250ms as fixed
latency.

There is no reason for such a large latency. This patch adds a parameter
latency_msec to the module to set the fixed latency at load time of the
module. The parameter can range from 5 to 500 milliseconds. With this
patch, I was able to run a tunnel sink at 7ms latency without problems.
---
 src/modules/module-tunnel.c | 27 +--
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index c21c7847..2068deca 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -79,6 +79,7 @@ PA_MODULE_USAGE(
 "format= "
 "channels= "
 "rate= "
+"latency_msec= "
 "channel_map=");
 #else
 PA_MODULE_DESCRIPTION("Tunnel module for sources");
@@ -92,6 +93,7 @@ PA_MODULE_USAGE(
 "format= "
 "channels= "
 "rate= "
+"latency_msec= "
 "channel_map=");
 #endif
 
@@ -106,6 +108,7 @@ static const char* const valid_modargs[] = {
 "format",
 "channels",
 "rate",
+"latency_msec",
 #ifdef TUNNEL_SINK
 "sink_name",
 "sink_properties",
@@ -135,8 +138,7 @@ enum {
 SINK_MESSAGE_POST
 };
 
-#define DEFAULT_TLENGTH_MSEC 150
-#define DEFAULT_MINREQ_MSEC 25
+#define DEFAULT_LATENCY_MSEC 100
 
 #else
 
@@ -147,7 +149,7 @@ enum {
 SOURCE_MESSAGE_GET_LATENCY_SNAPSHOT
 };
 
-#define DEFAULT_FRAGSIZE_MSEC 25
+#define DEFAULT_LATENCY_MSEC 25
 
 #endif
 
@@ -213,6 +215,7 @@ struct userdata {
 uint32_t ctag;
 uint32_t device_index;
 uint32_t channel;
+uint32_t latency;
 
 int64_t counter;
 uint64_t receive_counter;
@@ -1672,11 +1675,11 @@ static void setup_complete_callback(pa_pdispatch *pd, 
uint32_t command, uint32_t
 u->maxlength = 4*1024*1024;
 
 #ifdef TUNNEL_SINK
-u->tlength = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * 
DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec);
-u->minreq = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * 
DEFAULT_MINREQ_MSEC, &u->sink->sample_spec);
+u->tlength = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * u->latency, 
&u->sink->sample_spec);
+u->minreq = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * u->latency / 4, 
&u->sink->sample_spec);
 u->prebuf = u->tlength;
 #else
-u->fragsize = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * 
DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec);
+u->fragsize = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * u->latency, 
&u->source->sample_spec);
 #endif
 
 #ifdef TUNNEL_SINK
@@ -1946,6 +1949,7 @@ int pa__init(pa_module*m) {
 pa_sample_spec ss;
 pa_channel_map map;
 char *dn = NULL;
+uint32_t latency_msec;
 #ifdef TUNNEL_SINK
 pa_sink_new_data data;
 #else
@@ -2009,6 +2013,15 @@ int pa__init(pa_module*m) {
 goto fail;
 }
 
+/* Allow latencies between 5ms and 500ms */
+latency_msec = DEFAULT_LATENCY_MSEC;
+if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || 
latency_msec < 5 || latency_msec > 500) {
+pa_log("Invalid latency specification");
+goto fail;
+}
+
+u->latency = latency_msec;
+
 cookie_path = pa_modargs_get_value(ma, "cookie", NULL);
 server = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL));
 
@@ -2190,6 +2203,7 @@ int pa__init(pa_module*m) {
 
 pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 pa_sink_set_rtpoll(u->sink, u->rtpoll);
+pa_sink_set_fixed_latency(u->sink, latency_msec * PA_USEC_PER_MSEC);
 
 #else
 
@@ -2230,6 +2244,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, latency_msec * PA_USEC_PER_MSEC);
 
 u->mcalign = pa_mcalign_new(pa_frame_size(&u->source->sample_spec));
 #endif
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 4/6] combine-sink: Use configured resampler, reduce update time to 1s

2018-04-09 Thread Georg Chini
Currently the combine-sink uses the trivial resampler by default.

This patch changes the default to the configured resampler.
Also the default update time is changed from 10s to 1s to achieve
faster convergence and higher precision.
---
 src/modules/module-combine-sink.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/modules/module-combine-sink.c 
b/src/modules/module-combine-sink.c
index 559f6575..79b4c6d9 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -64,7 +64,7 @@ PA_MODULE_USAGE(
 
 #define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
 
-#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
+#define DEFAULT_ADJUST_TIME_USEC (1*PA_USEC_PER_SEC)
 
 #define BLOCK_USEC (PA_USEC_PER_MSEC * 200)
 
@@ -1421,7 +1421,7 @@ int pa__init(pa_module*m) {
 struct userdata *u;
 pa_modargs *ma = NULL;
 const char *slaves, *rm;
-int resample_method = PA_RESAMPLER_TRIVIAL;
+int resample_method;
 pa_sample_spec ss;
 pa_channel_map map;
 struct output *o;
@@ -1437,6 +1437,7 @@ int pa__init(pa_module*m) {
 goto fail;
 }
 
+resample_method = m->core->resample_method;
 if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) {
 if ((resample_method = pa_parse_resample_method(rm)) < 0) {
 pa_log("invalid resample method '%s'", rm);
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 5/6] tunnel: Fix latency calculations

2018-04-09 Thread Georg Chini
Currently module-tunnel uses only a rough estimate of the current stream
latency and reports wrong latencies in certain situations. This leads to
very inexact and unstable latency reports for the virtual sink.

This patch fixes the issue by introducing latency snapshots like they
are used in module-loopback. Because the latency reports are now correct,
the update interval for latency re-calculations can be reduced to 1s.
---
 src/modules/module-tunnel.c | 48 +
 1 file changed, 36 insertions(+), 12 deletions(-)

diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index a9f26ad7..c21c7847 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -121,7 +121,7 @@ static const char* const valid_modargs[] = {
 
 #define DEFAULT_TIMEOUT 5
 
-#define LATENCY_INTERVAL (10*PA_USEC_PER_SEC)
+#define LATENCY_INTERVAL (1*PA_USEC_PER_SEC)
 
 #define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
 
@@ -131,6 +131,7 @@ enum {
 SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX,
 SINK_MESSAGE_REMOTE_SUSPEND,
 SINK_MESSAGE_UPDATE_LATENCY,
+SINK_MESSAGE_GET_LATENCY_SNAPSHOT,
 SINK_MESSAGE_POST
 };
 
@@ -142,7 +143,8 @@ enum {
 enum {
 SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
 SOURCE_MESSAGE_REMOTE_SUSPEND,
-SOURCE_MESSAGE_UPDATE_LATENCY
+SOURCE_MESSAGE_UPDATE_LATENCY,
+SOURCE_MESSAGE_GET_LATENCY_SNAPSHOT
 };
 
 #define DEFAULT_FRAGSIZE_MSEC 25
@@ -212,7 +214,9 @@ struct userdata {
 uint32_t device_index;
 uint32_t channel;
 
-int64_t counter, counter_delta;
+int64_t counter;
+uint64_t receive_counter;
+uint64_t receive_snapshot;
 
 bool remote_corked:1;
 bool remote_suspended:1;
@@ -517,6 +521,13 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 return 0;
 }
 
+case SINK_MESSAGE_GET_LATENCY_SNAPSHOT: {
+int64_t *send_counter = data;
+
+*send_counter = u->counter;
+return 0;
+}
+
 case SINK_MESSAGE_REQUEST:
 
 pa_assert(offset > 0);
@@ -559,7 +570,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void 
*data, int64_t offse
 
 pa_pstream_send_memblock(u->pstream, u->channel, 0, 
PA_SEEK_RELATIVE, chunk);
 
-u->counter_delta += (int64_t) chunk->length;
+u->receive_counter += chunk->length;
 
 return 0;
 }
@@ -628,6 +639,13 @@ static int source_process_msg(pa_msgobject *o, int code, 
void *data, int64_t off
 return 0;
 }
 
+case SOURCE_MESSAGE_GET_LATENCY_SNAPSHOT: {
+int64_t *send_counter = data;
+
+*send_counter = u->counter;
+return 0;
+}
+
 case SOURCE_MESSAGE_POST: {
 pa_memchunk c;
 
@@ -779,6 +797,9 @@ static void stream_get_latency_callback(pa_pdispatch *pd, 
uint32_t command, uint
 struct timeval local, remote, now;
 pa_sample_spec *ss;
 int64_t delay;
+#ifdef TUNNEL_SINK
+uint64_t send_counter;
+#endif
 
 pa_assert(pd);
 pa_assert(u);
@@ -826,7 +847,7 @@ static void stream_get_latency_callback(pa_pdispatch *pd, 
uint32_t command, uint
 pa_gettimeofday(&now);
 
 /* Calculate transport usec */
-if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
+if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now) < 
0) {
 /* local and remote seem to have synchronized clocks */
 #ifdef TUNNEL_SINK
 u->transport_usec = pa_timeval_diff(&remote, &local);
@@ -859,11 +880,12 @@ static void stream_get_latency_callback(pa_pdispatch *pd, 
uint32_t command, uint
 delay += (int64_t) u->transport_usec;
 #endif
 
-/* Now correct by what we have have read/written since we requested the 
update */
+/* Now correct by what we have have written since we requested the update. 
This
+ * is not necessary for the source, because if data is received between 
request
+ * and reply, it was already posted before we requested the source 
latency. */
 #ifdef TUNNEL_SINK
-delay += (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);
-#else
-delay -= (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);
+pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), 
SINK_MESSAGE_GET_LATENCY_SNAPSHOT, &send_counter, 0, NULL);
+delay += (int64_t) pa_bytes_to_usec(send_counter - u->receive_snapshot, 
ss);
 #endif
 
 #ifdef TUNNEL_SINK
@@ -901,7 +923,7 @@ static void request_latency(struct userdata *u) {
 pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, 
stream_get_latency_callback, u, NULL);
 
 u->ignore_latency_before = tag;
-u->counter_delta = 0;
+u->receive_snapshot = u->receive_counter;
 }
 
 /* Called from main context */
@@ -1815,7 +1837,7 @@ static void pstream_memblock_callback(pa_pstream *p, 
uint32_t channel, int64_t o
 
 pa_asyncmsgq_send(u->source->a

[pulseaudio-discuss] [PATCH 01/10] pulsecore: Add alternative time smoother implementation

2018-04-09 Thread Georg Chini
This patch adds an alternative time smoother implementation based on the theory
found at 
https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt.

The functions were written to replace the current smoother functions nearly on
a one-to-one basis, though there are a few differences:
- The smoother_2_put() function takes a byte count instead of a sound card
  time as argument. This was changed because in most places a sample count
  was converted to a time before passing it to the smoother.
- The smoother needs to know sample rate and frame size to convert byte
  counts to time.
- A smoother_2_get_delay() function was added to directly retrieve the stream
  delay from the smoother.
- A hack for USB devices was added which works around an issue in the alsa
  latency reports for USB devices.

The smoother delivers much better precision than the current implementation.
For results, see the document referenced above.

The new functions are still unused. The following patches will convert all
callers of the smoother functions so that they can use both smoother
implementations, depending on a configure option.
---
 po/POTFILES.in  |   1 +
 src/Makefile.am |   1 +
 src/pulsecore/time-smoother_2.c | 408 
 src/pulsecore/time-smoother_2.h |  53 ++
 4 files changed, 463 insertions(+)
 create mode 100644 src/pulsecore/time-smoother_2.c
 create mode 100644 src/pulsecore/time-smoother_2.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0b519464..1269435f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -170,6 +170,7 @@ src/pulsecore/thread-mq.c
 src/pulsecore/thread-posix.c
 src/pulsecore/thread-win32.c
 src/pulsecore/time-smoother.c
+src/pulsecore/time-smoother_2.c
 src/pulsecore/tokenizer.c
 src/pulsecore/x11prop.c
 src/pulsecore/x11wrap.c
diff --git a/src/Makefile.am b/src/Makefile.am
index aba8e1f2..59b703db 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -733,6 +733,7 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/svolume_mmx.c pulsecore/svolume_sse.c \
pulsecore/tagstruct.c pulsecore/tagstruct.h \
pulsecore/time-smoother.c pulsecore/time-smoother.h \
+   pulsecore/time-smoother_2.c pulsecore/time-smoother_2.h \
pulsecore/tokenizer.c pulsecore/tokenizer.h \
pulsecore/usergroup.c pulsecore/usergroup.h \
pulsecore/sndfile-util.c pulsecore/sndfile-util.h \
diff --git a/src/pulsecore/time-smoother_2.c b/src/pulsecore/time-smoother_2.c
new file mode 100644
index ..8f4447e0
--- /dev/null
+++ b/src/pulsecore/time-smoother_2.c
@@ -0,0 +1,408 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see .
+***/
+
+/* The code in this file is based on the theoretical background found at
+ * https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt.
+ * The theory has never been reviewed, so it may be inaccurate in places. */
+
+#ifdef HAVE_CONFIG_H
+#include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+
+#include "time-smoother_2.h"
+
+struct pa_smoother_2 {
+/* Values set when the smoother is created */
+pa_usec_t smoother_window_time;
+uint32_t rate;
+uint32_t frame_size;
+
+/* USB hack parameters */
+bool usb_hack;
+bool enable_usb_hack;
+uint32_t hack_threshold;
+
+/* Smoother state */
+bool init;
+bool paused;
+
+/* Current byte count start value */
+double start_pos;
+/* System time corresponding to start_pos */
+pa_usec_t start_time;
+/* Conversion factor between time domains */
+double time_factor;
+
+/* Used if the smoother is paused while still in init state */
+pa_usec_t fixup_time;
+
+/* Time offset for USB devices */
+int64_t time_offset;
+
+/* Various time stamps */
+pa_usec_t resume_time;
+pa_usec_t pause_time;
+pa_usec_t smoother_start_time;
+pa_usec_t last_time;
+
+/* Variables used for Kalman filter */
+double time_variance;
+double time_factor_variance;
+double kalman_variance;
+
+/* Variables used for low pass filter */
+double drift_filter;
+double drift_filter_1;
+};
+
+/* Create new smoother */
+pa_smoother_2* pa_smoother_2_new(pa_usec_t window, pa_usec_t time_stamp, 
uint32_t frame_size, uin

[pulseaudio-discuss] [PATCH 00/10] pulsecore: Add new time smoother

2018-04-09 Thread Georg Chini
This patch set adds a new time smoother to PA, which delivers higher
precision than the current version. More details in the commit message
of the first patch.

The first patch adds the functionality, the other patches are a
straight forward addition of the new smoother functions to all
callers of the time smoother. A configure option is used to switch
between the two smoother implementations. It is currently disabled
by default but should be enabled once some broader testing was done.

The last three patches could not be tested, so any tests would be
appreciated.

Georg Chini (10):
  pulsecore: Add alternative time smoother implementation
  Add configure option --enable-smoother-2 to enable alternative
smoother code
  alsa sink/source: Allow alsa to use alternative smoother code
  bluetooth: Allow bluetooth to use alternative smoother code
  stream: Allow stream.c to use alternative smoother code
  combine-sink: Allow module-combine-sink to use alternative smoother
code
  tunnel: Allow module-tunnel to use alternative smoother code
  esound-sink: Allow module-esound-sink to use alternative smoother code
  raop-sink: Allow module-raop-sink to use alternative smoother code
  solaris: Allow module-solaris to use alternative smoother code

 configure.ac |   7 +
 po/POTFILES.in   |   1 +
 src/Makefile.am  |   1 +
 src/modules/alsa/alsa-sink.c |  86 +-
 src/modules/alsa/alsa-source.c   |  67 -
 src/modules/bluetooth/module-bluez5-device.c |  57 +++-
 src/modules/module-combine-sink.c|  52 +++-
 src/modules/module-esound-sink.c |  51 +++-
 src/modules/module-solaris.c |  43 +++
 src/modules/module-tunnel.c  |  72 -
 src/modules/raop/raop-sink.c |  40 ++-
 src/pulse/internal.h |  10 +
 src/pulse/stream.c   |  52 +++-
 src/pulsecore/time-smoother_2.c  | 408 +++
 src/pulsecore/time-smoother_2.h  |  53 
 15 files changed, 979 insertions(+), 21 deletions(-)
 create mode 100644 src/pulsecore/time-smoother_2.c
 create mode 100644 src/pulsecore/time-smoother_2.h

-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 03/10] alsa sink/source: Allow alsa to use alternative smoother code

2018-04-09 Thread Georg Chini
---
 src/modules/alsa/alsa-sink.c   | 86 --
 src/modules/alsa/alsa-source.c | 67 ++--
 2 files changed, 146 insertions(+), 7 deletions(-)

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index eb79a444..d2e495e6 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -51,7 +51,12 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
 
 #include 
 
@@ -77,11 +82,15 @@
 #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*/
 
+#ifdef USE_SMOOTHER_2
+#define SMOOTHER_WINDOW_USEC  (15*PA_USEC_PER_SEC) /* 15s   -- 
smoother windows size */
+#else
 #define SMOOTHER_WINDOW_USEC  (10*PA_USEC_PER_SEC) /* 10s   -- 
smoother windows size */
 #define SMOOTHER_ADJUST_USEC  (1*PA_USEC_PER_SEC)  /* 1s-- 
smoother adjust time */
 
 #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 interval */
+#endif
 
 #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 */
 
@@ -144,11 +153,18 @@ struct userdata {
 
 pa_rtpoll_item *alsa_rtpoll_item;
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *smoother;
+#else
 pa_smoother *smoother;
+#endif
 uint64_t write_count;
 uint64_t since_start;
+
+#ifndef USE_SMOOTHER_2
 pa_usec_t smoother_interval;
 pa_usec_t last_smoother_update;
+#endif
 
 pa_idxset *formats;
 
@@ -870,7 +886,10 @@ static void update_smoother(struct userdata *u) {
 snd_pcm_sframes_t delay = 0;
 int64_t position;
 int err;
-pa_usec_t now1 = 0, now2;
+pa_usec_t now1 = 0;
+#ifndef USE_SMOOTHER_2
+pa_usec_t now2;
+#endif
 snd_pcm_status_t *status;
 snd_htimestamp_t htstamp = { 0, 0 };
 
@@ -893,13 +912,16 @@ static void update_smoother(struct userdata *u) {
 if (now1 <= 0)
 now1 = pa_rtclock_now();
 
+position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) 
u->frame_size);
+
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_put(u->smoother, now1, position);
+#else
 /* 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;
 
@@ -910,18 +932,26 @@ static void update_smoother(struct userdata *u) {
 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);
+#endif
 }
 
 static int64_t sink_get_latency(struct userdata *u) {
 int64_t delay;
-pa_usec_t now1, now2;
+pa_usec_t now1;
+#ifndef USE_SMOOTHER_2
+pa_usec_t now2;
+#endif
 
 pa_assert(u);
 
 now1 = pa_rtclock_now();
+#ifdef USE_SMOOTHER_2
+delay = pa_smoother_2_get_delay(u->smoother, now1, u->write_count);
+#else
 now2 = pa_smoother_get(u->smoother, now1);
 
 delay = (int64_t) pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) 
- (int64_t) now2;
+#endif
 
 if (u->memchunk.memblock)
 delay += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
@@ -947,7 +977,11 @@ static void suspend(struct userdata *u) {
 pa_assert(u);
 pa_assert(u->pcm_handle);
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_pause(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_pause(u->smoother, pa_rtclock_now());
+#endif
 
 /* Let's suspend -- we don't call snd_pcm_drain() here since that might
  * take awfully long with our long buffer sizes today. */
@@ -1145,9 +1179,13 @@ static int unsuspend(struct userdata *u) {
 goto fail;
 
 u->write_count = 0;
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
 u->smoother_interval = SMOOTHER_MIN_INTERVAL;
 u->last_smoother_update = 0;
+#endif
 
 u->first = true;
 u->since_start = 0;
@@ -1693,6 +1731,9 @@ static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec 
*spec, bool passthroug
 if (!PA_SINK_IS_OPENED(s->state)) {
 pa_log_info("Updating rate for device %s, new rate is %d", 
u->device_name, spec->rate);
 u->sink->sample_spec.rate = spec->rate;
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_set_rate(u->smoother

[pulseaudio-discuss] [PATCH 05/10] stream: Allow stream.c to use alternative smoother code

2018-04-09 Thread Georg Chini
---
 src/pulse/internal.h | 10 ++
 src/pulse/stream.c   | 52 +++-
 2 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 01d2b6e4..0d18aa71 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -40,7 +40,13 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
+
 #ifdef HAVE_DBUS
 #include 
 #endif
@@ -204,7 +210,11 @@ struct pa_stream {
 pa_time_event *auto_timing_update_event;
 pa_usec_t auto_timing_interval_usec;
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *smoother;
+#else
 pa_smoother *smoother;
+#endif
 
 /* Callbacks */
 pa_stream_notify_cb_t state_callback;
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index ee95757f..461eb6dd 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -48,9 +48,11 @@
 #define AUTO_TIMING_INTERVAL_START_USEC (10*PA_USEC_PER_MSEC)
 #define AUTO_TIMING_INTERVAL_END_USEC (1500*PA_USEC_PER_MSEC)
 
-#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
 #define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
+#ifndef USE_SMOOTHER_2
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
 #define SMOOTHER_MIN_HISTORY (4)
+#endif
 
 pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec 
*ss, const pa_channel_map *map) {
 return pa_stream_new_with_proplist(c, name, ss, map, NULL);
@@ -303,7 +305,11 @@ static void stream_free(pa_stream *s) {
 pa_proplist_free(s->proplist);
 
 if (s->smoother)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_free(s->smoother);
+#else
 pa_smoother_free(s->smoother);
+#endif
 
 for (i = 0; i < s->n_formats; i++)
 pa_format_info_free(s->req_formats[i]);
@@ -463,7 +469,11 @@ static void check_smoother_status(pa_stream *s, bool 
aposteriori, bool force_sta
 }
 
 if (s->suspended || s->corked || force_stop)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_pause(s->smoother, x);
+#else
 pa_smoother_pause(s->smoother, x);
+#endif
 else if (force_start || s->buffer_attr.prebuf == 0) {
 
 if (!s->timing_info_valid &&
@@ -482,7 +492,11 @@ static void check_smoother_status(pa_stream *s, bool 
aposteriori, bool force_sta
 return;
 }
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_resume(s->smoother, x);
+#else
 pa_smoother_resume(s->smoother, x, true);
+#endif
 }
 
 /* Please note that we have no idea if playback actually started
@@ -1120,6 +1134,11 @@ void pa_create_stream_callback(pa_pdispatch *pd, 
uint32_t command, uint32_t tag,
 s->sample_spec = ss;
 }
 
+#ifdef USE_SMOOTHER_2
+if (s->flags & PA_STREAM_INTERPOLATE_TIMING)
+pa_smoother_2_set_sample_spec(s->smoother, pa_rtclock_now(), 
&s->sample_spec);
+#endif
+
 if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) {
 pa_usec_t usec;
 
@@ -1254,6 +1273,9 @@ static int create_stream(
 x = pa_rtclock_now();
 
 pa_assert(!s->smoother);
+#ifdef USE_SMOOTHER_2
+s->smoother = pa_smoother_2_new(SMOOTHER_HISTORY_TIME, x, 0, 0);
+#else
 s->smoother = pa_smoother_new(
 SMOOTHER_ADJUST_TIME,
 SMOOTHER_HISTORY_TIME,
@@ -1262,6 +1284,7 @@ static int create_stream(
 SMOOTHER_MIN_HISTORY,
 x,
 true);
+#endif
 }
 
 if (!dev)
@@ -1792,6 +1815,12 @@ static pa_usec_t calc_time(pa_stream *s, bool 
ignore_transport) {
 return usec;
 }
 
+#ifdef USE_SMOOTHER_2
+static inline size_t calc_bytes(pa_stream *s, bool ignore_transport) {
+return pa_usec_to_bytes(calc_time(s, ignore_transport), &s->sample_spec);
+}
+#endif
+
 static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t 
command, uint32_t tag, pa_tagstruct *t, void *userdata) {
 pa_operation *o = userdata;
 struct timeval local, remote, now;
@@ -1950,15 +1979,27 @@ static void 
stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
 }
 
 if (!i->playing)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_pause(o->stream->smoother, x);
+#else
 pa_smoother_pause(o->stream->smoother, x);
+#endif
 
 /* Update the smoother */
 if ((o->stream->direction == PA_STREAM_PLAYBACK && 
!i->read_index_corrupt) ||
 (o->stream->direction == PA_STREAM_RECORD && 
!i->write_index_corrupt))
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_put(o->stream->smoother, u, 
calc_bytes(o->stream, true));
+#else
 pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, 
true));
+#endif
 
 if (i->playing)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_resume(o->stream->smoother, x);
+#else
 pa_smoother_resume(o->stream->smoother, x, true);
+#endif
 }
 }
 
@@ -2467,7 +2508,12 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec)

[pulseaudio-discuss] [PATCH 02/10] Add configure option --enable-smoother-2 to enable alternative smoother code

2018-04-09 Thread Georg Chini
---
 configure.ac | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/configure.ac b/configure.ac
index b0855a46..57cb3a92 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1399,7 +1399,14 @@ AS_IF([test "x$os_is_win32" != "x1"],
 AS_IF([test "x$ax_pthread_ok" = "xyes"],
 AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris]))
 
+ Support for new smoother code (optional) 
 
+AC_ARG_ENABLE([smoother-2],
+AS_HELP_STRING([--enable-smoother-2],[Enable optional new smoother 
support]))
+
+AS_IF([test "x$enable_smoother_2" == "xyes"], [USE_SMOOTHER_2=1], 
[USE_SMOOTHER_2=0])
+
+AS_IF([test "x$USE_SMOOTHER_2" = "x1"], AC_DEFINE([USE_SMOOTHER_2], 1, [Define 
this to enable new smoother support]))
 
 ###
 #Output   #
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 08/10] esound-sink: Allow module-esound-sink to use alternative smoother code

2018-04-09 Thread Georg Chini
This is untested.
---
 src/modules/module-esound-sink.c | 51 +++-
 1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 5ff04516..52bfa496 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -60,7 +60,13 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
+
 #include 
 #include 
 #include 
@@ -110,7 +116,12 @@ struct userdata {
 esd_format_t format;
 int32_t rate;
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *smoother;
+#else
 pa_smoother *smoother;
+#endif
+
 int fd;
 
 int64_t offset;
@@ -142,12 +153,16 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 switch (code) {
 
 case PA_SINK_MESSAGE_GET_LATENCY: {
+#ifdef USE_SMOOTHER_2
+*((int64_t*) data) = pa_smoother_2_get_delay(u->smoother, 
pa_rtclock_now(), (uint64_t)u->offset + u->memchunk.length);
+#else
 pa_usec_t w, r;
 
 r = pa_smoother_get(u->smoother, pa_rtclock_now());
 w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, 
&u->sink->sample_spec);
 
 *((int64_t*) data) = (int64_t)w - r;
+#endif
 return 0;
 }
 
@@ -185,15 +200,22 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, 
pa_sink_state_t new_state,
 case PA_SINK_SUSPENDED:
 pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_pause(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_pause(u->smoother, pa_rtclock_now());
+#endif
 break;
 
 case PA_SINK_IDLE:
 case PA_SINK_RUNNING:
 
 if (s->thread_info.state == PA_SINK_SUSPENDED)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_resume(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
-
+#endif
 break;
 
 case PA_SINK_UNLINKED:
@@ -215,7 +237,11 @@ static void thread_func(void *userdata) {
 
 pa_thread_mq_install(&u->thread_mq);
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+#endif
 
 for (;;) {
 int ret;
@@ -229,7 +255,11 @@ static void thread_func(void *userdata) {
 
 /* Render some data and write it to the fifo */
 if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && 
pollfd->revents) {
+#ifdef USE_SMOOTHER_2
+size_t bytes;
+#else
 pa_usec_t usec;
+#endif
 int64_t n;
 
 for (;;) {
@@ -300,6 +330,16 @@ static void thread_func(void *userdata) {
 }
 #endif
 
+#ifdef USE_SMOOTHER_2
+bytes = pa_usec_to_bytes(u->latency, &u->sink->sample_spec);
+
+if ((uint64_t)n > bytes)
+bytes = n - bytes;
+else
+bytes = 0;
+
+pa_smoother_2_put(u->smoother, pa_rtclock_now(), bytes);
+#else
 usec = pa_bytes_to_usec((uint64_t) n, &u->sink->sample_spec);
 
 if (usec > u->latency)
@@ -308,6 +348,7 @@ static void thread_func(void *userdata) {
 usec = 0;
 
 pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
+#endif
 }
 
 /* Hmm, nothing to do. Let's sleep */
@@ -561,6 +602,9 @@ int pa__init(pa_module*m) {
 u->module = m;
 m->userdata = u;
 u->fd = -1;
+#ifdef USE_SMOOTHER_2
+u->smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), 
pa_frame_size(&ss), ss.rate);
+#else
 u->smoother = pa_smoother_new(
 PA_USEC_PER_SEC,
 PA_USEC_PER_SEC*2,
@@ -569,6 +613,7 @@ int pa__init(pa_module*m) {
 10,
 0,
 false);
+#endif
 pa_memchunk_reset(&u->memchunk);
 u->offset = 0;
 
@@ -725,7 +770,11 @@ void pa__done(pa_module*m) {
 pa_xfree(u->write_data);
 
 if (u->smoother)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_free(u->smoother);
+#else
 pa_smoother_free(u->smoother);
+#endif
 
 if (u->fd >= 0)
 pa_close(u->fd);
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 04/10] bluetooth: Allow bluetooth to use alternative smoother code

2018-04-09 Thread Georg Chini
---
 src/modules/bluetooth/module-bluez5-device.c | 57 ++--
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index d40bbb0c..935ee2ce 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -43,7 +43,12 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
 
 #include "a2dp-codecs.h"
 #include "bluez5-util.h"
@@ -147,7 +152,13 @@ struct userdata {
 uint64_t read_index;
 uint64_t write_index;
 pa_usec_t started_at;
+
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *read_smoother;
+#else
 pa_smoother *read_smoother;
+#endif
+
 pa_memchunk write_memchunk;
 pa_sample_spec sample_spec;
 struct sbc_info sbc_info;
@@ -408,8 +419,13 @@ static int sco_process_push(struct userdata *u) {
 u->source_buffered_blocks++;
 
 if (u->source_buffered_blocks >= max_blocks) {
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_resume(u->read_smoother, tstamp);
+pa_smoother_2_put(u->read_smoother, tstamp, u->read_index);
+#else
 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);
+#endif
 
 u->source_buffer.length = u->source_buffer.index;
 u->source_buffer.index = 0;
@@ -663,8 +679,14 @@ static int a2dp_process_push(struct userdata *u) {
 }
 
 u->read_index += (uint64_t) total_written;
+
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_resume(u->read_smoother, tstamp);
+pa_smoother_2_put(u->read_smoother, tstamp, u->read_index);
+#else
 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);
+#endif
 
 memchunk.length -= to_write;
 
@@ -791,7 +813,11 @@ static void teardown_stream(struct userdata *u) {
 }
 
 if (u->read_smoother) {
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_free(u->read_smoother);
+#else
 pa_smoother_free(u->read_smoother);
+#endif
 u->read_smoother = NULL;
 }
 
@@ -935,7 +961,11 @@ static void setup_stream(struct userdata *u) {
 u->stream_setup_done = true;
 
 if (u->source)
+#ifdef USE_SMOOTHER_2
+u->read_smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, 
pa_rtclock_now(), pa_frame_size(&u->sample_spec), u->sample_spec.rate);
+#else
 u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, 2*PA_USEC_PER_SEC, 
true, true, 10, pa_rtclock_now(), true);
+#endif
 }
 
 /* Called from I/O thread, returns true if the transport was acquired or
@@ -962,13 +992,19 @@ static int source_process_msg(pa_msgobject *o, int code, 
void *data, int64_t off
 switch (code) {
 
 case PA_SOURCE_MESSAGE_GET_LATENCY: {
-int64_t wi, ri;
+#ifndef USE_SMOOTHER_2
+ int64_t wi, ri;
+#endif
 
 if (u->read_smoother) {
+#ifdef USE_SMOOTHER_2
+*((int64_t*) data) = u->source->thread_info.fixed_latency - 
pa_smoother_2_get_delay(u->read_smoother, pa_rtclock_now(), u->read_index);
+#else
 wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
 
 *((int64_t*) data) = u->source->thread_info.fixed_latency + wi 
- ri;
+#endif
 *((int64_t*) data) += u->source_buffered_blocks * 
pa_bytes_to_usec(u->read_block_size, &u->source->sample_spec);
 } else
 *((int64_t*) data) = 0;
@@ -1004,8 +1040,11 @@ static int source_set_state_in_io_thread_cb(pa_source 
*s, pa_source_state_t new_
 transport_release(u);
 
 if (u->read_smoother)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_pause(u->read_smoother, pa_rtclock_now());
+#else
 pa_smoother_pause(u->read_smoother, pa_rtclock_now());
-
+#endif
 break;
 
 case PA_SOURCE_IDLE:
@@ -1139,17 +1178,23 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 switch (code) {
 
 case PA_SINK_MESSAGE_GET_LATENCY: {
-int64_t wi = 0, ri = 0;
+int64_t wi, ri, delay = 0;
 
 if (u->read_smoother) {
+#ifdef USE_SMOOTHER_2
+delay = pa_smoother_2_get_delay(u->read_smoother, 
pa_rtclock_now(), u->write_index + u->write_block_size);
+#else
 ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 wi = pa_bytes_to_usec(u->write_index + u->write_block_size, 
&u->sample_spec);
+delay = wi - ri;
+#endif
 } else if (u->started_at) {
 ri = pa_rtclock_now() - u->started_at;
 wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+delay = wi - ri;
 }
 
-*((int64_t*) data)

[pulseaudio-discuss] [PATCH 06/10] combine-sink: Allow module-combine-sink to use alternative smoother code

2018-04-09 Thread Georg Chini
---
 src/modules/module-combine-sink.c | 52 ---
 1 file changed, 48 insertions(+), 4 deletions(-)

diff --git a/src/modules/module-combine-sink.c 
b/src/modules/module-combine-sink.c
index 79b4c6d9..1e719f8f 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -42,7 +42,13 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
+
 #include 
 
 PA_MODULE_AUTHOR("Lennart Poettering");
@@ -164,7 +170,11 @@ struct userdata {
 pa_atomic_t running;  /* we cache that value here, so that every 
thread can query it cheaply */
 pa_usec_t timestamp;
 bool in_null_mode;
-pa_smoother *smoother;
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *smoother;
+#else
+ pa_smoother *smoother;
+#endif
 uint64_t counter;
 
 uint64_t snapshot_counter;
@@ -399,8 +409,13 @@ static void process_render_null(struct userdata *u, 
pa_usec_t now) {
 
 /* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, 
(unsigned long) nbytes); */
 
-pa_smoother_put(u->thread_info.smoother, now,
-pa_bytes_to_usec(u->thread_info.counter, 
&u->sink->sample_spec) - (u->thread_info.timestamp - now));
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_put(u->thread_info.smoother, now,
+u->thread_info.counter - 
pa_usec_to_bytes(u->thread_info.timestamp - now, &u->sink->sample_spec));
+#else
+ pa_smoother_put(u->thread_info.smoother, now,
+ pa_bytes_to_usec(u->thread_info.counter, 
&u->sink->sample_spec) - (u->thread_info.timestamp - now));
+#endif
 }
 
 static void thread_func(void *userdata) {
@@ -855,9 +870,15 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, 
pa_sink_state_t new_state,
 
 if (running) {
 u->thread_info.render_timestamp = 0;
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_resume(u->thread_info.smoother, pa_rtclock_now());
+} else
+pa_smoother_2_pause(u->thread_info.smoother, pa_rtclock_now());
+#else
 pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), true);
 } else
 pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
+#endif
 
 return 0;
 }
@@ -1005,15 +1026,20 @@ 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 x, y, c;
 int64_t *delay = data;
 
+#ifdef USE_SMOOTHER_2
+*delay = pa_smoother_2_get_delay(u->thread_info.smoother, 
pa_rtclock_now(), u->thread_info.counter);
+#else
+pa_usec_t x, y, c;
+
 x = pa_rtclock_now();
 y = pa_smoother_get(u->thread_info.smoother, x);
 
 c = pa_bytes_to_usec(u->thread_info.counter, 
&u->sink->sample_spec);
 
 *delay = (int64_t)c - y;
+#endif
 
 return 0;
 }
@@ -1035,6 +1061,12 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 return 0;
 
 case SINK_MESSAGE_UPDATE_LATENCY: {
+#ifdef USE_SMOOTHER_2
+size_t latency;
+
+latency = pa_usec_to_bytes((pa_usec_t)offset,  
&u->sink->sample_spec);
+pa_smoother_2_put(u->thread_info.smoother, 
u->thread_info.snapshot_time, (int64_t)u->thread_info.snapshot_counter - 
latency);
+#else
 pa_usec_t x, y, latency = (pa_usec_t) offset;
 
 /* It may be possible that thread_info.counter has been increased
@@ -1049,6 +1081,7 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 y = 0;
 
 pa_smoother_put(u->thread_info.smoother, x, y);
+#endif
 return 0;
 }
 
@@ -1457,6 +1490,7 @@ int pa__init(pa_module*m) {
 
 u->resample_method = resample_method;
 u->outputs = pa_idxset_new(NULL, NULL);
+#ifndef USE_SMOOTHER_2
 u->thread_info.smoother = pa_smoother_new(
 PA_USEC_PER_SEC,
 PA_USEC_PER_SEC*2,
@@ -1465,6 +1499,7 @@ int pa__init(pa_module*m) {
 10,
 pa_rtclock_now(),
 true);
+#endif
 
 adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
 if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
@@ -1571,6 +1606,11 @@ int pa__init(pa_module*m) {
 goto fail;
 }
 
+#ifdef USE_SMOOTHER_2
+/* The smoother window size needs to be larger than the time between 
updates */
+u->thread_info.smoother = pa_smoother_2_new(u->adjust_time + 
5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&u->sink->sample_spec), 
u->sink->sample_spec.rate);
+#endif
+
 u->sink->parent.process_msg = sink_process_msg;
 u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
@@ -1712,7 +1752,11 @@ void pa__done(pa_module*m) {
 u->core->mainloop->time_free(u->

[pulseaudio-discuss] [PATCH 07/10] tunnel: Allow module-tunnel to use alternative smoother code

2018-04-09 Thread Georg Chini
---
 src/modules/module-tunnel.c | 72 +++--
 1 file changed, 69 insertions(+), 3 deletions(-)

diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 2068deca..99aba68c 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -48,7 +48,13 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
+
 #include 
 #include 
 #include 
@@ -231,7 +237,11 @@ struct userdata {
 
 pa_time_event *time_event;
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *smoother;
+#else
 pa_smoother *smoother;
+#endif
 
 char *device_description;
 char *server_fqdn;
@@ -424,9 +434,15 @@ static void check_smoother_status(struct userdata *u, bool 
past) {
 x += u->thread_transport_usec;
 
 if (u->remote_suspended || u->remote_corked)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_pause(u->smoother, x);
+else
+pa_smoother_2_resume(u->smoother, x);
+#else
 pa_smoother_pause(u->smoother, x);
 else
 pa_smoother_resume(u->smoother, x, true);
+#endif
 }
 
 /* Called from IO thread context */
@@ -514,13 +530,18 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 }
 
 case PA_SINK_MESSAGE_GET_LATENCY: {
-pa_usec_t yl, yr;
 int64_t *usec = data;
 
+#ifdef USE_SMOOTHER_2
+*usec = pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), 
u->counter);
+#else
+pa_usec_t yl, yr;
+
 yl = pa_bytes_to_usec((uint64_t) u->counter, 
&u->sink->sample_spec);
 yr = pa_smoother_get(u->smoother, pa_rtclock_now());
 
 *usec = (int64_t)yl - yr;
+#endif
 return 0;
 }
 
@@ -547,6 +568,21 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 return 0;
 
 case SINK_MESSAGE_UPDATE_LATENCY: {
+#ifdef USE_SMOOTHER_2
+int64_t bytes;
+
+if (offset < 0)
+bytes = - pa_usec_to_bytes(- offset, &u->sink->sample_spec);
+else
+bytes = pa_usec_to_bytes(offset, &u->sink->sample_spec);
+
+if (u->counter > bytes)
+bytes = u->counter - bytes;
+else
+bytes = 0;
+
+ pa_smoother_2_put(u->smoother, pa_rtclock_now(), bytes);
+#else
 pa_usec_t y;
 
 y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
@@ -557,6 +593,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void 
*data, int64_t offse
 y = 0;
 
 pa_smoother_put(u->smoother, pa_rtclock_now(), y);
+#endif
 
 /* We can access this freely here, since the main thread is 
waiting for us */
 u->thread_transport_usec = u->transport_usec;
@@ -632,13 +669,18 @@ static int source_process_msg(pa_msgobject *o, int code, 
void *data, int64_t off
 }
 
 case PA_SOURCE_MESSAGE_GET_LATENCY: {
-pa_usec_t yr, yl;
 int64_t *usec = data;
 
+#ifdef USE_SMOOTHER_2
+*usec = - pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), 
u->counter);
+#else
+pa_usec_t yr, yl;
+
 yl = pa_bytes_to_usec((uint64_t) u->counter, 
&PA_SOURCE(o)->sample_spec);
 yr = pa_smoother_get(u->smoother, pa_rtclock_now());
 
 *usec = (int64_t)yr - yl;
+#endif
 return 0;
 }
 
@@ -673,12 +715,25 @@ static int source_process_msg(pa_msgobject *o, int code, 
void *data, int64_t off
 return 0;
 
 case SOURCE_MESSAGE_UPDATE_LATENCY: {
+#ifdef USE_SMOOTHER_2
+int64_t bytes;
+
+if (offset < 0)
+bytes = - pa_usec_to_bytes(- offset, &u->source->sample_spec);
+else
+bytes = pa_usec_to_bytes(offset, &u->source->sample_spec);
+
+bytes += u->counter;
+
+pa_smoother_2_put(u->smoother, pa_rtclock_now(), bytes);
+#else
 pa_usec_t y;
 
 y = pa_bytes_to_usec((uint64_t) u->counter, 
&u->source->sample_spec);
-y += (pa_usec_t) offset;
+y += offset;
 
 pa_smoother_put(u->smoother, pa_rtclock_now(), y);
+#endif
 
 /* We can access this freely here, since the main thread is 
waiting for us */
 u->thread_transport_usec = u->transport_usec;
@@ -1983,6 +2038,7 @@ int pa__init(pa_module*m) {
 u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
 u->source = NULL;
 #endif
+#ifndef USE_SMOOTHER_2
 u->smoother = pa_smoother_new(
 PA_USEC_PER_SEC,
 PA_USEC_PER_SEC*2,
@@ -1991,6 +2047,7 @@ int pa__init(pa_module*m) {
 10,
 pa_rtclock_now(),
 false);
+#endif
 u->ctag = 1;
 u->device_index = u->channel = PA_INVALID_INDEX;
 u->time_event = NULL;
@@ -2139,6 +2196,11 @@ int pa__init(pa_module*m) {

[pulseaudio-discuss] [PATCH 09/10] raop-sink: Allow module-raop-sink to use alternative smoother code

2018-04-09 Thread Georg Chini
This is untested.
---
 src/modules/raop/raop-sink.c | 40 +++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index ec6f8262..5103e8e7 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -59,7 +59,12 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
 
 #include "raop-sink.h"
 #include "raop-client.h"
@@ -87,7 +92,11 @@ struct userdata {
 
 pa_usec_t delay;
 pa_usec_t start;
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *smoother;
+#else
 pa_smoother *smoother;
+#endif
 uint64_t write_count;
 
 uint32_t latency;
@@ -112,16 +121,22 @@ static void raop_state_cb(pa_raop_state_t state, void 
*userdata) {
 }
 
 static int64_t sink_get_latency(const struct userdata *u) {
+#ifndef USE_SMOOTHER_2
 pa_usec_t now;
+#endif
 int64_t latency;
 
 pa_assert(u);
 pa_assert(u->smoother);
 
+#ifdef USE_SMOOTHER_2
+latency = pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), 
u->write_count);
+#else
 now = pa_rtclock_now();
 now = pa_smoother_get(u->smoother, now);
 
 latency = pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - 
(int64_t) now;
+#endif
 
 /* RAOP default latency */
 latency += u->latency * PA_USEC_PER_MSEC;
@@ -262,7 +277,11 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, 
pa_sink_state_t new_state,
 pa_log_debug("RAOP: RUNNING");
 
 now = pa_rtclock_now();
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_reset(u->smoother, now);
+#else
 pa_smoother_reset(u->smoother, now, false);
+#endif
 
 if (!pa_raop_client_is_alive(u->raop)) {
 /* Connecting will trigger a RECORD and start steaming */
@@ -347,15 +366,22 @@ static void thread_func(void *userdata) {
 pa_log_debug("Thread starting up");
 
 pa_thread_mq_install(&u->thread_mq);
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+#endif
 
 for (;;) {
 struct pollfd *pollfd = NULL;
 unsigned int i, nbfds = 0;
-pa_usec_t now, estimated, intvl;
+pa_usec_t now, intvl;
 uint64_t position;
 size_t index;
 int ret;
+#ifndef USE_SMOOTHER_2
+pa_usec_t estimated;
+#endif
 
 if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
 if (u->sink->thread_info.rewind_requested)
@@ -444,8 +470,12 @@ static void thread_func(void *userdata) {
 position = u->write_count - pa_usec_to_bytes(u->delay, 
&u->sink->sample_spec);
 
 now = pa_rtclock_now();
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_put(u->smoother, now, position);
+#else
 estimated = pa_bytes_to_usec(position, &u->sink->sample_spec);
 pa_smoother_put(u->smoother, now, estimated);
+#endif
 
 if (u->oob && !pollfd->revents) {
 /* Sleep until next packet transmission */
@@ -601,6 +631,9 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, 
const char *driver) {
 pa_memchunk_reset(&u->memchunk);
 
 u->delay = 0;
+#ifdef USE_SMOOTHER_2
+u->smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), 
pa_frame_size(&ss), ss.rate);
+#else
 u->smoother = pa_smoother_new(
 PA_USEC_PER_SEC,
 PA_USEC_PER_SEC*2,
@@ -609,6 +642,7 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, 
const char *driver) {
 10,
 0,
 false);
+#endif
 u->write_count = 0;
 
 if (pa_streq(protocol, "TCP")) {
@@ -785,7 +819,11 @@ static void userdata_free(struct userdata *u) {
 u->raop = NULL;
 
 if (u->smoother)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_free(u->smoother);
+#else
 pa_smoother_free(u->smoother);
+#endif
 u->smoother = NULL;
 
 if (u->card)
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 10/10] solaris: Allow module-solaris to use alternative smoother code

2018-04-09 Thread Georg Chini
This is untested.
---
 src/modules/module-solaris.c | 43 +++
 1 file changed, 43 insertions(+)

diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index 240ed855..4642446a 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -60,7 +60,12 @@
 #include 
 #include 
 #include 
+
+#ifdef USE_SMOOTHER_2
+#include 
+#else
 #include 
+#endif
 
 PA_MODULE_AUTHOR("Pierre Ossman");
 PA_MODULE_DESCRIPTION("Solaris Sink/Source");
@@ -110,7 +115,11 @@ struct userdata {
 
 int32_t minimum_request;
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2 *smoother;
+#else
 pa_smoother *smoother;
+#endif
 };
 
 static const char* const valid_modargs[] = {
@@ -164,7 +173,11 @@ 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;
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_put(u->smoother, pa_rtclock_now(), played_bytes);
+#else
 pa_smoother_put(u->smoother, pa_rtclock_now(), 
pa_bytes_to_usec(played_bytes, &u->sink->sample_spec));
+#endif
 
 if (u->written_bytes > played_bytes)
 return u->written_bytes - played_bytes;
@@ -413,7 +426,11 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, 
pa_sink_state_t new_state,
 
 pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_pause(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_pause(u->smoother, pa_rtclock_now());
+#endif
 
 if (!u->source || u->source_suspended)
 suspend(u);
@@ -425,7 +442,11 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, 
pa_sink_state_t new_state,
 case PA_SINK_RUNNING:
 
 if (s->thread_info.state == PA_SINK_SUSPENDED) {
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_resume(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+#endif
 
 if (!u->source || u->source_suspended) {
 bool mute;
@@ -654,7 +675,11 @@ static void thread_func(void *userdata) {
 
 pa_thread_mq_install(&u->thread_mq);
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+#endif
 
 for (;;) {
 /* Render some data and write it to the dsp */
@@ -680,7 +705,11 @@ static void thread_func(void *userdata) {
 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
 
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
 pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
+#endif
 }
 
 for (;;) {
@@ -738,7 +767,11 @@ static void thread_func(void *userdata) {
 }
 
 ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, 
&u->sink->sample_spec);
+#ifdef USE_SMOOTHER_2
+xsleep_interval = pa_smoother_2_translate(u->smoother, 
ysleep_interval);
+#else
 xsleep_interval = pa_smoother_translate(u->smoother, xtime0, 
ysleep_interval);
+#endif
 pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + 
PA_MIN(xsleep_interval, ysleep_interval));
 } else
 pa_rtpoll_set_timer_disabled(u->rtpoll);
@@ -886,8 +919,10 @@ int pa__init(pa_module *m) {
 
 u = pa_xnew0(struct userdata, 1);
 
+#ifndef USE_SMOOTHER_2
 if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, 
true, true, 10, pa_rtclock_now(), true)))
 goto fail;
+#endif
 
 /*
  * For a process (or several processes) to use the same audio device for 
both
@@ -903,6 +938,10 @@ int pa__init(pa_module *m) {
 }
 u->frame_size = pa_frame_size(&ss);
 
+#ifdef USE_SMOOTHER_2
+u->smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), 
u->frame_size, ss.rate);
+#endif
+
 u->minimum_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, 
&ss);
 
 buffer_length_msec = 100;
@@ -1144,7 +1183,11 @@ void pa__done(pa_module *m) {
 close(u->fd);
 
 if (u->smoother)
+#ifdef USE_SMOOTHER_2
+pa_smoother_2_free(u->smoother);
+#else
 pa_smoother_free(u->smoother);
+#endif
 
 pa_xfree(u->device_name);
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 00/10] loopback: Optimize latency stabilization

2018-04-09 Thread Georg Chini
This is a re-base of the remaining patches from the loopback series
I sent in February 2017. There are no major changes to the series.

Georg Chini (10):
  loopback: Do not detect underruns during initial latency adjustments
  loopback: Limit controller step size to 2.01‰
  loopback: Optimize adaptive re-sampling
  loopback: Add latency prediction and Kalman filter
  loopback: Track prediction error; debug and cosmetic changes
  loopback: Add adjust_threshold_usec parameter
  loopback: Only use controller weight after target latency has been
crossed twice
  loopback: Add low_device_latency parameter
  loopback: Add adjust_time_msec parameter to allow adjust times below
1s
  loopback: Add log_interval and log_interval_msec parameter

 src/modules/module-loopback.c | 364 +-
 1 file changed, 328 insertions(+), 36 deletions(-)

-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 01/10] loopback: Do not detect underruns during initial latency adjustments

2018-04-09 Thread Georg Chini
Currently module-loopback detects underruns even if sink_input_pop_cb()
was not yet called twice and initial latency adjustments are active.
This leads to unnecessary rewind requests.

This patch delays detecting underruns until the initial adjustments
are done.
---
 src/modules/module-loopback.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 31702e32..32780380 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -906,7 +906,8 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int 
code, void *data, in
  * right-away */
 if (u->sink_input->sink->thread_info.state != PA_SINK_SUSPENDED &&
 u->sink_input->thread_info.underrun_for > 0 &&
-pa_memblockq_is_readable(u->memblockq)) {
+pa_memblockq_is_readable(u->memblockq) &&
+u->output_thread_info.pop_called) {
 
 pa_asyncmsgq_post(pa_thread_mq_get()->outq, 
PA_MSGOBJECT(u->msg), LOOPBACK_MESSAGE_UNDERRUN, NULL, 0, NULL, NULL);
 /* If called from within the pop callback skip the rewind */
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 04/10] loopback: Add latency prediction and Kalman filter

2018-04-09 Thread Georg Chini
A Kalman filter is added to further reduce noise. The Kalman filter needs a
latency prediction as input, so estimate the next expected latency as well.
Again, theory is at
https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt
---
 src/modules/module-loopback.c | 67 ++-
 1 file changed, 66 insertions(+), 1 deletion(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 12193716..19a40b89 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -108,11 +108,19 @@ struct userdata {
 
 /* State variable of the latency controller */
 int32_t last_latency_difference;
+int64_t last_source_latency_offset;
+int64_t last_sink_latency_offset;
+int64_t next_latency_with_drift;
+int64_t next_latency_at_optimum_rate_with_drift;
 
 /* Filter varables used for 2nd order filter */
 double drift_filter;
 double drift_compensation_rate;
 
+/* Variables for Kalman filter */
+double latency_variance;
+double kalman_variance;
+
 /* lower latency limit found by underruns */
 pa_usec_t underrun_latency_limit;
 
@@ -374,6 +382,7 @@ static void adjust_rates(struct userdata *u) {
 pa_usec_t current_buffer_latency, snapshot_delay;
 int64_t current_source_sink_latency, current_latency, 
latency_at_optimum_rate;
 pa_usec_t final_latency, now;
+double filtered_latency, current_latency_error, latency_correction, 
base_rate_with_drift;
 
 pa_assert(u);
 pa_assert_ctl_context();
@@ -443,6 +452,28 @@ static void adjust_rates(struct userdata *u) {
 final_latency = PA_MAX(u->latency, u->minimum_latency);
 latency_difference = (int32_t)(current_latency - final_latency);
 
+/* Do not filter or calculate error if source or sink changed or if there 
was an underrun */
+if (u->source_sink_changed || u->underrun_occured) {
+/* Initial conditions are very unsure, so use a high variance */
+u->kalman_variance = 1000;
+filtered_latency = latency_at_optimum_rate;
+u->next_latency_at_optimum_rate_with_drift = latency_at_optimum_rate;
+u->next_latency_with_drift = current_latency;
+
+} else {
+/* Correct predictions if one of the latency offsets changed between 
iterations */
+u->next_latency_at_optimum_rate_with_drift += u->source_latency_offset 
- u->last_source_latency_offset;
+u->next_latency_at_optimum_rate_with_drift += u->sink_latency_offset - 
u->last_sink_latency_offset;
+u->next_latency_with_drift += u->source_latency_offset - 
u->last_source_latency_offset;
+u->next_latency_with_drift += u->sink_latency_offset - 
u->last_sink_latency_offset;
+/* Low pass filtered latency variance */
+current_latency_error = (double)abs((int32_t)(latency_at_optimum_rate 
- u->next_latency_at_optimum_rate_with_drift));
+u->latency_variance = (1.0 - FILTER_PARAMETER) * u->latency_variance + 
FILTER_PARAMETER * current_latency_error * current_latency_error;
+/* Kalman filter */
+filtered_latency = (latency_at_optimum_rate * u->kalman_variance + 
u->next_latency_at_optimum_rate_with_drift * u->latency_variance) / 
(u->kalman_variance + u->latency_variance);
+u->kalman_variance = u->kalman_variance * u->latency_variance / 
(u->kalman_variance + u->latency_variance) + u->latency_variance / 4 + 200;
+}
+
 pa_log_debug("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) current_buffer_latency / PA_USEC_PER_MSEC,
@@ -452,7 +483,7 @@ static void adjust_rates(struct userdata *u) {
 pa_log_debug("Loopback latency at optimum rate is %0.2f ms", 
(double)latency_at_optimum_rate / PA_USEC_PER_MSEC);
 
 /* Calculate new rate */
-new_rate = rate_controller(u, base_rate, old_rate, 
(int32_t)(latency_at_optimum_rate - final_latency), latency_difference);
+new_rate = rate_controller(u, base_rate, old_rate, 
(int32_t)(filtered_latency - final_latency), latency_difference);
 
 /* Save current latency difference at new rate for next cycle and reset 
flags */
 u->last_latency_difference = current_source_sink_latency + 
current_buffer_latency * old_rate / new_rate - final_latency;
@@ -460,9 +491,35 @@ static void adjust_rates(struct userdata *u) {
 /* Set variables that may change between calls of adjust_rate() */
 u->source_sink_changed = false;
 u->underrun_occured = false;
+u->last_source_latency_offset = u->source_latency_offset;
+u->last_sink_latency_offset = u->sink_latency_offset;
 u->source_latency_offset_changed = false;
 u->sink_latency_offset_changed = false;
 
+/* Predicton of next latency */
+
+/* Evaluate optimum rate */
+base_rate_with_drift = u->drift_compensation_rate + base_rate;
+
+/* Latency correction on next iteration */
+l

[pulseaudio-discuss] [PATCH 02/10] loopback: Limit controller step size to 2.01‰

2018-04-09 Thread Georg Chini
The current loopback controller can produce a rate jump of up to 1% at startup,
which may be audible. To prevent large initial jumps, a second controller is
introduced, which produces a rate, that is not more than 2‰ away from the last
rate. Only during the startup phase, the rates produced by this controller will
be nearer to the base rate than those produced by the original controller.
Therefore choosing the rate which is nearer to the base rate will ensure that
the secondary controller only moderates the startup phase and has no influence
during continued operation.
The maximum step size of the original controller after the initial jump is
limited to 2.01‰ of the base rate, see documentation at
https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt
---
 src/modules/module-loopback.c | 28 +++-
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 32780380..9ca84b9d 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -232,21 +232,31 @@ static void teardown(struct userdata *u) {
 
 /* rate controller, called from main context
  * - maximum deviation from base rate is less than 1%
- * - can create audible artifacts by changing the rate too quickly
+ * - controller step size is limited to 2.01‰
  * - exhibits hunting with USB or Bluetooth sources
  */
 static uint32_t rate_controller(
-uint32_t base_rate,
-pa_usec_t adjust_time,
+struct userdata *u,
+uint32_t base_rate, uint32_t old_rate,
 int32_t latency_difference_usec) {
 
-uint32_t new_rate;
-double min_cycles;
+uint32_t new_rate, new_rate_1, new_rate_2;
+double min_cycles_1, min_cycles_2;
+
+/* Calculate next rate that is not more than 2‰ away from the last rate */
+min_cycles_1 = (double)abs(latency_difference_usec) / u->real_adjust_time 
/ 0.002 + 1;
+new_rate_1 = old_rate + base_rate * (double)latency_difference_usec / 
min_cycles_1 / u->real_adjust_time;
 
 /* Calculate best rate to correct the current latency offset, limit at
- * slightly below 1% difference from base_rate */
-min_cycles = (double)abs(latency_difference_usec) / adjust_time / 0.01 + 1;
-new_rate = base_rate * (1.0 + (double)latency_difference_usec / min_cycles 
/ adjust_time);
+ * 1% difference from base_rate */
+min_cycles_2 = (double)abs(latency_difference_usec) / u->real_adjust_time 
/ 0.01 + 1;
+new_rate_2 = (double)base_rate * (1.0 + (double)latency_difference_usec / 
min_cycles_2 / u->real_adjust_time);
+
+/* Choose the rate that is nearer to base_rate */
+if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate))
+new_rate = new_rate_1;
+else
+new_rate = new_rate_2;
 
 return new_rate;
 }
@@ -392,7 +402,7 @@ static void adjust_rates(struct userdata *u) {
 pa_log_debug("Loopback latency at base rate is %0.2f ms", 
(double)latency_at_optimum_rate / PA_USEC_PER_MSEC);
 
 /* Calculate new rate */
-new_rate = rate_controller(base_rate, u->real_adjust_time, 
latency_difference);
+new_rate = rate_controller(u, base_rate, old_rate, latency_difference);
 
 u->source_sink_changed = false;
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 03/10] loopback: Optimize adaptive re-sampling

2018-04-09 Thread Georg Chini
The current code assumes that the time domains of source and sink are
equal. This leads to a saw-tooth characteristics of the resulting end
to end latency.
This patch adds an iterative calculation of an optimum rate which accounts
for the difference between the source and sink time domains, thereby massively
enhancing the latency stability. Theoretical background for the calculation
can be found at
https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt
---
 src/modules/module-loopback.c | 118 --
 1 file changed, 103 insertions(+), 15 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 9ca84b9d..12193716 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -58,6 +58,8 @@ PA_MODULE_USAGE(
 
 #define DEFAULT_LATENCY_MSEC 200
 
+#define FILTER_PARAMETER 0.125
+
 #define MEMBLOCKQ_MAXLENGTH (1024*1024*32)
 
 #define MIN_DEVICE_LATENCY (2.5*PA_USEC_PER_MSEC)
@@ -104,6 +106,13 @@ struct userdata {
 int64_t sink_latency_offset;
 pa_usec_t minimum_latency;
 
+/* State variable of the latency controller */
+int32_t last_latency_difference;
+
+/* Filter varables used for 2nd order filter */
+double drift_filter;
+double drift_compensation_rate;
+
 /* lower latency limit found by underruns */
 pa_usec_t underrun_latency_limit;
 
@@ -112,8 +121,13 @@ struct userdata {
 uint32_t underrun_counter;
 uint32_t adjust_counter;
 
+/* Various booleans */
 bool fixed_alsa_source;
 bool source_sink_changed;
+bool underrun_occured;
+bool source_latency_offset_changed;
+bool sink_latency_offset_changed;
+bool initial_adjust_pending;
 
 /* Used for sink input and source output snapshots */
 struct {
@@ -190,6 +204,7 @@ enum {
 LOOPBACK_MESSAGE_SOURCE_LATENCY_RANGE_CHANGED,
 LOOPBACK_MESSAGE_SINK_LATENCY_RANGE_CHANGED,
 LOOPBACK_MESSAGE_UNDERRUN,
+LOOPBACK_MESSAGE_ADJUST_DONE,
 };
 
 static void enable_adjust_timer(struct userdata *u, bool enable);
@@ -233,24 +248,26 @@ static void teardown(struct userdata *u) {
 /* rate controller, called from main context
  * - maximum deviation from base rate is less than 1%
  * - controller step size is limited to 2.01‰
+ * - will calculate an optimum rate
  * - exhibits hunting with USB or Bluetooth sources
  */
 static uint32_t rate_controller(
 struct userdata *u,
 uint32_t base_rate, uint32_t old_rate,
-int32_t latency_difference_usec) {
+int32_t latency_difference_at_optimum_rate,
+int32_t latency_difference_at_base_rate) {
 
-uint32_t new_rate, new_rate_1, new_rate_2;
-double min_cycles_1, min_cycles_2;
+double new_rate, new_rate_1, new_rate_2;
+double min_cycles_1, min_cycles_2, drift_rate, latency_drift;
 
 /* Calculate next rate that is not more than 2‰ away from the last rate */
-min_cycles_1 = (double)abs(latency_difference_usec) / u->real_adjust_time 
/ 0.002 + 1;
-new_rate_1 = old_rate + base_rate * (double)latency_difference_usec / 
min_cycles_1 / u->real_adjust_time;
+min_cycles_1 = (double)abs(latency_difference_at_optimum_rate) / 
u->real_adjust_time / 0.002 + 1;
+new_rate_1 = old_rate + base_rate * 
(double)latency_difference_at_optimum_rate / min_cycles_1 / u->real_adjust_time;
 
 /* Calculate best rate to correct the current latency offset, limit at
  * 1% difference from base_rate */
-min_cycles_2 = (double)abs(latency_difference_usec) / u->real_adjust_time 
/ 0.01 + 1;
-new_rate_2 = (double)base_rate * (1.0 + (double)latency_difference_usec / 
min_cycles_2 / u->real_adjust_time);
+min_cycles_2 = (double)abs(latency_difference_at_optimum_rate) / 
u->real_adjust_time / 0.01 + 1;
+new_rate_2 = (double)base_rate * (1.0 + 
(double)latency_difference_at_optimum_rate / min_cycles_2 / 
u->real_adjust_time);
 
 /* Choose the rate that is nearer to base_rate */
 if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate))
@@ -258,7 +275,37 @@ static uint32_t rate_controller(
 else
 new_rate = new_rate_2;
 
-return new_rate;
+/* Calculate rate difference between source and sink. Skip calculation
+ * after a source/sink change, an underrun or latency offset change */
+
+if (!u->underrun_occured && !u->source_sink_changed && 
!u->source_latency_offset_changed && !u->sink_latency_offset_changed) {
+/* Latency difference between last iterations */
+latency_drift = latency_difference_at_base_rate - 
u->last_latency_difference;
+
+/* Calculate frequency difference between source and sink */
+drift_rate = latency_drift * old_rate / u->real_adjust_time + old_rate 
- base_rate;
+
+/* The maximum accepted sample rate difference between source and
+ * sink is 1% of the base rate. If the result is larger, something
+ * went wrong, so do not use i

[pulseaudio-discuss] [PATCH 10/10] loopback: Add log_interval and log_interval_msec parameter

2018-04-09 Thread Georg Chini
Add a log_interval parameter to control the amount of logging. Default is
no logging. Like for adjust_time, two parameters exist: log_interval specifies
the interval in seconds while log_interval_msec is in ms.
If the log interval is too small, logging will occur on every iteration.
---
 src/modules/module-loopback.c | 77 +--
 1 file changed, 60 insertions(+), 17 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index fd3f794e..2eff4c0c 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -48,6 +48,8 @@ PA_MODULE_USAGE(
 "latency_msec= "
 "max_latency_msec= "
 "low_device_latency= "
+"log_interval= "
+"log_interval_msec= "
 "adjust_threshold_usec= "
 "format= "
 "rate= "
@@ -101,6 +103,7 @@ struct userdata {
 pa_usec_t adjust_time;
 uint32_t adjust_threshold;
 bool low_device_latency;
+uint32_t log_interval;
 
 /* Latency boundaries and current values */
 pa_usec_t min_source_latency;
@@ -137,6 +140,7 @@ struct userdata {
 uint32_t underrun_counter;
 uint32_t adjust_counter;
 uint32_t target_latency_cross_counter;
+uint32_t log_counter;
 
 /* Various booleans */
 bool fixed_alsa_source;
@@ -194,6 +198,8 @@ static const char* const valid_modargs[] = {
 "latency_msec",
 "max_latency_msec",
 "low_device_latency",
+"log_interval",
+"log_interval_msec",
 "adjust_threshold_usec",
 "format",
 "rate",
@@ -509,18 +515,25 @@ static void adjust_rates(struct userdata *u) {
 /* Calculate new rate */
 new_rate = rate_controller(u, base_rate, old_rate, 
(int32_t)(filtered_latency - final_latency), latency_difference);
 
-pa_log_debug("Loopback status %s to %s:\nSource latency: %0.2f ms\n
Buffer: %0.2f ms\nSink latency: %0.2f ms\nEnd-to-end latency: %0.2f 
ms\n"
- "Deviation from target latency at optimum rate: %0.2f 
usec\nAverage prediction error: ± %0.2f usec\nOptimum rate: %0.2f Hz\n  
  Deviation from base rate: %i Hz",
-u->source_output->source->name,
-u->sink_input->sink->name,
-(double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
-(double) current_buffer_latency / PA_USEC_PER_MSEC,
-(double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC,
-(double) current_latency / PA_USEC_PER_MSEC,
-(double) latency_at_optimum_rate - final_latency,
-(double) u->latency_error,
-u->drift_compensation_rate + base_rate,
-(int32_t)(new_rate - base_rate));
+/* Log every log_interval iterations if the log_interval parameter is set 
*/
+if (u->log_interval != 0) {
+u->log_counter--;
+if (u->log_counter == 0) {
+pa_log_debug("Loopback status %s to %s:\nSource latency: %0.2f 
ms\nBuffer: %0.2f ms\nSink latency: %0.2f ms\nEnd-to-end latency: 
%0.2f ms\n"
+ "Deviation from target latency at optimum rate: 
%0.2f usec\nAverage prediction error: ± %0.2f usec\nOptimum rate: %0.2f 
Hz\nDeviation from base rate: %i Hz",
+u->source_output->source->name,
+u->sink_input->sink->name,
+(double) u->latency_snapshot.source_latency / 
PA_USEC_PER_MSEC,
+(double) current_buffer_latency / PA_USEC_PER_MSEC,
+(double) u->latency_snapshot.sink_latency / 
PA_USEC_PER_MSEC,
+(double) current_latency / PA_USEC_PER_MSEC,
+(double) latency_at_optimum_rate - final_latency,
+(double) u->latency_error,
+u->drift_compensation_rate + base_rate,
+(int32_t)(new_rate - base_rate));
+u->log_counter = u->log_interval;
+}
+}
 
 /* If the latency difference changed sign, we have crossed the target 
latency. */
 if ((int64_t)latency_difference * u->last_latency_difference < 0)
@@ -900,11 +913,12 @@ static void source_output_moving_cb(pa_source_output *o, 
pa_source *dest) {
 u->iteration_counter = 0;
 u->underrun_counter = 0;
 
-/* Reset booleans, latency error and counter */
+/* Reset booleans, latency error and counters */
 u->source_sink_changed = true;
 u->underrun_occured = false;
 u->source_latency_offset_changed = false;
 u->target_latency_cross_counter = 0;
+u->log_counter = u->log_interval;
 u->latency_error = 0;
 
 /* Send a mesage to the output thread that the source has changed.
@@ -1299,11 +1313,12 @@ static void sink_input_moving_cb(pa_sink_input *i, 
pa_sink *dest) {
 u->iteration_counter = 0;
 u->underrun_counter = 0;
 
-/* Reset booleans, latency error and counter */
+/

[pulseaudio-discuss] [PATCH 07/10] loopback: Only use controller weight after target latency has been crossed twice

2018-04-09 Thread Georg Chini
The previous patch slows down initial convergence. Therefore do not use
the controller weight until we can assume that we reached an equilibrium.
Because it takes some time before the reported latency values are reliable,
assume that a steady state is reached when the target latency has been
crossed twice.
---
 src/modules/module-loopback.c | 27 +++
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 9f18ad1f..eeb93264 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -133,6 +133,7 @@ struct userdata {
 uint32_t iteration_counter;
 uint32_t underrun_counter;
 uint32_t adjust_counter;
+uint32_t target_latency_cross_counter;
 
 /* Various booleans */
 bool fixed_alsa_source;
@@ -281,10 +282,14 @@ static uint32_t rate_controller(
  * of 0.5 Hz needs to be applied by the controller when the latency
  * difference gets larger than the threshold. The weight follows
  * from the definition of the controller. The minimum will only
- * be reached when one adjust threshold away from the target. */
+ * be reached when one adjust threshold away from the target. Start
+ * using the weight after the target latency has been reached for the
+ * second time to accelerate initial convergence. The second time has
+ * been chosen because it takes a while before the smoother returns
+ * reliable latencies. */
 controller_weight = 1;
 min_weight = PA_CLAMP(0.5 / (double)base_rate * (100.0 + 
(double)u->real_adjust_time / u->adjust_threshold), 0, 1.0);
-if ((double)abs((int)(old_rate - base_rate_with_drift)) / 
base_rate_with_drift < 0.002)
+if ((double)abs((int)(old_rate - base_rate_with_drift)) / 
base_rate_with_drift < 0.002 && u->target_latency_cross_counter >= 2)
 controller_weight = 
PA_CLAMP((double)abs(latency_difference_at_optimum_rate) / u->adjust_threshold 
* min_weight, min_weight, 1.0);
 
 /* Calculate next rate that is not more than 2‰ away from the last rate */
@@ -512,6 +517,10 @@ static void adjust_rates(struct userdata *u) {
 u->drift_compensation_rate + base_rate,
 (int32_t)(new_rate - base_rate));
 
+/* If the latency difference changed sign, we have crossed the target 
latency. */
+if ((int64_t)latency_difference * u->last_latency_difference < 0)
+u->target_latency_cross_counter++;
+
 /* Save current latency difference at new rate for next cycle and reset 
flags */
 u->last_latency_difference = current_source_sink_latency + 
current_buffer_latency * old_rate / new_rate - final_latency;
 
@@ -883,10 +892,11 @@ static void source_output_moving_cb(pa_source_output *o, 
pa_source *dest) {
 u->iteration_counter = 0;
 u->underrun_counter = 0;
 
-/* Reset booleans and latency error */
+/* Reset booleans, latency error and counter */
 u->source_sink_changed = true;
 u->underrun_occured = false;
 u->source_latency_offset_changed = false;
+u->target_latency_cross_counter = 0;
 u->latency_error = 0;
 
 /* Send a mesage to the output thread that the source has changed.
@@ -1278,10 +1288,11 @@ static void sink_input_moving_cb(pa_sink_input *i, 
pa_sink *dest) {
 u->iteration_counter = 0;
 u->underrun_counter = 0;
 
-/* Reset booleans and latency error */
+/* Reset booleans, latency error and counter */
 u->source_sink_changed = true;
 u->underrun_occured = false;
 u->sink_latency_offset_changed = false;
+u->target_latency_cross_counter = 0;
 u->latency_error = 0;
 
 u->output_thread_info.pop_called = false;
@@ -1398,6 +1409,7 @@ static int loopback_process_msg_cb(pa_msgobject *o, int 
code, void *userdata, in
 
 u->underrun_counter++;
 u->underrun_occured = true;
+u->target_latency_cross_counter = 0;
 pa_log_debug("Underrun detected, counter incremented to %u", 
u->underrun_counter);
 
 return 0;
@@ -1425,6 +1437,9 @@ static pa_hook_result_t 
sink_port_latency_offset_changed_cb(pa_core *core, pa_si
 u->sink_latency_offset = sink->port_latency_offset;
 update_minimum_latency(u, sink, true);
 
+/* We might need to adjust again, reset counter */
+u->target_latency_cross_counter = 0;
+
 return PA_HOOK_OK;
 }
 
@@ -1440,6 +1455,9 @@ static pa_hook_result_t 
source_port_latency_offset_changed_cb(pa_core *core, pa_
 u->source_latency_offset = source->port_latency_offset;
 update_minimum_latency(u, u->sink_input->sink, true);
 
+/* We might need to adjust again, reset counter */
+u->target_latency_cross_counter = 0;
+
 return PA_HOOK_OK;
 }
 
@@ -1575,6 +1593,7 @@ int pa__init(pa_module *m) {
 u->sink_latency_offset_changed = false;
 u->latency_error = 0;
 u->adjust_threshold = adjust_threshold;
+u->target_latency_cross_counter = 0;
 u->initial_adjust_pendin

[pulseaudio-discuss] [PATCH 05/10] loopback: Track prediction error; debug and cosmetic changes

2018-04-09 Thread Georg Chini
---
 src/modules/module-loopback.c | 34 +++---
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 19a40b89..1db39ef4 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -36,7 +36,7 @@
 #include 
 #include 
 
-PA_MODULE_AUTHOR("Pierre-Louis Bossart");
+PA_MODULE_AUTHOR("Pierre-Louis Bossart, Georg Chini");
 PA_MODULE_DESCRIPTION("Loopback from source to sink");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(false);
@@ -117,9 +117,10 @@ struct userdata {
 double drift_filter;
 double drift_compensation_rate;
 
-/* Variables for Kalman filter */
+/* Variables for Kalman filter and error tracking*/
 double latency_variance;
 double kalman_variance;
+double latency_error;
 
 /* lower latency limit found by underruns */
 pa_usec_t underrun_latency_limit;
@@ -466,6 +467,8 @@ static void adjust_rates(struct userdata *u) {
 u->next_latency_at_optimum_rate_with_drift += u->sink_latency_offset - 
u->last_sink_latency_offset;
 u->next_latency_with_drift += u->source_latency_offset - 
u->last_source_latency_offset;
 u->next_latency_with_drift += u->sink_latency_offset - 
u->last_sink_latency_offset;
+/* Low pass filtered latency error. This value reflects how well the 
measured values match the prediction. */
+u->latency_error = (1 - FILTER_PARAMETER) * u->latency_error + 
FILTER_PARAMETER * (double)abs((int32_t)(current_latency - 
u->next_latency_with_drift));
 /* Low pass filtered latency variance */
 current_latency_error = (double)abs((int32_t)(latency_at_optimum_rate 
- u->next_latency_at_optimum_rate_with_drift));
 u->latency_variance = (1.0 - FILTER_PARAMETER) * u->latency_variance + 
FILTER_PARAMETER * current_latency_error * current_latency_error;
@@ -474,17 +477,22 @@ static void adjust_rates(struct userdata *u) {
 u->kalman_variance = u->kalman_variance * u->latency_variance / 
(u->kalman_variance + u->latency_variance) + u->latency_variance / 4 + 200;
 }
 
-pa_log_debug("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) current_buffer_latency / PA_USEC_PER_MSEC,
-(double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
-(double) current_latency / PA_USEC_PER_MSEC);
-
-pa_log_debug("Loopback latency at optimum rate is %0.2f ms", 
(double)latency_at_optimum_rate / PA_USEC_PER_MSEC);
-
 /* Calculate new rate */
 new_rate = rate_controller(u, base_rate, old_rate, 
(int32_t)(filtered_latency - final_latency), latency_difference);
 
+pa_log_debug("Loopback status %s to %s:\nSource latency: %0.2f ms\n
Buffer: %0.2f ms\nSink latency: %0.2f ms\nEnd-to-end latency: %0.2f 
ms\n"
+ "Deviation from target latency at optimum rate: %0.2f 
usec\nAverage prediction error: ± %0.2f usec\nOptimum rate: %0.2f Hz\n  
  Deviation from base rate: %i Hz",
+u->source_output->source->name,
+u->sink_input->sink->name,
+(double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
+(double) current_buffer_latency / PA_USEC_PER_MSEC,
+(double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC,
+(double) current_latency / PA_USEC_PER_MSEC,
+(double) latency_at_optimum_rate - final_latency,
+(double) u->latency_error,
+u->drift_compensation_rate + base_rate,
+(int32_t)(new_rate - base_rate));
+
 /* Save current latency difference at new rate for next cycle and reset 
flags */
 u->last_latency_difference = current_source_sink_latency + 
current_buffer_latency * old_rate / new_rate - final_latency;
 
@@ -522,7 +530,6 @@ static void adjust_rates(struct userdata *u) {
 
 /* Set rate */
 pa_sink_input_set_rate(u->sink_input, new_rate);
-pa_log_debug("[%s] Updated sampling rate to %lu Hz.", 
u->sink_input->sink->name, (unsigned long) new_rate);
 }
 
 /* Called from main context */
@@ -857,9 +864,11 @@ static void source_output_moving_cb(pa_source_output *o, 
pa_source *dest) {
 u->iteration_counter = 0;
 u->underrun_counter = 0;
 
+/* Reset booleans and latency error */
 u->source_sink_changed = true;
 u->underrun_occured = false;
 u->source_latency_offset_changed = false;
+u->latency_error = 0;
 
 /* Send a mesage to the output thread that the source has changed.
  * If the sink is invalid here during a profile switching sit

[pulseaudio-discuss] [PATCH 08/10] loopback: Add low_device_latency parameter

2018-04-09 Thread Georg Chini
For USB devices the latency jitter strongly depends on device latency. Therefore
a boolean low_device_latency parameter is introduced to half the device latency.
Normally 1/3 of the configured end-to-end latency is used, with the parameter
this is changed to 1/6. In many situations the parameter can improve latency
stability but it will also lead to significantly higher CPU consumption.
---
 src/modules/module-loopback.c | 20 ++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index eeb93264..048fe4f9 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -46,6 +46,7 @@ PA_MODULE_USAGE(
 "adjust_time= "
 "latency_msec= "
 "max_latency_msec= "
+"low_device_latency= "
 "adjust_threshold_usec= "
 "format= "
 "rate= "
@@ -98,6 +99,7 @@ struct userdata {
 pa_usec_t max_latency;
 pa_usec_t adjust_time;
 uint32_t adjust_threshold;
+bool low_device_latency;
 
 /* Latency boundaries and current values */
 pa_usec_t min_source_latency;
@@ -189,6 +191,7 @@ static const char* const valid_modargs[] = {
 "adjust_time",
 "latency_msec",
 "max_latency_msec",
+"low_device_latency",
 "adjust_threshold_usec",
 "format",
 "rate",
@@ -776,11 +779,14 @@ static void update_effective_source_latency(struct 
userdata *u, pa_source *sourc
  * Set source output latency to one third of the overall latency if possible.
  * The choice of one third is rather arbitrary somewhere between the minimum
  * possible latency which would cause a lot of CPU load and half the configured
- * latency which would quickly lead to underruns */
+ * latency which would quickly lead to underruns. In low device latency mode 
set
+ * source to one sixth of the overall latency. */
 static void set_source_output_latency(struct userdata *u, pa_source *source) {
 pa_usec_t latency, requested_latency;
 
 requested_latency = u->latency / 3;
+if (u->low_device_latency)
+requested_latency = u->latency / 6;
 
 /* Normally we try to configure sink and source latency equally. If the
  * sink latency cannot match the requested source latency try to set the
@@ -1149,11 +1155,14 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, 
int code, void *data, in
  * Set sink input latency to one third of the overall latency if possible.
  * The choice of one third is rather arbitrary somewhere between the minimum
  * possible latency which would cause a lot of CPU load and half the configured
- * latency which would quickly lead to underruns. */
+ * latency which would quickly lead to underruns. In low device latency mode
+ * set sink to one sixth of the overall latency. */
 static void set_sink_input_latency(struct userdata *u, pa_sink *sink) {
  pa_usec_t latency, requested_latency;
 
 requested_latency = u->latency / 3;
+if (u->low_device_latency)
+requested_latency = u->latency / 6;
 
 /* Normally we try to configure sink and source latency equally. If the
  * source latency cannot match the requested sink latency try to set the
@@ -1482,6 +1491,7 @@ int pa__init(pa_module *m) {
 uint32_t adjust_time_sec;
 const char *n;
 bool remix = true;
+bool low_device_latency = false;
 
 pa_assert(m);
 
@@ -1574,6 +1584,11 @@ int pa__init(pa_module *m) {
 max_latency_msec = latency_msec;
 }
 
+if (pa_modargs_get_value_boolean(ma, "low_device_latency", 
&low_device_latency) < 0) {
+pa_log("Invalid boolean device latency parameter");
+goto fail;
+}
+
 m->userdata = u = pa_xnew0(struct userdata, 1);
 u->core = m->core;
 u->module = m;
@@ -1594,6 +1609,7 @@ int pa__init(pa_module *m) {
 u->latency_error = 0;
 u->adjust_threshold = adjust_threshold;
 u->target_latency_cross_counter = 0;
+u->low_device_latency = low_device_latency;
 u->initial_adjust_pending = true;
 
 adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 06/10] loopback: Add adjust_threshold_usec parameter

2018-04-09 Thread Georg Chini
In many situations, the P-controller is too sensitive and therefore exhibits 
rate hunting.
To avoid rate hunting, the sensibility of the controller is set by the new 
parameter
adjust_threshold_usec. The parameter value is the deviation from the target 
latency in usec
which is needed to produce a 1 Hz deviation from the optimum sample rate.
The default is set to 200 usec, which should be sufficient in most cases. If 
the accuracy
of the latency reports is bad and rate hunting is observed, the parameter must 
be increased,
while it can be lowered to achieve less latency jitter if the latency reports 
are accurate.
More details at
https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt
---
 src/modules/module-loopback.c | 43 +++
 1 file changed, 35 insertions(+), 8 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 1db39ef4..9f18ad1f 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -46,6 +46,7 @@ PA_MODULE_USAGE(
 "adjust_time= "
 "latency_msec= "
 "max_latency_msec= "
+"adjust_threshold_usec= "
 "format= "
 "rate= "
 "channels= "
@@ -60,6 +61,8 @@ PA_MODULE_USAGE(
 
 #define FILTER_PARAMETER 0.125
 
+#define DEFAULT_ADJUST_THRESHOLD_USEC 250
+
 #define MEMBLOCKQ_MAXLENGTH (1024*1024*32)
 
 #define MIN_DEVICE_LATENCY (2.5*PA_USEC_PER_MSEC)
@@ -94,6 +97,7 @@ struct userdata {
 pa_usec_t latency;
 pa_usec_t max_latency;
 pa_usec_t adjust_time;
+uint32_t adjust_threshold;
 
 /* Latency boundaries and current values */
 pa_usec_t min_source_latency;
@@ -184,6 +188,7 @@ static const char* const valid_modargs[] = {
 "adjust_time",
 "latency_msec",
 "max_latency_msec",
+"adjust_threshold_usec",
 "format",
 "rate",
 "channels",
@@ -255,11 +260,10 @@ static void teardown(struct userdata *u) {
 }
 
 /* rate controller, called from main context
- * - maximum deviation from base rate is less than 1%
- * - controller step size is limited to 2.01‰
+ * - maximum deviation from optimum rate for P-controller is less than 1%
+ * - P-controller step size is limited to 2.01‰
  * - will calculate an optimum rate
- * - exhibits hunting with USB or Bluetooth sources
- */
+*/
 static uint32_t rate_controller(
 struct userdata *u,
 uint32_t base_rate, uint32_t old_rate,
@@ -267,7 +271,21 @@ static uint32_t rate_controller(
 int32_t latency_difference_at_base_rate) {
 
 double new_rate, new_rate_1, new_rate_2;
-double min_cycles_1, min_cycles_2, drift_rate, latency_drift;
+double min_cycles_1, min_cycles_2, drift_rate, latency_drift, 
controller_weight, min_weight;
+uint32_t base_rate_with_drift;
+
+base_rate_with_drift = (int)(base_rate + u->drift_compensation_rate);
+
+/* If we are less than 2‰ away from the optimum rate, lower weight of the
+ * P-controller. The weight is determined by the fact that a correction
+ * of 0.5 Hz needs to be applied by the controller when the latency
+ * difference gets larger than the threshold. The weight follows
+ * from the definition of the controller. The minimum will only
+ * be reached when one adjust threshold away from the target. */
+controller_weight = 1;
+min_weight = PA_CLAMP(0.5 / (double)base_rate * (100.0 + 
(double)u->real_adjust_time / u->adjust_threshold), 0, 1.0);
+if ((double)abs((int)(old_rate - base_rate_with_drift)) / 
base_rate_with_drift < 0.002)
+controller_weight = 
PA_CLAMP((double)abs(latency_difference_at_optimum_rate) / u->adjust_threshold 
* min_weight, min_weight, 1.0);
 
 /* Calculate next rate that is not more than 2‰ away from the last rate */
 min_cycles_1 = (double)abs(latency_difference_at_optimum_rate) / 
u->real_adjust_time / 0.002 + 1;
@@ -276,10 +294,11 @@ static uint32_t rate_controller(
 /* Calculate best rate to correct the current latency offset, limit at
  * 1% difference from base_rate */
 min_cycles_2 = (double)abs(latency_difference_at_optimum_rate) / 
u->real_adjust_time / 0.01 + 1;
-new_rate_2 = (double)base_rate * (1.0 + 
(double)latency_difference_at_optimum_rate / min_cycles_2 / 
u->real_adjust_time);
+new_rate_2 = (double)base_rate * (1.0 + controller_weight * 
latency_difference_at_optimum_rate / min_cycles_2 / u->real_adjust_time);
 
-/* Choose the rate that is nearer to base_rate */
-if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate))
+/* Choose the rate that is nearer to base_rate unless we are already near
+ * to the desired latency and rate */
+if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate) && 
controller_weight > 0.99)
 new_rate = new_rate_1;
 else
 new_rate = new_rate_2;
@@ -1435,6 +1454,7 @@ int pa__init(pa_module *m) {
 bool source_dont_move;
 uint32_t latency_msec;
 uint32_t max_l

[pulseaudio-discuss] [PATCH 09/10] loopback: Add adjust_time_msec parameter to allow adjust times below 1s

2018-04-09 Thread Georg Chini
A new parameter is introduced to allow specifying smaller adjust_time values
than 1s. This may be useful for a better latency control, although with alsa
devices and the current smoother code no improvement could be found.
This patch also changes the default adjust time to 1s, the old value of 10s
does not allow a tight control of the end to end latency and would lead to
unnecessary jitter.
---
 src/modules/module-loopback.c | 19 +++
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 048fe4f9..fd3f794e 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -44,6 +44,7 @@ PA_MODULE_USAGE(
 "source= "
 "sink= "
 "adjust_time= "
+"adjust_time_msec= "
 "latency_msec= "
 "max_latency_msec= "
 "low_device_latency= "
@@ -68,7 +69,7 @@ PA_MODULE_USAGE(
 
 #define MIN_DEVICE_LATENCY (2.5*PA_USEC_PER_MSEC)
 
-#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
+#define DEFAULT_ADJUST_TIME_USEC (1*PA_USEC_PER_SEC)
 
 typedef struct loopback_msg loopback_msg;
 
@@ -189,6 +190,7 @@ static const char* const valid_modargs[] = {
 "source",
 "sink",
 "adjust_time",
+"adjust_time_msec",
 "latency_msec",
 "max_latency_msec",
 "low_device_latency",
@@ -1489,6 +1491,7 @@ int pa__init(pa_module *m) {
 bool channels_set = false;
 pa_memchunk silence;
 uint32_t adjust_time_sec;
+uint32_t adjust_time_msec;
 const char *n;
 bool remix = true;
 bool low_device_latency = false;
@@ -1618,10 +1621,18 @@ int pa__init(pa_module *m) {
 goto fail;
 }
 
-if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
+adjust_time_msec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_MSEC;
+if (pa_modargs_get_value_u32(ma, "adjust_time_msec", &adjust_time_msec) < 
0 || (adjust_time_msec != 0 && adjust_time_msec < 100)) {
+pa_log("Failed to parse adjust_time_msec value");
+goto fail;
+}
+
+/* If adjust_time and adjust_time_msec are both specified, prefer the 
adjust_time_msec value */
+u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
+if (adjust_time_msec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_MSEC)
+u->adjust_time = adjust_time_msec * PA_USEC_PER_MSEC;
+else 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;
 
 u->real_adjust_time = u->adjust_time;
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 3/8] pactl, pacmd, cli-command: Add send-message command

2018-04-09 Thread Georg Chini
The patch also adds some API documentation.
---
 doc/messaging_api.txt| 15 ++
 man/pactl.1.xml.in   |  7 +++
 man/pulse-cli-syntax.5.xml.in|  7 +++
 shell-completion/bash/pulseaudio |  5 +++--
 shell-completion/zsh/_pulseaudio |  2 ++
 src/pulse/introspect.h   |  3 ++-
 src/pulsecore/cli-command.c  | 44 
 src/utils/pacmd.c|  1 +
 src/utils/pactl.c| 43 ++-
 9 files changed, 123 insertions(+), 4 deletions(-)
 create mode 100644 doc/messaging_api.txt

diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
new file mode 100644
index ..cbc06e75
--- /dev/null
+++ b/doc/messaging_api.txt
@@ -0,0 +1,15 @@
+Message API reference
+
+The message API allows any object within pulseaudio to register a message
+handler. A message handler is a function that can be called by clients using
+PA_COMMAND_SEND_OBJECT_MESSAGE. A message consists at least of an object path
+and a message command, both specified as strings. Additional parameters can
+be specified using a single string, but are not mandatory. The message handler
+returns an error number as defined in def.h and also returns a string in
+the "response" variable. The following reference lists available messages,
+their parameters and return values.
+
+Recipient:
+Message:
+Parameters:
+Return value:
diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
index 39569b6b..4052fae3 100644
--- a/man/pactl.1.xml.in
+++ b/man/pactl.1.xml.in
@@ -245,6 +245,13 @@ License along with PulseAudio; if not, see 
.
   'ac3-iec61937, format.rate = "[ 32000, 44100, 48000 ]"').

 
+
+  send-message RECIPIENT MESSAGE 
MESSAGE_PARAMETERS
+  Send a message string to the specified recipient object. If 
applicable an additional string containing
+  message parameters can be specified. A string is returned as a response 
to the message. For available message
+  commands see 
https://cgit.freedesktop.org/pulseaudio/pulseaudio/tree/doc/messaging_api.txt.
+
+
 
   subscribe
   Subscribe to events, pactl does not exit by itself, but 
keeps waiting for new events.
diff --git a/man/pulse-cli-syntax.5.xml.in b/man/pulse-cli-syntax.5.xml.in
index 0a0fabaf..83f55d6b 100644
--- a/man/pulse-cli-syntax.5.xml.in
+++ b/man/pulse-cli-syntax.5.xml.in
@@ -297,6 +297,13 @@ License along with PulseAudio; if not, see 
.
   Debug: Show shared properties.
 
 
+
+  send-message recipient message 
message_parameters
+  Send a message string to the specified recipient object. If 
applicable an additional string containing
+  message parameters can be specified. A string is returned as a response 
to the message. For available message
+  commands see 
https://cgit.freedesktop.org/pulseaudio/pulseaudio/tree/doc/messaging_api.txt.
+
+
 
   exit
   Terminate the daemon. If you want to terminate a CLI
diff --git a/shell-completion/bash/pulseaudio b/shell-completion/bash/pulseaudio
index e473b9c2..797ec067 100644
--- a/shell-completion/bash/pulseaudio
+++ b/shell-completion/bash/pulseaudio
@@ -120,7 +120,8 @@ _pactl() {
 set-source-port set-sink-volume set-source-volume
 set-sink-input-volume set-source-output-volume 
set-sink-mute
 set-source-mute set-sink-input-mute set-source-output-mute
-set-sink-formats set-port-latency-offset subscribe help)
+set-sink-formats set-port-latency-offset subscribe 
send-message
+help)
 
 _init_completion -n = || return
 preprev=${words[$cword-2]}
@@ -270,7 +271,7 @@ _pacmd() {
 move-sink-input move-source-output suspend-sink 
suspend-source
 suspend set-card-profile set-sink-port set-source-port
 set-port-latency-offset set-log-target set-log-level 
set-log-meta
-set-log-time set-log-backtrace)
+set-log-time set-log-backtrace send-message)
 _init_completion -n = || return
 preprev=${words[$cword-2]}
 
diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio
index 0e9e89bd..a2817ebb 100644
--- a/shell-completion/zsh/_pulseaudio
+++ b/shell-completion/zsh/_pulseaudio
@@ -263,6 +263,7 @@ _pactl_completion() {
 'set-sink-input-mute: mute a stream'
 'set-source-output-mute: mute a recording stream'
 'set-sink-formats: set supported formats of a sink'
+'send-message: send a message to a pulseaudio object'
 'subscribe: subscribe to events'
 )
 
@@ -561,6 +562,7 @@ _pacmd_completion() {
 'dump: show daemon configuration'
 'dump-volumes: show the state of all volumes'
 'shared: show shared properties'
+'s

[pulseaudio-discuss] [PATCH 4/8] core: add message handler

2018-04-09 Thread Georg Chini
This patch adds a small message handler to the core which enables
clients to list available handlers via the list-handlers message.
Command: pacmd send-message /core list-handlers
pactl can be used with the same parameters.

The patch also introduces a convention for the return string.
It consists of a list of elements where curly braces are used
to separate elements. Each element can itself contain further
elements. For example consider a message that returns multiple
elements which each contain an integer and an array of float.
A response string would look like that:
{{Integer} {{1st float} {2nd float} ...}}{...}
---
 doc/messaging_api.txt   | 21 +--
 src/pulsecore/core.c| 45 +
 src/pulsecore/message-handler.c | 24 ++
 3 files changed, 84 insertions(+), 6 deletions(-)

diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
index cbc06e75..431a5df2 100644
--- a/doc/messaging_api.txt
+++ b/doc/messaging_api.txt
@@ -6,10 +6,19 @@ PA_COMMAND_SEND_OBJECT_MESSAGE. A message consists at least 
of an object path
 and a message command, both specified as strings. Additional parameters can
 be specified using a single string, but are not mandatory. The message handler
 returns an error number as defined in def.h and also returns a string in
-the "response" variable. The following reference lists available messages,
-their parameters and return values.
+the "response" variable. If the string is not empty it consists of elements.
+Curly braces are used to separate elements. Each element can itself contain
+further elements. For example consider a message that returns multiple elements
+which each contain an integer and an array of float. A response string would
+look like that:
+{{Integer} {{1st float} {2nd float} ...}}{...}
+Any characters that are not enclosed in curly braces are ignored (all 
characters
+between { and {, between } and } and between } and {). The same syntax is used
+to specify message parameters. The following reference lists available 
messages,
+their parameters and return values. If a return value is enclosed in {}, this
+means that multiple elements of the same type may be returned.
 
-Recipient:
-Message:
-Parameters:
-Return value:
+Object path: /core
+Message: list-handlers
+Parameters: None
+Return value: {{{Handler name} {Description}} ...}
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index f4723728..10cd6f2f 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -33,11 +33,13 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #include "core.h"
 
@@ -61,6 +63,45 @@ static int core_process_msg(pa_msgobject *o, int code, void 
*userdata, int64_t o
 
 static void core_free(pa_object *o);
 
+/* Returns a list of handlers. */
+static char *message_handler_list(pa_core *c) {
+pa_strbuf *buf;
+void *state = NULL;
+struct pa_message_handler *handler;
+
+buf = pa_strbuf_new();
+
+pa_strbuf_putc(buf, '{');
+PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
+pa_strbuf_putc(buf, '{');
+
+pa_strbuf_printf(buf, "{%s} {", handler->object_path);
+if (handler->description)
+pa_strbuf_puts(buf, handler->description);
+
+pa_strbuf_puts(buf, "}}");
+}
+pa_strbuf_putc(buf, '}');
+
+return pa_strbuf_to_string_free(buf);
+}
+
+static int core_message_handler(const char *object_path, const char *message, 
const char *message_parameters, char **response, void *userdata) {
+pa_core *c;
+
+pa_assert(c = (pa_core *) userdata);
+pa_assert(message);
+pa_assert(response);
+pa_assert(pa_safe_streq(object_path, "/core"));
+
+if (pa_streq(message, "list-handlers")) {
+*response = message_handler_list(c);
+return PA_OK;
+}
+
+return -PA_ERR_NOTIMPLEMENTED;
+}
+
 pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, 
size_t shm_size) {
 pa_core* c;
 pa_mempool *pool;
@@ -105,6 +146,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool 
enable_memfd, size_t
 c->shared = pa_hashmap_new(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func);
 c->message_handlers = pa_hashmap_new(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func);
 
+pa_message_handler_register(c, "/core", "Core message handler", 
core_message_handler, (void *) c);
+
 c->default_source = NULL;
 c->default_sink = NULL;
 
@@ -205,6 +248,8 @@ static void core_free(pa_object *o) {
 pa_assert(pa_hashmap_isempty(c->shared));
 pa_hashmap_free(c->shared);
 
+pa_message_handler_unregister(c, "/core");
+
 pa_assert(pa_hashmap_isempty(c->message_handlers));
 pa_hashmap_free(c->message_handlers);
 
diff --git a/src/pulsecore/message-handler.c b/src/pulsecore/message-handler.c
index 7555a18f..18c62fc2 100644
--- a/src/pulsecore/message-handler.c
+++ b/src/pulsecore/me

[pulseaudio-discuss] [PATCH 0/8] core: Add message sending/receiving

2018-04-09 Thread Georg Chini
This is a re-base of the messaging patches. It does not contain
changes. The first two patches have already been reviewed, I
nevertheless included them for completeness.

Georg Chini (8):
  core: add simple message interface
  protocol-native: add message sending capability
  pactl, pacmd, cli-command: Add send-message command
  core: add message handler
  pactl: Implement list message-handlers
  message-params: Allow parameter strings to contain escaped curly
braces
  message-params: Add read/write functions for various simple data types
  message-params: Add read functions for arrays

 PROTOCOL |  14 +
 configure.ac |   2 +-
 doc/messaging_api.txt|  72 +
 man/pactl.1.xml.in   |   9 +-
 man/pulse-cli-syntax.5.xml.in|   7 +
 shell-completion/bash/pulseaudio |   7 +-
 shell-completion/zsh/_pulseaudio |   3 +
 src/Makefile.am  |   3 +
 src/map-file |  21 ++
 src/pulse/introspect.c   |  71 +
 src/pulse/introspect.h   |  17 +
 src/pulse/message-params.c   | 654 +++
 src/pulse/message-params.h   | 109 +++
 src/pulsecore/cli-command.c  |  44 +++
 src/pulsecore/core.c |  49 +++
 src/pulsecore/core.h |   2 +-
 src/pulsecore/message-handler.c  | 148 +
 src/pulsecore/message-handler.h  |  50 +++
 src/pulsecore/native-common.h|   3 +
 src/pulsecore/pdispatch.c|   3 +
 src/pulsecore/protocol-native.c  |  52 
 src/utils/pacmd.c|   1 +
 src/utils/pactl.c| 106 ++-
 23 files changed, 1438 insertions(+), 9 deletions(-)
 create mode 100644 doc/messaging_api.txt
 create mode 100644 src/pulse/message-params.c
 create mode 100644 src/pulse/message-params.h
 create mode 100644 src/pulsecore/message-handler.c
 create mode 100644 src/pulsecore/message-handler.h

-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 5/8] pactl: Implement list message-handlers

2018-04-09 Thread Georg Chini
For better readability, "pactl list message-handlers" is introduced which
prints a formatted output of "pactl send-message /core list-handlers".

The patch also adds the functions pa_message_param_split_list() and
pa_message_param_read_string() for easy parsing of the message response
string. Because the function needs to modify the parameter string,
the message handler and the pa_context_string_callback function now
receive a char* instead of a const char* as parameter argument.
---
 man/pactl.1.xml.in   |   2 +-
 shell-completion/bash/pulseaudio |   2 +-
 shell-completion/zsh/_pulseaudio |   1 +
 src/Makefile.am  |   1 +
 src/map-file |   2 +
 src/pulse/introspect.c   |  11 +++-
 src/pulse/introspect.h   |   2 +-
 src/pulse/message-params.c   | 116 +++
 src/pulse/message-params.h   |  41 ++
 src/pulsecore/core.c |   2 +-
 src/pulsecore/message-handler.c  |   9 ++-
 src/pulsecore/message-handler.h  |   2 +-
 src/utils/pactl.c|  65 +-
 13 files changed, 245 insertions(+), 11 deletions(-)
 create mode 100644 src/pulse/message-params.c
 create mode 100644 src/pulse/message-params.h

diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
index 4052fae3..66c0bda9 100644
--- a/man/pactl.1.xml.in
+++ b/man/pactl.1.xml.in
@@ -76,7 +76,7 @@ License along with PulseAudio; if not, see 
.
 
   list [short] [TYPE]
   Dump all currently loaded modules, available sinks, sources, 
streams, etc.  TYPE must be one of:
-  modules, sinks, sources, sink-inputs, source-outputs, clients, samples, 
cards.  If not specified, all info is listed.  If
+  modules, sinks, sources, sink-inputs, source-outputs, clients, samples, 
cards, message-handlers.  If not specified, all info is listed.  If
   short is given, output is in a tabular format, for easy parsing by 
scripts.
 
 
diff --git a/shell-completion/bash/pulseaudio b/shell-completion/bash/pulseaudio
index 797ec067..24d2aa1c 100644
--- a/shell-completion/bash/pulseaudio
+++ b/shell-completion/bash/pulseaudio
@@ -113,7 +113,7 @@ _pactl() {
 local comps
 local flags='-h --help --version -s --server= --client-name='
 local list_types='short sinks sources sink-inputs source-outputs cards
-modules samples clients'
+modules samples clients message-handlers'
 local commands=(stat info list exit upload-sample play-sample remove-sample
 load-module unload-module move-sink-input 
move-source-output
 suspend-sink suspend-source set-card-profile set-sink-port
diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio
index a2817ebb..c24d0387 100644
--- a/shell-completion/zsh/_pulseaudio
+++ b/shell-completion/zsh/_pulseaudio
@@ -285,6 +285,7 @@ _pactl_completion() {
 'clients: list connected clients'
 'samples: list samples'
 'cards: list available cards'
+'message-handlers: list available message-handlers'
 )
 
 if ((CURRENT == 2)); then
diff --git a/src/Makefile.am b/src/Makefile.am
index 27e5f875..ccdad8ff 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -896,6 +896,7 @@ libpulse_la_SOURCES = \
pulse/timeval.c pulse/timeval.h \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
+   pulse/message-params.c pulse/message-params.h \
pulse/volume.c pulse/volume.h \
pulse/xmalloc.c pulse/xmalloc.h
 
diff --git a/src/map-file b/src/map-file
index 4d747f19..385731dc 100644
--- a/src/map-file
+++ b/src/map-file
@@ -225,6 +225,8 @@ pa_mainloop_quit;
 pa_mainloop_run;
 pa_mainloop_set_poll_func;
 pa_mainloop_wakeup;
+pa_message_param_read_string;
+pa_message_param_split_list;
 pa_msleep;
 pa_operation_cancel;
 pa_operation_get_state;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 76bfee41..23901eb3 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -2215,8 +2215,15 @@ static void context_string_callback(pa_pdispatch *pd, 
uint32_t command, uint32_t
 response = "";
 
 if (o->callback) {
-pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback;
-cb(o->context, success, response, o->userdata);
+char *response_copy;
+pa_context_string_cb_t cb;
+
+response_copy = pa_xstrdup(response);
+
+cb = (pa_context_string_cb_t) o->callback;
+cb(o->context, success, response_copy, o->userdata);
+
+pa_xfree(response_copy);
 }
 
 finish:
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index fbe5b668..bcec48da 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -449,7 +449,7 @@ pa_operation* pa_context_unload_module(pa_context *c, 
uint32_t idx

[pulseaudio-discuss] [PATCH 1/8] core: add simple message interface

2018-04-09 Thread Georg Chini
This patch adds a new feature to the core which allows to send messages
to objects. An object can register/unregister a message handler with
pa_message_handler_{register, unregister}() while a message can be sent
to the handler using the pa_message_handler_send_message() function.
A message has 4 arguments (apart from passing the core):

object_path: The path identifying the object that will receive the message
message: message command
message_parameters: A string containing additional parameters
response: Pointer to a response string that will be filled by the
  message handler. The caller is responsible to free the string.

The patch is a precondition for the following patches that allow clients
to send messages to pulseaudio objects.

There is no restriction on object names, except that an object path
always starts with a "/". The intention is to use a path-like syntax,
for example /core/sink_1 for a sink or /name/instances/index for modules.
The exact naming convention still needs to be agreed.
---
 src/Makefile.am |   1 +
 src/pulsecore/core.c|   4 ++
 src/pulsecore/core.h|   2 +-
 src/pulsecore/message-handler.c | 104 
 src/pulsecore/message-handler.h |  50 +++
 5 files changed, 160 insertions(+), 1 deletion(-)
 create mode 100644 src/pulsecore/message-handler.c
 create mode 100644 src/pulsecore/message-handler.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 59b703db..27e5f875 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -964,6 +964,7 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/core-scache.c pulsecore/core-scache.h \
pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
pulsecore/core.c pulsecore/core.h \
+   pulsecore/message-handler.c pulsecore/message-handler.h \
pulsecore/hook-list.c pulsecore/hook-list.h \
pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \
pulsecore/modargs.c pulsecore/modargs.h \
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index da42a13e..f4723728 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -103,6 +103,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool 
enable_memfd, size_t
 
 c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func);
 c->shared = pa_hashmap_new(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func);
+c->message_handlers = pa_hashmap_new(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func);
 
 c->default_source = NULL;
 c->default_sink = NULL;
@@ -204,6 +205,9 @@ static void core_free(pa_object *o) {
 pa_assert(pa_hashmap_isempty(c->shared));
 pa_hashmap_free(c->shared);
 
+pa_assert(pa_hashmap_isempty(c->message_handlers));
+pa_hashmap_free(c->message_handlers);
+
 pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
 pa_hashmap_free(c->modules_pending_unload);
 
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 38622f61..d03897c4 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -164,7 +164,7 @@ struct pa_core {
 pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, 
*source_outputs, *modules, *scache;
 
 /* Some hashmaps for all sorts of entities */
-pa_hashmap *namereg, *shared;
+pa_hashmap *namereg, *shared, *message_handlers;
 
 /* The default sink/source as configured by the user. If the user hasn't
  * explicitly configured anything, these are set to NULL. These are strings
diff --git a/src/pulsecore/message-handler.c b/src/pulsecore/message-handler.c
new file mode 100644
index ..7555a18f
--- /dev/null
+++ b/src/pulsecore/message-handler.c
@@ -0,0 +1,104 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see .
+***/
+
+#ifdef HAVE_CONFIG_H
+#include 
+#endif
+
+#include 
+#include 
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "message-handler.h"
+
+/* Message handler functions */
+
+/* Register message handler for the specified object. object_path must be a 
unique name starting with "/". */
+void pa_message_handler_register(pa_core *c, const char *object_path, const 
char *description, pa_message_handler_cb_t cb, void *userdata) {
+struct pa_message_handler

[pulseaudio-discuss] [PATCH 8/8] message-params: Add read functions for arrays

2018-04-09 Thread Georg Chini
---
 doc/messaging_api.txt  |   5 ++
 src/map-file   |   4 +
 src/pulse/message-params.c | 202 +
 src/pulse/message-params.h |  12 +++
 4 files changed, 223 insertions(+)

diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
index d080b783..57a92f58 100644
--- a/doc/messaging_api.txt
+++ b/doc/messaging_api.txt
@@ -53,6 +53,11 @@ pa_message_param_read_double() - read a double from a 
parameter list
 pa_message_param_read_int64() - read an integer from a parameter list
 pa_message_param_read_uint64() - read an unsigned from a parameter list
 pa_message_param_read_bool() - read a boolean from a parameter list
+pa_message_param_read_double_array() - read an array of double from list
+pa_message_param_read_int64_array() - read an array of int64 from list
+pa_message_param_read_uint64_array() - read an array of uint64 from list
+pa_message_param_read_string_array() - read an array of strings from a list
+
 
 All read functions return 1 on success, 0 if end of string is found and -1 on
 parse error. Additionally, for the numeric/boolean read functions, 2 is 
returned
diff --git a/src/map-file b/src/map-file
index ab8c21c6..88d892ef 100644
--- a/src/map-file
+++ b/src/map-file
@@ -230,9 +230,13 @@ pa_message_param_end_list;
 pa_message_param_new;
 pa_message_param_read_bool;
 pa_message_param_read_double;
+pa_message_param_read_double_array;
 pa_message_param_read_int64;
+pa_message_param_read_int64_array;
 pa_message_param_read_string;
+pa_message_param_read_string_array;
 pa_message_param_read_uint64;
+pa_message_param_read_uint64_array;
 pa_message_param_split_list;
 pa_message_param_to_string;
 pa_message_param_write_bool;
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index 93972399..b9846863 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -38,6 +38,8 @@ struct pa_message_param {
 pa_strbuf *buffer;
 };
 
+/* Helper functions */
+
 /* Remove escaping from a string */
 static char *unescape(char *value) {
 char *tmp;
@@ -62,6 +64,59 @@ static char *unescape(char *value) {
 return value;
 }
 
+/* Count number of top level elements in parameter list */
+static int count_elements(const char *c) {
+const char *s;
+uint32_t element_count;
+bool found_element, found_backslash;
+int open_braces;
+
+if (!c || *c == 0)
+return PA_PARAM_LIST_END;
+
+element_count = 0;
+open_braces = 0;
+found_element = false;
+found_backslash = false;
+s = c;
+
+/* Count elements in list */
+while (*s != 0) {
+
+/* Skip escaped curly braces. */
+if (*s == '\\') {
+found_backslash = true;
+s++;
+continue;
+}
+
+if (*s == '{' && !found_backslash) {
+found_element = true;
+open_braces++;
+}
+if (*s == '}' && !found_backslash)
+open_braces--;
+
+/* unexpected closing brace, parse error */
+if (open_braces < 0)
+return PA_PARAM_PARSE_ERROR;
+
+if (open_braces == 0 && found_element) {
+element_count++;
+found_element = false;
+}
+
+found_backslash = false;
+s++;
+}
+
+/* missing closing brace, parse error */
+if (open_braces > 0)
+return PA_PARAM_PARSE_ERROR;
+
+return element_count;
+}
+
 /* Read functions */
 
 /* Split the specified string into elements. An element is defined as
@@ -300,6 +355,153 @@ int pa_message_param_read_bool(char *c, bool *result, 
void **state) {
 return PA_PARAM_OK;
 }
 
+/* Converts a parameter list to a string array. Escaping is removed
+ * from a string if the string does not contain a list. If allocate
+ * is true, new strings will be allocated, otherwise pointers to
+ * sub-strings within c will be returned. */
+int pa_message_param_read_string_array(char *c, char ***parameter_list, bool 
allocate) {
+char *start_pos;
+void *state = NULL;
+uint32_t element_count, i;
+bool is_unpacked;
+int err;
+
+pa_assert(parameter_list);
+
+*parameter_list = NULL;
+
+/* Count elements, return if no element was found or parse error. */
+if ((element_count = count_elements(c)) <= 0)
+return element_count;
+
+/* Allocate array */
+*parameter_list = pa_xmalloc0(element_count * sizeof(char *));
+
+i = 0;
+while ((err = pa_message_param_split_list(c, &start_pos, &is_unpacked, 
&state)) > 0) {
+(*parameter_list)[i] = start_pos;
+if (is_unpacked)
+(*parameter_list)[i] = unescape(start_pos);
+
+/* If new strings are allocated, they must be freed by the caller */
+if (allocate)
+(*parameter_list)[i] = pa_xstrdup((*parameter_list)[i]);
+
+i++;
+}
+
+if (err < 0) {
+if (allocate) {
+for (i = 0; i < element_count; i++)
+pa_xfree((*parameter_list)[i]);
+}

[pulseaudio-discuss] [PATCH 7/8] message-params: Add read/write functions for various simple data types

2018-04-09 Thread Georg Chini
See doc/messaging_api.txt for the added functions. All read functions
return 1 on success, 0 if end of string is found and -1 on parse error.
Additionally, for the numeric/boolean read functions, 2 is returned if
the list element is empty.
---
 doc/messaging_api.txt  |  15 +++-
 src/map-file   |   9 +++
 src/pulse/message-params.c | 185 +++--
 src/pulse/message-params.h |  35 +
 4 files changed, 236 insertions(+), 8 deletions(-)

diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
index 0e6be53f..d080b783 100644
--- a/doc/messaging_api.txt
+++ b/doc/messaging_api.txt
@@ -27,6 +27,11 @@ the structure
 pa_message_param_begin_list() - starts a list
 pa_message_param_end_list() - ends a list
 pa_message_param_write_string() - writes a string to a pa_message_param 
structure
+pa_message_param_write_double() - writes a double to a pa_message_param 
structure
+pa_message_param_write_int64() - writes an integer to a pa_message_param 
structure
+pa_message_param_write_uint64() - writes an unsigned to a pa_message_param 
structure
+pa_message_param_write_bool() - writes a boolean to a pa_message_param 
structure
+pa_message_param_write_raw() - writes raw a string to a pa_message_param 
structure
 
 For string parameters that contain curly braces, those braces must be escaped
 by adding a "\" before them. This however means that a trailing backslash would
@@ -44,9 +49,15 @@ Other strings can be passed without modification.
 For reading, the following functions are available:
 pa_message_param_split_list() - parse message parameter string
 pa_message_param_read_string() - read a string from a parameter list
+pa_message_param_read_double() - read a double from a parameter list
+pa_message_param_read_int64() - read an integer from a parameter list
+pa_message_param_read_uint64() - read an unsigned from a parameter list
+pa_message_param_read_bool() - read a boolean from a parameter list
 
-pa_message_param_read_string() also reverts the changes that
-pa_message_param_write_string() might have introduced.
+All read functions return 1 on success, 0 if end of string is found and -1 on
+parse error. Additionally, for the numeric/boolean read functions, 2 is 
returned
+if the list element is empty. Also pa_message_param_read_string() reverts the
+changes that pa_message_param_write_string() might have introduced.
 
 Reference:
 
diff --git a/src/map-file b/src/map-file
index 372d190d..ab8c21c6 100644
--- a/src/map-file
+++ b/src/map-file
@@ -228,10 +228,19 @@ pa_mainloop_wakeup;
 pa_message_param_begin_list;
 pa_message_param_end_list;
 pa_message_param_new;
+pa_message_param_read_bool;
+pa_message_param_read_double;
+pa_message_param_read_int64;
 pa_message_param_read_string;
+pa_message_param_read_uint64;
 pa_message_param_split_list;
 pa_message_param_to_string;
+pa_message_param_write_bool;
+pa_message_param_write_double;
+pa_message_param_write_int64;
+pa_message_param_write_raw;
 pa_message_param_write_string;
+pa_message_param_write_uint64;
 pa_msleep;
 pa_operation_cancel;
 pa_operation_get_state;
diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c
index d68ec59d..93972399 100644
--- a/src/pulse/message-params.c
+++ b/src/pulse/message-params.c
@@ -23,6 +23,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include 
@@ -84,7 +85,7 @@ int pa_message_param_split_list(char *c, char **result, bool 
*is_unpacked, void
 
 /* Empty or no string */
 if (!current || *current == 0)
-return 0;
+return PA_PARAM_LIST_END;
 
 /* Find opening brace */
 while (*current != 0) {
@@ -101,7 +102,7 @@ int pa_message_param_split_list(char *c, char **result, 
bool *is_unpacked, void
 
 /* unexpected closing brace, parse error */
 if (*current == '}' && !found_backslash)
-return -1;
+return PA_PARAM_PARSE_ERROR;
 
 found_backslash = false;
 current++;
@@ -109,7 +110,7 @@ int pa_message_param_split_list(char *c, char **result, 
bool *is_unpacked, void
 
 /* No opening brace found, end of string */
 if (*current == 0)
-return 0;
+return PA_PARAM_LIST_END;
 
 if (is_unpacked)
 *is_unpacked = true;
@@ -140,7 +141,7 @@ int pa_message_param_split_list(char *c, char **result, 
bool *is_unpacked, void
 /* Parse error, closing brace missing */
 if (open_braces != 0) {
 *result = NULL;
-return -1;
+return PA_PARAM_PARSE_ERROR;
 }
 
 /* Replace } with 0 */
@@ -148,7 +149,7 @@ int pa_message_param_split_list(char *c, char **result, 
bool *is_unpacked, void
 
 *state = current + 1;
 
-return 1;
+return PA_PARAM_OK;
 }
 
 /* Read a string from the parameter list. The state pointer is
@@ -165,7 +166,7 @@ int pa_message_param_read_string(char *c, char **result, 
bool allocate, void **s
 
 *result = NULL;
 
-if ((err = pa_message_param_split_list(c, &start_pos, &is_unpacked, 
s

[pulseaudio-discuss] [PATCH 6/8] message-params: Allow parameter strings to contain escaped curly braces

2018-04-09 Thread Georg Chini
The patch adds the possibility to escape curly braces within parameter strings
and introduces several new functions that can be used for writing parameters.

For writing, the structure pa_message_param, which is a wrapper for pa_strbuf
has been created. Following new write functions are available:

pa_message_param_new() - creates a new pa_message_param structure
pa_message_param_to_string() - converts a pa_message_param to string and frees
the structure
pa_message_param_begin_list() - starts a list
pa_message_param_end_list() - ends a list
pa_message_param_write_string() - writes a string to a pa_message_param 
structure

For string parameters that contain curly braces, those braces must be escaped
by adding a "\" before them. This however means that a trailing backslash would
falsely escape the closing bracket. To avoid this, single quotes must be added
at start and end of the string. The function pa_message_param_write_string()
has a parameter do_escape. If true, the necessary escaping is added. Escaping
is only needed if a string might fulfill one of the following conditions:

- It contains curly braces
- It contains a trailing "\"
- It is enclosed in single quotes

Other strings can be passed without modification.

For reading, pa_message_param_read_string() reverts the changes that
pa_message_param_write_string() might have introduced.

The patch also adds more restrictions on the object path name. Now only
alphanumeric characters and one of "_", ".", "-" and "/" are allowed.
The path name may not end with a /. If the user specifies a trailing / when
sending a message, it will be removed.
---
 doc/messaging_api.txt   |  34 +++-
 src/Makefile.am |   3 +-
 src/map-file|   5 ++
 src/pulse/message-params.c  | 181 ++--
 src/pulse/message-params.h  |  23 -
 src/pulsecore/core.c|  20 ++---
 src/pulsecore/message-handler.c |  53 +++-
 src/utils/pactl.c   |   4 +-
 8 files changed, 279 insertions(+), 44 deletions(-)

diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
index 431a5df2..0e6be53f 100644
--- a/doc/messaging_api.txt
+++ b/doc/messaging_api.txt
@@ -14,10 +14,42 @@ look like that:
 {{Integer} {{1st float} {2nd float} ...}}{...}
 Any characters that are not enclosed in curly braces are ignored (all 
characters
 between { and {, between } and } and between } and {). The same syntax is used
-to specify message parameters. The following reference lists available 
messages,
+to specify message parameters. The reference further down lists available 
messages,
 their parameters and return values. If a return value is enclosed in {}, this
 means that multiple elements of the same type may be returned.
 
+There are several functions that simplify reading and writing message parameter
+strings. For writing, the structure pa_message_param can be used. Following
+functions are available:
+pa_message_param_new() - creates a new pa_message_param structure
+pa_message_param_to_string() - converts a pa_message_param to string and frees
+the structure
+pa_message_param_begin_list() - starts a list
+pa_message_param_end_list() - ends a list
+pa_message_param_write_string() - writes a string to a pa_message_param 
structure
+
+For string parameters that contain curly braces, those braces must be escaped
+by adding a "\" before them. This however means that a trailing backslash would
+falsely escape the closing bracket. To avoid this, single quotes must be added
+at start and end of the string. The function pa_message_param_write_string()
+has a parameter do_escape. If true, the necessary escaping is added. Escaping
+is only needed if a string might fulfill one of the following conditions:
+
+- It contains curly braces
+- It contains a trailing "\"
+- It is enclosed in single quotes
+
+Other strings can be passed without modification.
+
+For reading, the following functions are available:
+pa_message_param_split_list() - parse message parameter string
+pa_message_param_read_string() - read a string from a parameter list
+
+pa_message_param_read_string() also reverts the changes that
+pa_message_param_write_string() might have introduced.
+
+Reference:
+
 Object path: /core
 Message: list-handlers
 Parameters: None
diff --git a/src/Makefile.am b/src/Makefile.am
index ccdad8ff..72d8cf22 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -675,6 +675,7 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
pulse/timeval.c pulse/timeval.h \
pulse/rtclock.c pulse/rtclock.h \
pulse/volume.c pulse/volume.h \
+   pulse/message-params.c pulse/message-params.h \
pulsecore/atomic.h \
pulsecore/authkey.c pulsecore/authkey.h \
pulsecore/conf-parser.c pulsecore/conf-parser.h \
@@ -884,6 +885,7 @@ libpulse_la_SOURCES = \
pulse/mainloop-api.c pulse/mainloop-api.h \
puls

[pulseaudio-discuss] [PATCH 2/8] protocol-native: add message sending capability

2018-04-09 Thread Georg Chini
This patch adds the PA_COMMAND_SEND_OBJECT_MESSAGE command to protocol-native
so that clients can use the messaging feature introduced in the previous patch.

Sending messages can in effect replace the extension system for modules. The
approach is more flexible than the extension interface because a generic string
format is used to exchange information. Furthermore the messaging system can be
used for any object, not only for modules, and is easier to implement than
extensions.
---
 PROTOCOL| 14 +
 configure.ac|  2 +-
 src/map-file|  1 +
 src/pulse/introspect.c  | 64 +
 src/pulse/introspect.h  | 16 +++
 src/pulsecore/native-common.h   |  3 ++
 src/pulsecore/pdispatch.c   |  3 ++
 src/pulsecore/protocol-native.c | 52 +
 8 files changed, 154 insertions(+), 1 deletion(-)

diff --git a/PROTOCOL b/PROTOCOL
index 546998b7..f693cd3d 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -420,6 +420,20 @@ memfd support only to 10.0+ clients.
 
 Check commit 451d1d676237c81 for further details.
 
+## v33, implemented by >= 13.0
+
+Added new command for communication with objects.
+
+PA_COMMAND_SEND_OBJECT_MESSAGE:
+sends a message to an object identified by an object path
+
+parameters:
+string object_path - unique path identifying the object
+string message - message command
+string message_parameters - additional parameters if required
+
+The command returns a string.
+
  If you just changed the protocol, read this
 ## module-tunnel depends on the sink/source/sink-input/source-input protocol
 ## internals, so if you changed these, you might have broken module-tunnel.
diff --git a/configure.ac b/configure.ac
index 57cb3a92..5537d921 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,7 +40,7 @@ AC_SUBST(PA_MINOR, pa_minor)
 AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
 
 AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 32)
+AC_SUBST(PA_PROTOCOL_VERSION, 33)
 
 # The stable ABI for client applications, for the version info x:y:z
 # always will hold y=z
diff --git a/src/map-file b/src/map-file
index 9b6cba22..4d747f19 100644
--- a/src/map-file
+++ b/src/map-file
@@ -87,6 +87,7 @@ pa_context_remove_autoload_by_name;
 pa_context_remove_sample;
 pa_context_rttime_new;
 pa_context_rttime_restart;
+pa_context_send_message_to_object;
 pa_context_set_card_profile_by_index;
 pa_context_set_card_profile_by_name;
 pa_context_set_default_sink;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 510d784a..76bfee41 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -2184,3 +2184,67 @@ pa_operation* 
pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, in
 
 return o;
 }
+
+/** Object response string processing **/
+
+static void context_string_callback(pa_pdispatch *pd, uint32_t command, 
uint32_t tag, pa_tagstruct *t, void *userdata) {
+pa_operation *o = userdata;
+const char *response;
+int success = 1;
+
+pa_assert(pd);
+pa_assert(o);
+pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+if (!o->context)
+goto finish;
+
+if (command != PA_COMMAND_REPLY) {
+if (pa_context_handle_error(o->context, command, t, false) < 0)
+goto finish;
+
+success = 0;
+response = "";
+} else if (pa_tagstruct_gets(t, &response)  < 0 ||
+   !pa_tagstruct_eof(t)) {
+pa_context_fail(o->context, PA_ERR_PROTOCOL);
+goto finish;
+}
+
+if (!response)
+response = "";
+
+if (o->callback) {
+pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback;
+cb(o->context, success, response, o->userdata);
+}
+
+finish:
+pa_operation_done(o);
+pa_operation_unref(o);
+}
+
+pa_operation* pa_context_send_message_to_object(pa_context *c, const char 
*object_path, const char *message, const char *message_parameters, 
pa_context_string_cb_t cb, void *userdata) {
+pa_operation *o;
+pa_tagstruct *t;
+uint32_t tag;
+
+pa_assert(c);
+pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, 
PA_ERR_BADSTATE);
+
+o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+t = pa_tagstruct_command(c, PA_COMMAND_SEND_OBJECT_MESSAGE, &tag);
+
+pa_tagstruct_puts(t, object_path);
+pa_tagstruct_puts(t, message);
+pa_tagstruct_puts(t, message_parameters);
+
+pa_pstream_send_tagstruct(c->pstream, t);
+pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, 
context_string_callback, pa_operation_ref(o), (pa_free_cb_t) 
pa_operation_unref);
+
+return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 43389b73..7e47e740 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -204,6 +204,12 @@
  * 

[pulseaudio-discuss] [PATCH 3/3] message-handler: Send signal on handler events

2018-04-09 Thread Georg Chini
---
 src/pulsecore/message-handler.c | 25 +
 1 file changed, 25 insertions(+)

diff --git a/src/pulsecore/message-handler.c b/src/pulsecore/message-handler.c
index 75310667..a983e136 100644
--- a/src/pulsecore/message-handler.c
+++ b/src/pulsecore/message-handler.c
@@ -23,6 +23,7 @@
 #include 
 
 #include 
+#include 
 
 #include 
 #include 
@@ -64,6 +65,7 @@ static bool object_path_is_valid(const char *test_string) {
 /* Register message handler for the specified object. object_path must be a 
unique name starting with "/". */
 void pa_message_handler_register(pa_core *c, const char *object_path, const 
char *description, pa_message_handler_cb_t cb, void *userdata) {
 struct pa_message_handler *handler;
+char *sig_param;
 
 pa_assert(c);
 pa_assert(object_path);
@@ -80,11 +82,17 @@ void pa_message_handler_register(pa_core *c, const char 
*object_path, const char
 handler->description = pa_xstrdup(description);
 
 pa_assert_se(pa_hashmap_put(c->message_handlers, handler->object_path, 
handler) == 0);
+
+/* Notify clients that a handler was added. */
+sig_param = pa_sprintf_malloc("{%s}", object_path);
+pa_signal_post(c, "message-api", 1, "handler-added", sig_param);
+pa_xfree(sig_param);
 }
 
 /* Unregister a message handler */
 void pa_message_handler_unregister(pa_core *c, const char *object_path) {
 struct pa_message_handler *handler;
+char *sig_param;
 
 pa_assert(c);
 pa_assert(object_path);
@@ -94,6 +102,11 @@ void pa_message_handler_unregister(pa_core *c, const char 
*object_path) {
 pa_xfree(handler->object_path);
 pa_xfree(handler->description);
 pa_xfree(handler);
+
+/* Notify clients that a handler was removed. */
+sig_param = pa_sprintf_malloc("{%s}", object_path);
+pa_signal_post(c, "message-api", 1, "handler-removed", sig_param);
+pa_xfree(sig_param);
 }
 
 /* Send a message to an object identified by object_path */
@@ -134,6 +147,8 @@ int pa_message_handler_send_message(pa_core *c, const char 
*object_path, const c
 /* Set handler description */
 int pa_message_handler_set_description(pa_core *c, const char *object_path, 
const char *description) {
 struct pa_message_handler *handler;
+char *sig_param;
+pa_message_param *param;
 
 pa_assert(c);
 pa_assert(object_path);
@@ -141,9 +156,19 @@ int pa_message_handler_set_description(pa_core *c, const 
char *object_path, cons
 if (!(handler = pa_hashmap_get(c->message_handlers, object_path)))
 return -PA_ERR_NOENTITY;
 
+param = pa_message_param_new();
+pa_message_param_write_string(param, object_path, false);
+pa_message_param_write_string(param, handler->description, true);
+pa_message_param_write_string(param, description, true);
+sig_param = pa_message_param_to_string(param);
+
 pa_xfree(handler->description);
 handler->description = pa_xstrdup(description);
 
+/* Notify clients that a handler description changed. */
+pa_signal_post(c, "message-api", 1, "handler-changed", sig_param);
+pa_xfree(sig_param);
+
 return PA_OK;
 }
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 2/3] pactl: Add signal receiver

2018-04-09 Thread Georg Chini
---
 man/pactl.1.xml.in |  2 +-
 src/utils/pactl.c  | 20 
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
index 66c0bda9..5279791b 100644
--- a/man/pactl.1.xml.in
+++ b/man/pactl.1.xml.in
@@ -254,7 +254,7 @@ License along with PulseAudio; if not, see 
.
 
 
   subscribe
-  Subscribe to events, pactl does not exit by itself, but 
keeps waiting for new events.
+  Subscribe to events and signals, pactl does not exit by 
itself, but keeps waiting for new events.
 
 
   
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 1f13e60c..87827d38 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -1251,6 +1251,17 @@ static void context_subscribe_callback(pa_context *c, 
pa_subscription_event_type
 fflush(stdout);
 }
 
+static void context_signal_callback(pa_context *c, const char 
*signal_object_path, const char *signal, char *signal_parameters, void 
*userdata) {
+pa_assert(c);
+
+printf(_("Signal '%s' from %s\n"),
+   signal,
+   signal_object_path);
+if (signal_parameters)
+printf(_("Signal parameters: '%s'\n"), signal_parameters);
+fflush(stdout);
+}
+
 static void context_state_callback(pa_context *c, void *userdata) {
 pa_operation *o = NULL;
 
@@ -1498,6 +1509,15 @@ static void context_state_callback(pa_context *c, void 
*userdata) {
  PA_SUBSCRIPTION_MASK_CARD,
  NULL,
  NULL);
+if (o) {
+pa_operation_unref(o);
+actions++;
+}
+
+pa_context_set_signal_callback(c, context_signal_callback, 
NULL);
+
+o = pa_context_subscribe_signals(c, (uint64_t) -1, NULL, 
NULL);
+
 break;
 
 default:
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 1/3] protocol-native: Add signal receiving capability

2018-04-09 Thread Georg Chini
This patch extends the client subscription API, so that signals sent from
PulseAudio can be processed. Within PulseAudio, a signal can be emitted
using pa_signal_post(). The interface can be used to notify the client of
events that are not covered by the subscription API (for example a button
press event on a bluetooth headset).

Setting up signal notification is very similar to using subscriptions.
First the client needs to subscribe with pa_context_subscribe_signals()
and then sets up a signal handler using pa_context_set_signal_callback().

The signal handler will receive three arguments in addition to the usual
context and userdata:
object_path - string that specifies the origin of the signal
signal - string that specifies the type of the signal
signal_parameters - optional string for additional information
---
 PROTOCOL|  6 
 src/map-file|  2 ++
 src/pulse/def.h |  6 
 src/pulse/internal.h|  2 ++
 src/pulse/subscribe.c   | 71 +
 src/pulse/subscribe.h   |  9 ++
 src/pulsecore/core.h|  1 +
 src/pulsecore/message-handler.c | 16 ++
 src/pulsecore/message-handler.h | 13 
 src/pulsecore/native-common.h   |  3 ++
 src/pulsecore/pdispatch.c   |  1 +
 src/pulsecore/protocol-native.c | 56 
 12 files changed, 180 insertions(+), 6 deletions(-)

diff --git a/PROTOCOL b/PROTOCOL
index f693cd3d..d20f48c6 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -434,6 +434,12 @@ parameters:
 
 The command returns a string.
 
+Added signal sending capability and command PA_COMMAND_SUBSCRIBE_SIGNALS.
+This command allows clients to subscribe to signals that PulseAudio sends.
+
+parameters:
+uint64_t facility_mask - subscription mask
+
  If you just changed the protocol, read this
 ## module-tunnel depends on the sink/source/sink-input/source-input protocol
 ## internals, so if you changed these, you might have broken module-tunnel.
diff --git a/src/map-file b/src/map-file
index 88d892ef..e3f86bf9 100644
--- a/src/map-file
+++ b/src/map-file
@@ -94,6 +94,7 @@ pa_context_set_default_sink;
 pa_context_set_default_source;
 pa_context_set_event_callback;
 pa_context_set_name;
+pa_context_set_signal_callback;
 pa_context_set_sink_input_mute;
 pa_context_set_sink_input_volume;
 pa_context_set_sink_mute_by_index;
@@ -114,6 +115,7 @@ pa_context_set_state_callback;
 pa_context_set_subscribe_callback;
 pa_context_stat;
 pa_context_subscribe;
+pa_context_subscribe_signals;
 pa_context_suspend_sink_by_index;
 pa_context_suspend_sink_by_name;
 pa_context_suspend_source_by_index;
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 100df5b5..964fa9fa 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -615,6 +615,11 @@ typedef enum pa_subscription_event_type {
 PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
 /**< An object was removed */
 
+/** \cond fulldocs */
+PA_SUBSCRIPTION_EVENT_SIGNAL = 0x0040U,
+/** A signal was issued. Only used internally. */
+/** \endcond */
+
 PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
 /**< A mask to extract the event operation from an event value */
 
@@ -650,6 +655,7 @@ typedef enum pa_subscription_event_type {
 #define PA_SUBSCRIPTION_EVENT_NEW PA_SUBSCRIPTION_EVENT_NEW
 #define PA_SUBSCRIPTION_EVENT_CHANGE PA_SUBSCRIPTION_EVENT_CHANGE
 #define PA_SUBSCRIPTION_EVENT_REMOVE PA_SUBSCRIPTION_EVENT_REMOVE
+#define PA_SUBSCRIPTION_EVENT_SIGNAL PA_SUBSCRIPTION_EVENT_SIGNAL
 #define PA_SUBSCRIPTION_EVENT_TYPE_MASK PA_SUBSCRIPTION_EVENT_TYPE_MASK
 /** \endcond */
 
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 0d18aa71..a1b4957e 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -95,6 +95,8 @@ struct pa_context {
 void *subscribe_userdata;
 pa_context_event_cb_t event_callback;
 void *event_userdata;
+pa_context_signal_cb_t signal_callback;
+void *signal_userdata;
 
 pa_mempool *mempool;
 
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
index e7cce2e3..fa5567fb 100644
--- a/src/pulse/subscribe.c
+++ b/src/pulse/subscribe.c
@@ -32,7 +32,6 @@
 void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t 
tag, pa_tagstruct *t, void *userdata) {
 pa_context *c = userdata;
 pa_subscription_event_type_t e;
-uint32_t idx;
 
 pa_assert(pd);
 pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
@@ -42,15 +41,44 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t 
command, uint32_t tag
 
 pa_context_ref(c);
 
-if (pa_tagstruct_getu32(t, &e) < 0 ||
-pa_tagstruct_getu32(t, &idx) < 0 ||
-!pa_tagstruct_eof(t)) {
+if (pa_tagstruct_getu32(t, &e) < 0) {
 pa_context_fail(c, PA_ERR_PROTOCOL);
 goto finish;
 }
 
-if (c->subscribe_callback)
-c->subscribe_callback(c, e, idx, c->subscribe_userdata);
+if (e != PA_SUBSCRIPTION_EVENT_SIGNAL) {
+uint

[pulseaudio-discuss] [PATCH 0/3] core: Add signal sending/receiving

2018-04-09 Thread Georg Chini
This patch set adds signal sending capability to the core. This is
used to send signals when message handlers are added/removed or
when the description changes.

This patch set depends on the messaging patch set.

Georg Chini (3):
  protocol-native: Add signal receiving capability
  pactl: Add signal receiver
  message-handler: Send signal on handler events

 PROTOCOL|  6 
 man/pactl.1.xml.in  |  2 +-
 src/map-file|  2 ++
 src/pulse/def.h |  6 
 src/pulse/internal.h|  2 ++
 src/pulse/subscribe.c   | 71 +
 src/pulse/subscribe.h   |  9 ++
 src/pulsecore/core.h|  1 +
 src/pulsecore/message-handler.c | 41 
 src/pulsecore/message-handler.h | 13 
 src/pulsecore/native-common.h   |  3 ++
 src/pulsecore/pdispatch.c   |  1 +
 src/pulsecore/protocol-native.c | 56 
 src/utils/pactl.c   | 20 
 14 files changed, 226 insertions(+), 7 deletions(-)

-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 1/3] card: add infrastructure to enable/disable jack detection

2018-04-09 Thread Georg Chini
This patch adds a card message handler which will be used to enable
or disable jack detection on a per port basis. Only the necessary
variables and functions are added without changing functionality.
---
 src/pulsecore/card.c| 129 +++-
 src/pulsecore/card.h|   4 ++
 src/pulsecore/device-port.h |   2 +
 3 files changed, 133 insertions(+), 2 deletions(-)

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index bc5b75b0..e91c034f 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -27,11 +27,13 @@
 
 #include 
 #include 
+#include 
 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "card.h"
@@ -86,6 +88,7 @@ pa_card_new_data* pa_card_new_data_init(pa_card_new_data 
*data) {
 data->proplist = pa_proplist_new();
 data->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_card_profile_free);
 data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref);
+data->jack_detection = true;
 return data;
 }
 
@@ -120,9 +123,115 @@ void pa_card_new_data_done(pa_card_new_data *data) {
 pa_xfree(data->name);
 }
 
+static int card_message_handler(const char *object_path, const char *message, 
char *message_parameters, char **response, void *userdata) {
+pa_card *c;
+char *port_name;
+bool jack_detection;
+void *state = NULL;
+void *state2;
+pa_device_port *port = NULL;
+
+pa_assert(c = (pa_card *) userdata);
+pa_assert(message);
+pa_assert(response);
+
+/* Get the first argument of the message */
+if (pa_message_param_read_string(message_parameters, &port_name, false, 
&state) <= 0)
+return -PA_ERR_INVALID;
+
+/* If the port argument is not "all", retrieve the port */
+if (!pa_streq(port_name, "all")) {
+if (!(port = pa_hashmap_get(c->ports, port_name)))
+return -PA_ERR_INVALID;
+}
+
+if (pa_streq(message, "set-jack-detection")) {
+
+/* Get the requested detection state */
+if (pa_message_param_read_bool(message_parameters, &jack_detection, 
&state) <= 0)
+return -PA_ERR_INVALID;
+
+if (!port) {
+c->jack_detection = jack_detection;
+
+PA_HASHMAP_FOREACH(port, c->ports, state2)
+port->jack_detection = c->jack_detection;
+
+} else
+port->jack_detection = jack_detection;
+
+return PA_OK;
+
+} else if (pa_streq(message, "get-jack-detection")) {
+pa_message_param *param;
+
+param = pa_message_param_new();
+if (!port) {
+pa_message_param_begin_list(param);
+pa_message_param_write_string(param, c->name, false);
+pa_message_param_write_bool(param, c->jack_detection);
+pa_message_param_end_list(param);
+
+PA_HASHMAP_FOREACH(port, c->ports, state2) {
+pa_message_param_begin_list(param);
+pa_message_param_write_string(param, port->name, false);
+pa_message_param_write_bool(param, port->jack_detection);
+pa_message_param_end_list(param);
+}
+
+} else {
+
+pa_message_param_begin_list(param);
+pa_message_param_write_string(param, port->name, false);
+pa_message_param_write_bool(param, port->jack_detection);
+pa_message_param_end_list(param);
+}
+
+*response = pa_message_param_to_string(param);
+return PA_OK;
+
+} else if (pa_streq(message, "set-port-state")) {
+
+/* Not implemented because jack_detection is still unused
+ * and manually setting a port state would require to disable
+ * jack detection */
+return -PA_ERR_NOTIMPLEMENTED;
+
+} else if (pa_streq(message, "get-port-state")) {
+pa_message_param *param;
+uint64_t current_state;
+
+param = pa_message_param_new();
+if (!port) {
+PA_HASHMAP_FOREACH(port, c->ports, state2) {
+pa_message_param_begin_list(param);
+pa_message_param_write_string(param, port->name, false);
+current_state = port->available;
+pa_message_param_write_uint64(param, current_state);
+pa_message_param_end_list(param);
+}
+
+} else {
+
+pa_message_param_begin_list(param);
+pa_message_param_write_string(param, port->name, false);
+current_state = port->available;
+pa_message_param_write_uint64(param, current_state);
+pa_message_param_end_list(param);
+}
+
+*response = pa_message_param_to_string(param);
+return PA_OK;
+
+}
+
+return -PA_ERR_NOTIMPLEMENTED;
+}
+
 pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
 pa_card *c;
-const char 

[pulseaudio-discuss] [PATCH 0/3] card: Add messages to control jack detection

2018-04-09 Thread Georg Chini
This patch set uses the message API to implement messages that can
control jack detection and port availability. Jack detection can be
switched on and off for individual ports or whole cards. The current
port state can be queried.

Additionally, a port can be forced to some state. This implies
switching off jack detection for that port.

Because the set depends on the not yet reviewed messaging patches
it only implements the basic functionality and does not provide
pactl commands.

Georg Chini (3):
  card: add infrastructure to enable/disable jack detection
  device-port: Add messages to enable/disable jack detection
  core: prefer available devices during default source/sink selection

 doc/messaging_api.txt|  22 
 src/modules/alsa/alsa-ucm.c  |   2 +-
 src/modules/alsa/module-alsa-card.c  |   4 +-
 src/modules/bluetooth/module-bluez5-device.c |   4 +-
 src/pulsecore/card.c | 169 ++-
 src/pulsecore/card.h |   4 +
 src/pulsecore/core.c |  20 +++-
 src/pulsecore/device-port.c  |  15 ++-
 src/pulsecore/device-port.h  |   5 +-
 9 files changed, 234 insertions(+), 11 deletions(-)

-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH 2/3] device-port: Add messages to enable/disable jack detection

2018-04-09 Thread Georg Chini
With this patch, messages can be sent to the cards to enable/disable
jack detection for the whole card or single ports, manually set a port
state and to retrieve the current state of jack detection and port
availability.
---
 doc/messaging_api.txt| 22 
 src/modules/alsa/alsa-ucm.c  |  2 +-
 src/modules/alsa/module-alsa-card.c  |  4 +--
 src/modules/bluetooth/module-bluez5-device.c |  4 +--
 src/pulsecore/card.c | 52 
 src/pulsecore/device-port.c  | 15 +++-
 src/pulsecore/device-port.h  |  3 +-
 7 files changed, 89 insertions(+), 13 deletions(-)

diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
index 57a92f58..b3dfede0 100644
--- a/doc/messaging_api.txt
+++ b/doc/messaging_api.txt
@@ -70,3 +70,25 @@ Object path: /core
 Message: list-handlers
 Parameters: None
 Return value: {{{Handler name} {Description}} ...}
+
+Object path: /cards/
+Message: set-jack-detection
+Parameters: {port_name|all}{0|1}
+Return value: None
+
+Object path: /cards/
+Message: get-jack-detection
+Parameters: {port_name|all}
+Return value: {{port_name}{0|1}} ...
+If "all" is specified, the first value returned is the setting
+for the card.
+
+Object path: /cards/
+Message: set-port-state
+Parameters: {port_name|all}{availability}
+Return value: None
+
+Object path: /cards/
+Message: get-port-state
+Parameters: {port_name|all}
+Return value: {{port_name}{availability}} ...
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index b42c0407..03073a53 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1880,7 +1880,7 @@ static void ucm_port_update_available(struct ucm_port 
*port) {
 }
 }
 
-pa_device_port_set_available(port->core_port, available);
+pa_device_port_set_available(port->core_port, available, false);
 }
 
 #else /* HAVE_ALSA_UCM */
diff --git a/src/modules/alsa/module-alsa-card.c 
b/src/modules/alsa/module-alsa-card.c
index 385d61d2..104bb05d 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -421,10 +421,10 @@ static int report_jack_state(snd_mixer_elem_t *melem, 
unsigned int mask) {
 
 for (tp = tports; tp->port; tp++)
 if (tp->avail != PA_AVAILABLE_NO)
-   pa_device_port_set_available(tp->port, tp->avail);
+   pa_device_port_set_available(tp->port, tp->avail, false);
 for (tp = tports; tp->port; tp++)
 if (tp->avail == PA_AVAILABLE_NO)
-   pa_device_port_set_available(tp->port, tp->avail);
+   pa_device_port_set_available(tp->port, tp->avail, false);
 
 for (tp = tports; tp->port; tp++) {
 pa_alsa_port_data *data;
diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index 935ee2ce..560de934 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -2253,9 +2253,9 @@ static void handle_transport_state_change(struct userdata 
*u, struct pa_bluetoot
 
 /* Update port availability */
 pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name));
-pa_device_port_set_available(port, get_port_availability(u, 
PA_DIRECTION_OUTPUT));
+pa_device_port_set_available(port, get_port_availability(u, 
PA_DIRECTION_OUTPUT), false);
 pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name));
-pa_device_port_set_available(port, get_port_availability(u, 
PA_DIRECTION_INPUT));
+pa_device_port_set_available(port, get_port_availability(u, 
PA_DIRECTION_INPUT), false);
 
 /* Acquire or release transport as needed */
 acquire = (t->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile 
== t->profile);
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index e91c034f..e056711c 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -127,6 +127,7 @@ static int card_message_handler(const char *object_path, 
const char *message, ch
 pa_card *c;
 char *port_name;
 bool jack_detection;
+uint64_t port_state;
 void *state = NULL;
 void *state2;
 pa_device_port *port = NULL;
@@ -154,11 +155,29 @@ static int card_message_handler(const char *object_path, 
const char *message, ch
 if (!port) {
 c->jack_detection = jack_detection;
 
-PA_HASHMAP_FOREACH(port, c->ports, state2)
+PA_HASHMAP_FOREACH(port, c->ports, state2) {
+pa_available_t avail = PA_AVAILABLE_UNKNOWN;
+
+/* If jack detection was enabled, set the port state
+ * to the hardware state. */
+if (c->jack_detection)
+avail = port->hw_available;
+
 port->jack_detection = c->jack_detection;
+pa_device_port_set_available(port, avail, true);
+}
+
+} else {
+pa_available_t avail

[pulseaudio-discuss] [PATCH 3/3] core: prefer available devices during default source/sink selection

2018-04-09 Thread Georg Chini
Because ports with disabled jack detection are set to availability
unknown by default, available ports should be preferred over ports
with unknown availability.
---
 src/pulsecore/core.c | 20 ++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index acd47cbb..8c074afe 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -325,7 +325,15 @@ static int compare_sinks(pa_sink *a, pa_sink *b) {
 
 core = a->core;
 
-/* Available sinks always beat unavailable sinks. */
+/* Available sinks always beat sinks with unknown availability. */
+if ((!a->active_port || a->active_port->available <= PA_AVAILABLE_UNKNOWN)
+&& b->active_port && b->active_port->available == PA_AVAILABLE_YES)
+return -1;
+if ((!b->active_port || b->active_port->available <= PA_AVAILABLE_UNKNOWN)
+&& a->active_port && a->active_port->available == PA_AVAILABLE_YES)
+return 1;
+
+/* Possibly available sinks always beat unavailable sinks. */
 if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
 && (!b->active_port || b->active_port->available != 
PA_AVAILABLE_NO))
 return -1;
@@ -402,7 +410,15 @@ static int compare_sources(pa_source *a, pa_source *b) {
 
 core = a->core;
 
-/* Available sources always beat unavailable sources. */
+/* Available sources always beat sources with unknown availability. */
+if ((!a->active_port || a->active_port->available <= PA_AVAILABLE_UNKNOWN)
+&& b->active_port && b->active_port->available == PA_AVAILABLE_YES)
+return -1;
+if ((!b->active_port || b->active_port->available <= PA_AVAILABLE_UNKNOWN)
+&& a->active_port && a->active_port->available == PA_AVAILABLE_YES)
+return 1;
+
+/* Possibly available sources always beat unavailable sources. */
 if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
 && (!b->active_port || b->active_port->available != 
PA_AVAILABLE_NO))
 return -1;
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] bluez5-device: Fix memory leak in sco_process_render()

2018-04-10 Thread Georg Chini

On 10.04.2018 10:21, Raman Shishniou wrote:

Hello,

On 04/09/2018 07:15 PM, Georg Chini wrote:

sco_process_render does not unref the memblock when it encounters an error.

This patch fixes the issue. It also changes the return value to 1 in the case
of EAGAIN. Because the data was already rendered and cannot be re-sent, we
have to discard the block.
---
  src/modules/bluetooth/module-bluez5-device.c | 12 +---
  1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index 95d288ef..b81c233c 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -282,9 +282,13 @@ static int sco_process_render(struct userdata *u) {
  if (errno == EINTR)
  /* Retry right away if we got interrupted */
  continue;
-else if (errno == EAGAIN)
-/* Hmm, apparently the socket was not writable, give up for now */
-return 0;
+
+pa_memblock_unref(memchunk.memblock);
+
+if (errno == EAGAIN)
+/* Hmm, apparently the socket was not writable, give up for now.
+ * Because the data was already rendered, let's discard the block. 
*/
+return 1;
  

1. errno value can be changed during pa_memblock_unref()


I don't think so. The only possible system calls used during unref should be
calls to free() and because we always use pa_xfree(), the errno value will
be preserved.


2. I think the same changes are required for a2dp_process_render() too.


No, a2dp_process_render() works slightly different. It keeps the memchunk
in userdata and tries to re-send the same block again.





  pa_log_error("Failed to write data to SCO socket: %s", 
pa_cstrerror(errno));
  return -1;
@@ -296,6 +300,8 @@ static int sco_process_render(struct userdata *u) {
  pa_log_error("Wrote memory block to socket only partially! %llu written, 
wanted to write %llu.",
  (unsigned long long) l,
  (unsigned long long) memchunk.length);
+
+pa_memblock_unref(memchunk.memblock);
  return -1;
  }
  


--
Raman



___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] bluez5-device: Fix memory leak in sco_process_render()

2018-04-10 Thread Georg Chini

On 10.04.2018 13:04, Raman Shishniou wrote:

Hello,

On 04/10/2018 01:38 PM, Georg Chini wrote:

On 10.04.2018 10:21, Raman Shishniou wrote:

Hello,

On 04/09/2018 07:15 PM, Georg Chini wrote:

sco_process_render does not unref the memblock when it encounters an error.

This patch fixes the issue. It also changes the return value to 1 in the case
of EAGAIN. Because the data was already rendered and cannot be re-sent, we
have to discard the block.
---
   src/modules/bluetooth/module-bluez5-device.c | 12 +---
   1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index 95d288ef..b81c233c 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -282,9 +282,13 @@ static int sco_process_render(struct userdata *u) {
   if (errno == EINTR)
   /* Retry right away if we got interrupted */
   continue;
-else if (errno == EAGAIN)
-/* Hmm, apparently the socket was not writable, give up for now */
-return 0;
+
+pa_memblock_unref(memchunk.memblock);
+
+if (errno == EAGAIN)
+/* Hmm, apparently the socket was not writable, give up for now.
+ * Because the data was already rendered, let's discard the block. 
*/
+return 1;
   

1. errno value can be changed during pa_memblock_unref()

I don't think so. The only possible system calls used during unref should be
calls to free() and because we always use pa_xfree(), the errno value will
be preserved.


I seen callback processing during memblock_free()


You are right, b->per_type.user.free_cb() is a user defined function
which could do anything. I'll send an updated version.




2. I think the same changes are required for a2dp_process_render() too.

No, a2dp_process_render() works slightly different. It keeps the memchunk
in userdata and tries to re-send the same block again.




   pa_log_error("Failed to write data to SCO socket: %s", 
pa_cstrerror(errno));
   return -1;
@@ -296,6 +300,8 @@ static int sco_process_render(struct userdata *u) {
   pa_log_error("Wrote memory block to socket only partially! %llu written, 
wanted to write %llu.",
   (unsigned long long) l,
   (unsigned long long) memchunk.length);
+
+pa_memblock_unref(memchunk.memblock);
   return -1;
   }
  

--
Raman




___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] bluez5-device, bluez5-discover: add hsp_source_buffer_msec argument

2018-04-10 Thread Georg Chini

On 10.04.2018 14:52, Luiz Augusto von Dentz wrote:

Hi George,

On Mon, Apr 9, 2018 at 7:16 PM, Georg Chini  wrote:

Currently the PA bluetooth code calls source_push() for each HSP source packet.
The default HSP MTU is 48 bytes, this means that source_push() is called every
3ms, which leads to increased CPU load especially on embedded systems.

This patch adds a hsp_source_buffer_msec argument to module-bluez5-discover and
module-bluez5-device. The argument gives the number of milliseconds of audio 
which
are buffered, before source_push() is called. The value can range from 0 to 
100ms,
and is rounded down to the next multiple of the MTU size. Therefore a value of 
less
than 2*MTU time corresponds to the original behavior.

Well SCO is, as the name suggests, synchronous or to be more precise
it isochronous so I wonder if this has been tested? It seems to me
this will start to behave like A2DP which buffer frames for a while
before sending, though A2DP is not really for voice where latency may
cause problems like lip sync issues or talking over someone else on a
call because the audio has a few second delay. I get that using 3ms
when the  default-fragment-size-msec is bigger than that may not make
any difference so we should probably use that instead.


The communication between PA and the head set is not changed by the
patch. It is only between PA and the client. Instead of pushing the data to
the client immediately, with this option PA buffers some blocks before
sending them on.




---
  src/modules/bluetooth/module-bluetooth-discover.c |  1 +
  src/modules/bluetooth/module-bluez5-device.c  | 68 +--
  src/modules/bluetooth/module-bluez5-discover.c| 14 -
  3 files changed, 64 insertions(+), 19 deletions(-)

I do have patches changing the bluez5 modules to bluetooth, I wonder
what happen to that.



If your patch gets into the tree first, I'll re-base my patch (again).
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH v2] bluez5-device: Fix memory leak in sco_process_render()

2018-04-10 Thread Georg Chini
sco_process_render does not unref the memblock when it encounters an error.

This patch fixes the issue. It also changes the return value to 1 in the case
of EAGAIN. Because the data was already rendered and cannot be re-sent, we
have to discard the block.
---
Changes in v2: Save errno before calling pa_memblock_unref()

 src/modules/bluetooth/module-bluez5-device.c | 19 ++-
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index 95d288ef..640ff058 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -253,6 +253,7 @@ static void connect_ports(struct userdata *u, void 
*new_data, pa_direction_t dir
 static int sco_process_render(struct userdata *u) {
 ssize_t l;
 pa_memchunk memchunk;
+int saved_errno;
 
 pa_assert(u);
 pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
@@ -279,14 +280,20 @@ static int sco_process_render(struct userdata *u) {
 if (l > 0)
 break;
 
-if (errno == EINTR)
+saved_errno = errno;
+
+if (saved_errno == EINTR)
 /* Retry right away if we got interrupted */
 continue;
-else if (errno == EAGAIN)
-/* Hmm, apparently the socket was not writable, give up for now */
-return 0;
 
-pa_log_error("Failed to write data to SCO socket: %s", 
pa_cstrerror(errno));
+pa_memblock_unref(memchunk.memblock);
+
+if (saved_errno == EAGAIN)
+/* Hmm, apparently the socket was not writable, give up for now.
+ * Because the data was already rendered, let's discard the block. 
*/
+return 1;
+
+pa_log_error("Failed to write data to SCO socket: %s", 
pa_cstrerror(saved_errno));
 return -1;
 }
 
@@ -296,6 +303,8 @@ static int sco_process_render(struct userdata *u) {
 pa_log_error("Wrote memory block to socket only partially! %llu 
written, wanted to write %llu.",
 (unsigned long long) l,
 (unsigned long long) memchunk.length);
+
+pa_memblock_unref(memchunk.memblock);
 return -1;
 }
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v3 00/12] Add module-gsettings

2018-04-18 Thread Georg Chini

On 17.04.2018 08:07, Tanu Kaskinen wrote:

Here are the PulseAudio patches for the GConf -> GSettings migration.
The first patch is the original patch from Sylvain Baubeau, rebased on
current master, and the rest of the patch set are various fixups
(mostly very simple stuff).

The first patch doesn't necessarily need to be reviewed, since I have
already reviewed it, but note that stdin-util.c and .h don't follow the
usual style conventions. I decided not to fix the style, because the
code will hopefully be removed some day anyway (when moving from
GSettings to the native protocol, which requires adding the necessary
features to the native protocol), but if someone has a problem with
that, then I can fix the style issues as well.

Sylvain Baubeau (1):
   module-gsettings: new module to store configuration using gsettings

Tanu Kaskinen (11):
   .gitignore: add module-gsettings related things
   default.pa: add module-gsettings
   gsettings: add the modules schema to the schema description file
   gconf, gsettings: fix config.h includes
   gsettings: rename "module" to "module-group"
   build-sys: remove a redundant enable_gsettings check
   gsettings: check that children haven't been deleted before using them
   gsettings: remove bad signal connection
   gsettings: free the module-group GSettings objects after use
   gsettings: free group_names after use
   build-sys: enable GSettings by default

  configure.ac   |  34 ++-
  src/.gitignore |   2 +
  src/Makefile.am|  31 ++-
  src/daemon/default.pa.in   |  13 +
  src/modules/gconf/module-gconf.c   | 290 +
  src/modules/gsettings/gsettings-helper.c   | 130 +
  src/modules/gsettings/module-gsettings.c   | 114 
  .../org.freedesktop.pulseaudio.gschema.xml | 115 
  src/modules/stdin-util.c   | 279 
  src/modules/stdin-util.h   |  85 ++
  10 files changed, 803 insertions(+), 290 deletions(-)
  create mode 100644 src/modules/gsettings/gsettings-helper.c
  create mode 100644 src/modules/gsettings/module-gsettings.c
  create mode 100644 
src/modules/gsettings/org.freedesktop.pulseaudio.gschema.xml
  create mode 100644 src/modules/stdin-util.c
  create mode 100644 src/modules/stdin-util.h


Patches look all good to me. One remark to the first patch: Should
the copyright notice in the new files not be removed?

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v3 00/12] Add module-gsettings

2018-04-19 Thread Georg Chini

On 19.04.2018 13:06, Tanu Kaskinen wrote:

On Wed, 2018-04-18 at 19:29 +0200, Georg Chini wrote:

On 17.04.2018 08:07, Tanu Kaskinen wrote:

Here are the PulseAudio patches for the GConf -> GSettings migration.
The first patch is the original patch from Sylvain Baubeau, rebased on
current master, and the rest of the patch set are various fixups
(mostly very simple stuff).

The first patch doesn't necessarily need to be reviewed, since I have
already reviewed it, but note that stdin-util.c and .h don't follow the
usual style conventions. I decided not to fix the style, because the
code will hopefully be removed some day anyway (when moving from
GSettings to the native protocol, which requires adding the necessary
features to the native protocol), but if someone has a problem with
that, then I can fix the style issues as well.

Sylvain Baubeau (1):
module-gsettings: new module to store configuration using gsettings

Tanu Kaskinen (11):
.gitignore: add module-gsettings related things
default.pa: add module-gsettings
gsettings: add the modules schema to the schema description file
gconf, gsettings: fix config.h includes
gsettings: rename "module" to "module-group"
build-sys: remove a redundant enable_gsettings check
gsettings: check that children haven't been deleted before using them
gsettings: remove bad signal connection
gsettings: free the module-group GSettings objects after use
gsettings: free group_names after use
build-sys: enable GSettings by default

   configure.ac   |  34 ++-
   src/.gitignore |   2 +
   src/Makefile.am|  31 ++-
   src/daemon/default.pa.in   |  13 +
   src/modules/gconf/module-gconf.c   | 290 
+
   src/modules/gsettings/gsettings-helper.c   | 130 +
   src/modules/gsettings/module-gsettings.c   | 114 
   .../org.freedesktop.pulseaudio.gschema.xml | 115 
   src/modules/stdin-util.c   | 279 
   src/modules/stdin-util.h   |  85 ++
   10 files changed, 803 insertions(+), 290 deletions(-)
   create mode 100644 src/modules/gsettings/gsettings-helper.c
   create mode 100644 src/modules/gsettings/module-gsettings.c
   create mode 100644 
src/modules/gsettings/org.freedesktop.pulseaudio.gschema.xml
   create mode 100644 src/modules/stdin-util.c
   create mode 100644 src/modules/stdin-util.h


Patches look all good to me. One remark to the first patch: Should
the copyright notice in the new files not be removed?

Well, we don't have any rule to avoid adding new copyright notices. I
don't personally really care.

If you want to have that kind of rule, I think we should then remove
all copyright notices (except maybe those that are in files that are
copied from external projects).

Thanks for the review, I'll push these. I'll still post one more patch
that moves the gsettings-data-convert call from paprefs to pulseaudio.


Well, in my patches you complained about the " Copyright
Lennard Poettering 2006" in the preamble of new files.
But I don't care, so leave it as it is.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] core: Expose API to elevate a thread to realtime priority

2018-04-21 Thread Georg Chini

On 21.04.2018 06:21, Arun Raghavan wrote:

This should make it easier for clients to elevate their audio threads to
real time priority without having to dig through much through specific
system internals.
---
  src/modules/alsa/alsa-sink.c |   3 +-
  src/modules/alsa/alsa-source.c   |   3 +-
  src/modules/bluetooth/module-bluez5-device.c |   2 +-
  src/modules/jack/module-jack-sink.c  |   4 +-
  src/modules/jack/module-jack-source.c|   4 +-
  src/modules/macosx/module-coreaudio-device.c |   2 +-
  src/modules/module-combine-sink.c|   3 +-
  src/modules/module-solaris.c |   2 +-
  src/modules/module-waveout.c |   2 +-
  src/modules/oss/module-oss.c |   2 +-
  src/pulse/util.c | 176 +++
  src/pulse/util.h |   3 +
  src/pulsecore/core-util.c| 171 +-
  src/pulsecore/core-util.h|   1 -
  src/tests/lo-test-util.c |   2 +-
  src/tests/rtstutter.c|   2 +-
  16 files changed, 198 insertions(+), 184 deletions(-)


diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index e28b6aa7c..32579739b 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -81,7 +81,6 @@ char *pa_strlcpy(char *b, const char *s, size_t l);
  
  char *pa_parent_dir(const char *fn);
  
-int pa_make_realtime(int rtprio);

  int pa_raise_priority(int nice_level);
  void pa_reset_priority(void);
  


Should pa_raise_priority() and pa_reset_priority() also be moved to util.c?
I think, if a client can make a thread real time it should also be able to
reset the priority to normal.
Otherwise LGTM.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH v3] bluez5-device: Fix memory leak in sco_process_render()

2018-04-26 Thread Georg Chini
sco_process_render does not unref the memblock when it encounters an error.

This patch fixes the issue. It also changes the return value to 1 in the case
of EAGAIN. Because the data was already rendered and cannot be re-sent, we
have to discard the block.

Because the modified EAGAIN handling prevents the log message about EAGAIN
after POLLOUT from being printed, the log message was moved to
a2dp/sco_process_render().
---
Changes in v2: Save errno before calling pa_memblock_unref()
Changes in v3: Move log message to a2dp/sco_process_render()

 src/modules/bluetooth/module-bluez5-device.c | 28 +++-
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index 95d288ef..a45eba3f 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -253,6 +253,7 @@ static void connect_ports(struct userdata *u, void 
*new_data, pa_direction_t dir
 static int sco_process_render(struct userdata *u) {
 ssize_t l;
 pa_memchunk memchunk;
+int saved_errno;
 
 pa_assert(u);
 pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
@@ -279,14 +280,22 @@ static int sco_process_render(struct userdata *u) {
 if (l > 0)
 break;
 
-if (errno == EINTR)
+saved_errno = errno;
+
+if (saved_errno == EINTR)
 /* Retry right away if we got interrupted */
 continue;
-else if (errno == EAGAIN)
-/* Hmm, apparently the socket was not writable, give up for now */
-return 0;
 
-pa_log_error("Failed to write data to SCO socket: %s", 
pa_cstrerror(errno));
+pa_memblock_unref(memchunk.memblock);
+
+if (saved_errno == EAGAIN) {
+/* Hmm, apparently the socket was not writable, give up for now.
+ * Because the data was already rendered, let's discard the block. 
*/
+pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there 
is a temporary connection loss.");
+return 1;
+}
+
+pa_log_error("Failed to write data to SCO socket: %s", 
pa_cstrerror(saved_errno));
 return -1;
 }
 
@@ -296,6 +305,8 @@ static int sco_process_render(struct userdata *u) {
 pa_log_error("Wrote memory block to socket only partially! %llu 
written, wanted to write %llu.",
 (unsigned long long) l,
 (unsigned long long) memchunk.length);
+
+pa_memblock_unref(memchunk.memblock);
 return -1;
 }
 
@@ -514,9 +525,11 @@ static int a2dp_process_render(struct userdata *u) {
 /* Retry right away if we got interrupted */
 continue;
 
-else if (errno == EAGAIN)
+else if (errno == EAGAIN) {
 /* Hmm, apparently the socket was not writable, give up for 
now */
+pa_log_debug("Got EAGAIN on write() after POLLOUT, probably 
there is a temporary connection loss.");
 break;
+}
 
 pa_log_error("Failed to write data to socket: %s", 
pa_cstrerror(errno));
 ret = -1;
@@ -1471,9 +1484,6 @@ static int write_block(struct userdata *u) {
 return -1;
 }
 
-if (n_written == 0)
-pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a 
temporary connection loss.");
-
 return n_written;
 }
 
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] gsettings: run gsettings-data-convert from gsettings-helper

2018-04-26 Thread Georg Chini

On 23.04.2018 15:16, Tanu Kaskinen wrote:

This was originally planned to be done by paprefs when it starts, but
since the schema is now fully controlled by pulseaudio, it makes sense
to run the conversion from pulseaudio instead.
---
  src/Makefile.am  |   6 +
  src/modules/gsettings/gsettings-helper.c |   6 +
  src/modules/gsettings/pulseaudio.convert | 160 +++
  3 files changed, 172 insertions(+)
  create mode 100644 src/modules/gsettings/pulseaudio.convert


LGTM
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-04-26 Thread Georg Chini

On 26.04.2018 15:13, Tanu Kaskinen wrote:

On Thu, 2018-03-29 at 15:33 +0200, Georg Chini wrote:

The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid lags
after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
Changes in v2:
  - fix issues pointed out by Tanu
  - set writable to false for HSP only if a block really needs to be written

  src/modules/bluetooth/module-bluez5-device.c | 289 ++-
  1 file changed, 191 insertions(+), 98 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index c3acc1dc..dac1eb2a 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -56,9 +56,8 @@ PA_MODULE_LOAD_ONCE(false);
  PA_MODULE_USAGE("path="
  "autodetect_mtu=");
  
-#define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)

  #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
-#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_PLAYBACK_SCO  (25 * PA_USEC_PER_MSEC)

Why is this changed? The commit message mentions nothing about this.


Ups, sorry, I forgot to mention. I changed it because it delivers much
better A/V sync for SCO. When testing the A2DP delay issue, I also
tested HSP. In fact, as HSP is a synchronous connection, the delay
for SCO playback/record should be smaller than for A2DP, but I
decided to use the same value for all four constants to keep it
simple.




  #define FIXED_LATENCY_RECORD_A2DP   (25 * PA_USEC_PER_MSEC)
  #define FIXED_LATENCY_RECORD_SCO(25 * PA_USEC_PER_MSEC)
  
@@ -660,6 +659,38 @@ static int a2dp_process_push(struct userdata *u) {

  return ret;
  }
  
+static void update_buffer_size(struct userdata *u) {

+int old_bufsize;
+socklen_t len = sizeof(int);
+int ret;
+
+ret = getsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &old_bufsize, &len);
+if (ret == -1) {
+pa_log_warn("Changing bluetooth buffer size: Failed to 
getsockopt(SO_SNDBUF): %s", pa_cstrerror(errno));
+} else {
+int new_bufsize;
+
+/* Set send buffer size as small as possible. The minimum value is 
1024 according to the
+ * socket man page. The data is written to the socket in chunks of 
write_block_size, so
+ * there should at least be room for two chunks in the buffer. 
Generally, write_block_size
+ * is larger than 512. If not, use the next multiple of 
write_block_size which is larger
+ * than 1024. */
+new_bufsize = 2 * u->write_block_size;
+if (new_bufsize < 1024)
+new_bufsize = (1024 / u->write_block_size + 1) * 
u->write_block_size;
+
+/* The kernel internally doubles the buffer size that was set by 
setsockopt and getsockopt
+ * returns the doubled value. */
+if (new_bufsize != old_bufsize / 2) {
+ret = setsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, 
&new_bufsize, len);
+if (ret == -1)
+pa_log_warn("Changing bluetooth buffer size: Failed to change from 
%d to %d: %s", old_bufsize / 2, new_bufsize, pa_cstrerror(errno));
+else
+pa_log_info("Changing bluetooth buffer size: Changed from %d to 
%d", old_bufsize / 2, new_bufsize);
+}
+}
+}
+
  /* Run from I/O thread */
  static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) {
  struct sbc_info *sbc_info;
@@ -694,6 +725,15 @@ static void a2dp_set_bitpool(struct userdata *u, uint8_t 
bitpool) {
  pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
  pa_sink_set_fixed_latency_within_thread(u->sink,
  FIXED_LATENCY_PLAYBACK_A2DP + pa_bytes_to_usec(u->write_block_size, 
&u->sample_spec));
+
+/* If there is still data in the memchunk, we have to discard it
+ * because the write_block_size may have changed. */
+if (u->write_memchunk.memblock) {
+pa_memblock_unref(u->write_memchunk.memblock);
+pa_memchunk_reset(&u->write_memchunk);
+}
+
+update_buffer_size(u);
  }
  
  /* Run from I/O thread */

@@ -852,8 +892,10 @@ static void setup_stream(struct userdata *u) {
  
  pa_log_debug("Stream properly set up, we're ready to roll!");
  
-if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)

+if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
  a2dp_set_bitpool(u, u->sbc_info.max_bitpool);
+update_buffer_size(u);
+}
  
  u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);

  pollfd = pa_rtpoll_item_get_pollfd(u->

Re: [pulseaudio-discuss] [PATCH v2] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-04-26 Thread Georg Chini

On 26.04.2018 17:38, Georg Chini wrote:

On 26.04.2018 15:13, Tanu Kaskinen wrote:

On Thu, 2018-03-29 at 15:33 +0200, Georg Chini wrote:

The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid 
lags

after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746
---
Changes in v2:
  - fix issues pointed out by Tanu
  - set writable to false for HSP only if a block really needs to be 
written


  src/modules/bluetooth/module-bluez5-device.c | 289 
++-

  1 file changed, 191 insertions(+), 98 deletions(-)

  +    blocks_to_write -= result;
+    if (blocks_to_write > 0)
+    writable = false;

This "blocks_to_write > 0" check is new. In the previous version
writable was set to false unconditionally, and that's how I believe it
should be done. I guess this is an optimization: we won't write
anything before we get POLLIN, so to you it seemed unnecessary to wake
up on POLLOUT. Getting the POLLOUT notification is indeed unnecessary
in itself, but we still need to set POLLOUT when polling, because
otherwise when we wake up due to POLLIN, pollfd->revents will not have
POLLOUT set even if the socket is writable. As a result we won't write
when we're supposed to.

Or actually... if we wake up on POLLIN, and POLLOUT isn't in revents
even if the socket is writable, we'll set POLLOUT on the subsequent
poll, and that will return immediately, so there's not much delay with
the write. So maybe the "blocks_to_write > 0" is fine after all. Some
kind of a comment would probably be good in any case. For example:

"writable controls whether we set POLLOUT when polling - we set it to
false to enable POLLOUT. If there are more blocks to write, we want to
be woken up immediately when the socket becomes writable. If there
aren't currently any more blocks to write, then we'll have to wait
until we've received more data, so in that case we only want to set
POLLIN. Note that when we are woken up the next time, POLLOUT won't be
set in revents even if the socket has meanwhile become writable, which
may seem bad, but in that case we'll set POLLOUT in the subsequent
poll, and the poll will return immediately, so our writes won't be
delayed."


Yes it is an optimization and it works fine and significantly reduces
CPU load on slow systems (which was the reason to implement it).
I will add your comment to clarify.

Thinking again, you will end up in the situation you describe anyway:
If writable is set to false and POLLOUT is set, the rtpoll run will
immediately return. Then writable will be true, but since we have no
block to write, it will not be set to false. So again you disable POLLOUT,
just one iteration later (unless POLLIN was also set).
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH v4] bluez5-device: Rewrite of thread function, reduce send buffer size for a2dp sink

2018-05-04 Thread Georg Chini
The rewrite of the thread function does not change functionality much,
most of it is only cleanup, minor bug fixing  and documentation work.

This patch also changes the send buffer size for a2dp sink to avoid lags
after temporary connection drops, following the proof-of-concept patch
posted by Dmitry Kalyanov.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=58746

Additionally the patch changes the fixed latency for HSP playback from 125
to 25 ms. Tests showed that this produces better audio sync, which is
expected as HSP should have smaller latency than A2DP.
---

Changes in v3:
 - fix issues pointed out by Tanu
 - set writable to false for HSP only if a block really needs to be written

Changes in v4:
 - Add comment suggested by Tanu
 - Change commit message

 src/modules/bluetooth/module-bluez5-device.c | 300 ++-
 1 file changed, 202 insertions(+), 98 deletions(-)

diff --git a/src/modules/bluetooth/module-bluez5-device.c 
b/src/modules/bluetooth/module-bluez5-device.c
index c3acc1dc..edfa12fa 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -56,9 +56,8 @@ PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE("path="
 "autodetect_mtu=");
 
-#define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
-#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_PLAYBACK_SCO  (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_RECORD_A2DP   (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_RECORD_SCO(25 * PA_USEC_PER_MSEC)
 
@@ -660,6 +659,38 @@ static int a2dp_process_push(struct userdata *u) {
 return ret;
 }
 
+static void update_buffer_size(struct userdata *u) {
+int old_bufsize;
+socklen_t len = sizeof(int);
+int ret;
+
+ret = getsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &old_bufsize, &len);
+if (ret == -1) {
+pa_log_warn("Changing bluetooth buffer size: Failed to 
getsockopt(SO_SNDBUF): %s", pa_cstrerror(errno));
+} else {
+int new_bufsize;
+
+/* Set send buffer size as small as possible. The minimum value is 
1024 according to the
+ * socket man page. The data is written to the socket in chunks of 
write_block_size, so
+ * there should at least be room for two chunks in the buffer. 
Generally, write_block_size
+ * is larger than 512. If not, use the next multiple of 
write_block_size which is larger
+ * than 1024. */
+new_bufsize = 2 * u->write_block_size;
+if (new_bufsize < 1024)
+new_bufsize = (1024 / u->write_block_size + 1) * 
u->write_block_size;
+
+/* The kernel internally doubles the buffer size that was set by 
setsockopt and getsockopt
+ * returns the doubled value. */
+if (new_bufsize != old_bufsize / 2) {
+ret = setsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, 
&new_bufsize, len);
+if (ret == -1)
+pa_log_warn("Changing bluetooth buffer size: Failed to change 
from %d to %d: %s", old_bufsize / 2, new_bufsize, pa_cstrerror(errno));
+else
+pa_log_info("Changing bluetooth buffer size: Changed from %d 
to %d", old_bufsize / 2, new_bufsize);
+}
+}
+}
+
 /* Run from I/O thread */
 static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) {
 struct sbc_info *sbc_info;
@@ -694,6 +725,15 @@ static void a2dp_set_bitpool(struct userdata *u, uint8_t 
bitpool) {
 pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
 pa_sink_set_fixed_latency_within_thread(u->sink,
 FIXED_LATENCY_PLAYBACK_A2DP + 
pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+
+/* If there is still data in the memchunk, we have to discard it
+ * because the write_block_size may have changed. */
+if (u->write_memchunk.memblock) {
+pa_memblock_unref(u->write_memchunk.memblock);
+pa_memchunk_reset(&u->write_memchunk);
+}
+
+update_buffer_size(u);
 }
 
 /* Run from I/O thread */
@@ -852,8 +892,10 @@ static void setup_stream(struct userdata *u) {
 
 pa_log_debug("Stream properly set up, we're ready to roll!");
 
-if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
 a2dp_set_bitpool(u, u->sbc_info.max_bitpool);
+update_buffer_size(u);
+}
 
 u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
@@ -1068,12 +1110,12 @@ static int sink_process_msg(pa_msgobject *o, int code, 
void *data, int64_t offse
 switch (code) {
 
 case PA_SINK_MESSAGE_GET_LATENCY: {
-int64_t wi, ri;
+int64_t wi = 0, ri = 0;
 
 if (u->read_smoother) {
 ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 wi = pa_bytes_to_usec(u->write_index +

[pulseaudio-discuss] [PATCH v2] alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()

2018-05-04 Thread Georg Chini
The current code does not call snd_pcm_status_set_audio_htstamp_config()
to configure the way timestamps are updated in ALSA. In kernel 4.14 and
above a bug in ALSA has been fixed which changes timmestamp behavior.
This leads to inconsistencies in the delay reporting because the time
stamp no longer reflects the time when the delay was updated if the
ALSA report_delay flag is not set. Therefore latencies are not calculated
correctly.

This patch uses snd_pcm_status_set_audio_htstamp_config() to set the
ALSA report_delay flag to 1 before the call to snd_pcm_status(). With
this, time stamps are updated as expected.
---

Changes in v2:
 - updated commit message

 src/modules/alsa/alsa-util.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 61fb4903..b91a0e98 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,6 +1187,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
 size_t abs_k;
 int err;
 snd_pcm_sframes_t avail = 0;
+snd_pcm_audio_tstamp_config_t tstamp_config;
 
 pa_assert(pcm);
 pa_assert(delay);
@@ -1200,6 +1201,12 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
  * avail, delay and timestamp values in a single kernel call to improve
  * timer-based scheduling */
 
+/* The time stamp configuration needs to be set so that the
+ * ALSA code will use the internal delay reported by the driver */
+tstamp_config.type_requested = 1; /* ALSA default time stamp type */
+tstamp_config.report_delay = 1;
+snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+
 if ((err = snd_pcm_status(pcm, status)) < 0)
 return err;
 
-- 
2.16.2

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v3] bluez5-device: Fix memory leak in sco_process_render()

2018-05-08 Thread Georg Chini

On 07.05.2018 16:28, Tanu Kaskinen wrote:

On Thu, 2018-04-26 at 13:24 +0200, Georg Chini wrote:

sco_process_render does not unref the memblock when it encounters an error.

This patch fixes the issue. It also changes the return value to 1 in the case
of EAGAIN. Because the data was already rendered and cannot be re-sent, we
have to discard the block.

Because the modified EAGAIN handling prevents the log message about EAGAIN
after POLLOUT from being printed, the log message was moved to
a2dp/sco_process_render().
---
Changes in v2: Save errno before calling pa_memblock_unref()
Changes in v3: Move log message to a2dp/sco_process_render()

  src/modules/bluetooth/module-bluez5-device.c | 28 +++-
  1 file changed, 19 insertions(+), 9 deletions(-)

Looks good to me.


Thanks for the reviews. I pushed both patches now.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH v2] alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()

2018-05-13 Thread Georg Chini

On 11.05.2018 10:09, Tanu Kaskinen wrote:

On Fri, 2018-05-04 at 21:10 +0200, Georg Chini wrote:

The current code does not call snd_pcm_status_set_audio_htstamp_config()
to configure the way timestamps are updated in ALSA. In kernel 4.14 and
above a bug in ALSA has been fixed which changes timmestamp behavior.
This leads to inconsistencies in the delay reporting because the time
stamp no longer reflects the time when the delay was updated if the
ALSA report_delay flag is not set. Therefore latencies are not calculated
correctly.

This patch uses snd_pcm_status_set_audio_htstamp_config() to set the
ALSA report_delay flag to 1 before the call to snd_pcm_status(). With
this, time stamps are updated as expected.
---

Changes in v2:
  - updated commit message

  src/modules/alsa/alsa-util.c | 7 +++
  1 file changed, 7 insertions(+)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 61fb4903..b91a0e98 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,6 +1187,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
  size_t abs_k;
  int err;
  snd_pcm_sframes_t avail = 0;
+snd_pcm_audio_tstamp_config_t tstamp_config;
  
  pa_assert(pcm);

  pa_assert(delay);
@@ -1200,6 +1201,12 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
   * avail, delay and timestamp values in a single kernel call to improve
   * timer-based scheduling */
  
+/* The time stamp configuration needs to be set so that the

+ * ALSA code will use the internal delay reported by the driver */
+tstamp_config.type_requested = 1; /* ALSA default time stamp type */
+tstamp_config.report_delay = 1;
+snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+
  if ((err = snd_pcm_status(pcm, status)) < 0)
  return err;
  

Looks good to me.

This patch breaks the build on older ALSA versions. Should we change the 
dependency
to 1.1.0 which introduces snd_pcm_status_set_audio_htstamp_config() or 
make the

usage of the function dependent on ALSA version?

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


[pulseaudio-discuss] [PATCH] alsa-util: Use time stamp config only for alsa versions >= 1.1.0

2018-05-14 Thread Georg Chini
The commit "alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()"
broke the build on ALSA versions below 1.1.0 because the time stamp
configuration function was introduced in 1.1.0.

This patch makes the usage of snd_pcm_status_set_audio_htstamp_config()
dependent on ALSA version.
---
 src/modules/alsa/alsa-util.c | 8 +++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index b91a0e98..41134ea5 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,7 +1187,9 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
 size_t abs_k;
 int err;
 snd_pcm_sframes_t avail = 0;
+#if (SND_LIB_VERSION >= ((1<<16)|(1<<8)|0)) /* API additions in 1.1.0 */
 snd_pcm_audio_tstamp_config_t tstamp_config;
+#endif
 
 pa_assert(pcm);
 pa_assert(delay);
@@ -1201,11 +1203,15 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t 
*status, snd_pcm_sframes
  * avail, delay and timestamp values in a single kernel call to improve
  * timer-based scheduling */
 
+#if (SND_LIB_VERSION >= ((1<<16)|(1<<8)|0)) /* API additions in 1.1.0 */
+
 /* The time stamp configuration needs to be set so that the
- * ALSA code will use the internal delay reported by the driver */
+ * ALSA code will use the internal delay reported by the driver.
+ * The time stamp configuration was introduced in alsa version 1.1.0. */
 tstamp_config.type_requested = 1; /* ALSA default time stamp type */
 tstamp_config.report_delay = 1;
 snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+#endif
 
 if ((err = snd_pcm_status(pcm, status)) < 0)
 return err;
-- 
2.14.1

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] alsa-util: Use time stamp config only for alsa versions >= 1.1.0

2018-05-15 Thread Georg Chini

On 15.05.2018 15:12, Arun Raghavan wrote:

On Tue, 15 May 2018, at 11:30 AM, Georg Chini wrote:

The commit "alsa-util: Set ALSA report_delay flag in pa_alsa_safe_delay()"
broke the build on ALSA versions below 1.1.0 because the time stamp
configuration function was introduced in 1.1.0.

This patch makes the usage of snd_pcm_status_set_audio_htstamp_config()
dependent on ALSA version.
---
  src/modules/alsa/alsa-util.c | 8 +++-
  1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index b91a0e98..41134ea5 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,7 +1187,9 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm,
snd_pcm_status_t *status, snd_pcm_sframes
  size_t abs_k;
  int err;
  snd_pcm_sframes_t avail = 0;
+#if (SND_LIB_VERSION >= ((1<<16)|(1<<8)|0)) /* API additions in 1.1.0
*/
  snd_pcm_audio_tstamp_config_t tstamp_config;
+#endif
  
  pa_assert(pcm);

  pa_assert(delay);
@@ -1201,11 +1203,15 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm,
snd_pcm_status_t *status, snd_pcm_sframes
   * avail, delay and timestamp values in a single kernel call to
improve
   * timer-based scheduling */
  
+#if (SND_LIB_VERSION >= ((1<<16)|(1<<8)|0)) /* API additions in 1.1.0 */

+
  /* The time stamp configuration needs to be set so that the
- * ALSA code will use the internal delay reported by the driver */
+ * ALSA code will use the internal delay reported by the driver.
+ * The time stamp configuration was introduced in alsa version 1.1.0. */
  tstamp_config.type_requested = 1; /* ALSA default time stamp type */
  tstamp_config.report_delay = 1;
  snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+#endif
  
  if ((err = snd_pcm_status(pcm, status)) < 0)

  return err;
--

Looks good.

-- Arun


Thanks, pushed.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] alsa-util: Use time stamp config only for alsa versions >= 1.1.0

2018-05-16 Thread Georg Chini

On 15.05.2018 21:12, Pierre-Louis Bossart wrote:

On 5/15/18 8:12 AM, Arun Raghavan wrote:

On Tue, 15 May 2018, at 11:30 AM, Georg Chini wrote:
The commit "alsa-util: Set ALSA report_delay flag in 
pa_alsa_safe_delay()"

broke the build on ALSA versions below 1.1.0 because the time stamp
configuration function was introduced in 1.1.0.

This patch makes the usage of snd_pcm_status_set_audio_htstamp_config()
dependent on ALSA version.
---
  src/modules/alsa/alsa-util.c | 8 +++-
  1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/modules/alsa/alsa-util.c 
b/src/modules/alsa/alsa-util.c

index b91a0e98..41134ea5 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1187,7 +1187,9 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm,
snd_pcm_status_t *status, snd_pcm_sframes
  size_t abs_k;
  int err;
  snd_pcm_sframes_t avail = 0;
+#if (SND_LIB_VERSION >= ((1<<16)|(1<<8)|0)) /* API additions in 1.1.0
*/
  snd_pcm_audio_tstamp_config_t tstamp_config;
+#endif
    pa_assert(pcm);
  pa_assert(delay);
@@ -1201,11 +1203,15 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm,
snd_pcm_status_t *status, snd_pcm_sframes
   * avail, delay and timestamp values in a single kernel call to
improve
   * timer-based scheduling */
  +#if (SND_LIB_VERSION >= ((1<<16)|(1<<8)|0)) /* API additions in 
1.1.0 */

+
  /* The time stamp configuration needs to be set so that the
- * ALSA code will use the internal delay reported by the driver */
+ * ALSA code will use the internal delay reported by the driver.
+ * The time stamp configuration was introduced in alsa version 
1.1.0. */
  tstamp_config.type_requested = 1; /* ALSA default time stamp 
type */

  tstamp_config.report_delay = 1;
  snd_pcm_status_set_audio_htstamp_config(status, &tstamp_config);
+#endif
    if ((err = snd_pcm_status(pcm, status)) < 0)
  return err;
--


Looks good.


Takashi and I mentioned it's not quite right but what can I say...
I don't understand - Takashi explicitly stated that the flag needs to be 
set.

So what do you think is not quite right and how should it be?
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] Bluetooth - HSP / HFP - source based timing & hard-coded MTU causes SCO packets to be badly delayed

2018-05-20 Thread Georg Chini

On 19.05.2018 22:34, Attila Tőkés wrote:

Hi,

Apparently, the source based timing logic (when using the 
headset_head_unit profile there is a sink and a source) from:

https://github.com/pulseaudio/pulseaudio/blob/master/src/modules/bluetooth/module-bluez5-device.c#L1581

combined with the hard-coded MTU of 48 bytes / packet can cause SCO 
packets to be badly delayed with some devices.



For example, the following happens with the CYW43438 chip used in the 
Raspberry Pi 3:


PulseAudio (hard-coded) MTU - 48 bytes / packet

Real MTU - 64 bytes / packet

Packet Size negotiated at the eSCO connect - 60 bytes / packet

The CYW43438 uses the negotiated packet size of 60 bytes / packet, so 
it sends bigger, 60 bytes SCO packets less often.


Based on the above logic from module-bluez5-device.c, PulseAudio sends 
SCO packets (one at a time) only when SCO packet are received from 
CYW43438. Because of the packet size of 60 bytes, these packets are 
sent less often than PulseAudio would need to send its 48 byte packets.


The result is that the playback (paplay) is way to slow: 12.16 
seconds, instead of 9.72 seconds (+25%).


HCI (+SCO) log:
https://www.dropbox.com/s/yy7a1sltmzbsakk/bt-log-bcm.txt?dl=0

---

Using autodetect_mtu=yes resolves the delay, but the problem does not 
seems to be the MTU.


What problem remains after setting autodetect_mtu=yes?



---

The issue, I think is the same as in:

[pulseaudio-discuss] [PATCH] Fix: Distorted audio during Bluetooth SCO 
HFP/HSP playback


https://lists.freedesktop.org/archives/pulseaudio-discuss/2017-January/027448.html

---


The link you reference describes exactly the MTU issue. We had to revert 
setting

autodetect_mtu=yes by default because there seem to be more head sets where
it does not work with autodetect_mtu = yes than those which require it.



I tried to change module-bluez5-device.c to always use system clock 
based timing (as for A2DP) and this seems to fix the problem.


What is the reason to use this source based timing, instead of using 
system clock based timing as for A2DP ? It's just optimization? Or it 
is some requirement of the Bluetooth spec? Using the system clock 
would be more reliable, I think.cuss


The SCO protocol requires that for each packet sent by the head set one 
has to be sent
back from the system to the head set. So the timing is controlled by the 
head set and
it is not guaranteed that this timing matches the system clock. 
Therefore you cannot
use system clock and you would probably see a delay between recording 
and playback
after some time. A2DP playback is different because the head set has no 
active part

in the timing of events.

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] alsa-mixer: make the mono mapping a fallback only

2018-06-07 Thread Georg Chini

On 01.06.2018 10:24, Tanu Kaskinen wrote:

If a sound card doesn't have the "front" device defined for it, we have
to use the "hw" device for stereo. Not so long ago, the analog-stereo
mapping had "hw:%f" in its device-strings and everything worked great,
except that it caused trouble with the Intel HDMI LPE driver that uses
the first "hw" device for HDMI, and we were incorrectly detecting it as
an analog device. That problem was fixed in commit ea3ebd09, which
removed "hw:%f" from analog-stereo and added a new stereo fallback
mapping for "hw".

Now the problem is that if a sound card doesn't have the "front" device
defined for it, and it supports both mono and stereo, only the mono
mapping is used, because the stereo mapping is only a fallback. This
patch makes the mono mapping a fallback too, so the mono mapping is used
only if there's absolutely nothing else that works.

This can cause trouble at least in theory. Maybe someone actually wants
to use mono output on a card that supports both mono and stereo. But
that seems quite unlikely.
---
  src/modules/alsa/alsa-mixer.c  |  1 +
  .../alsa/mixer/profile-sets/default.conf   | 18 ++
  2 files changed, 11 insertions(+), 8 deletions(-)


Looks good to me. I don't mind having it in 12.0, but let Arun have
the last word.

BTW, when are we going to release 12.0? I have not heard any
complaints yet.
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] PulseAudio Crash GDB log

2018-06-12 Thread Georg Chini

On 12.06.2018 19:31, Sathish Narasimman wrote:

Hi,

May I please, know what is wrong in the log

Program received signal SIGSEGV, Segmentation fault.
0x71469664 in loopback_process_msg_cb (o=0x87428, code=1, userdata=0x0,
    offset=0, chunk=0x0) at modules/module-loopback.c:1161
1161                current_latency = 
pa_sink_get_requested_latency(u->sink_input->sink);

(gdb) bt
#0  0x71469664 in loopback_process_msg_cb (o=0x87428, code=1, 
userdata=0x0,

    offset=0, chunk=0x0) at modules/module-loopback.c:1161
#1  0x76f0f4cc in pa_asyncmsgq_dispatch (object=0x87428, code=1, 
userdata=0x0,

    offset=0, memchunk=0x7efff2ac) at pulsecore/asyncmsgq.c:323
#2  0x76f93478 in asyncmsgq_read_cb (api=0x373f4, e=0xf4ea0, fd=14,
    events=PA_IO_EVENT_INPUT, userdata=0xf43f8) at 
pulsecore/thread-mq.c:69

#3  0x76e0d8ec in dispatch_pollfds (m=0x373b0) at pulse/mainloop.c:655
#4  0x76e0eb4c in pa_mainloop_dispatch (m=0x373b0) at pulse/mainloop.c:898
#5  0x76e0ed84 in pa_mainloop_iterate (m=0x373b0, block=1, 
retval=0x7efff4fc)

    at pulse/mainloop.c:929


Under which conditions does that happen? Which version of PA are you using?
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] PulseAudio Crash GDB log

2018-06-15 Thread Georg Chini

On 13.06.2018 05:26, Sathish Narasimman wrote:

Hi,

I am trying to establish SCO connection between 2 latops.

Handsfree profile
Laptop 1 - Hanndsfree AG
Laptop 2 Handsfree only


How do you try to do that? I do not really understand your setup.
Do you have pulseaudio on both ends? Can you provide PA logs?



In Lap 1 I am getting this error when trying to pair and connect.
During connection time the PA crashes.

NOTE: using ofono backend

Thanks,
Sathish N

On Tue, Jun 12, 2018 at 11:55 PM, Georg Chini <mailto:ge...@chini.tk>> wrote:


On 12.06.2018 19:31, Sathish Narasimman wrote:

Hi,

May I please, know what is wrong in the log

Program received signal SIGSEGV, Segmentation fault.
0x71469664 in loopback_process_msg_cb (o=0x87428, code=1,
userdata=0x0,
    offset=0, chunk=0x0) at modules/module-loopback.c:1161
1161                current_latency =
pa_sink_get_requested_latency(u->sink_input->sink);
(gdb) bt
#0  0x71469664 in loopback_process_msg_cb (o=0x87428, code=1,
userdata=0x0,
    offset=0, chunk=0x0) at modules/module-loopback.c:1161
#1  0x76f0f4cc in pa_asyncmsgq_dispatch (object=0x87428,
code=1, userdata=0x0,
    offset=0, memchunk=0x7efff2ac) at pulsecore/asyncmsgq.c:323
#2  0x76f93478 in asyncmsgq_read_cb (api=0x373f4, e=0xf4ea0,
fd=14,
    events=PA_IO_EVENT_INPUT, userdata=0xf43f8) at
pulsecore/thread-mq.c:69
#3  0x76e0d8ec in dispatch_pollfds (m=0x373b0) at
pulse/mainloop.c:655
#4  0x76e0eb4c in pa_mainloop_dispatch (m=0x373b0) at
pulse/mainloop.c:898
#5  0x76e0ed84 in pa_mainloop_iterate (m=0x373b0, block=1,
retval=0x7efff4fc)
    at pulse/mainloop.c:929

Under which conditions does that happen? Which version of PA are
you using?




___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [ANNOUNCE] PulseAudio 12.0

2018-06-27 Thread Georg Chini

On 27.06.2018 09:26, Tanu Kaskinen wrote:

On Thu, 2018-06-21 at 12:52 -0400, Felipe Sateler wrote:

On Wed, Jun 20, 2018 at 5:25 PM Tanu Kaskinen  wrote:


May I have your attention, please?

PulseAudio 12.0 has entered our world, and it claims to be superior to
its predecessors at least in the following ways:


\o/

I have uploaded to debian unstable, and activated the gsettings module. Can
we get a new paprefs release too? Or should I just package a git snapshot?

A new paprefs release should come "soon". (It might come even sooner if
the release is not left for me to do - Arun or Georg, feel free to do
the release if you have time.)

Sorry, I am currently very busy and do not have much time for pulseaudio 
work.


___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] switch-on-port-available: ignore bluetooth cards

2018-06-30 Thread Georg Chini

On 29.06.2018 13:33, Tanu Kaskinen wrote:

When the user manually switches the profile of a bluetooth headset from
"off" to "a2dp_sink", the port availability changes from "unknown" to
"yes", which triggered a recursive profile change in
module-switch-on-port-available. Such recursivity isn't (and possibly
can't) be handled well (that is, PulseAudio crashed), so let's avoid
doing bluetooth profile changes from module-switch-on-port-available
(they're useless anyway).

Bug link: https://bugs.freedesktop.org/show_bug.cgi?id=107044
---
  src/modules/module-switch-on-port-available.c | 8 
  1 file changed, 8 insertions(+)

diff --git a/src/modules/module-switch-on-port-available.c 
b/src/modules/module-switch-on-port-available.c
index 8fd3c9e5f..321db361f 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -281,6 +281,14 @@ static pa_hook_result_t 
port_available_hook_callback(pa_core *c, pa_device_port
  return PA_HOOK_OK;
  }
  
+/* Our profile switching logic caused trouble with bluetooth headsets (see

+ * https://bugs.freedesktop.org/show_bug.cgi?id=107044) and
+ * module-bluetooth-policy takes care of automatic profile switching
+ * anyway, so we ignore bluetooth cards in
+ * module-switch-on-port-available. */
+if (pa_safe_streq(pa_proplist_gets(port->card->proplist, PA_PROP_DEVICE_BUS), 
"bluetooth"))
+return PA_HOOK_OK;
+
  switch (port->available) {
  case PA_AVAILABLE_YES:
  switch_to_port(port);


LGTM

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH 0/3] Various state related cleanups

2018-06-30 Thread Georg Chini

On 26.06.2018 15:25, Tanu Kaskinen wrote:

The first patch is a reaction to a bug report[1] that pointed out a
weird thing in the sink input drained state handling. I came to the
conclusion that we can just remove the entire drained state.

After removing the drained state, pa_sink_input_get_state() didn't have
anything to do any more, except to just return the state variable.
pa_source_output_get_state() didn't do anything either. The second patch
removes those functions.

Then I noticed that pa_sink_get_state() and pa_source_get_state() don't
do anything either, so the third patch removes those as well.

[1] https://bugs.freedesktop.org/show_bug.cgi?id=106982


All three look good.
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] Bluetooth connection disconnects every few minutes

2018-07-02 Thread Georg Chini

On 02.07.2018 17:58, Julian Sikorski wrote:

W dniu 29.06.2018 o 21:47, Julian Sikorski pisze:

Hi list,

I have noticed that the bluetooth connection between my laptop (Intel
7260) and my headphones (Sennheiser Momentum Wirelless) is very
unreliable. While under Windows 10 devices stay connected for hours on
end, under Fedora 28 the connection is lost every few minutes at most.
Often the connection will be reestablished only to be lost again.
bluetoothd shows messages like:

cze 29 21:31:13 snowball2 bluetoothd[3832]: Endpoint unregistered:
sender=:1.145 path=/MediaEndpoint/A2DPSource
cze 29 21:31:13 snowball2 bluetoothd[3832]: Endpoint unregistered:
sender=:1.145 path=/MediaEndpoint/A2DPSink
cze 29 21:31:13 snowball2 bluetoothd[3832]: Endpoint registered:
sender=:1.145 path=/MediaEndpoint/A2DPSource
cze 29 21:31:13 snowball2 bluetoothd[3832]: Endpoint registered:
sender=:1.145 path=/MediaEndpoint/A2DPSink
cze 29 21:31:39 snowball2 bluetoothd[3832]:
/org/bluez/hci0/dev_00_1B_66_81_8D_76/fd5: fd(25) ready
cze 29 21:33:02 snowball2 bluetoothd[3832]: Unable to get io data for
Headset Voice gateway: getpeername: Transport endpoint is not connected
(107)
cze 29 21:33:02 snowball2 bluetoothd[3832]: Endpoint unregistered:
sender=:1.145 path=/MediaEndpoint/A2DPSource
cze 29 21:33:02 snowball2 bluetoothd[3832]: Endpoint unregistered:
sender=:1.145 path=/MediaEndpoint/A2DPSink
cze 29 21:33:03 snowball2 bluetoothd[3832]: Endpoint registered:
sender=:1.145 path=/MediaEndpoint/A2DPSource
cze 29 21:33:03 snowball2 bluetoothd[3832]: Endpoint registered:
sender=:1.145 path=/MediaEndpoint/A2DPSink
cze 29 21:33:31 snowball2 bluetoothd[3832]:
/org/bluez/hci0/dev_00_1B_66_81_8D_76/fd6: fd(25) ready
cze 29 21:35:38 snowball2 bluetoothd[3832]: Unable to get io data for
Headset Voice gateway: getpeername: Transport endpoint is not connected
(107)

dmesg for the same period looks as follows:

2018-06-29T21:30:58,743292+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:31:12,613199+02:00 usb 2-1.4: USB disconnect, device number 8
2018-06-29T21:31:12,809196+02:00 usb 2-1.4: new full-speed USB device
number 9 using ehci-pci
2018-06-29T21:31:12,889200+02:00 usb 2-1.4: New USB device found,
idVendor=8087, idProduct=07dc, bcdDevice= 0.01
2018-06-29T21:31:12,889203+02:00 usb 2-1.4: New USB device strings:
Mfr=0, Product=0, SerialNumber=0
2018-06-29T21:31:12,903435+02:00 Bluetooth: hci0: read Intel version:
3707100180012d0d2a
2018-06-29T21:31:12,903438+02:00 Bluetooth: hci0: Intel device is
already patched. patch num: 2a
2018-06-29T21:31:13,002369+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:31:28,853543+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:31:39,625447+02:00 input: 00:1B:66:81:8D:76 as
/devices/virtual/input/input31
2018-06-29T21:31:44,742251+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:32:00,746866+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:32:16,747075+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:32:32,746099+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:32:48,747212+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:33:02,403071+02:00 usb 2-1.4: USB disconnect, device number 9
2018-06-29T21:33:02,595275+02:00 usb 2-1.4: new full-speed USB device
number 10 using ehci-pci
2018-06-29T21:33:02,675493+02:00 usb 2-1.4: New USB device found,
idVendor=8087, idProduct=07dc, bcdDevice= 0.01
2018-06-29T21:33:02,675502+02:00 usb 2-1.4: New USB device strings:
Mfr=0, Product=0, SerialNumber=0
2018-06-29T21:33:02,691213+02:00 Bluetooth: hci0: read Intel version:
3707100180012d0d2a
2018-06-29T21:33:02,691216+02:00 Bluetooth: hci0: Intel device is
already patched. patch num: 2a
2018-06-29T21:33:02,791228+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:33:18,854324+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:33:31,481782+02:00 input: 00:1B:66:81:8D:76 as
/devices/virtual/input/input32
2018-06-29T21:33:34,742175+02:00 Bluetooth: hci0: last event is not cmd
complete (0x0f)
2018-06-29T21:35:37,864703+02:00 usb 2-1.4: USB disconnect, device number 10
2018-06-29T21:35:38,056428+02:00 usb 2-1.4: new full-speed USB device
number 11 using ehci-pci
2018-06-29T21:35:38,140706+02:00 usb 2-1.4: New USB device found,
idVendor=8087, idProduct=07dc, bcdDevice= 0.01
2018-06-29T21:35:38,140712+02:00 usb 2-1.4: New USB device strings:
Mfr=0, Product=0, SerialNumber=0
2018-06-29T21:35:38,155840+02:00 Bluetooth: hci0: read Intel version:
3707100180012d0d2a
2018-06-29T21:35:38,155843+02:00 Bluetooth: hci0: Intel device is
already patched. patch num: 2a

I am not sure where to look further. Does it look like an issue with
pulseaudio, or a deeper problem with linux bluetooth stack? Thanks for
your input in advance!

Best regards,
Julian

This is what is logged by pulseaudio at the

Re: [pulseaudio-discuss] [PATCH] ladspa-sink: fix search path

2018-07-07 Thread Georg Chini

On 04.07.2018 13:25, Tanu Kaskinen wrote:

Having a single level macro for stringizing LADSPA_PATH doesn't work,
because the '#' preprocessor operator doesn't expand any macros in its
parameter. As a result, we used the string "LADSPA_PATH" as the search
path, and obviously no plugins were ever found.

This adds a two-level macro in macro.h and uses that to expand and
stringize LADSPA_PATH.

Bug link: https://bugs.freedesktop.org/show_bug.cgi?id=107078
---
  src/modules/module-ladspa-sink.c | 14 ++
  src/pulsecore/macro.h|  6 ++
  2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index c654530a3..0bde922bf 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -1054,15 +1054,13 @@ int pa__init(pa_module*m) {
  u->output = NULL;
  u->ss = ss;
  
-/* If the LADSPA_PATH environment variable is not set, we use the

- * LADSPA_PATH preprocessor macro instead. The macro can contain characters
- * that need to be escaped (especially on Windows backslashes are common).
- * The "#" preprocessor operator helpfully adds the required escaping while
- * turning the LADSPA_PATH macro into a string. */
-#define QUOTE_MACRO(x) #x
  if (!(e = getenv("LADSPA_PATH")))
-e = QUOTE_MACRO(LADSPA_PATH);
-#undef QUOTE_MACRO
+/* The LADSPA_PATH preprocessor macro isn't a string literal (i.e. it
+ * doesn't contain quotes), because otherwise the build system would
+ * have an extra burden of getting the escaping right (Windows paths
+ * are especially tricky). PA_EXPAND_AND_STRINGIZE does the necessary
+ * escaping. */
+e = PA_EXPAND_AND_STRINGIZE(LADSPA_PATH);
  
  /* FIXME: This is not exactly thread safe */

  t = pa_xstrdup(lt_dlgetsearchpath());
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index dbce5cd06..bb15b7f01 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -298,6 +298,12 @@ static inline size_t PA_ALIGN(size_t l) {
   ? (-1 - PA_INT_TYPE_MAX(type))\
   : (type) 0))
  
+/* The '#' preprocessor operator doesn't expand any macros that are in the

+ * parameter, which is why we need a separate macro for those cases where the
+ * parameter contains a macro that needs expanding. */
+#define PA_STRINGIZE(x) #x
+#define PA_EXPAND_AND_STRINGIZE(x) PA_STRINGIZE(x)
+
  /* We include this at the very last place */
  #include 
  


LGTM

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] [PATCH] pipe-sink, pipe-source: fix file permissions

2018-07-07 Thread Georg Chini

On 04.07.2018 12:40, Tanu Kaskinen wrote:

We recently changed the umask of the daemon from 022 to 077, which broke
module-pipe-sink in the system mode, because nobody was allowed to read
from the pipe.

module-pipe-source in the system mode was probably always broken,
because the old umask of 022 should prevent anyone from writing to the
pipe.

This patch uses chmod() after the file creation to set the permissions
to 0666, which is what the fkfifo() call tried to set.

Bug link: https://bugs.freedesktop.org/show_bug.cgi?id=107070
---

Should the permissions really be 666? Would not 660 be better,
so that there is at least some control who may access the pipe?
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] bluetooth: HFP | WBS | pollfd->revent POLLOUT not hit

2018-07-07 Thread Georg Chini

On 06.07.2018 08:43, Sathish Narasimman wrote:

Hi Luiz,

Yes, I am doing the polling. I an not changing that part of the code. 
In which it is default in which NBS also uses.


Thanks,
Sathish N


The current code does not poll for POLLOUT in all situations,
see comment in the code. You should check if POLLOUT is
really set in pollfd->events. Actually a situation like you
describe is "normal" but should not impact sound quality,
again see comment in the code. You could try if it helps to
remove the "if (blocks_to_write > 0)" and set writable=false
unconditionally.



On Wed, Jul 4, 2018 at 9:29 PM, Luiz Augusto von Dentz 
mailto:luiz.de...@gmail.com>> wrote:


Hi Sathish,

On Mon, Jul 2, 2018 at 4:25 PM, Sathish Narasimman
mailto:nsathis...@gmail.com>> wrote:
> Hi,
>
> I am trying to make WBS with PulseAudio. I am almost successful in
> establishing the connection and making a WBS traffic.
>
> But out of 10 attempts or sometimes the TX packets are not sent
due to
> POLLOUT is not reflected in the pollfd
>                 /* Test if the stream is writable */
>                 if (pollfd->revents & POLLOUT)
>                     writable = true;

Are you sure you are polling with POLLOUT?

> I would like to know what causes the stream as not writable.
> What are the connecting modules to resolve this?
> This happening only with WBS as NBS works good without any problem.
>
>
> Thanks,
> Sathish N
>
> ___
> pulseaudio-discuss mailing list
> pulseaudio-discuss@lists.freedesktop.org

>
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

>



-- 
Luiz Augusto von Dentz

___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org

https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss





___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss



___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] bluetooth: HFP | WBS | pollfd->revent POLLOUT not hit

2018-07-07 Thread Georg Chini

On 07.07.2018 12:07, Georg Chini wrote:

On 06.07.2018 08:43, Sathish Narasimman wrote:

Hi Luiz,

Yes, I am doing the polling. I an not changing that part of the code. 
In which it is default in which NBS also uses.


Thanks,
Sathish N


The current code does not poll for POLLOUT in all situations,
see comment in the code. You should check if POLLOUT is
really set in pollfd->events. Actually a situation like you
describe is "normal" but should not impact sound quality,
again see comment in the code. You could try if it helps to
remove the "if (blocks_to_write > 0)" and set writable=false
unconditionally.


In fact, in all situations where you are woken up by POLLOUT but you
do not have a block to write, POLLOUT will not be polled on the next
iteration to prevent unnecessary looping. Therefore removing the if
condition may not help much. Does the write_block_size equal the
read_block_size? In theory, different sizes are supported, but I think
it could cause issues.





On Wed, Jul 4, 2018 at 9:29 PM, Luiz Augusto von Dentz 
mailto:luiz.de...@gmail.com>> wrote:


Hi Sathish,

On Mon, Jul 2, 2018 at 4:25 PM, Sathish Narasimman
mailto:nsathis...@gmail.com>> wrote:
> Hi,
>
> I am trying to make WBS with PulseAudio. I am almost successful in
> establishing the connection and making a WBS traffic.
>
> But out of 10 attempts or sometimes the TX packets are not sent
due to
> POLLOUT is not reflected in the pollfd
>                 /* Test if the stream is writable */
>                 if (pollfd->revents & POLLOUT)
>                     writable = true;

Are you sure you are polling with POLLOUT?

> I would like to know what causes the stream as not writable.
> What are the connecting modules to resolve this?
> This happening only with WBS as NBS works good without any problem.
>
>
> Thanks,
> Sathish N
>



___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Re: [pulseaudio-discuss] Bluetooth connection disconnects every few minutes

2018-07-08 Thread Georg Chini

On 08.07.2018 15:21, Julian Sikorski wrote:

W dniu 02.07.2018 o 20:09, Julian Sikorski pisze:

W dniu 02.07.2018 o 18:04, Georg Chini pisze:

On 02.07.2018 17:58, Julian Sikorski wrote:

W dniu 29.06.2018 o 21:47, Julian Sikorski pisze:

Hi list,

I have noticed that the bluetooth connection between my laptop (Intel
7260) and my headphones (Sennheiser Momentum Wirelless) is very
unreliable. While under Windows 10 devices stay connected for hours on
end, under Fedora 28 the connection is lost every few minutes at most.
Often the connection will be reestablished only to be lost again.
bluetoothd shows messages like:




I am not sure where to look further. Does it look like an issue with
pulseaudio, or a deeper problem with linux bluetooth stack? Thanks for
your input in advance!

Best regards,
Julian

This is what is logged by pulseaudio at the time the connection is lost:

( 118.064|  34.694) I: [bluetooth] module-bluez5-device.c: FD error:
POLLERR POLLHUP
( 118.064|   0.000) I: [bluetooth] bluez5-util.c: Transport
/org/bluez/hci0/dev_00_1B_66_81_8D_76/fd27 auto-released by BlueZ or
already released
( 118.064|   0.000) I: [pulseaudio] backend-native.c: Lost RFCOMM
connection.



 From what you are writing, it looks to me as if the issue is in the USB
stack and the BT dongle keeps disconnecting/connecting. Have you
tried another dongle?

Hi,

I unfortunately do not own any other dongles. I tried getting some
useful info with btmon but the log seems flooded with way too many
messages to make anything out.


Hi Georg,

it looks like the problem is more related to how the dongle interacts
with this specific headphone model. I have recently bought another one
for running (AfterShokz Trekz Air) and it works perfectly, connecting
automatically, staying connected and even switching profiles
automatically without issues so far.
The hci0: last event is not cmd complete (0x0f) message seems harmless
as it spams the dmesg output regardless of the device connected (and
also when no device is connected at all.
It appears that whatever is happening it makes the dongle reconnect:

[nie lip  8 15:14:12 2018] usb 2-1.4: USB disconnect, device number 6
[nie lip  8 15:14:12 2018] usb 2-1.4: new full-speed USB device number 7
using ehci-pci
[nie lip  8 15:14:12 2018] usb 2-1.4: New USB device found,
idVendor=8087, idProduct=07dc, bcdDevice= 0.01
[nie lip  8 15:14:12 2018] usb 2-1.4: New USB device strings: Mfr=0,
Product=0, SerialNumber=0
[nie lip  8 15:14:12 2018] Bluetooth: hci0: read Intel version:
3707100180012d0d2a
[nie lip  8 15:14:12 2018] Bluetooth: hci0: Intel device is already
patched. patch num: 2a

Where would you recommend to look for reasons for this behaviour? btmon?
Thank you for the pointers!

Best regards,
Julian



Hi Julian,

sorry, I have no further ideas. Maybe Luiz can help you to investigate.
To me it looks like the headphone is sending something that makes the
dongle reset.

Regards
    Georg
___
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


<    1   2   3   4   5   6   7   8   9   10   >