/[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 221 - (show annotations) (download)
Fri Aug 20 17:25:19 2004 UTC (19 years, 8 months ago) by schoenebeck
File size: 20523 byte(s)
* src/drivers/midi/MidiInputDeviceAlsa.cpp: implemented port parameter
 "NAME" which now updates the registered ALSA seq port name as well, fixed
  port parameter "ALSA_SEQ_BINDINGS" to allow more than one binding
* src/network/lscp.y: fixed symbol STRINGVAL (that is strings encapsulated
  into apostrophes) which didn't allow space characters
* changed all driver names and driver paramaters to upper case
* fixed typo in LSCP documentation
  (section 5.3.12, was: "SET MIDI_INPUT_PORT PARAMETER",
   should be: "SET MIDI_INPUT_PORT_PARAMETER")

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
20 * MA 02111-1307 USA *
21 ***************************************************************************/
22
23 #include "AudioOutputDeviceAlsa.h"
24 #include "AudioOutputDeviceFactory.h"
25
26 namespace LinuxSampler {
27
28 REGISTER_AUDIO_OUTPUT_DRIVER(AudioOutputDeviceAlsa);
29
30 /* Common parameters for now they'll have to be registered here. */
31 REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterActive);
32 REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterSampleRate);
33 REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterChannels);
34
35 /* Driver specific parameters */
36 REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterCard);
37 REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterFragments);
38 REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterFragmentSize);
39
40
41
42 // *************** ParameterCard ***************
43 // *
44
45 AudioOutputDeviceAlsa::ParameterCard::ParameterCard() : DeviceCreationParameterString() {
46 InitWithDefault(); // use default card
47 }
48
49 AudioOutputDeviceAlsa::ParameterCard::ParameterCard(String s) throw (LinuxSamplerException) : DeviceCreationParameterString(s) {
50 SetValue(s); // try to use given card
51 }
52
53 String AudioOutputDeviceAlsa::ParameterCard::Description() {
54 return "Sound card to be used";
55 }
56
57 bool AudioOutputDeviceAlsa::ParameterCard::Fix() {
58 return true;
59 }
60
61 bool AudioOutputDeviceAlsa::ParameterCard::Mandatory() {
62 return false;
63 }
64
65 std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterCard::DependsAsParameters() {
66 return std::map<String,DeviceCreationParameter*>(); // no dependencies
67 }
68
69 optional<String> AudioOutputDeviceAlsa::ParameterCard::DefaultAsString(std::map<String,String> Parameters) {
70 std::vector<String> cards = PossibilitiesAsString(Parameters);
71 if (cards.empty()) throw LinuxSamplerException("AudioOutputDeviceAlsa: Can't find any card");
72 return cards[0]; // first card by default
73 }
74
75 std::vector<String> AudioOutputDeviceAlsa::ParameterCard::PossibilitiesAsString(std::map<String,String> Parameters) {
76 int err;
77 std::vector<String> CardNames;
78
79 // iterate through all cards
80 int card_index = -1;
81 while (snd_card_next(&card_index) >= 0 && card_index >= 0) {
82 String hw_name = "hw:" + ToString(card_index);
83 snd_ctl_t* hCardCtrl;
84 if ((err = snd_ctl_open(&hCardCtrl, hw_name.c_str(), 0)) < 0) {
85 std::cerr << "AudioOutputDeviceAlsa: Cannot open sound control for card " << card_index << " - " << snd_strerror(err) << std::endl;
86 continue;
87 }
88
89 // iterate through all devices of that card
90 int device_index = -1;
91 while (!snd_ctl_pcm_next_device(hCardCtrl, &device_index) && device_index >= 0) {
92 String name = ToString(card_index) + "," + ToString(device_index);
93 //dmsg(1,("[possibility:%s]", name.c_str()));
94 CardNames.push_back(name);
95 }
96
97 snd_ctl_close(hCardCtrl);
98 }
99
100 return CardNames;
101 }
102
103 void AudioOutputDeviceAlsa::ParameterCard::OnSetValue(String s) throw (LinuxSamplerException) {
104 // not posssible, as parameter is fix
105 }
106
107 String AudioOutputDeviceAlsa::ParameterCard::Name() {
108 return "CARD";
109 }
110
111
112
113 // *************** ParameterFragments ***************
114 // *
115
116 AudioOutputDeviceAlsa::ParameterFragments::ParameterFragments() : DeviceCreationParameterInt() {
117 InitWithDefault();
118 }
119
120 AudioOutputDeviceAlsa::ParameterFragments::ParameterFragments(String s) throw (LinuxSamplerException) : DeviceCreationParameterInt(s) {
121 }
122
123 String AudioOutputDeviceAlsa::ParameterFragments::Description() {
124 return "Number of buffer fragments";
125 }
126
127 bool AudioOutputDeviceAlsa::ParameterFragments::Fix() {
128 return true;
129 }
130
131 bool AudioOutputDeviceAlsa::ParameterFragments::Mandatory() {
132 return false;
133 }
134
135 std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterFragments::DependsAsParameters() {
136 static ParameterCard card;
137 std::map<String,DeviceCreationParameter*> dependencies;
138 dependencies[card.Name()] = &card;
139 return dependencies;
140 }
141
142 optional<int> AudioOutputDeviceAlsa::ParameterFragments::DefaultAsInt(std::map<String,String> Parameters) {
143 return 2; // until done
144 }
145
146 optional<int> AudioOutputDeviceAlsa::ParameterFragments::RangeMinAsInt(std::map<String,String> Parameters) {
147 return optional<int>::nothing;
148 }
149
150 optional<int> AudioOutputDeviceAlsa::ParameterFragments::RangeMaxAsInt(std::map<String,String> Parameters) {
151 return optional<int>::nothing;
152 }
153
154 std::vector<int> AudioOutputDeviceAlsa::ParameterFragments::PossibilitiesAsInt(std::map<String,String> Parameters) {
155 return std::vector<int>();
156 }
157
158 void AudioOutputDeviceAlsa::ParameterFragments::OnSetValue(int i) throw (LinuxSamplerException) {
159 // not posssible, as parameter is fix
160 }
161
162 String AudioOutputDeviceAlsa::ParameterFragments::Name() {
163 return "FRAGMENTS";
164 }
165
166
167
168 // *************** ParameterFragmentSize ***************
169 // *
170
171 AudioOutputDeviceAlsa::ParameterFragmentSize::ParameterFragmentSize() : DeviceCreationParameterInt() {
172 InitWithDefault();
173 }
174
175 AudioOutputDeviceAlsa::ParameterFragmentSize::ParameterFragmentSize(String s) throw (LinuxSamplerException) : DeviceCreationParameterInt(s) {
176 }
177
178 String AudioOutputDeviceAlsa::ParameterFragmentSize::Description() {
179 return "Size of each buffer fragment";
180 }
181
182 bool AudioOutputDeviceAlsa::ParameterFragmentSize::Fix() {
183 return true;
184 }
185
186 bool AudioOutputDeviceAlsa::ParameterFragmentSize::Mandatory() {
187 return false;
188 }
189
190 std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::ParameterFragmentSize::DependsAsParameters() {
191 static ParameterCard card;
192 std::map<String,DeviceCreationParameter*> dependencies;
193 dependencies[card.Name()] = &card;
194 return dependencies;
195 }
196
197 optional<int> AudioOutputDeviceAlsa::ParameterFragmentSize::DefaultAsInt(std::map<String,String> Parameters) {
198 return 128; // until done
199 }
200
201 optional<int> AudioOutputDeviceAlsa::ParameterFragmentSize::RangeMinAsInt(std::map<String,String> Parameters) {
202 return optional<int>::nothing;
203 }
204
205 optional<int> AudioOutputDeviceAlsa::ParameterFragmentSize::RangeMaxAsInt(std::map<String,String> Parameters) {
206 return optional<int>::nothing;
207 }
208
209 std::vector<int> AudioOutputDeviceAlsa::ParameterFragmentSize::PossibilitiesAsInt(std::map<String,String> Parameters) {
210 return std::vector<int>();
211 }
212
213 void AudioOutputDeviceAlsa::ParameterFragmentSize::OnSetValue(int i) throw (LinuxSamplerException) {
214 // not posssible, as parameter is fix
215 }
216
217 String AudioOutputDeviceAlsa::ParameterFragmentSize::Name() {
218 return "FRAGMENTSIZE";
219 }
220
221
222
223 // *************** AudioOutputDeviceAlsa ***************
224 // *
225
226 /**
227 * Create and initialize Alsa audio output device with given parameters.
228 *
229 * @param Parameters - optional parameters
230 * @throws AudioOutputException if output device cannot be opened
231 */
232 AudioOutputDeviceAlsa::AudioOutputDeviceAlsa(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters), Thread(true, 1, 0) {
233 pcm_handle = NULL;
234 stream = SND_PCM_STREAM_PLAYBACK;
235 this->uiAlsaChannels = ((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt();
236 this->uiSamplerate = ((DeviceCreationParameterInt*)Parameters["SAMPLERATE"])->ValueAsInt();
237 this->FragmentSize = ((DeviceCreationParameterInt*)Parameters["FRAGMENTSIZE"])->ValueAsInt();
238 uint Fragments = ((DeviceCreationParameterInt*)Parameters["FRAGMENTS"])->ValueAsInt();
239 String Card = ((DeviceCreationParameterString*)Parameters["CARD"])->ValueAsString();
240
241 dmsg(1,("Checking if hw parameters supported...\n"));
242 if (HardwareParametersSupported(Card, uiAlsaChannels, uiSamplerate, Fragments, FragmentSize)) {
243 pcm_name = "hw:" + Card;
244 }
245 else {
246 printf("Warning: your soundcard doesn't support chosen hardware parameters; ");
247 printf("trying to compensate support lack with plughw...");
248 fflush(stdout);
249 pcm_name = "plughw:" + Card;
250 }
251 dmsg(1,("HW check completed.\n"));
252
253 int err;
254
255 snd_pcm_hw_params_alloca(&hwparams); // Allocate the snd_pcm_hw_params_t structure on the stack.
256
257 /* Open PCM. The last parameter of this function is the mode. */
258 /* If this is set to 0, the standard mode is used. Possible */
259 /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC. */
260 /* If SND_PCM_NONBLOCK is used, read / write access to the */
261 /* PCM device will return immediately. If SND_PCM_ASYNC is */
262 /* specified, SIGIO will be emitted whenever a period has */
263 /* been completely processed by the soundcard. */
264 if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0)) < 0) {
265 throw AudioOutputException(String("Error opening PCM device ") + pcm_name + ": " + snd_strerror(err));
266 }
267
268 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
269 throw AudioOutputException(String("Error, cannot initialize hardware parameter structure: ") + snd_strerror(err));
270 }
271
272 /* Set access type. This can be either */
273 /* SND_PCM_ACCESS_RW_INTERLEAVED or */
274 /* SND_PCM_ACCESS_RW_NONINTERLEAVED. */
275 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
276 throw AudioOutputException(String("Error snd_pcm_hw_params_set_access: ") + snd_strerror(err));
277 }
278
279 /* Set sample format */
280 #if WORDS_BIGENDIAN
281 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE)) < 0)
282 #else // little endian
283 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
284 #endif
285 {
286 throw AudioOutputException(String("Error setting sample format: ") + snd_strerror(err));
287 }
288
289 int dir = 0;
290
291 /* Set sample rate. If the exact rate is not supported */
292 /* by the hardware, use nearest possible rate. */
293 #if ALSA_MAJOR > 0
294 if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &uiSamplerate, &dir)) < 0)
295 #else
296 if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, uiSamplerate, &dir)) < 0)
297 #endif
298 {
299 throw AudioOutputException(String("Error setting sample rate: ") + snd_strerror(err));
300 }
301
302 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, uiAlsaChannels)) < 0) {
303 throw AudioOutputException(String("Error setting number of channels: ") + snd_strerror(err));
304 }
305
306 /* Set number of periods. Periods used to be called fragments. */
307 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, Fragments, dir)) < 0) {
308 throw AudioOutputException(String("Error setting number of periods: ") + snd_strerror(err));
309 }
310
311 /* Set buffer size (in frames). The resulting latency is given by */
312 /* latency = periodsize * periods / (rate * bytes_per_frame) */
313 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (FragmentSize * Fragments))) < 0) {
314 throw AudioOutputException(String("Error setting buffersize: ") + snd_strerror(err));
315 }
316
317 /* Apply HW parameter settings to */
318 /* PCM device and prepare device */
319 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
320 throw AudioOutputException(String("Error setting HW params: ") + snd_strerror(err));
321 }
322
323 if (snd_pcm_sw_params_malloc(&swparams) != 0) {
324 throw AudioOutputException(String("Error in snd_pcm_sw_params_malloc: ") + snd_strerror(err));
325 }
326
327 if (snd_pcm_sw_params_current(pcm_handle, swparams) != 0) {
328 throw AudioOutputException(String("Error in snd_pcm_sw_params_current: ") + snd_strerror(err));
329 }
330
331 if (snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xffffffff) != 0) {
332 throw AudioOutputException(String("Error in snd_pcm_sw_params_set_stop_threshold: ") + snd_strerror(err));
333 }
334
335 if (snd_pcm_sw_params(pcm_handle, swparams) != 0) {
336 throw AudioOutputException(String("Error in snd_pcm_sw_params: ") + snd_strerror(err));
337 }
338
339 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
340 throw AudioOutputException(String("Error snd_pcm_prepare: ") + snd_strerror(err));
341 }
342
343 // allocate Alsa output buffer
344 pAlsaOutputBuffer = new int16_t[uiAlsaChannels * FragmentSize];
345
346 // create audio channels for this audio device to which the sampler engines can write to
347 for (int i = 0; i < uiAlsaChannels; i++) this->Channels.push_back(new AudioChannel(FragmentSize));
348
349 if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) {
350 Play();
351 }
352 }
353
354 AudioOutputDeviceAlsa::~AudioOutputDeviceAlsa() {
355 //dmsg(0,("Stopping Alsa Thread..."));
356 //StopThread(); //FIXME: commented out due to a bug in thread.cpp (StopThread() doesn't return at all)
357 //dmsg(0,("OK\n"));
358
359 //FIXME: currently commented out due to segfault
360 //snd_pcm_close(pcm_handle);
361
362 // destroy all audio channels
363 for (int c = 0; c < Channels.size(); c++) delete Channels[c];
364
365 if (pAlsaOutputBuffer) {
366 //FIXME: currently commented out due to segfault
367 //delete[] pOutputBuffer;
368 }
369 }
370
371 /**
372 * Checks if sound card supports the chosen parameters.
373 *
374 * @returns true if hardware supports it
375 */
376 bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) {
377 pcm_name = "hw:" + card;
378 if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false;
379 snd_pcm_hw_params_alloca(&hwparams);
380 if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
381 snd_pcm_close(pcm_handle);
382 return false;
383 }
384 if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
385 snd_pcm_close(pcm_handle);
386 return false;
387 }
388 #if WORDS_BIGENDIAN
389 if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0)
390 #else // little endian
391 if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
392 #endif
393 {
394 snd_pcm_close(pcm_handle);
395 return false;
396 }
397 int dir = 0;
398 if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) {
399 snd_pcm_close(pcm_handle);
400 return false;
401 }
402 if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) {
403 snd_pcm_close(pcm_handle);
404 return false;
405 }
406 if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) {
407 snd_pcm_close(pcm_handle);
408 return false;
409 }
410 if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) {
411 snd_pcm_close(pcm_handle);
412 return false;
413 }
414
415 snd_pcm_close(pcm_handle);
416 return true;
417 }
418
419 void AudioOutputDeviceAlsa::Play() {
420 StartThread();
421 }
422
423 bool AudioOutputDeviceAlsa::IsPlaying() {
424 return IsRunning(); // if Thread is running
425 }
426
427 void AudioOutputDeviceAlsa::Stop() {
428 StopThread();
429 }
430
431 void AudioOutputDeviceAlsa::AcquireChannels(uint Channels) {
432 if (Channels > uiAlsaChannels) {
433 // just create mix channel(s)
434 for (int i = uiAlsaChannels; i < Channels; i++) {
435 AudioChannel* pNewChannel = new AudioChannel(this->Channels[i % uiAlsaChannels]);
436 this->Channels.push_back(pNewChannel);
437 }
438 }
439 }
440
441 uint AudioOutputDeviceAlsa::MaxSamplesPerCycle() {
442 return FragmentSize;
443 }
444
445 uint AudioOutputDeviceAlsa::SampleRate() {
446 return uiSamplerate;
447 }
448
449 String AudioOutputDeviceAlsa::Name() {
450 return "ALSA";
451 }
452
453 String AudioOutputDeviceAlsa::Driver() {
454 return Name();
455 }
456
457 String AudioOutputDeviceAlsa::Description() {
458 return "Advanced Linux Sound Architecture";
459 }
460
461 String AudioOutputDeviceAlsa::Version() {
462 String s = "$Revision: 1.13 $";
463 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
464 }
465
466 /**
467 * Entry point for the thread.
468 */
469 int AudioOutputDeviceAlsa::Main() {
470 while (true) {
471
472 // let all connected engines render 'FragmentSize' sample points
473 RenderAudio(FragmentSize);
474
475 // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
476 // range (-32768..+32767), check clipping and copy to Alsa output buffer
477 // (note: we use interleaved output method to Alsa)
478 for (int c = 0; c < uiAlsaChannels; c++) {
479 float* in = Channels[c]->Buffer();
480 for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
481 float sample_point = in[i] * 32768.0f;
482 if (sample_point < -32768.0) sample_point = -32768.0;
483 if (sample_point > 32767.0) sample_point = 32767.0;
484 pAlsaOutputBuffer[o] = (int16_t) sample_point;
485 }
486 }
487
488 // output sound
489 int res = Output();
490 if (res < 0) {
491 fprintf(stderr, "Alsa: Audio output error, exiting.\n");
492 exit(EXIT_FAILURE);
493 }
494 }
495 }
496
497 /**
498 * Will be called after every audio fragment cycle, to output the audio data
499 * of the current fragment to the soundcard.
500 *
501 * @returns 0 on success, a value < 0 on error
502 */
503 int AudioOutputDeviceAlsa::Output() {
504 int err = snd_pcm_writei(pcm_handle, pAlsaOutputBuffer, FragmentSize);
505 if (err < 0) {
506 fprintf(stderr, "Error snd_pcm_writei failed: %s\n", snd_strerror(err));
507 return -1;
508 }
509 return 0;
510 }
511
512 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC