Add documentation

This commit is contained in:
badaix 2025-02-16 22:07:09 +01:00 committed by Johannes Pohl
parent f0985cbce4
commit 76e35e3e18
34 changed files with 199 additions and 78 deletions

View file

@ -153,6 +153,7 @@ public:
virtual void getNextMessage(const MessageHandler<msg::BaseMessage>& handler) = 0; virtual void getNextMessage(const MessageHandler<msg::BaseMessage>& handler) = 0;
protected: protected:
/// Send @p buffer, return result in @p write_handler
virtual void write(boost::asio::streambuf& buffer, WriteHandler&& write_handler) = 0; virtual void write(boost::asio::streambuf& buffer, WriteHandler&& write_handler) = 0;
/// Connect to @p endpoint /// Connect to @p endpoint

View file

@ -129,7 +129,7 @@ Controller::Controller(boost::asio::io_context& io_context, const ClientSettings
#endif // HAS_OPENSSL #endif // HAS_OPENSSL
} }
/// Helper to create a player instance
template <typename PlayerType> template <typename PlayerType>
std::unique_ptr<Player> Controller::createPlayer(ClientSettings::Player& settings, const std::string& player_name) std::unique_ptr<Player> Controller::createPlayer(ClientSettings::Player& settings, const std::string& player_name)
{ {

View file

@ -46,8 +46,8 @@ struct riff_wave_header
/// See https://en.wikipedia.org/wiki/WAV /// See https://en.wikipedia.org/wiki/WAV
struct chunk_header struct chunk_header
{ {
uint32_t id; uint32_t id; ///< id
uint32_t sz; uint32_t sz; ///< size
}; };
@ -55,12 +55,12 @@ struct chunk_header
/// See https://en.wikipedia.org/wiki/WAV /// See https://en.wikipedia.org/wiki/WAV
struct chunk_fmt struct chunk_fmt
{ {
uint16_t audio_format; ///< uint16_t audio_format; ///< format
uint16_t num_channels; uint16_t num_channels; ///< channels
uint32_t sample_rate; uint32_t sample_rate; ///< sample rate
uint32_t byte_rate; uint32_t byte_rate; ///< byte rate
uint16_t block_align; uint16_t block_align; ///< block align
uint16_t bits_per_sample; uint16_t bits_per_sample; ///< bps
}; };

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -36,10 +36,12 @@ template <class T>
class DoubleBuffer class DoubleBuffer
{ {
public: public:
DoubleBuffer(size_t size = 10) : bufferSize(size) /// c'tor
explicit DoubleBuffer(size_t size = 10) : bufferSize(size)
{ {
} }
/// Add @p element, pop last, if buffer is full
inline void add(const T& element) inline void add(const T& element)
{ {
buffer.push_back(element); buffer.push_back(element);
@ -47,6 +49,7 @@ public:
buffer.pop_front(); buffer.pop_front();
} }
/// Add @p element, pop last, if buffer is full
inline void add(T&& element) inline void add(T&& element)
{ {
buffer.push_back(std::move(element)); buffer.push_back(std::move(element));
@ -54,7 +57,7 @@ public:
buffer.pop_front(); buffer.pop_front();
} }
/// Median as mean over N values around the median /// @return median as mean over N values around the median
T median(uint16_t mean = 1) const T median(uint16_t mean = 1) const
{ {
if (buffer.empty()) if (buffer.empty())
@ -78,6 +81,7 @@ public:
} }
} }
/// @return mean value
double mean() const double mean() const
{ {
if (buffer.empty()) if (buffer.empty())
@ -88,6 +92,7 @@ public:
return mean; return mean;
} }
/// @return @p percentile percentile
T percentile(unsigned int percentile) const T percentile(unsigned int percentile) const
{ {
if (buffer.empty()) if (buffer.empty())
@ -97,6 +102,7 @@ public:
return tmpBuffer[(size_t)((tmpBuffer.size() - 1) * ((float)percentile / (float)100))]; return tmpBuffer[(size_t)((tmpBuffer.size() - 1) * ((float)percentile / (float)100))];
} }
/// @return array of different percentiles
template <std::size_t Size> template <std::size_t Size>
std::array<T, Size> percentiles(std::array<uint8_t, Size> percentiles) const std::array<T, Size> percentiles(std::array<uint8_t, Size> percentiles) const
{ {
@ -112,31 +118,37 @@ public:
return result; return result;
} }
/// @return if the buffer is full
inline bool full() const inline bool full() const
{ {
return (buffer.size() == bufferSize); return (buffer.size() == bufferSize);
} }
/// Clear the buffer
inline void clear() inline void clear()
{ {
buffer.clear(); buffer.clear();
} }
/// @return current size of the buffer
inline size_t size() const inline size_t size() const
{ {
return buffer.size(); return buffer.size();
} }
/// @return if the buffer is empty
inline bool empty() const inline bool empty() const
{ {
return buffer.empty(); return buffer.empty();
} }
/// Set size of the buffer
void setSize(size_t size) void setSize(size_t size)
{ {
bufferSize = size; bufferSize = size;
} }
/// @return the raw buffer
const std::deque<T>& getBuffer() const const std::deque<T>& getBuffer() const
{ {
return buffer; return buffer;

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -44,7 +44,9 @@ static constexpr auto ALSA = "alsa";
class AlsaPlayer : public Player class AlsaPlayer : public Player
{ {
public: public:
/// c'tor
AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream); AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
/// d'tor
~AlsaPlayer() override; ~AlsaPlayer() override;
void start() override; void start() override;

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2022 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#ifndef CORE_AUDIO_PLAYER_HPP
#define CORE_AUDIO_PLAYER_HPP #pragma once
// local headers // local headers
#include "player.hpp" #include "player.hpp"
@ -45,13 +45,17 @@ static constexpr auto COREAUDIO = "coreaudio";
class CoreAudioPlayer : public Player class CoreAudioPlayer : public Player
{ {
public: public:
/// c'tor
CoreAudioPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream); CoreAudioPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
/// d'tor
virtual ~CoreAudioPlayer(); virtual ~CoreAudioPlayer();
/// Callback funtion for audio data to be played
void playerCallback(AudioQueueRef queue, AudioQueueBufferRef bufferRef); void playerCallback(AudioQueueRef queue, AudioQueueBufferRef bufferRef);
/// @return list of available pcm devices
static std::vector<PcmDevice> pcm_list(); static std::vector<PcmDevice> pcm_list();
protected: private:
void worker() override; void worker() override;
bool needsThread() const override; bool needsThread() const override;
@ -67,5 +71,3 @@ protected:
}; };
} // namespace player } // namespace player
#endif

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2022 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,8 +16,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#ifndef FILE_PLAYER_HPP
#define FILE_PLAYER_HPP #pragma once
// local headers // local headers
#include "player.hpp" #include "player.hpp"
@ -40,8 +41,10 @@ static constexpr auto FILE = "file";
class FilePlayer : public Player class FilePlayer : public Player
{ {
public: public:
/// c'tor
FilePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream); FilePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
virtual ~FilePlayer(); /// d'tor
~FilePlayer() override;
void start() override; void start() override;
void stop() override; void stop() override;
@ -49,7 +52,7 @@ public:
/// List the dummy file PCM device /// List the dummy file PCM device
static std::vector<PcmDevice> pcm_list(const std::string& parameter); static std::vector<PcmDevice> pcm_list(const std::string& parameter);
protected: private:
void requestAudio(); void requestAudio();
void loop(); void loop();
bool needsThread() const override; bool needsThread() const override;
@ -60,5 +63,3 @@ protected:
}; };
} // namespace player } // namespace player
#endif

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2022 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#ifndef OPEN_SL_PLAYER_HPP #pragma once
#define OPEN_SL_PLAYER_HPP
// local headers // local headers
#include "player.hpp" #include "player.hpp"
@ -35,7 +35,9 @@ namespace player
static constexpr auto OPENSL = "opensl"; static constexpr auto OPENSL = "opensl";
typedef int (*AndroidAudioCallback)(short* buffer, int num_samples);
// typedef int (*AndroidAudioCallback)(short* buffer, int num_samples);
using AndroidAudioCallback = int (*)(short*, int);
/// OpenSL Audio Player /// OpenSL Audio Player
@ -45,15 +47,18 @@ typedef int (*AndroidAudioCallback)(short* buffer, int num_samples);
class OpenslPlayer : public Player class OpenslPlayer : public Player
{ {
public: public:
/// c'tor
OpenslPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream); OpenslPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
virtual ~OpenslPlayer(); /// d'tor
~OpenslPlayer() override;
void start() override; void start() override;
void stop() override; void stop() override;
/// Callback to feed data to the player API
void playerCallback(SLAndroidSimpleBufferQueueItf bq); void playerCallback(SLAndroidSimpleBufferQueueItf bq);
protected: private:
void initOpensl(); void initOpensl();
void uninitOpensl(); void uninitOpensl();
@ -83,5 +88,3 @@ protected:
}; };
} // namespace player } // namespace player
#endif

View file

@ -54,6 +54,7 @@ public:
bool mute{false}; ///< muted? bool mute{false}; ///< muted?
}; };
/// Callback for volume changes on the local client, to notify the server
using volume_callback = std::function<void(const Volume& volume)>; using volume_callback = std::function<void(const Volume& volume)>;
/// c'tor /// c'tor
@ -89,7 +90,6 @@ protected:
/// set the hardware mixer volume /// set the hardware mixer volume
/// @param volume the volume on range [0..1], muted or not /// @param volume the volume on range [0..1], muted or not
/// @param muted muted or not
virtual void setHardwareVolume(const Volume& volume); virtual void setHardwareVolume(const Volume& volume);
/// set volume polynomial: volume^exp /// set volume polynomial: volume^exp
@ -102,7 +102,6 @@ protected:
/// Notify the server about hardware volume changes /// Notify the server about hardware volume changes
/// @param volume the volume in range [0..1] /// @param volume the volume in range [0..1]
/// @param muted if muted or not
void notifyVolumeChange(const Volume& volume) const void notifyVolumeChange(const Volume& volume) const
{ {
if (onVolumeChanged_) if (onVolumeChanged_)

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -42,7 +42,9 @@ static constexpr auto PULSE = "pulse";
class PulsePlayer : public Player class PulsePlayer : public Player
{ {
public: public:
/// c'tor
PulsePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream); PulsePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
/// d'tor
virtual ~PulsePlayer(); virtual ~PulsePlayer();
void start() override; void start() override;
@ -51,7 +53,7 @@ public:
/// List the system's audio output devices /// List the system's audio output devices
static std::vector<PcmDevice> pcm_list(const std::string& parameter); static std::vector<PcmDevice> pcm_list(const std::string& parameter);
protected: private:
bool needsThread() const override; bool needsThread() const override;
void worker() override; void worker() override;

View file

@ -51,6 +51,7 @@ static constexpr auto LOG_TAG = "WASAPI";
template <typename T> template <typename T>
struct COMMemDeleter struct COMMemDeleter
{ {
/// Operator()
void operator()(T* obj) void operator()(T* obj)
{ {
if (obj != NULL) if (obj != NULL)

View file

@ -16,8 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#ifndef WASAPI_PLAYER_HPP #pragma once
#define WASAPI_PLAYER_HPP
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4100) #pragma warning(disable : 4100)
@ -42,31 +41,37 @@ class AudioSessionEventListener : public IAudioSessionEvents
bool muted_ = false; bool muted_ = false;
public: public:
/// c'tor
AudioSessionEventListener() : _cRef(1) AudioSessionEventListener() : _cRef(1)
{ {
} }
/// @return volume
float getVolume() float getVolume()
{ {
return volume_; return volume_;
} }
/// @return if muted
bool getMuted() bool getMuted()
{ {
return muted_; return muted_;
} }
/// d'tor
~AudioSessionEventListener() ~AudioSessionEventListener()
{ {
} }
// IUnknown methods -- AddRef, Release, and QueryInterface // IUnknown methods -- AddRef, Release, and QueryInterface
/// documentation missing
ULONG STDMETHODCALLTYPE AddRef() ULONG STDMETHODCALLTYPE AddRef()
{ {
return InterlockedIncrement(&_cRef); return InterlockedIncrement(&_cRef);
} }
/// documentation missing
ULONG STDMETHODCALLTYPE Release() ULONG STDMETHODCALLTYPE Release()
{ {
ULONG ulRef = InterlockedDecrement(&_cRef); ULONG ulRef = InterlockedDecrement(&_cRef);
@ -77,34 +82,42 @@ public:
return ulRef; return ulRef;
} }
/// documentation missing
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface);
// Notification methods for audio session events // Notification methods for audio session events
/// OnDisplayNameChanged callback
HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext)
{ {
return S_OK; return S_OK;
} }
/// OnIconPathChanged callback
HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext) HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext)
{ {
return S_OK; return S_OK;
} }
/// OnSimpleVolumeChanged callback
HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext); HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext);
/// OnChannelVolumeChanged callback
HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext) HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext)
{ {
return S_OK; return S_OK;
} }
/// OnGroupingParamChanged callback
HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext) HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext)
{ {
return S_OK; return S_OK;
} }
/// OnStateChanged callback
HRESULT STDMETHODCALLTYPE OnStateChanged(AudioSessionState NewState); HRESULT STDMETHODCALLTYPE OnStateChanged(AudioSessionState NewState);
/// OnSessionDisconnected callback
HRESULT STDMETHODCALLTYPE OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason); HRESULT STDMETHODCALLTYPE OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason);
}; };
@ -117,19 +130,23 @@ class AudioEndpointVolumeCallback : public IAudioEndpointVolumeCallback
bool muted_ = false; bool muted_ = false;
public: public:
/// c'tor
AudioEndpointVolumeCallback() : _cRef(1) AudioEndpointVolumeCallback() : _cRef(1)
{ {
} }
/// d'tor
~AudioEndpointVolumeCallback() ~AudioEndpointVolumeCallback()
{ {
} }
/// @return volume
float getVolume() float getVolume()
{ {
return volume_; return volume_;
} }
/// @return if muted
bool getMuted() bool getMuted()
{ {
return muted_; return muted_;
@ -137,11 +154,13 @@ public:
// IUnknown methods -- AddRef, Release, and QueryInterface // IUnknown methods -- AddRef, Release, and QueryInterface
/// documentation missing
ULONG STDMETHODCALLTYPE AddRef() ULONG STDMETHODCALLTYPE AddRef()
{ {
return InterlockedIncrement(&_cRef); return InterlockedIncrement(&_cRef);
} }
/// documentation missing
ULONG STDMETHODCALLTYPE Release() ULONG STDMETHODCALLTYPE Release()
{ {
ULONG ulRef = InterlockedDecrement(&_cRef); ULONG ulRef = InterlockedDecrement(&_cRef);
@ -152,6 +171,7 @@ public:
return ulRef; return ulRef;
} }
/// documentation missing
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface)
{ {
if (IID_IUnknown == riid) if (IID_IUnknown == riid)
@ -175,6 +195,7 @@ public:
// Callback method for endpoint-volume-change notifications. // Callback method for endpoint-volume-change notifications.
/// documentation missing
HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify); HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify);
}; };
@ -184,14 +205,17 @@ static constexpr auto WASAPI = "wasapi";
class WASAPIPlayer : public Player class WASAPIPlayer : public Player
{ {
public: public:
/// c'tor
WASAPIPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream); WASAPIPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
/// d'tor
virtual ~WASAPIPlayer(); virtual ~WASAPIPlayer();
/// @return list of available PCM devices
static std::vector<PcmDevice> pcm_list(); static std::vector<PcmDevice> pcm_list();
protected: protected:
virtual void worker(); void worker() override;
virtual bool needsThread() const override bool needsThread() const override
{ {
return true; return true;
} }
@ -206,5 +230,3 @@ private:
#pragma warning(pop) #pragma warning(pop)
} // namespace player } // namespace player
#endif

View file

@ -45,11 +45,14 @@
class Stream class Stream
{ {
public: public:
/// c'tor
Stream(const SampleFormat& in_format, const SampleFormat& out_format); Stream(const SampleFormat& in_format, const SampleFormat& out_format);
/// d'tor
virtual ~Stream() = default; virtual ~Stream() = default;
/// Adds PCM data to the queue /// Adds PCM data to the queue
void addChunk(std::unique_ptr<msg::PcmChunk> chunk); void addChunk(std::unique_ptr<msg::PcmChunk> chunk);
/// Remove all chunks from the queue
void clearChunks(); void clearChunks();
/// Get PCM data, which will be played out in "outputBufferDacTime" time /// Get PCM data, which will be played out in "outputBufferDacTime" time
@ -68,11 +71,13 @@ public:
/// "Server buffer": playout latency, e.g. 1000ms /// "Server buffer": playout latency, e.g. 1000ms
void setBufferLen(size_t bufferLenMs); void setBufferLen(size_t bufferLenMs);
/// @return sampleformat
const SampleFormat& getFormat() const const SampleFormat& getFormat() const
{ {
return format_; return format_;
} }
/// @return if chunk was avabilable within @p timeout
bool waitForChunk(const std::chrono::milliseconds& timeout) const; bool waitForChunk(const std::chrono::milliseconds& timeout) const;
private: private:

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2023 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -40,15 +40,19 @@
class TimeProvider class TimeProvider
{ {
public: public:
/// @return singleton
static TimeProvider& getInstance() static TimeProvider& getInstance()
{ {
static TimeProvider instance; static TimeProvider instance;
return instance; return instance;
} }
/// Set diff to server
void setDiffToServer(double ms); void setDiffToServer(double ms);
/// Set diff from round-trip-times client-to-server and server-to-client
void setDiff(const tv& c2s, const tv& s2c); void setDiff(const tv& c2s, const tv& s2c);
/// @return time diff to server
template <typename T> template <typename T>
inline T getDiffToServer() const inline T getDiffToServer() const
{ {
@ -60,22 +64,26 @@ public:
long getDiffToServerMs(); long getDiffToServerMs();
*/ */
/// @return time since epoch
template <typename T> template <typename T>
static T sinceEpoche(const chronos::time_point_clk& point) static T sinceEpoche(const chronos::time_point_clk& point)
{ {
return std::chrono::duration_cast<T>(point.time_since_epoch()); return std::chrono::duration_cast<T>(point.time_since_epoch());
} }
/// @return time_point from @p timeval
static chronos::time_point_clk toTimePoint(const tv& timeval) static chronos::time_point_clk toTimePoint(const tv& timeval)
{ {
return chronos::time_point_clk(chronos::usec(timeval.usec) + chronos::sec(timeval.sec)); return chronos::time_point_clk(chronos::usec(timeval.usec) + chronos::sec(timeval.sec));
} }
/// @return local time
inline static chronos::time_point_clk now() inline static chronos::time_point_clk now()
{ {
return chronos::clk::now(); return chronos::clk::now();
} }
/// @return server time
inline static chronos::time_point_clk serverNow() inline static chronos::time_point_clk serverNow()
{ {
return chronos::clk::now() + TimeProvider::getInstance().getDiffToServer<chronos::usec>(); return chronos::clk::now() + TimeProvider::getInstance().getDiffToServer<chronos::usec>();

View file

@ -54,6 +54,7 @@ public:
return static_cast<uint32_t>(sizeof(uint32_t) + msg.dump().size()); return static_cast<uint32_t>(sizeof(uint32_t) + msg.dump().size());
} }
/// the json message payload
json msg; json msg;

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -39,18 +39,21 @@ namespace msg
class WireChunk : public BaseMessage class WireChunk : public BaseMessage
{ {
public: public:
explicit WireChunk(uint32_t size = 0) : BaseMessage(message_type::kWireChunk), payloadSize(size), payload(nullptr) /// c'tor
explicit WireChunk(uint32_t size = 0) : BaseMessage(message_type::kWireChunk), payloadSize(size)
{ {
if (size > 0) if (size > 0)
payload = static_cast<char*>(malloc(size * sizeof(char))); payload = static_cast<char*>(malloc(size * sizeof(char)));
} }
/// c'tor
WireChunk(const WireChunk& wireChunk) : BaseMessage(message_type::kWireChunk), timestamp(wireChunk.timestamp), payloadSize(wireChunk.payloadSize) WireChunk(const WireChunk& wireChunk) : BaseMessage(message_type::kWireChunk), timestamp(wireChunk.timestamp), payloadSize(wireChunk.payloadSize)
{ {
payload = static_cast<char*>(malloc(payloadSize)); payload = static_cast<char*>(malloc(payloadSize));
memcpy(payload, wireChunk.payload, payloadSize); memcpy(payload, wireChunk.payload, payloadSize);
} }
/// d'tor
~WireChunk() override ~WireChunk() override
{ {
free(payload); free(payload);
@ -68,15 +71,17 @@ public:
return sizeof(tv) + sizeof(int32_t) + payloadSize; return sizeof(tv) + sizeof(int32_t) + payloadSize;
} }
/// @return playout time of the chunk (server time)
virtual chronos::time_point_clk start() const virtual chronos::time_point_clk start() const
{ {
return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec)); return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec));
} }
tv timestamp; tv timestamp; ///< playout timestamp (server time)
uint32_t payloadSize; uint32_t payloadSize; ///< size of the payload
char* payload; char* payload{nullptr}; ///< raw chunk payload
/// @return tuple of payload and payload size
template <typename T> template <typename T>
std::pair<T*, size_t> getPayload() const std::pair<T*, size_t> getPayload() const
{ {

View file

@ -30,6 +30,12 @@ template <typename T>
class Queue class Queue
{ {
public: public:
/// c'tor
Queue() = default;
Queue(const Queue&) = delete; ///< disable copying
Queue& operator=(const Queue&) = delete; ///< disable assignment
/// @return next element, delete from queue
T pop() T pop()
{ {
std::unique_lock<std::mutex> mlock(mutex_); std::unique_lock<std::mutex> mlock(mutex_);
@ -41,6 +47,7 @@ public:
return val; return val;
} }
/// abort wait
void abort_wait() void abort_wait()
{ {
{ {
@ -50,6 +57,7 @@ public:
cond_.notify_one(); cond_.notify_one();
} }
/// wait for @p timeout for new element, @return if an element has been added
bool wait_for(const std::chrono::microseconds& timeout) const bool wait_for(const std::chrono::microseconds& timeout) const
{ {
std::unique_lock<std::mutex> mlock(mutex_); std::unique_lock<std::mutex> mlock(mutex_);
@ -60,6 +68,7 @@ public:
return !queue_.empty() && !abort_; return !queue_.empty() && !abort_;
} }
/// @return if an element has been returned in @p item within @p timeout
bool try_pop(T& item, const std::chrono::microseconds& timeout = std::chrono::microseconds(0)) bool try_pop(T& item, const std::chrono::microseconds& timeout = std::chrono::microseconds(0))
{ {
std::unique_lock<std::mutex> mlock(mutex_); std::unique_lock<std::mutex> mlock(mutex_);
@ -79,6 +88,7 @@ public:
return true; return true;
} }
/// return next element in @p item, wait for an element if queue is empty
void pop(T& item) void pop(T& item)
{ {
std::unique_lock<std::mutex> mlock(mutex_); std::unique_lock<std::mutex> mlock(mutex_);
@ -89,6 +99,7 @@ public:
queue_.pop_front(); queue_.pop_front();
} }
/// Add @p item to the queue
void push_front(const T& item) void push_front(const T& item)
{ {
{ {
@ -98,6 +109,7 @@ public:
cond_.notify_one(); cond_.notify_one();
} }
/// return a copy of the next element in @p copy, @return false if the queue is empty
bool back_copy(T& copy) bool back_copy(T& copy)
{ {
std::lock_guard<std::mutex> mlock(mutex_); std::lock_guard<std::mutex> mlock(mutex_);
@ -107,6 +119,7 @@ public:
return true; return true;
} }
/// return a copy of the last element in @p copy, @return false if the queue is empty
bool front_copy(T& copy) bool front_copy(T& copy)
{ {
std::lock_guard<std::mutex> mlock(mutex_); std::lock_guard<std::mutex> mlock(mutex_);
@ -116,6 +129,7 @@ public:
return true; return true;
} }
/// Add element @p item at the front of the queue
void push_front(T&& item) void push_front(T&& item)
{ {
{ {
@ -125,6 +139,7 @@ public:
cond_.notify_one(); cond_.notify_one();
} }
/// Add element @p item at the end of the queue
void push(const T& item) void push(const T& item)
{ {
{ {
@ -134,6 +149,7 @@ public:
cond_.notify_one(); cond_.notify_one();
} }
/// Add element @p item at the end of the queue
void push(T&& item) void push(T&& item)
{ {
{ {
@ -143,21 +159,19 @@ public:
cond_.notify_one(); cond_.notify_one();
} }
/// @return number of elements in the queue
size_t size() const size_t size() const
{ {
std::lock_guard<std::mutex> mlock(mutex_); std::lock_guard<std::mutex> mlock(mutex_);
return queue_.size(); return queue_.size();
} }
/// @return if the queue is empty
bool empty() const bool empty() const
{ {
return (size() == 0); return (size() == 0);
} }
Queue() = default;
Queue(const Queue&) = delete; // disable copying
Queue& operator=(const Queue&) = delete; // disable assignment
private: private:
std::deque<T> queue_; std::deque<T> queue_;
mutable std::atomic<bool> abort_; mutable std::atomic<bool> abort_;

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,7 +21,9 @@
// local headers // local headers
#include "common/aixlog.hpp" #include "common/aixlog.hpp"
#ifndef HAS_SOXR
#include "common/snap_exception.hpp" #include "common/snap_exception.hpp"
#endif
// standard headers // standard headers
#include <cmath> #include <cmath>
@ -195,7 +197,7 @@ std::shared_ptr<msg::PcmChunk> Resampler::resample(const msg::PcmChunk& chunk)
} }
shared_ptr<msg::PcmChunk> Resampler::resample(shared_ptr<msg::PcmChunk> chunk) std::shared_ptr<msg::PcmChunk> Resampler::resample(std::shared_ptr<msg::PcmChunk> chunk)
{ {
#ifndef HAS_SOXR #ifndef HAS_SOXR
return chunk; return chunk;

View file

@ -38,57 +38,70 @@
class SampleFormat class SampleFormat
{ {
public: public:
/// c'tor
SampleFormat(); SampleFormat();
/// c'tor
SampleFormat(const std::string& format); SampleFormat(const std::string& format);
/// c'tor
SampleFormat(uint32_t rate, uint16_t bits, uint16_t channels); SampleFormat(uint32_t rate, uint16_t bits, uint16_t channels);
/// @return sampleformat as string rate:bits::channels
std::string toString() const; std::string toString() const;
/// Set @p format (rate:bits::channels)
void setFormat(const std::string& format); void setFormat(const std::string& format);
/// Set format
void setFormat(uint32_t rate, uint16_t bits, uint16_t channels); void setFormat(uint32_t rate, uint16_t bits, uint16_t channels);
/// @return if has format
bool isInitialized() const bool isInitialized() const
{ {
return ((rate_ != 0) || (bits_ != 0) || (channels_ != 0)); return ((rate_ != 0) || (bits_ != 0) || (channels_ != 0));
} }
/// @return rate
uint32_t rate() const uint32_t rate() const
{ {
return rate_; return rate_;
} }
/// @return bits
uint16_t bits() const uint16_t bits() const
{ {
return bits_; return bits_;
} }
/// @return channels
uint16_t channels() const uint16_t channels() const
{ {
return channels_; return channels_;
} }
// size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits) /// @return size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits)
uint16_t sampleSize() const uint16_t sampleSize() const
{ {
return sample_size_; return sample_size_;
} }
// size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit) /// @return size in [bytes] of a frame (sum of sample sizes = num-channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit)
uint16_t frameSize() const uint16_t frameSize() const
{ {
return frame_size_; return frame_size_;
} }
/// @return rate per ms (= rate / 1000)
inline double msRate() const inline double msRate() const
{ {
return static_cast<double>(rate_) / 1000.; return static_cast<double>(rate_) / 1000.;
} }
/// @return rate per micro seconds (= rate / 1000000)
inline double usRate() const inline double usRate() const
{ {
return static_cast<double>(rate_) / 1000000.; return static_cast<double>(rate_) / 1000000.;
} }
/// @return rate per nano seconds (= rate / 1000000000)
inline double nsRate() const inline double nsRate() const
{ {
return static_cast<double>(rate_) / 1000000000.; return static_cast<double>(rate_) / 1000000000.;

View file

@ -79,7 +79,7 @@ public:
/// Authenticate with basic scheme /// Authenticate with basic scheme
ErrorCode authenticateBasic(const std::string& credentials); ErrorCode authenticateBasic(const std::string& credentials);
/// Authenticate with <user>:<password> /// Authenticate with user:password
ErrorCode authenticatePlain(const std::string& user_password); ErrorCode authenticatePlain(const std::string& user_password);
/// Authenticate with bearer scheme /// Authenticate with bearer scheme
// ErrorCode authenticateBearer(const std::string& token); // ErrorCode authenticateBearer(const std::string& token);
@ -88,7 +88,7 @@ public:
/// Authenticate with scheme ("basic" or "bearer") and auth param /// Authenticate with scheme ("basic" or "bearer") and auth param
ErrorCode authenticate(const std::string& scheme, const std::string& param); ErrorCode authenticate(const std::string& scheme, const std::string& param);
/// @return JWS token for @p username and @p password // @return JWS token for @p username and @p password
// ErrorOr<std::string> getToken(const std::string& username, const std::string& password) const; // ErrorOr<std::string> getToken(const std::string& username, const std::string& password) const;
/// @return if the authenticated user has permission to access @p ressource /// @return if the authenticated user has permission to access @p ressource
bool hasPermission(const std::string& resource) const; bool hasPermission(const std::string& resource) const;

View file

@ -146,7 +146,7 @@ void ControlServer::onMessageReceived(std::shared_ptr<ControlSession> session, c
} }
void ControlServer::onNewSession(shared_ptr<ControlSession> session) void ControlServer::onNewSession(std::shared_ptr<ControlSession> session)
{ {
std::lock_guard<std::recursive_mutex> mlock(session_mutex_); std::lock_guard<std::recursive_mutex> mlock(session_mutex_);
session->start(); session->start();

View file

@ -25,6 +25,7 @@
#include "FLAC/stream_encoder.h" #include "FLAC/stream_encoder.h"
// standard headers // standard headers
#include <array>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>

View file

@ -76,6 +76,7 @@ struct ServerSettings
/// c'tor /// c'tor
Authorization() = default; Authorization() = default;
/// Init with @p conf_roles and @p conf_users
void init(const std::vector<std::string>& conf_roles, const std::vector<std::string>& conf_users) void init(const std::vector<std::string>& conf_roles, const std::vector<std::string>& conf_users)
{ {
roles.clear(); roles.clear();

View file

@ -87,8 +87,9 @@ public:
buffer_ = boost::asio::buffer(message_->data); buffer_ = boost::asio::buffer(message_->data);
} }
// Implement the ConstBufferSequence requirements. /// const buffer.
using value_type = boost::asio::const_buffer; using value_type = boost::asio::const_buffer;
/// const buffer iterator
using const_iterator = const boost::asio::const_buffer*; using const_iterator = const boost::asio::const_buffer*;
/// begin iterator /// begin iterator

View file

@ -35,11 +35,11 @@ namespace streamreader
/// Tage entry?? /// Tage entry??
struct TageEntry struct TageEntry
{ {
std::string code; std::string code; ///< code
std::string type; std::string type; ///< type
std::string data; std::string data; ///< data
bool isBase64{false}; bool isBase64{false}; ///< is base64?
int length{0}; int length{0}; ///< length
}; };
/// Starts shairport-sync and reads PCM data from stdout /// Starts shairport-sync and reads PCM data from stdout

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -47,7 +47,7 @@ public:
void start() override; void start() override;
void stop() override; void stop() override;
protected: private:
void do_read(); void do_read();
void initAlsa(); void initAlsa();
void uninitAlsa(); void uninitAlsa();

View file

@ -45,7 +45,9 @@ public:
/// c'tor. Encoded PCM data is passed to the PipeListener /// c'tor. Encoded PCM data is passed to the PipeListener
AsioStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); AsioStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri);
/// Start the stream reader, init the encoder and optionally the stream control
void start() override; void start() override;
/// Stop the stream reader
void stop() override; void stop() override;
protected: protected:

View file

@ -30,7 +30,9 @@ namespace detail
struct category : public std::error_category struct category : public std::error_category
{ {
public: public:
/// @return category name
const char* name() const noexcept override; const char* name() const noexcept override;
/// @return error message for @p value
std::string message(int value) const override; std::string message(int value) const override;
}; };

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
Copyright (C) 2024 Marcus Weseloh <marcus@weseloh.cc> Copyright (C) 2024 Marcus Weseloh <marcus@weseloh.cc>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -49,7 +49,7 @@ public:
void start() override; void start() override;
void stop() override; void stop() override;
protected: private:
bool openJackConnection(); bool openJackConnection();
void closeJackConnection(); void closeJackConnection();
bool createJackPorts(); bool createJackPorts();

View file

@ -40,7 +40,7 @@ public:
/// ctor. Encoded PCM data is passed to the PipeListener /// ctor. Encoded PCM data is passed to the PipeListener
LibrespotStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); LibrespotStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri);
protected: private:
bool killall_; bool killall_;
void onStderrMsg(const std::string& line) override; void onStderrMsg(const std::string& line) override;

View file

@ -114,13 +114,13 @@ public:
virtual void onStateChanged(const PcmStream* pcmStream, ReaderState state) = 0; virtual void onStateChanged(const PcmStream* pcmStream, ReaderState state) = 0;
/// Chunk @p chunk of @p pcmStream has read /// Chunk @p chunk of @p pcmStream has read
virtual void onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& chunk) = 0; virtual void onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& chunk) = 0;
/// Chunk @p chunk with duration @p duration of stream @pcmStream has been encoded /// Chunk @p chunk with duration @p duration of stream @p pcmStream has been encoded
virtual void onChunkEncoded(const PcmStream* pcmStream, std::shared_ptr<msg::PcmChunk> chunk, double duration) = 0; virtual void onChunkEncoded(const PcmStream* pcmStream, std::shared_ptr<msg::PcmChunk> chunk, double duration) = 0;
/// Stream @p pcmStream muissed to read audio with duration @p ms /// Stream @p pcmStream muissed to read audio with duration @p ms
virtual void onResync(const PcmStream* pcmStream, double ms) = 0; virtual void onResync(const PcmStream* pcmStream, double ms) = 0;
}; };
/// Handler function for command results
using ResultHandler = std::function<void(const snapcast::ErrorCode& ec)>; using ResultHandler = std::function<void(const snapcast::ErrorCode& ec)>;
/// c'tor. Encoded PCM data is passed to the PcmStream::Listener /// c'tor. Encoded PCM data is passed to the PcmStream::Listener

View file

@ -42,29 +42,45 @@ namespace streamreader
{ {
/// Stream control base class
/// Controls a stream via "command" (play, pause, next, ...)
/// Provides status information (playback status, position, metadata, ...)
class StreamControl class StreamControl
{ {
public: public:
/// Request handler coming from the plugin
using OnRequest = std::function<void(const jsonrpcpp::Request& response)>; using OnRequest = std::function<void(const jsonrpcpp::Request& response)>;
/// Notification handler coming from the plugin
using OnNotification = std::function<void(const jsonrpcpp::Notification& response)>; using OnNotification = std::function<void(const jsonrpcpp::Notification& response)>;
/// Response handler coming from the plugin
using OnResponse = std::function<void(const jsonrpcpp::Response& response)>; using OnResponse = std::function<void(const jsonrpcpp::Response& response)>;
/// Log handler coming from the plugin
using OnLog = std::function<void(std::string message)>; using OnLog = std::function<void(std::string message)>;
/// c'tor
explicit StreamControl(const boost::asio::any_io_executor& executor); explicit StreamControl(const boost::asio::any_io_executor& executor);
/// d'tor
virtual ~StreamControl() = default; virtual ~StreamControl() = default;
/// Start the stream control, calls abstract "doStart"
void start(const std::string& stream_id, const ServerSettings& server_setttings, const OnNotification& notification_handler, void start(const std::string& stream_id, const ServerSettings& server_setttings, const OnNotification& notification_handler,
const OnRequest& request_handler, const OnLog& log_handler); const OnRequest& request_handler, const OnLog& log_handler);
/// Issue a command to the stream, calls abstract "doCommand"
void command(const jsonrpcpp::Request& request, const OnResponse& response_handler); void command(const jsonrpcpp::Request& request, const OnResponse& response_handler);
protected: protected:
/// abstract "command" interface: send a json request to the plugin
virtual void doCommand(const jsonrpcpp::Request& request) = 0; virtual void doCommand(const jsonrpcpp::Request& request) = 0;
/// abstract "start" interface: starts and initializes the plugin
virtual void doStart(const std::string& stream_id, const ServerSettings& server_setttings) = 0; virtual void doStart(const std::string& stream_id, const ServerSettings& server_setttings) = 0;
/// a @p json message has been received from the plugin
void onReceive(const std::string& json); void onReceive(const std::string& json);
/// a @p message log request has been received from the plugin
void onLog(std::string message); void onLog(std::string message);
/// asio executor
boost::asio::any_io_executor executor_; boost::asio::any_io_executor executor_;
private: private:
@ -76,10 +92,14 @@ private:
}; };
/// Script based stream control
/// Executes a script (e.g. Python) and communicates via stdout/stdin with the script
class ScriptStreamControl : public StreamControl class ScriptStreamControl : public StreamControl
{ {
public: public:
/// c'tor
ScriptStreamControl(const boost::asio::any_io_executor& executor, const std::filesystem::path& plugin_dir, std::string script, std::string params); ScriptStreamControl(const boost::asio::any_io_executor& executor, const std::filesystem::path& plugin_dir, std::string script, std::string params);
/// d'tor
virtual ~ScriptStreamControl() = default; virtual ~ScriptStreamControl() = default;
private: private:

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -39,10 +39,10 @@ namespace streamreader
class TcpStream : public AsioStream<tcp::socket> class TcpStream : public AsioStream<tcp::socket>
{ {
public: public:
/// ctor. Encoded PCM data is passed to the PipeListener /// c'tor. Encoded PCM data is passed to the PipeListener
TcpStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); TcpStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri);
protected: private:
void connect() override; void connect() override;
void disconnect() override; void disconnect() override;
std::unique_ptr<tcp::acceptor> acceptor_; std::unique_ptr<tcp::acceptor> acceptor_;

View file

@ -36,10 +36,11 @@ class Watchdog;
class Watchdog class Watchdog
{ {
public: public:
/// Watchdog timeout handler
using TimeoutHandler = std::function<void(std::chrono::milliseconds ms)>; using TimeoutHandler = std::function<void(std::chrono::milliseconds ms)>;
/// c'tor /// c'tor
explicit Watchdog(const boost::asio::any_io_executor& executor);//, WatchdogListener* listener = nullptr); explicit Watchdog(const boost::asio::any_io_executor& executor);
/// d'tor /// d'tor
virtual ~Watchdog(); virtual ~Watchdog();