Commit Graph

131 Commits

Author SHA1 Message Date
Max Kellermann
8f6c750064 output/alsa: improve silence generator test in DrainInternal()
There cannot be partial frames in the buffer, so we don't need
GetPeriodPosition(); it's enough to check whether head has been moved
in this period.
2019-06-27 21:17:37 +02:00
Max Kellermann
9fc1668de3 output/alsa: hold back snd_pcm_writei() until period_buffer is full
This gives MPD more control, because attempts to avoid having partial
periods in the ALSA period buffer.  For example, this means that
DrainInternal() doesn't need to generate silence to fill the partial
period.
2019-06-27 15:40:07 +02:00
Max Kellermann
2f243f2295 pcm/Export: rename GetFrameSize() to GetOutputFrameSize() 2019-06-26 15:46:49 +02:00
Max Kellermann
e69fd0300a pcm/Export: rename CalcSourceSize() to CalcInputSize() 2019-06-26 15:46:05 +02:00
Max Kellermann
f43cafbf7d pcm/Export: eliminate the AudioFormat parameter from GetFrameSize() 2019-06-26 15:42:47 +02:00
Max Kellermann
28e07e900f pcm/Export: convert the DSD bools to an enum
These options are exclusive.
2019-06-17 22:35:00 +02:00
Max Kellermann
47bb1cd8b5 output/{alsa,osx}: use ConstBuffer::empty() 2019-06-17 22:09:47 +02:00
Max Kellermann
ccc96e25d3 output/{alsa,osx}: remove comment after PcmExport::Export()
Returning an empty buffer is a normal result now (since commit
79839db3a3), and doesn't deserve such a
big comment.
2019-06-17 22:09:06 +02:00
Max Kellermann
a139279575 Copyright year 2019 2019-06-17 11:17:30 +02:00
Max Kellermann
fd5e74dbd0 pcm/Pcm{Dop,Export}: drop "Pcm" prefix 2019-06-16 12:11:44 +02:00
Max Kellermann
973c87b351 event/Call, ...: use wait() with predicate 2019-05-07 20:01:45 +02:00
Max Kellermann
92022658f9 thread/Cond: add wait() overload which takes a unique_lock<>
Just like std::condition_variable, which however has no way to specify
the std::mutex directly.
2019-04-26 11:51:45 +02:00
Max Kellermann
b51bae5500 thread/*Cond: rename methods to match std::condition_variable 2019-04-25 19:46:43 +02:00
Max Kellermann
4cdcaa8630 output/alsa: don't call snd_pcm_drain() if nothing was written
Works around a problem where MPD goes into a busy loop because
snd_pcm_drain() always returns `-EAGAIN` without making any progress
(fixes #425).

This problem was triggered by snd_pcm_drain() after snd_pcm_cancel()
and snd_pcm_prepare(), but without submitting any data with
snd_pcm_writei().

I believe this is a kernel bug: in non-blocking mode, the kernel's
snd_pcm_drain() function returns early.  In this mode, it only checks
whether snd_pcm_drain_done() has been called already, but
snd_pcm_drain_done() is never called if no data was submitted.

In blocking mode, the following `for` loop detects this condition, so
snd_pcm_drain_done() is not necessary, but without this extra check,
we get `-EAGAIN` forever.
2018-11-16 12:49:37 +01:00
Max Kellermann
436ba3c96c output/alsa: drain the whole ring_buffer, not just one period
This fixes a problem which caused a failure with snd_pcm_writei()
because snd_pcm_drain() had already been called in the previous
iteration.  This commit makes sure that snd_pcm_drain() is only called
after the final snd_pcm_writei() call.

This fixes discarded samples at the end of playback.
2018-11-14 13:35:17 +01:00
Max Kellermann
5d12f52873 output/alsa: clear error after reopening device
When a playback error has occurred, MPD would never recover until one
restarts MPD.
2018-11-14 13:20:54 +01:00
Max Kellermann
b477f86c92 output/alsa: don't lock the mutex in CancelInternal()
CancelInternal() doesn't need to be protected because it is called
synchronously from Cancel().
2018-11-14 11:50:51 +01:00
Max Kellermann
020371f145 output/alsa: wake up the client thread after generating silence
Fixes a theoretical race condition which could occur in Drain() (but
was extremely unlikely).
2018-11-14 11:48:55 +01:00
Max Kellermann
ccafe3f3cf output/alsa: don't generate silence if ALSA-PCM buffer has enough data
If our `ring_buffer` is smaller than the ALSA-PCM buffer (if the
latter has more than the 4 periods we allocate), it can happen that
the start threshold is crossed and ALSA switches to
`SND_PCM_STATE_RUNNING`, but the `ring_buffer` is empty.  In this
case, MPDD will generate silence, even though the ALSA-PCM buffer has
enough data.  This causes stuttering (#420).

This commit amends an older workaround for a similar problem (commit
e08598e7e2) by adding a snd_pcm_avail()
check, and only generate silence if there is less than one period of
data in the ALSA-PCM buffer.

Fixes #420
2018-11-14 11:17:59 +01:00
Max Kellermann
3830748de5 output/alsa: clear the period_buffer in LockCaughtError()
The method Cancel() assumes that the `period_buffer` must be empty
when `active==false`, but that is not the case when Play() fails.

Of course the assertion in Cancel() is not 100% correct, but I decided
to rather fix this in LockCaughtError() because the `period_buffer`
should only be accessed from within the RTIO thread, and this is the
only code path where `active` can be set to `false` with a non-empty
`period_buffer`.

Fixes #423
2018-11-14 10:24:08 +01:00
Max Kellermann
1a43f5145d output/alsa: throw on snd_pcm_writei() error while draining
This implements real error handling, and avoids calling
CancelInternal() from this code path.
2018-11-14 10:08:29 +01:00
Max Kellermann
7f143a83c1 output/alsa: fix wrong use of errno
alsa-lib doesn't set errno, it returns errors as negative integers.
2018-11-14 10:07:23 +01:00
Max Kellermann
6ccc254179 output/alsa: throw after snd_pcm_drain() error 2018-11-14 10:04:10 +01:00
Max Kellermann
7db2450447 output/alsa: refactor the drain EAGAIN workaround 2018-11-14 10:00:50 +01:00
Max Kellermann
6c2a6a65e0 output/alsa: remove snd_pcm_state() check from DrainInternal()
This check was added 9 years ago in commit
4dc25d3908 to work around a dmix bug
which I assume has been fixed long ago.

Removing this fixes another corner case: if draining is requested
before the start threshold is reached, the PCM is still in
SND_PCM_STATE_PREPARED but not yet SND_PCM_STATE_RUNNING, which means
the submitted data will never be played.  This corner case is
realistic when playing songs shorter than the ALSA buffer (if the
buffer is very large).
2018-11-14 09:48:24 +01:00
Max Kellermann
4247a757b3 output/alsa: call snd_pcm_prepare() if draining is requested early
This fixes a corner case which has probably never occurred and
probably never will: if Cancel() is called, and then Play() followed
by Drain(), the plugin should really play that data.  However
currently, this never happens, because snd_pcm_prepare() is never
called.
2018-11-14 09:43:14 +01:00
Max Kellermann
fee75dc766 {output,mixer}/alsa: use snd_pcm_poll_descriptors_revents()
This call was missing, causing very high CPU usage when the ALSA
output plugin was used with dmix.

Closes #391
2018-11-11 12:37:29 +01:00
Max Kellermann
12308a0f55 lib/alsa/NonBlock: move the functions into a class managing the state 2018-11-11 12:37:25 +01:00
Max Kellermann
f5c9071494 *: copyright year 2018 2018-10-31 17:54:59 +01:00
Max Kellermann
2fb5dbe62b output/alsa: use spsc_queue::reset()
This method was added in Boost 1.58.
2018-07-16 11:04:01 +02:00
Max Kellermann
a8568d7246 Merge branch 'v0.20.x' 2018-05-03 20:15:11 +02:00
Max Kellermann
ac61fd1d78 {input,output}/alsa: work around -Wswitch due to SND_PCM_STATE_PRIVATE1 2018-05-03 11:59:18 +02:00
Max Kellermann
e08598e7e2 output/alsa: don't submit silence if PCM is not yet running
See code comment.  Closes #260
2018-04-26 17:14:42 +02:00
Max Kellermann
d301a79dcf output/alsa: CopyRingToPeriodBuffer() returns bool 2018-04-26 16:44:23 +02:00
Max Kellermann
98d76627a5 output/alsa: disable events in Cancel()
Don't reactivate the PCM device immediately after Cancel() is
finished; if Cancel() gets called this may mean that new data may take
a while to produce, or no data at all will be produced because the
current song is being stopped.

Once new data is available, Play() will automatically reactivate the
PCM.

This fixes underruns when switching songs manually (closes #264).
2018-04-26 16:41:49 +02:00
Max Kellermann
8a809013f3 output/alsa: reset "active" on error
Use the "active" flag in PrepareSockets() instead of LockHasError().
2018-04-26 16:18:59 +02:00
Max Kellermann
485ed0b156 output/alsa: make the "active" attribute thread-safe 2018-04-26 16:18:52 +02:00
Max Kellermann
7d546f80f9 output/alsa: merge Activate() and UnlockActivate()
Prepare to make the "active" attribute thread-safe.
2018-04-26 16:18:18 +02:00
Max Kellermann
5dfdc15f93 output/alsa: rethrow errors in Drain() 2018-04-26 16:17:14 +02:00
Max Kellermann
a6e5873443 output/alsa: thread-safety documentation for attribute "active" 2018-04-26 15:54:15 +02:00
Max Kellermann
e2cf777538 output/alsa: simplify MultiSocketMonitor::GetEventLoop() calls 2018-04-26 15:54:01 +02:00
Max Kellermann
9c61760713 output/alsa: remove obsolete comment, was moved to Alsa::PeriodBuffer::Allocate() 2018-04-10 15:42:29 +02:00
Max Kellermann
be65c7d5d0 config/Block: add method GetPositiveValue()
Adds missing checks to several plugins.
2018-01-02 17:24:34 +01:00
Max Kellermann
8ac73a9eba output/alsa: add attributes "dop" and "allowed_formats" 2017-12-19 11:57:52 +01:00
Max Kellermann
914df18bf9 Main, ...: catch any exception, not just std::runtime_error 2017-12-19 10:56:23 +01:00
Max Kellermann
7d579e7400 lib/alsa/NonBlock: throw exception on error
Avoid another potential deadlock: if no file descriptors are
registered, our non-blocking ALSA code cannot ever work.
2017-11-14 21:08:07 +01:00
Max Kellermann
e0f777d4eb output/alsa: move code to LockCaughtError() 2017-11-14 21:07:59 +01:00
Max Kellermann
febd1ad09c output/alsa: fix deadlock bug
After UnlockActivate() returns, we not only need to check for errors,
but also for more room in the ring buffer.  If we don't check the ring
buffer, it may be drained already, and the cond.wait() call will never
finish.

Closes #151
2017-11-14 21:02:53 +01:00
Max Kellermann
c582a9faae event/MultiSocketMonitor: add "noexcept" 2017-11-12 17:42:50 +01:00
Max Kellermann
390e830994 {mixer,output}/alsa: migrate from DeferredMonitor to DeferEvent 2017-11-12 17:27:58 +01:00