/[svn]/linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceAlsa.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceAlsa.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1049 - (show annotations) (download)
Wed Feb 28 06:53:42 2007 UTC (17 years, 1 month ago) by schoenebeck
File size: 32598 byte(s)
* the ALSA audio output driver parameters now reflect the correct
  parameter value ranges for the respective selected sound card
  (patch by Till Wimmer, a bit fixed and extended)
* bumped version to 0.4.0.4cvs

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

  ViewVC Help
Powered by ViewVC