Mercurial > hg > xemacs-beta
view src/alsaplay.c @ 5882:bbe4146603db
Reduce regexp usage, now CL-oriented non-regexp code available, core Lisp
lisp/ChangeLog addition:
2015-04-01 Aidan Kehoe <kehoea@parhasard.net>
When calling #'string-match with a REGEXP without regular
expression special characters, call #'search, #'mismatch, #'find,
etc. instead, making our code less likely to side-effect other
functions' match data and a little faster.
* apropos.el (apropos-command):
* apropos.el (apropos):
Call (position ?\n ...) rather than (string-match "\n" ...) here.
* buff-menu.el:
* buff-menu.el (buffers-menu-omit-invisible-buffers):
Don't fire up the regexp engine just to check if a string starts
with a space.
* buff-menu.el (select-buffers-tab-buffers-by-mode):
Don't fire up the regexp engine just to compare mode basenames.
* buff-menu.el (format-buffers-tab-line):
* buff-menu.el (build-buffers-tab-internal): Moved to being a
label within the following.
* buff-menu.el (buffers-tab-items): Use the label.
* bytecomp.el (byte-compile-log-1):
Don't fire up the regexp engine just to look for a newline.
* cus-edit.el (get):
Ditto.
* cus-edit.el (custom-variable-value-create):
Ditto, but for a colon.
* descr-text.el (describe-text-sexp):
Ditto.
* descr-text.el (describe-char-unicode-data):
Use #'split-string-by-char given that we're just looking for a
semicolon.
* descr-text.el (describe-char):
Don't fire up the regexp engine just to look for a newline.
* disass.el (disassemble-internal):
Ditto.
* files.el (file-name-sans-extension):
Implement this using #'position.
* files.el (file-name-extension):
Correct this function's docstring, implement it in terms of
#'position.
* files.el (insert-directory):
Don't fire up the regexp engine to split a string by space; don't
reverse the list of switches, this is actually a longstand bug as
far as I can see.
* gnuserv.el (gnuserv-process-filter):
Use #'position here, instead of consing inside #'split-string
needlessly.
* gtk-file-dialog.el (gtk-file-dialog-update-dropdown):
Use #'split-string-by-char here, don't fire up #'split-string for
directory-sep-char.
* gtk-font-menu.el (hack-font-truename):
Implement this more cheaply in terms of #'find,
#'split-string-by-char, #'equal, rather than #'string-match,
#'split-string, #'string-equal.
* hyper-apropos.el (hyper-apropos-grok-functions):
* hyper-apropos.el (hyper-apropos-grok-variables):
Look for a newline using #'position rather than #'string-match in
these functions.
* info.el (Info-insert-dir):
* info.el (Info-insert-file-contents):
* info.el (Info-follow-reference):
* info.el (Info-extract-menu-node-name):
* info.el (Info-menu):
Look for fixed strings using #'position or #'search as appropriate
in this file.
* ldap.el (ldap-decode-string):
* ldap.el (ldap-encode-string):
#'encode-coding-string, #'decode-coding-string are always
available, don't check if they're fboundp.
* ldap.el (ldap-decode-address):
* ldap.el (ldap-encode-address):
Use #'split-string-by-char in these functions.
* lisp-mnt.el (lm-creation-date):
* lisp-mnt.el (lm-last-modified-date):
Don't fire up the regexp engine just to look for spaces in this file.
* menubar-items.el (default-menubar):
Use (not (mismatch ...)) rather than #'string-match here, for
simple regexp.
Use (search "beta" ...) rather than (string-match "beta" ...)
* menubar-items.el (sort-buffers-menu-alphabetically):
* menubar-items.el (sort-buffers-menu-by-mode-then-alphabetically):
* menubar-items.el (group-buffers-menu-by-mode-then-alphabetically):
Don't fire up the regexp engine to check if a string starts with
a space or an asterisk.
Use the more fine-grained results of #'compare-strings; compare
case-insensitively for the buffer menu.
* menubar-items.el (list-all-buffers):
* menubar-items.el (tutorials-menu-filter):
Use #'equal rather than #'string-equal, which, in this context,
has the drawback of not having a bytecode, and no redeeming
features.
* minibuf.el:
* minibuf.el (un-substitute-in-file-name):
Use #'count, rather than counting the occurences of $ using the
regexp engine.
* minibuf.el (read-file-name-internal-1):
Don't fire up the regexp engine to search for ?=.
* mouse.el (mouse-eval-sexp):
Check for newline with #'find.
* msw-font-menu.el (mswindows-reset-device-font-menus):
Split a string by newline with #'split-string-by-char.
* mule/japanese.el:
* mule/japanese.el ("Japanese"):
Use #'search rather than #'string-match; canoncase before
comparing; fix a bug I had introduced where I had been making case
insensitive comparisons where the case mattered.
* mule/korea-util.el (default-korean-keyboard):
Look for ?3 using #'find, not #'string-march.
* mule/korea-util.el (quail-hangul-switch-hanja):
Search for a fixed string using #'search.
* mule/mule-cmds.el (set-locale-for-language-environment):
#'position, #'substitute rather than #'string-match,
#'replace-in-string.
* newcomment.el (comment-make-extra-lines):
Use #'search rather than #'string-match for a simple string.
* package-get.el (package-get-remote-filename):
Use #'position when looking for ?@
* process.el (setenv):
* process.el (read-envvar-name):
Use #'position when looking for ?=.
* replace.el (map-query-replace-regexp):
Use #'split-string-by-char instead of using an inline
implementation of it.
* select.el (select-convert-from-cf-text):
* select.el (select-convert-from-cf-unicodetext):
Use #'position rather than #'string-match in these functions.
* setup-paths.el (paths-emacs-data-root-p):
Use #'search when looking for simple string.
* sound.el (load-sound-file):
Use #'split-string-by-char rather than an inline reimplementation
of same.
* startup.el (splash-screen-window-body):
* startup.el (splash-screen-tty-body):
Search for simple strings using #'search.
* version.el (emacs-version):
Ditto.
* x-font-menu.el (hack-font-truename):
Implement this more cheaply in terms of #'find,
#'split-string-by-char, #'equal, rather than #'string-match,
#'split-string, #'string-equal.
* x-font-menu.el (x-reset-device-font-menus-core):
Use #'split-string-by-char here.
* x-init.el (x-initialize-keyboard):
Search for a simple string using #'search.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Wed, 01 Apr 2015 14:28:20 +0100 |
parents | 308d34e9f07d |
children |
line wrap: on
line source
/* Play sound with the ALSA library Copyright (C) 2006 Jerry James. This file is part of XEmacs. XEmacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. XEmacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with XEmacs. If not, see <http://www.gnu.org/licenses/>. */ /* Synched up with: Not in FSF. */ /* TODO: Support asynchronous sound playing; see the NAS support in sound.c */ #include <config.h> #include "lisp.h" #include "sound.h" #include "sysfile.h" /* We can't just include <alsa/asoundlib.h> because it tries to redefine * several symbols defined by the previous header files. */ #include <alsa/version.h> #include <alsa/input.h> #include <alsa/output.h> #include <alsa/conf.h> #include <alsa/global.h> #include <alsa/pcm.h> #include <alsa/error.h> #include <alsa/hwdep.h> #include <alsa/rawmidi.h> #include <alsa/control.h> #include <alsa/mixer.h> #define ALSA_VERSION(major,minor,sub) (((major)<<16) | ((minor)<<8)| (sub)) struct mixer_state { snd_mixer_t *mixer; snd_mixer_elem_t *vol_ctl; /* Which channels need the old volume restored */ int reset_front_left; int reset_front_center; int reset_front_right; int reset_rear_left; int reset_rear_right; int reset_woofer; #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) int reset_rear_center; int reset_side_left; int reset_side_right; #endif /* Old volumes for the channels */ long front_left_vol; long front_center_vol; long front_right_vol; long rear_left_vol; long rear_right_vol; long woofer_vol; #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) long rear_center_vol; long side_left_vol; long side_right_vol; #endif }; /* Assemble a machine half-word in little-endian order */ #define HALF_LE(arr,start) (arr[start] + (arr[start + 1] << 8)) /* Assemble a machine word in little-endian order */ #define WORD_LE(arr,start) (arr[start] + (arr[start + 1] << 8) + \ (arr[start + 2] << 16) + (arr[start + 3] << 24)) /* Assemble a machine word in big-endian order */ #define WORD_BE(arr,start) ((arr[start] << 24) + (arr[start + 1] << 16) + \ (arr[start + 2] << 8) + arr[start + 3]) /* This function was inspired by miscplay.c. * Examine sound data to determine its format. * * TODO: Detect other formats that ALSA can play, such as GSM and MPEG. */ static snd_pcm_format_t analyze_format (const Binbyte *format, int *speed, int *tracks) { if (!memcmp (format, "Creative Voice File\x1A\x1A\x00", 22) && HALF_LE (format, 22) == ((0x1233 - HALF_LE (format, 24)) & 0xFFFF)) { /* VOC */ *speed = 8000; *tracks = 2; return SND_PCM_FORMAT_U8; } else if (!memcmp (format, "RIFF", 4) && !memcmp (format + 8, "WAVEfmt ", 8)) { /* WAVE */ *speed = WORD_LE (format, 24); *tracks = format[22]; return format[32] / format[22] == 1 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16_LE; } else if (!memcmp (format, ".snd", 4)) { /* Sun/NeXT Audio (big endian) */ if (WORD_BE (format, 4) < 24) { *speed = 8000; *tracks = 1; return SND_PCM_FORMAT_MU_LAW; } *speed = WORD_BE (format, 16); *tracks = format[23]; if (!memcmp (format + 12, "\000\000\000", 3)) { switch (format[15]) { case 1: case 17: case 29: return SND_PCM_FORMAT_MU_LAW; case 2: return SND_PCM_FORMAT_S8; case 3: return SND_PCM_FORMAT_S16_BE; case 4: return SND_PCM_FORMAT_S24_BE; case 5: return SND_PCM_FORMAT_S32_BE; case 23: case 24: case 25: case 26: return SND_PCM_FORMAT_IMA_ADPCM; case 27: return SND_PCM_FORMAT_A_LAW; default: break; } } return SND_PCM_FORMAT_UNKNOWN; } else if (!memcmp (format, ".sd", 4)) { /* DEC Audio (little endian) */ if (WORD_LE (format, 4) < 24) { *speed = 8000; *tracks = 1; return SND_PCM_FORMAT_MU_LAW; } *speed = WORD_LE (format, 16); *tracks = format[20]; if (!memcmp (format + 13, "\000\000\000", 3)) { switch (format[12]) { case 1: case 17: case 29: return SND_PCM_FORMAT_MU_LAW; case 2: return SND_PCM_FORMAT_S8; case 3: return SND_PCM_FORMAT_S16_LE; case 4: return SND_PCM_FORMAT_S24_LE; case 5: return SND_PCM_FORMAT_S32_LE; case 23: case 24: case 25: case 26: return SND_PCM_FORMAT_IMA_ADPCM; case 27: return SND_PCM_FORMAT_A_LAW; default: break; } } return SND_PCM_FORMAT_UNKNOWN; } else { /* We don't know what it is. Guess that it is mono audio in unsigned * byte format. Maybe we should error if we reach this point. */ *speed = 8000; *tracks = 1; return SND_PCM_FORMAT_U8; } } /* Set the volume: if any errors occur, we accept the existing volume */ static void set_volume (struct mixer_state *mix, int volume) { snd_mixer_selem_id_t *volume_id; long min_vol, max_vol, dev_vol; if (snd_mixer_open (&mix->mixer, 0) < 0) return; if (snd_mixer_attach (mix->mixer, "default") < 0) return; if (snd_mixer_selem_register (mix->mixer, NULL, NULL) < 0) return; if (snd_mixer_load (mix->mixer) < 0) return; snd_mixer_selem_id_alloca (&volume_id); snd_mixer_selem_id_set_name (volume_id, "PCM"); if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) == NULL) { snd_mixer_selem_id_set_name (volume_id, "Master"); if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) == NULL) return; } /* Translate the Lisp volume range to the device volume range */ #if SND_LIB_VERSION < ALSA_VERSION (1, 0, 10) snd_mixer_selem_get_playback_volume_range (mix->vol_ctl, &min_vol, &max_vol); #else if (snd_mixer_selem_get_playback_volume_range (mix->vol_ctl, &min_vol, &max_vol) < 0) return; #endif dev_vol = volume * (max_vol - min_vol) / 100 + min_vol; /* Record the old volumes */ if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, &mix->front_left_vol) >= 0) mix->reset_front_left = 1; if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, &mix->front_center_vol) >= 0) mix->reset_front_center = 1; if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, &mix->front_right_vol) >= 0) mix->reset_front_right = 1; if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, &mix->rear_left_vol) >= 0) mix->reset_rear_left = 1; if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, &mix->rear_right_vol) >= 0) mix->reset_rear_right = 1; if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, &mix->woofer_vol) >= 0) mix->reset_woofer = 1; #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, &mix->rear_center_vol) >= 0) mix->reset_rear_center = 1; if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, &mix->side_left_vol) >= 0) mix->reset_side_left = 1; if (snd_mixer_selem_get_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, &mix->side_right_vol) >= 0) mix->reset_side_right = 1; #endif /* Set the volume */ snd_mixer_selem_set_playback_volume_all (mix->vol_ctl, dev_vol); } static void reset_volume (const struct mixer_state *mix) { if (mix->reset_front_left) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, mix->front_left_vol); if (mix->reset_front_center) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, mix->front_center_vol); if (mix->reset_front_right) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, mix->front_right_vol); if (mix->reset_rear_left) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, mix->rear_left_vol); if (mix->reset_rear_right) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, mix->rear_right_vol); if (mix->reset_woofer) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, mix->woofer_vol); #if SND_LIB_VERSION >= ALSA_VERSION (1, 0, 10) if (mix->reset_rear_center) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, mix->rear_center_vol); if (mix->reset_side_left) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, mix->side_left_vol); if (mix->reset_side_right) snd_mixer_selem_set_playback_volume (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, mix->side_right_vol); #endif snd_mixer_close (mix->mixer); } int alsa_play_sound_data (const Binbyte *data, int length, int volume) { snd_pcm_t *pcm_handle; snd_pcm_hw_params_t *hwparams; snd_pcm_format_t format; struct mixer_state mix; int speed, tracks, err; /* Set the PCM parameters */ if ((err = snd_pcm_open (&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) goto error_pcm_open; snd_pcm_hw_params_alloca (&hwparams); if ((err = snd_pcm_hw_params_any (pcm_handle, hwparams)) < 0) goto error_pcm; format = analyze_format (data, &speed, &tracks); if ((err = snd_pcm_hw_params_set_access (pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) goto error_pcm; if ((err = snd_pcm_hw_params_set_format (pcm_handle, hwparams, format)) < 0) goto error_pcm; if ((err = snd_pcm_hw_params_set_rate (pcm_handle, hwparams, speed, 0)) < 0) goto error_pcm; if ((err = snd_pcm_hw_params_set_channels (pcm_handle, hwparams, tracks)) < 0) goto error_pcm; if ((err = snd_pcm_hw_params (pcm_handle, hwparams)) < 0) goto error_pcm; /* Set the volume */ memset (&mix, 0, sizeof (mix)); set_volume (&mix, volume); /* Play the sound */ if ((err = snd_pcm_writei (pcm_handle, data, length)) < 0) goto error_mixer; /* Put the volume back the way it used to be */ reset_volume (&mix); /* Release resources */ snd_pcm_close (pcm_handle); return 1; error_mixer: reset_volume (&mix); error_pcm: snd_pcm_close (pcm_handle); error_pcm_open: sound_perror (snd_strerror (err)); return 0; } /* Read the sound file into an internal buffer, then call * alsa_play_sound_data. */ int alsa_play_sound_file (const Extbyte *sound_file, int volume) { Binbyte *data; int fd, retval; struct stat st; fd = retry_open (sound_file, O_RDONLY, 0); if (fd < 0) { sound_perror (sound_file); return 0; } qxe_fstat (fd, &st); data = xnew_array (Binbyte, st.st_size); retry_read (fd, data, st.st_size); retry_close (fd); retval = alsa_play_sound_data (data, st.st_size, volume); xfree (data); return retval; }