mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-22 05:26:17 +02:00
Make sending of silence configurable
This commit is contained in:
parent
90c7ba8a67
commit
4b7c313fa0
4 changed files with 22 additions and 6 deletions
|
@ -138,10 +138,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>
|
alsa://?name=<name>&device=<alsa device>[&send_silence=false][&idle_threshold=100]
|
||||||
```
|
```
|
||||||
|
|
||||||
`device` is an alsa device name or identifier, e.g. `default` or `hw:0,0`
|
#### Available parameters
|
||||||
|
|
||||||
|
- `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
|
||||||
|
- `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:
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ doc_root = /usr/share/snapserver/snapweb
|
||||||
# Non blocking sources support the dryout_ms parameter: when no new data is read from the source, send silence to the clients
|
# Non blocking sources support the dryout_ms parameter: when no new data is read from the source, send silence to the clients
|
||||||
# Available types are:
|
# Available types are:
|
||||||
# pipe: pipe:///<path/to/pipe>?name=<name>[&mode=create][&dryout_ms=2000], mode can be "create" or "read"
|
# pipe: pipe:///<path/to/pipe>?name=<name>[&mode=create][&dryout_ms=2000], mode can be "create" or "read"
|
||||||
# librespot: librespot:///<path/to/librespot>?name=<name>[&dryout_ms=2000][&username=<my username>&password=<my password>][&devicename=Snapcast][&bitrate=320][&wd_timeout=7800][&volume=100][&onevent=""][&nomalize=false][&autoplay=false]
|
# librespot: librespot:///<path/to/librespot>?name=<name>[&dryout_ms=2000][&username=<my username>&password=<my password>][&devicename=Snapcast][&bitrate=320][&wd_timeout=7800][&volume=100][&onevent=""][&nomalize=false][&autoplay=false][¶ms=<generic librepsot process arguments>]
|
||||||
# note that you need to have the librespot binary on your machine
|
# note that you need to have the librespot binary on your machine
|
||||||
# sampleformat will be set to "44100:16:2"
|
# sampleformat will be set to "44100:16:2"
|
||||||
# file: file:///<path/to/PCM/file>?name=<name>
|
# file: file:///<path/to/PCM/file>?name=<name>
|
||||||
|
@ -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>
|
# alsa: alsa://?name=<name>&device=<alsa device>[&send_silence=false][&idle_threshold=100]
|
||||||
# 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
|
||||||
|
|
|
@ -69,6 +69,8 @@ AlsaStream::AlsaStream(PcmListener* pcmListener, boost::asio::io_context& ioc, c
|
||||||
: PcmStream(pcmListener, ioc, uri), handle_(nullptr), read_timer_(ioc), silence_(0ms)
|
: PcmStream(pcmListener, ioc, uri), handle_(nullptr), read_timer_(ioc), silence_(0ms)
|
||||||
{
|
{
|
||||||
device_ = uri_.getQuery("device", "hw:0");
|
device_ = uri_.getQuery("device", "hw:0");
|
||||||
|
send_silence_ = (uri_.getQuery("send_silence", "false") == "true");
|
||||||
|
idle_threshold_ = std::chrono::milliseconds(std::max(cpt::stoi(uri_.getQuery("idle_threshold", "100")), 10));
|
||||||
LOG(DEBUG, LOG_TAG) << "Device: " << device_ << "\n";
|
LOG(DEBUG, LOG_TAG) << "Device: " << device_ << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +217,7 @@ void AlsaStream::do_read()
|
||||||
if (std::memcmp(chunk_->payload, silent_chunk_.data(), silent_chunk_.size()) == 0)
|
if (std::memcmp(chunk_->payload, silent_chunk_.data(), silent_chunk_.size()) == 0)
|
||||||
{
|
{
|
||||||
silence_ += chunk_->duration<std::chrono::microseconds>();
|
silence_ += chunk_->duration<std::chrono::microseconds>();
|
||||||
if (silence_ > 100ms)
|
if (silence_ > idle_threshold_)
|
||||||
{
|
{
|
||||||
setState(ReaderState::kIdle);
|
setState(ReaderState::kIdle);
|
||||||
}
|
}
|
||||||
|
@ -223,6 +225,8 @@ void AlsaStream::do_read()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
silence_ = 0ms;
|
silence_ = 0ms;
|
||||||
|
if ((state_ == ReaderState::kIdle) && !send_silence_)
|
||||||
|
first_ = true;
|
||||||
setState(ReaderState::kPlaying);
|
setState(ReaderState::kPlaying);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +238,10 @@ void AlsaStream::do_read()
|
||||||
tvEncodedChunk_ = std::chrono::steady_clock::now() - duration;
|
tvEncodedChunk_ = std::chrono::steady_clock::now() - duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((state_ == ReaderState::kPlaying) || ((state_ == ReaderState::kIdle) && send_silence_))
|
||||||
|
{
|
||||||
chunkRead(*chunk_);
|
chunkRead(*chunk_);
|
||||||
|
}
|
||||||
|
|
||||||
nextTick_ += duration;
|
nextTick_ += duration;
|
||||||
auto currentTick = std::chrono::steady_clock::now();
|
auto currentTick = std::chrono::steady_clock::now();
|
||||||
|
|
|
@ -56,6 +56,11 @@ protected:
|
||||||
std::vector<char> silent_chunk_;
|
std::vector<char> silent_chunk_;
|
||||||
std::chrono::microseconds silence_;
|
std::chrono::microseconds silence_;
|
||||||
std::string lastException_;
|
std::string lastException_;
|
||||||
|
|
||||||
|
/// send silent chunks to clients
|
||||||
|
bool send_silence_;
|
||||||
|
/// silence duration before switching the stream to idle
|
||||||
|
std::chrono::milliseconds idle_threshold_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace streamreader
|
} // namespace streamreader
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue