Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 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 60 : 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 60 : 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, 149 : const std::string& dev, 150 : snd_pcm_stream_t stream, 151 : AudioFormat& format); 152 : 153 : /** 154 : * Number of audio cards on which capture stream has been opened 155 : */ 156 : int indexIn_; 157 : 158 : /** 159 : * Number of audio cards on which playback stream has been opened 160 : */ 161 : int indexOut_; 162 : 163 : /** 164 : * Number of audio cards on which ringtone stream has been opened 165 : */ 166 : int indexRing_; 167 : 168 : NON_COPYABLE(AlsaLayer); 169 : 170 : /** 171 : * Drop the pending frames and close the capture device 172 : * ALSA Library API 173 : */ 174 : void closeCaptureStream(); 175 : void stopCaptureStream(); 176 : void startCaptureStream(); 177 : void prepareCaptureStream(); 178 : 179 : void closePlaybackStream(); 180 : void stopPlaybackStream(); 181 : void startPlaybackStream(); 182 : void closeRingtoneStream(); 183 : 184 : bool alsa_set_params(snd_pcm_t* pcm_handle, AudioFormat& format); 185 : 186 : void startThread(); 187 : void stopThread(); 188 : 189 : /** 190 : * Copy a data buffer in the internal ring buffer 191 : * ALSA Library API 192 : * @param buffer The non-interleaved data to be copied 193 : * @param frames Frames in the buffer 194 : */ 195 : void write(const AudioFrame& buffer, snd_pcm_t* handle); 196 : 197 : /** 198 : * Read data from the internal ring buffer 199 : * ALSA Library API 200 : * @param buffer The buffer to stock the read data 201 : * @param frames The number of frames to get 202 : * @return int The number of frames actually read 203 : */ 204 : std::unique_ptr<AudioFrame> read(unsigned frames); 205 : 206 : virtual void updatePreference(AudioPreference& pref, int index, AudioDeviceType type); 207 : 208 : /** 209 : * Handles to manipulate playback stream 210 : * ALSA Library API 211 : */ 212 : snd_pcm_t* playbackHandle_ {nullptr}; 213 : 214 : /** 215 : * Handles to manipulate ringtone stream 216 : * 217 : */ 218 : snd_pcm_t* ringtoneHandle_ {nullptr}; 219 : 220 : /** 221 : * Handles to manipulate capture stream 222 : * ALSA Library API 223 : */ 224 : snd_pcm_t* captureHandle_ {nullptr}; 225 : 226 : /** 227 : * name of the alsa audio plugin used 228 : */ 229 : std::string audioPlugin_; 230 : 231 : bool is_capture_prepared_ {false}; 232 : bool is_playback_running_ {false}; 233 : bool is_capture_running_ {false}; 234 : bool is_playback_open_ {false}; 235 : bool is_capture_open_ {false}; 236 : 237 : std::atomic_bool running_ {false}; 238 : std::thread audioThread_; 239 : }; 240 : 241 : } // namespace jami