/[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 200 - (hide annotations) (download)
Tue Jul 13 22:04:16 2004 UTC (19 years, 9 months ago) by schoenebeck
File size: 14152 byte(s)
moved directory '/src/audiodriver' -> '/src/drivers/audio'

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 "../InputOutputDevice.h"
24     #include "AudioOutputDeviceAlsa.h"
25     #include "AudioOutputDeviceFactory.h"
26    
27     namespace LinuxSampler {
28    
29     REGISTER_AUDIO_OUTPUT_DRIVER(AudioOutputDeviceAlsa);
30    
31     /* Common parameters for now they'll have to be registered here. */
32     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterActive);
33     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterSampleRate);
34     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterChannels);
35    
36     /* Driver specific parameters */
37     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterCard);
38     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterFragments);
39     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceAlsa, ParameterFragmentSize);
40    
41     /**
42     * Create and initialize Alsa audio output device with given parameters.
43     *
44     * @param Parameters - optional parameters
45     * @throws AudioOutputException if output device cannot be opened
46     */
47     AudioOutputDeviceAlsa::AudioOutputDeviceAlsa(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters), Thread(true, 1, 0) {
48     pcm_handle = NULL;
49     stream = SND_PCM_STREAM_PLAYBACK;
50     this->uiAlsaChannels = ((DeviceCreationParameterInt*)Parameters["channels"])->ValueAsInt();
51     this->uiSamplerate = ((DeviceCreationParameterInt*)Parameters["samplerate"])->ValueAsInt();
52     this->FragmentSize = ((DeviceCreationParameterInt*)Parameters["fragmentsize"])->ValueAsInt();
53     uint Fragments = ((DeviceCreationParameterInt*)Parameters["fragments"])->ValueAsInt();
54     String Card = Parameters["card"]->Value();
55    
56     dmsg(1,("Checking if hw parameters supported...\n"));
57     if (HardwareParametersSupported(Card, uiAlsaChannels, uiSamplerate, Fragments, FragmentSize)) {
58     pcm_name = "hw:" + Card;
59     }
60     else {
61     printf("Warning: your soundcard doesn't support chosen hardware parameters; ");
62     printf("trying to compensate support lack with plughw...");
63     fflush(stdout);
64     pcm_name = "plughw:" + Card;
65     }
66     dmsg(1,("HW check completed.\n"));
67    
68     int err;
69    
70     snd_pcm_hw_params_alloca(&hwparams); // Allocate the snd_pcm_hw_params_t structure on the stack.
71    
72     /* Open PCM. The last parameter of this function is the mode. */
73     /* If this is set to 0, the standard mode is used. Possible */
74     /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC. */
75     /* If SND_PCM_NONBLOCK is used, read / write access to the */
76     /* PCM device will return immediately. If SND_PCM_ASYNC is */
77     /* specified, SIGIO will be emitted whenever a period has */
78     /* been completely processed by the soundcard. */
79     if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0)) < 0) {
80     throw AudioOutputException(String("Error opening PCM device ") + pcm_name + ": " + snd_strerror(err));
81     }
82    
83     if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
84     throw AudioOutputException(String("Error, cannot initialize hardware parameter structure: ") + snd_strerror(err));
85     }
86    
87     /* Set access type. This can be either */
88     /* SND_PCM_ACCESS_RW_INTERLEAVED or */
89     /* SND_PCM_ACCESS_RW_NONINTERLEAVED. */
90     if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
91     throw AudioOutputException(String("Error snd_pcm_hw_params_set_access: ") + snd_strerror(err));
92     }
93    
94     /* Set sample format */
95     #if WORDS_BIGENDIAN
96     if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE)) < 0)
97     #else // little endian
98     if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
99     #endif
100     {
101     throw AudioOutputException(String("Error setting sample format: ") + snd_strerror(err));
102     }
103    
104     int dir = 0;
105    
106     /* Set sample rate. If the exact rate is not supported */
107     /* by the hardware, use nearest possible rate. */
108     #if ALSA_MAJOR > 0
109     if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &uiSamplerate, &dir)) < 0)
110     #else
111     if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, uiSamplerate, &dir)) < 0)
112     #endif
113     {
114     throw AudioOutputException(String("Error setting sample rate: ") + snd_strerror(err));
115     }
116    
117     if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, uiAlsaChannels)) < 0) {
118     throw AudioOutputException(String("Error setting number of channels: ") + snd_strerror(err));
119     }
120    
121     /* Set number of periods. Periods used to be called fragments. */
122     if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, Fragments, dir)) < 0) {
123     throw AudioOutputException(String("Error setting number of periods: ") + snd_strerror(err));
124     }
125    
126     /* Set buffer size (in frames). The resulting latency is given by */
127     /* latency = periodsize * periods / (rate * bytes_per_frame) */
128     if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (FragmentSize * Fragments))) < 0) {
129     throw AudioOutputException(String("Error setting buffersize: ") + snd_strerror(err));
130     }
131    
132     /* Apply HW parameter settings to */
133     /* PCM device and prepare device */
134     if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
135     throw AudioOutputException(String("Error setting HW params: ") + snd_strerror(err));
136     }
137    
138     if (snd_pcm_sw_params_malloc(&swparams) != 0) {
139     throw AudioOutputException(String("Error in snd_pcm_sw_params_malloc: ") + snd_strerror(err));
140     }
141    
142     if (snd_pcm_sw_params_current(pcm_handle, swparams) != 0) {
143     throw AudioOutputException(String("Error in snd_pcm_sw_params_current: ") + snd_strerror(err));
144     }
145    
146     if (snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xffffffff) != 0) {
147     throw AudioOutputException(String("Error in snd_pcm_sw_params_set_stop_threshold: ") + snd_strerror(err));
148     }
149    
150     if (snd_pcm_sw_params(pcm_handle, swparams) != 0) {
151     throw AudioOutputException(String("Error in snd_pcm_sw_params: ") + snd_strerror(err));
152     }
153    
154     if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
155     throw AudioOutputException(String("Error snd_pcm_prepare: ") + snd_strerror(err));
156     }
157    
158     // allocate Alsa output buffer
159     pAlsaOutputBuffer = new int16_t[uiAlsaChannels * FragmentSize];
160    
161     // create audio channels for this audio device to which the sampler engines can write to
162     for (int i = 0; i < uiAlsaChannels; i++) this->Channels.push_back(new AudioChannel(FragmentSize));
163    
164     if (((DeviceCreationParameterBool*)Parameters["active"])->ValueAsBool()) {
165     Play();
166     }
167     }
168    
169     AudioOutputDeviceAlsa::~AudioOutputDeviceAlsa() {
170     //dmsg(0,("Stopping Alsa Thread..."));
171     //StopThread(); //FIXME: commented out due to a bug in thread.cpp (StopThread() doesn't return at all)
172     //dmsg(0,("OK\n"));
173    
174     //FIXME: currently commented out due to segfault
175     //snd_pcm_close(pcm_handle);
176    
177     // destroy all audio channels
178     for (int c = 0; c < Channels.size(); c++) delete Channels[c];
179    
180     if (pAlsaOutputBuffer) {
181     //FIXME: currently commented out due to segfault
182     //delete[] pOutputBuffer;
183     }
184     }
185    
186     /**
187     * Checks if sound card supports the chosen parameters.
188     *
189     * @returns true if hardware supports it
190     */
191     bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) {
192     pcm_name = "hw:" + card;
193     if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false;
194     snd_pcm_hw_params_alloca(&hwparams);
195     if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
196     snd_pcm_close(pcm_handle);
197     return false;
198     }
199     if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
200     snd_pcm_close(pcm_handle);
201     return false;
202     }
203     #if WORDS_BIGENDIAN
204     if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0)
205     #else // little endian
206     if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
207     #endif
208     {
209     snd_pcm_close(pcm_handle);
210     return false;
211     }
212     int dir = 0;
213     if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) {
214     snd_pcm_close(pcm_handle);
215     return false;
216     }
217     if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) {
218     snd_pcm_close(pcm_handle);
219     return false;
220     }
221     if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) {
222     snd_pcm_close(pcm_handle);
223     return false;
224     }
225     if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) {
226     snd_pcm_close(pcm_handle);
227     return false;
228     }
229    
230     snd_pcm_close(pcm_handle);
231     return true;
232     }
233    
234     void AudioOutputDeviceAlsa::Play() {
235     StartThread();
236     }
237    
238     bool AudioOutputDeviceAlsa::IsPlaying() {
239     return IsRunning(); // if Thread is running
240     }
241    
242     void AudioOutputDeviceAlsa::Stop() {
243     StopThread();
244     }
245    
246     void AudioOutputDeviceAlsa::AcquireChannels(uint Channels) {
247     if (Channels > uiAlsaChannels) {
248     // just create mix channel(s)
249     for (int i = uiAlsaChannels; i < Channels; i++) {
250     AudioChannel* pNewChannel = new AudioChannel(this->Channels[i % uiAlsaChannels]);
251     this->Channels.push_back(pNewChannel);
252     }
253     }
254     }
255    
256     uint AudioOutputDeviceAlsa::MaxSamplesPerCycle() {
257     return FragmentSize;
258     }
259    
260     uint AudioOutputDeviceAlsa::SampleRate() {
261     return uiSamplerate;
262     }
263    
264     String AudioOutputDeviceAlsa::Name() {
265     return "Alsa";
266     }
267    
268     String AudioOutputDeviceAlsa::Driver() {
269     return Name();
270     }
271    
272     String AudioOutputDeviceAlsa::Description() {
273     return "Advanced Linux Sound Architecture";
274     }
275    
276     String AudioOutputDeviceAlsa::Version() {
277     String s = "$Revision: 1.9 $";
278     return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
279     }
280    
281     /**
282     * Entry point for the thread.
283     */
284     int AudioOutputDeviceAlsa::Main() {
285     while (true) {
286    
287     // let all connected engines render 'FragmentSize' sample points
288     RenderAudio(FragmentSize);
289    
290     // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
291     // range (-32768..+32767), check clipping and copy to Alsa output buffer
292     // (note: we use interleaved output method to Alsa)
293     for (int c = 0; c < uiAlsaChannels; c++) {
294     float* in = Channels[c]->Buffer();
295     for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
296     float sample_point = in[i] * 32768.0f;
297     if (sample_point < -32768.0) sample_point = -32768.0;
298     if (sample_point > 32767.0) sample_point = 32767.0;
299     pAlsaOutputBuffer[o] = (int16_t) sample_point;
300     }
301     }
302    
303     // output sound
304     int res = Output();
305     if (res < 0) {
306     fprintf(stderr, "Alsa: Audio output error, exiting.\n");
307     exit(EXIT_FAILURE);
308     }
309     }
310     }
311    
312     /**
313     * Will be called after every audio fragment cycle, to output the audio data
314     * of the current fragment to the soundcard.
315     *
316     * @returns 0 on success, a value < 0 on error
317     */
318     int AudioOutputDeviceAlsa::Output() {
319     int err = snd_pcm_writei(pcm_handle, pAlsaOutputBuffer, FragmentSize);
320     if (err < 0) {
321     fprintf(stderr, "Error snd_pcm_writei failed: %s\n", snd_strerror(err));
322     return -1;
323     }
324     return 0;
325     }
326    
327     } // namespace LinuxSampler
328    

  ViewVC Help
Powered by ViewVC