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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /***************************************************************************
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("Alsa",AudioOutputDeviceAlsa);
29
30 /**
31 * Create and initialize Alsa audio output device with given parameters.
32 *
33 * @param Parameters - optional parameters
34 * @throws AudioOutputException if output device cannot be opened
35 */
36 AudioOutputDeviceAlsa::AudioOutputDeviceAlsa(std::map<String,String> Parameters) : AudioOutputDevice(CreateParameters(Parameters)), Thread(true, 1, 0) {
37 pcm_handle = NULL;
38 stream = SND_PCM_STREAM_PLAYBACK;
39 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
45 dmsg(1,("Checking if hw parameters supported...\n"));
46 if (HardwareParametersSupported(Card, uiAlsaChannels, uiSamplerate, Fragments, FragmentSize)) {
47 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 dmsg(1,("HW check completed.\n"));
56
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 if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &uiSamplerate, &dir)) < 0)
99 #else
100 if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, uiSamplerate, &dir)) < 0)
101 #endif
102 {
103 throw AudioOutputException(String("Error setting sample rate: ") + snd_strerror(err));
104 }
105
106 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, uiAlsaChannels)) < 0) {
107 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 pAlsaOutputBuffer = new int16_t[uiAlsaChannels * FragmentSize];
149
150 // create audio channels for this audio device to which the sampler engines can write to
151 for (int i = 0; i < uiAlsaChannels; i++) this->Channels.push_back(new AudioChannel(FragmentSize));
152 }
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 std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::CreateParameters(std::map<String,String> Parameters) {
172 std::map<String,DeviceCreationParameter*> result;
173 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 return result;
177 }
178
179 /**
180 * Checks if sound card supports the chosen parameters.
181 *
182 * @returns true if hardware supports it
183 */
184 bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) {
185 pcm_name = "hw:" + card;
186 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 String AudioOutputDeviceAlsa::Description() {
258 return "Advanced Linux Sound Architecture";
259 }
260
261 String AudioOutputDeviceAlsa::Version() {
262 String s = "$Revision: 1.5 $";
263 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 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 return available_parameters;
276 }
277
278 /**
279 * Entry point for the thread.
280 */
281 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