/[svn]/linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceAlsa.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceAlsa.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 214 - (hide 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 schoenebeck 200 /***************************************************************************
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 schoenebeck 212
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 schoenebeck 200 /**
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 schoenebeck 214 String Card = ((DeviceCreationParameterString*)Parameters["card"])->ValueAsString();
240 schoenebeck 200
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 schoenebeck 214 String s = "$Revision: 1.12 $";
463 schoenebeck 200 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