/[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 53 - (show annotations) (download)
Mon Apr 26 17:15:51 2004 UTC (19 years, 11 months ago) by schoenebeck
File size: 12500 byte(s)
* completely restructured source tree
* implemented multi channel support
* implemented instrument manager, which controls sharing of instruments
  between multiple sampler engines / sampler channels
* created abstract classes 'AudioOutputDevice' and 'MidiInputDevice' for
  convenient implementation of further audio output driver and MIDI input
  driver for LinuxSampler
* implemented following LSCP commands: 'SET CHANNEL MIDI INPUT TYPE',
  'LOAD ENGINE', 'GET CHANNELS', 'ADD CHANNEL', 'REMOVE CHANNEL',
  'SET CHANNEL AUDIO OUTPUT TYPE'
* temporarily removed all command line options
* LSCP server is now launched by default

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003 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
25 namespace LinuxSampler {
26
27 /**
28 * Open and initialize Alsa output device with given parameters.
29 *
30 * @param Channels - number of audio channels
31 * @param Fragments - number of audio fragments
32 * @param FragmentSize - size of each fragment (in sample points)
33 * @param Card - Alsa soundcard ID (optional)
34 * @throws AudioOutputException if output device cannot be opened
35 */
36 AudioOutputDeviceAlsa::AudioOutputDeviceAlsa(uint Channels, uint Samplerate, uint Fragments, uint FragmentSize, String Card) : Thread(true, 1, 0) {
37 pcm_handle = NULL;
38 stream = SND_PCM_STREAM_PLAYBACK;
39 this->uiAlsaChannels = Channels;
40 this->uiSamplerate = Samplerate;
41 this->FragmentSize = FragmentSize;
42
43 if (HardwareParametersSupported(Channels, Samplerate, Fragments, FragmentSize)) {
44 pcm_name = "hw:" + Card;
45 }
46 else {
47 printf("Warning: your soundcard doesn't support chosen hardware parameters; ");
48 printf("trying to compensate support lack with plughw...");
49 fflush(stdout);
50 pcm_name = "plughw:" + Card;
51 }
52
53 int err;
54
55 snd_pcm_hw_params_alloca(&hwparams); // Allocate the snd_pcm_hw_params_t structure on the stack.
56
57 /* Open PCM. The last parameter of this function is the mode. */
58 /* If this is set to 0, the standard mode is used. Possible */
59 /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC. */
60 /* If SND_PCM_NONBLOCK is used, read / write access to the */
61 /* PCM device will return immediately. If SND_PCM_ASYNC is */
62 /* specified, SIGIO will be emitted whenever a period has */
63 /* been completely processed by the soundcard. */
64 if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0)) < 0) {
65 throw AudioOutputException(String("Error opening PCM device ") + pcm_name + ": " + snd_strerror(err));
66 }
67
68 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
69 throw AudioOutputException(String("Error, cannot initialize hardware parameter structure: ") + snd_strerror(err));
70 }
71
72 /* Set access type. This can be either */
73 /* SND_PCM_ACCESS_RW_INTERLEAVED or */
74 /* SND_PCM_ACCESS_RW_NONINTERLEAVED. */
75 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
76 throw AudioOutputException(String("Error snd_pcm_hw_params_set_access: ") + snd_strerror(err));
77 }
78
79 /* Set sample format */
80 #if WORDS_BIGENDIAN
81 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE)) < 0)
82 #else // little endian
83 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
84 #endif
85 {
86 throw AudioOutputException(String("Error setting sample format: ") + snd_strerror(err));
87 }
88
89 int dir = 0;
90
91 /* Set sample rate. If the exact rate is not supported */
92 /* by the hardware, use nearest possible rate. */
93 #if ALSA_MAJOR > 0
94 if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &Samplerate, &dir)) < 0)
95 #else
96 if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, Samplerate, &dir)) < 0)
97 #endif
98 {
99 throw AudioOutputException(String("Error setting sample rate: ") + snd_strerror(err));
100 }
101
102 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, Channels)) < 0) {
103 throw AudioOutputException(String("Error setting number of channels: ") + snd_strerror(err));
104 }
105
106 /* Set number of periods. Periods used to be called fragments. */
107 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, Fragments, dir)) < 0) {
108 throw AudioOutputException(String("Error setting number of periods: ") + snd_strerror(err));
109 }
110
111 /* Set buffer size (in frames). The resulting latency is given by */
112 /* latency = periodsize * periods / (rate * bytes_per_frame) */
113 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (FragmentSize * Fragments))) < 0) {
114 throw AudioOutputException(String("Error setting buffersize: ") + snd_strerror(err));
115 }
116
117 /* Apply HW parameter settings to */
118 /* PCM device and prepare device */
119 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
120 throw AudioOutputException(String("Error setting HW params: ") + snd_strerror(err));
121 }
122
123 if (snd_pcm_sw_params_malloc(&swparams) != 0) {
124 throw AudioOutputException(String("Error in snd_pcm_sw_params_malloc: ") + snd_strerror(err));
125 }
126
127 if (snd_pcm_sw_params_current(pcm_handle, swparams) != 0) {
128 throw AudioOutputException(String("Error in snd_pcm_sw_params_current: ") + snd_strerror(err));
129 }
130
131 if (snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xffffffff) != 0) {
132 throw AudioOutputException(String("Error in snd_pcm_sw_params_set_stop_threshold: ") + snd_strerror(err));
133 }
134
135 if (snd_pcm_sw_params(pcm_handle, swparams) != 0) {
136 throw AudioOutputException(String("Error in snd_pcm_sw_params: ") + snd_strerror(err));
137 }
138
139 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
140 throw AudioOutputException(String("Error snd_pcm_prepare: ") + snd_strerror(err));
141 }
142
143 // allocate Alsa output buffer
144 pAlsaOutputBuffer = new int16_t[Channels * FragmentSize];
145
146 // create audio channels for this audio device to which the sampler engines can write to
147 for (int i = 0; i < Channels; i++) this->Channels.push_back(new AudioChannel(FragmentSize));
148 }
149
150 AudioOutputDeviceAlsa::~AudioOutputDeviceAlsa() {
151 //dmsg(0,("Stopping Alsa Thread..."));
152 //StopThread(); //FIXME: commented out due to a bug in thread.cpp (StopThread() doesn't return at all)
153 //dmsg(0,("OK\n"));
154
155 //FIXME: currently commented out due to segfault
156 //snd_pcm_close(pcm_handle);
157
158 // destroy all audio channels
159 for (int c = 0; c < Channels.size(); c++) delete Channels[c];
160
161 if (pAlsaOutputBuffer) {
162 //FIXME: currently commented out due to segfault
163 //delete[] pOutputBuffer;
164 }
165 }
166
167 /**
168 * Checks if sound card supports the chosen parameters.
169 *
170 * @returns true if hardware supports it
171 */
172 bool AudioOutputDeviceAlsa::HardwareParametersSupported(uint channels, int samplerate, uint numfragments, uint fragmentsize) {
173 pcm_name = "hw:0,0";
174 if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false;
175 snd_pcm_hw_params_alloca(&hwparams);
176 if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
177 snd_pcm_close(pcm_handle);
178 return false;
179 }
180 if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
181 snd_pcm_close(pcm_handle);
182 return false;
183 }
184 #if WORDS_BIGENDIAN
185 if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0)
186 #else // little endian
187 if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
188 #endif
189 {
190 snd_pcm_close(pcm_handle);
191 return false;
192 }
193 int dir = 0;
194 if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) {
195 snd_pcm_close(pcm_handle);
196 return false;
197 }
198 if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) {
199 snd_pcm_close(pcm_handle);
200 return false;
201 }
202 if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) {
203 snd_pcm_close(pcm_handle);
204 return false;
205 }
206 if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) {
207 snd_pcm_close(pcm_handle);
208 return false;
209 }
210
211 snd_pcm_close(pcm_handle);
212 return true;
213 }
214
215 void AudioOutputDeviceAlsa::Play() {
216 StartThread();
217 }
218
219 bool AudioOutputDeviceAlsa::IsPlaying() {
220 return IsRunning(); // if Thread is running
221 }
222
223 void AudioOutputDeviceAlsa::Stop() {
224 StopThread();
225 }
226
227 void AudioOutputDeviceAlsa::AcquireChannels(uint Channels) {
228 if (Channels > uiAlsaChannels) {
229 // just create mix channel(s)
230 for (int i = uiAlsaChannels; i < Channels; i++) {
231 AudioChannel* pNewChannel = new AudioChannel(this->Channels[i % uiAlsaChannels]);
232 this->Channels.push_back(pNewChannel);
233 }
234 }
235 }
236
237 uint AudioOutputDeviceAlsa::MaxSamplesPerCycle() {
238 return FragmentSize;
239 }
240
241 uint AudioOutputDeviceAlsa::SampleRate() {
242 return uiSamplerate;
243 }
244
245 int AudioOutputDeviceAlsa::Main() {
246 while (true) {
247
248 // let all connected engines render 'FragmentSize' sample points
249 RenderAudio(FragmentSize);
250
251 // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
252 // range (-32768..+32767), check clipping and copy to Alsa output buffer
253 // (note: we use interleaved output method to Alsa)
254 for (int c = 0; c < uiAlsaChannels; c++) {
255 float* in = Channels[c]->Buffer();
256 for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
257 float sample_point = in[i] * 32768.0f;
258 if (sample_point < -32768.0) sample_point = -32768.0;
259 if (sample_point > 32767.0) sample_point = 32767.0;
260 pAlsaOutputBuffer[o] = (int16_t) sample_point;
261 }
262 }
263
264 // output sound
265 int res = Output();
266 if (res < 0) {
267 fprintf(stderr, "Alsa: Audio output error, exiting.\n");
268 exit(EXIT_FAILURE);
269 }
270 }
271 }
272
273 /**
274 * Will be called after every audio fragment cycle, to output the audio data
275 * of the current fragment to the soundcard.
276 *
277 * @returns 0 on success, a value < 0 on error
278 */
279 int AudioOutputDeviceAlsa::Output() {
280 int err = snd_pcm_writei(pcm_handle, pAlsaOutputBuffer, FragmentSize);
281 if (err < 0) {
282 fprintf(stderr, "Error snd_pcm_writei failed: %s\n", snd_strerror(err));
283 return -1;
284 }
285 return 0;
286 }
287
288 } // namespace LinuxSampler
289

  ViewVC Help
Powered by ViewVC