Mercurial > hg > xemacs-beta
annotate src/alsaplay.c @ 5518:3cc7470ea71c
gnuclient: if TMPDIR was set and connect failed, try again with /tmp
2011-06-03 Aidan Kehoe <kehoea@parhasard.net>
* gnuslib.c (connect_to_unix_server):
Retry with /tmp as a directory in which to search for Unix sockets
if an attempt to connect with some other directory failed (which
may be because gnuclient and gnuserv don't share an environment
value for TMPDIR, or because gnuserv was compiled with USE_TMPDIR
turned off).
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Fri, 03 Jun 2011 18:40:57 +0100 |
parents | 308d34e9f07d |
children |
rev | line source |
---|---|
3308 | 1 /* Play sound with the ALSA library |
2 Copyright (C) 2006 Jerry James. | |
3 | |
4 This file is part of XEmacs. | |
5 | |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
4976
diff
changeset
|
6 XEmacs is free software: you can redistribute it and/or modify it |
3308 | 7 under the terms of the GNU General Public License as published by the |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
4976
diff
changeset
|
8 Free Software Foundation, either version 3 of the License, or (at your |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
4976
diff
changeset
|
9 option) any later version. |
3308 | 10 |
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
4976
diff
changeset
|
17 along with XEmacs. If not, see <http://www.gnu.org/licenses/>. */ |
3308 | 18 |
19 /* Synched up with: Not in FSF. */ | |
20 | |
21 /* TODO: Support asynchronous sound playing; see the NAS support in sound.c */ | |
22 | |
23 #include <config.h> | |
24 #include "lisp.h" | |
25 #include "sound.h" | |
26 #include "sysfile.h" | |
27 | |
28 /* We can't just include <alsa/asoundlib.h> because it tries to redefine | |
29 * several symbols defined by the previous header files. | |
30 */ | |
3347 | 31 #include <alsa/version.h> |
3308 | 32 #include <alsa/input.h> |
33 #include <alsa/output.h> | |
34 #include <alsa/conf.h> | |
35 #include <alsa/global.h> | |
36 #include <alsa/pcm.h> | |
37 #include <alsa/error.h> | |
38 #include <alsa/hwdep.h> | |
39 #include <alsa/rawmidi.h> | |
40 #include <alsa/control.h> | |
41 #include <alsa/mixer.h> | |
42 | |
3335 | 43 #define ALSA_VERSION(major,minor,sub) (((major)<<16) | ((minor)<<8)| (sub)) |
44 | |
3308 | 45 struct mixer_state |
46 { | |
47 snd_mixer_t *mixer; | |
48 snd_mixer_elem_t *vol_ctl; | |
49 | |
50 /* Which channels need the old volume restored */ | |
51 int reset_front_left; | |
52 int reset_front_center; | |
53 int reset_front_right; | |
54 int reset_rear_left; | |
3335 | 55 int reset_rear_right; |
56 int reset_woofer; | |
57 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
3308 | 58 int reset_rear_center; |
59 int reset_side_left; | |
60 int reset_side_right; | |
3335 | 61 #endif |
3308 | 62 |
63 /* Old volumes for the channels */ | |
64 long front_left_vol; | |
65 long front_center_vol; | |
66 long front_right_vol; | |
67 long rear_left_vol; | |
3335 | 68 long rear_right_vol; |
69 long woofer_vol; | |
70 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
3308 | 71 long rear_center_vol; |
72 long side_left_vol; | |
73 long side_right_vol; | |
3335 | 74 #endif |
3308 | 75 }; |
76 | |
77 /* Assemble a machine half-word in little-endian order */ | |
78 #define HALF_LE(arr,start) (arr[start] + (arr[start + 1] << 8)) | |
79 | |
80 /* Assemble a machine word in little-endian order */ | |
81 #define WORD_LE(arr,start) (arr[start] + (arr[start + 1] << 8) + \ | |
82 (arr[start + 2] << 16) + (arr[start + 3] << 24)) | |
83 | |
84 /* Assemble a machine word in big-endian order */ | |
85 #define WORD_BE(arr,start) ((arr[start] << 24) + (arr[start + 1] << 16) + \ | |
86 (arr[start + 2] << 8) + arr[start + 3]) | |
87 | |
88 /* This function was inspired by miscplay.c. | |
89 * Examine sound data to determine its format. | |
90 * | |
91 * TODO: Detect other formats that ALSA can play, such as GSM and MPEG. | |
92 */ | |
93 static snd_pcm_format_t | |
94 analyze_format (const Binbyte *format, int *speed, int *tracks) | |
95 { | |
96 if (!memcmp (format, "Creative Voice File\x1A\x1A\x00", 22) && | |
97 HALF_LE (format, 22) == ((0x1233 - HALF_LE (format, 24)) & 0xFFFF)) | |
98 { | |
99 /* VOC */ | |
100 *speed = 8000; | |
101 *tracks = 2; | |
102 return SND_PCM_FORMAT_U8; | |
103 } | |
104 else if (!memcmp (format, "RIFF", 4) && !memcmp (format + 8, "WAVEfmt ", 8)) | |
105 { | |
106 /* WAVE */ | |
107 *speed = WORD_LE (format, 24); | |
108 *tracks = format[22]; | |
109 return format[32] / format[22] == 1 | |
110 ? SND_PCM_FORMAT_U8 | |
111 : SND_PCM_FORMAT_S16_LE; | |
112 } | |
113 else if (!memcmp (format, ".snd", 4)) | |
114 { | |
115 /* Sun/NeXT Audio (big endian) */ | |
116 if (WORD_BE (format, 4) < 24) | |
117 { | |
118 *speed = 8000; | |
119 *tracks = 1; | |
120 return SND_PCM_FORMAT_MU_LAW; | |
121 } | |
122 *speed = WORD_BE (format, 16); | |
123 *tracks = format[23]; | |
124 if (!memcmp (format + 12, "\000\000\000", 3)) | |
125 { | |
126 switch (format[15]) | |
127 { | |
128 case 1: | |
129 case 17: | |
130 case 29: | |
131 return SND_PCM_FORMAT_MU_LAW; | |
132 case 2: | |
133 return SND_PCM_FORMAT_S8; | |
134 case 3: | |
135 return SND_PCM_FORMAT_S16_BE; | |
136 case 4: | |
137 return SND_PCM_FORMAT_S24_BE; | |
138 case 5: | |
139 return SND_PCM_FORMAT_S32_BE; | |
140 case 23: | |
141 case 24: | |
142 case 25: | |
143 case 26: | |
144 return SND_PCM_FORMAT_IMA_ADPCM; | |
145 case 27: | |
146 return SND_PCM_FORMAT_A_LAW; | |
147 default: | |
148 break; | |
149 } | |
150 } | |
151 return SND_PCM_FORMAT_UNKNOWN; | |
152 } | |
153 else if (!memcmp (format, ".sd", 4)) | |
154 { | |
155 /* DEC Audio (little endian) */ | |
156 if (WORD_LE (format, 4) < 24) | |
157 { | |
158 *speed = 8000; | |
159 *tracks = 1; | |
160 return SND_PCM_FORMAT_MU_LAW; | |
161 } | |
162 *speed = WORD_LE (format, 16); | |
163 *tracks = format[20]; | |
164 if (!memcmp (format + 13, "\000\000\000", 3)) | |
165 { | |
166 switch (format[12]) | |
167 { | |
168 case 1: | |
169 case 17: | |
170 case 29: | |
171 return SND_PCM_FORMAT_MU_LAW; | |
172 case 2: | |
173 return SND_PCM_FORMAT_S8; | |
174 case 3: | |
175 return SND_PCM_FORMAT_S16_LE; | |
176 case 4: | |
177 return SND_PCM_FORMAT_S24_LE; | |
178 case 5: | |
179 return SND_PCM_FORMAT_S32_LE; | |
180 case 23: | |
181 case 24: | |
182 case 25: | |
183 case 26: | |
184 return SND_PCM_FORMAT_IMA_ADPCM; | |
185 case 27: | |
186 return SND_PCM_FORMAT_A_LAW; | |
187 default: | |
188 break; | |
189 } | |
190 } | |
191 return SND_PCM_FORMAT_UNKNOWN; | |
192 } | |
193 else | |
194 { | |
195 /* We don't know what it is. Guess that it is mono audio in unsigned | |
196 * byte format. Maybe we should error if we reach this point. | |
197 */ | |
198 *speed = 8000; | |
199 *tracks = 1; | |
200 return SND_PCM_FORMAT_U8; | |
201 } | |
202 } | |
203 | |
204 /* Set the volume: if any errors occur, we accept the existing volume */ | |
205 static void | |
206 set_volume (struct mixer_state *mix, int volume) | |
207 { | |
208 snd_mixer_selem_id_t *volume_id; | |
209 long min_vol, max_vol, dev_vol; | |
210 | |
211 if (snd_mixer_open (&mix->mixer, 0) < 0) | |
212 return; | |
213 | |
214 if (snd_mixer_attach (mix->mixer, "default") < 0) | |
215 return; | |
216 | |
217 if (snd_mixer_selem_register (mix->mixer, NULL, NULL) < 0) | |
218 return; | |
219 | |
220 if (snd_mixer_load (mix->mixer) < 0) | |
221 return; | |
222 | |
223 snd_mixer_selem_id_alloca (&volume_id); | |
224 snd_mixer_selem_id_set_name (volume_id, "PCM"); | |
225 | |
226 if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) == NULL) | |
227 { | |
228 snd_mixer_selem_id_set_name (volume_id, "Master"); | |
229 if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) | |
230 == NULL) | |
231 return; | |
232 } | |
233 | |
234 /* Translate the Lisp volume range to the device volume range */ | |
3335 | 235 #if SND_LIB_VERSION < ALSA_VERSION (1, 0, 10) |
236 snd_mixer_selem_get_playback_volume_range (mix->vol_ctl, &min_vol, &max_vol); | |
237 #else | |
3308 | 238 if (snd_mixer_selem_get_playback_volume_range (mix->vol_ctl, &min_vol, |
239 &max_vol) < 0) | |
240 return; | |
3335 | 241 #endif |
3308 | 242 |
243 dev_vol = volume * (max_vol - min_vol) / 100 + min_vol; | |
244 | |
245 /* Record the old volumes */ | |
246 if (snd_mixer_selem_get_playback_volume | |
247 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, &mix->front_left_vol) >= 0) | |
248 mix->reset_front_left = 1; | |
249 | |
250 if (snd_mixer_selem_get_playback_volume | |
251 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, &mix->front_center_vol) >= 0) | |
252 mix->reset_front_center = 1; | |
253 | |
254 if (snd_mixer_selem_get_playback_volume | |
255 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, &mix->front_right_vol) >= 0) | |
256 mix->reset_front_right = 1; | |
257 | |
258 if (snd_mixer_selem_get_playback_volume | |
259 (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, &mix->rear_left_vol) >= 0) | |
260 mix->reset_rear_left = 1; | |
261 | |
262 if (snd_mixer_selem_get_playback_volume | |
3335 | 263 (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, &mix->rear_right_vol) >= 0) |
264 mix->reset_rear_right = 1; | |
265 | |
266 if (snd_mixer_selem_get_playback_volume | |
267 (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, &mix->woofer_vol) >= 0) | |
268 mix->reset_woofer = 1; | |
269 | |
270 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
271 if (snd_mixer_selem_get_playback_volume | |
3308 | 272 (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, &mix->rear_center_vol) >= 0) |
273 mix->reset_rear_center = 1; | |
274 | |
275 if (snd_mixer_selem_get_playback_volume | |
276 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, &mix->side_left_vol) >= 0) | |
277 mix->reset_side_left = 1; | |
278 | |
279 if (snd_mixer_selem_get_playback_volume | |
280 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, &mix->side_right_vol) >= 0) | |
281 mix->reset_side_right = 1; | |
3335 | 282 #endif |
3308 | 283 |
284 /* Set the volume */ | |
285 snd_mixer_selem_set_playback_volume_all (mix->vol_ctl, dev_vol); | |
286 } | |
287 | |
288 static void | |
289 reset_volume (const struct mixer_state *mix) | |
290 { | |
291 if (mix->reset_front_left) | |
292 snd_mixer_selem_set_playback_volume | |
293 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, mix->front_left_vol); | |
294 | |
295 if (mix->reset_front_center) | |
296 snd_mixer_selem_set_playback_volume | |
297 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, mix->front_center_vol); | |
298 | |
299 if (mix->reset_front_right) | |
300 snd_mixer_selem_set_playback_volume | |
301 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, mix->front_right_vol); | |
302 | |
303 if (mix->reset_rear_left) | |
304 snd_mixer_selem_set_playback_volume | |
305 (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, mix->rear_left_vol); | |
306 | |
3335 | 307 if (mix->reset_rear_right) |
308 snd_mixer_selem_set_playback_volume | |
309 (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, mix->rear_right_vol); | |
310 | |
311 if (mix->reset_woofer) | |
312 snd_mixer_selem_set_playback_volume | |
313 (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, mix->woofer_vol); | |
314 | |
315 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
3308 | 316 if (mix->reset_rear_center) |
317 snd_mixer_selem_set_playback_volume | |
318 (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, mix->rear_center_vol); | |
319 | |
320 if (mix->reset_side_left) | |
321 snd_mixer_selem_set_playback_volume | |
322 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, mix->side_left_vol); | |
323 | |
324 if (mix->reset_side_right) | |
325 snd_mixer_selem_set_playback_volume | |
326 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, mix->side_right_vol); | |
3335 | 327 #endif |
3308 | 328 |
329 snd_mixer_close (mix->mixer); | |
330 } | |
331 | |
332 int | |
333 alsa_play_sound_data (const Binbyte *data, int length, int volume) | |
334 { | |
335 snd_pcm_t *pcm_handle; | |
336 snd_pcm_hw_params_t *hwparams; | |
337 snd_pcm_format_t format; | |
338 struct mixer_state mix; | |
339 int speed, tracks, err; | |
340 | |
341 /* Set the PCM parameters */ | |
342 if ((err = snd_pcm_open (&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, | |
343 0)) < 0) | |
344 goto error_pcm_open; | |
345 | |
346 snd_pcm_hw_params_alloca (&hwparams); | |
347 | |
348 if ((err = snd_pcm_hw_params_any (pcm_handle, hwparams)) < 0) | |
349 goto error_pcm; | |
350 | |
351 format = analyze_format (data, &speed, &tracks); | |
352 | |
353 if ((err = snd_pcm_hw_params_set_access (pcm_handle, hwparams, | |
354 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | |
355 goto error_pcm; | |
356 | |
357 if ((err = snd_pcm_hw_params_set_format (pcm_handle, hwparams, format)) < 0) | |
358 goto error_pcm; | |
359 | |
360 if ((err = snd_pcm_hw_params_set_rate (pcm_handle, hwparams, speed, 0)) < 0) | |
361 goto error_pcm; | |
362 | |
363 if ((err = snd_pcm_hw_params_set_channels (pcm_handle, hwparams, tracks)) | |
364 < 0) | |
365 goto error_pcm; | |
366 | |
367 if ((err = snd_pcm_hw_params (pcm_handle, hwparams)) < 0) | |
368 goto error_pcm; | |
369 | |
370 /* Set the volume */ | |
371 memset (&mix, 0, sizeof (mix)); | |
372 set_volume (&mix, volume); | |
373 | |
374 /* Play the sound */ | |
375 if ((err = snd_pcm_writei (pcm_handle, data, length)) < 0) | |
376 goto error_mixer; | |
377 | |
378 /* Put the volume back the way it used to be */ | |
379 reset_volume (&mix); | |
380 | |
381 /* Release resources */ | |
382 snd_pcm_close (pcm_handle); | |
383 return 1; | |
384 | |
385 error_mixer: | |
386 reset_volume (&mix); | |
387 error_pcm: | |
388 snd_pcm_close (pcm_handle); | |
389 error_pcm_open: | |
390 sound_perror (snd_strerror (err)); | |
391 return 0; | |
392 } | |
393 | |
394 /* Read the sound file into an internal buffer, then call | |
395 * alsa_play_sound_data. | |
396 */ | |
397 int | |
398 alsa_play_sound_file (const Extbyte *sound_file, int volume) | |
399 { | |
400 Binbyte *data; | |
401 int fd, retval; | |
402 struct stat st; | |
403 | |
404 fd = retry_open (sound_file, O_RDONLY, 0); | |
405 if (fd < 0) { | |
406 sound_perror (sound_file); | |
407 return 0; | |
408 } | |
409 | |
410 qxe_fstat (fd, &st); | |
411 data = xnew_array (Binbyte, st.st_size); | |
412 retry_read (fd, data, st.st_size); | |
413 retry_close (fd); | |
414 retval = alsa_play_sound_data (data, st.st_size, volume); | |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
3347
diff
changeset
|
415 xfree (data); |
3308 | 416 return retval; |
417 } |