1 /***************************************************************************
2  *                                                                         *
3  *   LinuxSampler - modular, streaming capable sampler                     *
4  *                                                                         *
5  *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6  *   Copyright (C) 2005 - 2020 Christian Schoenebeck                       *
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 2 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., 59 Temple Place, Suite 330, Boston,                 *
21  *   MA  02111-1307  USA                                                   *
22  ***************************************************************************/
23 
24 #include "AudioOutputDeviceAlsa.h"
25 #include "AudioOutputDeviceFactory.h"
26 
27 namespace LinuxSampler {
28 
29 // *************** ParameterCard ***************
30 // *
31 
ParameterCard()32     AudioOutputDeviceAlsa::ParameterCard::ParameterCard() : DeviceCreationParameterString() {
33         InitWithDefault(); // use default card
34     }
35 
ParameterCard(String s)36     AudioOutputDeviceAlsa::ParameterCard::ParameterCard(String s) throw (Exception) : DeviceCreationParameterString(s) {
37     }
38 
Description()39     String AudioOutputDeviceAlsa::ParameterCard::Description() {
40         return "Sound card to be used";
41     }
42 
Fix()43     bool AudioOutputDeviceAlsa::ParameterCard::Fix() {
44         return true;
45     }
46 
Mandatory()47     bool AudioOutputDeviceAlsa::ParameterCard::Mandatory() {
48         return false;
49     }
50 
DependsAsParameters()51     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterCard::DependsAsParameters() {
52         return std::map<String,DeviceCreationParameter*>(); // no dependencies
53     }
54 
DefaultAsString(std::map<String,String> Parameters)55     optional<String> AudioOutputDeviceAlsa::ParameterCard::DefaultAsString(std::map<String,String> Parameters) {
56         std::vector<String> cards = PossibilitiesAsString(Parameters);
57         if (cards.empty()) throw Exception("AudioOutputDeviceAlsa: Can't find any card");
58         return cards[0]; // first card by default
59     }
60 
PossibilitiesAsString(std::map<String,String> Parameters)61     std::vector<String> AudioOutputDeviceAlsa::ParameterCard::PossibilitiesAsString(std::map<String,String> Parameters) {
62         int err;
63         std::vector<String> CardNames;
64 
65         // iterate through all cards
66         int card_index = -1;
67         while (snd_card_next(&card_index) >= 0 && card_index >= 0) {
68             String hw_name = "hw:" + ToString(card_index);
69             snd_ctl_t* hCardCtrl;
70             if ((err = snd_ctl_open(&hCardCtrl, hw_name.c_str(), 0)) < 0) {
71                 std::cerr << "AudioOutputDeviceAlsa: Cannot open sound control for card " << card_index << " - " << snd_strerror(err) << std::endl;
72                 continue;
73             }
74 
75             // iterate through all devices of that card
76             int device_index = -1;
77             while (!snd_ctl_pcm_next_device(hCardCtrl, &device_index) && device_index >= 0) {
78                 String name = ToString(card_index) + "," + ToString(device_index);
79                 //dmsg(1,("[possibility:%s]", name.c_str()));
80                 CardNames.push_back(name);
81             }
82 
83             snd_ctl_close(hCardCtrl);
84         }
85 
86         return CardNames;
87     }
88 
OnSetValue(String s)89     void AudioOutputDeviceAlsa::ParameterCard::OnSetValue(String s) throw (Exception) {
90         // not posssible, as parameter is fix
91     }
92 
Name()93     String AudioOutputDeviceAlsa::ParameterCard::Name() {
94         return "CARD";
95     }
96 
97 
98 
99 // *************** ParameterSampleRate ***************
100 // *
101 
ParameterSampleRate()102     AudioOutputDeviceAlsa::ParameterSampleRate::ParameterSampleRate() : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate() {
103     }
104 
ParameterSampleRate(String s)105     AudioOutputDeviceAlsa::ParameterSampleRate::ParameterSampleRate(String s) : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate(s) {
106     }
107 
DependsAsParameters()108     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterSampleRate::DependsAsParameters() {
109         static ParameterCard card;
110         std::map<String,DeviceCreationParameter*> dependencies;
111         dependencies[card.Name()] = &card;
112         return dependencies;
113     }
114 
DefaultAsInt(std::map<String,String> Parameters)115     optional<int> AudioOutputDeviceAlsa::ParameterSampleRate::DefaultAsInt(std::map<String,String> Parameters) {
116         if (!Parameters.count("CARD")) return optional<int>::nothing;
117 
118         // obtain information from given sound card
119         ParameterCard card(Parameters["CARD"]);
120         String pcm_name = "hw:" + card.ValueAsString();
121         snd_pcm_t* pcm_handle = NULL;
122         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
123         snd_pcm_hw_params_t* hwparams;
124         snd_pcm_hw_params_alloca(&hwparams);
125         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
126             snd_pcm_close(pcm_handle);
127             return optional<int>::nothing;
128         }
129         uint rate = 44100;
130         if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, NULL) < 0) {
131             snd_pcm_close(pcm_handle);
132             return optional<int>::nothing;
133         }
134         snd_pcm_close(pcm_handle);
135         return rate;
136     }
137 
RangeMinAsInt(std::map<String,String> Parameters)138     optional<int> AudioOutputDeviceAlsa::ParameterSampleRate::RangeMinAsInt(std::map<String,String> Parameters) {
139         if (!Parameters.count("CARD")) return optional<int>::nothing;
140 
141         // obtain information from given sound card
142         ParameterCard card(Parameters["CARD"]);
143         String pcm_name = "hw:" + card.ValueAsString();
144         snd_pcm_t* pcm_handle = NULL;
145         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
146         snd_pcm_hw_params_t* hwparams;
147         snd_pcm_hw_params_alloca(&hwparams);
148         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
149             snd_pcm_close(pcm_handle);
150             return optional<int>::nothing;
151         }
152         uint rate;
153         if (snd_pcm_hw_params_get_rate_min(hwparams, &rate, NULL) < 0) {
154             snd_pcm_close(pcm_handle);
155             return optional<int>::nothing;
156         }
157         snd_pcm_close(pcm_handle);
158         return rate;
159     }
160 
RangeMaxAsInt(std::map<String,String> Parameters)161     optional<int> AudioOutputDeviceAlsa::ParameterSampleRate::RangeMaxAsInt(std::map<String,String> Parameters) {
162         if (!Parameters.count("CARD")) return optional<int>::nothing;
163 
164         // obtain information from given sound card
165         String pcm_name       = "hw:" + Parameters["CARD"];
166         snd_pcm_t* pcm_handle = NULL;
167         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
168         snd_pcm_hw_params_t* hwparams;
169         snd_pcm_hw_params_alloca(&hwparams);
170         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
171             snd_pcm_close(pcm_handle);
172             return optional<int>::nothing;
173         }
174         uint rate;
175         if (snd_pcm_hw_params_get_rate_max(hwparams, &rate, NULL) < 0) {
176             snd_pcm_close(pcm_handle);
177             return optional<int>::nothing;
178         }
179         snd_pcm_close(pcm_handle);
180         return rate;
181     }
182 
183 
184 
185 // *************** ParameterChannels ***************
186 // *
187 
ParameterChannels()188     AudioOutputDeviceAlsa::ParameterChannels::ParameterChannels() : AudioOutputDevice::ParameterChannels::ParameterChannels() {
189         //InitWithDefault();
190         // could not determine default value? ...
191         //if (ValueAsInt() == 0) SetValue(2); // ... then (try) a common value
192     }
193 
ParameterChannels(String s)194     AudioOutputDeviceAlsa::ParameterChannels::ParameterChannels(String s) : AudioOutputDevice::ParameterChannels::ParameterChannels(s) {
195     }
196 
DependsAsParameters()197     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterChannels::DependsAsParameters() {
198         static ParameterCard card;
199         std::map<String,DeviceCreationParameter*> dependencies;
200         dependencies[card.Name()] = &card;
201         return dependencies;
202     }
203 
DefaultAsInt(std::map<String,String> Parameters)204     optional<int> AudioOutputDeviceAlsa::ParameterChannels::DefaultAsInt(std::map<String,String> Parameters) {
205         if (!Parameters.count("CARD")) return optional<int>::nothing;
206 
207         // obtain information from given sound card
208         ParameterCard card(Parameters["CARD"]);
209         String pcm_name = "hw:" + card.ValueAsString();
210         snd_pcm_t* pcm_handle = NULL;
211         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
212         snd_pcm_hw_params_t* hwparams;
213         snd_pcm_hw_params_alloca(&hwparams);
214         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
215             snd_pcm_close(pcm_handle);
216             return optional<int>::nothing;
217         }
218         uint channels = 2;
219         if (snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &channels) < 0) {
220             snd_pcm_close(pcm_handle);
221             return optional<int>::nothing;
222         }
223         snd_pcm_close(pcm_handle);
224         return channels;
225     }
226 
RangeMinAsInt(std::map<String,String> Parameters)227     optional<int> AudioOutputDeviceAlsa::ParameterChannels::RangeMinAsInt(std::map<String,String> Parameters) {
228         uint channels = 1;
229         if (!Parameters.count("CARD")) return channels;
230 
231         // obtain information from given sound card
232         ParameterCard card(Parameters["CARD"]);
233         String pcm_name = "hw:" + card.ValueAsString();
234         snd_pcm_t* pcm_handle = NULL;
235         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return channels;
236         snd_pcm_hw_params_t* hwparams;
237         snd_pcm_hw_params_alloca(&hwparams);
238         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
239             snd_pcm_close(pcm_handle);
240             return channels;
241         }
242 
243         if (snd_pcm_hw_params_get_channels_min(hwparams, &channels) < 0) {
244             snd_pcm_close(pcm_handle);
245             return channels;
246         }
247         snd_pcm_close(pcm_handle);
248         return channels;
249     }
250 
RangeMaxAsInt(std::map<String,String> Parameters)251     optional<int> AudioOutputDeviceAlsa::ParameterChannels::RangeMaxAsInt(std::map<String,String> Parameters) {
252         uint channels = 100;
253         if (!Parameters.count("CARD")) return optional<int>::nothing;
254 
255         // obtain information from given sound card
256         String pcm_name       = "hw:" + Parameters["CARD"];
257         snd_pcm_t* pcm_handle = NULL;
258         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0)
259             return optional<int>::nothing;
260         snd_pcm_hw_params_t* hwparams;
261         snd_pcm_hw_params_alloca(&hwparams);
262         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
263             snd_pcm_close(pcm_handle);
264             return optional<int>::nothing;
265         }
266 
267         if (snd_pcm_hw_params_get_channels_max(hwparams, &channels) < 0) {
268             snd_pcm_close(pcm_handle);
269             return optional<int>::nothing;
270         }
271         snd_pcm_close(pcm_handle);
272         return channels;
273     }
274 
275 
276 
277 // *************** ParameterFragments ***************
278 // *
279 
ParameterFragments()280     AudioOutputDeviceAlsa::ParameterFragments::ParameterFragments() : DeviceCreationParameterInt() {
281         InitWithDefault();
282     }
283 
ParameterFragments(String s)284     AudioOutputDeviceAlsa::ParameterFragments::ParameterFragments(String s) throw (Exception) : DeviceCreationParameterInt(s) {
285     }
286 
Description()287     String AudioOutputDeviceAlsa::ParameterFragments::Description() {
288         return "Number of buffer fragments";
289     }
290 
Fix()291     bool AudioOutputDeviceAlsa::ParameterFragments::Fix() {
292         return true;
293     }
294 
Mandatory()295     bool AudioOutputDeviceAlsa::ParameterFragments::Mandatory() {
296         return false;
297     }
298 
DependsAsParameters()299     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterFragments::DependsAsParameters() {
300         static ParameterCard card;
301         std::map<String,DeviceCreationParameter*> dependencies;
302         dependencies[card.Name()] = &card;
303         return dependencies;
304     }
305 
DefaultAsInt(std::map<String,String> Parameters)306     optional<int> AudioOutputDeviceAlsa::ParameterFragments::DefaultAsInt(std::map<String,String> Parameters) {
307         if (!Parameters.count("CARD")) return optional<int>::nothing;
308 
309         // obtain information from given sound card
310         ParameterCard card(Parameters["CARD"]);
311         String pcm_name = "hw:" + card.ValueAsString();
312         snd_pcm_t* pcm_handle = NULL;
313         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
314         snd_pcm_hw_params_t* hwparams;
315         snd_pcm_hw_params_alloca(&hwparams);
316         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
317             snd_pcm_close(pcm_handle);
318             return optional<int>::nothing;
319         }
320         uint segs = 2;
321         if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &segs, NULL) < 0) {
322             snd_pcm_close(pcm_handle);
323             return optional<int>::nothing;
324         }
325         snd_pcm_close(pcm_handle);
326         return segs;
327     }
328 
RangeMinAsInt(std::map<String,String> Parameters)329     optional<int> AudioOutputDeviceAlsa::ParameterFragments::RangeMinAsInt(std::map<String,String> Parameters) {
330         if (!Parameters.count("CARD")) return optional<int>::nothing;
331 
332         // obtain information from given sound card
333         ParameterCard card(Parameters["CARD"]);
334         String pcm_name = "hw:" + card.ValueAsString();
335         snd_pcm_t* pcm_handle = NULL;
336         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
337         snd_pcm_hw_params_t* hwparams;
338         snd_pcm_hw_params_alloca(&hwparams);
339         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
340             snd_pcm_close(pcm_handle);
341             return optional<int>::nothing;
342         }
343         int dir = 0;
344         uint periods_min;
345         if (snd_pcm_hw_params_get_periods_min(hwparams, &periods_min, &dir) < 0) {
346             snd_pcm_close(pcm_handle);
347             return optional<int>::nothing;
348         }
349         snd_pcm_close(pcm_handle);
350         return (int) periods_min;
351     }
352 
RangeMaxAsInt(std::map<String,String> Parameters)353     optional<int> AudioOutputDeviceAlsa::ParameterFragments::RangeMaxAsInt(std::map<String,String> Parameters) {
354         if (!Parameters.count("CARD")) return optional<int>::nothing;
355 
356         // obtain information from given sound card
357         ParameterCard card(Parameters["CARD"]);
358         String pcm_name = "hw:" + card.ValueAsString();
359         snd_pcm_t* pcm_handle = NULL;
360         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
361         snd_pcm_hw_params_t* hwparams;
362         snd_pcm_hw_params_alloca(&hwparams);
363         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
364             snd_pcm_close(pcm_handle);
365             return optional<int>::nothing;
366         }
367         int dir = 0;
368         uint periods_max;
369         if (snd_pcm_hw_params_get_periods_max(hwparams, &periods_max, &dir) < 0) {
370             snd_pcm_close(pcm_handle);
371             return optional<int>::nothing;
372         }
373         snd_pcm_close(pcm_handle);
374         return (int) periods_max;
375     }
376 
PossibilitiesAsInt(std::map<String,String> Parameters)377     std::vector<int> AudioOutputDeviceAlsa::ParameterFragments::PossibilitiesAsInt(std::map<String,String> Parameters) {
378         return std::vector<int>();
379     }
380 
OnSetValue(int i)381     void AudioOutputDeviceAlsa::ParameterFragments::OnSetValue(int i) throw (Exception) {
382         // not posssible, as parameter is fix
383     }
384 
Name()385     String AudioOutputDeviceAlsa::ParameterFragments::Name() {
386         return "FRAGMENTS";
387     }
388 
389 
390 
391 // *************** ParameterFragmentSize ***************
392 // *
393 
ParameterFragmentSize()394     AudioOutputDeviceAlsa::ParameterFragmentSize::ParameterFragmentSize() : DeviceCreationParameterInt() {
395         InitWithDefault();
396     }
397 
ParameterFragmentSize(String s)398     AudioOutputDeviceAlsa::ParameterFragmentSize::ParameterFragmentSize(String s) throw (Exception) : DeviceCreationParameterInt(s) {
399     }
400 
Description()401     String AudioOutputDeviceAlsa::ParameterFragmentSize::Description() {
402         return "Size of each buffer fragment";
403     }
404 
Fix()405     bool AudioOutputDeviceAlsa::ParameterFragmentSize::Fix() {
406         return true;
407     }
408 
Mandatory()409     bool AudioOutputDeviceAlsa::ParameterFragmentSize::Mandatory() {
410         return false;
411     }
412 
DependsAsParameters()413     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterFragmentSize::DependsAsParameters() {
414         static ParameterCard card;
415         std::map<String,DeviceCreationParameter*> dependencies;
416         dependencies[card.Name()] = &card;
417         return dependencies;
418     }
419 
DefaultAsInt(std::map<String,String> Parameters)420     optional<int> AudioOutputDeviceAlsa::ParameterFragmentSize::DefaultAsInt(std::map<String,String> Parameters) {
421         if (!Parameters.count("CARD")) return optional<int>::nothing;
422 
423         // obtain information from given sound card
424         ParameterCard card(Parameters["CARD"]);
425         String pcm_name = "hw:" + card.ValueAsString();
426         snd_pcm_t* pcm_handle = NULL;
427         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
428         snd_pcm_hw_params_t* hwparams;
429         snd_pcm_hw_params_alloca(&hwparams);
430         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
431             snd_pcm_close(pcm_handle);
432             return optional<int>::nothing;
433         }
434         snd_pcm_uframes_t size = 128;
435         if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &size, NULL) < 0) {
436             snd_pcm_close(pcm_handle);
437             return optional<int>::nothing;
438         }
439         snd_pcm_close(pcm_handle);
440         return size;
441     }
442 
RangeMinAsInt(std::map<String,String> Parameters)443     optional<int> AudioOutputDeviceAlsa::ParameterFragmentSize::RangeMinAsInt(std::map<String,String> Parameters) {
444         if (!Parameters.count("CARD")) return optional<int>::nothing;
445 
446         // obtain information from given sound card
447         ParameterCard card(Parameters["CARD"]);
448         String pcm_name = "hw:" + card.ValueAsString();
449         snd_pcm_t* pcm_handle = NULL;
450         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
451         snd_pcm_hw_params_t* hwparams;
452         snd_pcm_hw_params_alloca(&hwparams);
453         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
454             snd_pcm_close(pcm_handle);
455             return optional<int>::nothing;
456         }
457         int dir = 0;
458         unsigned long period_size_min;
459         if (snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, &dir) < 0) {
460             snd_pcm_close(pcm_handle);
461             return optional<int>::nothing;
462         }
463         snd_pcm_close(pcm_handle);
464         return (int) period_size_min;
465     }
466 
RangeMaxAsInt(std::map<String,String> Parameters)467     optional<int> AudioOutputDeviceAlsa::ParameterFragmentSize::RangeMaxAsInt(std::map<String,String> Parameters) {
468         if (!Parameters.count("CARD")) return optional<int>::nothing;
469 
470         // obtain information from given sound card
471         ParameterCard card(Parameters["CARD"]);
472         String pcm_name = "hw:" + card.ValueAsString();
473         snd_pcm_t* pcm_handle = NULL;
474         if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
475         snd_pcm_hw_params_t* hwparams;
476         snd_pcm_hw_params_alloca(&hwparams);
477         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
478             snd_pcm_close(pcm_handle);
479             return optional<int>::nothing;
480         }
481         int dir = 0;
482         unsigned long period_size_max;
483         if (snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, &dir) < 0) {
484             snd_pcm_close(pcm_handle);
485             return optional<int>::nothing;
486         }
487         snd_pcm_close(pcm_handle);
488         return (int) period_size_max; //FIXME: might overflow int limit
489     }
490 
PossibilitiesAsInt(std::map<String,String> Parameters)491     std::vector<int> AudioOutputDeviceAlsa::ParameterFragmentSize::PossibilitiesAsInt(std::map<String,String> Parameters) {
492         return std::vector<int>();
493     }
494 
OnSetValue(int i)495     void AudioOutputDeviceAlsa::ParameterFragmentSize::OnSetValue(int i) throw (Exception) {
496         // not posssible, as parameter is fix
497     }
498 
Name()499     String AudioOutputDeviceAlsa::ParameterFragmentSize::Name() {
500         return "FRAGMENTSIZE";
501     }
502 
503 
504 
505 // *************** AudioOutputDeviceAlsa ***************
506 // *
507 
508     /**
509      * Create and initialize Alsa audio output device with given parameters.
510      *
511      * @param Parameters - optional parameters
512      * @throws AudioOutputException  if output device cannot be opened
513      */
AudioOutputDeviceAlsa(std::map<String,DeviceCreationParameter * > Parameters)514     AudioOutputDeviceAlsa::AudioOutputDeviceAlsa(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters), Thread(true, true, 1, 0) {
515         pcm_handle           = NULL;
516         stream               = SND_PCM_STREAM_PLAYBACK;
517         this->uiAlsaChannels = ((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt();
518         this->uiSamplerate   = ((DeviceCreationParameterInt*)Parameters["SAMPLERATE"])->ValueAsInt();
519         this->FragmentSize   = ((DeviceCreationParameterInt*)Parameters["FRAGMENTSIZE"])->ValueAsInt();
520         uint Fragments       = ((DeviceCreationParameterInt*)Parameters["FRAGMENTS"])->ValueAsInt();
521         String Card          = ((DeviceCreationParameterString*)Parameters["CARD"])->ValueAsString();
522 
523         dmsg(2,("Checking if hw parameters supported...\n"));
524         if (HardwareParametersSupported(Card, uiAlsaChannels, uiSamplerate, Fragments, FragmentSize)) {
525             pcm_name = "hw:" + Card;
526         }
527         else {
528             fprintf(stderr, "Warning: your soundcard doesn't support chosen hardware parameters; ");
529             fprintf(stderr, "trying to compensate support lack with plughw...");
530             fflush(stdout);
531             pcm_name = "plughw:" + Card;
532         }
533         dmsg(2,("HW check completed.\n"));
534 
535         int err;
536 
537         snd_pcm_hw_params_alloca(&hwparams);  // Allocate the snd_pcm_hw_params_t structure on the stack.
538 
539         /* Open PCM. The last parameter of this function is the mode. */
540         /* If this is set to 0, the standard mode is used. Possible   */
541         /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC.       */
542         /* If SND_PCM_NONBLOCK is used, read / write access to the    */
543         /* PCM device will return immediately. If SND_PCM_ASYNC is    */
544         /* specified, SIGIO will be emitted whenever a period has     */
545         /* been completely processed by the soundcard.                */
546         if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0)) < 0) {
547             throw AudioOutputException(String("Error opening PCM device ") + pcm_name + ": " + snd_strerror(err));
548         }
549 
550         if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
551             throw AudioOutputException(String("Error, cannot initialize hardware parameter structure: ") + snd_strerror(err));
552         }
553 
554         /* Set access type. This can be either    */
555         /* SND_PCM_ACCESS_RW_INTERLEAVED or       */
556         /* SND_PCM_ACCESS_RW_NONINTERLEAVED.      */
557         if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
558             throw AudioOutputException(String("Error snd_pcm_hw_params_set_access: ") + snd_strerror(err));
559         }
560 
561         /* Set sample format */
562         #if WORDS_BIGENDIAN
563         if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE)) < 0)
564         #else // little endian
565         if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
566         #endif
567         {
568             throw AudioOutputException(String("Error setting sample format: ") + snd_strerror(err));
569         }
570 
571         int dir = 0;
572 
573         /* Set sample rate. If the exact rate is not supported */
574         /* by the hardware, use nearest possible rate.         */
575         #if ALSA_MAJOR > 0
576         if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &uiSamplerate, &dir)) < 0)
577         #else
578         if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, uiSamplerate, &dir)) < 0)
579         #endif
580         {
581             throw AudioOutputException(String("Error setting sample rate: ") + snd_strerror(err));
582         }
583 
584         if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, uiAlsaChannels)) < 0) {
585             throw AudioOutputException(String("Error setting number of channels: ") + snd_strerror(err));
586         }
587 
588         /* Set number of periods. Periods used to be called fragments. */
589         if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, Fragments, dir)) < 0) {
590             throw AudioOutputException(String("Error setting number of ") + ToString(Fragments) + " periods: " + snd_strerror(err));
591         }
592 
593         /* Set buffer size (in frames). The resulting latency is given by */
594         /* latency = periodsize * periods / (rate * bytes_per_frame)     */
595         if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (FragmentSize * Fragments))) < 0) {
596             throw AudioOutputException(String("Error setting buffersize: ") + snd_strerror(err));
597         }
598 
599         /* Apply HW parameter settings to */
600         /* PCM device and prepare device  */
601         if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
602             throw AudioOutputException(String("Error setting HW params: ") + snd_strerror(err));
603         }
604 
605         if (snd_pcm_sw_params_malloc(&swparams) != 0) {
606             throw AudioOutputException(String("Error in snd_pcm_sw_params_malloc: ") + snd_strerror(err));
607         }
608 
609         if (snd_pcm_sw_params_current(pcm_handle, swparams) != 0) {
610             throw AudioOutputException(String("Error in snd_pcm_sw_params_current: ") + snd_strerror(err));
611         }
612 
613         if (snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xffffffff) != 0) {
614             throw AudioOutputException(String("Error in snd_pcm_sw_params_set_stop_threshold: ") + snd_strerror(err));
615         }
616 
617         if (snd_pcm_sw_params(pcm_handle, swparams) != 0) {
618             throw AudioOutputException(String("Error in snd_pcm_sw_params: ") + snd_strerror(err));
619         }
620 
621         if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
622             throw AudioOutputException(String("Error snd_pcm_prepare: ") + snd_strerror(err));
623         }
624 
625         // allocate Alsa output buffer
626         pAlsaOutputBuffer = new int16_t[uiAlsaChannels * FragmentSize];
627 
628         // create audio channels for this audio device to which the sampler engines can write to
629         for (int i = 0; i < uiAlsaChannels; i++) this->Channels.push_back(new AudioChannel(i, FragmentSize));
630 
631 	if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) {
632 		Play();
633 	}
634     }
635 
~AudioOutputDeviceAlsa()636     AudioOutputDeviceAlsa::~AudioOutputDeviceAlsa() {
637         //dmsg(0,("Stopping Alsa Thread..."));
638         //StopThread();  //FIXME: commented out due to a bug in thread.cpp (StopThread() doesn't return at all)
639         //dmsg(0,("OK\n"));
640 
641         snd_pcm_close(pcm_handle);
642 
643         if (pAlsaOutputBuffer) {
644             //FIXME: currently commented out due to segfault
645             //delete[] pOutputBuffer;
646         }
647     }
648 
649     /**
650      *  Checks if sound card supports the chosen parameters.
651      *
652      *  @returns  true if hardware supports it
653      *  @throws AudioOutputException - if device cannot be accessed
654      */
HardwareParametersSupported(String card,uint channels,int samplerate,uint numfragments,uint fragmentsize)655     bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) throw (AudioOutputException) {
656         pcm_name = "hw:" + card;
657         int err;
658         if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, SND_PCM_NONBLOCK)) < 0) {
659             throw AudioOutputException(String("Error opening PCM device ") + pcm_name + ": " + snd_strerror(err));
660         }
661         snd_pcm_hw_params_alloca(&hwparams);
662         if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
663             snd_pcm_close(pcm_handle);
664             return false;
665         }
666         if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
667             snd_pcm_close(pcm_handle);
668             return false;
669         }
670         #if WORDS_BIGENDIAN
671         if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0)
672         #else // little endian
673         if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
674         #endif
675         {
676             snd_pcm_close(pcm_handle);
677             return false;
678         }
679         int dir = 0;
680         if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) {
681             snd_pcm_close(pcm_handle);
682             return false;
683         }
684         if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) {
685             snd_pcm_close(pcm_handle);
686             return false;
687         }
688         if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) {
689             snd_pcm_close(pcm_handle);
690             return false;
691         }
692         if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) {
693             snd_pcm_close(pcm_handle);
694             return false;
695         }
696 
697         snd_pcm_close(pcm_handle);
698         return true;
699     }
700 
Play()701     void AudioOutputDeviceAlsa::Play() {
702         StartThread();
703     }
704 
IsPlaying()705     bool AudioOutputDeviceAlsa::IsPlaying() {
706         return IsRunning(); // if Thread is running
707     }
708 
Stop()709     void AudioOutputDeviceAlsa::Stop() {
710         StopThread();
711     }
712 
CreateChannel(uint ChannelNr)713     AudioChannel* AudioOutputDeviceAlsa::CreateChannel(uint ChannelNr) {
714         // just create a mix channel
715         return new AudioChannel(ChannelNr, Channel(ChannelNr % uiAlsaChannels));
716     }
717 
MaxSamplesPerCycle()718     uint AudioOutputDeviceAlsa::MaxSamplesPerCycle() {
719         return FragmentSize;
720     }
721 
SampleRate()722     uint AudioOutputDeviceAlsa::SampleRate() {
723         return uiSamplerate;
724     }
725 
Name()726     String AudioOutputDeviceAlsa::Name() {
727         return "ALSA";
728     }
729 
Driver()730     String AudioOutputDeviceAlsa::Driver() {
731         return Name();
732     }
733 
Description()734     String AudioOutputDeviceAlsa::Description() {
735         return "Advanced Linux Sound Architecture";
736     }
737 
Version()738     String AudioOutputDeviceAlsa::Version() {
739        String s = "$Revision: 3766 $";
740        return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
741     }
742 
743     /**
744      * Entry point for the thread.
745      */
Main()746     int AudioOutputDeviceAlsa::Main() {
747         #if DEBUG
748         Thread::setNameOfCaller("AlsaAudio");
749         #endif
750 
751         while (true) {
752             TestCancel();
753 
754             // prevent thread from being cancelled
755             // (e.g. to prevent deadlocks while holding mutex lock(s))
756             pushCancelable(false);
757 
758             // let all connected engines render 'FragmentSize' sample points
759             RenderAudio(FragmentSize);
760 
761             // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
762             // range (-32768..+32767), check clipping  and copy to Alsa output buffer
763             // (note: we use interleaved output method to Alsa)
764             for (int c = 0; c < uiAlsaChannels; c++) {
765                 float* in  = Channels[c]->Buffer();
766                 for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
767                     float sample_point = in[i] * 32768.0f;
768                     if (sample_point < -32768.0) sample_point = -32768.0;
769                     if (sample_point >  32767.0) sample_point =  32767.0;
770                     pAlsaOutputBuffer[o] = (int16_t) sample_point;
771                 }
772             }
773 
774             // output sound
775             int res = Output();
776             if (res < 0) {
777                 fprintf(stderr, "Alsa: Audio output error, exiting.\n");
778                 exit(EXIT_FAILURE);
779             }
780 
781             // now allow thread being cancelled again
782             // (since all mutexes are now unlocked)
783             popCancelable();
784         }
785         // just to suppress compiler warning
786         return EXIT_FAILURE;
787     }
788 
789     /**
790      *  Will be called after every audio fragment cycle, to output the audio data
791      *  of the current fragment to the soundcard.
792      *
793      *  @returns  0 on success, a value < 0 on error
794      */
Output()795     int AudioOutputDeviceAlsa::Output() {
796         int err = snd_pcm_writei(pcm_handle, pAlsaOutputBuffer, FragmentSize);
797         if (err < 0) {
798             fprintf(stderr, "Error snd_pcm_writei failed: %s\n", snd_strerror(err));
799             return -1;
800         }
801         return 0;
802     }
803 
804 } // namespace LinuxSampler
805