Mercurial > hg > xemacs-beta
annotate src/alsaplay.c @ 5043:d0c14ea98592
various frame-geometry fixes
-------------------- ChangeLog entries follow: --------------------
src/ChangeLog addition:
2010-02-15 Ben Wing <ben@xemacs.org>
* EmacsFrame.c:
* EmacsFrame.c (EmacsFrameResize):
* console-msw-impl.h:
* console-msw-impl.h (struct mswindows_frame):
* console-msw-impl.h (FRAME_MSWINDOWS_TARGET_RECT):
* device-tty.c:
* device-tty.c (tty_asynch_device_change):
* event-msw.c:
* event-msw.c (mswindows_wnd_proc):
* faces.c (Fface_list):
* faces.h:
* frame-gtk.c:
* frame-gtk.c (gtk_set_initial_frame_size):
* frame-gtk.c (gtk_set_frame_size):
* frame-msw.c:
* frame-msw.c (mswindows_init_frame_1):
* frame-msw.c (mswindows_set_frame_size):
* frame-msw.c (mswindows_size_frame_internal):
* frame-msw.c (msprinter_init_frame_3):
* frame.c:
* frame.c (enum):
* frame.c (Fmake_frame):
* frame.c (adjust_frame_size):
* frame.c (store_minibuf_frame_prop):
* frame.c (Fframe_property):
* frame.c (Fframe_properties):
* frame.c (Fframe_displayable_pixel_height):
* frame.c (Fframe_displayable_pixel_width):
* frame.c (internal_set_frame_size):
* frame.c (Fset_frame_height):
* frame.c (Fset_frame_pixel_height):
* frame.c (Fset_frame_displayable_pixel_height):
* frame.c (Fset_frame_width):
* frame.c (Fset_frame_pixel_width):
* frame.c (Fset_frame_displayable_pixel_width):
* frame.c (Fset_frame_size):
* frame.c (Fset_frame_pixel_size):
* frame.c (Fset_frame_displayable_pixel_size):
* frame.c (frame_conversion_internal_1):
* frame.c (get_frame_displayable_pixel_size):
* frame.c (change_frame_size_1):
* frame.c (change_frame_size):
* frame.c (generate_title_string):
* frame.h:
* gtk-xemacs.c:
* gtk-xemacs.c (gtk_xemacs_size_request):
* gtk-xemacs.c (gtk_xemacs_size_allocate):
* gtk-xemacs.c (gtk_xemacs_paint):
* gutter.c:
* gutter.c (update_gutter_geometry):
* redisplay.c (end_hold_frame_size_changes):
* redisplay.c (redisplay_frame):
* toolbar.c:
* toolbar.c (update_frame_toolbars_geometry):
* window.c:
* window.c (frame_pixsize_valid_p):
* window.c (check_frame_size):
Various fixes to frame geometry to make it a bit easier to understand
and fix some bugs.
1. IMPORTANT: Some renamings. Will need to be applied carefully to
the carbon repository, in the following order:
-- pixel_to_char_size -> pixel_to_frame_unit_size
-- char_to_pixel_size -> frame_unit_to_pixel_size
-- pixel_to_real_char_size -> pixel_to_char_size
-- char_to_real_pixel_size -> char_to_pixel_size
-- Reverse second and third arguments of change_frame_size() and
change_frame_size_1() to try to make functions consistent in
putting width before height.
-- Eliminate old round_size_to_char, because it didn't really
do anything differently from round_size_to_real_char()
-- round_size_to_real_char -> round_size_to_char; any places that
called the old round_size_to_char should just call the new one.
2. IMPORTANT FOR CARBON: The set_frame_size() method is now passed
sizes in "frame units", like all other frame-sizing functions,
rather than some hacked-up combination of char-cell units and
total pixel size. This only affects window systems that use
"pixelated geometry", and I'm not sure if Carbon is one of them.
MS Windows is pixelated, X and GTK are not. For pixelated-geometry
systems, the size in set_frame_size() is in displayable pixels
rather than total pixels and needs to be converted appropriately;
take a look at the changes made to mswindows_set_frame_size()
method if necessary.
3. Add a big long comment in frame.c describing how frame geometry
works.
4. Remove MS Windows-specific character height and width fields,
duplicative and unused.
5. frame-displayable-pixel-* and set-frame-displayable-pixel-*
didn't use to work on MS Windows, but they do now.
6. In general, clean up the handling of "pixelated geometry" so
that fewer functions have to worry about this. This is really
an abomination that should be removed entirely but that will
have to happen later. Fix some buggy code in
frame_conversion_internal() that happened to "work" because it
was countered by oppositely buggy code in change_frame_size().
7. Clean up some frame-size code in toolbar.c and use functions
already provided in frame.c instead of rolling its own.
8. Fix check_frame_size() in window.c, which formerly didn't take
pixelated geometry into account.
author | Ben Wing <ben@xemacs.org> |
---|---|
date | Mon, 15 Feb 2010 22:14:11 -0600 |
parents | 16112448d484 |
children | 308d34e9f07d |
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 | |
6 XEmacs is free software; you can redistribute it and/or modify it | |
7 under the terms of the GNU General Public License as published by the | |
8 Free Software Foundation; either version 2, or (at your option) any | |
9 later version. | |
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 | |
17 along with XEmacs; see the file COPYING. If not, write to | |
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 Boston, MA 02111-1307, USA. */ | |
20 | |
21 /* Synched up with: Not in FSF. */ | |
22 | |
23 /* TODO: Support asynchronous sound playing; see the NAS support in sound.c */ | |
24 | |
25 #include <config.h> | |
26 #include "lisp.h" | |
27 #include "sound.h" | |
28 #include "sysfile.h" | |
29 | |
30 /* We can't just include <alsa/asoundlib.h> because it tries to redefine | |
31 * several symbols defined by the previous header files. | |
32 */ | |
3347 | 33 #include <alsa/version.h> |
3308 | 34 #include <alsa/input.h> |
35 #include <alsa/output.h> | |
36 #include <alsa/conf.h> | |
37 #include <alsa/global.h> | |
38 #include <alsa/pcm.h> | |
39 #include <alsa/error.h> | |
40 #include <alsa/hwdep.h> | |
41 #include <alsa/rawmidi.h> | |
42 #include <alsa/control.h> | |
43 #include <alsa/mixer.h> | |
44 | |
3335 | 45 #define ALSA_VERSION(major,minor,sub) (((major)<<16) | ((minor)<<8)| (sub)) |
46 | |
3308 | 47 struct mixer_state |
48 { | |
49 snd_mixer_t *mixer; | |
50 snd_mixer_elem_t *vol_ctl; | |
51 | |
52 /* Which channels need the old volume restored */ | |
53 int reset_front_left; | |
54 int reset_front_center; | |
55 int reset_front_right; | |
56 int reset_rear_left; | |
3335 | 57 int reset_rear_right; |
58 int reset_woofer; | |
59 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
3308 | 60 int reset_rear_center; |
61 int reset_side_left; | |
62 int reset_side_right; | |
3335 | 63 #endif |
3308 | 64 |
65 /* Old volumes for the channels */ | |
66 long front_left_vol; | |
67 long front_center_vol; | |
68 long front_right_vol; | |
69 long rear_left_vol; | |
3335 | 70 long rear_right_vol; |
71 long woofer_vol; | |
72 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
3308 | 73 long rear_center_vol; |
74 long side_left_vol; | |
75 long side_right_vol; | |
3335 | 76 #endif |
3308 | 77 }; |
78 | |
79 /* Assemble a machine half-word in little-endian order */ | |
80 #define HALF_LE(arr,start) (arr[start] + (arr[start + 1] << 8)) | |
81 | |
82 /* Assemble a machine word in little-endian order */ | |
83 #define WORD_LE(arr,start) (arr[start] + (arr[start + 1] << 8) + \ | |
84 (arr[start + 2] << 16) + (arr[start + 3] << 24)) | |
85 | |
86 /* Assemble a machine word in big-endian order */ | |
87 #define WORD_BE(arr,start) ((arr[start] << 24) + (arr[start + 1] << 16) + \ | |
88 (arr[start + 2] << 8) + arr[start + 3]) | |
89 | |
90 /* This function was inspired by miscplay.c. | |
91 * Examine sound data to determine its format. | |
92 * | |
93 * TODO: Detect other formats that ALSA can play, such as GSM and MPEG. | |
94 */ | |
95 static snd_pcm_format_t | |
96 analyze_format (const Binbyte *format, int *speed, int *tracks) | |
97 { | |
98 if (!memcmp (format, "Creative Voice File\x1A\x1A\x00", 22) && | |
99 HALF_LE (format, 22) == ((0x1233 - HALF_LE (format, 24)) & 0xFFFF)) | |
100 { | |
101 /* VOC */ | |
102 *speed = 8000; | |
103 *tracks = 2; | |
104 return SND_PCM_FORMAT_U8; | |
105 } | |
106 else if (!memcmp (format, "RIFF", 4) && !memcmp (format + 8, "WAVEfmt ", 8)) | |
107 { | |
108 /* WAVE */ | |
109 *speed = WORD_LE (format, 24); | |
110 *tracks = format[22]; | |
111 return format[32] / format[22] == 1 | |
112 ? SND_PCM_FORMAT_U8 | |
113 : SND_PCM_FORMAT_S16_LE; | |
114 } | |
115 else if (!memcmp (format, ".snd", 4)) | |
116 { | |
117 /* Sun/NeXT Audio (big endian) */ | |
118 if (WORD_BE (format, 4) < 24) | |
119 { | |
120 *speed = 8000; | |
121 *tracks = 1; | |
122 return SND_PCM_FORMAT_MU_LAW; | |
123 } | |
124 *speed = WORD_BE (format, 16); | |
125 *tracks = format[23]; | |
126 if (!memcmp (format + 12, "\000\000\000", 3)) | |
127 { | |
128 switch (format[15]) | |
129 { | |
130 case 1: | |
131 case 17: | |
132 case 29: | |
133 return SND_PCM_FORMAT_MU_LAW; | |
134 case 2: | |
135 return SND_PCM_FORMAT_S8; | |
136 case 3: | |
137 return SND_PCM_FORMAT_S16_BE; | |
138 case 4: | |
139 return SND_PCM_FORMAT_S24_BE; | |
140 case 5: | |
141 return SND_PCM_FORMAT_S32_BE; | |
142 case 23: | |
143 case 24: | |
144 case 25: | |
145 case 26: | |
146 return SND_PCM_FORMAT_IMA_ADPCM; | |
147 case 27: | |
148 return SND_PCM_FORMAT_A_LAW; | |
149 default: | |
150 break; | |
151 } | |
152 } | |
153 return SND_PCM_FORMAT_UNKNOWN; | |
154 } | |
155 else if (!memcmp (format, ".sd", 4)) | |
156 { | |
157 /* DEC Audio (little endian) */ | |
158 if (WORD_LE (format, 4) < 24) | |
159 { | |
160 *speed = 8000; | |
161 *tracks = 1; | |
162 return SND_PCM_FORMAT_MU_LAW; | |
163 } | |
164 *speed = WORD_LE (format, 16); | |
165 *tracks = format[20]; | |
166 if (!memcmp (format + 13, "\000\000\000", 3)) | |
167 { | |
168 switch (format[12]) | |
169 { | |
170 case 1: | |
171 case 17: | |
172 case 29: | |
173 return SND_PCM_FORMAT_MU_LAW; | |
174 case 2: | |
175 return SND_PCM_FORMAT_S8; | |
176 case 3: | |
177 return SND_PCM_FORMAT_S16_LE; | |
178 case 4: | |
179 return SND_PCM_FORMAT_S24_LE; | |
180 case 5: | |
181 return SND_PCM_FORMAT_S32_LE; | |
182 case 23: | |
183 case 24: | |
184 case 25: | |
185 case 26: | |
186 return SND_PCM_FORMAT_IMA_ADPCM; | |
187 case 27: | |
188 return SND_PCM_FORMAT_A_LAW; | |
189 default: | |
190 break; | |
191 } | |
192 } | |
193 return SND_PCM_FORMAT_UNKNOWN; | |
194 } | |
195 else | |
196 { | |
197 /* We don't know what it is. Guess that it is mono audio in unsigned | |
198 * byte format. Maybe we should error if we reach this point. | |
199 */ | |
200 *speed = 8000; | |
201 *tracks = 1; | |
202 return SND_PCM_FORMAT_U8; | |
203 } | |
204 } | |
205 | |
206 /* Set the volume: if any errors occur, we accept the existing volume */ | |
207 static void | |
208 set_volume (struct mixer_state *mix, int volume) | |
209 { | |
210 snd_mixer_selem_id_t *volume_id; | |
211 long min_vol, max_vol, dev_vol; | |
212 | |
213 if (snd_mixer_open (&mix->mixer, 0) < 0) | |
214 return; | |
215 | |
216 if (snd_mixer_attach (mix->mixer, "default") < 0) | |
217 return; | |
218 | |
219 if (snd_mixer_selem_register (mix->mixer, NULL, NULL) < 0) | |
220 return; | |
221 | |
222 if (snd_mixer_load (mix->mixer) < 0) | |
223 return; | |
224 | |
225 snd_mixer_selem_id_alloca (&volume_id); | |
226 snd_mixer_selem_id_set_name (volume_id, "PCM"); | |
227 | |
228 if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) == NULL) | |
229 { | |
230 snd_mixer_selem_id_set_name (volume_id, "Master"); | |
231 if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) | |
232 == NULL) | |
233 return; | |
234 } | |
235 | |
236 /* Translate the Lisp volume range to the device volume range */ | |
3335 | 237 #if SND_LIB_VERSION < ALSA_VERSION (1, 0, 10) |
238 snd_mixer_selem_get_playback_volume_range (mix->vol_ctl, &min_vol, &max_vol); | |
239 #else | |
3308 | 240 if (snd_mixer_selem_get_playback_volume_range (mix->vol_ctl, &min_vol, |
241 &max_vol) < 0) | |
242 return; | |
3335 | 243 #endif |
3308 | 244 |
245 dev_vol = volume * (max_vol - min_vol) / 100 + min_vol; | |
246 | |
247 /* Record the old volumes */ | |
248 if (snd_mixer_selem_get_playback_volume | |
249 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, &mix->front_left_vol) >= 0) | |
250 mix->reset_front_left = 1; | |
251 | |
252 if (snd_mixer_selem_get_playback_volume | |
253 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, &mix->front_center_vol) >= 0) | |
254 mix->reset_front_center = 1; | |
255 | |
256 if (snd_mixer_selem_get_playback_volume | |
257 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, &mix->front_right_vol) >= 0) | |
258 mix->reset_front_right = 1; | |
259 | |
260 if (snd_mixer_selem_get_playback_volume | |
261 (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, &mix->rear_left_vol) >= 0) | |
262 mix->reset_rear_left = 1; | |
263 | |
264 if (snd_mixer_selem_get_playback_volume | |
3335 | 265 (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, &mix->rear_right_vol) >= 0) |
266 mix->reset_rear_right = 1; | |
267 | |
268 if (snd_mixer_selem_get_playback_volume | |
269 (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, &mix->woofer_vol) >= 0) | |
270 mix->reset_woofer = 1; | |
271 | |
272 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
273 if (snd_mixer_selem_get_playback_volume | |
3308 | 274 (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, &mix->rear_center_vol) >= 0) |
275 mix->reset_rear_center = 1; | |
276 | |
277 if (snd_mixer_selem_get_playback_volume | |
278 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, &mix->side_left_vol) >= 0) | |
279 mix->reset_side_left = 1; | |
280 | |
281 if (snd_mixer_selem_get_playback_volume | |
282 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, &mix->side_right_vol) >= 0) | |
283 mix->reset_side_right = 1; | |
3335 | 284 #endif |
3308 | 285 |
286 /* Set the volume */ | |
287 snd_mixer_selem_set_playback_volume_all (mix->vol_ctl, dev_vol); | |
288 } | |
289 | |
290 static void | |
291 reset_volume (const struct mixer_state *mix) | |
292 { | |
293 if (mix->reset_front_left) | |
294 snd_mixer_selem_set_playback_volume | |
295 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, mix->front_left_vol); | |
296 | |
297 if (mix->reset_front_center) | |
298 snd_mixer_selem_set_playback_volume | |
299 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, mix->front_center_vol); | |
300 | |
301 if (mix->reset_front_right) | |
302 snd_mixer_selem_set_playback_volume | |
303 (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, mix->front_right_vol); | |
304 | |
305 if (mix->reset_rear_left) | |
306 snd_mixer_selem_set_playback_volume | |
307 (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, mix->rear_left_vol); | |
308 | |
3335 | 309 if (mix->reset_rear_right) |
310 snd_mixer_selem_set_playback_volume | |
311 (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, mix->rear_right_vol); | |
312 | |
313 if (mix->reset_woofer) | |
314 snd_mixer_selem_set_playback_volume | |
315 (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, mix->woofer_vol); | |
316 | |
317 #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) | |
3308 | 318 if (mix->reset_rear_center) |
319 snd_mixer_selem_set_playback_volume | |
320 (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, mix->rear_center_vol); | |
321 | |
322 if (mix->reset_side_left) | |
323 snd_mixer_selem_set_playback_volume | |
324 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, mix->side_left_vol); | |
325 | |
326 if (mix->reset_side_right) | |
327 snd_mixer_selem_set_playback_volume | |
328 (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, mix->side_right_vol); | |
3335 | 329 #endif |
3308 | 330 |
331 snd_mixer_close (mix->mixer); | |
332 } | |
333 | |
334 int | |
335 alsa_play_sound_data (const Binbyte *data, int length, int volume) | |
336 { | |
337 snd_pcm_t *pcm_handle; | |
338 snd_pcm_hw_params_t *hwparams; | |
339 snd_pcm_format_t format; | |
340 struct mixer_state mix; | |
341 int speed, tracks, err; | |
342 | |
343 /* Set the PCM parameters */ | |
344 if ((err = snd_pcm_open (&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, | |
345 0)) < 0) | |
346 goto error_pcm_open; | |
347 | |
348 snd_pcm_hw_params_alloca (&hwparams); | |
349 | |
350 if ((err = snd_pcm_hw_params_any (pcm_handle, hwparams)) < 0) | |
351 goto error_pcm; | |
352 | |
353 format = analyze_format (data, &speed, &tracks); | |
354 | |
355 if ((err = snd_pcm_hw_params_set_access (pcm_handle, hwparams, | |
356 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | |
357 goto error_pcm; | |
358 | |
359 if ((err = snd_pcm_hw_params_set_format (pcm_handle, hwparams, format)) < 0) | |
360 goto error_pcm; | |
361 | |
362 if ((err = snd_pcm_hw_params_set_rate (pcm_handle, hwparams, speed, 0)) < 0) | |
363 goto error_pcm; | |
364 | |
365 if ((err = snd_pcm_hw_params_set_channels (pcm_handle, hwparams, tracks)) | |
366 < 0) | |
367 goto error_pcm; | |
368 | |
369 if ((err = snd_pcm_hw_params (pcm_handle, hwparams)) < 0) | |
370 goto error_pcm; | |
371 | |
372 /* Set the volume */ | |
373 memset (&mix, 0, sizeof (mix)); | |
374 set_volume (&mix, volume); | |
375 | |
376 /* Play the sound */ | |
377 if ((err = snd_pcm_writei (pcm_handle, data, length)) < 0) | |
378 goto error_mixer; | |
379 | |
380 /* Put the volume back the way it used to be */ | |
381 reset_volume (&mix); | |
382 | |
383 /* Release resources */ | |
384 snd_pcm_close (pcm_handle); | |
385 return 1; | |
386 | |
387 error_mixer: | |
388 reset_volume (&mix); | |
389 error_pcm: | |
390 snd_pcm_close (pcm_handle); | |
391 error_pcm_open: | |
392 sound_perror (snd_strerror (err)); | |
393 return 0; | |
394 } | |
395 | |
396 /* Read the sound file into an internal buffer, then call | |
397 * alsa_play_sound_data. | |
398 */ | |
399 int | |
400 alsa_play_sound_file (const Extbyte *sound_file, int volume) | |
401 { | |
402 Binbyte *data; | |
403 int fd, retval; | |
404 struct stat st; | |
405 | |
406 fd = retry_open (sound_file, O_RDONLY, 0); | |
407 if (fd < 0) { | |
408 sound_perror (sound_file); | |
409 return 0; | |
410 } | |
411 | |
412 qxe_fstat (fd, &st); | |
413 data = xnew_array (Binbyte, st.st_size); | |
414 retry_read (fd, data, st.st_size); | |
415 retry_close (fd); | |
416 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
|
417 xfree (data); |
3308 | 418 return retval; |
419 } |