/* $Id: native.c,v 1.4 1999/03/10 07:58:03 boyns Exp $ */
/*
* Copyright (C) 1993-99 Mark R. Boyns <boyns@doit.org>
*
* This file is part of rplay.
*
* rplay 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 2 of the License, or
* (at your option) any later version.
*
* rplay 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 rplay; see the file COPYING. If not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "spool.h"
#include "sound.h"
#include "buffer.h"
#include "rplayd.h"
#include "native.h"
#include "ulaw.h"
/* Native means 16-bit signed. */
static int ulaw_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int s8_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int u8_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int big_s16_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int big_u16_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int little_s16_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int little_u16_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int native_to_native( /* SPOOL *sp, char *native_buf, int nsamples, int nchannels */ );
static int native_to_ulaw( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_s8( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_u8( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_s16( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_u16( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_g721( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_g723_3( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_g723_5( /* char *native_buf, int nsamples, int nchannels */ );
static int native_to_gsm( /* char *native_buf, int nsamples, int nchannels */ );
/* Setup the to and from native table. */
NATIVE_TABLE native_table[] =
{
{
{0, 0},
{0, 0}},
{
{s8_to_native, s8_to_native},
{native_to_s8, native_to_s8}},
{
{u8_to_native, u8_to_native},
{native_to_u8, native_to_u8}},
{
{big_s16_to_native, little_s16_to_native},
{native_to_s16, native_to_s16}},
{
{big_u16_to_native, little_u16_to_native},
{native_to_u16, native_to_u16}},
{
{ulaw_to_native, ulaw_to_native},
{native_to_ulaw, native_to_ulaw}},
#ifdef HAVE_ADPCM
{
{native_to_native, native_to_native},
{native_to_g721, native_to_g721}},
{
{native_to_native, native_to_native},
{native_to_g723_3, native_to_g723_5}},
{
{native_to_native, native_to_native},
{native_to_g723_3, native_to_g723_5}},
#endif /* HAVE_ADPCM */
#ifdef HAVE_GSM
{
{native_to_native, native_to_native},
{native_to_gsm, native_to_gsm}},
#endif /* HAVE_GSM */
};
/* Initialize the native audio buffer. */
void
zero_native(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
memset(native_buf, 0, nsamples * nchannels * 2); /* 2 == 16-bit */
}
void
level(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
short *p = (short *) native_buf, sample;
int n = nsamples * nchannels;
long left_max = 0, right_max = 0;
/* Optimize the cases for stereo and mono output. */
if (nchannels == 2) /* stereo */
{
while (n--)
{
#ifdef FAKE_VOLUME
sample = (*p++ >> 7);
#else
sample = (*p++ * rplay_audio_volume) >> 14;
#endif
if (n & 1)
{
if (sample > right_max)
{
right_max = sample;
}
}
else
{
if (sample > left_max)
{
left_max = sample;
}
}
}
}
else
/* mono */
{
while (n--)
{
#ifdef FAKE_VOLUME
sample = (*p++ >> 7);
#else
sample = (*p++ * rplay_audio_volume) >> 14;
#endif
if (sample > left_max)
{
left_max = sample;
}
}
right_max = left_max;
}
rplay_audio_left_level = left_max;
rplay_audio_right_level = right_max;
}
#ifdef FAKE_VOLUME
/* Simulate hardware volume control. */
void
fake_volume(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
short *p = (short *) native_buf;
int n = nsamples * nchannels;
while (n--)
{
*p++ = (*p * rplay_audio_volume) >> 7;
}
}
#endif /* FAKE_VOLUME */
/* Move `sp' to the next sample. */
#define next_sample(sp, s) \
{ \
int sample_delta; \
\
sample_delta = sp->sample_index; \
sp->sample_index += sp->sample_factor; \
sample_delta = sp->sample_index - sample_delta; \
sp->offset = sample_delta * s->output_sample_size; \
if (s->samples > 0 \
&& sp->sample_index >= s->samples \
&& (s->type == SOUND_FILE || sp->si->eof)) \
{ \
return 0; \
} \
}
#define check_buffers(sp) \
{ \
/* Compute the next offset. */ \
sp->offset = sp->ptr - sp->ptr_end; \
\
/* Try to use curr_buffer's next buffer. */ \
if (sp->curr_buffer->next && sp->curr_buffer->next->nbytes > 0) \
{ \
BUFFER *b = sp->curr_buffer; \
\
/* report (REPORT_DEBUG, "* using curr_buffer->next\n"); */ \
\
sp->curr_buffer = sp->curr_buffer->next; \
sp->ptr = (unsigned char *)sp->curr_buffer->buf; \
sp->ptr_end = (unsigned char *)sp->curr_buffer->buf \
+ sp->curr_buffer->nbytes; \
\
buffer_destroy (b); \
} \
/* Try to use next_buffer. */ \
else if (sp->next_buffer) \
{ \
BUFFER *b = sp->curr_buffer; \
\
/* report (REPORT_DEBUG, "* using next_buffer\n"); */ \
\
sp->curr_buffer = sp->next_buffer; \
sp->ptr = (unsigned char *)sp->curr_buffer->buf; \
sp->ptr_end = (unsigned char *)sp->curr_buffer->buf \
+ sp->curr_buffer->nbytes; \
\
sp->next_buffer = NULL; \
\
buffer_dealloc (b, 0); \
} \
/* No more buffers are available. */ \
else \
{ \
/* report (REPORT_DEBUG, "* no buffers!\n"); */ \
\
buffer_dealloc (sp->curr_buffer, 0); \
sp->curr_buffer = NULL; \
\
break; \
} \
}
/* Return 0 when the sound is over, otherwise return 1. */
#define x_to_native(FUNC, SAMPLE_TO_NATIVE) \
static int \
FUNC (sp, native_buf, nsamples, nchannels) \
SPOOL *sp; \
short *native_buf; \
int nsamples; \
int nchannels; \
{ \
SOUND *curr_sound; \
short new_volume; \
int sample_count = 0; \
int curr_channel; \
short linear; \
short orig_linear; \
short *buf = (short *)native_buf; \
\
/* Find the sound that being played. */ \
curr_sound = sp->sound[sp->curr_sound]; \
\
/* Compute the new volume for the sound using priorities. */ \
new_volume = sp->curr_attrs->volume; \
if (spool_prio && spool_nplaying > 1) \
{ \
new_volume -= (spool_prio - sp->rp->priority) >> 1; \
new_volume = MAX(new_volume, RPLAY_MIN_VOLUME); \
} \
\
/* do oversampling */ \
if (sp->sample_factor < 1) \
{ \
while (sp->curr_buffer && sample_count < nsamples) \
{ \
sp->ptr += sp->offset; \
if (sp->ptr < sp->ptr_end) \
{ \
for (curr_channel = 0; curr_channel < nchannels; curr_channel++) \
{ \
if (curr_channel < curr_sound->channels) \
{ \
SAMPLE_TO_NATIVE; \
orig_linear = linear; \
linear = sp->oversample[curr_channel]; \
sp->oversample[curr_channel] += sp->oversample_inc[curr_channel]; \
if (sp->sample_index + sp->sample_factor > sp->offset + 1) \
{ \
sp->oversample_inc[curr_channel] = (orig_linear - \
sp->oversample[curr_channel]) * sp->sample_factor; \
} \
} \
*buf++ += ((linear * new_volume) >> 7); \
} \
sample_count++; \
next_sample (sp, curr_sound); \
} \
else \
{ \
check_buffers (sp); \
} \
} \
} \
/* read the sound as-is */ \
else \
{ \
while (sp->curr_buffer && sample_count < nsamples) \
{ \
sp->ptr += sp->offset; \
if (sp->ptr < sp->ptr_end) \
{ \
for (curr_channel = 0; curr_channel < nchannels; curr_channel++) \
{ \
if (curr_channel < curr_sound->channels) \
{ \
SAMPLE_TO_NATIVE; \
} \
*buf++ += ((linear * new_volume) >> 7); \
} \
sample_count++; \
next_sample (sp, curr_sound); \
} \
else \
{ \
check_buffers (sp); \
} \
} \
} \
\
return 1; \
}
/* ulaw */
x_to_native
(
ulaw_to_native,
linear = ulaw_to_linear(*(sp->ptr + curr_channel))
)
/* signed 8-bit */
x_to_native
(
s8_to_native,
linear = (char) *(sp->ptr + curr_channel);
linear <<= 8
)
/* unsigned 8-bit */
x_to_native
(
u8_to_native,
linear = (unsigned char) *(sp->ptr + curr_channel) ^ 0x80;
linear <<= 8
)
/* signed 16-bit big-endian */
x_to_native
(
big_s16_to_native,
linear = (short) (sp->ptr[curr_channel << 1] << 8 | sp->ptr[(curr_channel << 1) + 1])
)
/* signed 16-bit little-endian */
x_to_native
(
little_s16_to_native,
linear = (short) (sp->ptr[(curr_channel << 1) + 1] << 8 | sp->ptr[curr_channel << 1])
)
/* unsigned 16-bit big-endian */
x_to_native
(
big_u16_to_native,
linear = (short) (sp->ptr[curr_channel << 1] << 8 | sp->ptr[(curr_channel << 1) + 1]) ^ 0x8000;
)
/* unsigned 16-bit little-endian */
x_to_native
(
little_u16_to_native,
linear = (short) (sp->ptr[(curr_channel << 1) + 1] << 8 | sp->ptr[curr_channel << 1]) ^ 0x8000;
)
/* native to native */
x_to_native
(
native_to_native,
linear = (*((short *) sp->ptr + curr_channel))
)
static int
native_to_ulaw(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
short *from = (short *) native_buf;
char *to = (char *) native_buf;
int n = nsamples * nchannels;
while (n--)
{
*to++ = linear_to_ulaw(*from++);
}
return 0;
}
static int
native_to_s8(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
short *from = (short *) native_buf;
char *to = (char *) native_buf;
int n = nsamples * nchannels;
while (n--)
{
*to++ = (*from++ >> 8);
}
return 0;
}
static int
native_to_u8(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
short *from = (short *) native_buf;
char *to = (char *) native_buf;
int n = nsamples * nchannels;
while (n--)
{
*to++ = (*from++ >> 8) ^ 0x80;
}
return 0;
}
static int
native_to_s16(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
/* The native audio buffer is already signed 16-bit. */
return 0;
}
static int
native_to_u16(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
short *from = (short *) native_buf;
short *to = (short *) native_buf;
int n = nsamples * nchannels;
while (n--)
{
*to++ = *from++ ^ 0x8000;
}
return 0;
}
#ifdef HAVE_ADPCM
static int
native_to_g721(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
/* NOT SUPPORTED */
return 0;
}
static int
native_to_g723_3(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
/* NOT SUPPORTED */
return 0;
}
static int
native_to_g723_5(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
/* NOT SUPPORTED */
return 0;
}
#endif /* HAVE_ADPCM */
#ifdef HAVE_GSM
static int
native_to_gsm(native_buf, nsamples, nchannels)
char *native_buf;
int nsamples;
int nchannels;
{
/* NOT SUPPORTED */
return 0;
}
#endif /* HAVE_GSM */