mirror of
https://github.com/badaix/snapcast.git
synced 2025-07-14 23:27:40 +02:00
Avoid copying of the PCM stream
This commit is contained in:
parent
fcb40f325a
commit
888e19a8a4
3 changed files with 43 additions and 19 deletions
|
@ -57,6 +57,16 @@ Resampler::Resampler(const SampleFormat& in_format, const SampleFormat& out_form
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Resampler::resamplingNeeded() const
|
||||||
|
{
|
||||||
|
#ifdef HAS_SOXR
|
||||||
|
return soxr_ != nullptr;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// std::shared_ptr<msg::PcmChunk> Resampler::resample(std::shared_ptr<msg::PcmChunk> chunk, chronos::usec duration)
|
// std::shared_ptr<msg::PcmChunk> Resampler::resample(std::shared_ptr<msg::PcmChunk> chunk, chronos::usec duration)
|
||||||
// {
|
// {
|
||||||
// auto resampled_chunk = resample(chunk);
|
// auto resampled_chunk = resample(chunk);
|
||||||
|
@ -85,43 +95,44 @@ Resampler::Resampler(const SampleFormat& in_format, const SampleFormat& out_form
|
||||||
// // }
|
// // }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
shared_ptr<msg::PcmChunk> Resampler::resample(shared_ptr<msg::PcmChunk> chunk)
|
|
||||||
|
std::shared_ptr<msg::PcmChunk> Resampler::resample(const msg::PcmChunk& chunk)
|
||||||
{
|
{
|
||||||
#ifndef HAS_SOXR
|
#ifndef HAS_SOXR
|
||||||
return chunk;
|
return std::make_shared<msg::PcmChunk>(chunk);
|
||||||
#else
|
#else
|
||||||
if (soxr_ == nullptr)
|
if (!resamplingNeeded())
|
||||||
{
|
{
|
||||||
return chunk;
|
return std::make_shared<msg::PcmChunk>(chunk);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (in_format_.bits() == 24)
|
if (in_format_.bits() == 24)
|
||||||
{
|
{
|
||||||
// sox expects 32 bit input, shift 8 bits left
|
// sox expects 32 bit input, shift 8 bits left
|
||||||
int32_t* frames = (int32_t*)chunk->payload;
|
int32_t* frames = (int32_t*)chunk.payload;
|
||||||
for (size_t n = 0; n < chunk->getSampleCount(); ++n)
|
for (size_t n = 0; n < chunk.getSampleCount(); ++n)
|
||||||
frames[n] = frames[n] << 8;
|
frames[n] = frames[n] << 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t idone;
|
size_t idone;
|
||||||
size_t odone;
|
size_t odone;
|
||||||
auto resample_buffer_framesize = resample_buffer_.size() / out_format_.frameSize();
|
auto resample_buffer_framesize = resample_buffer_.size() / out_format_.frameSize();
|
||||||
auto error = soxr_process(soxr_, chunk->payload, chunk->getFrameCount(), &idone, resample_buffer_.data(), resample_buffer_framesize, &odone);
|
auto error = soxr_process(soxr_, chunk.payload, chunk.getFrameCount(), &idone, resample_buffer_.data(), resample_buffer_framesize, &odone);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
LOG(ERROR, LOG_TAG) << "Error soxr_process: " << error << "\n";
|
LOG(ERROR, LOG_TAG) << "Error soxr_process: " << error << "\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(TRACE, LOG_TAG) << "Resample idone: " << idone << "/" << chunk->getFrameCount() << ", odone: " << odone << "/"
|
LOG(TRACE, LOG_TAG) << "Resample idone: " << idone << "/" << chunk.getFrameCount() << ", odone: " << odone << "/"
|
||||||
<< resample_buffer_.size() / out_format_.frameSize() << ", delay: " << soxr_delay(soxr_) << "\n";
|
<< resample_buffer_.size() / out_format_.frameSize() << ", delay: " << soxr_delay(soxr_) << "\n";
|
||||||
|
|
||||||
// some data has been resampled (odone frames) and some is still in the pipe (soxr_delay frames)
|
// some data has been resampled (odone frames) and some is still in the pipe (soxr_delay frames)
|
||||||
if (odone > 0)
|
if (odone > 0)
|
||||||
{
|
{
|
||||||
// get the resampled ts from the input ts
|
// get the resampled ts from the input ts
|
||||||
auto input_end_ts = chunk->start() + chunk->duration<std::chrono::microseconds>();
|
auto input_end_ts = chunk.start() + chunk.duration<std::chrono::microseconds>();
|
||||||
double resampled_ms = (odone + soxr_delay(soxr_)) / out_format_.msRate();
|
double resampled_ms = (odone + soxr_delay(soxr_)) / out_format_.msRate();
|
||||||
auto resampled_start = input_end_ts - std::chrono::microseconds(static_cast<int>(resampled_ms * 1000.));
|
auto resampled_start = input_end_ts - std::chrono::microseconds(static_cast<int>(resampled_ms * 1000.));
|
||||||
|
|
||||||
|
@ -172,6 +183,23 @@ shared_ptr<msg::PcmChunk> Resampler::resample(shared_ptr<msg::PcmChunk> chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
shared_ptr<msg::PcmChunk> Resampler::resample(shared_ptr<msg::PcmChunk> chunk)
|
||||||
|
{
|
||||||
|
#ifndef HAS_SOXR
|
||||||
|
return chunk;
|
||||||
|
#else
|
||||||
|
if (!resamplingNeeded())
|
||||||
|
{
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return resample(*chunk);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Resampler::~Resampler()
|
Resampler::~Resampler()
|
||||||
{
|
{
|
||||||
#ifdef HAS_SOXR
|
#ifdef HAS_SOXR
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
|
|
||||||
// std::shared_ptr<msg::PcmChunk> resample(std::shared_ptr<msg::PcmChunk> chunk, chronos::usec duration);
|
// std::shared_ptr<msg::PcmChunk> resample(std::shared_ptr<msg::PcmChunk> chunk, chronos::usec duration);
|
||||||
std::shared_ptr<msg::PcmChunk> resample(std::shared_ptr<msg::PcmChunk> chunk);
|
std::shared_ptr<msg::PcmChunk> resample(std::shared_ptr<msg::PcmChunk> chunk);
|
||||||
|
std::shared_ptr<msg::PcmChunk> resample(const msg::PcmChunk& chunk);
|
||||||
|
bool resamplingNeeded() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<char> resample_buffer_;
|
std::vector<char> resample_buffer_;
|
||||||
|
|
|
@ -62,10 +62,7 @@ MetaStream::MetaStream(PcmListener* pcmListener, std::vector<std::shared_ptr<Pcm
|
||||||
if (!streams_.empty())
|
if (!streams_.empty())
|
||||||
{
|
{
|
||||||
active_stream_ = streams_.front();
|
active_stream_ = streams_.front();
|
||||||
if ((sampleFormat_.rate() != active_stream_->getSampleFormat().rate()) || (sampleFormat_.bits() != active_stream_->getSampleFormat().bits()))
|
|
||||||
resampler_ = make_unique<Resampler>(active_stream_->getSampleFormat(), sampleFormat_);
|
resampler_ = make_unique<Resampler>(active_stream_->getSampleFormat(), sampleFormat_);
|
||||||
else
|
|
||||||
resampler_ = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,10 +109,7 @@ void MetaStream::onStateChanged(const PcmStream* pcmStream, ReaderState state)
|
||||||
if (active_stream_ != stream)
|
if (active_stream_ != stream)
|
||||||
{
|
{
|
||||||
active_stream_ = stream;
|
active_stream_ = stream;
|
||||||
if ((sampleFormat_.rate() != active_stream_->getSampleFormat().rate()) || (sampleFormat_.bits() != active_stream_->getSampleFormat().bits()))
|
|
||||||
resampler_ = make_unique<Resampler>(active_stream_->getSampleFormat(), sampleFormat_);
|
resampler_ = make_unique<Resampler>(active_stream_->getSampleFormat(), sampleFormat_);
|
||||||
else
|
|
||||||
resampler_ = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(ReaderState::kPlaying);
|
setState(ReaderState::kPlaying);
|
||||||
|
@ -164,9 +158,9 @@ void MetaStream::onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resampler_)
|
if (resampler_ && resampler_->resamplingNeeded())
|
||||||
{
|
{
|
||||||
auto resampled_chunk = resampler_->resample(std::make_shared<msg::PcmChunk>(chunk));
|
auto resampled_chunk = resampler_->resample(chunk);
|
||||||
if (resampled_chunk)
|
if (resampled_chunk)
|
||||||
chunkRead(*resampled_chunk);
|
chunkRead(*resampled_chunk);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue