/[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 1305 - (show annotations) (download)
Mon Aug 27 07:51:28 2007 UTC (16 years, 8 months ago) by iliev
File size: 32439 byte(s)
* added default min and max values to restrict the number of allowed
  audio output channels and MIDI input ports
* the connection to the PCM interface is now closed when destroying
  an audio output device

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

  ViewVC Help
Powered by ViewVC