56f17c4164
Change-Id: Idb6af40df26f5b8499a40e8b98602261ef227044
263 lines
7.8 KiB
C
263 lines
7.8 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"
|
|
|
|
#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 */
|
|
struct dsp_config *dsp;
|
|
int mode;
|
|
const dsp_proc_fn_type fns[SOUND_CHAN_NUM_MODES];
|
|
} channel_mode_data =
|
|
{
|
|
.sw_gain = 0,
|
|
.sw_cross = 0,
|
|
.mode = SOUND_CHAN_STEREO,
|
|
.fns =
|
|
{
|
|
[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,
|
|
},
|
|
};
|
|
|
|
static dsp_proc_fn_type get_process_fn(void)
|
|
{
|
|
return channel_mode_data.fns[channel_mode_data.mode];
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
/* This is the initial function pointer when first enabled/changed in order
|
|
* to facilitate verification of the format compatibility at the proper time
|
|
* This gets called for changes even if stage is inactive. */
|
|
static void channel_mode_process_new_format(struct dsp_proc_entry *this,
|
|
struct dsp_buffer **buf_p)
|
|
{
|
|
struct channel_mode_data *data = (void *)this->data;
|
|
struct dsp_buffer *buf = *buf_p;
|
|
|
|
DSP_PRINT_FORMAT(DSP_PROC_CHANNEL_MODE, DSP_PROC_CHANNEL_MODE,
|
|
buf->format);
|
|
|
|
bool active = buf->format.num_channels >= 2;
|
|
dsp_proc_activate(data->dsp, DSP_PROC_CHANNEL_MODE, active);
|
|
|
|
if (!active)
|
|
{
|
|
/* Can't do this. Sleep until next change. */
|
|
DEBUGF(" DSP_PROC_CHANNEL_MODE- deactivated\n");
|
|
return;
|
|
}
|
|
|
|
/* Switch to the real function and call it once */
|
|
this->process[0] = get_process_fn();
|
|
dsp_proc_call(this, buf_p, (unsigned)buf->format.changed - 1);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* DSP message hook */
|
|
static intptr_t channel_mode_configure(struct dsp_proc_entry *this,
|
|
struct dsp_config *dsp,
|
|
unsigned int setting,
|
|
intptr_t value)
|
|
{
|
|
switch (setting)
|
|
{
|
|
case DSP_PROC_INIT:
|
|
if (value == 0)
|
|
{
|
|
/* New object */
|
|
this->data = (intptr_t)&channel_mode_data;
|
|
this->process[1] = channel_mode_process_new_format;
|
|
((struct channel_mode_data *)this->data)->dsp = dsp;
|
|
}
|
|
|
|
/* Force format change call each time */
|
|
this->process[0] = channel_mode_process_new_format;
|
|
dsp_proc_activate(dsp, DSP_PROC_CHANNEL_MODE, true);
|
|
break;
|
|
|
|
case DSP_PROC_CLOSE:
|
|
((struct channel_mode_data *)this->data)->dsp = NULL;
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Database entry */
|
|
DSP_PROC_DB_ENTRY(
|
|
CHANNEL_MODE,
|
|
channel_mode_configure);
|