186 lines
6.1 KiB
C
186 lines
6.1 KiB
C
|
/* Copyright 2004-2005 Sun Microsystems, Inc. All rights reserved.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the
|
||
|
* "Software"), to deal in the Software without restriction, including
|
||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||
|
* distribute, and/or sell copies of the Software, and to permit persons
|
||
|
* to whom the Software is furnished to do so, provided that the above
|
||
|
* copyright notice(s) and this permission notice appear in all copies of
|
||
|
* the Software and that both the above copyright notice(s) and this
|
||
|
* permission notice appear in supporting documentation.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||
|
* OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||
|
* HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
|
||
|
* INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
|
||
|
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||
|
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
*
|
||
|
* Except as contained in this notice, the name of a copyright holder
|
||
|
* shall not be used in advertising or otherwise to promote the sale, use
|
||
|
* or other dealings in this Software without prior written authorization
|
||
|
* of the copyright holder.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_XORG_CONFIG_H
|
||
|
#include <xorg-config.h>
|
||
|
#endif
|
||
|
|
||
|
#include <sys/audio.h>
|
||
|
#include <sys/uio.h>
|
||
|
#include <limits.h>
|
||
|
#include <math.h>
|
||
|
#include <poll.h>
|
||
|
|
||
|
#include "xf86.h"
|
||
|
#include "xf86Priv.h"
|
||
|
#include "xf86_OSlib.h"
|
||
|
|
||
|
#define BELL_RATE 48000 /* Samples per second */
|
||
|
#define BELL_HZ 50 /* Fraction of a second i.e. 1/x */
|
||
|
#define BELL_MS (1000/BELL_HZ) /* MS */
|
||
|
#define BELL_SAMPLES (BELL_RATE / BELL_HZ)
|
||
|
#define BELL_MIN 3 /* Min # of repeats */
|
||
|
|
||
|
#define AUDIO_DEVICE "/dev/audio"
|
||
|
|
||
|
_X_EXPORT void
|
||
|
xf86OSRingBell(int loudness, int pitch, int duration)
|
||
|
{
|
||
|
static short samples[BELL_SAMPLES];
|
||
|
static short silence[BELL_SAMPLES]; /* "The Sound of Silence" */
|
||
|
static int lastFreq;
|
||
|
int cnt;
|
||
|
int i;
|
||
|
int written;
|
||
|
int repeats;
|
||
|
int freq;
|
||
|
audio_info_t audioInfo;
|
||
|
struct iovec iov[IOV_MAX];
|
||
|
int iovcnt;
|
||
|
double ampl, cyclen, phase;
|
||
|
int audioFD;
|
||
|
|
||
|
if ((loudness <= 0) || (pitch <= 0) || (duration <= 0)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
lastFreq = 0;
|
||
|
bzero(silence, sizeof(silence));
|
||
|
|
||
|
audioFD = open(AUDIO_DEVICE, O_WRONLY | O_NONBLOCK);
|
||
|
if (audioFD == -1) {
|
||
|
xf86Msg(X_ERROR, "Bell: cannot open audio device \"%s\": %s\n",
|
||
|
AUDIO_DEVICE, strerror(errno));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
freq = pitch;
|
||
|
freq = min(freq, (BELL_RATE / 2) - 1);
|
||
|
freq = max(freq, 2 * BELL_HZ);
|
||
|
|
||
|
/*
|
||
|
* Ensure full waves per buffer
|
||
|
*/
|
||
|
freq -= freq % BELL_HZ;
|
||
|
|
||
|
if (freq != lastFreq) {
|
||
|
lastFreq = freq;
|
||
|
ampl = 16384.0;
|
||
|
|
||
|
cyclen = (double) freq / (double) BELL_RATE;
|
||
|
phase = 0.0;
|
||
|
|
||
|
for (i = 0; i < BELL_SAMPLES; i++) {
|
||
|
samples[i] = (short) (ampl * sin(2.0 * M_PI * phase));
|
||
|
phase += cyclen;
|
||
|
if (phase >= 1.0)
|
||
|
phase -= 1.0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
repeats = (duration + (BELL_MS / 2)) / BELL_MS;
|
||
|
repeats = max(repeats, BELL_MIN);
|
||
|
|
||
|
loudness = max(0, loudness);
|
||
|
loudness = min(loudness, 100);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
ErrorF("BELL : freq %d volume %d duration %d repeats %d\n",
|
||
|
freq, loudness, duration, repeats);
|
||
|
#endif
|
||
|
|
||
|
AUDIO_INITINFO(&audioInfo);
|
||
|
audioInfo.play.encoding = AUDIO_ENCODING_LINEAR;
|
||
|
audioInfo.play.sample_rate = BELL_RATE;
|
||
|
audioInfo.play.channels = 2;
|
||
|
audioInfo.play.precision = 16;
|
||
|
audioInfo.play.gain = min(AUDIO_MAX_GAIN, AUDIO_MAX_GAIN * loudness / 100);
|
||
|
|
||
|
if (ioctl(audioFD, AUDIO_SETINFO, &audioInfo) < 0){
|
||
|
xf86Msg(X_ERROR,
|
||
|
"Bell: AUDIO_SETINFO failed on audio device \"%s\": %s\n",
|
||
|
AUDIO_DEVICE, strerror(errno));
|
||
|
close(audioFD);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
iovcnt = 0;
|
||
|
|
||
|
for (cnt = 0; cnt <= repeats; cnt++) {
|
||
|
iov[iovcnt].iov_base = (char *) samples;
|
||
|
iov[iovcnt++].iov_len = sizeof(samples);
|
||
|
if (cnt == repeats) {
|
||
|
/* Insert a bit of silence so that multiple beeps are distinct and
|
||
|
* not compressed into a single tone.
|
||
|
*/
|
||
|
iov[iovcnt].iov_base = (char *) silence;
|
||
|
iov[iovcnt++].iov_len = sizeof(silence);
|
||
|
}
|
||
|
if ((iovcnt >= IOV_MAX) || (cnt == repeats)) {
|
||
|
written = writev(audioFD, iov, iovcnt);
|
||
|
|
||
|
if ((written < ((int)(sizeof(samples) * iovcnt)))) {
|
||
|
/* audio buffer was full! */
|
||
|
|
||
|
int naptime;
|
||
|
|
||
|
if (written == -1) {
|
||
|
if (errno != EAGAIN) {
|
||
|
xf86Msg(X_ERROR,
|
||
|
"Bell: writev failed on audio device \"%s\": %s\n",
|
||
|
AUDIO_DEVICE, strerror(errno));
|
||
|
close(audioFD);
|
||
|
return;
|
||
|
}
|
||
|
i = iovcnt;
|
||
|
} else {
|
||
|
i = ((sizeof(samples) * iovcnt) - written)
|
||
|
/ sizeof(samples);
|
||
|
}
|
||
|
cnt -= i;
|
||
|
|
||
|
/* sleep a little to allow audio buffer to drain */
|
||
|
naptime = BELL_MS * i;
|
||
|
poll(NULL, 0, naptime);
|
||
|
|
||
|
i = ((sizeof(samples) * iovcnt) - written) % sizeof(samples);
|
||
|
iovcnt = 0;
|
||
|
if ((written != -1) && (i > 0)) {
|
||
|
iov[iovcnt].iov_base = ((char *) samples) + i;
|
||
|
iov[iovcnt++].iov_len = sizeof(samples) - i;
|
||
|
}
|
||
|
} else {
|
||
|
iovcnt = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
close(audioFD);
|
||
|
return;
|
||
|
}
|