blob: 8dddedce0e16f4f90ee42e2e9eb6ca625a9a4af5 [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/audio.h>
#include <stropts.h>
#include "log.h"
#include "io_misc.h"
#include "pcm.h"
#define PCM_AUDIO_DEVICE_PATH "/dev/audio"
struct PcmDeviceStruct {
int fileDescriptor;
};
PcmDevice *
openPcmDevice (int errorLevel, const char *device) {
PcmDevice *pcm;
if ((pcm = malloc(sizeof(*pcm)))) {
if (!*device) device = getenv("AUDIODEV");
if (!device || !*device) device = PCM_AUDIO_DEVICE_PATH;
if ((pcm->fileDescriptor = open(device, O_WRONLY|O_NONBLOCK)) != -1) {
audio_info_t info;
AUDIO_INITINFO(&info);
#ifdef AUMODE_PLAY
info.mode = AUMODE_PLAY;
#endif /* AUMODE_PLAY */
#ifdef AUDIO_ENCODING_SLINEAR
info.play.encoding = AUDIO_ENCODING_SLINEAR;
#else /* AUDIO_ENCODING_SLINEAR */
info.play.encoding = AUDIO_ENCODING_LINEAR;
#endif /* AUDIO_ENCODING_SLINEAR */
info.play.sample_rate = 16000;
info.play.channels = 1;
info.play.precision = 16;
info.play.gain = AUDIO_MAX_GAIN;
if (ioctl(pcm->fileDescriptor, AUDIO_SETINFO, &info) == -1)
logMessage(errorLevel, "Cannot set audio info: %s", strerror(errno));
return pcm;
} else {
logMessage(errorLevel, "Cannot open PCM device: %s: %s", device, strerror(errno));
}
free(pcm);
} else {
logSystemError("PCM device allocation");
}
return NULL;
}
void
closePcmDevice (PcmDevice *pcm) {
close(pcm->fileDescriptor);
free(pcm);
}
int
writePcmData (PcmDevice *pcm, const unsigned char *buffer, int count) {
return writeFile(pcm->fileDescriptor, buffer, count) != -1;
}
static int
getPcmAudioInfo (PcmDevice *pcm, audio_info_t *info) {
if (ioctl(pcm->fileDescriptor, AUDIO_GETINFO, info) != -1) return 1;
logSystemError("AUDIO_GETINFO");
return 0;
}
int
getPcmBlockSize (PcmDevice *pcm) {
audio_info_t info;
if (getPcmAudioInfo(pcm, &info)) return (info.play.precision / 8 * info.play.channels) * 0X400;
return 0X100;
}
int
getPcmSampleRate (PcmDevice *pcm) {
audio_info_t info;
if (getPcmAudioInfo(pcm, &info)) return info.play.sample_rate;
return 8000;
}
int
setPcmSampleRate (PcmDevice *pcm, int rate) {
return getPcmSampleRate(pcm);
}
int
getPcmChannelCount (PcmDevice *pcm) {
audio_info_t info;
if (getPcmAudioInfo(pcm, &info)) return info.play.channels;
return 1;
}
int
setPcmChannelCount (PcmDevice *pcm, int channels) {
return getPcmChannelCount(pcm);
}
PcmAmplitudeFormat
getPcmAmplitudeFormat (PcmDevice *pcm) {
audio_info_t info;
if (getPcmAudioInfo(pcm, &info)) {
switch (info.play.encoding) {
default:
break;
#ifdef AUDIO_ENCODING_SLINEAR_BE
case AUDIO_ENCODING_SLINEAR_BE:
if (info.play.precision == 16) return PCM_FMT_S16B;
goto testLinearSigned8;
#endif /* AUDIO_ENCODING_SLINEAR_BE */
#ifdef AUDIO_ENCODING_SLINEAR_LE
case AUDIO_ENCODING_SLINEAR_LE:
if (info.play.precision == 16) return PCM_FMT_S16L;
goto testLinearSigned8;
#endif /* AUDIO_ENCODING_SLINEAR_LE */
#ifdef AUDIO_ENCODING_LINEAR
case AUDIO_ENCODING_LINEAR:
#ifdef WORDS_BIGENDIAN
if (info.play.precision == 16) return PCM_FMT_S16B;
#else /* WORDS_BIGENDIAN */
if (info.play.precision == 16) return PCM_FMT_S16L;
#endif /* WORDS_BIGENDIAN */
goto testLinearSigned8;
#endif /* AUDIO_ENCODING_LINEAR */
testLinearSigned8:
if (info.play.precision == 8) return PCM_FMT_S8;
break;
case AUDIO_ENCODING_LINEAR8:
return PCM_FMT_U8;
case AUDIO_ENCODING_ULAW:
return PCM_FMT_ULAW;
case AUDIO_ENCODING_ALAW:
return PCM_FMT_ALAW;
}
}
return PCM_FMT_UNKNOWN;
}
PcmAmplitudeFormat
setPcmAmplitudeFormat (PcmDevice *pcm, PcmAmplitudeFormat format) {
return getPcmAmplitudeFormat(pcm);
}
void
pushPcmOutput (PcmDevice *pcm) {
}
void
awaitPcmOutput (PcmDevice *pcm) {
ioctl(pcm->fileDescriptor, AUDIO_DRAIN);
}
void
cancelPcmOutput (PcmDevice *pcm) {
ioctl(pcm->fileDescriptor, I_FLUSH);
}