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