diff options
author | nia <nia@pkgsrc.org> | 2022-07-03 16:09:15 +0000 |
---|---|---|
committer | nia <nia@pkgsrc.org> | 2022-07-03 16:09:15 +0000 |
commit | 02abd1ef0037b69ccbca3af26d682e9be16a0052 (patch) | |
tree | 0aa6cd15eefaccc08af1062093de2ca3ed7f528d /audio | |
parent | 5377028327e31f884029f02b05656a0dba7e4465 (diff) | |
download | pkgsrc-02abd1ef0037b69ccbca3af26d682e9be16a0052.tar.gz |
add audio/snapcast
Snapcast is a multiroom client-server audio player, where all clients are
time synchronized with the server to play perfectly synced audio. It's not
a standalone player, but an extension that turns your existing audio player
into a Sonos-like multiroom solution.
Diffstat (limited to 'audio')
21 files changed, 920 insertions, 1 deletions
diff --git a/audio/Makefile b/audio/Makefile index e40853563f2..ac04df5e4d6 100644 --- a/audio/Makefile +++ b/audio/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.655 2022/07/01 18:36:27 nia Exp $ +# $NetBSD: Makefile,v 1.656 2022/07/03 16:09:15 nia Exp $ # COMMENT= Audio tools, players, and libraries @@ -442,6 +442,7 @@ SUBDIR+= shntool SUBDIR+= shorten SUBDIR+= sidplay SUBDIR+= sidplay2 +SUBDIR+= snapcast SUBDIR+= snd SUBDIR+= sndfile-tools SUBDIR+= solfege diff --git a/audio/snapcast/DESCR b/audio/snapcast/DESCR new file mode 100644 index 00000000000..5422fbb2f11 --- /dev/null +++ b/audio/snapcast/DESCR @@ -0,0 +1,4 @@ +Snapcast is a multiroom client-server audio player, where all clients are +time synchronized with the server to play perfectly synced audio. It's not +a standalone player, but an extension that turns your existing audio player +into a Sonos-like multiroom solution. diff --git a/audio/snapcast/Makefile b/audio/snapcast/Makefile new file mode 100644 index 00000000000..00a20d02f44 --- /dev/null +++ b/audio/snapcast/Makefile @@ -0,0 +1,79 @@ +# $NetBSD: Makefile,v 1.1 2022/07/03 16:09:15 nia Exp $ + +DISTNAME= snapcast-0.26.0 +CATEGORIES= audio +MASTER_SITES= ${MASTER_SITE_GITHUB:=badaix/} +GITHUB_TAG= v${PKGVERSION_NOREV} + +MAINTAINER= nia@NetBSD.org +HOMEPAGE= https://github.com/badaix/snapcast +COMMENT= Multiroom client-server audio player, +LICENSE= gnu-gpl-v3 + +USE_CMAKE= yes +USE_TOOLS+= pkg-config +USE_LANGUAGES= c c++ + +REPLACE_PYTHON+= server/etc/plug-ins/meta_mpd.py + +PYTHON_VERSIONS_INCOMPATIBLE= 27 + +CONF_FILES+= ${PREFIX}/share/examples/snapcast/snapserver.conf \ + ${PKG_SYSCONFDIR}/snapserver.conf + +SUBST_CLASSES+= etc +SUBST_STAGE.etc= pre-configure +SUBST_FILES.etc+= server/etc/snapserver.conf +SUBST_FILES.etc+= server/server_settings.hpp +SUBST_FILES.etc+= server/snapserver.cpp +SUBST_FILES.etc+= server/snapserver.1 +SUBST_VARS.etc+= PKG_SYSCONFDIR +SUBST_VARS.etc+= VARBASE + +RCD_SCRIPTS+= snapclient +RCD_SCRIPTS+= snapserver + +SNAPCLIENT_USER?= snapclient +SNAPCLIENT_GROUP?= snapclient + +SNAPSERVER_USER?= snapserver +SNAPSERVER_GROUP?= snapserver + +BUILD_DEFS+= VARBASE + +.include "../../mk/bsd.prefs.mk" + +FILES_SUBST+= SNAPCLIENT_USER=${SNAPCLIENT_USER} +FILES_SUBST+= SNAPCLIENT_GROUP=${SNAPCLIENT_GROUP} +FILES_SUBST+= VARBASE=${VARBASE} + +PKG_GROUPS+= ${SNAPCLIENT_GROUP} +PKG_GROUPS+= ${SNAPSERVER_GROUP} + +PKG_USERS+= ${SNAPCLIENT_USER}:${SNAPCLIENT_GROUP} +PKG_USERS+= ${SNAPSERVER_USER}:${SNAPSERVER_GROUP} + +OWN_DIRS_PERMS+= ${VARBASE}/run/snapclient \ + ${SNAPCLIENT_USER} ${SNAPCLIENT_GROUP} 0755 + +OWN_DIRS_PERMS+= ${VARBASE}/run/snapserver \ + ${SNAPCLIENT_USER} ${SNAPCLIENT_GROUP} 0755 + +OWN_DIRS_PERMS+= ${VARBASE}/lib/snapclient \ + ${SNAPSERVER_USER} ${SNAPSERVER_GROUP} 0755 + +OWN_DIRS_PERMS+= ${VARBASE}/lib/snapserver \ + ${SNAPSERVER_USER} ${SNAPSERVER_GROUP} 0755 + +post-install: + cd ${WRKSRC} && ${CHMOD} +r ${DESTDIR}${PREFIX}/share/snapserver/plug-ins/meta_mpd.py + +.include "options.mk" +.include "../../audio/flac/buildlink3.mk" +.include "../../audio/libopus/buildlink3.mk" +.include "../../audio/libsoxr/buildlink3.mk" +.include "../../audio/libvorbis/buildlink3.mk" +.include "../../devel/boost-headers/buildlink3.mk" +.include "../../lang/python/application.mk" +.include "../../textproc/expat/buildlink3.mk" +.include "../../mk/bsd.pkg.mk" diff --git a/audio/snapcast/PLIST b/audio/snapcast/PLIST new file mode 100644 index 00000000000..d0559374a01 --- /dev/null +++ b/audio/snapcast/PLIST @@ -0,0 +1,24 @@ +@comment $NetBSD: PLIST,v 1.1 2022/07/03 16:09:15 nia Exp $ +bin/snapclient +bin/snapserver +man/man1/snapclient.1 +man/man1/snapserver.1 +share/examples/snapcast/snapserver.conf +share/pixmaps/snapcast.svg +share/snapserver/index.html +share/snapserver/plug-ins/meta_mpd.py +share/snapserver/snapweb/10-seconds-of-silence.mp3 +share/snapserver/snapweb/3rd-party/libflac.js +share/snapserver/snapweb/config.js +share/snapserver/snapweb/favicon.ico +share/snapserver/snapweb/index.html +share/snapserver/snapweb/launcher-icon.png +share/snapserver/snapweb/manifest.json +share/snapserver/snapweb/mute_icon.png +share/snapserver/snapweb/play.png +share/snapserver/snapweb/snapcast-512.png +share/snapserver/snapweb/snapcontrol.js +share/snapserver/snapweb/snapstream.js +share/snapserver/snapweb/speaker_icon.png +share/snapserver/snapweb/stop.png +share/snapserver/snapweb/styles.css diff --git a/audio/snapcast/distinfo b/audio/snapcast/distinfo new file mode 100644 index 00000000000..7a4f9a0c71f --- /dev/null +++ b/audio/snapcast/distinfo @@ -0,0 +1,18 @@ +$NetBSD: distinfo,v 1.1 2022/07/03 16:09:15 nia Exp $ + +BLAKE2s (snapcast-0.26.0.tar.gz) = e0ef56ee25d30c8536158949c5e5f85b32a8c410303da939ef844bb3dc300012 +SHA512 (snapcast-0.26.0.tar.gz) = fc7885e42a11794e33314544083251ffbb91a0cf160c6d4b854c56f57ffe9f38f75c7594478c9edabfe9076959938cd8de891dd456e66202692de664a75cde71 +Size (snapcast-0.26.0.tar.gz) = 1537036 bytes +SHA1 (patch-CMakeLists.txt) = c3f02503c918e6843ab18d987b3e886e22e13865 +SHA1 (patch-client_CMakeLists.txt) = 48559046bd578e2d75f97b4ec422d4a56b567733 +SHA1 (patch-client_controller.cpp) = a87b5515a519ab579c36786b9727b58934128148 +SHA1 (patch-client_player_sun__player.cpp) = 6e98d22c9deaccc3bf2ac14b7e275dc1c8bc771a +SHA1 (patch-client_player_sun__player.hpp) = d8eeba9f4c16e85833baba95c07f9a0600763752 +SHA1 (patch-client_snapclient.cpp) = d682d4c1de438251d1510d40387e9a0b2bcf926f +SHA1 (patch-common_utils.hpp) = 8184a65459accd76b55e8e9e95d1911439fb4d8a +SHA1 (patch-server_CMakeLists.txt) = 49144e902844bd3308871a625f5da56575904855 +SHA1 (patch-server_etc_snapserver.conf) = a740795aa764ffb5870a4d798518a0464ca3517b +SHA1 (patch-server_server__settings.hpp) = 50950a4855ecc336dbec146b86935fa18942dc1d +SHA1 (patch-server_snapserver.1) = 3459c6109635d1ad72c1aee92e302088a2317007 +SHA1 (patch-server_snapserver.cpp) = 62d5dcbfe369f9095e9b6c695680650f7eb458b6 +SHA1 (patch-server_streamreader_pipe__stream.cpp) = aec6fd900e0aca776510c3b413e457b9082b01b5 diff --git a/audio/snapcast/files/snapclient.sh b/audio/snapcast/files/snapclient.sh new file mode 100644 index 00000000000..4de961f9962 --- /dev/null +++ b/audio/snapcast/files/snapclient.sh @@ -0,0 +1,23 @@ +#!@RCD_SCRIPTS_SHELL@ +# +# $NetBSD: snapclient.sh,v 1.1 2022/07/03 16:09:15 nia Exp $ +# +# PROVIDE: snapclient +# KEYWORD: shutdown +# + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="snapclient" +rcvar=${name} +command="@PREFIX@/bin/snapclient" +command_args="-d" +pidfile="@VARBASE@/run/snapclient/pid" +snapclient_user=@SNAPCLIENT_USER@ +snapclient_group=@SNAPCLIENT_GROUP@ + +load_rc_config $name +run_rc_command "$1" diff --git a/audio/snapcast/files/snapserver.sh b/audio/snapcast/files/snapserver.sh new file mode 100644 index 00000000000..e69b4ac11de --- /dev/null +++ b/audio/snapcast/files/snapserver.sh @@ -0,0 +1,21 @@ +#!@RCD_SCRIPTS_SHELL@ +# +# $NetBSD: snapserver.sh,v 1.1 2022/07/03 16:09:15 nia Exp $ +# +# PROVIDE: snapserver +# KEYWORD: shutdown +# + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="snapserver" +rcvar=${name} +command="@PREFIX@/bin/snapserver" +command_args="-d" +pidfile="@VARBASE@/run/snapserver/pid" + +load_rc_config $name +run_rc_command "$1" diff --git a/audio/snapcast/options.mk b/audio/snapcast/options.mk new file mode 100644 index 00000000000..4dab7a7d865 --- /dev/null +++ b/audio/snapcast/options.mk @@ -0,0 +1,28 @@ +# $NetBSD: options.mk,v 1.1 2022/07/03 16:09:15 nia Exp $ + +PKG_OPTIONS_VAR= PKG_OPTIONS.snapcast +PKG_SUPPORTED_OPTIONS= alsa avahi pulseaudio +PKG_SUGGESTED_OPTIONS.Linux+= alsa + +.include "../../mk/bsd.options.mk" + +.if !empty(PKG_OPTIONS:Malsa) +CMAKE_ARGS+= -DBUILD_WITH_ALSA=ON +. include "../../audio/alsa-lib/buildlink3.mk" +.else +CMAKE_ARGS+= -DBUILD_WITH_ALSA=OFF +.endif + +.if !empty(PKG_OPTIONS:Mavahi) +CMAKE_ARGS+= -DBUILD_WITH_AVAHI=ON +. include "../../net/avahi/buildlink3.mk" +.else +CMAKE_ARGS+= -DBUILD_WITH_AVAHI=OFF +.endif + +.if !empty(PKG_OPTIONS:Mpulseaudio) +CMAKE_ARGS+= -DBUILD_WITH_PULSE=ON +. include "../../audio/pulseaudio/buildlink3.mk" +.else +CMAKE_ARGS+= -DBUILD_WITH_PULSE=OFF +.endif diff --git a/audio/snapcast/patches/patch-CMakeLists.txt b/audio/snapcast/patches/patch-CMakeLists.txt new file mode 100644 index 00000000000..68e70b28d7f --- /dev/null +++ b/audio/snapcast/patches/patch-CMakeLists.txt @@ -0,0 +1,32 @@ +$NetBSD: patch-CMakeLists.txt,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Make ALSA optional. Add Sun Audio support for NetBSD. + +--- CMakeLists.txt.orig 2021-12-22 17:40:36.000000000 +0000 ++++ CMakeLists.txt +@@ -178,10 +178,12 @@ if(NOT WIN32 AND NOT ANDROID) + list(APPEND INCLUDE_DIRS "/usr/local/include") + else() + +- pkg_search_module(ALSA REQUIRED alsa) ++ if(BUILD_WITH_ALSA) ++ pkg_search_module(ALSA alsa) + if (ALSA_FOUND) + add_definitions(-DHAS_ALSA) + endif (ALSA_FOUND) ++ endif(BUILD_WITH_ALSA) + + if(BUILD_WITH_PULSE) + pkg_search_module(PULSE libpulse) +@@ -206,6 +208,11 @@ if(NOT WIN32 AND NOT ANDROID) + link_directories("/usr/local/lib") + list(APPEND INCLUDE_DIRS "/usr/local/include") + endif() ++ ++ check_include_file("sys/audioio.h" SUN_FOUND) ++ if (SUN_FOUND) ++ add_definitions(-DHAS_SUN) ++ endif (SUN_FOUND) + endif() + + pkg_search_module(SOXR soxr) diff --git a/audio/snapcast/patches/patch-client_CMakeLists.txt b/audio/snapcast/patches/patch-client_CMakeLists.txt new file mode 100644 index 00000000000..d732fd2dd1f --- /dev/null +++ b/audio/snapcast/patches/patch-client_CMakeLists.txt @@ -0,0 +1,17 @@ +$NetBSD: patch-client_CMakeLists.txt,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Add Sun Audio support for NetBSD. + +--- client/CMakeLists.txt.orig 2021-12-22 17:40:36.000000000 +0000 ++++ client/CMakeLists.txt +@@ -53,6 +53,10 @@ elseif(NOT ANDROID) + list(APPEND CLIENT_LIBRARIES ${PULSE_LIBRARIES}) + list(APPEND CLIENT_INCLUDE ${PULSE_INCLUDE_DIRS}) + endif (PULSE_FOUND) ++ ++ if (SUN_FOUND) ++ list(APPEND CLIENT_SOURCES player/sun_player.cpp) ++ endif (SUN_FOUND) + endif (MACOSX) + + if (ANDROID) diff --git a/audio/snapcast/patches/patch-client_controller.cpp b/audio/snapcast/patches/patch-client_controller.cpp new file mode 100644 index 00000000000..b8cc083617b --- /dev/null +++ b/audio/snapcast/patches/patch-client_controller.cpp @@ -0,0 +1,37 @@ +$NetBSD: patch-client_controller.cpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Add Sun Audio support for NetBSD. + +--- client/controller.cpp.orig 2021-12-22 17:40:36.000000000 +0000 ++++ client/controller.cpp +@@ -38,6 +38,9 @@ + #ifdef HAS_PULSE + #include "player/pulse_player.hpp" + #endif ++#ifdef HAS_SUN ++#include "player/sun_player.hpp" ++#endif + #ifdef HAS_OPENSL + #include "player/opensl_player.hpp" + #endif +@@ -92,6 +95,9 @@ std::unique_ptr<Player> Controller::crea + std::vector<std::string> Controller::getSupportedPlayerNames() + { + std::vector<std::string> result; ++#ifdef HAS_SUN ++ result.emplace_back(player::SUN); ++#endif + #ifdef HAS_ALSA + result.emplace_back(player::ALSA); + #endif +@@ -188,6 +194,10 @@ void Controller::getNextMessage() + stream_ = make_shared<Stream>(sampleFormat_, settings_.player.sample_format); + stream_->setBufferLen(std::max(0, serverSettings_->getBufferMs() - serverSettings_->getLatency() - settings_.player.latency)); + ++#ifdef HAS_SUN ++ if (!player_) ++ player_ = createPlayer<SunPlayer>(settings_.player, player::SUN); ++#endif + #ifdef HAS_ALSA + if (!player_) + player_ = createPlayer<AlsaPlayer>(settings_.player, player::ALSA); diff --git a/audio/snapcast/patches/patch-client_player_sun__player.cpp b/audio/snapcast/patches/patch-client_player_sun__player.cpp new file mode 100644 index 00000000000..608ed25cca1 --- /dev/null +++ b/audio/snapcast/patches/patch-client_player_sun__player.cpp @@ -0,0 +1,312 @@ +$NetBSD: patch-client_player_sun__player.cpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Add Sun Audio support for NetBSD. + +--- client/player/sun_player.cpp.orig 2022-07-03 14:25:19.031712372 +0000 ++++ client/player/sun_player.cpp +@@ -0,0 +1,305 @@ ++/*** ++ This file is part of snapcast ++ Copyright (C) 2014-2021 Johannes Pohl ++ ++ 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 3 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, see <http://www.gnu.org/licenses/>. ++***/ ++ ++#include <sys/audioio.h> ++#include <sys/ioctl.h> ++#include <fcntl.h> ++#include <unistd.h> ++#include "sun_player.hpp" ++#include "common/aixlog.hpp" ++#include "common/snap_exception.hpp" ++#include "common/str_compat.hpp" ++#include "common/utils/logging.hpp" ++#include "common/utils/string_utils.hpp" ++ ++#ifndef SUN_MAXDEVS ++#define SUN_MAXDEVS (16) ++#endif ++ ++#ifndef AUDIO_GETBUFINFO ++#define AUDIO_GETBUFINFO AUDIO_GETINFO ++#endif ++ ++#ifndef AUDIO_ENCODING_SLIENAR ++#define AUDIO_ENCODING_SLIENAR AUDIO_ENCODING_LINEAR8; ++#endif ++ ++#ifndef AUDIO_ENCODING_SLIENAR_LE ++#define AUDIO_ENCODING_SLIENAR_LE AUDIO_ENCODING_LINEAR; ++#endif ++ ++using namespace std::chrono_literals; ++using namespace std; ++ ++namespace player ++{ ++ ++static constexpr std::chrono::milliseconds BUFFER_TIME = 10ms; ++static constexpr int PERIODS = 3; ++static constexpr int MIN_PERIODS = 1; ++ ++static constexpr auto LOG_TAG = "Sun"; ++ ++SunPlayer::SunPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream) ++ : Player(io_context, settings, stream), handle_(-1) ++{ ++} ++ ++void SunPlayer::initSun() ++{ ++ std::lock_guard<std::recursive_mutex> lock(mutex_); ++ const char *dev; ++ const SampleFormat& format = stream_->getFormat(); ++ struct audio_info info; ++ uint32_t rate = format.rate(); ++ int channels = format.channels(); ++ ++ // Open the PCM device in playback mode ++ if (settings_.pcm_device.name == "default") ++ dev = "/dev/audio"; ++ else ++ dev = settings_.pcm_device.name.c_str(); ++ ++ if ((handle_ = open(dev, O_WRONLY)) < 0) ++ throw SnapException("Can't open " + settings_.pcm_device.name + ", error: " + strerror(errno)); ++ ++ AUDIO_INITINFO(&info); ++ ++ switch (format.bits()) { ++ case 8: ++ info.play.encoding = AUDIO_ENCODING_SLINEAR; ++ info.play.precision = 8; ++ break; ++ case 16: ++ info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; ++ info.play.precision = 16; ++ break; ++ case 32: ++ info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; ++ info.play.precision = 32; ++ break; ++ default: ++ throw SnapException("Unsupported sample format: " + cpt::to_string(format.bits())); ++ break; ++ } ++ ++ if (ioctl(handle_, AUDIO_SETINFO, &info) < 0) ++ { ++ throw SnapException("Unsupported sample format: " + cpt::to_string(format.bits())); ++ } ++ ++ AUDIO_INITINFO(&info); ++ ++ info.play.channels = channels; ++ ++ if (ioctl(handle_, AUDIO_SETINFO, &info) < 0) ++ { ++ throw SnapException("Can't set channel count: " + string(strerror(errno))); ++ } ++ ++ AUDIO_INITINFO(&info); ++ ++ info.play.sample_rate = rate; ++ ++ if (ioctl(handle_, AUDIO_SETINFO, &info) < 0) ++ { ++ throw SnapException("Can't set rate: " + string(strerror(errno))); ++ } ++ ++ (void)ioctl(handle_, AUDIO_GETINFO, &info); ++ ++ rate = info.play.sample_rate; ++ ++ if (format.rate() != rate) ++ LOG(WARNING, LOG_TAG) << "Could not set sample rate to " << format.rate() << " Hz, using: " << rate << " Hz\n"; ++ ++ AUDIO_INITINFO(&info); ++ info.hiwat = PERIODS; ++ info.lowat = MIN_PERIODS; ++ (void)ioctl(handle_, AUDIO_SETINFO, &info); ++} ++ ++ ++void SunPlayer::uninitSun() ++{ ++ std::lock_guard<std::recursive_mutex> lock(mutex_); ++ if (handle_ != -1) ++ { ++ close(handle_); ++ handle_ = -1; ++ } ++} ++ ++ ++void SunPlayer::start() ++{ ++ try ++ { ++ initSun(); ++ } ++ catch (const SnapException& e) ++ { ++ LOG(ERROR, LOG_TAG) << "Exception: " << e.what() << ", code: " << e.code() << "\n"; ++ // Accept "Device or ressource busy", the worker loop will retry ++ if (e.code() != -EBUSY) ++ throw; ++ } ++ ++ Player::start(); ++} ++ ++ ++SunPlayer::~SunPlayer() ++{ ++ stop(); ++} ++ ++ ++void SunPlayer::stop() ++{ ++ Player::stop(); ++ uninitSun(); ++} ++ ++ ++bool SunPlayer::needsThread() const ++{ ++ return true; ++} ++ ++void SunPlayer::worker() ++{ ++ unsigned int framesDelay; ++ unsigned int framesAvail; ++ long lastChunkTick = chronos::getTickCount(); ++ const SampleFormat& format = stream_->getFormat(); ++ struct audio_info info; ++ ++ while (active_) ++ { ++ if (handle_ == -1) ++ { ++ try ++ { ++ initSun(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG(ERROR, LOG_TAG) << "Exception in initSun: " << e.what() << endl; ++ chronos::sleep(100); ++ } ++ if (handle_ == -1) ++ continue; ++ } ++ ++ if (ioctl(handle_, AUDIO_GETBUFINFO, &info) == -1) { ++ this_thread::sleep_for(10ms); ++ continue; ++ } ++ ++ framesDelay = info.play.seek / format.frameSize(); ++ framesAvail = (info.play.buffer_size - info.play.seek) / format.frameSize(); ++ ++ if (buffer_.size() < static_cast<size_t>(info.play.buffer_size)) ++ { ++ LOG(DEBUG, LOG_TAG) << "Resizing buffer from " << buffer_.size() << " to " << info.play.buffer_size << "\n"; ++ buffer_.resize(info.play.buffer_size); ++ } ++ ++ if (framesAvail == 0) ++ { ++ auto frame_time = std::chrono::microseconds(static_cast<int>((info.blocksize / format.frameSize()) / format.usRate())); ++ std::chrono::microseconds wait = std::min(frame_time / 2, std::chrono::microseconds(10ms)); ++ LOG(DEBUG, LOG_TAG) << "No frames available, waiting for " << wait.count() << " us\n"; ++ this_thread::sleep_for(wait); ++ continue; ++ } ++ ++ ++ chronos::usec delay(static_cast<chronos::usec::rep>(1000 * static_cast<double>(framesDelay) / format.msRate())); ++ ++ if (stream_->getPlayerChunk(buffer_.data(), delay, framesAvail)) ++ { ++ lastChunkTick = chronos::getTickCount(); ++ adjustVolume(buffer_.data(), framesAvail); ++ if (write(handle_, buffer_.data(), framesAvail * format.frameSize()) < 0) ++ { ++ LOG(ERROR, LOG_TAG) << "ERROR. Can't write to PCM device: " << strerror(errno) << "\n"; ++ uninitSun(); ++ } ++ } ++ else ++ { ++ LOG(INFO, LOG_TAG) << "Failed to get chunk\n"; ++ while (active_ && !stream_->waitForChunk(100ms)) ++ { ++ static utils::logging::TimeConditional cond(2s); ++ LOG(DEBUG, LOG_TAG) << cond << "Waiting for chunk\n"; ++ if ((handle_ != -1) && (chronos::getTickCount() - lastChunkTick > 5000)) ++ { ++ LOG(NOTICE, LOG_TAG) << "No chunk received for 5000ms. Closing audio device.\n"; ++ uninitSun(); ++ stream_->clearChunks(); ++ } ++ } ++ } ++ } ++} ++ ++ ++ ++vector<PcmDevice> SunPlayer::pcm_list() ++{ ++ std::string name; ++ struct audio_device dev; ++ vector<PcmDevice> result; ++ PcmDevice pcmDevice; ++ int i; ++ int fd; ++ int props; ++ ++ for (i = 0; i < SUN_MAXDEVS; ++i) ++ { ++ name = "/dev/audio" + cpt::to_string(i); ++ fd = open(name.c_str(), O_WRONLY); ++ if (fd == -1) ++ break; ++ if (ioctl(fd, AUDIO_GETPROPS, &props) == -1) ++ { ++ close(fd); ++ break; ++ } ++ if (ioctl(fd, AUDIO_GETDEV, &dev) == -1) ++ { ++ close(fd); ++ break; ++ } ++ close(fd); ++ if ((props & AUDIO_PROP_PLAYBACK) == 0) ++ { ++ continue; ++ } ++ pcmDevice.name = name; ++ pcmDevice.description = string(dev.name) + " " + string(dev.version); ++ pcmDevice.idx = i; ++ result.push_back(pcmDevice); ++ } ++ return result; ++} ++ ++} // namespace player diff --git a/audio/snapcast/patches/patch-client_player_sun__player.hpp b/audio/snapcast/patches/patch-client_player_sun__player.hpp new file mode 100644 index 00000000000..ab29dd3ca7a --- /dev/null +++ b/audio/snapcast/patches/patch-client_player_sun__player.hpp @@ -0,0 +1,74 @@ +$NetBSD: patch-client_player_sun__player.hpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Add Sun Audio support for NetBSD. + +--- client/player/sun_player.hpp.orig 2022-07-03 13:22:32.864495808 +0000 ++++ client/player/sun_player.hpp +@@ -0,0 +1,67 @@ ++/*** ++ This file is part of snapcast ++ Copyright (C) 2014-2020 Johannes Pohl ++ ++ 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 3 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, see <http://www.gnu.org/licenses/>. ++***/ ++ ++#ifndef SUN_PLAYER_HPP ++#define SUN_PLAYER_HPP ++ ++#include "player.hpp" ++ ++#include <chrono> ++#include <optional> ++ ++ ++namespace player ++{ ++ ++static constexpr auto SUN = "sun"; ++ ++/// Audio Player ++/** ++ * Audio player implementation using Sun/NetBSD audio ++ */ ++class SunPlayer : public Player ++{ ++public: ++ SunPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream); ++ ~SunPlayer() override; ++ ++ void start() override; ++ void stop() override; ++ ++ /// List the system's audio output devices ++ static std::vector<PcmDevice> pcm_list(); ++ ++protected: ++ void worker() override; ++ bool needsThread() const override; ++ ++private: ++ /// initialize audio device ++ void initSun(); ++ /// close audio device ++ void uninitSun(); ++ ++ int handle_; ++ ++ std::recursive_mutex mutex_; ++ std::vector<char> buffer_; ++}; ++ ++} // namespace player ++ ++#endif diff --git a/audio/snapcast/patches/patch-client_snapclient.cpp b/audio/snapcast/patches/patch-client_snapclient.cpp new file mode 100644 index 00000000000..d8a9f8977ba --- /dev/null +++ b/audio/snapcast/patches/patch-client_snapclient.cpp @@ -0,0 +1,59 @@ +$NetBSD: patch-client_snapclient.cpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Add Sun Audio support for NetBSD. + +--- client/snapclient.cpp.orig 2021-12-22 17:40:36.000000000 +0000 ++++ client/snapclient.cpp +@@ -32,6 +32,9 @@ + #ifdef HAS_PULSE + #include "player/pulse_player.hpp" + #endif ++#ifdef HAS_SUN ++#include "player/sun_player.hpp" ++#endif + #ifdef HAS_WASAPI + #include "player/wasapi_player.hpp" + #endif +@@ -62,8 +65,12 @@ PcmDevice getPcmDevice(const std::string + { + LOG(DEBUG, LOG_TAG) << "Trying to get PCM device for player: " << player << ", parameter: " + << ", card: " << soundcard << "\n"; +-#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI) ++#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI) || defined(HAS_SUN) + vector<PcmDevice> pcm_devices; ++#if defined(HAS_SUN) ++ if (player == player::SUN) ++ pcm_devices = SunPlayer::pcm_list(); ++#endif + #if defined(HAS_ALSA) + if (player == player::ALSA) + pcm_devices = AlsaPlayer::pcm_list(); +@@ -142,7 +149,7 @@ int main(int argc, char** argv) + op.add<Value<string>>("", "hostID", "unique host id, default is MAC address", "", &settings.host_id); + + // PCM device specific +-#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI) ++#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI) || defined(HAS_SUN) + auto listSwitch = op.add<Switch>("l", "list", "list PCM devices"); + /*auto soundcardValue =*/op.add<Value<string>>("s", "soundcard", "index or name of the pcm device", pcm_device, &pcm_device); + #endif +@@ -210,7 +217,7 @@ int main(int argc, char** argv) + + settings.player.player_name = utils::string::split_left(settings.player.player_name, ':', settings.player.parameter); + +-#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI) ++#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI) || defined(HAS_SUN) + if (listSwitch->is_set()) + { + try +@@ -224,6 +231,10 @@ int main(int argc, char** argv) + if (settings.player.player_name == player::PULSE) + pcm_devices = PulsePlayer::pcm_list(settings.player.parameter); + #endif ++#if defined(HAS_SUN) ++ if (settings.player.player_name == player::SUN) ++ pcm_devices = SunPlayer::pcm_list(); ++#endif + #if defined(HAS_WASAPI) + if (settings.player.player_name == player::WASAPI) + pcm_devices = WASAPIPlayer::pcm_list(); diff --git a/audio/snapcast/patches/patch-common_utils.hpp b/audio/snapcast/patches/patch-common_utils.hpp new file mode 100644 index 00000000000..0f5cebbb7cf --- /dev/null +++ b/audio/snapcast/patches/patch-common_utils.hpp @@ -0,0 +1,67 @@ +$NetBSD: patch-common_utils.hpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Add NetBSD support. + +--- common/utils.hpp.orig 2021-12-22 17:40:36.000000000 +0000 ++++ common/utils.hpp +@@ -1,5 +1,4 @@ +-/*** +- This file is part of snapcast ++/*** This file is part of snapcast + Copyright (C) 2014-2020 Johannes Pohl + + This program is free software: you can redistribute it and/or modify +@@ -44,7 +43,7 @@ + #include <sys/stat.h> + #include <sys/types.h> + #include <vector> +-#if !defined(WINDOWS) && !defined(FREEBSD) ++#if defined(MACOS) || defined(__linux__) + #include <sys/sysinfo.h> + #endif + #ifdef MACOS +@@ -53,6 +52,10 @@ + #include <ifaddrs.h> + #include <net/if_dl.h> + #endif ++#ifdef __NetBSD__ ++#include <ifaddrs.h> ++#include <net/if_dl.h> ++#endif + #ifdef ANDROID + #include <sys/system_properties.h> + #endif +@@ -306,7 +309,7 @@ static std::string getMacAddress(int soc + { + if (!(ifr.ifr_flags & IFF_LOOPBACK)) // don't count loopback + { +-#ifdef MACOS ++#if defined(MACOS) || defined(__NetBSD__) + /// Dirty Mac version + struct ifaddrs *ifap, *ifaptr; + unsigned char* ptr; +@@ -333,6 +336,7 @@ static std::string getMacAddress(int soc + } + #endif + ++#ifndef __NetBSD__ + #ifdef FREEBSD + if (ioctl(sock, SIOCGIFMAC, &ifr) == 0) + #else +@@ -355,6 +359,7 @@ static std::string getMacAddress(int soc + return line; + } + } ++#endif + } + } + else +@@ -369,7 +374,7 @@ static std::string getMacAddress(int soc + return ""; + + char mac[19]; +-#ifndef FREEBSD ++#if !defined(FREEBSD) && !defined(__NetBSD__) + sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)ifr.ifr_hwaddr.sa_data[0], (unsigned char)ifr.ifr_hwaddr.sa_data[1], + (unsigned char)ifr.ifr_hwaddr.sa_data[2], (unsigned char)ifr.ifr_hwaddr.sa_data[3], (unsigned char)ifr.ifr_hwaddr.sa_data[4], + (unsigned char)ifr.ifr_hwaddr.sa_data[5]); diff --git a/audio/snapcast/patches/patch-server_CMakeLists.txt b/audio/snapcast/patches/patch-server_CMakeLists.txt new file mode 100644 index 00000000000..0ad3991dc0e --- /dev/null +++ b/audio/snapcast/patches/patch-server_CMakeLists.txt @@ -0,0 +1,15 @@ +$NetBSD: patch-server_CMakeLists.txt,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Install config files to examples, per pkgsrc conventions. + +--- server/CMakeLists.txt.orig 2021-12-22 17:40:36.000000000 +0000 ++++ server/CMakeLists.txt +@@ -114,7 +114,7 @@ else() + + install(TARGETS snapserver COMPONENT server DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES snapserver.1 COMPONENT server DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +- install(FILES etc/snapserver.conf COMPONENT server DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}) ++ install(FILES etc/snapserver.conf COMPONENT server DESTINATION ${CMAKE_INSTALL_DATADIR}/examples/snapcast) + install(FILES etc/index.html COMPONENT server DESTINATION ${CMAKE_INSTALL_DATADIR}/snapserver) + install(DIRECTORY etc/snapweb/ DESTINATION ${CMAKE_INSTALL_DATADIR}/snapserver/snapweb) + install(FILES etc/plug-ins/meta_mpd.py PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE DESTINATION ${CMAKE_INSTALL_DATADIR}/snapserver/plug-ins/) diff --git a/audio/snapcast/patches/patch-server_etc_snapserver.conf b/audio/snapcast/patches/patch-server_etc_snapserver.conf new file mode 100644 index 00000000000..23018efc151 --- /dev/null +++ b/audio/snapcast/patches/patch-server_etc_snapserver.conf @@ -0,0 +1,24 @@ +$NetBSD: patch-server_etc_snapserver.conf,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Don't hardcode various paths, allow pkgsrc to substitute them. + +--- server/etc/snapserver.conf.orig 2021-12-22 17:40:36.000000000 +0000 ++++ server/etc/snapserver.conf +@@ -30,7 +30,7 @@ + #threads = -1 + + # the pid file when running as daemon +-#pidfile = /var/run/snapserver/pid ++#pidfile = @VARBASE@/run/snapserver/pid + + # the user to run as when daemonized + #user = snapserver +@@ -39,7 +39,7 @@ + + # directory where persistent data is stored (server.json) + # if empty, data dir will be +-# - "/var/lib/snapserver/" when running as daemon ++# - "@VARBASE@/lib/snapserver/" when running as daemon + # - "$HOME/.config/snapserver/" when not running as daemon + #datadir = + diff --git a/audio/snapcast/patches/patch-server_server__settings.hpp b/audio/snapcast/patches/patch-server_server__settings.hpp new file mode 100644 index 00000000000..16bfe48671c --- /dev/null +++ b/audio/snapcast/patches/patch-server_server__settings.hpp @@ -0,0 +1,15 @@ +$NetBSD: patch-server_server__settings.hpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Don't hardcode various paths, allow pkgsrc to substitute them. + +--- server/server_settings.hpp.orig 2021-12-22 17:40:36.000000000 +0000 ++++ server/server_settings.hpp +@@ -30,7 +30,7 @@ struct ServerSettings + struct Server + { + int threads{-1}; +- std::string pid_file{"/var/run/snapserver/pid"}; ++ std::string pid_file{"@VARBASE@/run/snapserver/pid"}; + std::string user{"snapserver"}; + std::string group{""}; + std::string data_dir{""}; diff --git a/audio/snapcast/patches/patch-server_snapserver.1 b/audio/snapcast/patches/patch-server_snapserver.1 new file mode 100644 index 00000000000..b0d4c9885b6 --- /dev/null +++ b/audio/snapcast/patches/patch-server_snapserver.1 @@ -0,0 +1,30 @@ +$NetBSD: patch-server_snapserver.1,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Don't hardcode various paths, allow pkgsrc to substitute them. + +--- server/snapserver.1.orig 2021-12-22 17:40:36.000000000 +0000 ++++ server/snapserver.1 +@@ -25,20 +25,17 @@ Show version number + Daemonize + optional process priority [-20..19] + .TP +-\fB-c, --config arg (=/etc/snapserver.conf)\fR ++\fB-c, --config arg (=@PKG_SYSCONFDIR@/snapserver.conf)\fR + path to the configuration file + .SH FILES + .TP + \fI/tmp/snapfifo\fR + PCM input fifo file + .TP +-\fI/etc/default/snapserver\fR +-the daemon default configuration file +-.TP +-\fI/etc/snapserver.conf\fR ++\fI@PKG_SYSCONFDIR@/snapserver.conf\fR + the snapserver configuration file + .TP +-\fI~/.config/snapcast/server.json\fR or (if $HOME is not set) \fI/var/lib/snapcast/server.json\fR ++\fI~/.config/snapcast/server.json\fR or (if $HOME is not set) \fI@VARBASE@/lib/snapcast/server.json\fR + persistent server data file + .SH "COPYRIGHT" + Copyright (C) 2014-2020 Johannes Pohl (snapcast@badaix.de). diff --git a/audio/snapcast/patches/patch-server_snapserver.cpp b/audio/snapcast/patches/patch-server_snapserver.cpp new file mode 100644 index 00000000000..4e19ee62556 --- /dev/null +++ b/audio/snapcast/patches/patch-server_snapserver.cpp @@ -0,0 +1,24 @@ +$NetBSD: patch-server_snapserver.cpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Don't hardcode various paths, allow pkgsrc to substitute them. + +--- server/snapserver.cpp.orig 2021-12-22 17:40:36.000000000 +0000 ++++ server/snapserver.cpp +@@ -58,7 +58,7 @@ int main(int argc, char* argv[]) + { + ServerSettings settings; + std::string pcmSource = "pipe:///tmp/snapfifo?name=default"; +- std::string config_file = "/etc/snapserver.conf"; ++ std::string config_file = "@PKG_SYSCONFDIR@/snapserver.conf"; + + OptionParser op("Allowed options"); + auto helpSwitch = op.add<Switch>("h", "help", "Produce help message, use -hh to show options from config file"); +@@ -252,7 +252,7 @@ int main(int argc, char* argv[]) + throw std::invalid_argument("user must not be empty"); + + if (settings.server.data_dir.empty()) +- settings.server.data_dir = "/var/lib/snapserver"; ++ settings.server.data_dir = "@VARBASE@/lib/snapserver"; + Config::instance().init(settings.server.data_dir, settings.server.user, settings.server.group); + + daemon = std::make_unique<Daemon>(settings.server.user, settings.server.group, settings.server.pid_file); diff --git a/audio/snapcast/patches/patch-server_streamreader_pipe__stream.cpp b/audio/snapcast/patches/patch-server_streamreader_pipe__stream.cpp new file mode 100644 index 00000000000..02a8a94163a --- /dev/null +++ b/audio/snapcast/patches/patch-server_streamreader_pipe__stream.cpp @@ -0,0 +1,15 @@ +$NetBSD: patch-server_streamreader_pipe__stream.cpp,v 1.1 2022/07/03 16:09:15 nia Exp $ + +Add NetBSD support. + +--- server/streamreader/pipe_stream.cpp.orig 2021-12-22 17:40:36.000000000 +0000 ++++ server/streamreader/pipe_stream.cpp +@@ -59,7 +59,7 @@ void PipeStream::do_connect() + { + int fd = open(uri_.path.c_str(), O_RDONLY | O_NONBLOCK); + int pipe_size = -1; +-#if !defined(MACOS) && !defined(FREEBSD) ++#ifdef __linux__ + pipe_size = fcntl(fd, F_GETPIPE_SZ); + #endif + LOG(TRACE, LOG_TAG) << "Stream: " << name_ << ", connect to pipe: " << uri_.path << ", fd: " << fd << ", pipe size: " << pipe_size << "\n"; |