filter/Filter: add virtual method Flush()

This will be used by filters which have internal buffers which need to
be flushed at the end, e.g. the "soxr" resampler.
This commit is contained in:
Max Kellermann 2018-01-01 19:22:45 +01:00
parent 14f669f4fb
commit 6d0d8cf9cf
9 changed files with 134 additions and 7 deletions

View File

@ -1644,7 +1644,7 @@ libfilter_api_a_SOURCES = \
src/filter/Observer.cxx src/filter/Observer.hxx \
src/filter/FilterPlugin.hxx \
src/filter/Prepared.hxx \
src/filter/Filter.hxx
src/filter/Filter.cxx src/filter/Filter.hxx
libfilter_plugins_a_SOURCES = \
src/AudioCompress/config.h \

27
src/filter/Filter.cxx Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "Filter.hxx"
#include "util/ConstBuffer.hxx"
ConstBuffer<void>
Filter::Flush()
{
return nullptr;
}

View File

@ -62,6 +62,12 @@ public:
* or Reset() call)
*/
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src) = 0;
/**
* Flush pending data and return it. This should be called
* repepatedly until it returns nullptr.
*/
virtual ConstBuffer<void> Flush();
};
#endif

View File

@ -75,6 +75,10 @@ public:
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override {
return filter->FilterPCM(src);
}
ConstBuffer<void> Flush() noexcept override {
return filter->Flush();
}
};
Filter *

View File

@ -43,6 +43,11 @@ class ChainFilter final : public Filter {
std::list<Child> children;
/**
* The child which will be flushed in the next Flush() call.
*/
std::list<Child>::iterator flushing = children.end();
public:
explicit ChainFilter(AudioFormat _audio_format)
:Filter(_audio_format) {}
@ -54,11 +59,20 @@ public:
assert(out_audio_format.IsValid());
children.emplace_back(name, std::move(filter));
RewindFlush();
}
/* virtual methods from class Filter */
void Reset() noexcept override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override;
ConstBuffer<void> Flush() override;
private:
void RewindFlush() {
flushing = children.begin();
}
};
class PreparedChainFilter final : public PreparedFilter {
@ -118,21 +132,44 @@ PreparedChainFilter::Open(AudioFormat &in_audio_format)
void
ChainFilter::Reset() noexcept
{
RewindFlush();
for (auto &child : children)
child.filter->Reset();
}
template<typename I>
static ConstBuffer<void>
ApplyFilterChain(I begin, I end, ConstBuffer<void> src)
{
for (auto i = begin; i != end; ++i)
/* feed the output of the previous filter as input
into the current one */
src = i->filter->FilterPCM(src);
return src;
}
ConstBuffer<void>
ChainFilter::FilterPCM(ConstBuffer<void> src)
{
for (auto &child : children) {
/* feed the output of the previous filter as input
into the current one */
src = child.filter->FilterPCM(src);
}
RewindFlush();
/* return the output of the last filter */
return src;
return ApplyFilterChain(children.begin(), children.end(), src);
}
ConstBuffer<void>
ChainFilter::Flush()
{
for (auto end = children.end(); flushing != end; ++flushing) {
auto data = flushing->filter->Flush();
if (!data.IsNull())
return ApplyFilterChain(std::next(flushing), end,
data);
}
return nullptr;
}
std::unique_ptr<PreparedFilter>

View File

@ -244,3 +244,11 @@ AudioOutputSource::ConsumeData(size_t nbytes) noexcept
if (pending_data.empty())
pipe.Consume(*std::exchange(current_chunk, nullptr));
}
ConstBuffer<void>
AudioOutputSource::Flush()
{
return filter
? filter->Flush()
: nullptr;
}

View File

@ -195,6 +195,11 @@ public:
pipe.ClearTail(chunk);
}
/**
* Wrapper for Filter::Flush().
*/
ConstBuffer<void> Flush();
private:
void OpenFilter(AudioFormat audio_format,
PreparedFilter *prepared_replay_gain_filter,

View File

@ -362,11 +362,42 @@ AudioOutputControl::InternalPause() noexcept
skip_delay = true;
}
static void
PlayFull(FilteredAudioOutput &output, ConstBuffer<void> _buffer)
{
auto buffer = ConstBuffer<uint8_t>::FromVoid(_buffer);
while (!buffer.empty()) {
size_t nbytes = output.Play(buffer.data, buffer.size);
assert(nbytes > 0);
buffer.skip_front(nbytes);
}
}
inline void
AudioOutputControl::InternalDrain() noexcept
{
const ScopeUnlock unlock(mutex);
try {
/* flush the filter and play its remaining output */
while (true) {
auto buffer = source.Flush();
if (buffer.IsNull())
break;
PlayFull(*output, buffer);
}
} catch (...) {
FormatError(std::current_exception(),
"Failed to flush filter on %s", GetLogName());
InternalCloseError(std::current_exception());
return;
}
output->Drain();
}

View File

@ -86,6 +86,15 @@ try {
output.size);
}
while (true) {
auto output = state.Flush();
if (output.IsNull())
break;
gcc_unused ssize_t ignored = write(1, output.data,
output.size);
}
state.Close();
return EXIT_SUCCESS;