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

Annotation of /linuxsampler/trunk/src/audiodriver/AudioOutputDeviceAlsa.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 56 - (hide annotations) (download)
Tue Apr 27 09:21:58 2004 UTC (20 years ago) by schoenebeck
File size: 12500 byte(s)
updated copyright header for 2004

1 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 53 * *
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