/[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 1599 - (show annotations) (download)
Fri Dec 28 15:47:33 2007 UTC (16 years, 3 months ago) by schoenebeck
File size: 32581 byte(s)
* removed the nonsense audio channel constraint (which was hard coded to
  max. 100 audio channels) for most audio drivers

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 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
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
280 AudioOutputDeviceAlsa::ParameterFragments::ParameterFragments() : DeviceCreationParameterInt() {
281 InitWithDefault();
282 }
283
284 AudioOutputDeviceAlsa::ParameterFragments::ParameterFragments(String s) throw (Exception) : DeviceCreationParameterInt(s) {
285 }
286
287 String AudioOutputDeviceAlsa::ParameterFragments::Description() {
288 return "Number of buffer fragments";
289 }
290
291 bool AudioOutputDeviceAlsa::ParameterFragments::Fix() {
292 return true;
293 }
294
295 bool AudioOutputDeviceAlsa::ParameterFragments::Mandatory() {
296 return false;
297 }
298
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
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
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
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
377 std::vector<int> AudioOutputDeviceAlsa::ParameterFragments::PossibilitiesAsInt(std::map<String,String> Parameters) {
378 return std::vector<int>();
379 }
380
381 void AudioOutputDeviceAlsa::ParameterFragments::OnSetValue(int i) throw (Exception) {
382 // not posssible, as parameter is fix
383 }
384
385 String AudioOutputDeviceAlsa::ParameterFragments::Name() {
386 return "FRAGMENTS";
387 }
388
389
390
391 // *************** ParameterFragmentSize ***************
392 // *
393
394 AudioOutputDeviceAlsa::ParameterFragmentSize::ParameterFragmentSize() : DeviceCreationParameterInt() {
395 InitWithDefault();
396 }
397
398 AudioOutputDeviceAlsa::ParameterFragmentSize::ParameterFragmentSize(String s) throw (Exception) : DeviceCreationParameterInt(s) {
399 }
400
401 String AudioOutputDeviceAlsa::ParameterFragmentSize::Description() {
402 return "Size of each buffer fragment";
403 }
404
405 bool AudioOutputDeviceAlsa::ParameterFragmentSize::Fix() {
406 return true;
407 }
408
409 bool AudioOutputDeviceAlsa::ParameterFragmentSize::Mandatory() {
410 return false;
411 }
412
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
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
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
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
491 std::vector<int> AudioOutputDeviceAlsa::ParameterFragmentSize::PossibilitiesAsInt(std::map<String,String> Parameters) {
492 return std::vector<int>();
493 }
494
495 void AudioOutputDeviceAlsa::ParameterFragmentSize::OnSetValue(int i) throw (Exception) {
496 // not posssible, as parameter is fix
497 }
498
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 */
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
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 */
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
701 void AudioOutputDeviceAlsa::Play() {
702 StartThread();
703 }
704
705 bool AudioOutputDeviceAlsa::IsPlaying() {
706 return IsRunning(); // if Thread is running
707 }
708
709 void AudioOutputDeviceAlsa::Stop() {
710 StopThread();
711 }
712
713 AudioChannel* AudioOutputDeviceAlsa::CreateChannel(uint ChannelNr) {
714 // just create a mix channel
715 return new AudioChannel(ChannelNr, Channel(ChannelNr % uiAlsaChannels));
716 }
717
718 uint AudioOutputDeviceAlsa::MaxSamplesPerCycle() {
719 return FragmentSize;
720 }
721
722 uint AudioOutputDeviceAlsa::SampleRate() {
723 return uiSamplerate;
724 }
725
726 String AudioOutputDeviceAlsa::Name() {
727 return "ALSA";
728 }
729
730 String AudioOutputDeviceAlsa::Driver() {
731 return Name();
732 }
733
734 String AudioOutputDeviceAlsa::Description() {
735 return "Advanced Linux Sound Architecture";
736 }
737
738 String AudioOutputDeviceAlsa::Version() {
739 String s = "$Revision: 1.25 $";
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 */
746 int AudioOutputDeviceAlsa::Main() {
747 while (true) {
748
749 // let all connected engines render 'FragmentSize' sample points
750 RenderAudio(FragmentSize);
751
752 // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
753 // range (-32768..+32767), check clipping and copy to Alsa output buffer
754 // (note: we use interleaved output method to Alsa)
755 for (int c = 0; c < uiAlsaChannels; c++) {
756 float* in = Channels[c]->Buffer();
757 for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
758 float sample_point = in[i] * 32768.0f;
759 if (sample_point < -32768.0) sample_point = -32768.0;
760 if (sample_point > 32767.0) sample_point = 32767.0;
761 pAlsaOutputBuffer[o] = (int16_t) sample_point;
762 }
763 }
764
765 // output sound
766 int res = Output();
767 if (res < 0) {
768 fprintf(stderr, "Alsa: Audio output error, exiting.\n");
769 exit(EXIT_FAILURE);
770 }
771 }
772 // just to suppress compiler warning
773 return EXIT_FAILURE;
774 }
775
776 /**
777 * Will be called after every audio fragment cycle, to output the audio data
778 * of the current fragment to the soundcard.
779 *
780 * @returns 0 on success, a value < 0 on error
781 */
782 int AudioOutputDeviceAlsa::Output() {
783 int err = snd_pcm_writei(pcm_handle, pAlsaOutputBuffer, FragmentSize);
784 if (err < 0) {
785 fprintf(stderr, "Error snd_pcm_writei failed: %s\n", snd_strerror(err));
786 return -1;
787 }
788 return 0;
789 }
790
791 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC