Line data Source code
1 : /* 2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : #pragma once 18 : 19 : #include "audio/audiolayer.h" 20 : #include "noncopyable.h" 21 : 22 : #include <alsa/asoundlib.h> 23 : 24 : #include <memory> 25 : #include <thread> 26 : 27 : #define PCM_DMIX "plug:dmix" /** Alsa plugin for software mixing */ 28 : 29 : // Error codes for error handling 30 : #define ALSA_CAPTURE_DEVICE 0x0001 /** Error while opening capture device */ 31 : #define ALSA_PLAYBACK_DEVICE 0x0010 /** Error while opening playback device */ 32 : 33 : /** 34 : * @file AlsaLayer.h 35 : * @brief Main sound class. Manages the data transfers between the application and the hardware. 36 : */ 37 : 38 : namespace jami { 39 : 40 : class AlsaThread; 41 : class RingBuffer; 42 : class AudioPreference; 43 : 44 : /** Associate a sound card index to its string description */ 45 : typedef std::pair<int, std::string> HwIDPair; 46 : 47 : class AlsaLayer : public AudioLayer 48 : { 49 : public: 50 : /** 51 : * Constructor 52 : */ 53 : AlsaLayer(const AudioPreference& pref); 54 : 55 : /** 56 : * Destructor 57 : */ 58 : ~AlsaLayer(); 59 : 60 : /** 61 : * Start the capture stream and prepare the playback stream. 62 : * The playback starts accordingly to its threshold 63 : * ALSA Library API 64 : */ 65 : virtual void startStream(AudioDeviceType stream = AudioDeviceType::ALL); 66 : 67 : /** 68 : * Stop the playback and capture streams. 69 : * Drops the pending frames and put the capture and playback handles to PREPARED state 70 : * ALSA Library API 71 : */ 72 : virtual void stopStream(AudioDeviceType stream = AudioDeviceType::ALL); 73 : 74 : /** 75 : * Concatenate two strings. Used to build a valid pcm device name. 76 : * @param plugin the alsa PCM name 77 : * @param card the sound card number 78 : * @return std::string the concatenated string 79 : */ 80 : std::string buildDeviceTopo(const std::string& plugin, int card); 81 : 82 : /** 83 : * Scan the sound card available on the system 84 : * @return std::vector<std::string> The vector containing the string description of the card 85 : */ 86 : virtual std::vector<std::string> getCaptureDeviceList() const; 87 : virtual std::vector<std::string> getPlaybackDeviceList() const; 88 : 89 : /** 90 : * Check if the given index corresponds to an existing sound card and supports the specified 91 : *streaming mode 92 : * @param card An index 93 : * @param stream The stream mode 94 : * AudioDeviceType::CAPTURE 95 : * AudioDeviceType::PLAYBACK 96 : * AudioDeviceType::RINGTONE 97 : * @return bool True if it exists and supports the mode 98 : * false otherwise 99 : */ 100 : static bool soundCardIndexExists(int card, AudioDeviceType stream); 101 : 102 : /** 103 : * An index is associated with its string description 104 : * @param description The string description 105 : * @return int Its index 106 : */ 107 : int getAudioDeviceIndex(const std::string& description, AudioDeviceType type) const; 108 : std::string getAudioDeviceName(int index, AudioDeviceType type) const; 109 : 110 : void playback(); 111 : void ringtone(); 112 : void capture(); 113 : 114 : /** 115 : * Get the index of the audio card for capture 116 : * @return int The index of the card used for capture 117 : * 0 for the first available card on the system, 1 ... 118 : */ 119 0 : virtual int getIndexCapture() const { return indexIn_; } 120 : 121 : /** 122 : * Get the index of the audio card for playback 123 : * @return int The index of the card used for playback 124 : * 0 for the first available card on the system, 1 ... 125 : */ 126 55 : virtual int getIndexPlayback() const { return indexOut_; } 127 : 128 : /** 129 : * Get the index of the audio card for ringtone (could be differnet from playback) 130 : * @return int The index of the card used for ringtone 131 : * 0 for the first available card on the system, 1 ... 132 : */ 133 55 : virtual int getIndexRingtone() const { return indexRing_; } 134 : 135 : void run(); 136 : 137 : private: 138 : /** 139 : * Returns a map of audio device hardware description and index 140 : */ 141 : std::vector<HwIDPair> getAudioDeviceIndexMap(bool getCapture) const; 142 : 143 : /** 144 : * Calls snd_pcm_open and retries if device is busy, since dmix plugin 145 : * will often hold on to a device temporarily after it has been opened 146 : * and closed. 147 : */ 148 : bool openDevice(snd_pcm_t** pcm, const std::string& dev, snd_pcm_stream_t stream, AudioFormat& format); 149 : 150 : /** 151 : * Number of audio cards on which capture stream has been opened 152 : */ 153 : int indexIn_; 154 : 155 : /** 156 : * Number of audio cards on which playback stream has been opened 157 : */ 158 : int indexOut_; 159 : 160 : /** 161 : * Number of audio cards on which ringtone stream has been opened 162 : */ 163 : int indexRing_; 164 : 165 : NON_COPYABLE(AlsaLayer); 166 : 167 : /** 168 : * Drop the pending frames and close the capture device 169 : * ALSA Library API 170 : */ 171 : void closeCaptureStream(); 172 : void stopCaptureStream(); 173 : void startCaptureStream(); 174 : void prepareCaptureStream(); 175 : 176 : void closePlaybackStream(); 177 : void stopPlaybackStream(); 178 : void startPlaybackStream(); 179 : void closeRingtoneStream(); 180 : 181 : bool alsa_set_params(snd_pcm_t* pcm_handle, AudioFormat& format); 182 : 183 : void startThread(); 184 : void stopThread(); 185 : 186 : /** 187 : * Copy a data buffer in the internal ring buffer 188 : * ALSA Library API 189 : * @param buffer The non-interleaved data to be copied 190 : * @param frames Frames in the buffer 191 : */ 192 : void write(const AudioFrame& buffer, snd_pcm_t* handle); 193 : 194 : /** 195 : * Read data from the internal ring buffer 196 : * ALSA Library API 197 : * @param buffer The buffer to stock the read data 198 : * @param frames The number of frames to get 199 : * @return int The number of frames actually read 200 : */ 201 : std::unique_ptr<AudioFrame> read(unsigned frames); 202 : 203 : virtual void updatePreference(AudioPreference& pref, int index, AudioDeviceType type); 204 : 205 : /** 206 : * Handles to manipulate playback stream 207 : * ALSA Library API 208 : */ 209 : snd_pcm_t* playbackHandle_ {nullptr}; 210 : 211 : /** 212 : * Handles to manipulate ringtone stream 213 : * 214 : */ 215 : snd_pcm_t* ringtoneHandle_ {nullptr}; 216 : 217 : /** 218 : * Handles to manipulate capture stream 219 : * ALSA Library API 220 : */ 221 : snd_pcm_t* captureHandle_ {nullptr}; 222 : 223 : /** 224 : * name of the alsa audio plugin used 225 : */ 226 : std::string audioPlugin_; 227 : 228 : bool is_capture_prepared_ {false}; 229 : bool is_playback_running_ {false}; 230 : bool is_capture_running_ {false}; 231 : bool is_playback_open_ {false}; 232 : bool is_capture_open_ {false}; 233 : 234 : std::atomic_bool running_ {false}; 235 : std::thread audioThread_; 236 : }; 237 : 238 : } // namespace jami