/[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 139 - (show annotations) (download)
Sun Jun 20 23:42:44 2004 UTC (19 years, 10 months ago) by senkov
File size: 15466 byte(s)
* Fixes and hacks to make CREATE AUDIO_OUTPUT_DEVICE work

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 //FIXME: common stuff still has to be created somewhere.
174 // AND filled with values, but we are only passing result
175 // of this method to base, so common stuff will be part
176 // of this result for now.
177 result["channels"] = OptionalParameter<ParameterChannels>::New(this, Parameters["channels"]);
178 result["samplerate"] = OptionalParameter<ParameterSampleRate>::New(this, Parameters["samplerate"]);
179 result["active"] = OptionalParameter<ParameterActive>::New(this, Parameters["active"]);
180
181 //Alsa specific
182 result["card"] = OptionalParameter<ParameterCard>::New(this, Parameters["card"]); // additional parameter, individually for this driver
183 result["fragments"] = OptionalParameter<ParameterFragments>::New(this, Parameters["fragments"]); // additional parameter, individually for this driver
184 result["fragmentsize"] = OptionalParameter<ParameterFragmentSize>::New(this, Parameters["fragmentsize"]); // additional parameter, individually for this driver
185 return result;
186 }
187
188 /**
189 * Checks if sound card supports the chosen parameters.
190 *
191 * @returns true if hardware supports it
192 */
193 bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) {
194 pcm_name = "hw:" + card;
195 if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false;
196 snd_pcm_hw_params_alloca(&hwparams);
197 if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
198 snd_pcm_close(pcm_handle);
199 return false;
200 }
201 if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
202 snd_pcm_close(pcm_handle);
203 return false;
204 }
205 #if WORDS_BIGENDIAN
206 if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0)
207 #else // little endian
208 if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
209 #endif
210 {
211 snd_pcm_close(pcm_handle);
212 return false;
213 }
214 int dir = 0;
215 if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) {
216 snd_pcm_close(pcm_handle);
217 return false;
218 }
219 if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) {
220 snd_pcm_close(pcm_handle);
221 return false;
222 }
223 if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) {
224 snd_pcm_close(pcm_handle);
225 return false;
226 }
227 if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) {
228 snd_pcm_close(pcm_handle);
229 return false;
230 }
231
232 snd_pcm_close(pcm_handle);
233 return true;
234 }
235
236 void AudioOutputDeviceAlsa::Play() {
237 StartThread();
238 }
239
240 bool AudioOutputDeviceAlsa::IsPlaying() {
241 return IsRunning(); // if Thread is running
242 }
243
244 void AudioOutputDeviceAlsa::Stop() {
245 StopThread();
246 }
247
248 void AudioOutputDeviceAlsa::AcquireChannels(uint Channels) {
249 if (Channels > uiAlsaChannels) {
250 // just create mix channel(s)
251 for (int i = uiAlsaChannels; i < Channels; i++) {
252 AudioChannel* pNewChannel = new AudioChannel(this->Channels[i % uiAlsaChannels]);
253 this->Channels.push_back(pNewChannel);
254 }
255 }
256 }
257
258 uint AudioOutputDeviceAlsa::MaxSamplesPerCycle() {
259 return FragmentSize;
260 }
261
262 uint AudioOutputDeviceAlsa::SampleRate() {
263 return uiSamplerate;
264 }
265
266 String AudioOutputDeviceAlsa::Description() {
267 return "Advanced Linux Sound Architecture";
268 }
269
270 String AudioOutputDeviceAlsa::Version() {
271 String s = "$Revision: 1.6 $";
272 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
273 }
274
275 std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::AvailableParameters() {
276 // FIXME: not a good solution to get the commot parameters (ACTIVE,SAMPERATE,CHANNELS which have to be offered by all audio output drivers)
277 std::map<String,DeviceCreationParameter*> available_parameters = AudioOutputDevice::AvailableParameters();
278 static ParameterCard param_card(NULL);
279 static ParameterFragments param_fragments(NULL);
280 static ParameterFragmentSize param_fragmentsize(NULL);
281 available_parameters["card"] = &param_card; // additional parameter, individually for this driver
282 available_parameters["fragments"] = &param_fragments; // additional parameter, individually for this driver
283 available_parameters["fragmentsize"] = &param_fragmentsize; // additional parameter, individually for this driver
284 return available_parameters;
285 }
286
287 /**
288 * Entry point for the thread.
289 */
290 int AudioOutputDeviceAlsa::Main() {
291 while (true) {
292
293 // let all connected engines render 'FragmentSize' sample points
294 RenderAudio(FragmentSize);
295
296 // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
297 // range (-32768..+32767), check clipping and copy to Alsa output buffer
298 // (note: we use interleaved output method to Alsa)
299 for (int c = 0; c < uiAlsaChannels; c++) {
300 float* in = Channels[c]->Buffer();
301 for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
302 float sample_point = in[i] * 32768.0f;
303 if (sample_point < -32768.0) sample_point = -32768.0;
304 if (sample_point > 32767.0) sample_point = 32767.0;
305 pAlsaOutputBuffer[o] = (int16_t) sample_point;
306 }
307 }
308
309 // output sound
310 int res = Output();
311 if (res < 0) {
312 fprintf(stderr, "Alsa: Audio output error, exiting.\n");
313 exit(EXIT_FAILURE);
314 }
315 }
316 }
317
318 /**
319 * Will be called after every audio fragment cycle, to output the audio data
320 * of the current fragment to the soundcard.
321 *
322 * @returns 0 on success, a value < 0 on error
323 */
324 int AudioOutputDeviceAlsa::Output() {
325 int err = snd_pcm_writei(pcm_handle, pAlsaOutputBuffer, FragmentSize);
326 if (err < 0) {
327 fprintf(stderr, "Error snd_pcm_writei failed: %s\n", snd_strerror(err));
328 return -1;
329 }
330 return 0;
331 }
332
333 } // namespace LinuxSampler
334

  ViewVC Help
Powered by ViewVC