mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-04 04:36:44 +02:00
Add silence threshold for alsa stream
This commit is contained in:
parent
5d7aedeb31
commit
216714bd33
5 changed files with 61 additions and 4 deletions
|
@ -77,6 +77,12 @@ public:
|
||||||
uint32_t payloadSize;
|
uint32_t payloadSize;
|
||||||
char* payload;
|
char* payload;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::pair<T*, size_t> getPayload() const
|
||||||
|
{
|
||||||
|
return std::make_pair<T*, size_t>(reinterpret_cast<T*>(payload), payloadSize / sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void doserialize(std::ostream& stream) const override
|
void doserialize(std::ostream& stream) const override
|
||||||
{
|
{
|
||||||
|
|
|
@ -142,13 +142,14 @@ output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format
|
||||||
Captures audio from an alsa device
|
Captures audio from an alsa device
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
alsa://?name=<name>&device=<alsa device>[&send_silence=false][&idle_threshold=100]
|
alsa://?name=<name>&device=<alsa device>[&send_silence=false][&idle_threshold=100][&silence_threshold_percent=0.0]
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Available parameters
|
#### Available parameters
|
||||||
|
|
||||||
- `device`: alsa device name or identifier, e.g. `default` or `hw:0,0` or `hw:0,0,0`
|
- `device`: alsa device name or identifier, e.g. `default` or `hw:0,0` or `hw:0,0,0`
|
||||||
- `idle_threshold`: switch stream state from playing to idle after receiving `idle_threshold` milliseconds of silence
|
- `idle_threshold`: switch stream state from playing to idle after receiving `idle_threshold` milliseconds of silence
|
||||||
|
- `silence_threshold_percent`: percent (float) of the max amplitude to be considered as silence
|
||||||
- `send_silence`: forward silence to clients when stream state is `idle`
|
- `send_silence`: forward silence to clients when stream state is `idle`
|
||||||
|
|
||||||
The output of any audio player that uses alsa can be redirected to Snapcast by using an alsa loopback device:
|
The output of any audio player that uses alsa can be redirected to Snapcast by using an alsa loopback device:
|
||||||
|
|
|
@ -125,7 +125,7 @@ doc_root = /usr/share/snapserver/snapweb
|
||||||
# sampleformat will be set to "44100:16:2"
|
# sampleformat will be set to "44100:16:2"
|
||||||
# tcp server: tcp://<listen IP, e.g. 127.0.0.1>:<port>?name=<name>[&mode=server]
|
# tcp server: tcp://<listen IP, e.g. 127.0.0.1>:<port>?name=<name>[&mode=server]
|
||||||
# tcp client: tcp://<server IP, e.g. 127.0.0.1>:<port>?name=<name>&mode=client
|
# tcp client: tcp://<server IP, e.g. 127.0.0.1>:<port>?name=<name>&mode=client
|
||||||
# alsa: alsa://?name=<name>&device=<alsa device>[&send_silence=false][&idle_threshold=100]
|
# alsa: alsa://?name=<name>&device=<alsa device>[&send_silence=false][&idle_threshold=100][&silence_threshold_percent=0.0]
|
||||||
# meta: meta:///<name of source#1>/<name of source#2>/.../<name of source#N>?name=<name>
|
# meta: meta:///<name of source#1>/<name of source#2>/.../<name of source#N>?name=<name>
|
||||||
source = pipe:///tmp/snapfifo?name=default
|
source = pipe:///tmp/snapfifo?name=default
|
||||||
#source = tcp://127.0.0.1?name=mopidy_tcp
|
#source = tcp://127.0.0.1?name=mopidy_tcp
|
||||||
|
|
|
@ -71,7 +71,18 @@ AlsaStream::AlsaStream(PcmListener* pcmListener, boost::asio::io_context& ioc, c
|
||||||
device_ = uri_.getQuery("device", "hw:0");
|
device_ = uri_.getQuery("device", "hw:0");
|
||||||
send_silence_ = (uri_.getQuery("send_silence", "false") == "true");
|
send_silence_ = (uri_.getQuery("send_silence", "false") == "true");
|
||||||
idle_threshold_ = std::chrono::milliseconds(std::max(cpt::stoi(uri_.getQuery("idle_threshold", "100")), 10));
|
idle_threshold_ = std::chrono::milliseconds(std::max(cpt::stoi(uri_.getQuery("idle_threshold", "100")), 10));
|
||||||
LOG(DEBUG, LOG_TAG) << "Device: " << device_ << "\n";
|
double silence_threshold_percent = 0.;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
silence_threshold_percent = cpt::stod(uri_.getQuery("silence_threshold_percent", "0"));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
int32_t max_amplitude = std::pow(2, sampleFormat_.bits() - 1) - 1;
|
||||||
|
silence_threshold_ = max_amplitude * (silence_threshold_percent / 100.);
|
||||||
|
LOG(DEBUG, LOG_TAG) << "Device: " << device_ << ", silence threshold percent: " << silence_threshold_percent
|
||||||
|
<< ", silence threshold amplitude: " << silence_threshold_ << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,6 +177,41 @@ void AlsaStream::uninitAlsa()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AlsaStream::isSilent(const msg::PcmChunk& chunk) const
|
||||||
|
{
|
||||||
|
if (silence_threshold_ == 0)
|
||||||
|
return (std::memcmp(chunk.payload, silent_chunk_.data(), silent_chunk_.size()) == 0);
|
||||||
|
|
||||||
|
if (sampleFormat_.sampleSize() == 1)
|
||||||
|
{
|
||||||
|
auto payload = chunk.getPayload<int8_t>();
|
||||||
|
for (size_t n = 0; n < payload.second; ++n)
|
||||||
|
{
|
||||||
|
if (abs(payload.first[n]) > silence_threshold_)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sampleFormat_.sampleSize() == 2)
|
||||||
|
{
|
||||||
|
auto payload = chunk.getPayload<int16_t>();
|
||||||
|
for (size_t n = 0; n < payload.second; ++n)
|
||||||
|
{
|
||||||
|
if (abs(payload.first[n]) > silence_threshold_)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sampleFormat_.sampleSize() == 4)
|
||||||
|
{
|
||||||
|
auto payload = chunk.getPayload<int32_t>();
|
||||||
|
for (size_t n = 0; n < payload.second; ++n)
|
||||||
|
{
|
||||||
|
if (abs(payload.first[n]) > silence_threshold_)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void AlsaStream::do_read()
|
void AlsaStream::do_read()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -210,7 +256,7 @@ void AlsaStream::do_read()
|
||||||
}
|
}
|
||||||
} while (len < toRead);
|
} while (len < toRead);
|
||||||
|
|
||||||
if (std::memcmp(chunk_->payload, silent_chunk_.data(), silent_chunk_.size()) == 0)
|
if (isSilent(*chunk_))
|
||||||
{
|
{
|
||||||
silence_ += chunk_->duration<std::chrono::microseconds>();
|
silence_ += chunk_->duration<std::chrono::microseconds>();
|
||||||
if (silence_ > idle_threshold_)
|
if (silence_ > idle_threshold_)
|
||||||
|
|
|
@ -47,6 +47,9 @@ protected:
|
||||||
void initAlsa();
|
void initAlsa();
|
||||||
void uninitAlsa();
|
void uninitAlsa();
|
||||||
|
|
||||||
|
/// check if the chunk's volume is below the silence threshold
|
||||||
|
bool isSilent(const msg::PcmChunk& chunk) const;
|
||||||
|
|
||||||
snd_pcm_t* handle_;
|
snd_pcm_t* handle_;
|
||||||
std::unique_ptr<msg::PcmChunk> chunk_;
|
std::unique_ptr<msg::PcmChunk> chunk_;
|
||||||
bool first_;
|
bool first_;
|
||||||
|
@ -61,6 +64,7 @@ protected:
|
||||||
bool send_silence_;
|
bool send_silence_;
|
||||||
/// silence duration before switching the stream to idle
|
/// silence duration before switching the stream to idle
|
||||||
std::chrono::milliseconds idle_threshold_;
|
std::chrono::milliseconds idle_threshold_;
|
||||||
|
int32_t silence_threshold_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace streamreader
|
} // namespace streamreader
|
||||||
|
|
Loading…
Add table
Reference in a new issue