1 /***
2     This file is part of snapcast
3     Copyright (C) 2014-2020  Johannes Pohl
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 ***/
18 
19 #ifndef ALSA_PLAYER_HPP
20 #define ALSA_PLAYER_HPP
21 
22 #include "player.hpp"
23 
24 #include <alsa/asoundlib.h>
25 #include <boost/optional.hpp>
26 #include <chrono>
27 
28 
29 namespace player
30 {
31 
32 static constexpr auto ALSA = "alsa";
33 
34 /// Audio Player
35 /**
36  * Audio player implementation using Alsa
37  */
38 class AlsaPlayer : public Player
39 {
40 public:
41     AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
42     ~AlsaPlayer() override;
43 
44     void start() override;
45     void stop() override;
46 
47     /// List the system's audio output devices
48     static std::vector<PcmDevice> pcm_list();
49 
50 protected:
51     void worker() override;
52     bool needsThread() const override;
53 
54 private:
55     /// initialize alsa and the mixer (if neccessary)
56     void initAlsa();
57     /// free alsa and optionally the mixer
58     /// @param uninit_mixer free the mixer
59     void uninitAlsa(bool uninit_mixer);
60     bool getAvailDelay(snd_pcm_sframes_t& avail, snd_pcm_sframes_t& delay);
61 
62     void initMixer();
63     void uninitMixer();
64 
65     bool getHardwareVolume(double& volume, bool& muted) override;
66     void setHardwareVolume(double volume, bool muted) override;
67 
68     void waitForEvent();
69 
70     snd_pcm_t* handle_;
71     snd_ctl_t* ctl_;
72 
73     snd_mixer_t* mixer_;
74     snd_mixer_elem_t* elem_;
75     std::string mixer_name_;
76     std::string mixer_device_;
77 
78     std::unique_ptr<pollfd, std::function<void(pollfd*)>> fd_;
79     std::vector<char> buffer_;
80     snd_pcm_uframes_t frames_;
81     boost::asio::posix::stream_descriptor sd_;
82     std::chrono::time_point<std::chrono::steady_clock> last_change_;
83     std::recursive_mutex mutex_;
84     boost::asio::steady_timer timer_;
85 
86     boost::optional<std::chrono::microseconds> buffer_time_;
87     boost::optional<uint32_t> periods_;
88 };
89 
90 } // namespace player
91 
92 #endif