/[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 208 - (hide annotations) (download)
Thu Jul 15 22:20:28 2004 UTC (19 years, 9 months ago) by schoenebeck
File size: 14119 byte(s)
removed unnecessary #include

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

  ViewVC Help
Powered by ViewVC