Commit | Line | Data |
---|---|---|
ef79bbde P |
1 | /*************************************************************************** |
2 | alsa.c - description | |
3 | ------------------- | |
4 | begin : Sat Mar 01 2003 | |
5 | copyright : (C) 2002 by Pete Bernert | |
6 | email : BlackDove@addcom.de | |
7 | ***************************************************************************/ | |
8 | /*************************************************************************** | |
9 | * * | |
10 | * This program is free software; you can redistribute it and/or modify * | |
11 | * it under the terms of the GNU General Public License as published by * | |
12 | * the Free Software Foundation; either version 2 of the License, or * | |
13 | * (at your option) any later version. See also the license.txt file for * | |
14 | * additional informations. * | |
15 | * * | |
16 | ***************************************************************************/ | |
17 | ||
07c13dfd | 18 | #include <stdio.h> |
650adfd2 | 19 | #include <string.h> |
ef79bbde P |
20 | #define ALSA_PCM_NEW_HW_PARAMS_API |
21 | #define ALSA_PCM_NEW_SW_PARAMS_API | |
22 | #include <alsa/asoundlib.h> | |
07c13dfd | 23 | #include "out.h" |
ef79bbde P |
24 | |
25 | static snd_pcm_t *handle = NULL; | |
26 | static snd_pcm_uframes_t buffer_size; | |
27 | ||
1b908f9e | 28 | static void alsa_finish(void); |
29 | ||
ef79bbde | 30 | // SETUP SOUND |
07c13dfd | 31 | static int alsa_init(void) |
ef79bbde P |
32 | { |
33 | snd_pcm_hw_params_t *hwparams; | |
34 | snd_pcm_status_t *status; | |
1b908f9e | 35 | snd_ctl_t *ctl_handle = NULL; |
36 | snd_ctl_card_info_t *info; | |
ef79bbde P |
37 | unsigned int pspeed; |
38 | int pchannels; | |
39 | int format; | |
40 | unsigned int buffer_time = 100000; | |
41 | unsigned int period_time = buffer_time / 4; | |
1b908f9e | 42 | const char *alsa_name = "default"; |
43 | const char *name; | |
44 | int retval = -1; | |
ef79bbde P |
45 | int err; |
46 | ||
1b908f9e | 47 | name = getenv("ALSA_NAME"); |
48 | if (name != NULL) | |
49 | alsa_name = name; | |
50 | ||
51 | snd_ctl_card_info_alloca(&info); | |
52 | if ((err = snd_ctl_open(&ctl_handle, alsa_name, 0)) < 0) { | |
53 | printf("control open: %s\n", snd_strerror(err)); | |
54 | } | |
55 | else if ((err = snd_ctl_card_info(ctl_handle, info)) < 0) { | |
56 | printf("control info: %s\n", snd_strerror(err)); | |
57 | snd_ctl_card_info_clear(info); | |
58 | } | |
59 | if (ctl_handle != NULL) | |
60 | snd_ctl_close(ctl_handle); | |
61 | ||
62 | name = snd_ctl_card_info_get_name(info); | |
63 | if (name != NULL) { | |
64 | if (strcasecmp(name, "PulseAudio") == 0) { | |
65 | // PulseAudio's ALSA emulation is known to be broken.. | |
66 | printf("alsa: refusing to run under PulseAudio's emulation\n"); | |
67 | return -1; | |
68 | } | |
69 | else { | |
70 | printf("alsa: using '%s', set ALSA_NAME to change\n", name); | |
71 | } | |
72 | } | |
73 | ||
97ea4077 | 74 | pchannels=2; |
ef79bbde P |
75 | |
76 | pspeed = 44100; | |
77 | format = SND_PCM_FORMAT_S16; | |
78 | ||
1b908f9e | 79 | if ((err = snd_pcm_open(&handle, alsa_name, |
ef79bbde P |
80 | SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) |
81 | { | |
82 | printf("Audio open error: %s\n", snd_strerror(err)); | |
07c13dfd | 83 | return -1; |
ef79bbde P |
84 | } |
85 | ||
86 | if((err = snd_pcm_nonblock(handle, 0))<0) | |
87 | { | |
88 | printf("Can't set blocking moded: %s\n", snd_strerror(err)); | |
1b908f9e | 89 | goto out; |
ef79bbde P |
90 | } |
91 | ||
92 | snd_pcm_hw_params_alloca(&hwparams); | |
93 | ||
94 | if((err=snd_pcm_hw_params_any(handle, hwparams))<0) | |
95 | { | |
96 | printf("Broken configuration for this PCM: %s\n", snd_strerror(err)); | |
1b908f9e | 97 | goto out; |
ef79bbde P |
98 | } |
99 | ||
100 | if((err=snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED))<0) | |
101 | { | |
102 | printf("Access type not available: %s\n", snd_strerror(err)); | |
1b908f9e | 103 | goto out; |
ef79bbde P |
104 | } |
105 | ||
106 | if((err=snd_pcm_hw_params_set_format(handle, hwparams, format))<0) | |
107 | { | |
108 | printf("Sample format not available: %s\n", snd_strerror(err)); | |
1b908f9e | 109 | goto out; |
ef79bbde P |
110 | } |
111 | ||
112 | if((err=snd_pcm_hw_params_set_channels(handle, hwparams, pchannels))<0) | |
113 | { | |
114 | printf("Channels count not available: %s\n", snd_strerror(err)); | |
1b908f9e | 115 | goto out; |
ef79bbde P |
116 | } |
117 | ||
118 | if((err=snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0))<0) | |
119 | { | |
120 | printf("Rate not available: %s\n", snd_strerror(err)); | |
1b908f9e | 121 | goto out; |
ef79bbde P |
122 | } |
123 | ||
124 | if((err=snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0))<0) | |
125 | { | |
126 | printf("Buffer time error: %s\n", snd_strerror(err)); | |
1b908f9e | 127 | goto out; |
ef79bbde P |
128 | } |
129 | ||
130 | if((err=snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0))<0) | |
131 | { | |
132 | printf("Period time error: %s\n", snd_strerror(err)); | |
1b908f9e | 133 | goto out; |
ef79bbde P |
134 | } |
135 | ||
136 | if((err=snd_pcm_hw_params(handle, hwparams))<0) | |
137 | { | |
138 | printf("Unable to install hw params: %s\n", snd_strerror(err)); | |
1b908f9e | 139 | goto out; |
ef79bbde P |
140 | } |
141 | ||
142 | snd_pcm_status_alloca(&status); | |
143 | if((err=snd_pcm_status(handle, status))<0) | |
144 | { | |
145 | printf("Unable to get status: %s\n", snd_strerror(err)); | |
1b908f9e | 146 | goto out; |
ef79bbde P |
147 | } |
148 | ||
149 | buffer_size = snd_pcm_status_get_avail(status); | |
1b908f9e | 150 | retval = 0; |
151 | ||
152 | out: | |
153 | if (retval != 0) | |
154 | alsa_finish(); | |
155 | return retval; | |
ef79bbde P |
156 | } |
157 | ||
158 | // REMOVE SOUND | |
07c13dfd | 159 | static void alsa_finish(void) |
ef79bbde P |
160 | { |
161 | if(handle != NULL) | |
162 | { | |
163 | snd_pcm_drop(handle); | |
164 | snd_pcm_close(handle); | |
165 | handle = NULL; | |
166 | } | |
167 | } | |
168 | ||
169 | // GET BYTES BUFFERED | |
07c13dfd | 170 | static int alsa_busy(void) |
ef79bbde | 171 | { |
07c13dfd | 172 | int l; |
ef79bbde P |
173 | |
174 | if (handle == NULL) // failed to open? | |
f8edb5bc | 175 | return 1; |
ef79bbde P |
176 | l = snd_pcm_avail(handle); |
177 | if (l < 0) return 0; | |
178 | if (l < buffer_size / 2) // can we write in at least the half of fragments? | |
f8edb5bc | 179 | l = 1; // -> no? wait |
ef79bbde P |
180 | else l = 0; // -> else go on |
181 | ||
182 | return l; | |
183 | } | |
184 | ||
185 | // FEED SOUND DATA | |
07c13dfd | 186 | static void alsa_feed(void *pSound, int lBytes) |
ef79bbde | 187 | { |
650adfd2 | 188 | char sbuf[4096]; |
189 | ||
ef79bbde P |
190 | if (handle == NULL) return; |
191 | ||
192 | if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN) | |
650adfd2 | 193 | { |
194 | memset(sbuf, 0, sizeof(sbuf)); | |
195 | snd_pcm_prepare(handle); | |
196 | snd_pcm_writei(handle, sbuf, sizeof(sbuf) / 4); | |
197 | snd_pcm_writei(handle, sbuf, sizeof(sbuf) / 4); | |
e541b8e0 | 198 | snd_pcm_writei(handle, sbuf, sizeof(sbuf) / 4); |
199 | } | |
200 | else | |
201 | { | |
202 | int l = snd_pcm_avail(handle); | |
203 | if (l < lBytes / 4) | |
204 | { | |
205 | if (l == 0) | |
206 | return; | |
207 | ||
208 | lBytes = l * 4; | |
209 | } | |
650adfd2 | 210 | } |
e541b8e0 | 211 | |
212 | snd_pcm_writei(handle, pSound, lBytes / 4); | |
ef79bbde | 213 | } |
07c13dfd | 214 | |
215 | void out_register_alsa(struct out_driver *drv) | |
216 | { | |
217 | drv->name = "alsa"; | |
218 | drv->init = alsa_init; | |
219 | drv->finish = alsa_finish; | |
220 | drv->busy = alsa_busy; | |
221 | drv->feed = alsa_feed; | |
222 | } |