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

Contents of /linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceAlsa.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 208 - (show 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 /***************************************************************************
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 String s = "$Revision: 1.10 $";
277 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