diff options
Diffstat (limited to 'audio/snapcast/patches/patch-client_player_sun__player.cpp')
-rw-r--r-- | audio/snapcast/patches/patch-client_player_sun__player.cpp | 312 |
1 files changed, 312 insertions, 0 deletions
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 |