/[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 143 - (show annotations) (download)
Wed Jun 23 18:54:08 2004 UTC (19 years, 9 months ago) by capela
File size: 15541 byte(s)
* SET CHANNEL AUDIO_OUTPUT_TYPE <chan> <driver> command is back!
  creates an audio output device instance of the given driver type
  ('Jack' or 'Alsa') with default parameters if none exists,
  otherwise it just picks the first available device and assign
  it to the intended sampler channel.

* The AudioOutputDevice class get's a new pure virtual method,
  Driver(), which is implemented on both of the existing inherited
  classes, AudioOutputDeviceAlsa and AudioOutputDeviceJack, with
  the sole purpose to return the driver type name as a String
  ('Alsa' and 'Jack', respectively).

* The quoting on the filename argument for the LOAD INSTRUMENT
  command has been made optional; you can have both ways, with
  single quotes or none, keeping compability with older LSCP
  specification.

* An additional sanity check is made on LOAD INSTRUMENT, whether
  the sampler channel has an audio output device assigned, thus
  preventing the server from crashing on instrument file load.

* The GET AUDIO_OUTPUT_DEVICE INFO now includes the missing
  'driver' item, as predicted by the draft protocol document.

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::Driver() {
267 return "Alsa";
268 }
269
270 String AudioOutputDeviceAlsa::Description() {
271 return "Advanced Linux Sound Architecture";
272 }
273
274 String AudioOutputDeviceAlsa::Version() {
275 String s = "$Revision: 1.7 $";
276 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
277 }
278
279 std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::AvailableParameters() {
280 // FIXME: not a good solution to get the commot parameters (ACTIVE,SAMPERATE,CHANNELS which have to be offered by all audio output drivers)
281 std::map<String,DeviceCreationParameter*> available_parameters = AudioOutputDevice::AvailableParameters();
282 static ParameterCard param_card(NULL);
283 static ParameterFragments param_fragments(NULL);
284 static ParameterFragmentSize param_fragmentsize(NULL);
285 available_parameters["card"] = &param_card; // additional parameter, individually for this driver
286 available_parameters["fragments"] = &param_fragments; // additional parameter, individually for this driver
287 available_parameters["fragmentsize"] = &param_fragmentsize; // additional parameter, individually for this driver
288 return available_parameters;
289 }
290
291 /**
292 * Entry point for the thread.
293 */
294 int AudioOutputDeviceAlsa::Main() {
295 while (true) {
296
297 // let all connected engines render 'FragmentSize' sample points
298 RenderAudio(FragmentSize);
299
300 // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
301 // range (-32768..+32767), check clipping and copy to Alsa output buffer
302 // (note: we use interleaved output method to Alsa)
303 for (int c = 0; c < uiAlsaChannels; c++) {
304 float* in = Channels[c]->Buffer();
305 for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
306 float sample_point = in[i] * 32768.0f;
307 if (sample_point < -32768.0) sample_point = -32768.0;
308 if (sample_point > 32767.0) sample_point = 32767.0;
309 pAlsaOutputBuffer[o] = (int16_t) sample_point;
310 }
311 }
312
313 // output sound
314 int res = Output();
315 if (res < 0) {
316 fprintf(stderr, "Alsa: Audio output error, exiting.\n");
317 exit(EXIT_FAILURE);
318 }
319 }
320 }
321
322 /**
323 * Will be called after every audio fragment cycle, to output the audio data
324 * of the current fragment to the soundcard.
325 *
326 * @returns 0 on success, a value < 0 on error
327 */
328 int AudioOutputDeviceAlsa::Output() {
329 int err = snd_pcm_writei(pcm_handle, pAlsaOutputBuffer, FragmentSize);
330 if (err < 0) {
331 fprintf(stderr, "Error snd_pcm_writei failed: %s\n", snd_strerror(err));
332 return -1;
333 }
334 return 0;
335 }
336
337 } // namespace LinuxSampler
338

  ViewVC Help
Powered by ViewVC