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