blob: 79ed4ccb7146f5b31d7ab228b8df18fb63f4bd0e [file] [log] [blame]
/*
* 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 "log.h"
#include "pcm.h"
#define UNSIGNED_TO_SIGNED amplitude += INT16_MIN
static inline PcmSampleSize
makePcmSample_8 (PcmSample *sample, int16_t amplitude) {
sample->bytes[0] = amplitude >> 8;
return 1;
}
static PcmSampleSize
makePcmSample_S8 (PcmSample *sample, int16_t amplitude) {
return makePcmSample_8(sample, amplitude);
}
static PcmSampleSize
makePcmSample_U8 (PcmSample *sample, int16_t amplitude) {
UNSIGNED_TO_SIGNED;
return makePcmSample_8(sample, amplitude);
}
static inline PcmSampleSize
makePcmSample_16N (PcmSample *sample, int16_t amplitude) {
union {
unsigned char *bytes;
int16_t *s16;
} overlay;
overlay.bytes = sample->bytes;
*overlay.s16 = amplitude;
return 2;
}
static PcmSampleSize
makePcmSample_S16N (PcmSample *sample, int16_t amplitude) {
return makePcmSample_16N(sample, amplitude);
}
static PcmSampleSize
makePcmSample_U16N (PcmSample *sample, int16_t amplitude) {
UNSIGNED_TO_SIGNED;
return makePcmSample_16N(sample, amplitude);
}
#ifdef WORDS_BIGENDIAN
#define makePcmSample_S16B makePcmSample_S16N
#define makePcmSample_U16B makePcmSample_U16N
#else /* WORDS_BIGENDIAN */
static inline PcmSampleSize
makePcmSample_16B (PcmSample *sample, int16_t amplitude) {
sample->bytes[0] = amplitude >> 8;
sample->bytes[1] = amplitude;
return 2;
}
static PcmSampleSize
makePcmSample_S16B (PcmSample *sample, int16_t amplitude) {
return makePcmSample_16B(sample, amplitude);
}
static PcmSampleSize
makePcmSample_U16B (PcmSample *sample, int16_t amplitude) {
UNSIGNED_TO_SIGNED;
return makePcmSample_16B(sample, amplitude);
}
#endif /* WORDS_BIGENDIAN */
#ifndef WORDS_BIGENDIAN
#define makePcmSample_S16L makePcmSample_S16N
#define makePcmSample_U16L makePcmSample_U16N
#else /* WORDS_BIGENDIAN */
static inline PcmSampleSize
makePcmSample_16L (PcmSample *sample, int16_t amplitude) {
sample->bytes[0] = amplitude;
sample->bytes[1] = amplitude >> 8;
return 2;
}
static PcmSampleSize
makePcmSample_S16L (PcmSample *sample, int16_t amplitude) {
return makePcmSample_16L(sample, amplitude);
}
static PcmSampleSize
makePcmSample_U16L (PcmSample *sample, int16_t amplitude) {
UNSIGNED_TO_SIGNED;
return makePcmSample_16L(sample, amplitude);
}
#endif /* WORDS_BIGENDIAN */
static PcmSampleSize
makePcmSample_ULAW (PcmSample *sample, int16_t amplitude) {
int negative = amplitude < 0;
int exponent = 0X7;
unsigned char value;
const unsigned int bias = 0X84;
const unsigned int clip = 0X7FFF - bias;
if (negative) amplitude = -amplitude;
if (amplitude > clip) amplitude = clip;
amplitude += bias;
while ((exponent > 0) && !(amplitude & 0X4000)) {
amplitude <<= 1;
--exponent;
}
value = (exponent << 4) | ((amplitude >> 10) & 0X0F);
if (negative) value |= 0X80;
sample->bytes[0] = ~value;
return 1;
}
static PcmSampleSize
makePcmSample_ALAW (PcmSample *sample, int16_t amplitude) {
int negative = amplitude < 0;
int exponent = 0X7;
unsigned char value;
if (negative) amplitude = -amplitude;
while ((exponent > 0) && !(amplitude & 0X4000)) {
amplitude <<= 1;
--exponent;
}
if (!exponent) amplitude >>= 1;
value = (exponent << 4) | ((amplitude >> 10) & 0X0F);
if (negative) value |= 0X80;
sample->bytes[0] = value ^ 0X55;
return 1;
}
static PcmSampleSize
makePcmSample_UNKNOWN (PcmSample *sample, int16_t amplitude) {
return 0;
}
PcmSampleMaker
getPcmSampleMaker (PcmAmplitudeFormat format) {
#define PCM_SAMPLE_MAKER_ENTRY(format) [PCM_FMT_##format] = makePcmSample_##format
static PcmSampleMaker const pcmSampleMakers[] = {
PCM_SAMPLE_MAKER_ENTRY(S8),
PCM_SAMPLE_MAKER_ENTRY(U8),
PCM_SAMPLE_MAKER_ENTRY(S16B),
PCM_SAMPLE_MAKER_ENTRY(U16B),
PCM_SAMPLE_MAKER_ENTRY(S16L),
PCM_SAMPLE_MAKER_ENTRY(U16L),
PCM_SAMPLE_MAKER_ENTRY(ULAW),
PCM_SAMPLE_MAKER_ENTRY(ALAW),
PCM_SAMPLE_MAKER_ENTRY(UNKNOWN)
};
if (format < ARRAY_COUNT(pcmSampleMakers)) {
PcmSampleMaker sampleMaker = pcmSampleMakers[format];
if (sampleMaker) return sampleMaker;
}
logMessage(LOG_WARNING, "unsupported PCM format: %d", format);
return pcmSampleMakers[PCM_FMT_UNKNOWN];
}