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

Annotation of /linuxsampler/trunk/src/audiodriver/AudioOutputDeviceAlsa.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 136 - (hide annotations) (download)
Sun Jun 20 16:03:35 2004 UTC (19 years, 10 months ago) by senkov
File size: 14889 byte(s)
* Update audiodriver to supply parameters in compliance
 with the latest LSCP spec

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

  ViewVC Help
Powered by ViewVC