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:
parent
14f669f4fb
commit
6d0d8cf9cf
@ -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
27
src/filter/Filter.cxx
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user