/[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 221 - (hide 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 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 schoenebeck 221 return "CARD";
109 schoenebeck 212 }
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 schoenebeck 221 return "FRAGMENTS";
164 schoenebeck 212 }
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 schoenebeck 221 return "FRAGMENTSIZE";
219 schoenebeck 212 }
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 schoenebeck 221 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 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 schoenebeck 221 if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) {
350 schoenebeck 200 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 schoenebeck 221 return "ALSA";
451 schoenebeck 200 }
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 221 String s = "$Revision: 1.13 $";
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

  ViewVC Help
Powered by ViewVC