/[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 214 - (show annotations) (download)
Sat Aug 14 23:00:44 2004 UTC (19 years, 8 months ago) by schoenebeck
File size: 20524 byte(s)
* src/drivers/DeviceParameter.cpp: fixed return values for
classes 'DeviceRuntimeParameterString' and 'DeviceCreationParameterString'
which returned their values without being encapsulated into apostrophes,
fixed return values for 'DeviceRuntimeParameterBool' and
'DeviceCreationParameterBool' to be returned in lower case (as defined in
the LSCP documentation)
* src/network/lscp.y: key value pairs now also allow strings (without
spaces) to be not encapsulated into apostrophes (e.g. foo=bar instead of
foo='bar')
* src/linuxsampler.cpp: show on the console which TCP port the LSCP server
is using

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.12 $";
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
513

  ViewVC Help
Powered by ViewVC