/[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 174 - (show annotations) (download)
Tue Jul 6 03:27:38 2004 UTC (19 years, 9 months ago) by senkov
File size: 14160 byte(s)
* Reworked the infrastructure to allow for parameter
registration and creation
* Changed alsa audio output and midi drivers
to work with new infrastructure

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 "../drivers/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.8 $";
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