rockbox/lib/rbcodec/dsp/channel_mode.c
Michael Sevakis 78a45b47de Cleanup and simplify latest DSP code incarnation.
Some things can just be a bit simpler in handling the list of stages
and some things, especially format change handling, can be simplified
for each stage implementation. Format changes are sent through the
configure() callback.

Hide some internal details and variables from processing stages and
let the core deal with it.

Do some miscellaneous cleanup and keep things a bit better factored.

Change-Id: I19dd8ce1d0b792ba914d426013088a49a52ecb7e
2013-05-04 13:43:33 -04:00

252 lines
7.3 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 Thom Johansen
* Copyright (C) 2012 Michael Sevakis
*
* This program 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.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "system.h"
#include "sound.h"
#include "fixedpoint.h"
#include "fracmul.h"
#include "dsp_proc_entry.h"
#include "channel_mode.h"
#include <string.h>
#if 0
/* SOUND_CHAN_STEREO mode is a noop so has no function - just outline one for
* completeness. */
void channel_mode_proc_stereo(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
#endif
void channel_mode_proc_mono(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
void channel_mode_proc_mono_left(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
void channel_mode_proc_mono_right(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
void channel_mode_proc_custom(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
static struct channel_mode_data
{
long sw_gain; /* 00h: for mode: custom */
long sw_cross; /* 04h: for mode: custom */
int mode;
} channel_mode_data =
{
.sw_gain = 0,
.sw_cross = 0,
.mode = SOUND_CHAN_STEREO
};
#if 0
/* SOUND_CHAN_STEREO mode is a noop so has no function - just outline one for
* completeness. */
void channel_mode_proc_stereo(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
/* The channels are each just themselves */
(void)this; (void)buf_p;
}
#endif
#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
/* Unoptimized routines */
void channel_mode_proc_mono(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
struct dsp_buffer *buf = *buf_p;
int32_t *sl = buf->p32[0];
int32_t *sr = buf->p32[1];
int count = buf->remcount;
do
{
int32_t lr = *sl / 2 + *sr / 2;
*sl++ = lr;
*sr++ = lr;
}
while (--count > 0);
(void)this;
}
void channel_mode_proc_custom(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
struct channel_mode_data *data = (void *)this->data;
struct dsp_buffer *buf = *buf_p;
int32_t *sl = buf->p32[0];
int32_t *sr = buf->p32[1];
int count = buf->remcount;
const int32_t gain = data->sw_gain;
const int32_t cross = data->sw_cross;
do
{
int32_t l = *sl;
int32_t r = *sr;
*sl++ = FRACMUL(l, gain) + FRACMUL(r, cross);
*sr++ = FRACMUL(r, gain) + FRACMUL(l, cross);
}
while (--count > 0);
}
void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
struct dsp_buffer *buf = *buf_p;
int32_t *sl = buf->p32[0];
int32_t *sr = buf->p32[1];
int count = buf->remcount;
do
{
int32_t ch = *sl / 2 - *sr / 2;
*sl++ = ch;
*sr++ = -ch;
}
while (--count > 0);
(void)this;
}
#endif /* CPU */
void channel_mode_proc_mono_left(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
/* Just copy over the other channel */
struct dsp_buffer *buf = *buf_p;
memcpy(buf->p32[1], buf->p32[0], buf->remcount * sizeof (int32_t));
(void)this;
}
void channel_mode_proc_mono_right(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
/* Just copy over the other channel */
struct dsp_buffer *buf = *buf_p;
memcpy(buf->p32[0], buf->p32[1], buf->remcount * sizeof (int32_t));
(void)this;
}
void channel_mode_set_config(int value)
{
if (value < 0 || value >= SOUND_CHAN_NUM_MODES)
value = SOUND_CHAN_STEREO; /* Out of range */
if (value == channel_mode_data.mode)
return;
channel_mode_data.mode = value;
dsp_proc_enable(dsp_get_config(CODEC_IDX_AUDIO), DSP_PROC_CHANNEL_MODE,
value != SOUND_CHAN_STEREO);
}
void channel_mode_custom_set_width(int value)
{
long width, straight, cross;
width = value * 0x7fffff / 100;
if (value <= 100)
{
straight = (0x7fffff + width) / 2;
cross = straight - width;
}
else
{
/* straight = (1 + width) / (2 * width) */
straight = fp_div(0x7fffff + width, width, 22);
cross = straight - 0x7fffff;
}
channel_mode_data.sw_gain = straight << 8;
channel_mode_data.sw_cross = cross << 8;
}
static void update_process_fn(struct dsp_proc_entry *this)
{
static const dsp_proc_fn_type fns[SOUND_CHAN_NUM_MODES] =
{
[SOUND_CHAN_STEREO] = NULL,
[SOUND_CHAN_MONO] = channel_mode_proc_mono,
[SOUND_CHAN_CUSTOM] = channel_mode_proc_custom,
[SOUND_CHAN_MONO_LEFT] = channel_mode_proc_mono_left,
[SOUND_CHAN_MONO_RIGHT] = channel_mode_proc_mono_right,
[SOUND_CHAN_KARAOKE] = channel_mode_proc_karaoke,
};
this->process = fns[((struct channel_mode_data *)this->data)->mode];
}
/* Handle format changes and verify the format compatibility */
static intptr_t channel_mode_new_format(struct dsp_proc_entry *this,
struct dsp_config *dsp,
struct sample_format *format)
{
DSP_PRINT_FORMAT(DSP_PROC_CHANNEL_MODE, format);
bool active = format->num_channels >= 2;
dsp_proc_activate(dsp, DSP_PROC_CHANNEL_MODE, active);
if (active)
return PROC_NEW_FORMAT_OK;
/* Can't do this. Sleep until next change. */
DEBUGF(" DSP_PROC_CHANNEL_MODE- deactivated\n");
return PROC_NEW_FORMAT_DEACTIVATED;
(void)this;
}
/* DSP message hook */
static intptr_t channel_mode_configure(struct dsp_proc_entry *this,
struct dsp_config *dsp,
unsigned int setting,
intptr_t value)
{
intptr_t retval = 0;
switch (setting)
{
case DSP_PROC_INIT:
if (value == 0)
this->data = (intptr_t)&channel_mode_data;
update_process_fn(this);
break;
case DSP_PROC_NEW_FORMAT:
retval = channel_mode_new_format(this, dsp,
(struct sample_format *)value);
break;
}
return retval;
}
/* Database entry */
DSP_PROC_DB_ENTRY(
CHANNEL_MODE,
channel_mode_configure);